212 lines
4.5 KiB
Markdown
212 lines
4.5 KiB
Markdown
# std.bit_set
|
|
|
|
Densely stored sets of integers with efficient set operations (union, intersection, complement). Each integer gets a single bit.
|
|
|
|
## When to Use
|
|
|
|
- Track presence/absence of items from a known finite set
|
|
- Set operations (union, intersection, difference)
|
|
- Bit flags with variable size
|
|
- Compact storage when max value is known
|
|
|
|
## Variants
|
|
|
|
| Type | Size | Allocation |
|
|
|------|------|------------|
|
|
| `IntegerBitSet(N)` | Compile-time, N <= 128 | None (single integer) |
|
|
| `ArrayBitSet(usize, N)` | Compile-time, any N | None (array) |
|
|
| `StaticBitSet(N)` | Compile-time | Auto-selects Integer or Array |
|
|
| `DynamicBitSet` | Runtime | Allocator (managed) |
|
|
| `DynamicBitSetUnmanaged` | Runtime | Allocator (unmanaged) |
|
|
|
|
## Static Bit Set (Compile-Time Size)
|
|
|
|
```zig
|
|
const std = @import("std");
|
|
|
|
// StaticBitSet auto-selects best implementation
|
|
const Flags = std.StaticBitSet(64);
|
|
|
|
var flags = Flags.initEmpty();
|
|
flags.set(5);
|
|
flags.set(10);
|
|
|
|
if (flags.isSet(5)) {
|
|
// bit 5 is set
|
|
}
|
|
|
|
flags.unset(5);
|
|
flags.toggle(10);
|
|
|
|
const count = flags.count(); // number of set bits
|
|
```
|
|
|
|
## Dynamic Bit Set (Runtime Size)
|
|
|
|
```zig
|
|
var bits = try std.DynamicBitSet.initEmpty(allocator, 1000);
|
|
defer bits.deinit();
|
|
|
|
bits.set(42);
|
|
bits.set(100);
|
|
|
|
// Resize dynamically
|
|
try bits.resize(2000, false); // false = new bits are 0
|
|
try bits.resize(2000, true); // true = new bits are 1
|
|
|
|
// Clone
|
|
var copy = try bits.clone(allocator);
|
|
defer copy.deinit();
|
|
```
|
|
|
|
## Set Operations
|
|
|
|
```zig
|
|
var a = Flags.initEmpty();
|
|
var b = Flags.initEmpty();
|
|
a.set(1); a.set(2);
|
|
b.set(2); b.set(3);
|
|
|
|
// In-place operations (modify a)
|
|
a.setUnion(b); // a = a | b (bits in either)
|
|
a.setIntersection(b); // a = a & b (bits in both)
|
|
a.toggleSet(b); // a = a ^ b (flip bits that are in b)
|
|
|
|
// Return new set (pure functions)
|
|
const union_set = a.unionWith(b);
|
|
const intersection = a.intersectWith(b);
|
|
const xor_set = a.xorWith(b);
|
|
const diff = a.differenceWith(b); // a - b
|
|
const comp = a.complement(); // ~a
|
|
```
|
|
|
|
## Comparison
|
|
|
|
```zig
|
|
if (a.eql(b)) {
|
|
// same bits set
|
|
}
|
|
|
|
if (a.subsetOf(b)) {
|
|
// all bits in a are also in b
|
|
}
|
|
|
|
if (a.supersetOf(b)) {
|
|
// all bits in b are also in a
|
|
}
|
|
```
|
|
|
|
## Iteration
|
|
|
|
```zig
|
|
var flags = Flags.initEmpty();
|
|
flags.set(1); flags.set(5); flags.set(10);
|
|
|
|
// Iterate set bits (ascending order by default)
|
|
var it = flags.iterator(.{});
|
|
while (it.next()) |index| {
|
|
std.debug.print("bit {} is set\n", .{index});
|
|
}
|
|
|
|
// Iterate unset bits
|
|
var unset_it = flags.iterator(.{ .kind = .unset });
|
|
|
|
// Reverse order
|
|
var rev_it = flags.iterator(.{ .direction = .reverse });
|
|
```
|
|
|
|
## Range Operations
|
|
|
|
```zig
|
|
// Set/unset a range of bits
|
|
flags.setRangeValue(.{ .start = 10, .end = 20 }, true); // set bits 10-19
|
|
flags.setRangeValue(.{ .start = 10, .end = 20 }, false); // unset bits 10-19
|
|
```
|
|
|
|
## Find Operations
|
|
|
|
```zig
|
|
// Find first/last set bit
|
|
if (flags.findFirstSet()) |index| {
|
|
// index of lowest set bit
|
|
}
|
|
|
|
if (flags.findLastSet()) |index| {
|
|
// index of highest set bit
|
|
}
|
|
|
|
// Find and toggle (atomic-like)
|
|
if (flags.toggleFirstSet()) |index| {
|
|
// returns index and unsets the bit
|
|
}
|
|
```
|
|
|
|
## Toggle All
|
|
|
|
```zig
|
|
flags.toggleAll(); // flip every bit
|
|
```
|
|
|
|
## Unmanaged Dynamic BitSet
|
|
|
|
```zig
|
|
// For when you don't want to store the allocator
|
|
var bits: std.DynamicBitSetUnmanaged = .{};
|
|
try bits.resize(allocator, 100, false);
|
|
defer bits.deinit(allocator);
|
|
|
|
bits.set(50);
|
|
```
|
|
|
|
## Complete Example: Permission Flags
|
|
|
|
```zig
|
|
const std = @import("std");
|
|
|
|
const Permission = enum(u8) {
|
|
read = 0,
|
|
write = 1,
|
|
execute = 2,
|
|
delete = 3,
|
|
admin = 4,
|
|
};
|
|
|
|
const Permissions = std.StaticBitSet(8);
|
|
|
|
fn hasPermission(perms: Permissions, p: Permission) bool {
|
|
return perms.isSet(@intFromEnum(p));
|
|
}
|
|
|
|
fn grant(perms: *Permissions, p: Permission) void {
|
|
perms.set(@intFromEnum(p));
|
|
}
|
|
|
|
fn revoke(perms: *Permissions, p: Permission) void {
|
|
perms.unset(@intFromEnum(p));
|
|
}
|
|
|
|
pub fn main() void {
|
|
var user_perms = Permissions.initEmpty();
|
|
grant(&user_perms, .read);
|
|
grant(&user_perms, .write);
|
|
|
|
var admin_perms = Permissions.initFull();
|
|
|
|
// Check if user has all admin permissions
|
|
if (user_perms.subsetOf(admin_perms)) {
|
|
// user can do everything admin can (not in this case)
|
|
}
|
|
|
|
// Grant user all of admin's permissions
|
|
user_perms.setUnion(admin_perms);
|
|
}
|
|
```
|
|
|
|
## Notes
|
|
|
|
- `StaticBitSet` is zero-allocation, copyable by value
|
|
- `DynamicBitSet` requires allocation, call `deinit()`
|
|
- `initFull()` creates set with all bits set
|
|
- Iteration order is index order, not insertion order
|
|
- Use `std.enums.EnumSet` for enum-based bit flags
|