From 54871d4bffa7ba5b824c17db67685c186bc6c8e0 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Tue, 28 Nov 2023 10:38:44 -0800 Subject: [PATCH] feat(bitfield): add `u128` support (#467) This branch adds support for `u128` as a representation for the `bitfield!` macro, and packing specs for 128-bit numbers. Closes #466 --- bitfield/src/bitfield.rs | 35 ++++++++++++++++++++++++++++++++--- bitfield/src/from_bits.rs | 20 +++++++++++--------- bitfield/src/pack.rs | 13 +++++++++++-- 3 files changed, 54 insertions(+), 14 deletions(-) diff --git a/bitfield/src/bitfield.rs b/bitfield/src/bitfield.rs index 0100c2c0..0be46a9e 100644 --- a/bitfield/src/bitfield.rs +++ b/bitfield/src/bitfield.rs @@ -11,8 +11,8 @@ /// # Generated Implementations /// /// The `bitfield!` macro generates a type with the following functions, where -/// `{int}` is the integer type that represents the bitfield (one of `u8`, -/// `u16`,`u32`, `u64`, or `usize`): +/// `{int}` is the integer type that represents the bitfield (one of [`u8`], +/// [`u16`], [`u32`], [`u64`], [`u128`], or [`usize`]): /// /// - `const fn new() -> Self`: Returns a new instance of the bitfield type with /// all bits zeroed. @@ -833,11 +833,12 @@ macro_rules! bitfield { // }; (@t usize, $V:ty, $F:ty) => { $crate::PackUsize<$V, $F> }; + (@t u128, $V:ty, $F:ty) => { $crate::Pack128<$V, $F> }; (@t u64, $V:ty, $F:ty) => { $crate::Pack64<$V, $F> }; (@t u32, $V:ty, $F:ty) => { $crate::Pack32<$V, $F> }; (@t u16, $V:ty, $F:ty) => { $crate::Pack16<$V, $F> }; (@t u8, $V:ty, $F:ty) => { $crate::Pack8<$V, $F> }; - (@t $T:ty, $V:ty, $F:ty) => { compile_error!(concat!("unsupported bitfield type `", stringify!($T), "`; expected one of `usize`, `u64`, `u32`, `u16`, or `u8`")) } + (@t $T:ty, $V:ty, $F:ty) => { compile_error!(concat!("unsupported bitfield type `", stringify!($T), "`; expected one of `usize`, `u128`, `u64`, `u32`, `u16`, or `u8`")) } } #[cfg(test)] @@ -857,6 +858,21 @@ mod tests { } } + bitfield! { + /// This is only here to ensure it compiles... + #[allow(dead_code)] + struct TestBitfieldHuge { + const HELLO = 4; + const _RESERVED_1 = 3; + const WORLD: bool; + const HAVE: TestEnum; + const LOTS = 5; + const OF = 1; + const FUN = 6; + const REST = ..; + } + } + #[repr(u8)] #[derive(Debug)] enum TestEnum { @@ -885,6 +901,19 @@ mod tests { } } + impl FromBits for TestEnum { + const BITS: u32 = 2; + type Error = core::convert::Infallible; + + fn try_from_bits(bits: u128) -> Result { + FromBits::::try_from_bits(bits as u32) + } + + fn into_bits(self) -> u128 { + self as u8 as u128 + } + } + #[derive(Debug)] #[allow(dead_code)] struct TestDebug { diff --git a/bitfield/src/from_bits.rs b/bitfield/src/from_bits.rs index 32d77da0..7ec40449 100644 --- a/bitfield/src/from_bits.rs +++ b/bitfield/src/from_bits.rs @@ -337,19 +337,20 @@ macro_rules! impl_frombits_for_bool { } impl_frombits_for_bool! { - impl FromBits for bool {} + impl FromBits for bool {} } impl_frombits_for_ty! { - impl FromBits for u8 {} - impl FromBits for u16 {} - impl FromBits for u32 {} - impl FromBits for u64 {} + impl FromBits for u8 {} + impl FromBits for u16 {} + impl FromBits for u32 {} + impl FromBits for u64 {} + impl FromBits for u128 {} - impl FromBits for i8 {} - impl FromBits for i16 {} - impl FromBits for i32 {} - impl FromBits for i64 {} + impl FromBits for i8 {} + impl FromBits for i16 {} + impl FromBits for i32 {} + impl FromBits for i64 {} // Rust doesn't support 8 bit targets, so {u,i}size are always at least 16 bit wide, // source: https://doc.rust-lang.org/1.45.2/src/core/convert/num.rs.html#134-139 @@ -363,6 +364,7 @@ impl_frombits_for_ty! { impl FromBits for usize {} impl FromBits for isize {} + impl FromBits for usize {} } #[cfg(target_pointer_width = "16")] diff --git a/bitfield/src/pack.rs b/bitfield/src/pack.rs index 9ebe947b..3ee791c7 100644 --- a/bitfield/src/pack.rs +++ b/bitfield/src/pack.rs @@ -6,8 +6,9 @@ //! The bit packing utilities consist of a type that defines a specification for //! a bit range to pack into, and a wrapper type for an unsigned integer //! defining methods to pack bit ranges into it. Packing specs are defined for -//! [`u64`], [`u32`], [`u16`], and [`u8`], as [`Pack64`], [`Pack32`], -//! [`Pack16`], and [`Pack8`], respectively. +//! [`usize`], [`u128`], [`u64`], [`u32`], [`u16`], and [`u8`], as +//! [`PackUsize`], [`Pack128`], [`Pack64`], [`Pack32`], [`Pack16`], and +//! [`Pack8`], respectively. //! //! Note that the bit packing utilities are generic using macros, rather than //! using generics and traits, because they are intended to be usable in @@ -1079,6 +1080,7 @@ macro_rules! make_packers { make_packers! { pub struct PackUsize { bits: usize, packing: PackingUsize, pair: PairUsize } + pub struct Pack128 { bits: u128, packing: Packing128, pair: Pair128 } pub struct Pack64 { bits: u64, packing: Packing64, pair: Pair64, } pub struct Pack32 { bits: u32, packing: Packing32, pair: Pair32, } pub struct Pack16 { bits: u16, packing: Packing16, pair: Pair16, } @@ -1281,6 +1283,7 @@ mod tests { } test_pack_unpack! { + fn pack_unpack_128(128); fn pack_unpack_64(64); fn pack_unpack_32(32); fn pack_unpack_16(16); @@ -1288,6 +1291,8 @@ mod tests { } test_pack_methods! { + + fn pack_methods_128(128); fn pack_methods_64(64); fn pack_methods_32(32); fn pack_methods_16(16); @@ -1295,6 +1300,7 @@ mod tests { } test_from_range! { + fn pack_from_src_range_128(128); fn pack_from_src_range_64(64); fn pack_from_src_range_32(32); fn pack_from_src_range_16(16); @@ -1302,6 +1308,8 @@ mod tests { } test_pair_least_sig_zeroed! { + + fn pair_least_sig_zeroed_128(128); fn pair_least_sig_zeroed_64(64); fn pair_least_sig_zeroed_32(32); fn pair_least_sig_zeroed_16(16); @@ -1309,6 +1317,7 @@ mod tests { } test_pair_least_sig_arbitrary! { + fn pair_least_sig_arbitrary_128(128); fn pair_least_sig_arbitrary_64(64); fn pair_least_sig_arbitrary_32(32); fn pair_least_sig_arbitrary_16(16);