nexus-bits 0.1.0

Bit field packing for integer IDs
Documentation

nexus-bits

Bit field packing for integer IDs.

nexus-bits provides ergonomic bit field manipulation for packing multiple values into integers — common in trading systems for instrument IDs, order IDs, and wire protocols.

Why nexus-bits?

Several excellent bitfield crates exist (bitfield-struct, modular-bitfield, bitfield). nexus-bits targets a different use case:

Feature nexus-bits Others
Bit positions Explicit #[field(start = 4, len = 8)] Auto-sequential
Storage Pack to/from raw integers (u64, i64) Wrapped in struct
Overflow Result<T, FieldOverflow> Silent truncation
Tagged enums Discriminant-based variant layouts Not supported

When to use nexus-bits:

  • Matching existing wire formats or ID schemes with specific bit layouts
  • Working with IDs that must be plain integers (database keys, protocol fields)
  • Trading systems where silent truncation is unacceptable
  • Packing different data into the same integer based on a discriminant

When to use other crates:

  • Auto-layouting fields sequentially is fine
  • You want the bitfield wrapped in a type (not a raw integer)
  • Truncation on overflow is acceptable

Features

  • BitField<T> — Extract and set multi-bit fields at arbitrary positions
  • Flag<T> — Single-bit boolean flags
  • IntEnum — Derive macro for #[repr(u8)] enums to convert to/from integers
  • BitPacked — Derive macro for structs with automatic pack/unpack generation
  • no_std compatible
  • Compile-time validation — Field overlap and bounds checking at compile time
  • Runtime overflow detection — Returns Result on pack if values exceed field capacity

Usage

Manual bit field manipulation

use nexus_bits::{BitField, Flag};

const KIND: BitField<u64> = BitField::<u64>::new(0, 4);
const EXCHANGE: BitField<u64> = BitField::<u64>::new(4, 8);
const SYMBOL: BitField<u64> = BitField::<u64>::new(12, 20);
const IS_TEST: Flag<u64> = Flag::<u64>::new(63);

// Pack
let mut id: u64 = 0;
id = KIND.set(id, 1).unwrap();
id = EXCHANGE.set(id, 5).unwrap();
id = SYMBOL.set(id, 12345).unwrap();
id = IS_TEST.set(id);

// Unpack
assert_eq!(KIND.get(id), 1);
assert_eq!(EXCHANGE.get(id), 5);
assert_eq!(SYMBOL.get(id), 12345);
assert!(IS_TEST.is_set(id));

Derive macros (recommended)

use nexus_bits::{BitPacked, IntEnum};

#[derive(IntEnum, Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Exchange {
    Nasdaq = 0,
    Nyse = 1,
    Cboe = 2,
}

#[derive(BitPacked, Debug, Clone, Copy, PartialEq, Eq)]
#[packed(repr = u64)]
pub struct InstrumentId {
    #[field(start = 0, len = 4)]
    kind: u8,
    #[field(start = 4, len = 8)]
    exchange: Exchange,
    #[field(start = 12, len = 20)]
    symbol: u32,
    #[flag(63)]
    is_test: bool,
}

let id = InstrumentId {
    kind: 2,
    exchange: Exchange::Cboe,
    symbol: 123456,
    is_test: true,
};

// Pack to integer
let packed: u64 = id.pack().unwrap();

// Unpack from integer
let unpacked = InstrumentId::unpack(packed).unwrap();
assert_eq!(id, unpacked);

Snowflake-style IDs

use nexus_bits::BitPacked;

#[derive(BitPacked, Debug, Clone, Copy, PartialEq, Eq)]
#[packed(repr = u64)]
pub struct SnowflakeId {
    #[field(start = 0, len = 12)]
    sequence: u16,
    #[field(start = 12, len = 10)]
    worker: u16,
    #[field(start = 22, len = 42)]
    timestamp: u64,
}

Error Handling

Pack returns Result<T, FieldOverflow<T>> if a value exceeds its field capacity:

let id = InstrumentId {
    kind: 255,  // Exceeds 4-bit field (max 15)
    ..
};
assert!(id.pack().is_err());

Unpack for structs with IntEnum fields returns Result<Self, UnknownDiscriminant<T>>:

let raw: u64 = 0xFF;  // Invalid exchange value
let result = InstrumentId::unpack(raw);
assert!(result.is_err());

Structs with only primitive fields have infallible unpack returning Self directly.

Features

Feature Default Description
derive Yes Enables #[derive(BitPacked)] and #[derive(IntEnum)]

Disable derive macros for a minimal no_std build:

[dependencies]
nexus-bits = { version = "0.1", default-features = false }

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.