# Concise bit field extraction [![Doc Status]][docs] [![Latest Version]][crates.io] [doc status]: https://img.shields.io/docsrs/splitbits?style=plastic [docs]: https://docs.rs/splitbits/ [latest version]: https://img.shields.io/crates/v/splitbits?style=plastic [crates.io]: https://crates.io/crates/splitbits splitbits Concise macros for extracting bits from integers and combining bits into integers. No scary syntax. Minimal magic. ```rust use splitbits::splitbits; // Parse the template ("aaabbbbb"), apply it to the input, // then generate a struct populated with the bit field values. let fields = splitbits!(0b11110000, "aaabbbbb"); // Single-letter field names, // generated from the unique letters in the template above. assert_eq!(fields.a, 0b111); assert_eq!(fields.b, 0b10000); ``` # Why use splitbits? Splitbits replaces tedious, error-prone bit operations with a simple template format, making it easy to extract bits into variables. Every operation than can be executed at compile time is. Generated code should be as efficient as hand-written bit operations. Splitbits is intended for cases where [bitfield] is too heavy-weight syntactically: when you don't want to explicitly declare a new struct for data that you won't use as a return value or argument. [bitfield]: https://docs.rs/bitfield/latest/bitfield/ # The four base macros For additional examples, see each macro's page. - [splitbits!] - Extract bit fields out of an integer, storing them as fields of a struct. (See example above.) By default, each field will be stored in the smallest unsigned integer type possible. - [combinebits!] - Combine bits of multiple integers into a single integer. ```rust use splitbits::combinebits; let b: u8 = 0b1010_1010; let m: u8 = 0b1111; let e: u8 = 0b0000; let result = combinebits!("bbbb bbbb mmmm eeee"); assert_eq!(result, 0b1010_1010_1111_0000); ``` - [splitbits_then_combine!] - Extract bit fields from multiple integers then combine them into a single integer. ```rust use splitbits::splitbits_then_combine; let output = splitbits_then_combine!( 0b1111_0000, "aaaa ..bb", // input 0, input template 0, 0b1011_1111, "cc.. ....", // input 1, input template 1 "aaaa bbcc", // output template ); assert_eq!(output, 0b1111_0010); ``` - [replacebits!] - Replace some of the bits of an integer with bits from other integers. ```rust use splitbits::replacebits; let a: u16 = 0b101; let b: u8 = 0b01; // Placeholder periods in the template are the bits that will not be replaced. let result = replacebits!(0b10000001, "aaa..bb."); assert_eq!(result, 0b10100011); ``` # Macro variants The four base macros cover all the basic functionality that this crate offers and should be sufficient for most use-cases. However, in many situations better ergonomics can be achieved by using these more specialized macro variants. #### Hexadecimal All four base macros have equivalents that use hexadecimal digits for their templates rather than bits (binary digits). The variants are [splithex!], [combinehex!], [splithex_then_combine!], and [replacehex!]. #### Splitbits variants [splitbits!] itself has many variants which are intended for better ergonomics for the generated variables. The basic variants are: - [splitbits_named!] - Used when single-letter variable names aren't descriptive enough. This variant returns a tuple (instead of a struct) of the resulting fields, allowing the caller to assign individual long field names in the `let` binding. - [splitbits_named_into!] - Same as [splitbits_named!] except that the caller specifies the types of the resulting fields, not just their names. `into()` is called on each tuple field before it reaches the caller. This is useful for when the default type (the smallest integer type that will fit the field) is a smaller type than the caller would like to use, or if the caller has a newtype that they would like to use instead. - [splitbits_ux!] - Used when exact-width integers (e.g. u4, u7, u20) are needed, instead of just the standard types (u8, u16, u32, u64, u128, and bool). Requires the [ux] crate. [ux]: # Documentation Find thorough documentation of this crate and its many macro variants [here], including detailed template syntax, settings, and per-macro documentation and examples. [here]: # Milestones for future versions ### User-facing - Support template bases beyond binary and hexadecimal (base 8, 32, and 64). - Add setting for validating splitbits inputs by specifying literals in the template. - Add file-level config for setting defaults for the overflow and min settings. - Will allow macro invocations to be more concise at the call-site when the default settings are not desired for a project. - Allow non-standard template lengths. - Verify that splitbits can be used in no_std environments. It seems that the package itself doesn't need to be no_std, just the generated code. - Add support for different endianness for inputs and outputs? ### Performance - Add tests that verify that the intended code for each macro is what is generated. - Verify at the generated rust level. - Verify at the assembly level - Is the generated splitbits! struct elided or are we taking a performance hit? - Will be difficult due to different CPU architectures and instruction sets. - Remove all chained shift operations where possible - Always use overflow=corrupt for combinebits! and replacebits! if the input variable size exactly matches the field slot size. ### Code quality - Represent output as a syntax tree before final code generation. - Will enable cleaner generated code. - Will enable better optimization for generated code. - Will make adding new features easier. - Will improve code clarity and decrease bugginess by disentangling separate concerns. - Will fix bug where combinebits input types must not be larger than the template type. - Extract argument parsing from business logic. - Will improve code clarity and error handling. [splitbits!]: [combinebits!]: [splitbits_then_combine!]: [replacebits!]: [splitbits_named!]: [splitbits_named_into!]: [splitbits_ux!]: [splithex!]: [combinehex!]: [splithex_then_combine!]: [replacehex!]: