neobit

Crates.ioneobit
lib.rsneobit
version1.0.4
created_at2025-12-28 12:56:07.42299+00
updated_at2026-01-06 05:51:08.222042+00
descriptionZero-dependency, lightweight bitflags with readable debug output
homepage
repositoryhttps://github.com/but212/neobit
max_upload_size
id2008787
size138,336
Jeong-il Suk (but212)

documentation

https://docs.rs/neobit

README

neobit

Crates.io Docs Ask DeepWiki

A zero-dependency, bitflags macro for systems programming. Designed for no_std environments.

Why neobit?

Bit operations are simple. The library should be too.

neobit provides union, intersection, difference, complement, and membership testing. Nothing more, nothing less.

Quick Start

use neobit::neobit;

neobit! {
    pub struct Permissions: u8 {
        const READ    = 0b001;
        const WRITE   = 0b010;
        const EXECUTE = 0b100;
    }
}

fn main() {
    let perms = Permissions::READ | Permissions::WRITE;

    assert!(perms.contains(Permissions::READ));
    assert!(!perms.contains(Permissions::EXECUTE));

    println!("{:?}", perms);  // Permissions(READ | WRITE)
    
    // From trait - from raw bits
    let from_raw: Permissions = 0b111.into();
    assert!(from_raw.is_all());
    
    // All defined flags
    let all = Permissions::all();
    
    // Bit validation (returns Option)
    let valid = Permissions::from_bits(0b011);    // Some(...)
    let invalid = Permissions::from_bits(0b1000); // None
}

Features

  • Zero dependencies - Pure Rust, nothing else
  • no_std compatible - Works in embedded environments
  • All integer types - u8-u128 and i8-i128
  • Readable debug output - Flags(READ | WRITE) instead of Flags { bits: 3 }
  • const fn operations - Use in const contexts
  • Pure bitwise complement - !flags inverts all bits, not just known flags

Use Cases

  • Hardware register manipulation
  • Network protocol flags
  • System call flags (C FFI)
  • Embedded systems (no_std)
  • Security-sensitive projects requiring easy code audit

Limitations

Composite Constants in Macro

Composite constants can be defined in the macro, but require using .union().bits() syntax:

neobit! {
    pub struct Flags: u8 {
        const A = 0b001;     // ✅ Single bit
        const B = 0b010;     // ✅ Single bit
        const AB = Self::A.union(Self::B).bits();  // ✅ Composite constant - requires .bits()
    }
}

Alternatively, define composite constants outside the macro for cleaner syntax:

impl Flags {
    pub const AB: Self = Self::A.union(Self::B);  // ✅ Cleaner approach
}

API Overview

Construction

// Using From trait (recommended for hardware/FFI)
let flags_into: Flags = 0x1234.into();  // Preserves all bits
let flags_from = Flags::from(0x1234);   // Same as above

// Other construction methods
Flags::empty()                     // No flags set
Flags::all()                       // All defined flags
Flags::from_bits(bits)             // Validated, returns Option<Self>
Flags::from_bits_truncate(bits)    // Truncate unknown bits
Flags::from_bits_retain(bits)      // Keep all bits (same as From)

Operations

flags.contains(other)       // All bits in other are in flags
flags.intersects(other)     // Any bits in other are in flags
flags.is_empty()            // No bits set
flags.is_all()              // All defined flags set
flags.bits()                // Raw bit value

Mutation

flags.insert(other)         // Add flags
flags.remove(other)         // Remove flags
flags.toggle(other)         // Flip flags
flags.set(other, condition) // Set or remove based on bool

Operators

Operator Meaning const fn equivalent
| Union union()
& Intersection intersection()
^ Symmetric difference symmetric_difference()
! Complement complement()
- Difference difference()

All operators have *Assign variants (|=, &=, etc.).

Const Context

const MASK: Flags = Flags::A.union(Flags::B);
const ALL: Flags = Flags::all();

Type Conversion

neobit implements From<T> trait for seamless conversion (unlike bitflags which only provides TryFrom):

// From integer to flags (preserves all bits)
let flags: Flags = 0xFF.into();     // Recommended, equivalent to `Flags::from(0xFF)`
// From flags to integer
let bits1: u8 = flags.into();
let bits2 = u8::from(flags);
let bits3 = flags.bits();            // Also works

Note: From trait uses from_bits_retain internally, preserving all bits including unknown ones. This is intentional for hardware/FFI use cases.

Complement Behavior

neobit and bitflags implement complement() differently:

neobit! {
    pub struct Flags: u8 {
        const A = 0b01;
        const B = 0b10;
    }
}

let flags = Flags::A;  // 0b00000001

// neobit: Pure bitwise NOT
let comp = !flags;     // 0b11111110

// bitflags: Masked to known flags
// !flags             -> 0b00000010

neobit preserves all bit information, which is essential for hardware registers and protocol handling.

Signed Types

Signed integers are supported for C FFI compatibility, but be careful with ! (complement):

neobit! {
    pub struct SignedFlags: i8 {
        const A = 0b0001;
    }
}

let complement = !SignedFlags::A;
// i8: !0b0001 = -2 (two's complement)
// u8: !0b0001 = 254

// Prefer difference() for removing flags:
let all = SignedFlags::all();
let without_a = all.difference(SignedFlags::A);

C FFI Example

use neobit::neobit;

// Define flags matching a C header
neobit! {
    #[repr(transparent)]
    pub struct RegisterFlags: u32 {
        const READY   = 0x01;
        const ERROR   = 0x02;
        const BUSY    = 0x04;
        const DATA_RDY = 0x08;
    }
}

// Safe Rust wrapper around C functions
fn read_status() -> RegisterFlags {
    let raw = read_register();
    raw.into()  // From trait preserves all bits!
}

fn set_ready_flag() {
    let current = read_status();
    let updated = current | RegisterFlags::READY;
    write_register(updated.bits());
}

// See examples/c_ffi_simple.rs for a complete runnable example

Debug Output

Single-bit flags are shown by name. Composite constants are expanded:

println!("{:?}", Flags::READ);                    // Flags(READ)
println!("{:?}", Flags::READ | Flags::WRITE);     // Flags(READ | WRITE)
println!("{:?}", Flags::all());                    // Flags(READ | WRITE | EXECUTE)
println!("{:?}", Flags::empty());                  // Flags(empty)
println!("{:?}", Flags::from(0x80));              // Flags(0x80)

Examples

Check out the examples/ directory for comprehensive demonstrations:

Getting Started

  • quick_start.rs - Basic usage with file permissions
  • permissions.rs - File permission flags example
  • all_method.rs - Using the built-in all() method

Core Features

  • bit_validation.rs - Safe vs unchecked bit operations
  • validation.rs - Input validation patterns
  • operators_and_methods.rs - All available operations
  • type_conversion.rs - Converting between integers and flags
  • const_operations.rs - Using flags in const contexts
  • integer_types.rs - All supported integer types (u8-u128, i8-i128)

Advanced Usage

  • composite_flags.rs - Defining composite flag constants
  • complement_difference.rs - How neobit differs from bitflags
  • limitations.rs - Macro limitations and workarounds

Real-World Scenarios

  • c_ffi.rs - Complete C FFI integration example
  • c_ffi_simple.rs - Simple C FFI and hardware register example
  • hardware_register.rs - Hardware register manipulation
  • network_protocol.rs - Network protocol flags

Run them with:

cargo run --example <example_name>

Minimum Rust Version

Rust 1.56 or later.

License

Licensed under either of:

at your option.

Commit count: 77

cargo fmt