bitpiece/lib.rs
1//! **A Rust crate for effortlessly defining and manipulating bitfields with procedural macros.**
2//!
3//! `bitpiece` takes the complexity out of bit-level data manipulation. It provides a powerful `#[bitpiece]` macro that lets you define structs and enums as compact, typed bitfields, while automatically generating a safe, high-level API to interact with them. It's perfect for working with network protocols, hardware interfaces, or any scenario where data compactness is key.
4//!
5//! # Features
6//!
7//! - **Declarative & Simple**: Define complex bitfield layouts using simple Rust structs and enums.
8//! - **Type-Safe API**: The macro generates getters and setters for each field, so you work with `bool`, `u8`, `enum` types, etc., not raw bit shifts and masks.
9//! - **Flexible**: Supports defining types which have exotic bit lengths, for example a 6-bit struct made of two 3-bit fields.
10//! - **Nestable**: Compose complex bitfields by nesting `bitpiece` types within each other.
11//! - **Arbitrary-Width Integers**: Use the built-in `B1`-`B64` types (e.g., `B3`, `B7`, `B12`) for unsigned integers with non-standard bit lengths, or the `SB1`-`SB64` types for signed integers.
12//! - **Enums**: Supports using enums as bitfields, with automatic validation of input for non-exhaustive enums.
13//! - **Compile-Time Validation**: Optionally specify an expected bit length on your structs (e.g., `#[bitpiece(32)]`) to get a compile-time error if it doesn't match the sum of its fields.
14//! - **Safe & Unsafe APIs**: Provides both panicking (`from_bits`) and fallible (`try_from_bits`) APIs for creating bitpieces from raw integer values.
15//! - `#![no_std]` compatible.
16//!
17//! # Getting Started
18//!
19//! First, add `bitpiece` to your `Cargo.toml`:
20//!
21//! ```toml
22//! [dependencies]
23//! bitpiece = "0.1.0" # Use the latest version
24//! ```
25//!
26//! Now, let's define a bitfield for a hypothetical network packet header.
27//!
28//! ```rust
29//! use bitpiece::*;
30//!
31//! // Define a 2-bit enum for the packet's priority.
32//! // The macro automatically infers it needs 2 bits.
33//! #[bitpiece]
34//! #[derive(Debug, Clone, Copy, PartialEq, Eq)]
35//! enum Priority {
36//! Low = 0,
37//! Medium = 1,
38//! High = 2,
39//! Critical = 3,
40//! }
41//!
42//! // Define the packet header structure.
43//! // The macro calculates the total size (1 + 2 + 5 = 8 bits).
44//! #[bitpiece(8)] // The `(8)` is optional but validates the size at compile time.
45//! #[derive(Debug, Clone, Copy, PartialEq, Eq)]
46//! struct PacketHeader {
47//! is_fragment: bool,
48//! priority: Priority,
49//! payload_len: B5, // A 5-bit integer type
50//! }
51//!
52//! fn main() {
53//! // Create a new header from raw bits (e.g., received from a network).
54//! // Bits: 0b101_10_1 => is_fragment=1, priority=2 (High), payload_len=5
55//! let mut header = PacketHeader::from_bits(0b101101);
56//!
57//! // Use the generated getter methods to safely access fields.
58//! assert_eq!(header.is_fragment(), true);
59//! assert_eq!(header.priority(), Priority::High);
60//! assert_eq!(header.payload_len().get(), 5); // Use .get() for B-types
61//!
62//! // Use the generated setter methods to modify the header.
63//! header.set_priority(Priority::Critical);
64//! header.set_payload_len(B5::new(31)); // Set to max value (2^5 - 1)
65//!
66//! assert_eq!(header.priority(), Priority::Critical);
67//! assert_eq!(header.payload_len().get(), 31);
68//!
69//! // The underlying storage is automatically updated.
70//! // Bits: 0b11111_11_1
71//! assert_eq!(header.to_bits(), 0b11111111);
72//!
73//! // You can also construct a bitpiece from its fields directly.
74//! let from_fields = PacketHeader::from_fields(PacketHeaderFields {
75//! is_fragment: false,
76//! priority: Priority::Low,
77//! payload_len: B5::new(10),
78//! });
79//!
80//! assert_eq!(from_fields.to_bits(), 0b1010000);
81//! }
82//! ```
83//!
84//! # More Examples
85//!
86//! ## Nesting
87//!
88//! You can easily build complex structures by nesting `bitpiece` types.
89//!
90//! ```rust
91//! use bitpiece::*;
92//!
93//! #[bitpiece(4)]
94//! #[derive(Debug, Clone, Copy, PartialEq, Eq)]
95//! struct MacAddressPart {
96//! a: B1,
97//! b: B3,
98//! }
99//!
100//! #[bitpiece(16)]
101//! #[derive(Debug, Clone, Copy, PartialEq, Eq)]
102//! struct ProtocolInfo {
103//! part1: MacAddressPart,
104//! part2: MacAddressPart,
105//! flags: u8, // Standard integer types are also supported
106//! }
107//!
108//! fn main() {
109//! let mut info = ProtocolInfo::zeroes(); // zeroes() is a handy constructor
110//!
111//! info.set_part1(MacAddressPart::from_bits(0b1010));
112//!
113//! assert_eq!(info.part1().b().get(), 0b101);
114//! assert_eq!(info.to_bits(), 0b00000000_1010);
115//!
116//! // Set a field in a nested bitpiece
117//! info.part1_mut().set_b(B3::new(0b110));
118//!
119//! assert_eq!(info.part1().b().get(), 0b110);
120//! assert_eq!(info.to_bits(), 0b00000000_1100);
121//! }
122//! ```
123//!
124//! ## Non-Exhaustive Enums
125//!
126//! By default, an enum's bit-length is determined by its largest variant. If you try to create an enum from an invalid integer value, it will panic.
127//!
128//! Sometimes, however, an enum definition isn't complete, but you still want to handle known variants. For this, `bitpiece` generates a `try_from_bits` method.
129//!
130//! ```rust
131//! use bitpiece::*;
132//!
133//! #[bitpiece] // Bit length is inferred as 7 bits (from 120)
134//! #[derive(Debug, Clone, Copy, PartialEq, Eq)]
135//! enum OpCode {
136//! Read = 0,
137//! Write = 1,
138//! Sync = 80,
139//! Halt = 120,
140//! }
141//!
142//! fn main() {
143//! // try_from_bits returns an Option, which is great for safe parsing.
144//! let known_code = OpCode::try_from_bits(80);
145//! assert_eq!(known_code, Some(OpCode::Sync));
146//!
147//! let unknown_code = OpCode::try_from_bits(55);
148//! assert_eq!(unknown_code, None);
149//!
150//! // In contrast, from_bits will panic on an unknown variant.
151//! // let panicked = OpCode::from_bits(55); // This would panic!
152//! }
153//! ```
154//!
155//! ## Explicit Bit-Length on Enums
156//!
157//! You can give an enum a larger bit-width than it needs. This is useful when a protocol reserves a certain number of bits for an enum, even if not all values are currently used.
158//!
159//! ```rust
160//! use bitpiece::*;
161//!
162//! // This enum's highest value is 2, which only needs 2 bits.
163//! // But we can force it to occupy a full byte.
164//! #[bitpiece(8)]
165//! #[derive(Debug, Clone, Copy, PartialEq, Eq)]
166//! enum MessageType {
167//! Query = 0,
168//! Ack = 1,
169//! Nak = 2,
170//! }
171//!
172//! fn main() {
173//! // The underlying storage type will be u8.
174//! assert_eq!(MessageType::from_bits(1).to_bits(), 1u8);
175//!
176//! assert_eq!(MessageType::try_from_bits(200), None); // Fails, not a valid variant
177//! }
178//! ```
179//!
180//! # Generated API
181//!
182//! For a struct like `MyPiece { field_a: bool, field_b: B3 }`, the macro generates:
183//!
184//! - `MyPiece::from_bits(u8) -> Self`: Creates an instance from raw bits. Panics if any field gets an invalid value (e.g., for a non-exhaustive enum).
185//! - `MyPiece::try_from_bits(u8) -> Option<Self>`: Safely creates an instance, returning `None` if any field would be invalid.
186//! - `my_piece.to_bits() -> u8`: Returns the raw bits as the smallest possible integer storage type.
187//! - `MyPiece::from_fields(MyPieceFields) -> Self`: Creates an instance from a struct containing all the fields.
188//! - `my_piece.to_fields() -> MyPieceFields`: Deconstructs the instance into a struct of its fields.
189//! - `MyPiece::zeroes() -> Self`: A constructor where all bits are 0.
190//! - `my_piece.field_a() -> bool`: Getter for `field_a`.
191//! - `my_piece.set_field_a(bool)`: Setter for `field_a`.
192//! - `my_piece.field_b() -> B3`: Getter for `field_b`.
193//! - `my_piece.set_field_b(B3)`: Setter for `field_b`.
194//! - `my_piece.field_a_mut() -> BitPieceMut`: Advanced usage for mutable access, especially for nested pieces.
195//! - `my_piece.field_b_mut() -> BitPieceMut`: Same as above, but for field_b
196
197#![no_std]
198
199pub use bitpiece_macros::bitpiece;
200use core::{marker::PhantomData, num::TryFromIntError};
201use paste::paste;
202
203/// an empty struct used to represent a specific bit length.
204/// this is then combined with some traits ([`ExactAssociatedStorage`], [`AssociatedStorage`]) to perform operations on the
205/// specified bit length.
206pub struct BitLength<const BITS: usize>;
207
208/// a trait implemented for [`BitLength`] types that have an exact associated storage type, for example [`u8`] or [`u16`].
209pub trait ExactAssociatedStorage {
210 /// the exact storage type, for example [`u8`] or [`u16`].
211 type Storage: BitStorage;
212}
213
214/// a trait implemented for all [`BitLength`] types that are small enough and provides the minimal storage type required for
215/// storing that amount of bits. for example for bit lengths `0..8` this will be [`u8`].
216pub trait AssociatedStorage {
217 /// the storage type required for storing that amount of bits. for example for bit lengths `0..8` this will be [`u8`].
218 type Storage: BitStorage;
219}
220
221macro_rules! impl_exact_associated_storage {
222 { $($bit_length: literal),+ $(,)? } => {
223 $(
224 paste! {
225 impl ExactAssociatedStorage for BitLength<$bit_length> {
226 type Storage = [<u $bit_length>];
227 }
228 }
229 )+
230 }
231}
232impl_exact_associated_storage! { 8, 16, 32, 64 }
233
234/// calculate the bit length of the smallest type required to store that amount of bits. for example for bits lengths `0..8` this
235/// will return `8`.
236const fn exact_associated_storage_bit_length(bit_length: usize) -> usize {
237 let power_of_2 = bit_length.next_power_of_two();
238 if power_of_2 < 8 {
239 8
240 } else {
241 power_of_2
242 }
243}
244macro_rules! impl_associated_storage {
245 { $($bit_length: literal),+ $(,)? } => {
246 $(
247 impl AssociatedStorage for BitLength<$bit_length> {
248 type Storage = <BitLength< { exact_associated_storage_bit_length($bit_length) } > as ExactAssociatedStorage>::Storage;
249 }
250 )+
251 };
252}
253impl_associated_storage! {
254 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
255 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
256}
257
258/// a mutable reference to a bitpiece inside another bitpiece.
259pub trait BitPieceMut<'s, S: BitStorage + 's, P: BitPiece> {
260 fn new(storage: &'s mut S, start_bit_index: usize) -> Self;
261 fn get(&self) -> P;
262 fn set(&mut self, new_value: P);
263}
264
265/// a bitpiece.
266/// this is the core trait of this crate and represents a type with a specified bit length which can be used in a standalone way
267/// or inside another bitpiece.
268pub trait BitPiece: Clone + Copy {
269 /// the length in bits of this type.
270 const BITS: usize;
271
272 /// the storage type used internally to store the bits of this bitpiece.
273 type Bits: BitStorage;
274
275 /// the type used to represent a mutable reference to this type inside another bitpiece.
276 type Mut<'s, S: BitStorage + 's>: BitPieceMut<'s, S, Self>;
277
278 /// the type which represents the expanded view of this bitpiece.
279 type Fields;
280
281 /// constructs this type with a value of zero for all fields.
282 fn zeroes() -> Self {
283 Self::from_bits(Self::Bits::ZEROES)
284 }
285
286 /// constructs this type with a value of one for all fields.
287 fn ones() -> Self {
288 Self::from_bits(Self::Bits::ONES)
289 }
290
291 /// constructs this type from the given fields.
292 fn from_fields(fields: Self::Fields) -> Self;
293
294 /// return the values of all fields of this bitpiece.
295 fn to_fields(self) -> Self::Fields;
296
297 /// constructs this type from the given bits.
298 fn from_bits(bits: Self::Bits) -> Self {
299 Self::try_from_bits(bits).unwrap()
300 }
301
302 /// tries to construct this type from the given bits, if the given bits represent a valid value of this type.
303 fn try_from_bits(bits: Self::Bits) -> Option<Self>;
304
305 /// returns the underlying bits of this type.
306 fn to_bits(self) -> Self::Bits;
307}
308macro_rules! impl_bitpiece_for_unsigned_int_types {
309 { $($bit_len: literal),+ $(,)? } => {
310 $(
311 paste! {
312 impl BitPiece for [<u $bit_len>] {
313 const BITS: usize = $bit_len;
314 type Bits = Self;
315 type Fields = Self;
316 type Mut<'s, S: BitStorage + 's> = GenericBitPieceMut<'s, S, Self>;
317 fn from_fields(fields: Self::Fields) -> Self {
318 fields
319 }
320 fn to_fields(self) -> Self::Fields {
321 self
322 }
323 fn try_from_bits(bits: Self::Bits) -> Option<Self> {
324 Some(bits)
325 }
326 fn to_bits(self) -> Self::Bits {
327 self
328 }
329 }
330 }
331 )+
332 };
333}
334impl_bitpiece_for_unsigned_int_types! { 8, 16, 32, 64 }
335
336macro_rules! impl_bitpiece_for_signed_int_types {
337 { $($bit_len: literal),+ $(,)? } => {
338 $(
339 paste! {
340 impl BitPiece for [<i $bit_len>] {
341 const BITS: usize = $bit_len;
342 type Bits = [<u $bit_len>];
343 type Fields = Self;
344 type Mut<'s, S: BitStorage + 's> = GenericBitPieceMut<'s, S, Self>;
345 fn from_fields(fields: Self::Fields) -> Self {
346 fields
347 }
348 fn to_fields(self) -> Self::Fields {
349 self
350 }
351 fn try_from_bits(bits: Self::Bits) -> Option<Self> {
352 Some(bits as Self)
353 }
354 fn to_bits(self) -> Self::Bits {
355 self as Self::Bits
356 }
357 }
358 }
359 )+
360 };
361}
362impl_bitpiece_for_signed_int_types! { 8, 16, 32, 64 }
363
364/// a generic implementation of the [`BitPieceMut`] trait used for convenience.
365pub struct GenericBitPieceMut<'s, S: BitStorage + 's, P: BitPiece> {
366 bits: BitsMut<'s, S>,
367 phantom: PhantomData<P>,
368}
369
370impl<'s, S: BitStorage + 's, P: BitPiece> BitPieceMut<'s, S, P> for GenericBitPieceMut<'s, S, P> {
371 fn new(storage: &'s mut S, start_bit_index: usize) -> Self {
372 Self {
373 bits: BitsMut::new(storage, start_bit_index),
374 phantom: PhantomData,
375 }
376 }
377
378 fn get(&self) -> P {
379 let bits = self.bits.get_bits(0, P::BITS);
380 let correct_type_bits = P::Bits::from_u64(bits).unwrap();
381 P::from_bits(correct_type_bits)
382 }
383
384 fn set(&mut self, new_value: P) {
385 self.bits.set_bits(0, P::BITS, new_value.to_bits().to_u64())
386 }
387}
388
389impl BitPiece for bool {
390 const BITS: usize = 1;
391
392 type Bits = u8;
393
394 type Fields = bool;
395
396 type Mut<'s, S: BitStorage + 's> = GenericBitPieceMut<'s, S, Self>;
397
398 fn zeroes() -> Self {
399 return false;
400 }
401
402 fn from_fields(fields: Self::Fields) -> Self {
403 fields
404 }
405
406 fn to_fields(self) -> Self::Fields {
407 self
408 }
409
410 fn try_from_bits(bits: Self::Bits) -> Option<Self> {
411 Some(bits != 0)
412 }
413
414 fn to_bits(self) -> Self::Bits {
415 if self {
416 1
417 } else {
418 0
419 }
420 }
421}
422
423macro_rules! define_b_type {
424 { $bit_len: literal, $ident: ident, $storage: ty } => {
425 /// a type used to represent a field with a specific amount of bits.
426 #[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
427 pub struct $ident($storage);
428 impl BitPiece for $ident {
429 const BITS: usize = $bit_len;
430
431 type Bits = $storage;
432
433 type Fields = Self;
434
435 type Mut<'s, S: BitStorage + 's> = GenericBitPieceMut<'s, S, Self>;
436
437 fn from_fields(fields: Self::Fields) -> Self {
438 fields
439 }
440
441 fn to_fields(self) -> Self::Fields {
442 self
443 }
444
445 fn try_from_bits(bits: Self::Bits) -> Option<Self> {
446 Self::try_new(bits)
447 }
448
449 fn to_bits(self) -> Self::Bits {
450 self.0
451 }
452 }
453 impl $ident {
454 /// the max allowed value for this type.
455 pub const MAX: Self = Self(
456 if $bit_len == <$storage>::BITS {
457 // if the bit length is equal to the amount of bits in our storage type, avoid the overflow
458 // which will happen when shifting, and just returns the maximum value of the underlying
459 // storage type.
460 <$storage>::MAX
461 } else {
462 ((1 as $storage) << $bit_len).wrapping_sub(1)
463 }
464 );
465
466 /// the bit length of this type.
467 pub const BIT_LENGTH: usize = $bit_len;
468
469 /// creates a new instance of this bitfield type with the given value.
470 ///
471 /// this function panics if the value does not fit within the bit length of this type.
472 pub fn new(value: $storage) -> Self {
473 Self::try_new(value).unwrap()
474 }
475
476 /// creates a new instance of this bitfield type with the given value.
477 ///
478 /// if the value does not fit within the bit length of this type, returns `None`.
479 pub fn try_new(value: $storage) -> Option<Self> {
480 if value <= Self::MAX.0 {
481 Some(Self(value))
482 } else {
483 None
484 }
485 }
486
487 /// creates a new instance of this bitfield type with the given value, without checking that the value
488 /// fits within the bit length of this type.
489 ///
490 /// # safety
491 /// the provided value must fit within the bit length of this type.
492 pub unsafe fn new_unchecked(value: $storage) -> Self {
493 Self(value)
494 }
495
496 /// returns the inner value.
497 pub fn get(&self) -> $storage {
498 self.0
499 }
500 }
501 impl core::fmt::Display for $ident {
502 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
503 core::fmt::Display::fmt(&self.0, f)
504 }
505 }
506 impl core::fmt::Debug for $ident {
507 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
508 core::fmt::Debug::fmt(&self.0, f)
509 }
510 }
511 };
512}
513macro_rules! define_b_types {
514 { $($bit_len: literal),+ $(,)? } => {
515 $(
516 paste!{
517 define_b_type! { $bit_len, [<B $bit_len>], <BitLength<$bit_len> as AssociatedStorage>::Storage }
518 }
519 )+
520 };
521}
522define_b_types! {
523 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
524 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64
525}
526
527/// a type which can be used as the internal storage of a bitpiece.
528pub trait BitStorage: BitPiece {
529 const ZEROES: Self;
530 const ONES: Self;
531
532 /// the signed version of this storage integer type.
533 type Signed;
534
535 fn to_u64(self) -> u64;
536 fn from_u64(value: u64) -> Result<Self, TryFromIntError>;
537}
538
539impl BitStorage for u64 {
540 const ZEROES: Self = 0;
541 const ONES: Self = u64::MAX;
542
543 type Signed = i64;
544
545 fn to_u64(self) -> u64 {
546 self
547 }
548
549 fn from_u64(value: u64) -> Result<Self, TryFromIntError> {
550 Ok(value)
551 }
552}
553
554macro_rules! impl_bit_storage_for_small_unsigned_int_types {
555 { $($bit_len: literal),+ } => {
556 $(
557 paste::paste! {
558 impl BitStorage for [<u $bit_len>] {
559 const ZEROES: Self = 0;
560 const ONES: Self = Self::MAX;
561 type Signed = [<i $bit_len>];
562 fn to_u64(self) -> u64 {
563 self as u64
564 }
565 fn from_u64(value: u64) -> Result<Self, TryFromIntError> {
566 value.try_into()
567 }
568 }
569 }
570 )+
571 };
572}
573impl_bit_storage_for_small_unsigned_int_types! { 8, 16, 32 }
574
575/// a convenience type for interacting with the bits of an underlying storage type, starting at a specific bit index.
576/// this is useful for implementing mutable references.
577pub struct BitsMut<'s, S: BitStorage> {
578 pub storage: &'s mut S,
579 pub start_bit_index: usize,
580}
581impl<'s, S: BitStorage> BitsMut<'s, S> {
582 #[inline(always)]
583 pub fn new(storage: &'s mut S, start_bit_index: usize) -> Self {
584 Self {
585 storage,
586 start_bit_index,
587 }
588 }
589
590 /// returns `len` bits starting at relative bit index `rel_bit_index`.
591 #[inline(always)]
592 pub fn get_bits(&self, rel_bit_index: usize, len: usize) -> u64 {
593 extract_bits(
594 self.storage.to_u64(),
595 self.start_bit_index + rel_bit_index,
596 len,
597 )
598 }
599
600 /// modifies the `len` bits starting at relative bit index `rel_bit_index` to the given `new_value`.
601 #[inline(always)]
602 pub fn set_bits(&mut self, rel_bit_index: usize, len: usize, new_value: u64) {
603 *self.storage = S::from_u64(modify_bits(
604 self.storage.to_u64(),
605 self.start_bit_index + rel_bit_index,
606 len,
607 new_value,
608 ))
609 .unwrap();
610 }
611}
612
613#[inline(always)]
614const fn extract_bits_mask(len: usize) -> u64 {
615 (1u64 << len).wrapping_sub(1)
616}
617
618#[inline(always)]
619const fn extract_bits_shifted_mask(offset: usize, len: usize) -> u64 {
620 extract_bits_mask(len) << offset
621}
622
623/// extracts some bits from a value
624#[inline(always)]
625pub const fn extract_bits(value: u64, offset: usize, len: usize) -> u64 {
626 let mask = extract_bits_mask(len);
627 (value >> offset) & mask
628}
629
630/// extracts some bits (mask only, no shift) from a value
631#[inline(always)]
632pub const fn extract_bits_noshift(value: u64, offset: usize, len: usize) -> u64 {
633 let mask = extract_bits_mask(len);
634 let shifted_mask = mask << offset;
635 value & shifted_mask
636}
637/// returns a new value with the specified bit range modified to the new value
638#[inline(always)]
639pub const fn modify_bits(value: u64, offset: usize, len: usize, new_value: u64) -> u64 {
640 let shifted_mask = extract_bits_shifted_mask(offset, len);
641
642 let without_original_bits = value & (!shifted_mask);
643 let shifted_new_value = new_value << offset;
644 without_original_bits | shifted_new_value
645}
646
647macro_rules! define_sb_type {
648 { $bit_len: literal, $ident: ident, $storage: ty, $storage_signed: ty } => {
649 /// a type used to represent a field with a specific amount of bits.
650 #[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
651 pub struct $ident($storage_signed);
652 impl BitPiece for $ident {
653 const BITS: usize = $bit_len;
654
655 type Bits = $storage;
656
657 type Fields = Self;
658
659 type Mut<'s, S: BitStorage + 's> = GenericBitPieceMut<'s, S, Self>;
660
661 fn from_fields(fields: Self::Fields) -> Self {
662 fields
663 }
664
665 fn to_fields(self) -> Self::Fields {
666 self
667 }
668
669 fn try_from_bits(bits: Self::Bits) -> Option<Self> {
670 // extract the sign bit according to the bit length of this type.
671 let sign_bit = (bits >> ($bit_len - 1)) & 1;
672
673 // sign extend if needed
674 let sign_extended = if sign_bit != 0 {
675 // set all bits above the bit length to 1, which will sign extend it
676 bits | (!Self::MASK)
677 } else {
678 bits
679 };
680 Self::try_new(sign_extended as $storage_signed)
681 }
682
683 fn to_bits(self) -> Self::Bits {
684 (self.0 as $storage) & Self::MASK
685 }
686 }
687 impl $ident {
688 /// a mask of the bit length of this type.
689 const MASK: $storage = (
690 if $bit_len == <$storage>::BITS {
691 // if the bit length is equal to the amount of bits in our storage type, avoid the overflow
692 // which will happen when shifting, and just returns the maximum value of the underlying
693 // storage type.
694 <$storage>::MAX
695 } else {
696 ((1 as $storage) << $bit_len).wrapping_sub(1)
697 }
698 );
699
700 /// the max allowed value for this type.
701 pub const MAX: Self = Self(((1 as $storage) << ($bit_len - 1)).wrapping_sub(1) as $storage_signed);
702
703 /// the minimum allowed value for this type.
704 pub const MIN: Self = Self(((1 as $storage) << ($bit_len - 1)).wrapping_neg() as $storage_signed);
705
706 /// the bit length of this type.
707 pub const BIT_LENGTH: usize = $bit_len;
708
709 /// creates a new instance of this bitfield type with the given value.
710 ///
711 /// this function panics if the value does not fit within the bit length of this type.
712 pub fn new(value: $storage_signed) -> Self {
713 Self::try_new(value).unwrap()
714 }
715
716 /// creates a new instance of this bitfield type with the given value.
717 ///
718 /// if the value does not fit within the bit length of this type, returns `None`.
719 pub fn try_new(value: $storage_signed) -> Option<Self> {
720 if value <= Self::MAX.0 && value >= Self::MIN.0 {
721 Some(Self(value))
722 } else {
723 None
724 }
725 }
726
727 /// creates a new instance of this bitfield type with the given value, without checking that the value
728 /// fits within the bit length of this type.
729 ///
730 /// # safety
731 /// the provided value must fit within the bit length of this type.
732 pub unsafe fn new_unchecked(value: $storage_signed) -> Self {
733 Self(value)
734 }
735
736 /// returns the inner value.
737 pub fn get(&self) -> $storage_signed {
738 self.0
739 }
740 }
741 impl core::fmt::Display for $ident {
742 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
743 core::fmt::Display::fmt(&self.0, f)
744 }
745 }
746 impl core::fmt::Debug for $ident {
747 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
748 core::fmt::Debug::fmt(&self.0, f)
749 }
750 }
751 };
752}
753macro_rules! define_sb_types {
754 { $($bit_len: literal),+ $(,)? } => {
755 $(
756 paste!{
757 define_sb_type! {
758 $bit_len,
759 [<SB $bit_len>],
760 <BitLength<$bit_len> as AssociatedStorage>::Storage,
761 <<BitLength<$bit_len> as AssociatedStorage>::Storage as BitStorage>::Signed
762 }
763 }
764 )+
765 };
766}
767define_sb_types! {
768 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
769 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64
770}