Custom
Recipe | Crates | Categories |
---|---|---|
Define and Operate on a Type Represented as a Bitfield | ||
flagset | ||
bitvec |
A bitfield is a data structure that efficiently stores a sequence of bits. It is a way to represent a set of boolean flags or options in a compact manner, using individual bits within an integer or an array of integers. Bitfields save memory and potentially improve performance.
Define and Operate on a Type Represented as a Bitfield
bitflags
⮳ offers a macro to generate structures which behave like bitflags. It creates type-safe bitfield type MyFlags
with help of bitflags::bitflags
⮳ macro and implements elementary clear
⮳ operation as well as std::fmt::Display
⮳ trait for it. Subsequently, shows basic bitwise operations and formatting.
use std::fmt; use bitflags::bitflags; bitflags! { // Attributes can be applied to flags types #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] struct MyFlags: u32 { const FLAG_A = 0b0000_0001; const FLAG_B = 0b0000_0010; const FLAG_C = 0b0000_0100; const FLAG_ABC = Self::FLAG_A.bits() | Self::FLAG_B.bits() | Self::FLAG_C.bits(); } } impl MyFlags { pub fn as_u64(&self) -> u64 { self.bits() as u64 } } impl fmt::Display for MyFlags { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:032b}", self.bits()) } } fn main() { let e1 = MyFlags::FLAG_A | MyFlags::FLAG_C; let e2 = MyFlags::FLAG_B | MyFlags::FLAG_C; assert_eq!((e1 | e2), MyFlags::FLAG_ABC); assert_eq!((e1 & e2), MyFlags::FLAG_C); assert_eq!((e1 - e2), MyFlags::FLAG_A); assert_eq!(!e2, MyFlags::FLAG_A); // Use the fmt::Display implementation above println!("e1: {} e2: {}", e1, e2); let flags = MyFlags::FLAG_ABC; assert_eq!(format!("{}", flags), "00000000000000000000000000000111"); assert_eq!(format!("{:?}", MyFlags::FLAG_B), "MyFlags(FLAG_B)"); assert_eq!( format!("{:?}", MyFlags::FLAG_A | MyFlags::FLAG_B), "MyFlags(FLAG_A | FLAG_B)" ); println!("{:?}", flags); }
flagset
A flagset refers to a bitfield used to represent a set of boolean flags or options, where each bit has a specific meaning.
flagset
⮳ is a ergonomic approach to handling flags that combines the best of existing crates like bitflags
⮳ and enumflags
⮳ without their downsides.
use std::os::raw::c_int;
use flagset::FlagSet;
use flagset::flags;
// FlagSet has no dependencies, incl. the stdlib, so it can be used in `no_std`
// libraries and applications.
// Flags are defined using the `flags!` macro:
flags! {
// Flag values can be defined implicitly
enum FlagsA: u8 { // Note the field-size type
Foo, // 0b0001
Bar, // 0b0010
Baz, // 0b0100
}
// Flag values can also be defined explicitly...
enum FlagsB: c_int {
Foo = 0x01,
Bar = 2,
Baz = 0b0110, // ...and overlap.
All = (FlagsB::Foo | FlagsB::Bar | FlagsB::Baz).bits(),
}
}
// Attributes can be used on the enumeration itself or any of the values.
// A collection of flags is a FlagSet<T>.
#[derive(Debug)]
struct Container(FlagSet<FlagsA>);
impl Container {
fn new(flags: impl Into<FlagSet<FlagsA>>) -> Container {
Container(flags.into())
}
}
fn main() {
let container = Container::new(FlagsA::Foo | FlagsA::Bar);
assert_eq!(container.0.bits(), 0b011);
println!("{:?}", container);
assert_eq!(Container::new(None).0.bits(), 0b000);
}
// Adapted from https://docs.rs/flagset/
bitvec
bitvec
⮳ provides efficient storage and manipulation of bit vectors. It addresses memory by bits, for packed collections and bitfields
use std::fmt::Debug; use bitvec::prelude::*; // Add this dependency to your Cargo.toml: // [dependencies] // bitvec = "1.0.1" fn main() { // Create a new BitVec with default parameters let mut bv = bitvec![u8, Msb0; 0, 1, 0, 1, 1, 0, 1, 0]; println!("Original BitVec: {}", bv); // Access individual bits println!("First bit: {}", bv[0]); println!("Second bit: {}", bv[1]); // Modify bits. // The parameters are: index, value. bv.set(0, true); bv.set(5, true); println!("Modified BitVec: {}", bv); // Get the length. println!("BitVec length: {}", bv.len()); // Check if all bits are set. println!("All bits set: {}", bv.all()); // Check if any bits are set println!("Any bits set: {}", bv.any()); // Count set bits println!("Number of set bits: {}", bv.count_ones()); // Iterate through bits print!("Iterating through bits: "); for bit in bv.iter() { print!("{} ", bit); } println!(); println!("\n===== Different Endianness and Storage Types ====="); // Create a BitVec with different storage types and endianness let bv_lsb0 = bitvec![u16, Lsb0; 0, 1, 0, 1, 1, 0, 1, 0]; println!("LSB0 BitVec: {}", bv_lsb0); let bv_u32 = bitvec![u32, Msb0; 0, 1, 0, 1, 1, 0, 1, 0]; println!("u32 BitVec: {}", bv_u32); println!("\n===== Bit Manipulation Operations ====="); // Create two BitVecs for bitwise operations let mut a = bitvec![u8, Msb0; 1, 1, 0, 0, 1, 0, 1, 0]; let b = bitvec![u8, Msb0; 0, 1, 1, 0, 0, 1, 1, 1]; println!("a: {}", a); println!("b: {}", b); // Bitwise AND let c = a.clone() & b.clone(); println!("a & b: {}", c); // Bitwise OR let d = a.clone() | b.clone(); println!("a | b: {}", d); // Bitwise XOR let e = a.clone() ^ b.clone(); println!("a ^ b: {}", e); // Bitwise NOT let f = !a.clone(); println!("!a: {}", f); // In-place operations: a &= &b; println!("a &= b: {}", a); println!("\n===== Slicing and Ranges ====="); let mut bv_slice = bitvec![u8, Msb0; 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0]; println!("Original BitVec: {}", bv_slice); // Get a slice: let slice = &bv_slice[2..6]; println!("Slice [2..6]: {}", slice); // Modify a slice: bv_slice[4..8].fill(true); println!("After filling [4..8] with true: {}", bv_slice); println!("\n===== Practical Applications ====="); // Example 1: Bit flags let mut flags = bitvec![u8, Msb0; 0; 8]; // Set flags flags.set(0, true); // READ flags.set(1, true); // WRITE flags.set(2, false); // EXECUTE println!("Permission flags: {}", flags); println!("Can read: {}", flags[0]); println!("Can write: {}", flags[1]); println!("Can execute: {}", flags[2]); // Example 2: Bit packing. // Pack multiple boolean values efficiently. let mut packed = BitVec::<u8, Msb0>::new(); // Appends single bits to the vector: packed.push(true); // is_active packed.push(false); // is_admin packed.push(true); // is_verified packed.push(false); // is_premium println!("Packed bits: {}", packed); // Example 3: Simple bloom filter fn create_bloom_filter<T: Debug>( size: usize, items: &[T], hash_fn: impl Fn(&T) -> usize, ) -> BitVec<u8> { let mut filter = bitvec![u8, Lsb0; 0; size]; for item in items { let hash = hash_fn(item) % size; filter.set(hash, true); println!("Added item {:?}, hash: {}", item, hash); } filter } let test_items = ["apple", "banana", "cherry"]; let bloom = create_bloom_filter(10, &test_items, |s| { s.len() * 7 + s.chars().next().unwrap() as usize }); println!("Bloom filter: {}", bloom); // Example 4: Bit-packed struct use bitvec::field::BitField; let mut data = bitvec![u16, Lsb0; 0; 16]; // Pack values into specific bit ranges data[0..3].store(0b101u8); data[3..8].store(0b10011u8); data[8..16].store(0b11001010u8); println!("Bit-packed data: {}", data); // Extract values let value1: u8 = data[0..3].load(); let value2: u8 = data[3..8].load(); let value3: u8 = data[8..16].load(); println!("Extracted values: {}, {}, {}", value1, value2, value3); }