Skip to content

Commit 3088cbd

Browse files
authored
Fix #38 - support rust_decimal. (#42)
1 parent 7f93b49 commit 3088cbd

6 files changed

Lines changed: 90 additions & 2 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ arrayvec = { version = "0.7", default-features = false, optional = true }
1919
bitcode_derive = { version = "=0.6.5", path = "./bitcode_derive", optional = true }
2020
bytemuck = { version = "1.14", features = [ "min_const_generics", "must_cast" ] }
2121
glam = { version = ">=0.21", default-features = false, optional = true }
22+
rust_decimal = { version = "1.36", default-features = false, optional = true }
2223
serde = { version = "1.0", default-features = false, features = [ "alloc" ], optional = true }
2324
time = { version = "0.3", default-features = false, optional = true }
2425
uuid = { version = "1.10", default-features = false, optional = true }

fuzz/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ cargo-fuzz = true
1010

1111
[dependencies]
1212
arrayvec = { version = "0.7", features = ["serde"] }
13-
bitcode = { path = "..", features = [ "arrayvec", "serde", "time" ] }
13+
bitcode = { path = "..", features = [ "arrayvec", "rust_decimal", "serde", "time" ] }
1414
libfuzzer-sys = "0.4"
15+
rust_decimal = "1.36.0"
1516
serde = { version ="1.0", features = [ "derive" ] }
1617
time = { version = "0.3", features = ["serde"]}
1718

fuzz/fuzz_targets/fuzz.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::fmt::Debug;
1010
use std::num::NonZeroU32;
1111
use std::time::Duration;
1212
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
13+
use rust_decimal::Decimal;
1314

1415
#[inline(never)]
1516
fn test_derive<T: Debug + PartialEq + Encode + DecodeOwned>(data: &[u8]) {
@@ -209,6 +210,7 @@ fuzz_target!(|data: &[u8]| {
209210
ArrayString<70>,
210211
ArrayVec<u8, 5>,
211212
ArrayVec<u8, 70>,
213+
Decimal,
212214
Duration,
213215
Ipv4Addr,
214216
Ipv6Addr,

src/ext/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ mod arrayvec;
33
#[cfg(feature = "glam")]
44
#[rustfmt::skip] // Makes impl_struct! calls way longer.
55
mod glam;
6+
#[cfg(feature = "rust_decimal")]
7+
mod rust_decimal;
68
#[cfg(feature = "time")]
79
mod time;
810
#[cfg(feature = "uuid")]

src/ext/rust_decimal.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
use crate::{
2+
convert::{impl_convert, ConvertFrom},
3+
int::ranged_int,
4+
};
5+
use rust_decimal::Decimal;
6+
7+
type Mantissa = [u8; 12];
8+
ranged_int!(Scale, u8, 0, Decimal::MAX_SCALE as u8);
9+
type DecimalEncode = (Mantissa, bool, u8);
10+
type DecimalDecode = (Mantissa, bool, Scale);
11+
12+
impl ConvertFrom<&Decimal> for DecimalEncode {
13+
#[inline(always)]
14+
fn convert_from(value: &Decimal) -> Self {
15+
let unpacked = value.unpack();
16+
let [a0, a1, a2, a3] = unpacked.lo.to_le_bytes();
17+
let [b0, b1, b2, b3] = unpacked.mid.to_le_bytes();
18+
let [c0, c1, c2, c3] = unpacked.hi.to_le_bytes();
19+
(
20+
[a0, a1, a2, a3, b0, b1, b2, b3, c0, c1, c2, c3],
21+
unpacked.negative,
22+
unpacked.scale as u8,
23+
)
24+
}
25+
}
26+
27+
impl ConvertFrom<DecimalDecode> for Decimal {
28+
#[inline(always)]
29+
fn convert_from(value: DecimalDecode) -> Self {
30+
let [a0, a1, a2, a3, b0, b1, b2, b3, c0, c1, c2, c3] = value.0;
31+
let lo = u32::from_le_bytes([a0, a1, a2, a3]);
32+
let mid = u32::from_le_bytes([b0, b1, b2, b3]);
33+
let hi = u32::from_le_bytes([c0, c1, c2, c3]);
34+
let mut ret = Self::from_parts(lo, mid, hi, false, value.2.into_inner() as u32);
35+
ret.set_sign_negative(value.1);
36+
ret
37+
}
38+
}
39+
40+
impl_convert!(Decimal, DecimalEncode, DecimalDecode);
41+
42+
#[cfg(test)]
43+
mod tests {
44+
use crate::{decode, encode};
45+
use rust_decimal::Decimal;
46+
use std::str::FromStr;
47+
48+
#[test]
49+
fn rust_decimal() {
50+
assert!(Decimal::MAX_SCALE <= u8::MAX as u32);
51+
52+
let vs = [
53+
Decimal::from(0),
54+
Decimal::from_f64_retain(-0f64).unwrap(),
55+
Decimal::from(-1),
56+
Decimal::from(1) / Decimal::from(2),
57+
Decimal::from(1),
58+
Decimal::from(999999999999999999u64),
59+
Decimal::from_str("3.100").unwrap(),
60+
];
61+
for v in vs {
62+
let d = decode::<Decimal>(&encode(&v)).unwrap();
63+
assert_eq!(d, v);
64+
assert_eq!(d.is_sign_negative(), v.is_sign_negative());
65+
assert_eq!(d.scale(), v.scale());
66+
}
67+
68+
assert!(crate::decode::<Decimal>(&crate::encode(&([42u8; 12], false, 28u8))).is_ok());
69+
assert!(crate::decode::<Decimal>(&crate::encode(&([42u8; 12], false, 29u8))).is_err());
70+
}
71+
72+
use alloc::vec::Vec;
73+
fn bench_data() -> Vec<Decimal> {
74+
crate::random_data(1000)
75+
.into_iter()
76+
.map(|(n, s): (i64, u32)| Decimal::new(n, s % (Decimal::MAX_SCALE + 1)))
77+
.collect()
78+
}
79+
crate::bench_encode_decode!(decimal_vec: Vec<_>);
80+
}

src/int.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,9 @@ macro_rules! ranged_int {
150150
type Bits = $int;
151151
#[inline(always)]
152152
fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
153-
($lower..=$upper).contains(bits)
153+
const LOWER: $int = $lower;
154+
const UPPER: $int = $upper;
155+
(LOWER..=UPPER).contains(bits)
154156
}
155157
}
156158
impl $type {

0 commit comments

Comments
 (0)