111 lines
3.7 KiB
Zig
111 lines
3.7 KiB
Zig
const std = @import("std");
|
|
const assert = std.debug.assert;
|
|
|
|
pub const Allocator = std.mem.Allocator;
|
|
pub const ArrayList = std.ArrayListUnmanaged;
|
|
pub const AutoHashMap = std.AutoHashMapUnmanaged;
|
|
pub const HashMap = std.HashMapUnmanaged;
|
|
pub const SegmentedList = std.SegmentedList;
|
|
pub const StringHashMap = std.StringHashMapUnmanaged;
|
|
pub const StringArrayHashMap = std.StringArrayHashMapUnmanaged;
|
|
|
|
pub fn BlockList(comptime T: type) type {
|
|
const item_count = 64;
|
|
const Block = struct {
|
|
items: [item_count]T = undefined,
|
|
bitset: Bitset = Bitset.initEmpty(),
|
|
|
|
const Bitset = std.StaticBitSet(item_count);
|
|
|
|
fn allocateIndex(block: *@This()) !u6 {
|
|
if (block.bitset.mask != std.math.maxInt(@TypeOf(block.bitset.mask))) {
|
|
const index = @ctz(~block.bitset.mask);
|
|
block.bitset.set(index);
|
|
return @intCast(index);
|
|
} else {
|
|
return error.OutOfMemory;
|
|
}
|
|
}
|
|
};
|
|
|
|
return struct {
|
|
blocks: ArrayList(Block) = .{},
|
|
len: usize = 0,
|
|
first_block: u32 = 0,
|
|
|
|
const List = @This();
|
|
|
|
pub const Index = packed struct(u32) {
|
|
valid: bool = true,
|
|
index: u6,
|
|
block: u25,
|
|
|
|
pub const invalid = Index{
|
|
.valid = false,
|
|
.index = 0,
|
|
.block = 0,
|
|
};
|
|
};
|
|
|
|
pub fn get(list: *List, index: Index) *T {
|
|
assert(index.valid);
|
|
return &list.blocks.items[index.block].items[index.index];
|
|
}
|
|
|
|
pub fn append(list: *List, allocator: Allocator, element: T) !Index {
|
|
try list.ensureCapacity(allocator, list.len + 1);
|
|
const max_allocation = list.blocks.items.len * item_count;
|
|
if (list.len < max_allocation) {
|
|
// Follow the guess
|
|
if (list.blocks.items[list.first_block].allocateIndex()) |index| {
|
|
list.blocks.items[list.first_block].items[index] = element;
|
|
return .{
|
|
.index = index,
|
|
.block = @intCast(list.first_block),
|
|
};
|
|
} else |_| {
|
|
@panic("TODO");
|
|
}
|
|
} else {
|
|
const block_index = list.blocks.items.len;
|
|
const new_block = list.blocks.addOneAssumeCapacity();
|
|
const index = new_block.allocateIndex() catch unreachable;
|
|
new_block.items[index] = element;
|
|
return .{
|
|
.index = index,
|
|
.block = @intCast(block_index),
|
|
};
|
|
}
|
|
}
|
|
|
|
pub fn ensureCapacity(list: *List, allocator: Allocator, new_capacity: usize) !void {
|
|
const max_allocation = list.blocks.items.len * item_count;
|
|
if (max_allocation < new_capacity) {
|
|
const block_count = new_capacity / item_count + @intFromBool(new_capacity % item_count != 0);
|
|
try list.blocks.ensureTotalCapacity(allocator, block_count);
|
|
}
|
|
}
|
|
|
|
test "Bitset index allocation" {
|
|
const expect = std.testing.expect;
|
|
var block = Block{};
|
|
for (0..item_count) |expected_index| {
|
|
const new_index = try block.allocateIndex();
|
|
try expect(new_index == expected_index);
|
|
}
|
|
|
|
_ = block.allocateIndex() catch return;
|
|
|
|
return error.TestUnexpectedResult;
|
|
}
|
|
};
|
|
}
|
|
|
|
pub fn enumFromString(comptime E: type, string: []const u8) ?E {
|
|
return inline for (@typeInfo(E).Enum.fields) |enum_field| {
|
|
if (std.mem.eql(u8, string, enum_field.name)) {
|
|
break @field(E, enum_field.name);
|
|
}
|
|
} else null;
|
|
}
|