replacebits!() { /* proc-macro */ }
Expand description
Replace some of the bits in an integer with bits from other variables, as specified by a template. Placeholders (periods) mark bits that will not be replaced.
Limitation: Currently this macro doesn’t take input variables as parameters, they must be captured from single-letter variables.
use splitbits::replacebits;
let a: u16 = 0b101;
let b: u8 = 0b00001;
let c: u128 = 0b0101;
let d = true;
let original = 0b1111_1111_0000_0000;
let replaced = replacebits!(original, "aaab bbbb .d.. cccc");
assert_eq!(replaced, 0b1010_0001_0100_0101);
Literals can be placed in the template to override specific bits:
use splitbits::replacebits;
let a: u16 = 0b10;
let result = replacebits!(0b10000001, "aa.11..0");
assert_eq!(result, 0b10011000);
Input variables can be split across multiple template slots:
use splitbits::replacebits;
let a: u16 = 0b101;
let result = replacebits!(0b1111_1111, "a... ..aa");
assert_eq!(result, 0b1111_1101);
§Field overflow behavior
If an input value is too large for its slot in the template, by default its most significant bits are truncated (but other overflow behavior options exist).
Note that input variable types frequently have more bits than the slots that they go into, which is why overflow behavior is needed in the first place.
In each of the following examples, the value of “a” requires 7 bits to represent, but its slot in the template is only 6 bits wide. So “a” is too large, causing its first ‘1’ bit to overflow.
§Default behavior (no overflow setting specified)
Truncate the most significant bits of the field when an overflow occurs so that the field still fits.
use splitbits::replacebits;
let original: u8 = 0b00001000;
let a: u8 = 0b01100001;
// Compiles to: ((a << 1) & 0b01111110) | (original & 0b10000001)
let replaced = replacebits!(original, ".aaaaaa.");
assert_eq!(replaced, 0b01000010);
§overflow=truncate (same as default behavior)
use splitbits::replacebits;
let original: u8 = 0b00001000;
let a: u8 = 0b01100001;
// Compiles to: ((a << 1) & 0b01111110) | (original & 0b10000001)
let replaced = replacebits!(overflow=truncate, original, ".aaaaaa.");
assert_eq!(replaced, 0b01000010);
§overflow=corrupt
The most efficient option, but corrupts the bits that precede the slot if an overflow occurs.
Warning! Only use this option if you have some other way of knowing that an overflow won’t occur. If an overflow occurs, bits that the template indicates will not be replaced, will be.
To increase safety here, ux input types can be used.
use splitbits::replacebits;
let original: u8 = 0b00001000;
let a: u8 = 0b01100001;
// Compiles to: (a << 1) | (original & 0b10000001)
let replaced = replacebits!(overflow=corrupt, original, ".aaaaaa.");
// The most significant bit is replaced, despite the template indicating that should not be,
// due to an invalidly large value assigned to "a" by the caller.
assert_eq!(replaced, 0b11000010);
§overflow=saturate
Sets all the bits of the field to 1s if an overflow occurs.
use splitbits::replacebits;
let original: u8 = 0b00001000;
let a: u8 = 0b01100001;
// Compiles to: min(a << 1, 0b01111110) | (original & 0b10000001)
let replaced = replacebits!(overflow=saturate, original, ".aaaaaa.");
assert_eq!(replaced, 0b01111110);
§overflow=panic
Results in a panic if “a” overflows its slot.
use splitbits::replacebits;
let original: u8 = 0b00001000;
let a: u8 = 0b01100001;
// Compiles to: assert!((a << 1) <= 0b01111110))
let _ = replacebits!(overflow=panic, original, ".aaaaaa.");