@@ -452,6 +452,18 @@ pub fn bitfield(args: TokenStream, input: TokenStream) -> TokenStream {
452452 TokenStream :: from ( expanded)
453453}
454454
455+ const fn mask_for_width_and_offset ( width : usize , offset : usize ) -> u128 {
456+ if width == 128 {
457+ return u128:: MAX ;
458+ }
459+ if width + offset > 128 {
460+ panic ! ( "bitfield!: Internal error: width + offset > 128" ) ;
461+ }
462+ // The wrapping subtraction is important for the case where width is 128.
463+ // A shift with 128 would yield a value of 0, and a regular subtraction of 1 could panic.
464+ ( ( 1_u128 << width) - 1 ) << offset
465+ }
466+
455467fn check_for_overlaps (
456468 field_definitions : & [ FieldDefinition ] ,
457469 input : & DeriveInput ,
@@ -461,7 +473,7 @@ fn check_for_overlaps(
461473 for field in field_definitions {
462474 let mut check_and_update_bitmask = |offset : usize , width : usize | {
463475 // Create an all-ones bitmask for the given range, e.g. 0b1110 for range (1..=3).
464- let mask = ( ( 1_u128 << width) - 1 ) << offset;
476+ let mask = mask_for_width_and_offset ( width, offset) ;
465477 if ( current_bitmask & mask) != 0 {
466478 return Err ( syn:: Error :: new_spanned (
467479 input,
@@ -556,3 +568,41 @@ fn const_name(field_name: &Ident, suffix: &str) -> Ident {
556568 syn:: parse_str :: < Ident > ( & name)
557569 . unwrap_or_else ( |_| panic ! ( "bitfield!: Error creating {name} name" ) )
558570}
571+
572+ #[ cfg( test) ]
573+ mod tests {
574+ use super :: * ;
575+
576+ #[ test]
577+ fn test_mask_underflow ( ) {
578+ assert_eq ! ( mask_for_width_and_offset( 128 , 0 ) , u128 :: MAX ) ;
579+ }
580+
581+ #[ test]
582+ #[ should_panic]
583+ fn test_mask_error ( ) {
584+ assert_eq ! ( mask_for_width_and_offset( 127 , 1 ) , u128 :: MAX - 1 ) ;
585+ // This panics.
586+ assert_eq ! ( mask_for_width_and_offset( 127 , 2 ) , u128 :: MAX ) ;
587+ }
588+
589+ #[ test]
590+ fn test_mask_0 ( ) {
591+ assert_eq ! ( mask_for_width_and_offset( 3 , 1 ) , 0b1110 ) ;
592+ }
593+
594+ #[ test]
595+ fn test_mask_1 ( ) {
596+ assert_eq ! ( mask_for_width_and_offset( 0 , 0 ) , 0 ) ;
597+ }
598+
599+ #[ test]
600+ fn test_mask_2 ( ) {
601+ assert_eq ! ( mask_for_width_and_offset( 1 , 0 ) , 1 ) ;
602+ }
603+
604+ #[ test]
605+ fn test_mask_3 ( ) {
606+ assert_eq ! ( mask_for_width_and_offset( 2 , 0 ) , 0b11 ) ;
607+ }
608+ }
0 commit comments