From 0fcbd2b34df3355061843396f58678edb8df865d Mon Sep 17 00:00:00 2001 From: Gregory Gaines Date: Thu, 4 Jun 2026 04:08:55 -0400 Subject: [PATCH 01/10] split array heap flag into std and no std variants --- bitfields/Cargo.toml | 10 +++-- bitfields_impl/Cargo.toml | 10 +++-- .../features/bitfield_struct_feature.rs | 11 ++++-- .../features/common/generator_helper.rs | 17 ++++++--- .../features/from_into_bits_feature.rs | 4 +- .../bitfield/features/from_traits_feature.rs | 3 +- .../features/invert_bit_ops_feature.rs | 2 +- .../src/parsing/bitfields/bitfield.rs | 9 ++++- .../bitfield_attribute/bitfield_arguments.rs | 38 ++++++++++++++----- .../bitfield_attribute_parser.rs | 4 +- 10 files changed, 76 insertions(+), 32 deletions(-) diff --git a/bitfields/Cargo.toml b/bitfields/Cargo.toml index d26f2b6..5df5533 100644 --- a/bitfields/Cargo.toml +++ b/bitfields/Cargo.toml @@ -44,7 +44,8 @@ default = [ "bitflag_from_endian_big", "bitflag_into_endian_big", "bitflag_derive_copy", - "disable_array_heap", + "disable_array_heap_no_std", + "disable_array_heap_std", ] order_lsb = [] @@ -98,8 +99,11 @@ disable_toggle_bit_ops = [] generate_builder = [] disable_builder = [] -enable_array_heap = [] -disable_array_heap = [] +enable_array_heap_std = [] +disable_array_heap_std = [] + +enable_array_heap_no_std = [] +disable_array_heap_no_std = [] bitflag_from_endian_big = [] bitflag_from_endian_little = [] diff --git a/bitfields_impl/Cargo.toml b/bitfields_impl/Cargo.toml index 8bda548..efa349e 100644 --- a/bitfields_impl/Cargo.toml +++ b/bitfields_impl/Cargo.toml @@ -49,7 +49,8 @@ default = [ "bitflag_from_endian_big", "bitflag_into_endian_big", "bitflag_derive_copy", - "disable_array_heap", + "disable_array_heap_no_std", + "disable_array_heap_std", ] order_lsb = [] @@ -103,8 +104,11 @@ disable_toggle_bit_ops = [] generate_builder = [] disable_builder = [] -enable_array_heap = [] -disable_array_heap = [] +enable_array_heap_std = [] +disable_array_heap_std = [] + +enable_array_heap_no_std = [] +disable_array_heap_no_std = [] bitflag_from_endian_big = [] bitflag_from_endian_little = [] diff --git a/bitfields_impl/src/generating/bitfield/features/bitfield_struct_feature.rs b/bitfields_impl/src/generating/bitfield/features/bitfield_struct_feature.rs index f88075e..0d9e926 100644 --- a/bitfields_impl/src/generating/bitfield/features/bitfield_struct_feature.rs +++ b/bitfields_impl/src/generating/bitfield/features/bitfield_struct_feature.rs @@ -80,8 +80,7 @@ impl BitfieldStructFeatureGenerator { } if bitfield.arguments().derive_copy() { - let is_heap_array = bitfield.arguments().array_heap() && !bitfield.is_integer_backed(); - if is_heap_array { + if bitfield.is_array_heap() { attributes_tokens.push(quote! { #[derive(core::clone::Clone)] }); @@ -105,10 +104,14 @@ impl BitfieldStructFeatureGenerator { /// Returns the backing storage type tokens for the bitfield struct field. fn get_backing_field_type_tokens(bitfield: &Bitfield) -> TokenStream { let inner = bitfield.spanned_data_type_token().to_tokens(); - if bitfield.arguments().array_heap() && !bitfield.is_integer_backed() { + if !bitfield.is_array_heap() { + inner + } else if bitfield.arguments().array_heap_std() { quote! { ::std::boxed::Box<#inner> } + } else if bitfield.arguments().array_heap_no_std() { + quote! { ::alloc::boxed::Box<#inner> } } else { - inner + unreachable!() } } diff --git a/bitfields_impl/src/generating/bitfield/features/common/generator_helper.rs b/bitfields_impl/src/generating/bitfield/features/common/generator_helper.rs index 18456b3..a7b4feb 100644 --- a/bitfields_impl/src/generating/bitfield/features/common/generator_helper.rs +++ b/bitfields_impl/src/generating/bitfield/features/common/generator_helper.rs @@ -17,8 +17,7 @@ pub enum BitsSource { /// Returns the function modifiers for generated functions (e.g., `const`). pub fn get_function_modifier_tokens(bitfield: &Bitfield) -> Option { - let is_heap_array = bitfield.arguments().array_heap() && !bitfield.is_integer_backed(); - (!bitfield.has_ignored_fields() && !is_heap_array && supports_const_mut_refs()) + (!bitfield.has_ignored_fields() && !bitfield.is_array_heap() && supports_const_mut_refs()) .then(|| quote::quote! { const }) } @@ -55,14 +54,20 @@ pub fn generate_bitfield_struct_initialization_tokens( length, } => { let length = length as usize; - if bitfield.arguments().array_heap() { + if !bitfield.is_array_heap() { + quote! { + [0u8; #length] + } + } else if bitfield.arguments().array_heap_std() { quote! { ::std::boxed::Box::new([0u8; #length]) } - } else { + } else if bitfield.arguments().array_heap_no_std() { quote! { - [0u8; #length] + ::alloc::boxed::Box::new([0u8; #length]) } + } else { + unreachable!() } }, DataType::Custom => { @@ -1004,6 +1009,6 @@ pub fn generate_backing_data_param_ident(bitfield: &Bitfield) -> TokenStream { } /// Returns the term used for the backing data of a bitfield. -pub const fn get_bits_or_bytes_term(bitfield: &Bitfield) -> &'static str { +pub fn get_bits_or_bytes_term(bitfield: &Bitfield) -> &'static str { if bitfield.is_integer_backed() { "bits" } else { "bytes" } } diff --git a/bitfields_impl/src/generating/bitfield/features/from_into_bits_feature.rs b/bitfields_impl/src/generating/bitfield/features/from_into_bits_feature.rs index a62d506..bf4adf3 100644 --- a/bitfields_impl/src/generating/bitfield/features/from_into_bits_feature.rs +++ b/bitfields_impl/src/generating/bitfield/features/from_into_bits_feature.rs @@ -517,7 +517,7 @@ impl FromIntoBitsFeature { let half_len = array_len / 2; let last_idx = array_len - 1; - let is_heap = bitfield.arguments().array_heap(); + let is_heap = bitfield.is_array_heap(); let into_bits_convert_tokens = |endian| match endian { ConversionEndian::Little => { @@ -601,7 +601,7 @@ impl FromIntoBitsFeature { let half_len = array_len / 2; let last_idx = array_len - 1; - let bits_access_tokens = if bitfield.arguments().array_heap() { + let bits_access_tokens = if bitfield.is_array_heap() { quote! { *#bitfield_internal_value_ident_tokens } } else { quote! { #bitfield_internal_value_ident_tokens } diff --git a/bitfields_impl/src/generating/bitfield/features/from_traits_feature.rs b/bitfields_impl/src/generating/bitfield/features/from_traits_feature.rs index e1616cf..7ed4485 100644 --- a/bitfields_impl/src/generating/bitfield/features/from_traits_feature.rs +++ b/bitfields_impl/src/generating/bitfield/features/from_traits_feature.rs @@ -116,8 +116,7 @@ impl FromTraitsFeature { bitfield.arguments().from_endian() }; - let is_heap_array = - into_bits && bitfield.arguments().array_heap() && !bitfield.is_integer_backed(); + let is_heap_array = into_bits && bitfield.is_array_heap() && !bitfield.is_integer_backed(); match conversion_endian { ConversionEndian::Little => { diff --git a/bitfields_impl/src/generating/bitfield/features/invert_bit_ops_feature.rs b/bitfields_impl/src/generating/bitfield/features/invert_bit_ops_feature.rs index 1d087d4..82cb39f 100644 --- a/bitfields_impl/src/generating/bitfield/features/invert_bit_ops_feature.rs +++ b/bitfields_impl/src/generating/bitfield/features/invert_bit_ops_feature.rs @@ -84,7 +84,7 @@ impl InvertBitOpsFeature { .array_length() .expect("array-backed bitfield must have a known length"); - let bits_init_tokens = if bitfield.arguments().array_heap() { + let bits_init_tokens = if bitfield.is_array_heap() { quote! { let mut bits = *#bitfield_internal_value_ident_tokens; } } else { quote! { let mut bits = #bitfield_internal_value_ident_tokens; } diff --git a/bitfields_impl/src/parsing/bitfields/bitfield.rs b/bitfields_impl/src/parsing/bitfields/bitfield.rs index cabf078..4844438 100644 --- a/bitfields_impl/src/parsing/bitfields/bitfield.rs +++ b/bitfields_impl/src/parsing/bitfields/bitfield.rs @@ -71,9 +71,16 @@ impl Bitfield { self.name_ident.to_token_stream() } - pub const fn is_integer_backed(&self) -> bool { + /// Returns if the bitfield is array backed. + pub fn is_integer_backed(&self) -> bool { matches!(self.spanned_data_type_token.data_type(), DataType::Integer(_)) } + + /// Returns if the bitfield is heap array backed. + pub fn is_array_heap(&self) -> bool { + (self.arguments().array_heap_std() || self.arguments().array_heap_no_std()) + && !self.is_integer_backed() + } } /// Represents a bitfield field. diff --git a/bitfields_impl/src/parsing/bitfields/bitfield_attribute/bitfield_arguments.rs b/bitfields_impl/src/parsing/bitfields/bitfield_attribute/bitfield_arguments.rs index 4812f39..41cc761 100644 --- a/bitfields_impl/src/parsing/bitfields/bitfield_attribute/bitfield_arguments.rs +++ b/bitfields_impl/src/parsing/bitfields/bitfield_attribute/bitfield_arguments.rs @@ -65,8 +65,11 @@ const DISABLE_TOGGLE_BIT_OPS_FEATURE_ENABLED: bool = cfg!(feature = "disable_tog const GENERATE_BUILDER_FEATURE_ENABLED: bool = cfg!(feature = "generate_builder"); const DISABLE_BUILDER_FEATURE_ENABLED: bool = cfg!(feature = "disable_builder"); -const ENABLE_ARRAY_HEAP_FEATURE_ENABLED: bool = cfg!(feature = "enable_array_heap"); -const DISABLE_ARRAY_HEAP_FEATURE_ENABLED: bool = cfg!(feature = "disable_array_heap"); +const ENABLE_ARRAY_HEAP_STD_FEATURE_ENABLED: bool = cfg!(feature = "enable_array_heap_std"); +const DISABLE_ARRAY_HEAP_STD_FEATURE_ENABLED: bool = cfg!(feature = "disable_array_heap_std"); + +const ENABLE_ARRAY_HEAP_NO_STD_FEATURE_ENABLED: bool = cfg!(feature = "enable_array_heap_no_std"); +const DISABLE_ARRAY_HEAP_NO_STD_FEATURE_ENABLED: bool = cfg!(feature = "disable_array_heap_no_std"); /// The order of the bits in the bitfield. /// @@ -165,11 +168,18 @@ pub struct BitfieldArguments { /// Whether a bitfield builder should be generated. generate_builder: bool, - /// Whether to allocate array-backed bitfield storage on the heap. + /// Whether to allocate array-backed bitfield storage on the heap using std. + /// + /// Useful when the array would be too large to live on the stack and has no + /// effect on integer-backed bitfields. + array_heap_std: bool, + + /// Whether to allocate array-backed bitfield storage on the heap using + /// alloc. /// /// Useful when the array would be too large to live on the stack and has no /// effect on integer-backed bitfields. - array_heap: bool, + array_heap_no_std: bool, /// Whether to force a panic during macro generation. force_panic: bool, @@ -251,7 +261,10 @@ impl Default for BitfieldArguments { GENERATE_BUILDER_FEATURE_ENABLED, DISABLE_BUILDER_FEATURE_ENABLED, ), - array_heap: ENABLE_ARRAY_HEAP_FEATURE_ENABLED && !DISABLE_ARRAY_HEAP_FEATURE_ENABLED, + array_heap_std: ENABLE_ARRAY_HEAP_STD_FEATURE_ENABLED + && !DISABLE_ARRAY_HEAP_STD_FEATURE_ENABLED, + array_heap_no_std: ENABLE_ARRAY_HEAP_NO_STD_FEATURE_ENABLED + && !DISABLE_ARRAY_HEAP_NO_STD_FEATURE_ENABLED, user_set_generate_write_bit_ops: false, force_panic: false, } @@ -333,8 +346,11 @@ enum BitfieldArgumentKey { #[strum(serialize = "builder")] Builder, - #[strum(serialize = "array_heap")] - ArrayHeap, + #[strum(serialize = "array_heap_std")] + ArrayHeapStd, + + #[strum(serialize = "array_heap_no_std")] + ArrayHeapNoStd, #[strum(serialize = "force_panic")] ForcePanic, @@ -430,8 +446,12 @@ impl Parse for BitfieldArguments { bitfield_arguments.generate_builder = parse_boolean_attribute_argument(argument)?; }, - BitfieldArgumentKey::ArrayHeap => { - bitfield_arguments.array_heap = parse_boolean_attribute_argument(argument)?; + BitfieldArgumentKey::ArrayHeapStd => { + bitfield_arguments.array_heap_std = parse_boolean_attribute_argument(argument)?; + }, + BitfieldArgumentKey::ArrayHeapNoStd => { + bitfield_arguments.array_heap_no_std = + parse_boolean_attribute_argument(argument)?; }, BitfieldArgumentKey::Copy => { bitfield_arguments.derive_copy = parse_boolean_attribute_argument(argument)?; diff --git a/bitfields_impl/src/parsing/bitfields/bitfield_attribute/bitfield_attribute_parser.rs b/bitfields_impl/src/parsing/bitfields/bitfield_attribute/bitfield_attribute_parser.rs index 4250bdb..9f8bb1e 100644 --- a/bitfields_impl/src/parsing/bitfields/bitfield_attribute/bitfield_attribute_parser.rs +++ b/bitfields_impl/src/parsing/bitfields/bitfield_attribute/bitfield_attribute_parser.rs @@ -53,7 +53,9 @@ impl BitfieldAttribute { | TypeParsingError::SizeTypeNotSupported => { Err(create_user_parsing_compiler_error( input.span(), - format!("{BITFIELD_ATTRIBUTE_NON_UNSIGNED_INTEGER_FIRST_ARGUMENT_ERROR_MESSAGE}."), + format!( + "{BITFIELD_ATTRIBUTE_NON_UNSIGNED_INTEGER_FIRST_ARGUMENT_ERROR_MESSAGE}." + ), )) }, TypeParsingError::UnexpectedFloat => Err(create_user_parsing_compiler_error( From 5a64afbed13ed6fa869c22a205059d4faa53f38a Mon Sep 17 00:00:00 2001 From: Gregory Gaines Date: Thu, 4 Jun 2026 04:09:16 -0400 Subject: [PATCH 02/10] add bitfield and bitflag code to no_std test --- .github/workflows/no-std.yml | 39 +++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/.github/workflows/no-std.yml b/.github/workflows/no-std.yml index a86bc9f..3c021b2 100644 --- a/.github/workflows/no-std.yml +++ b/.github/workflows/no-std.yml @@ -25,4 +25,41 @@ jobs: - run: | cd nostd-app cargo add bitfields --path ../bitfields --package nostd-app - - run: cargo build --target thumbv7em-none-eabihf \ No newline at end of file + - run: | + cat > nostd-app/src/main.rs << 'EOF' + #![no_std] + #![no_main] + + use bitfields::{bitfield, bitflag}; + + // pick a panicking behavior + use panic_halt as _; // you can put a breakpoint on `rust_begin_unwind` to catch panics + // use panic_abort as _; // requires nightly + // use panic_itm as _; // logs messages over ITM; requires ITM support + // use panic_semihosting as _; // logs messages to the host stderr; requires a debugger + + use cortex_m_rt::entry; + + #[entry] + fn main() -> ! { + loop { + #[bitfield(u32)] + pub struct Bitfield { + a: u8, + b: u8, + c: u8, + d: u8, + } + + #[bitflag(u8)] + #[derive(Debug, PartialEq)] + enum Flags { + #[base] + Unknown = 0, + A = 1, + B = 2, + } + } + } + EOF + - run: cargo build --target thumbv7em-none-eabihf From e84ccfc1dc987e4eb20f5b67558e23aa0c5c99b6 Mon Sep 17 00:00:00 2001 From: Gregory Gaines Date: Thu, 4 Jun 2026 04:19:01 -0400 Subject: [PATCH 03/10] update array-heap documentation --- README.md | 124 ++++++++++++++++++++++---------------- bitfields_impl/src/lib.rs | 97 ++++++++++++++++------------- 2 files changed, 128 insertions(+), 93 deletions(-) diff --git a/README.md b/README.md index 298e430..7dd13e1 100644 --- a/README.md +++ b/README.md @@ -23,30 +23,42 @@ systems (e.g. embedded development or emulators). ### 🛠️ Core Features -- **Custom & Nested Bitfield Types**: Supports user-defined types, nested bitfields, and enum-based bitflags. +- **Custom & Nested Bitfield Types**: Supports user-defined types, nested + bitfields, and enum-based bitflags. - **Bitflags**: Easily define bitflags using enums. -- **Array Support**: Supports array-backed bitfields and array fields, ideal for representing data exceeding 128 bits. -- **Endianness & Signed Data**: Built-in support for little/big endian conversions and 2's complement signed fields. +- **Array Support**: Supports array-backed bitfields and array fields, ideal for + representing data exceeding 128 bits. +- **Endianness & Signed Data**: Built-in support for little/big endian + conversions and 2's complement signed fields. - **Flexible Fields**: Field defaults and ignored fields. ### ⚡ Performance -- **Safe & Lightweight**: No `unsafe`, zero allocations, no runtime dependencies, and constant memory usage for primitive bitfields. -- **`no_std` Compatible**: Works in embedded and standard library-free environments. -- **Efficient**: Generates efficient, `const`-friendly code comparable to handwritten implementations. +- **Safe & Lightweight**: No `unsafe`, zero allocations, no runtime + dependencies, and constant memory usage for primitive bitfields. +- **`no_std` Compatible**: Works in embedded and standard library-free + environments. +- **Efficient**: Generates efficient, `const`-friendly code comparable to + handwritten implementations. ### 🛡️ Safety & Reliability -- **Compile-Time Validation**: Type, size, and bounds checks are validated at compile time. -- **Checked Accessors**: Optional checked accesses help catch out-of-bounds errors. +- **Compile-Time Validation**: Type, size, and bounds checks are validated at + compile time. +- **Checked Accessors**: Optional checked accesses help catch out-of-bounds + errors. - **Well Tested**: Comprehensive testing with over 90% code coverage. ### 💻 Developer Experience -- **Generated Bit Operations**: Automatically generates operations such as `get`, `set`, and `invert`. -- **Flexible Code Generation**: Configurable generation for different schema layouts and use cases. -- **Editor-Friendly**: Preserves syntax highlighting on annotated structs and enums. -- **Documentation & Examples**: Comprehensive documentation that compiles and examples. +- **Generated Bit Operations**: Automatically generates operations such as + `get`, `set`, and `invert`. +- **Flexible Code Generation**: Configurable generation for different schema + layouts and use cases. +- **Editor-Friendly**: Preserves syntax highlighting on annotated structs and + enums. +- **Documentation & Examples**: Comprehensive documentation that compiles and + examples. ## 📖 Table of Contents @@ -254,10 +266,11 @@ bitfields larger than `u128`. Just like primitive bitfields, the bitfield field bits must add up to the exact number of bits of the bitfield type. If you have an array backed bitfield that may overflow the stack, you can pass -the optional argument `#[bitfield(array_heap = true)]` to the bitfield, which -will box the array on the heap instead of the stack. Keep in mind that you -**lose constant memory, zero-allocation, and no_std guarantees when using heap -array bitfields**. +the optional argument `#[bitfield(array_heap_std = true)]` or +`#[bitfield(array_heap_no_std = true)]` (depending on your environment) to the +bitfield, which will box the array on the heap instead of the stack. Keep in +mind that you **lose constant memory and zero-allocation when using heap array +bitfields**. ```rust use bitfields::bitfield; @@ -268,8 +281,9 @@ struct ArrayBitfield { b: u8, } -#[bitfield([u8; 96], array_heap = true -)] /// Allocated on the heap, 768 bits (96 bytes). +#[bitfield( + [u8; 96], array_heap_std = true) +] /// Allocated on the heap, 768 bits (96 bytes). struct HeapArrayBitfield { a: u128, b: u128, @@ -374,7 +388,7 @@ a `u8` field will occupy 8 bits. Fields can have a default value, which must fit in the field type or the specified bits of the field. A default value must be a const variable or -a const function. Just be aware that const function and variable +a const function. Just be aware that const function and variable defaults lose their compile-time field size checks, so it's up to you to make sure they fit. @@ -420,7 +434,8 @@ fn main() { A bitfield can have signed (`i8`, `i16`, `i32`, `i64`, `i128`) types. Signed types are treated as 2's complement data types, where the most significant bit -representing the sign bit. For example, if you had a field with 5 bits, the value +representing the sign bit. For example, if you had a field with 5 bits, the +value range would be `-16` to `15`. The more bits you include, the larger the range! ```rust @@ -525,7 +540,7 @@ field. There are four access levels: fields. But use a **reserved field** when you want to pad/fill bits that have no user-facing identity (hardware reserved bits, alignment gaps). Use **`access = na`** when the field is a real, named field that you want to - completely lock down from the API while still keeping it distinguishable + completely lock down from the API while still keeping it distinguishable by name. Later on, if you want to expose the field, you can just change the access level without having to change the field name. @@ -783,7 +798,7 @@ struct ReservedBitfield { /// Fills the middle bits of the u16. #[bits(4, default = 0xF)] __: u8, - + /// Fils the end bits of the u16. #[bits(4, default = 0xF)] __: u8, @@ -796,7 +811,7 @@ fn main() { // bitfield.set__reserved(0xFF); // Compile error, reserved fields are inaccessible. assert_eq!(bitfield.into_bits(), 0xFF00); // All fields exposed when converted // to bits. - + let reserved_bitfield = ReservedBitfield::new(); assert_eq!(reserved_bitfield.a(), 0); // assert_eq!(reserved_bitfield.__(), 0xFF0); // Compile error, reserved inaccessible. @@ -816,8 +831,10 @@ the `#[bitflag]` attribute which generates `from_bits` and `into_bits` for enums automatically. Bitflags only supports unsigned types (`u8`, `u16`, `u32`, `u64`, `u128`) and -the one of the variants must be annotated with `#[base]` or `#[default]` which represents the -base value of the bitflag. If `#[base]` and `#[default]` are both present, `#[base]` takes precedence. +the one of the variants must be annotated with `#[base]` or `#[default]` which +represents the +base value of the bitflag. If `#[base]` and `#[default]` are both present, +`#[base]` takes precedence. ```rust use bitfields::bitfield; @@ -895,7 +912,8 @@ fn main() { #### Bitflag Configuration -Bitflags can be configured with arguments passed to the `#[bitflag(...)]` attribute (the first argument is always the backing unsigned integer type): +Bitflags can be configured with arguments passed to the `#[bitflag(...)]` +attribute (the first argument is always the backing unsigned integer type): | Argument | Values | Default | Description | |------------------|-----------------------------------|----------|------------------------------------------------------------------------------------------------------| @@ -1289,7 +1307,8 @@ fn main() { Sometimes the outside world is outside our control, like the endianness of how systems export data. Luckily, the endianness of the bitfield conversions can be controlled by specifying the `#[bitfield(from_endian = N, into_endian = N)]` -args. The possible endianness options are `little` or `big`. By default, the endianness +args. The possible endianness options are `little` or `big`. By default, the +endianness of both is `big`. This arg controls the endianness of the `from`, `into`, and `From` trait @@ -2166,34 +2185,35 @@ use bitfields::bitfield; set_get_bit_ops = true, invert_bit_ops = true, toggle_bit_ops = true, - array_heap = false, + array_heap_std = false, )] struct Example { a: u32, } ``` -| Argument | Values | Default | Description | -|-------------------|-------------------------------------------------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `` | `u8`, `u16`, `u32`, `u64`, `u128`, or `[u8; N]` | Required | The storage used by the generated bitfield. Primitive backing types support bitfields up to 128 bits. `[u8; N]` creates an array-backed bitfield for larger layouts. Field bit widths, excluding ignored fields, must add up exactly to the backing storage size. | -| `order` | `lsb`, `msb` | `lsb` | Controls how struct fields are assigned to bit offsets. `lsb` assigns the first non-ignored field to the least-significant bits. `msb` assigns the first non-ignored field to the most-significant bits. | -| `from_endian` | `big`, `little` | `big` | Default endian used by `from_bits`, `from_bytes`, `from_slice`, and `From for Bitfield`. Explicit helpers such as `from_le_bits` and `from_be_bytes` ignore this setting. | -| `into_endian` | `big`, `little` | `big` | Default endian used by `into_bits`, `into_bytes`, `into_slice`, and `From for Backing`. Explicit helpers such as `into_le_bits` and `into_be_bytes` ignore this setting. | -| `write_endian` | `big`, `little` | `big` | Default endian used by whole-bitfield write helpers such as `write_bits` and `write_bytes`. Explicit helpers such as `write_le_bits` and `write_be_bytes` ignore this setting. | -| `new` | `true`, `false` | `true` | Generates `new()` and `new_without_defaults()` constructors. Other generated features that need construction logic, such as `Default` and the builder, still inline equivalent initialization logic when this is disabled. | -| `from_into_bits` | `true`, `false` | `true` | Generates backing-data conversion functions. Primitive bitfields get `from_bits`, `from_bits_with_defaults`, endian-specific `from_*_bits` helpers, `into_bits`, and endian-specific `into_*_bits` helpers. Array-backed bitfields get the corresponding `bytes` and `slice` APIs. | -| `from_traits` | `true`, `false` | `true` | Generates `From for Bitfield` and `From for Backing`. These conversions use `from_endian` and `into_endian`. | -| `default` | `true`, `false` | `true` | Generates `Default` for the bitfield. The default value is equivalent to `new()`: zero-initialized storage with field defaults applied. | -| `debug` | `true`, `false` | `true` | Generates `core::fmt::Debug` for the bitfield. The implementation prints readable fields and their values. | -| `copy` | `true`, `false` | `true` | Derives `Copy` and `Clone` for primitive and stack array-backed bitfields. Heap array-backed bitfields derive `Clone` only because `Box<[u8; N]>` is not `Copy`. | -| `builder` | `true`, `false` | `true` | Generates the `Builder` type, `new`, `new_without_defaults`, `with_`, `checked_with_`, and `build`. Reserved fields do not get builder setters. | -| `bit_ops` | `true`, `false` | `true` | Master switch for bit operation groups. When `false`, all bit operation groups are disabled unless a specific bit operation group is explicitly set to `true`. | -| `write_bit_ops` | `true`, `false` | `true` | Generates whole-bitfield write helpers such as `write_bits`, `write_bits_with_defaults`, `write_le_bits`, `write_be_bits`, and `write_defaults` for primitive bitfields, or the corresponding `bytes` helpers for array-backed bitfields. | -| `clear_bit_ops` | `true`, `false` | `true` | Generates whole-bitfield clear helpers such as `clear_bits` / `clear_bytes`, `clear_bits_with_defaults` / `clear_bytes_with_defaults`, plus per-field helpers like `clear_()` and `clear__to_default()`. | -| `set_get_bit_ops` | `true`, `false` | `true` | Generates individual bit helpers (`get_bit`, `checked_get_bit`, `set_bit`, `checked_set_bit`) and range helpers (`get_bits_range` / `set_bits_range` for primitive bitfields, `get_bytes_range` / `set_bytes_range` for array-backed bitfields, plus checked variants). | -| `invert_bit_ops` | `true`, `false` | `true` | Generates inversion helpers such as `invert_bits` / `invert_bytes`, per-field `invert_()`, and readable-field `_inverted()` getters. | -| `toggle_bit_ops` | `true`, `false` | `true` | Accepted as a bit-operation group flag for configuration compatibility. In this version, there are no separate `toggle_*` APIs; use the generated invert helpers to toggle bits. | -| `array_heap` | `true`, `false` | `false` | For array-backed bitfields only, stores the backing `[u8; N]` in a `Box` instead of inline in the struct. This helps avoid large stack values but requires heap allocation and therefore gives up the zero-allocation and `no_std` guarantees for that bitfield. It has no effect on primitive-backed bitfields. | +| Argument | Values | Default | Description | +|---------------------|-------------------------------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `` | `u8`, `u16`, `u32`, `u64`, `u128`, or `[u8; N]` | Required | The storage used by the generated bitfield. Primitive backing types support bitfields up to 128 bits. `[u8; N]` creates an array-backed bitfield for larger layouts. Field bit widths, excluding ignored fields, must add up exactly to the backing storage size. | +| `order` | `lsb`, `msb` | `lsb` | Controls how struct fields are assigned to bit offsets. `lsb` assigns the first non-ignored field to the least-significant bits. `msb` assigns the first non-ignored field to the most-significant bits. | +| `from_endian` | `big`, `little` | `big` | Default endian used by `from_bits`, `from_bytes`, `from_slice`, and `From for Bitfield`. Explicit helpers such as `from_le_bits` and `from_be_bytes` ignore this setting. | +| `into_endian` | `big`, `little` | `big` | Default endian used by `into_bits`, `into_bytes`, `into_slice`, and `From for Backing`. Explicit helpers such as `into_le_bits` and `into_be_bytes` ignore this setting. | +| `write_endian` | `big`, `little` | `big` | Default endian used by whole-bitfield write helpers such as `write_bits` and `write_bytes`. Explicit helpers such as `write_le_bits` and `write_be_bytes` ignore this setting. | +| `new` | `true`, `false` | `true` | Generates `new()` and `new_without_defaults()` constructors. Other generated features that need construction logic, such as `Default` and the builder, still inline equivalent initialization logic when this is disabled. | +| `from_into_bits` | `true`, `false` | `true` | Generates backing-data conversion functions. Primitive bitfields get `from_bits`, `from_bits_with_defaults`, endian-specific `from_*_bits` helpers, `into_bits`, and endian-specific `into_*_bits` helpers. Array-backed bitfields get the corresponding `bytes` and `slice` APIs. | +| `from_traits` | `true`, `false` | `true` | Generates `From for Bitfield` and `From for Backing`. These conversions use `from_endian` and `into_endian`. | +| `default` | `true`, `false` | `true` | Generates `Default` for the bitfield. The default value is equivalent to `new()`: zero-initialized storage with field defaults applied. | +| `debug` | `true`, `false` | `true` | Generates `core::fmt::Debug` for the bitfield. The implementation prints readable fields and their values. | +| `copy` | `true`, `false` | `true` | Derives `Copy` and `Clone` for primitive and stack array-backed bitfields. Heap array-backed bitfields derive `Clone` only because `Box<[u8; N]>` is not `Copy`. | +| `builder` | `true`, `false` | `true` | Generates the `Builder` type, `new`, `new_without_defaults`, `with_`, `checked_with_`, and `build`. Reserved fields do not get builder setters. | +| `bit_ops` | `true`, `false` | `true` | Master switch for bit operation groups. When `false`, all bit operation groups are disabled unless a specific bit operation group is explicitly set to `true`. | +| `write_bit_ops` | `true`, `false` | `true` | Generates whole-bitfield write helpers such as `write_bits`, `write_bits_with_defaults`, `write_le_bits`, `write_be_bits`, and `write_defaults` for primitive bitfields, or the corresponding `bytes` helpers for array-backed bitfields. | +| `clear_bit_ops` | `true`, `false` | `true` | Generates whole-bitfield clear helpers such as `clear_bits` / `clear_bytes`, `clear_bits_with_defaults` / `clear_bytes_with_defaults`, plus per-field helpers like `clear_()` and `clear__to_default()`. | +| `set_get_bit_ops` | `true`, `false` | `true` | Generates individual bit helpers (`get_bit`, `checked_get_bit`, `set_bit`, `checked_set_bit`) and range helpers (`get_bits_range` / `set_bits_range` for primitive bitfields, `get_bytes_range` / `set_bytes_range` for array-backed bitfields, plus checked variants). | +| `invert_bit_ops` | `true`, `false` | `true` | Generates inversion helpers such as `invert_bits` / `invert_bytes`, per-field `invert_()`, and readable-field `_inverted()` getters. | +| `toggle_bit_ops` | `true`, `false` | `true` | Accepted as a bit-operation group flag for configuration compatibility. In this version, there are no separate `toggle_*` APIs; use the generated invert helpers to toggle bits. | +| `array_heap_std` | `true`, `false` | `false` | For array-backed bitfields only, stores the backing `[u8; N]` in a `Box` instead of inline in the struct. This helps avoid large stack values but requires heap allocation and therefore gives up the zero-allocation and `no_std` guarantees for that bitfield. It has no effect on primitive-backed bitfields. | +| `array_heap_no_std` | `true`, `false` | `false` | For array-backed bitfields only, stores the backing `[u8; N]` in a `Box` instead of inline in the struct. This helps avoid large stack values but requires heap allocation and therefore gives up the zero-allocation but **keeps** `no_std` guarantees for that bitfield. It has no effect on primitive-backed bitfields. | ```rust use bitfields::bitfield; @@ -2252,7 +2272,7 @@ This lets you generate only the bit-operation APIs you need. #### Global Cargo Feature Flags If you find yourself applying the same configuration arguments to many bitfields -in your codebase, you can set those defaults globally by **disabling default +in your codebase, you can set those defaults globally by **disabling default features** and enabling the corresponding Cargo features: - Constructors: `generate_new` / `disable_new` @@ -2274,7 +2294,9 @@ features** and enabling the corresponding Cargo features: `disable_invert_bit_ops` - Toggle bit operations: `generate_toggle_bit_ops` / `disable_toggle_bit_ops` -- Array heap storage: `enable_array_heap` / `disable_array_heap` +- Array heap std storage: `enable_array_heap_std` / `disable_array_heap_std` +- Array heap no_std storage: `enable_array_heap_no_std` / + `disable_array_heap_no_std` Endian and order defaults have dedicated feature names: diff --git a/bitfields_impl/src/lib.rs b/bitfields_impl/src/lib.rs index 1803831..33319d0 100644 --- a/bitfields_impl/src/lib.rs +++ b/bitfields_impl/src/lib.rs @@ -44,10 +44,11 @@ const INTERNAL_ERROR_MESSAGE: &str = "A major unexpected error has occurred. If /// field bits must add up to the exact number of bits of the bitfield type. /// /// If you have an array backed bitfield that may overflow the stack, you can pass -/// the optional argument `#[bitfield(array_heap = true)]` to the bitfield, which -/// will box the array on the heap instead of the stack. Keep in mind that you -/// **lose constant memory, zero-allocation, and no_std guarantees when using heap -/// array bitfields**. +/// the optional argument `#[bitfield(array_heap_std = true)]` or +/// `#[bitfield(array_heap_no_std = true)]` (depending on your environment) to the +/// bitfield, which will box the array on the heap instead of the stack. Keep in +/// mind that you **lose constant memory and zero-allocation when using heap array +/// bitfields**. /// /// ```rust /// # use bitfields_impl as bitfields; @@ -59,8 +60,9 @@ const INTERNAL_ERROR_MESSAGE: &str = "A major unexpected error has occurred. If /// b: u8, /// } /// -/// #[bitfield([u8; 96], array_heap = true -/// )] /// Allocated on the heap, 768 bits (96 bytes). +/// #[bitfield( +/// [u8; 96], array_heap_std = true) +/// ] /// Allocated on the heap, 768 bits (96 bytes). /// struct HeapArrayBitfield { /// a: u128, /// b: u128, @@ -167,7 +169,7 @@ const INTERNAL_ERROR_MESSAGE: &str = "A major unexpected error has occurred. If /// /// Fields can have a default value, which must fit in the field type or the /// specified bits of the field. A default value must be a const variable or -/// a const function. Just be aware that const function and variable +/// a const function. Just be aware that const function and variable /// defaults lose their compile-time field size checks, so it's /// up to you to make sure they fit. /// @@ -214,7 +216,8 @@ const INTERNAL_ERROR_MESSAGE: &str = "A major unexpected error has occurred. If /// /// A bitfield can have signed (`i8`, `i16`, `i32`, `i64`, `i128`) types. Signed /// types are treated as 2's complement data types, where the most significant bit -/// representing the sign bit. For example, if you had a field with 5 bits, the value +/// representing the sign bit. For example, if you had a field with 5 bits, the +/// value /// range would be `-16` to `15`. The more bits you include, the larger the range! /// /// ```rust @@ -322,7 +325,7 @@ const INTERNAL_ERROR_MESSAGE: &str = "A major unexpected error has occurred. If /// fields. But use a **reserved field** when you want to pad/fill bits that have /// no user-facing identity (hardware reserved bits, alignment gaps). Use /// **`access = na`** when the field is a real, named field that you want to -/// completely lock down from the API while still keeping it distinguishable +/// completely lock down from the API while still keeping it distinguishable /// by name. Later on, if you want to expose the field, you can just change /// the access level without having to change the field name. /// @@ -584,7 +587,7 @@ const INTERNAL_ERROR_MESSAGE: &str = "A major unexpected error has occurred. If /// /// Fills the middle bits of the u16. /// #[bits(4, default = 0xF)] /// __: u8, -/// +/// /// /// Fils the end bits of the u16. /// #[bits(4, default = 0xF)] /// __: u8, @@ -597,7 +600,7 @@ const INTERNAL_ERROR_MESSAGE: &str = "A major unexpected error has occurred. If /// // bitfield.set__reserved(0xFF); // Compile error, reserved fields are inaccessible. /// assert_eq!(bitfield.into_bits(), 0xFF00); // All fields exposed when converted /// // to bits. -/// +/// /// let reserved_bitfield = ReservedBitfield::new(); /// assert_eq!(reserved_bitfield.a(), 0); /// // assert_eq!(reserved_bitfield.__(), 0xFF0); // Compile error, reserved inaccessible. @@ -617,8 +620,10 @@ const INTERNAL_ERROR_MESSAGE: &str = "A major unexpected error has occurred. If /// automatically. /// /// Bitflags only supports unsigned types (`u8`, `u16`, `u32`, `u64`, `u128`) and -/// the one of the variants must be annotated with `#[base]` or `#[default]` which represents the -/// base value of the bitflag. If `#[base]` and `#[default]` are both present, `#[base]` takes precedence. +/// the one of the variants must be annotated with `#[base]` or `#[default]` which +/// represents the +/// base value of the bitflag. If `#[base]` and `#[default]` are both present, +/// `#[base]` takes precedence. /// /// ```rust /// # use bitfields_impl as bitfields; @@ -697,7 +702,8 @@ const INTERNAL_ERROR_MESSAGE: &str = "A major unexpected error has occurred. If /// /// #### Bitflag Configuration /// -/// Bitflags can be configured with arguments passed to the `#[bitflag(...)]` attribute (the first argument is always the backing unsigned integer type): +/// Bitflags can be configured with arguments passed to the `#[bitflag(...)]` +/// attribute (the first argument is always the backing unsigned integer type): /// /// | Argument | Values | Default | Description | /// |------------------|-----------------------------------|----------|------------------------------------------------------------------------------------------------------| @@ -1097,7 +1103,8 @@ const INTERNAL_ERROR_MESSAGE: &str = "A major unexpected error has occurred. If /// Sometimes the outside world is outside our control, like the endianness of how /// systems export data. Luckily, the endianness of the bitfield conversions can /// be controlled by specifying the `#[bitfield(from_endian = N, into_endian = N)]` -/// args. The possible endianness options are `little` or `big`. By default, the endianness +/// args. The possible endianness options are `little` or `big`. By default, the +/// endianness /// of both is `big`. /// /// This arg controls the endianness of the `from`, `into`, and `From` trait @@ -1996,34 +2003,35 @@ const INTERNAL_ERROR_MESSAGE: &str = "A major unexpected error has occurred. If /// set_get_bit_ops = true, /// invert_bit_ops = true, /// toggle_bit_ops = true, -/// array_heap = false, +/// array_heap_std = false, /// )] /// struct Example { /// a: u32, /// } /// ``` /// -/// | Argument | Values | Default | Description | -/// |-------------------|-------------------------------------------------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -/// | `` | `u8`, `u16`, `u32`, `u64`, `u128`, or `[u8; N]` | Required | The storage used by the generated bitfield. Primitive backing types support bitfields up to 128 bits. `[u8; N]` creates an array-backed bitfield for larger layouts. Field bit widths, excluding ignored fields, must add up exactly to the backing storage size. | -/// | `order` | `lsb`, `msb` | `lsb` | Controls how struct fields are assigned to bit offsets. `lsb` assigns the first non-ignored field to the least-significant bits. `msb` assigns the first non-ignored field to the most-significant bits. | -/// | `from_endian` | `big`, `little` | `big` | Default endian used by `from_bits`, `from_bytes`, `from_slice`, and `From for Bitfield`. Explicit helpers such as `from_le_bits` and `from_be_bytes` ignore this setting. | -/// | `into_endian` | `big`, `little` | `big` | Default endian used by `into_bits`, `into_bytes`, `into_slice`, and `From for Backing`. Explicit helpers such as `into_le_bits` and `into_be_bytes` ignore this setting. | -/// | `write_endian` | `big`, `little` | `big` | Default endian used by whole-bitfield write helpers such as `write_bits` and `write_bytes`. Explicit helpers such as `write_le_bits` and `write_be_bytes` ignore this setting. | -/// | `new` | `true`, `false` | `true` | Generates `new()` and `new_without_defaults()` constructors. Other generated features that need construction logic, such as `Default` and the builder, still inline equivalent initialization logic when this is disabled. | -/// | `from_into_bits` | `true`, `false` | `true` | Generates backing-data conversion functions. Primitive bitfields get `from_bits`, `from_bits_with_defaults`, endian-specific `from_*_bits` helpers, `into_bits`, and endian-specific `into_*_bits` helpers. Array-backed bitfields get the corresponding `bytes` and `slice` APIs. | -/// | `from_traits` | `true`, `false` | `true` | Generates `From for Bitfield` and `From for Backing`. These conversions use `from_endian` and `into_endian`. | -/// | `default` | `true`, `false` | `true` | Generates `Default` for the bitfield. The default value is equivalent to `new()`: zero-initialized storage with field defaults applied. | -/// | `debug` | `true`, `false` | `true` | Generates `core::fmt::Debug` for the bitfield. The implementation prints readable fields and their values. | -/// | `copy` | `true`, `false` | `true` | Derives `Copy` and `Clone` for primitive and stack array-backed bitfields. Heap array-backed bitfields derive `Clone` only because `Box<[u8; N]>` is not `Copy`. | -/// | `builder` | `true`, `false` | `true` | Generates the `Builder` type, `new`, `new_without_defaults`, `with_`, `checked_with_`, and `build`. Reserved fields do not get builder setters. | -/// | `bit_ops` | `true`, `false` | `true` | Master switch for bit operation groups. When `false`, all bit operation groups are disabled unless a specific bit operation group is explicitly set to `true`. | -/// | `write_bit_ops` | `true`, `false` | `true` | Generates whole-bitfield write helpers such as `write_bits`, `write_bits_with_defaults`, `write_le_bits`, `write_be_bits`, and `write_defaults` for primitive bitfields, or the corresponding `bytes` helpers for array-backed bitfields. | -/// | `clear_bit_ops` | `true`, `false` | `true` | Generates whole-bitfield clear helpers such as `clear_bits` / `clear_bytes`, `clear_bits_with_defaults` / `clear_bytes_with_defaults`, plus per-field helpers like `clear_()` and `clear__to_default()`. | -/// | `set_get_bit_ops` | `true`, `false` | `true` | Generates individual bit helpers (`get_bit`, `checked_get_bit`, `set_bit`, `checked_set_bit`) and range helpers (`get_bits_range` / `set_bits_range` for primitive bitfields, `get_bytes_range` / `set_bytes_range` for array-backed bitfields, plus checked variants). | -/// | `invert_bit_ops` | `true`, `false` | `true` | Generates inversion helpers such as `invert_bits` / `invert_bytes`, per-field `invert_()`, and readable-field `_inverted()` getters. | -/// | `toggle_bit_ops` | `true`, `false` | `true` | Accepted as a bit-operation group flag for configuration compatibility. In this version, there are no separate `toggle_*` APIs; use the generated invert helpers to toggle bits. | -/// | `array_heap` | `true`, `false` | `false` | For array-backed bitfields only, stores the backing `[u8; N]` in a `Box` instead of inline in the struct. This helps avoid large stack values but requires heap allocation and therefore gives up the zero-allocation and `no_std` guarantees for that bitfield. It has no effect on primitive-backed bitfields. | +/// | Argument | Values | Default | Description | +/// |---------------------|-------------------------------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +/// | `` | `u8`, `u16`, `u32`, `u64`, `u128`, or `[u8; N]` | Required | The storage used by the generated bitfield. Primitive backing types support bitfields up to 128 bits. `[u8; N]` creates an array-backed bitfield for larger layouts. Field bit widths, excluding ignored fields, must add up exactly to the backing storage size. | +/// | `order` | `lsb`, `msb` | `lsb` | Controls how struct fields are assigned to bit offsets. `lsb` assigns the first non-ignored field to the least-significant bits. `msb` assigns the first non-ignored field to the most-significant bits. | +/// | `from_endian` | `big`, `little` | `big` | Default endian used by `from_bits`, `from_bytes`, `from_slice`, and `From for Bitfield`. Explicit helpers such as `from_le_bits` and `from_be_bytes` ignore this setting. | +/// | `into_endian` | `big`, `little` | `big` | Default endian used by `into_bits`, `into_bytes`, `into_slice`, and `From for Backing`. Explicit helpers such as `into_le_bits` and `into_be_bytes` ignore this setting. | +/// | `write_endian` | `big`, `little` | `big` | Default endian used by whole-bitfield write helpers such as `write_bits` and `write_bytes`. Explicit helpers such as `write_le_bits` and `write_be_bytes` ignore this setting. | +/// | `new` | `true`, `false` | `true` | Generates `new()` and `new_without_defaults()` constructors. Other generated features that need construction logic, such as `Default` and the builder, still inline equivalent initialization logic when this is disabled. | +/// | `from_into_bits` | `true`, `false` | `true` | Generates backing-data conversion functions. Primitive bitfields get `from_bits`, `from_bits_with_defaults`, endian-specific `from_*_bits` helpers, `into_bits`, and endian-specific `into_*_bits` helpers. Array-backed bitfields get the corresponding `bytes` and `slice` APIs. | +/// | `from_traits` | `true`, `false` | `true` | Generates `From for Bitfield` and `From for Backing`. These conversions use `from_endian` and `into_endian`. | +/// | `default` | `true`, `false` | `true` | Generates `Default` for the bitfield. The default value is equivalent to `new()`: zero-initialized storage with field defaults applied. | +/// | `debug` | `true`, `false` | `true` | Generates `core::fmt::Debug` for the bitfield. The implementation prints readable fields and their values. | +/// | `copy` | `true`, `false` | `true` | Derives `Copy` and `Clone` for primitive and stack array-backed bitfields. Heap array-backed bitfields derive `Clone` only because `Box<[u8; N]>` is not `Copy`. | +/// | `builder` | `true`, `false` | `true` | Generates the `Builder` type, `new`, `new_without_defaults`, `with_`, `checked_with_`, and `build`. Reserved fields do not get builder setters. | +/// | `bit_ops` | `true`, `false` | `true` | Master switch for bit operation groups. When `false`, all bit operation groups are disabled unless a specific bit operation group is explicitly set to `true`. | +/// | `write_bit_ops` | `true`, `false` | `true` | Generates whole-bitfield write helpers such as `write_bits`, `write_bits_with_defaults`, `write_le_bits`, `write_be_bits`, and `write_defaults` for primitive bitfields, or the corresponding `bytes` helpers for array-backed bitfields. | +/// | `clear_bit_ops` | `true`, `false` | `true` | Generates whole-bitfield clear helpers such as `clear_bits` / `clear_bytes`, `clear_bits_with_defaults` / `clear_bytes_with_defaults`, plus per-field helpers like `clear_()` and `clear__to_default()`. | +/// | `set_get_bit_ops` | `true`, `false` | `true` | Generates individual bit helpers (`get_bit`, `checked_get_bit`, `set_bit`, `checked_set_bit`) and range helpers (`get_bits_range` / `set_bits_range` for primitive bitfields, `get_bytes_range` / `set_bytes_range` for array-backed bitfields, plus checked variants). | +/// | `invert_bit_ops` | `true`, `false` | `true` | Generates inversion helpers such as `invert_bits` / `invert_bytes`, per-field `invert_()`, and readable-field `_inverted()` getters. | +/// | `toggle_bit_ops` | `true`, `false` | `true` | Accepted as a bit-operation group flag for configuration compatibility. In this version, there are no separate `toggle_*` APIs; use the generated invert helpers to toggle bits. | +/// | `array_heap_std` | `true`, `false` | `false` | For array-backed bitfields only, stores the backing `[u8; N]` in a `Box` instead of inline in the struct. This helps avoid large stack values but requires heap allocation and therefore gives up the zero-allocation and `no_std` guarantees for that bitfield. It has no effect on primitive-backed bitfields. | +/// | `array_heap_no_std` | `true`, `false` | `false` | For array-backed bitfields only, stores the backing `[u8; N]` in a `Box` instead of inline in the struct. This helps avoid large stack values but requires heap allocation and therefore gives up the zero-allocation but **keeps** `no_std` guarantees for that bitfield. It has no effect on primitive-backed bitfields. | /// /// ```rust /// # use bitfields_impl as bitfields; @@ -2083,7 +2091,7 @@ const INTERNAL_ERROR_MESSAGE: &str = "A major unexpected error has occurred. If /// #### Global Cargo Feature Flags /// /// If you find yourself applying the same configuration arguments to many bitfields -/// in your codebase, you can set those defaults globally by **disabling default +/// in your codebase, you can set those defaults globally by **disabling default /// features** and enabling the corresponding Cargo features: /// /// - Constructors: `generate_new` / `disable_new` @@ -2105,7 +2113,9 @@ const INTERNAL_ERROR_MESSAGE: &str = "A major unexpected error has occurred. If /// `disable_invert_bit_ops` /// - Toggle bit operations: `generate_toggle_bit_ops` / /// `disable_toggle_bit_ops` -/// - Array heap storage: `enable_array_heap` / `disable_array_heap` +/// - Array heap std storage: `enable_array_heap_std` / `disable_array_heap_std` +/// - Array heap no_std storage: `enable_array_heap_no_std` / +/// `disable_array_heap_no_std` /// /// Endian and order defaults have dedicated feature names: /// @@ -2172,8 +2182,10 @@ fn check_force_panic(bitfield: &Bitfield) { /// automatically. /// /// Bitflags only supports unsigned types (`u8`, `u16`, `u32`, `u64`, `u128`) and -/// the one of the variants must be annotated with `#[base]` or `#[default]` which represents the -/// base value of the bitflag. If `#[base]` and `#[default]` are both present, `#[base]` takes precedence. +/// the one of the variants must be annotated with `#[base]` or `#[default]` which +/// represents the +/// base value of the bitflag. If `#[base]` and `#[default]` are both present, +/// `#[base]` takes precedence. /// /// ```rust /// # use bitfields_impl as bitfields; @@ -2252,7 +2264,8 @@ fn check_force_panic(bitfield: &Bitfield) { /// /// #### Bitflag Configuration /// -/// Bitflags can be configured with arguments passed to the `#[bitflag(...)]` attribute (the first argument is always the backing unsigned integer type): +/// Bitflags can be configured with arguments passed to the `#[bitflag(...)]` +/// attribute (the first argument is always the backing unsigned integer type): /// /// | Argument | Values | Default | Description | /// |------------------|-----------------------------------|----------|------------------------------------------------------------------------------------------------------| From aaa2e69669aefe94be609624afed8f6a808a7a00 Mon Sep 17 00:00:00 2001 From: Gregory Gaines Date: Thu, 4 Jun 2026 04:21:57 -0400 Subject: [PATCH 04/10] update compile error test cases output --- ...field_invalid_bitfield_enable_disable_bit_ops_value.stderr | 4 ++-- .../compile_error_cases/errors/bitfield_unknown_arg.stderr | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bitfields/tests/compile_error_cases/errors/bitfield_invalid_bitfield_enable_disable_bit_ops_value.stderr b/bitfields/tests/compile_error_cases/errors/bitfield_invalid_bitfield_enable_disable_bit_ops_value.stderr index e100ebc..1c79925 100644 --- a/bitfields/tests/compile_error_cases/errors/bitfield_invalid_bitfield_enable_disable_bit_ops_value.stderr +++ b/bitfields/tests/compile_error_cases/errors/bitfield_invalid_bitfield_enable_disable_bit_ops_value.stderr @@ -1,10 +1,10 @@ -error: Unknown argument 'enable_disable_bit_ops'. Valid arguments are: 'array_heap', 'bit_ops', 'builder', 'clear_bit_ops', 'copy', 'debug', 'default', 'from_endian', 'from_into_bits', 'from_traits', 'into_endian', 'invert_bit_ops', 'new', 'order', 'set_get_bit_ops', 'toggle_bit_ops', 'write_bit_ops', 'write_endian'. +error: Unknown argument 'enable_disable_bit_ops'. Valid arguments are: 'array_heap_no_std', 'array_heap_std', 'bit_ops', 'builder', 'clear_bit_ops', 'copy', 'debug', 'default', 'from_endian', 'from_into_bits', 'from_traits', 'into_endian', 'invert_bit_ops', 'new', 'order', 'set_get_bit_ops', 'toggle_bit_ops', 'write_bit_ops', 'write_endian'. --> tests/compile_error_cases/errors/bitfield_invalid_bitfield_enable_disable_bit_ops_value.rs:3:17 | 3 | #[bitfield(u32, enable_disable_bit_ops = 123)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: Unknown argument 'enable_disable_bit_ops'. Valid arguments are: 'array_heap', 'bit_ops', 'builder', 'clear_bit_ops', 'copy', 'debug', 'default', 'from_endian', 'from_into_bits', 'from_traits', 'into_endian', 'invert_bit_ops', 'new', 'order', 'set_get_bit_ops', 'toggle_bit_ops', 'write_bit_ops', 'write_endian'. +error: Unknown argument 'enable_disable_bit_ops'. Valid arguments are: 'array_heap_no_std', 'array_heap_std', 'bit_ops', 'builder', 'clear_bit_ops', 'copy', 'debug', 'default', 'from_endian', 'from_into_bits', 'from_traits', 'into_endian', 'invert_bit_ops', 'new', 'order', 'set_get_bit_ops', 'toggle_bit_ops', 'write_bit_ops', 'write_endian'. --> tests/compile_error_cases/errors/bitfield_invalid_bitfield_enable_disable_bit_ops_value.rs:9:17 | 9 | #[bitfield(u32, enable_disable_bit_ops = invalid)] diff --git a/bitfields/tests/compile_error_cases/errors/bitfield_unknown_arg.stderr b/bitfields/tests/compile_error_cases/errors/bitfield_unknown_arg.stderr index b32b758..77e79f6 100644 --- a/bitfields/tests/compile_error_cases/errors/bitfield_unknown_arg.stderr +++ b/bitfields/tests/compile_error_cases/errors/bitfield_unknown_arg.stderr @@ -1,4 +1,4 @@ -error: Unknown argument 'deez'. Valid arguments are: 'array_heap', 'bit_ops', 'builder', 'clear_bit_ops', 'copy', 'debug', 'default', 'from_endian', 'from_into_bits', 'from_traits', 'into_endian', 'invert_bit_ops', 'new', 'order', 'set_get_bit_ops', 'toggle_bit_ops', 'write_bit_ops', 'write_endian'. +error: Unknown argument 'deez'. Valid arguments are: 'array_heap_no_std', 'array_heap_std', 'bit_ops', 'builder', 'clear_bit_ops', 'copy', 'debug', 'default', 'from_endian', 'from_into_bits', 'from_traits', 'into_endian', 'invert_bit_ops', 'new', 'order', 'set_get_bit_ops', 'toggle_bit_ops', 'write_bit_ops', 'write_endian'. --> tests/compile_error_cases/errors/bitfield_unknown_arg.rs:3:17 | 3 | #[bitfield(u32, deez = what)] From d9bb18496cc735057e5f46500c538b5e5ba556d7 Mon Sep 17 00:00:00 2001 From: Gregory Gaines Date: Thu, 4 Jun 2026 04:30:18 -0400 Subject: [PATCH 05/10] add array heap bitfield to no-std test --- .github/workflows/no-std.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/no-std.yml b/.github/workflows/no-std.yml index 3c021b2..a28ae7b 100644 --- a/.github/workflows/no-std.yml +++ b/.github/workflows/no-std.yml @@ -50,6 +50,14 @@ jobs: c: u8, d: u8, } + + #[bitfield([u8; 4], array_heap_no_std = true)] + pub struct ArrayHeapBitfield { + a: u8, + b: u8, + c: u8, + d: u8, + } #[bitflag(u8)] #[derive(Debug, PartialEq)] From 4ce28fb6497c28a79e2d0dd24f9c94d18b216148 Mon Sep 17 00:00:00 2001 From: Gregory Gaines Date: Thu, 4 Jun 2026 04:30:26 -0400 Subject: [PATCH 06/10] add array heap std test --- bitfields/tests/lib_array_backed_tests.rs | 34 +++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/bitfields/tests/lib_array_backed_tests.rs b/bitfields/tests/lib_array_backed_tests.rs index 8e01798..5613e3d 100644 --- a/bitfields/tests/lib_array_backed_tests.rs +++ b/bitfields/tests/lib_array_backed_tests.rs @@ -4977,4 +4977,38 @@ mod tests { "clear_bytes: _reserved must retain its default 0x78" ); } + + #[test] + fn bitfield_array_heap_std_true() { + #[bitfield([u8; 4], array_heap_std = true)] + struct Bitfield { + a: u8, + b: u8, + c: u8, + d: u8, + } + + let bitfield = Bitfield::from_slice(&[0x11, 0x22, 0x33, 0x44]); + assert_eq!(bitfield.a(), 0x44); + assert_eq!(bitfield.b(), 0x33); + assert_eq!(bitfield.c(), 0x22); + assert_eq!(bitfield.d(), 0x11); + } + + #[test] + fn bitfield_array_heap_std_false() { + #[bitfield([u8; 4], array_heap_std = false)] + struct Bitfield { + a: u8, + b: u8, + c: u8, + d: u8, + } + + let bitfield = Bitfield::from_slice(&[0x11, 0x22, 0x33, 0x44]); + assert_eq!(bitfield.a(), 0x44); + assert_eq!(bitfield.b(), 0x33); + assert_eq!(bitfield.c(), 0x22); + assert_eq!(bitfield.d(), 0x11); + } } From 0fdc03cd72d85d8e10527e1bff4b7aaeac2759bf Mon Sep 17 00:00:00 2001 From: Gregory Gaines Date: Thu, 4 Jun 2026 05:03:29 -0400 Subject: [PATCH 07/10] add test for array heap no_std in set --- Cargo.lock | 7 +++++ bitfields/Cargo.toml | 1 + .../errors/arrays/array_heap_no_std_in_std.rs | 30 +++++++++++++++++++ .../arrays/array_heap_no_std_in_std.stderr | 4 +++ 4 files changed, 42 insertions(+) create mode 100644 bitfields/tests/compile_error_cases/errors/arrays/array_heap_no_std_in_std.rs create mode 100644 bitfields/tests/compile_error_cases/errors/arrays/array_heap_no_std_in_std.stderr diff --git a/Cargo.lock b/Cargo.lock index 1cb8220..89a4290 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,6 +7,7 @@ name = "bitfields" version = "2.0.8" dependencies = [ "bitfields-impl", + "dummy-alloc", "trybuild", ] @@ -57,6 +58,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "dummy-alloc" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fadb95bab5cf4a3b4d608255efdb2493a7755ba6e602bd88c95a6912609d2550" + [[package]] name = "equivalent" version = "1.0.1" diff --git a/bitfields/Cargo.toml b/bitfields/Cargo.toml index 5df5533..8a97b06 100644 --- a/bitfields/Cargo.toml +++ b/bitfields/Cargo.toml @@ -16,6 +16,7 @@ bitfields-impl = { path = "../bitfields_impl", version = "2.0.8" } [dev-dependencies] trybuild = "1.0.116" +dummy-alloc = "1.0.0" # Represents the bitfield arguments the user can enable or disable by default for # all bitfields instead of specifying them for each bitfield. diff --git a/bitfields/tests/compile_error_cases/errors/arrays/array_heap_no_std_in_std.rs b/bitfields/tests/compile_error_cases/errors/arrays/array_heap_no_std_in_std.rs new file mode 100644 index 0000000..fda354d --- /dev/null +++ b/bitfields/tests/compile_error_cases/errors/arrays/array_heap_no_std_in_std.rs @@ -0,0 +1,30 @@ +#![no_std] + +use bitfields::bitfield; +use core::panic::PanicInfo; +extern crate alloc; +use dummy_alloc::DummyAllocator; + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + loop {} +} + +#[global_allocator] +static ALLOCATOR: DummyAllocator = DummyAllocator; + +#[bitfield([u8; 4], array_heap_no_std = true)] +struct ArrayHeapBitfield { + a: u8, + b: u8, + c: u8, + d: u8, +} + +fn main() { + let bitfield = ArrayHeapBitfield::from_slice(&[0x11, 0x22, 0x33, 0x44]); + assert_eq!(bitfield.a(), 0x44); + assert_eq!(bitfield.b(), 0x33); + assert_eq!(bitfield.c(), 0x22); + assert_eq!(bitfield.d(), 0x11); +} diff --git a/bitfields/tests/compile_error_cases/errors/arrays/array_heap_no_std_in_std.stderr b/bitfields/tests/compile_error_cases/errors/arrays/array_heap_no_std_in_std.stderr new file mode 100644 index 0000000..8ace0cc --- /dev/null +++ b/bitfields/tests/compile_error_cases/errors/arrays/array_heap_no_std_in_std.stderr @@ -0,0 +1,4 @@ +error: unwinding panics are not supported without std + | + = help: using nightly cargo, use -Zbuild-std with panic="abort" to avoid unwinding + = note: since the core library is usually precompiled with panic="unwind", rebuilding your crate with panic="abort" may not be enough to fix the problem From 3e567bea030bca733e825f94915ad79af252eec3 Mon Sep 17 00:00:00 2001 From: Gregory Gaines Date: Thu, 4 Jun 2026 05:08:38 -0400 Subject: [PATCH 08/10] add alloc crate to no_std test --- .github/workflows/no-std.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/no-std.yml b/.github/workflows/no-std.yml index a28ae7b..f238db1 100644 --- a/.github/workflows/no-std.yml +++ b/.github/workflows/no-std.yml @@ -39,6 +39,7 @@ jobs: // use panic_semihosting as _; // logs messages to the host stderr; requires a debugger use cortex_m_rt::entry; + extern crate alloc; #[entry] fn main() -> ! { From 0931d4ba81d5f37cd41d65d52d64f647728c6130 Mon Sep 17 00:00:00 2001 From: Gregory Gaines Date: Thu, 4 Jun 2026 05:15:32 -0400 Subject: [PATCH 09/10] add global allocated to no_std test --- .github/workflows/no-std.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/no-std.yml b/.github/workflows/no-std.yml index f238db1..3c9069f 100644 --- a/.github/workflows/no-std.yml +++ b/.github/workflows/no-std.yml @@ -25,6 +25,7 @@ jobs: - run: | cd nostd-app cargo add bitfields --path ../bitfields --package nostd-app + cargo add buddy_system_allocator --path ../buddy_system_allocator --package nostd-app - run: | cat > nostd-app/src/main.rs << 'EOF' #![no_std] @@ -39,7 +40,11 @@ jobs: // use panic_semihosting as _; // logs messages to the host stderr; requires a debugger use cortex_m_rt::entry; + use buddy_system_allocator::LockedHeap; extern crate alloc; + + #[global_allocator] + static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::empty(); #[entry] fn main() -> ! { From 389ceb536ef6c86a7849bb7ca9e745e55e88ecce Mon Sep 17 00:00:00 2001 From: Gregory Gaines Date: Thu, 4 Jun 2026 05:21:52 -0400 Subject: [PATCH 10/10] fix global allocator dep in no_std test --- .github/workflows/no-std.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/no-std.yml b/.github/workflows/no-std.yml index 3c9069f..ad9b6cf 100644 --- a/.github/workflows/no-std.yml +++ b/.github/workflows/no-std.yml @@ -25,7 +25,7 @@ jobs: - run: | cd nostd-app cargo add bitfields --path ../bitfields --package nostd-app - cargo add buddy_system_allocator --path ../buddy_system_allocator --package nostd-app + cargo add buddy_system_allocator --package nostd-app - run: | cat > nostd-app/src/main.rs << 'EOF' #![no_std]