bitkit
Bit manipulation that reads like the comment you'd have to write next to it.
// before // after
& ! up?
& 0b1111 new.extract?
& 1 == 1 new.has_bit?
x & x.wrapping_neg new.isolate_lowest_set_bit
| encode_2d
unsafe new.gather
One Bits<T> newtype. One Result<_, BitError> for everything fallible.
No unsafe in your code. No panics on bad indices. And — on x86-64 BMI2 —
the gather / scatter methods above lower to a single hardware instruction.
Highlights
- One newtype, one error model, one range convention. Every bit
operation is a method on
Bits<T>returningResult<_, BitError>, accepting anyRange<u32>or(u32, u32)tuple. - Hardware-accelerated
gather/scatter(PEXT/PDEP on x86-64 BMI2) with a portable SWAR fallback. The safe wrapper measures within 0.2 ns of the raw unsafebitintrcrate on capable hosts. - Morton (Z-order) curves in a dedicated module — 83× faster than
morton-encodingon Intel BMI2, 8.4× faster even on portable SWAR. See BENCHMARKS.md. no_std, zero runtime dependencies, nounsafein the default build path (one contained internal module uses BMI2 intrinsics).const fneverywhere it can be.- MSRV 1.74.
Install
[]
= "3"
no_std:
= { = "3", = false }
What it looks like
Bit fields
use *;
let header = new;
let version = header.extract?; // -> Bits::<u8>::new(0b0100)
let ihl = header.extract?; // -> Bits::<u8>::new(0b0101)
let packed = new
.insert?
.insert?; // -> 0b0100_0101
Iterators
let mask = new;
let indexes: = mask.set_bits.collect; // [0, 1, 3]
// All subsets of a bitmask, in O(2^popcount) time
for subset in mask.submasks
Binary protocol parsing
let buf = ;
let len = read_u16_be?; // 0x1234
let flags = new.extract?; // top 3 bits
Hardware-accelerated bit gather (PEXT)
let v = new;
let mask = new;
let packed = v.gather; // -> Bits::<u32>::new(0b1111)
// One PEXT instruction on BMI2; portable SWAR otherwise.
Morton (Z-order) curves
let z = encode_2d; // -> 37247404
assert_eq!;
Flag sets
const READ: u32 = 1 << 0;
const WRITE: u32 = 1 << 1;
let mut f = empty;
f.enable;
assert!;
What this crate is not
- Not
bitvec. It doesn't store arbitrary-length bit sequences or expose bit-precise references. - Not
bitflags.Flags<T>is for ad-hoc manipulation where bit positions are data; for fixed named sets, usebitflags. - Not a serialization framework. For declarative binary parsing, use
dekuon top.
Modules
| module | purpose |
|---|---|
[Bits<T>] |
The primary type: bit ops, masks, fields, popcount, gather/scatter. |
[Flags<T>] |
Generic flag-set newtype. |
[align] |
Power-of-two alignment over usize. |
[bytes] |
Read/write integers from &[u8] with explicit endianness. |
[morton] |
Z-order curve encode / decode (2D, 3D). |
[format] |
Allocation-free grouped-binary Display. |
[explain] (feature) |
Educational metadata for common bit hacks. |
Feature flags
| flag | default | purpose |
|---|---|---|
std |
yes | std::error::Error for BitError. |
alloc |
yes | Allocation-backed helpers in optional modules. |
explain |
no | Educational metadata for common bit hacks. |
runtime-detect |
no | Use is_x86_feature_detected! at runtime to pick the BMI2 path. |
Examples
Performance
| benchmark | x86 BMI2 | ARM SWAR |
|---|---|---|
Bits::gather / scatter vs bitintr (raw unsafe) |
tied | 1.6× faster |
bitkit::morton::encode_2d vs morton-encoding crate |
83× faster | 8.4× faster |
Bits::extract, Bits::count_ones vs inline |
tied | tied |
Bits::set_bit vs bitvec::set |
2.0× faster | 1.6× faster |
Full table, methodology, and reproduction recipe in BENCHMARKS.md.
License
MIT — see LICENSE or https://opensource.org/licenses/MIT.