diff --git a/bindgen-tests/tests/expectations/tests/align_bitfields_32bit.rs b/bindgen-tests/tests/expectations/tests/align_bitfields_32bit.rs index 4b0c28a8b2..58ad41fab4 100644 --- a/bindgen-tests/tests/expectations/tests/align_bitfields_32bit.rs +++ b/bindgen-tests/tests/expectations/tests/align_bitfields_32bit.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/bitfield-32bit-overflow.rs b/bindgen-tests/tests/expectations/tests/bitfield-32bit-overflow.rs index 93ef1bfb58..0ebcbe9e02 100644 --- a/bindgen-tests/tests/expectations/tests/bitfield-32bit-overflow.rs +++ b/bindgen-tests/tests/expectations/tests/bitfield-32bit-overflow.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/bitfield-large.rs b/bindgen-tests/tests/expectations/tests/bitfield-large.rs index 38bf531559..e6ff1ff7b5 100644 --- a/bindgen-tests/tests/expectations/tests/bitfield-large.rs +++ b/bindgen-tests/tests/expectations/tests/bitfield-large.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/bitfield-linux-32.rs b/bindgen-tests/tests/expectations/tests/bitfield-linux-32.rs index 190ba7ea99..4e9d8ef071 100644 --- a/bindgen-tests/tests/expectations/tests/bitfield-linux-32.rs +++ b/bindgen-tests/tests/expectations/tests/bitfield-linux-32.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/bitfield-method-same-name.rs b/bindgen-tests/tests/expectations/tests/bitfield-method-same-name.rs index 8ed479dd7b..4dbe2b93f0 100644 --- a/bindgen-tests/tests/expectations/tests/bitfield-method-same-name.rs +++ b/bindgen-tests/tests/expectations/tests/bitfield-method-same-name.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/bitfield-template.rs b/bindgen-tests/tests/expectations/tests/bitfield-template.rs index b775997dac..8900807591 100644 --- a/bindgen-tests/tests/expectations/tests/bitfield-template.rs +++ b/bindgen-tests/tests/expectations/tests/bitfield-template.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/bitfield_align.rs b/bindgen-tests/tests/expectations/tests/bitfield_align.rs index a49dfe6f02..db3a33ccea 100644 --- a/bindgen-tests/tests/expectations/tests/bitfield_align.rs +++ b/bindgen-tests/tests/expectations/tests/bitfield_align.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/bitfield_align_2.rs b/bindgen-tests/tests/expectations/tests/bitfield_align_2.rs index a20afeaa3b..a483384558 100644 --- a/bindgen-tests/tests/expectations/tests/bitfield_align_2.rs +++ b/bindgen-tests/tests/expectations/tests/bitfield_align_2.rs @@ -71,65 +71,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -139,372 +174,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(u32)] diff --git a/bindgen-tests/tests/expectations/tests/bitfield_method_mangling.rs b/bindgen-tests/tests/expectations/tests/bitfield_method_mangling.rs index 6de16c61ba..08da268fec 100644 --- a/bindgen-tests/tests/expectations/tests/bitfield_method_mangling.rs +++ b/bindgen-tests/tests/expectations/tests/bitfield_method_mangling.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/bitfield_pack_offset.rs b/bindgen-tests/tests/expectations/tests/bitfield_pack_offset.rs index 868fb9ad3c..ed10258428 100644 --- a/bindgen-tests/tests/expectations/tests/bitfield_pack_offset.rs +++ b/bindgen-tests/tests/expectations/tests/bitfield_pack_offset.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/bitfield_pragma_packed.rs b/bindgen-tests/tests/expectations/tests/bitfield_pragma_packed.rs index 065eefdf66..58dccbc181 100644 --- a/bindgen-tests/tests/expectations/tests/bitfield_pragma_packed.rs +++ b/bindgen-tests/tests/expectations/tests/bitfield_pragma_packed.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/default_visibility_crate.rs b/bindgen-tests/tests/expectations/tests/default_visibility_crate.rs index 1fd8c961d6..d208b5fbf3 100644 --- a/bindgen-tests/tests/expectations/tests/default_visibility_crate.rs +++ b/bindgen-tests/tests/expectations/tests/default_visibility_crate.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/default_visibility_private.rs b/bindgen-tests/tests/expectations/tests/default_visibility_private.rs index 3485310eea..8655b9166d 100644 --- a/bindgen-tests/tests/expectations/tests/default_visibility_private.rs +++ b/bindgen-tests/tests/expectations/tests/default_visibility_private.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/default_visibility_private_respects_cxx_access_spec.rs b/bindgen-tests/tests/expectations/tests/default_visibility_private_respects_cxx_access_spec.rs index 6a0b90224a..b91d6887f2 100644 --- a/bindgen-tests/tests/expectations/tests/default_visibility_private_respects_cxx_access_spec.rs +++ b/bindgen-tests/tests/expectations/tests/default_visibility_private_respects_cxx_access_spec.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/derive-bitfield-method-same-name.rs b/bindgen-tests/tests/expectations/tests/derive-bitfield-method-same-name.rs index a1bfbdf31b..7803979383 100644 --- a/bindgen-tests/tests/expectations/tests/derive-bitfield-method-same-name.rs +++ b/bindgen-tests/tests/expectations/tests/derive-bitfield-method-same-name.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } /** Because this struct have array larger than 32 items diff --git a/bindgen-tests/tests/expectations/tests/derive-debug-bitfield-1-51.rs b/bindgen-tests/tests/expectations/tests/derive-debug-bitfield-1-51.rs index a917464c22..c255b057c4 100644 --- a/bindgen-tests/tests/expectations/tests/derive-debug-bitfield-1-51.rs +++ b/bindgen-tests/tests/expectations/tests/derive-debug-bitfield-1-51.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/derive-debug-bitfield-core.rs b/bindgen-tests/tests/expectations/tests/derive-debug-bitfield-core.rs index 15bb8eeade..34047d59ad 100644 --- a/bindgen-tests/tests/expectations/tests/derive-debug-bitfield-core.rs +++ b/bindgen-tests/tests/expectations/tests/derive-debug-bitfield-core.rs @@ -71,65 +71,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -139,372 +174,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/derive-debug-bitfield.rs b/bindgen-tests/tests/expectations/tests/derive-debug-bitfield.rs index a917464c22..c255b057c4 100644 --- a/bindgen-tests/tests/expectations/tests/derive-debug-bitfield.rs +++ b/bindgen-tests/tests/expectations/tests/derive-debug-bitfield.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/derive-partialeq-bitfield.rs b/bindgen-tests/tests/expectations/tests/derive-partialeq-bitfield.rs index 9fe8a9c995..28235728f2 100644 --- a/bindgen-tests/tests/expectations/tests/derive-partialeq-bitfield.rs +++ b/bindgen-tests/tests/expectations/tests/derive-partialeq-bitfield.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/divide-by-zero-in-struct-layout.rs b/bindgen-tests/tests/expectations/tests/divide-by-zero-in-struct-layout.rs index 88463439f0..355aa40c16 100644 --- a/bindgen-tests/tests/expectations/tests/divide-by-zero-in-struct-layout.rs +++ b/bindgen-tests/tests/expectations/tests/divide-by-zero-in-struct-layout.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/field-rename-callback.rs b/bindgen-tests/tests/expectations/tests/field-rename-callback.rs index fcd015e7f9..08905b9ed3 100644 --- a/bindgen-tests/tests/expectations/tests/field-rename-callback.rs +++ b/bindgen-tests/tests/expectations/tests/field-rename-callback.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/field-visibility-callback.rs b/bindgen-tests/tests/expectations/tests/field-visibility-callback.rs index 73ae2e1858..a1f8e83884 100644 --- a/bindgen-tests/tests/expectations/tests/field-visibility-callback.rs +++ b/bindgen-tests/tests/expectations/tests/field-visibility-callback.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/field-visibility.rs b/bindgen-tests/tests/expectations/tests/field-visibility.rs index 9fc88af92f..bd29911116 100644 --- a/bindgen-tests/tests/expectations/tests/field-visibility.rs +++ b/bindgen-tests/tests/expectations/tests/field-visibility.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/incomplete-array-padding.rs b/bindgen-tests/tests/expectations/tests/incomplete-array-padding.rs index 61171c627a..19f524b033 100644 --- a/bindgen-tests/tests/expectations/tests/incomplete-array-padding.rs +++ b/bindgen-tests/tests/expectations/tests/incomplete-array-padding.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/issue-1034.rs b/bindgen-tests/tests/expectations/tests/issue-1034.rs index 4ae9980886..429ccfaff0 100644 --- a/bindgen-tests/tests/expectations/tests/issue-1034.rs +++ b/bindgen-tests/tests/expectations/tests/issue-1034.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/issue-1076-unnamed-bitfield-alignment.rs b/bindgen-tests/tests/expectations/tests/issue-1076-unnamed-bitfield-alignment.rs index 7690e99d55..0b2effe28e 100644 --- a/bindgen-tests/tests/expectations/tests/issue-1076-unnamed-bitfield-alignment.rs +++ b/bindgen-tests/tests/expectations/tests/issue-1076-unnamed-bitfield-alignment.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/issue-1947.rs b/bindgen-tests/tests/expectations/tests/issue-1947.rs index dee0672c28..ae21ba5bce 100644 --- a/bindgen-tests/tests/expectations/tests/issue-1947.rs +++ b/bindgen-tests/tests/expectations/tests/issue-1947.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } pub type U8 = ::std::os::raw::c_uchar; diff --git a/bindgen-tests/tests/expectations/tests/issue-3454-enum-newtype-bitfield.rs b/bindgen-tests/tests/expectations/tests/issue-3454-enum-newtype-bitfield.rs index e5792505ae..4ce8f56354 100644 --- a/bindgen-tests/tests/expectations/tests/issue-3454-enum-newtype-bitfield.rs +++ b/bindgen-tests/tests/expectations/tests/issue-3454-enum-newtype-bitfield.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } impl pipe_resource_usage { diff --git a/bindgen-tests/tests/expectations/tests/issue-739-pointer-wide-bitfield.rs b/bindgen-tests/tests/expectations/tests/issue-739-pointer-wide-bitfield.rs index 02a6fd4afe..9ff57599d6 100644 --- a/bindgen-tests/tests/expectations/tests/issue-739-pointer-wide-bitfield.rs +++ b/bindgen-tests/tests/expectations/tests/issue-739-pointer-wide-bitfield.rs @@ -71,65 +71,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -139,372 +174,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/issue-743.rs b/bindgen-tests/tests/expectations/tests/issue-743.rs index 4d69fb9d8a..94c62f0c0d 100644 --- a/bindgen-tests/tests/expectations/tests/issue-743.rs +++ b/bindgen-tests/tests/expectations/tests/issue-743.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/issue-816.rs b/bindgen-tests/tests/expectations/tests/issue-816.rs index 6f9a5000b6..44339dc229 100644 --- a/bindgen-tests/tests/expectations/tests/issue-816.rs +++ b/bindgen-tests/tests/expectations/tests/issue-816.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/jsval_layout_opaque.rs b/bindgen-tests/tests/expectations/tests/jsval_layout_opaque.rs index 2c707f7861..eb7c95d266 100644 --- a/bindgen-tests/tests/expectations/tests/jsval_layout_opaque.rs +++ b/bindgen-tests/tests/expectations/tests/jsval_layout_opaque.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } pub const JSVAL_TAG_SHIFT: u32 = 47; diff --git a/bindgen-tests/tests/expectations/tests/layout_align.rs b/bindgen-tests/tests/expectations/tests/layout_align.rs index 4cedd7f840..a5378c04d9 100644 --- a/bindgen-tests/tests/expectations/tests/layout_align.rs +++ b/bindgen-tests/tests/expectations/tests/layout_align.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/layout_eth_conf.rs b/bindgen-tests/tests/expectations/tests/layout_eth_conf.rs index e01a74a0ba..9917a1a1e2 100644 --- a/bindgen-tests/tests/expectations/tests/layout_eth_conf.rs +++ b/bindgen-tests/tests/expectations/tests/layout_eth_conf.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } pub const ETH_MQ_RX_RSS_FLAG: u32 = 1; diff --git a/bindgen-tests/tests/expectations/tests/layout_mbuf.rs b/bindgen-tests/tests/expectations/tests/layout_mbuf.rs index e9f867750c..8772a77484 100644 --- a/bindgen-tests/tests/expectations/tests/layout_mbuf.rs +++ b/bindgen-tests/tests/expectations/tests/layout_mbuf.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } pub const RTE_CACHE_LINE_MIN_SIZE: u32 = 64; diff --git a/bindgen-tests/tests/expectations/tests/only_bitfields.rs b/bindgen-tests/tests/expectations/tests/only_bitfields.rs index a4e6e5872d..7a0f41eb46 100644 --- a/bindgen-tests/tests/expectations/tests/only_bitfields.rs +++ b/bindgen-tests/tests/expectations/tests/only_bitfields.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/packed-bitfield.rs b/bindgen-tests/tests/expectations/tests/packed-bitfield.rs index e88c8e7037..0e013ddeb6 100644 --- a/bindgen-tests/tests/expectations/tests/packed-bitfield.rs +++ b/bindgen-tests/tests/expectations/tests/packed-bitfield.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C, packed)] diff --git a/bindgen-tests/tests/expectations/tests/private_fields.rs b/bindgen-tests/tests/expectations/tests/private_fields.rs index c53c005e79..0f967da558 100644 --- a/bindgen-tests/tests/expectations/tests/private_fields.rs +++ b/bindgen-tests/tests/expectations/tests/private_fields.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/redundant-packed-and-align.rs b/bindgen-tests/tests/expectations/tests/redundant-packed-and-align.rs index 6c79d35bb4..6c4f93eb6e 100644 --- a/bindgen-tests/tests/expectations/tests/redundant-packed-and-align.rs +++ b/bindgen-tests/tests/expectations/tests/redundant-packed-and-align.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/struct_with_bitfields.rs b/bindgen-tests/tests/expectations/tests/struct_with_bitfields.rs index 762f2e64e8..5b4ab29be5 100644 --- a/bindgen-tests/tests/expectations/tests/struct_with_bitfields.rs +++ b/bindgen-tests/tests/expectations/tests/struct_with_bitfields.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/timex.rs b/bindgen-tests/tests/expectations/tests/timex.rs index bd9ef6e2d3..1982d80e7b 100644 --- a/bindgen-tests/tests/expectations/tests/timex.rs +++ b/bindgen-tests/tests/expectations/tests/timex.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/union_bitfield.rs b/bindgen-tests/tests/expectations/tests/union_bitfield.rs index 8f9292397e..eded13d0a4 100644 --- a/bindgen-tests/tests/expectations/tests/union_bitfield.rs +++ b/bindgen-tests/tests/expectations/tests/union_bitfield.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/union_with_anon_struct_bitfield.rs b/bindgen-tests/tests/expectations/tests/union_with_anon_struct_bitfield.rs index df7700e7e2..1d2b41d702 100644 --- a/bindgen-tests/tests/expectations/tests/union_with_anon_struct_bitfield.rs +++ b/bindgen-tests/tests/expectations/tests/union_with_anon_struct_bitfield.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(C)] diff --git a/bindgen-tests/tests/expectations/tests/weird_bitfields.rs b/bindgen-tests/tests/expectations/tests/weird_bitfields.rs index d3c976d695..30111dec53 100644 --- a/bindgen-tests/tests/expectations/tests/weird_bitfields.rs +++ b/bindgen-tests/tests/expectations/tests/weird_bitfields.rs @@ -70,65 +70,100 @@ where } #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), - ); - if bit_width == 0 { - return 0; - } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { Self::get_bits(storage.as_ptr(), storage.len(), bit_offset, bit_width) } + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - val } +} +impl __BindgenBitfieldUnit { #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + const fn field_layout( + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } val >>= bit_shift; + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); + } if bit_width < 64 { val &= (1u64 << bit_width) - 1; } @@ -138,372 +173,97 @@ where val } #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= self.storage.as_ref().len(), + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + mut val: u64, + ) { + let (start_byte, bit_shift, bytes_needed) = Self::field_layout( + storage_len, + bit_offset, + bit_width, ); if bit_width == 0 { return; } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + if bit_width == 64 && bit_shift == 0 { + unsafe { storage.add(start_byte).cast::().write_unaligned(val) }; + return; } + let field_mask = if bit_width == 64 { !0u64 } else { (1u64 << bit_width) - 1 }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) - | (byte_val & byte_mask); + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); } } } #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 - <= core::mem::size_of::(), - ); - if bit_width == 0 { - return; - } - let mut val = val; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - val <<= bit_shift; - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const(&self) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as usize) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val - |= (self.storage[start_byte + i].reverse_bits() as u64) - << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const(&mut self, val: u64) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = (self.storage[start_byte + i] - & !byte_mask) | (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return 0; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val as u64 - } else { - let mut val = 0u64; - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - val >>= bit_shift; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const( this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - if BIT_WIDTH == 0 { - return; - } - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - let storage_ptr = this.cast::<[u8; N]>().cast::(); - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - val <<= bit_shift; - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } #[repr(u32)] diff --git a/bindgen/codegen/bitfield_unit.rs b/bindgen/codegen/bitfield_unit.rs index f6e7d57063..75803dd6a5 100644 --- a/bindgen/codegen/bitfield_unit.rs +++ b/bindgen/codegen/bitfield_unit.rs @@ -92,399 +92,230 @@ where #[inline] pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= - self.storage.as_ref().len() - ); - - if bit_width == 0 { - return 0; + let Self { storage } = self; + let storage = storage.as_ref(); + unsafe { + Self::get_bits( + storage.as_ptr(), + storage.len(), + bit_offset, + bit_width, + ) } + } - let mut val = 0u64; - let storage = self.storage.as_ref(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - val |= - (storage[start_byte + i].reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - val |= (storage[start_byte + i] as u64) << (i * 8); - } + #[inline] + pub unsafe fn raw_get( + this: *const Self, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + ) } + } - val >>= bit_shift; - - // Mask to bit_width - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + let Self { storage } = self; + let storage = storage.as_mut(); + unsafe { + Self::set_bits( + storage.as_mut_ptr(), + storage.len(), + bit_offset, + bit_width, + val, + ); } + } - if cfg!(target_endian = "big") { - // Reverse bits within the field width - val = val.reverse_bits() >> (64 - bit_width as usize); + #[inline] + pub unsafe fn raw_set( + this: *mut Self, + bit_offset: usize, + bit_width: u8, + val: u64, + ) { + unsafe { + Self::set_bits( + core::ptr::addr_of_mut!((*this).storage).cast(), + core::mem::size_of::(), + bit_offset, + bit_width, + val, + ); } - - val } +} +impl __BindgenBitfieldUnit { + // Bounds checks must remain enabled in release builds: the safe accessors + // pass slice pointers to the shared raw implementation. #[inline] - pub unsafe fn raw_get( - this: *const Self, + const fn field_layout( + storage_len: usize, bit_offset: usize, bit_width: u8, - ) -> u64 { + ) -> (usize, usize, usize) { debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= - core::mem::size_of::() - ); + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + debug_assert!(start_byte < storage_len); + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + assert!(start_byte + bytes_needed <= storage_len); + (start_byte, bit_shift, bytes_needed) + } + // The caller must provide initialized storage for the accessed bytes. + // Work with byte pointers so raw access never creates a reference to Self + // or to storage outside the field. + #[inline] + const unsafe fn get_bits( + storage: *const u8, + storage_len: usize, + bit_offset: usize, + bit_width: u8, + ) -> u64 { + let (start_byte, bit_shift, bytes_needed) = + Self::field_layout(storage_len, bit_offset, bit_width); if bit_width == 0 { return 0; } let mut val = 0u64; - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - - let storage_ptr = - unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - - if cfg!(target_endian = "big") { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - } - } else { - for i in 0..bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - } + let mut i = 0; + while i < bytes_needed && i < 8 { + let byte = unsafe { *storage.add(start_byte + i) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (i * 8); + i += 1; } - val >>= bit_shift; - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; - } - - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - bit_width as usize); - } - - val - } - - #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= - self.storage.as_ref().len() - ); - - if bit_width == 0 { - return; + // An unaligned field can span nine bytes. Its final byte must be + // shifted into the result after making room for it in the first word. + if bytes_needed == 9 { + let byte = unsafe { *storage.add(start_byte + 8) }; + let byte = if cfg!(target_endian = "big") { + byte.reverse_bits() + } else { + byte + }; + val |= (byte as u64) << (64 - bit_shift); } - let mut val = val; - - // Mask to bit_width if bit_width < 64 { val &= (1u64 << bit_width) - 1; } - if cfg!(target_endian = "big") { - // Reverse bits to match storage layout val = val.reverse_bits() >> (64 - bit_width as usize); } - - let storage = self.storage.as_mut(); - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - - // Shift val to align with byte boundary - val <<= bit_shift; - - // Create mask for the bits we're writing - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << bit_width) - 1) << bit_shift - }; - - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - - if cfg!(target_endian = "big") { - let byte = storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - storage[start_byte + i] = new_byte.reverse_bits(); - } else { - storage[start_byte + i] = (storage[start_byte + i] & - !byte_mask) | - (byte_val & byte_mask); - } - } + val } + // The caller must provide writable, initialized storage for the accessed + // bytes. Bits outside the field are preserved. #[inline] - pub unsafe fn raw_set( - this: *mut Self, + unsafe fn set_bits( + storage: *mut u8, + storage_len: usize, bit_offset: usize, bit_width: u8, - val: u64, + mut val: u64, ) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!( - (bit_offset + (bit_width as usize) + 7) / 8 <= - core::mem::size_of::() - ); - + let (start_byte, bit_shift, bytes_needed) = + Self::field_layout(storage_len, bit_offset, bit_width); if bit_width == 0 { return; } - let mut val = val; - - if bit_width < 64 { - val &= (1u64 << bit_width) - 1; + // A full, byte-aligned field needs no read-modify-write. Store it + // directly so byte-loop vectorization cannot obscure the word store. + if bit_width == 64 && bit_shift == 0 { + unsafe { + storage.add(start_byte).cast::().write_unaligned(val) + }; + return; } + let field_mask = if bit_width == 64 { + !0u64 + } else { + (1u64 << bit_width) - 1 + }; + val &= field_mask; if cfg!(target_endian = "big") { val = val.reverse_bits() >> (64 - bit_width as usize); } - let start_byte = bit_offset / 8; - let bit_shift = bit_offset % 8; - let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; - - val <<= bit_shift; + // Save the bits belonging to the ninth byte before shifting the + // value and mask into the first word. + if bytes_needed == 9 { + let shift = 64 - bit_shift; + unsafe { + Self::set_byte( + storage.add(start_byte + 8), + (val >> shift) as u8, + (field_mask >> shift) as u8, + ); + } + } + let val = val << bit_shift; + let field_mask = field_mask << bit_shift; + for i in 0..bytes_needed.min(8) { + unsafe { + Self::set_byte( + storage.add(start_byte + i), + (val >> (i * 8)) as u8, + (field_mask >> (i * 8)) as u8, + ); + } + } + } - let field_mask = if bit_width as usize + bit_shift >= 64 { - !0u64 << bit_shift + #[inline] + unsafe fn set_byte(byte: *mut u8, val: u8, mask: u8) { + let (val, mask) = if cfg!(target_endian = "big") { + (val.reverse_bits(), mask.reverse_bits()) } else { - ((1u64 << bit_width) - 1) << bit_shift + (val, mask) }; - - let storage_ptr = - unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; - - for i in 0..bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = - (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - } + unsafe { *byte = (*byte & !mask) | (val & mask) }; } } -/// Const-generic methods for efficient bitfield access when offset and width -/// are known at compile time. +/// Const-generic methods for bitfield access when offset and width are known +/// at compile time. Inlining exposes these constants to the shared implementation. impl __BindgenBitfieldUnit<[u8; N]> { /// Get a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const fn get_const( &self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - - if BIT_WIDTH == 0 { - return 0; - } - - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - - // Use usize for fields that fit, u64 only when necessary. - // The compiler eliminates the unused branch since BIT_WIDTH is const. - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i].reverse_bits() - as usize) << - (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as usize) << (i * 8); - i += 1; - } - } - - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> - (usize::BITS as usize - BIT_WIDTH as usize); - } - - val as u64 - } else { - let mut val = 0u64; - - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i].reverse_bits() as u64) << - (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - val |= (self.storage[start_byte + i] as u64) << (i * 8); - i += 1; - } - } - - val >>= bit_shift; - - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - - val - } + let Self { storage } = self; + unsafe { Self::get_bits(storage.as_ptr(), N, BIT_OFFSET, BIT_WIDTH) } } /// Set a field using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub fn set_const( &mut self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - - if BIT_WIDTH == 0 { - return; - } - - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - - // Use usize for fields that fit, u64 only when necessary. - // The compiler eliminates the unused branch since BIT_WIDTH is const. - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> - (usize::BITS as usize - BIT_WIDTH as usize); - } - - val <<= bit_shift; - - let field_mask = - if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = - (self.storage[start_byte + i] & !byte_mask) | - (byte_val & byte_mask); - } - i += 1; - } - } else { - let mut val = val; - - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - - val <<= bit_shift; - - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - - if cfg!(target_endian = "big") { - let byte = self.storage[start_byte + i].reverse_bits(); - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - self.storage[start_byte + i] = new_byte.reverse_bits(); - } else { - self.storage[start_byte + i] = - (self.storage[start_byte + i] & !byte_mask) | - (byte_val & byte_mask); - } - i += 1; - } - } + self.set(BIT_OFFSET, BIT_WIDTH, val); } /// Raw pointer get using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub const unsafe fn raw_get_const< const BIT_OFFSET: usize, @@ -492,87 +323,17 @@ impl __BindgenBitfieldUnit<[u8; N]> { >( this: *const Self, ) -> u64 { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - - if BIT_WIDTH == 0 { - return 0; - } - - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - - let storage_ptr = - unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; - - // Use usize for fields that fit, u64 only when necessary. - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = 0usize; - - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as usize) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as usize) << (i * 8); - i += 1; - } - } - - val >>= bit_shift; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> - (usize::BITS as usize - BIT_WIDTH as usize); - } - - val as u64 - } else { - let mut val = 0u64; - - if cfg!(target_endian = "big") { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte.reverse_bits() as u64) << (i * 8); - i += 1; - } - } else { - let mut i = 0; - while i < bytes_needed { - let byte = unsafe { *storage_ptr.add(start_byte + i) }; - val |= (byte as u64) << (i * 8); - i += 1; - } - } - - val >>= bit_shift; - - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - - val + unsafe { + Self::get_bits( + core::ptr::addr_of!((*this).storage).cast(), + N, + BIT_OFFSET, + BIT_WIDTH, + ) } } /// Raw pointer set using const generics for compile-time optimization. - /// Uses native word size operations when the field fits in usize. #[inline] pub unsafe fn raw_set_const< const BIT_OFFSET: usize, @@ -581,97 +342,6 @@ impl __BindgenBitfieldUnit<[u8; N]> { this: *mut Self, val: u64, ) { - debug_assert!(BIT_WIDTH <= 64); - debug_assert!(BIT_OFFSET / 8 < N); - debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); - - if BIT_WIDTH == 0 { - return; - } - - let start_byte = BIT_OFFSET / 8; - let bit_shift = BIT_OFFSET % 8; - let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; - - // Cast through pointer types instead of using addr_of_mut! for const compatibility - let storage_ptr = this.cast::<[u8; N]>().cast::(); - - // Use usize for fields that fit, u64 only when necessary. - if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { - let mut val = val as usize; - if (BIT_WIDTH as u32) < usize::BITS { - val &= (1usize << BIT_WIDTH) - 1; - } - - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> - (usize::BITS as usize - BIT_WIDTH as usize); - } - - val <<= bit_shift; - - let field_mask = - if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { - !0usize << bit_shift - } else { - ((1usize << BIT_WIDTH) - 1) << bit_shift - }; - - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = - (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } else { - let mut val = val; - - if BIT_WIDTH < 64 { - val &= (1u64 << BIT_WIDTH) - 1; - } - - if cfg!(target_endian = "big") { - val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); - } - - val <<= bit_shift; - - let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { - !0u64 << bit_shift - } else { - ((1u64 << BIT_WIDTH) - 1) << bit_shift - }; - - let mut i = 0; - while i < bytes_needed { - let byte_val = (val >> (i * 8)) as u8; - let byte_mask = (field_mask >> (i * 8)) as u8; - let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; - - if cfg!(target_endian = "big") { - let byte = unsafe { (*byte_ptr).reverse_bits() }; - let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); - unsafe { *byte_ptr = new_byte.reverse_bits() }; - } else { - unsafe { - *byte_ptr = - (*byte_ptr & !byte_mask) | (byte_val & byte_mask) - }; - } - i += 1; - } - } + unsafe { Self::raw_set(this, BIT_OFFSET, BIT_WIDTH, val) }; } } diff --git a/bindgen/codegen/bitfield_unit_tests.rs b/bindgen/codegen/bitfield_unit_tests.rs index baa5c938a5..4cf0ea2760 100644 --- a/bindgen/codegen/bitfield_unit_tests.rs +++ b/bindgen/codegen/bitfield_unit_tests.rs @@ -23,6 +23,162 @@ use super::bitfield_unit::__BindgenBitfieldUnit; +// Compare every accessor with the bit-at-a-time operations, including bits +// outside the field so a setter cannot silently overwrite its neighbors. +fn check_field() { + for initial in [0, 0x96, 0xFF] { + let unit = __BindgenBitfieldUnit::new([initial; 10]); + let mut expected = 0u64; + for i in 0..WIDTH as usize { + let value_bit = if cfg!(target_endian = "big") { + WIDTH as usize - 1 - i + } else { + i + }; + expected |= u64::from(unit.get_bit(OFFSET + i)) << value_bit; + } + assert_eq!(unit.get(OFFSET, WIDTH), expected); + assert_eq!(unit.get_const::(), expected); + unsafe { + assert_eq!( + __BindgenBitfieldUnit::raw_get(&unit, OFFSET, WIDTH), + expected + ); + assert_eq!( + __BindgenBitfieldUnit::raw_get_const::(&unit), + expected + ); + } + + for value in [0, 1, 1 << 63, 0x0123_4567_89AB_CDEF, u64::MAX] { + let mut expected = unit; + for i in 0..WIDTH as usize { + let value_bit = if cfg!(target_endian = "big") { + WIDTH as usize - 1 - i + } else { + i + }; + expected.set_bit(OFFSET + i, value & (1 << value_bit) != 0); + } + let mut actual = unit; + actual.set(OFFSET, WIDTH, value); + assert_eq!(actual, expected); + let mut actual = unit; + actual.set_const::(value); + assert_eq!(actual, expected); + let mut actual = unit; + unsafe { + __BindgenBitfieldUnit::raw_set( + &mut actual, + OFFSET, + WIDTH, + value, + ); + }; + assert_eq!(actual, expected); + let mut actual = unit; + unsafe { + __BindgenBitfieldUnit::raw_set_const::( + &mut actual, + value, + ); + }; + assert_eq!(actual, expected); + } + } +} + +#[test] +fn bitfield_unit_fields() { + const UNIT: __BindgenBitfieldUnit<[u8; 9]> = + __BindgenBitfieldUnit::new([0xFF; 9]); + const VALUE: u64 = UNIT.get_const::<1, 64>(); + const RAW_VALUE: u64 = + unsafe { __BindgenBitfieldUnit::raw_get_const::<1, 64>(&UNIT) }; + assert_eq!(VALUE, u64::MAX); + assert_eq!(RAW_VALUE, u64::MAX); + + macro_rules! check_offsets { + ($($offset:literal),*) => {$( + check_field::<$offset, 0>(); + check_field::<$offset, 1>(); + check_field::<$offset, 2>(); + check_field::<$offset, 5>(); + check_field::<$offset, 8>(); + check_field::<$offset, 12>(); + check_field::<$offset, 16>(); + check_field::<$offset, 31>(); + check_field::<$offset, 32>(); + check_field::<$offset, 57>(); + check_field::<$offset, 58>(); + check_field::<$offset, 59>(); + check_field::<$offset, 60>(); + check_field::<$offset, 61>(); + check_field::<$offset, 62>(); + check_field::<$offset, 63>(); + check_field::<$offset, 64>(); + )*}; + } + // Include a nonzero starting byte as well as every intra-byte offset. + check_offsets!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); +} + +#[test] +fn bitfield_unit_indirect_storage() { + let mut unit = __BindgenBitfieldUnit::new(vec![0xFF; 9]); + assert_eq!(unit.get(1, 64), u64::MAX); + let mut expected = unit.clone(); + for i in 1..65 { + expected.set_bit(i, false); + } + unit.set(1, 64, 0); + assert_eq!(unit, expected); +} + +#[test] +fn bitfield_unit_raw_access_with_uninitialized_neighbors() { + let mut unit = + std::mem::MaybeUninit::<__BindgenBitfieldUnit<[u8; 16]>>::uninit(); + let unit = unit.as_mut_ptr(); + unsafe { + // Only bytes 1 through 9 are initialized. Raw access must not create + // a reference to the whole storage or touch the other bytes. + for i in 1..10 { + unit.cast::().add(i).write(0xFF); + } + assert_eq!(__BindgenBitfieldUnit::raw_get(unit, 9, 64), u64::MAX); + assert_eq!( + __BindgenBitfieldUnit::raw_get_const::<9, 64>(unit), + u64::MAX, + ); + __BindgenBitfieldUnit::raw_set(unit, 9, 64, 0); + assert_eq!(__BindgenBitfieldUnit::raw_get_const::<9, 64>(unit), 0); + __BindgenBitfieldUnit::raw_set_const::<9, 64>(unit, u64::MAX); + assert_eq!(__BindgenBitfieldUnit::raw_get(unit, 9, 64), u64::MAX); + } +} + +// The safe wrappers must bounds-check before entering the shared pointer code, +// including in release builds where debug assertions are disabled. +#[test] +#[should_panic(expected = "start_byte + bytes_needed <= storage_len")] +fn bitfield_unit_get_out_of_bounds() { + let unit = __BindgenBitfieldUnit::new([0xFF; 8]); + unit.get(1, 64); +} + +#[test] +fn bitfield_unit_set_out_of_bounds_preserves_storage() { + let mut unit = __BindgenBitfieldUnit::new([0xFF; 8]); + let before = unit; + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + unit.set(1, 64, 0); + })); + assert!(result.is_err()); + assert_eq!(unit, before); +} + +#[cfg(target_endian = "little")] #[test] fn bitfield_unit_get_bit() { let unit = __BindgenBitfieldUnit::<[u8; 2]>::new([0b10011101, 0b00011101]); @@ -45,6 +201,7 @@ fn bitfield_unit_get_bit() { ); } +#[cfg(target_endian = "little")] #[test] fn bitfield_unit_set_bit() { let mut unit = @@ -80,6 +237,7 @@ macro_rules! bitfield_unit_get { With $storage:expr , then get($start:expr, $len:expr) is $expected:expr; )* ) => { + #[cfg(target_endian = "little")] #[test] fn bitfield_unit_get() { $({ @@ -181,6 +339,7 @@ macro_rules! bitfield_unit_set { set($start:expr, $len:expr, $val:expr) is $expected:expr; )* ) => { + #[cfg(target_endian = "little")] #[test] fn bitfield_unit_set() { $( @@ -258,182 +417,3 @@ bitfield_unit_set! { set(7, 16, 0b1111111111111111) is 0b00000000011111111111111110000000; set(8, 16, 0b1111111111111111) is 0b00000000111111111111111100000000; } - -// Tests for const-generic methods -#[test] -fn bitfield_unit_get_const_matches_get() { - // Test that get_const produces same results as get - let unit = __BindgenBitfieldUnit::<[u8; 4]>::new([ - 0b01010101, 0b11111111, 0b00000000, 0b11111111, - ]); - - // Single byte tests - assert_eq!(unit.get_const::<0, 1>(), unit.get(0, 1)); - assert_eq!(unit.get_const::<1, 1>(), unit.get(1, 1)); - assert_eq!(unit.get_const::<0, 8>(), unit.get(0, 8)); - assert_eq!(unit.get_const::<3, 5>(), unit.get(3, 5)); - - // Cross-byte boundary tests - assert_eq!(unit.get_const::<0, 16>(), unit.get(0, 16)); - assert_eq!(unit.get_const::<4, 16>(), unit.get(4, 16)); - assert_eq!(unit.get_const::<7, 16>(), unit.get(7, 16)); - assert_eq!(unit.get_const::<8, 16>(), unit.get(8, 16)); - - // Large field - assert_eq!(unit.get_const::<0, 32>(), unit.get(0, 32)); -} - -#[test] -fn bitfield_unit_set_const_matches_set() { - // Test that set_const produces same results as set - let test_value = 0b101010101010; - - for offset in [0, 1, 3, 7, 8, 12] { - for width in [1, 2, 5, 8, 12] { - let mut unit_const = __BindgenBitfieldUnit::<[u8; 4]>::new([0; 4]); - let mut unit_runtime = - __BindgenBitfieldUnit::<[u8; 4]>::new([0; 4]); - - match (offset, width) { - (0, 1) => unit_const.set_const::<0, 1>(test_value), - (0, 2) => unit_const.set_const::<0, 2>(test_value), - (0, 5) => unit_const.set_const::<0, 5>(test_value), - (0, 8) => unit_const.set_const::<0, 8>(test_value), - (0, 12) => unit_const.set_const::<0, 12>(test_value), - (1, 1) => unit_const.set_const::<1, 1>(test_value), - (1, 2) => unit_const.set_const::<1, 2>(test_value), - (1, 5) => unit_const.set_const::<1, 5>(test_value), - (1, 8) => unit_const.set_const::<1, 8>(test_value), - (1, 12) => unit_const.set_const::<1, 12>(test_value), - (3, 1) => unit_const.set_const::<3, 1>(test_value), - (3, 2) => unit_const.set_const::<3, 2>(test_value), - (3, 5) => unit_const.set_const::<3, 5>(test_value), - (3, 8) => unit_const.set_const::<3, 8>(test_value), - (3, 12) => unit_const.set_const::<3, 12>(test_value), - (7, 1) => unit_const.set_const::<7, 1>(test_value), - (7, 2) => unit_const.set_const::<7, 2>(test_value), - (7, 5) => unit_const.set_const::<7, 5>(test_value), - (7, 8) => unit_const.set_const::<7, 8>(test_value), - (7, 12) => unit_const.set_const::<7, 12>(test_value), - (8, 1) => unit_const.set_const::<8, 1>(test_value), - (8, 2) => unit_const.set_const::<8, 2>(test_value), - (8, 5) => unit_const.set_const::<8, 5>(test_value), - (8, 8) => unit_const.set_const::<8, 8>(test_value), - (8, 12) => unit_const.set_const::<8, 12>(test_value), - (12, 1) => unit_const.set_const::<12, 1>(test_value), - (12, 2) => unit_const.set_const::<12, 2>(test_value), - (12, 5) => unit_const.set_const::<12, 5>(test_value), - (12, 8) => unit_const.set_const::<12, 8>(test_value), - (12, 12) => unit_const.set_const::<12, 12>(test_value), - _ => continue, - } - - unit_runtime.set(offset, width, test_value); - // Compare by reading back the full value - assert_eq!(unit_const.get(0, 32), unit_runtime.get(0, 32)); - } - } -} - -#[test] -fn bitfield_unit_raw_const_methods() { - let unit = __BindgenBitfieldUnit::<[u8; 2]>::new([0b10011101, 0b00011101]); - - // Test raw_get_const - unsafe { - assert_eq!( - __BindgenBitfieldUnit::raw_get_const::<0, 8>(&unit), - unit.get(0, 8) - ); - assert_eq!( - __BindgenBitfieldUnit::raw_get_const::<4, 8>(&unit), - unit.get(4, 8) - ); - assert_eq!( - __BindgenBitfieldUnit::raw_get_const::<0, 16>(&unit), - unit.get(0, 16) - ); - } - - // Test raw_set_const - let mut unit_const = __BindgenBitfieldUnit::<[u8; 2]>::new([0; 2]); - let mut unit_runtime = __BindgenBitfieldUnit::<[u8; 2]>::new([0; 2]); - - unsafe { - __BindgenBitfieldUnit::raw_set_const::<3, 5>(&mut unit_const, 0b11111); - } - unit_runtime.set(3, 5, 0b11111); - - // Compare by reading back - assert_eq!(unit_const.get(0, 16), unit_runtime.get(0, 16)); -} - -// Regression: const-generic accessors must not shift-overflow when -// BIT_WIDTH equals the native word size (usize::BITS). Previously -// `(1usize << BIT_WIDTH) - 1` panicked in debug builds for 64-bit -// fields on 64-bit targets (and 32-bit fields on 32-bit targets). -#[test] -fn bitfield_unit_const_full_word_width() { - let storage = [0x12u8, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]; - let unit = __BindgenBitfieldUnit::<[u8; 8]>::new(storage); - - // Width == 64 on 64-bit hosts, and width == 32 exercises the - // boundary on 32-bit hosts (still safe on 64-bit, just routes - // through a different branch). - assert_eq!(unit.get_const::<0, 64>(), unit.get(0, 64)); - assert_eq!(unit.get_const::<0, 32>(), unit.get(0, 32)); - - unsafe { - assert_eq!( - __BindgenBitfieldUnit::raw_get_const::<0, 64>(&unit), - unit.get(0, 64), - ); - } - - let mut unit_const = __BindgenBitfieldUnit::<[u8; 8]>::new([0; 8]); - let mut unit_runtime = __BindgenBitfieldUnit::<[u8; 8]>::new([0; 8]); - let value = 0xDEAD_BEEF_CAFE_BABE_u64; - - unit_const.set_const::<0, 64>(value); - unit_runtime.set(0, 64, value); - assert_eq!(unit_const.get(0, 64), unit_runtime.get(0, 64)); - - let mut unit_raw = __BindgenBitfieldUnit::<[u8; 8]>::new([0; 8]); - unsafe { - __BindgenBitfieldUnit::raw_set_const::<0, 64>(&mut unit_raw, value); - } - assert_eq!(unit_raw.get(0, 64), value); - - // Exercise the new `field_mask = !0 << bit_shift` branch with a - // non-zero bit_shift (BIT_WIDTH + bit_shift == usize::BITS but - // BIT_WIDTH < usize::BITS, so the value-mask still runs). - let mut unit_shifted_const = - __BindgenBitfieldUnit::<[u8; 9]>::new([0xAA; 9]); - let mut unit_shifted_runtime = - __BindgenBitfieldUnit::<[u8; 9]>::new([0xAA; 9]); - unit_shifted_const.set_const::<1, 63>(value & ((1u64 << 63) - 1)); - unit_shifted_runtime.set(1, 63, value & ((1u64 << 63) - 1)); - assert_eq!( - unit_shifted_const.get(0, 64), - unit_shifted_runtime.get(0, 64), - ); - assert_eq!( - unit_shifted_const.get_const::<1, 63>(), - unit_shifted_runtime.get(1, 63), - ); - - let mut unit_shifted_raw = __BindgenBitfieldUnit::<[u8; 9]>::new([0xAA; 9]); - let mut unit_shifted_raw_runtime = - __BindgenBitfieldUnit::<[u8; 9]>::new([0xAA; 9]); - unsafe { - __BindgenBitfieldUnit::raw_set_const::<1, 63>( - &mut unit_shifted_raw, - value & ((1u64 << 63) - 1), - ); - } - unit_shifted_raw_runtime.set(1, 63, value & ((1u64 << 63) - 1)); - assert_eq!( - unit_shifted_raw.get(0, 64), - unit_shifted_raw_runtime.get(0, 64), - ); -} diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 26a5de00c7..b2ec75ddca 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -11,7 +11,7 @@ pub(crate) mod struct_layout; #[cfg(test)] #[allow(warnings)] pub(crate) mod bitfield_unit; -#[cfg(all(test, target_endian = "little"))] +#[cfg(test)] mod bitfield_unit_tests; use self::dyngen::DynamicItems;