Custom

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 bitflags-crates.io bitflags-github bitflags-lib.rs cat-no-std

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

flagset flagset-crates.io flagset-github flagset-lib.rs

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-website bitvec bitvec-crates.io bitvec-github bitvec-lib.rs cat-data-structures cat-embedded cat-no-std cat-rust-patterns

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);
}