Skip to content
Merged
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ arrayvec = { version = "0.7", default-features = false, optional = true }
bitcode_derive = { version = "=0.6.5", path = "./bitcode_derive", optional = true }
bytemuck = { version = "1.14", features = [ "min_const_generics", "must_cast" ] }
glam = { version = ">=0.21", default-features = false, optional = true }
rust_decimal = { version = "1.36", default-features = false, optional = true }
serde = { version = "1.0", default-features = false, features = [ "alloc" ], optional = true }
time = { version = "0.3", default-features = false, optional = true }
uuid = { version = "1.10", default-features = false, optional = true }
Expand Down
3 changes: 2 additions & 1 deletion fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ cargo-fuzz = true

[dependencies]
arrayvec = { version = "0.7", features = ["serde"] }
bitcode = { path = "..", features = [ "arrayvec", "serde", "time" ] }
bitcode = { path = "..", features = [ "arrayvec", "rust_decimal", "serde", "time" ] }
libfuzzer-sys = "0.4"
rust_decimal = "1.36.0"
serde = { version ="1.0", features = [ "derive" ] }
time = { version = "0.3", features = ["serde"]}

Expand Down
2 changes: 2 additions & 0 deletions fuzz/fuzz_targets/fuzz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::fmt::Debug;
use std::num::NonZeroU32;
use std::time::Duration;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use rust_decimal::Decimal;

#[inline(never)]
fn test_derive<T: Debug + PartialEq + Encode + DecodeOwned>(data: &[u8]) {
Expand Down Expand Up @@ -209,6 +210,7 @@ fuzz_target!(|data: &[u8]| {
ArrayString<70>,
ArrayVec<u8, 5>,
ArrayVec<u8, 70>,
Decimal,
Duration,
Ipv4Addr,
Ipv6Addr,
Expand Down
2 changes: 2 additions & 0 deletions src/ext/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ mod arrayvec;
#[cfg(feature = "glam")]
#[rustfmt::skip] // Makes impl_struct! calls way longer.
mod glam;
#[cfg(feature = "rust_decimal")]
mod rust_decimal;
#[cfg(feature = "time")]
mod time;
#[cfg(feature = "uuid")]
Expand Down
80 changes: 80 additions & 0 deletions src/ext/rust_decimal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use crate::{
convert::{impl_convert, ConvertFrom},
int::ranged_int,
};
use rust_decimal::Decimal;

type Mantissa = [u8; 12];
ranged_int!(Scale, u8, 0, Decimal::MAX_SCALE as u8);
type DecimalEncode = (Mantissa, bool, u8);
type DecimalDecode = (Mantissa, bool, Scale);

impl ConvertFrom<&Decimal> for DecimalEncode {
#[inline(always)]
fn convert_from(value: &Decimal) -> Self {
let unpacked = value.unpack();
let [a0, a1, a2, a3] = unpacked.lo.to_le_bytes();
let [b0, b1, b2, b3] = unpacked.mid.to_le_bytes();
let [c0, c1, c2, c3] = unpacked.hi.to_le_bytes();
(
[a0, a1, a2, a3, b0, b1, b2, b3, c0, c1, c2, c3],
unpacked.negative,
unpacked.scale as u8,
)
}
}

impl ConvertFrom<DecimalDecode> for Decimal {
#[inline(always)]
fn convert_from(value: DecimalDecode) -> Self {
let [a0, a1, a2, a3, b0, b1, b2, b3, c0, c1, c2, c3] = value.0;
let lo = u32::from_le_bytes([a0, a1, a2, a3]);
let mid = u32::from_le_bytes([b0, b1, b2, b3]);
let hi = u32::from_le_bytes([c0, c1, c2, c3]);
let mut ret = Self::from_parts(lo, mid, hi, false, value.2.into_inner() as u32);
ret.set_sign_negative(value.1);
ret
}
}

impl_convert!(Decimal, DecimalEncode, DecimalDecode);

#[cfg(test)]
mod tests {
use crate::{decode, encode};
use rust_decimal::Decimal;
use std::str::FromStr;

#[test]
fn rust_decimal() {
assert!(Decimal::MAX_SCALE <= u8::MAX as u32);

let vs = [
Decimal::from(0),
Decimal::from_f64_retain(-0f64).unwrap(),
Decimal::from(-1),
Decimal::from(1) / Decimal::from(2),
Decimal::from(1),
Decimal::from(999999999999999999u64),
Decimal::from_str("3.100").unwrap(),
];
for v in vs {
let d = decode::<Decimal>(&encode(&v)).unwrap();
assert_eq!(d, v);
assert_eq!(d.is_sign_negative(), v.is_sign_negative());
assert_eq!(d.scale(), v.scale());
}

assert!(crate::decode::<Decimal>(&crate::encode(&([42u8; 12], false, 28u8))).is_ok());
assert!(crate::decode::<Decimal>(&crate::encode(&([42u8; 12], false, 29u8))).is_err());
}

use alloc::vec::Vec;
fn bench_data() -> Vec<Decimal> {
crate::random_data(1000)
.into_iter()
.map(|(n, s): (i64, u32)| Decimal::new(n, s % (Decimal::MAX_SCALE + 1)))
.collect()
}
crate::bench_encode_decode!(decimal_vec: Vec<_>);
}
4 changes: 3 additions & 1 deletion src/int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,9 @@ macro_rules! ranged_int {
type Bits = $int;
#[inline(always)]
fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
($lower..=$upper).contains(bits)
const LOWER: $int = $lower;
const UPPER: $int = $upper;
(LOWER..=UPPER).contains(bits)
}
}
impl $type {
Expand Down
Loading