From d89de0352b46f83dbc6eab8a2ba38efbb61e6941 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 14 Sep 2026 22:53:00 -0400 Subject: [PATCH] Fix bitfields spanning nine storage bytes 496817f7c replaced bit-at-a-time access with byte loops that assemble the field in a u64. A valid 64-bit field starting one bit into storage spans nine bytes, so get(1, 64) and set(1, 64, value) shift by 64 on the last iteration. The setter also discards the high bit when aligning the value. The existing full-word regression test covered aligned fields and fields ending at bit 64, leaving this crossing case untested. Handle the ninth byte separately, after shifting the first word on reads and before shifting away the high bits on writes. Share the read and write implementations across runtime, raw-pointer, and const-generic accessors instead of maintaining separate usize and u64 algorithms. The const-generic wrappers pass their constants into the inlineable helpers, and the getters remain usable in constants. Store byte-aligned 64-bit fields directly with write_unaligned. Such fields use native byte order and need no read-modify-write. This keeps AArch64 code generation to a single store instead of vectorizing eight byte extractions into shifts, shuffles, and constant-pool loads. Use byte pointers without creating references to the whole storage, so raw access can leave neighboring bytes uninitialized. Check the field's bounds before accessing storage, including in release builds, to preserve the safe accessors' bounds checking and reject invalid writes before modifying any bytes. Replace comparisons between accessors sharing the same algorithm with bit-at-a-time expected values. Cover narrow and full-width fields at every intra-byte offset, preservation of neighboring bits, constant evaluation, indirect storage, and bounds failures. Enable this coverage on both endiannesses while retaining the older little-endian-specific cases behind their existing condition. --- .../tests/align_bitfields_32bit.rs | 508 ++++---------- .../tests/bitfield-32bit-overflow.rs | 508 ++++---------- .../expectations/tests/bitfield-large.rs | 508 ++++---------- .../expectations/tests/bitfield-linux-32.rs | 508 ++++---------- .../tests/bitfield-method-same-name.rs | 508 ++++---------- .../expectations/tests/bitfield-template.rs | 508 ++++---------- .../expectations/tests/bitfield_align.rs | 508 ++++---------- .../expectations/tests/bitfield_align_2.rs | 508 ++++---------- .../tests/bitfield_method_mangling.rs | 508 ++++---------- .../tests/bitfield_pack_offset.rs | 508 ++++---------- .../tests/bitfield_pragma_packed.rs | 508 ++++---------- .../tests/default_visibility_crate.rs | 508 ++++---------- .../tests/default_visibility_private.rs | 508 ++++---------- ...bility_private_respects_cxx_access_spec.rs | 508 ++++---------- .../tests/derive-bitfield-method-same-name.rs | 508 ++++---------- .../tests/derive-debug-bitfield-1-51.rs | 508 ++++---------- .../tests/derive-debug-bitfield-core.rs | 508 ++++---------- .../tests/derive-debug-bitfield.rs | 508 ++++---------- .../tests/derive-partialeq-bitfield.rs | 508 ++++---------- .../tests/divide-by-zero-in-struct-layout.rs | 508 ++++---------- .../tests/field-rename-callback.rs | 508 ++++---------- .../tests/field-visibility-callback.rs | 508 ++++---------- .../expectations/tests/field-visibility.rs | 508 ++++---------- .../tests/incomplete-array-padding.rs | 508 ++++---------- .../tests/expectations/tests/issue-1034.rs | 508 ++++---------- .../issue-1076-unnamed-bitfield-alignment.rs | 508 ++++---------- .../tests/expectations/tests/issue-1947.rs | 508 ++++---------- .../tests/issue-3454-enum-newtype-bitfield.rs | 508 ++++---------- .../tests/issue-739-pointer-wide-bitfield.rs | 508 ++++---------- .../tests/expectations/tests/issue-743.rs | 508 ++++---------- .../tests/expectations/tests/issue-816.rs | 508 ++++---------- .../expectations/tests/jsval_layout_opaque.rs | 508 ++++---------- .../tests/expectations/tests/layout_align.rs | 508 ++++---------- .../expectations/tests/layout_eth_conf.rs | 508 ++++---------- .../tests/expectations/tests/layout_mbuf.rs | 508 ++++---------- .../expectations/tests/only_bitfields.rs | 508 ++++---------- .../expectations/tests/packed-bitfield.rs | 508 ++++---------- .../expectations/tests/private_fields.rs | 508 ++++---------- .../tests/redundant-packed-and-align.rs | 508 ++++---------- .../tests/struct_with_bitfields.rs | 508 ++++---------- .../tests/expectations/tests/timex.rs | 508 ++++---------- .../expectations/tests/union_bitfield.rs | 508 ++++---------- .../tests/union_with_anon_struct_bitfield.rs | 508 ++++---------- .../expectations/tests/weird_bitfields.rs | 508 ++++---------- bindgen/codegen/bitfield_unit.rs | 656 +++++------------- bindgen/codegen/bitfield_unit_tests.rs | 338 +++++---- bindgen/codegen/mod.rs | 2 +- 47 files changed, 6219 insertions(+), 17129 deletions(-) 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;