terminal/new: everything is OffsetBuf based

This commit is contained in:
Mitchell Hashimoto
2024-02-18 14:55:29 -08:00
parent 24354d8392
commit 334f651387
3 changed files with 161 additions and 97 deletions

View File

@ -41,6 +41,7 @@ const Wyhash = std.hash.Wyhash;
const Offset = @import("size.zig").Offset; const Offset = @import("size.zig").Offset;
const OffsetBuf = @import("size.zig").OffsetBuf; const OffsetBuf = @import("size.zig").OffsetBuf;
const getOffset = @import("size.zig").getOffset;
pub fn AutoOffsetHashMap(comptime K: type, comptime V: type) type { pub fn AutoOffsetHashMap(comptime K: type, comptime V: type) type {
return OffsetHashMap(K, V, AutoContext(K)); return OffsetHashMap(K, V, AutoContext(K));
@ -70,6 +71,7 @@ pub fn OffsetHashMap(
/// This is the pointer-based map that we're wrapping. /// This is the pointer-based map that we're wrapping.
pub const Unmanaged = HashMapUnmanaged(K, V, Context); pub const Unmanaged = HashMapUnmanaged(K, V, Context);
pub const Layout = Unmanaged.Layout;
/// This is the alignment that the base pointer must have. /// This is the alignment that the base pointer must have.
pub const base_align = Unmanaged.base_align; pub const base_align = Unmanaged.base_align;
@ -79,19 +81,21 @@ pub fn OffsetHashMap(
/// Returns the total size of the backing memory required for a /// Returns the total size of the backing memory required for a
/// HashMap with the given capacity. The base ptr must also be /// HashMap with the given capacity. The base ptr must also be
/// aligned to base_align. /// aligned to base_align.
pub fn bufferSize(cap: Unmanaged.Size) usize { pub fn layout(cap: Unmanaged.Size) Layout {
const layout = Unmanaged.layoutForCapacity(cap); return Unmanaged.layoutForCapacity(cap);
return layout.total_size;
} }
/// Initialize a new HashMap with the given capacity and backing /// Initialize a new HashMap with the given capacity and backing
/// memory. The backing memory must be aligned to base_align. /// memory. The backing memory must be aligned to base_align.
pub fn init(cap: Unmanaged.Size, buf: []u8) Self { pub fn init(buf: OffsetBuf, l: Layout) Self {
assert(@intFromPtr(buf.ptr) % base_align == 0); assert(@intFromPtr(buf.start()) % base_align == 0);
const m = Unmanaged.init(cap, buf); const m = Unmanaged.init(buf, l);
const offset = @intFromPtr(m.metadata.?) - @intFromPtr(buf.ptr); return .{ .metadata = getOffset(
return .{ .metadata = .{ .offset = @intCast(offset) } }; Unmanaged.Metadata,
buf,
@ptrCast(m.metadata.?),
) };
} }
/// Returns the pointer-based map from a base pointer. /// Returns the pointer-based map from a base pointer.
@ -283,21 +287,19 @@ fn HashMapUnmanaged(
/// Initialize a hash map with a given capacity and a buffer. The /// Initialize a hash map with a given capacity and a buffer. The
/// buffer must fit within the size defined by `layoutForCapacity`. /// buffer must fit within the size defined by `layoutForCapacity`.
pub fn init(new_capacity: Size, buf: []u8) Self { pub fn init(buf: OffsetBuf, layout: Layout) Self {
assert(@intFromPtr(buf.ptr) % base_align == 0); assert(@intFromPtr(buf.start()) % base_align == 0);
const layout = layoutForCapacity(new_capacity);
assert(buf.len >= layout.total_size);
// Get all our main pointers // Get all our main pointers
const metadata_ptr: [*]Metadata = @ptrFromInt(@intFromPtr(buf.ptr) + @sizeOf(Header)); const metadata_ptr: [*]Metadata = @ptrCast(buf.start() + @sizeOf(Header));
// Build our map // Build our map
var map: Self = .{ .metadata = metadata_ptr }; var map: Self = .{ .metadata = metadata_ptr };
const hdr = map.header(); const hdr = map.header();
hdr.capacity = new_capacity; hdr.capacity = layout.capacity;
hdr.size = 0; hdr.size = 0;
if (@sizeOf([*]K) != 0) hdr.keys = .{ .offset = @intCast(layout.keys_start) }; if (@sizeOf([*]K) != 0) hdr.keys = buf.member(K, layout.keys_start);
if (@sizeOf([*]V) != 0) hdr.values = .{ .offset = @intCast(layout.vals_start) }; if (@sizeOf([*]V) != 0) hdr.values = buf.member(V, layout.vals_start);
map.initMetadatas(); map.initMetadatas();
return map; return map;
@ -853,7 +855,7 @@ fn HashMapUnmanaged(
/// The actual size may be able to fit more than the given capacity /// The actual size may be able to fit more than the given capacity
/// because capacity is rounded up to the next power of two. This is /// because capacity is rounded up to the next power of two. This is
/// a design requirement for this hash map implementation. /// a design requirement for this hash map implementation.
fn layoutForCapacity(new_capacity: Size) Layout { pub fn layoutForCapacity(new_capacity: Size) Layout {
assert(std.math.isPowerOfTwo(new_capacity)); assert(std.math.isPowerOfTwo(new_capacity));
// Pack our metadata, keys, and values. // Pack our metadata, keys, and values.
@ -893,10 +895,11 @@ test "HashMap basic usage" {
const alloc = testing.allocator; const alloc = testing.allocator;
const cap = 16; const cap = 16;
const buf = try alloc.alloc(u8, Map.layoutForCapacity(cap).total_size); const layout = Map.layoutForCapacity(cap);
const buf = try alloc.alignedAlloc(u8, Map.base_align, layout.total_size);
defer alloc.free(buf); defer alloc.free(buf);
var map = Map.init(cap, buf); var map = Map.init(OffsetBuf.init(buf), layout);
const count = 5; const count = 5;
var i: u32 = 0; var i: u32 = 0;
@ -927,9 +930,10 @@ test "HashMap ensureTotalCapacity" {
const cap = 32; const cap = 32;
const alloc = testing.allocator; const alloc = testing.allocator;
const buf = try alloc.alloc(u8, Map.layoutForCapacity(cap).total_size); const layout = Map.layoutForCapacity(cap);
const buf = try alloc.alignedAlloc(u8, Map.base_align, layout.total_size);
defer alloc.free(buf); defer alloc.free(buf);
var map = Map.init(cap, buf); var map = Map.init(OffsetBuf.init(buf), layout);
const initial_capacity = map.capacity(); const initial_capacity = map.capacity();
try testing.expect(initial_capacity >= 20); try testing.expect(initial_capacity >= 20);
@ -946,9 +950,10 @@ test "HashMap ensureUnusedCapacity with tombstones" {
const cap = 32; const cap = 32;
const alloc = testing.allocator; const alloc = testing.allocator;
const buf = try alloc.alloc(u8, Map.layoutForCapacity(cap).total_size); const layout = Map.layoutForCapacity(cap);
const buf = try alloc.alignedAlloc(u8, Map.base_align, layout.total_size);
defer alloc.free(buf); defer alloc.free(buf);
var map = Map.init(cap, buf); var map = Map.init(OffsetBuf.init(buf), layout);
var i: i32 = 0; var i: i32 = 0;
while (i < 100) : (i += 1) { while (i < 100) : (i += 1) {
@ -963,9 +968,10 @@ test "HashMap clearRetainingCapacity" {
const cap = 16; const cap = 16;
const alloc = testing.allocator; const alloc = testing.allocator;
const buf = try alloc.alloc(u8, Map.layoutForCapacity(cap).total_size); const layout = Map.layoutForCapacity(cap);
const buf = try alloc.alignedAlloc(u8, Map.base_align, layout.total_size);
defer alloc.free(buf); defer alloc.free(buf);
var map = Map.init(cap, buf); var map = Map.init(OffsetBuf.init(buf), layout);
map.clearRetainingCapacity(); map.clearRetainingCapacity();
@ -993,9 +999,10 @@ test "HashMap ensureTotalCapacity with existing elements" {
const cap = Map.minimal_capacity; const cap = Map.minimal_capacity;
const alloc = testing.allocator; const alloc = testing.allocator;
const buf = try alloc.alloc(u8, Map.layoutForCapacity(cap).total_size); const layout = Map.layoutForCapacity(cap);
const buf = try alloc.alignedAlloc(u8, Map.base_align, layout.total_size);
defer alloc.free(buf); defer alloc.free(buf);
var map = Map.init(cap, buf); var map = Map.init(OffsetBuf.init(buf), layout);
try map.put(0, 0); try map.put(0, 0);
try expectEqual(map.count(), 1); try expectEqual(map.count(), 1);
@ -1011,9 +1018,10 @@ test "HashMap remove" {
const cap = 32; const cap = 32;
const alloc = testing.allocator; const alloc = testing.allocator;
const buf = try alloc.alloc(u8, Map.layoutForCapacity(cap).total_size); const layout = Map.layoutForCapacity(cap);
const buf = try alloc.alignedAlloc(u8, Map.base_align, layout.total_size);
defer alloc.free(buf); defer alloc.free(buf);
var map = Map.init(cap, buf); var map = Map.init(OffsetBuf.init(buf), layout);
var i: u32 = 0; var i: u32 = 0;
while (i < 16) : (i += 1) { while (i < 16) : (i += 1) {
@ -1048,9 +1056,10 @@ test "HashMap reverse removes" {
const cap = 32; const cap = 32;
const alloc = testing.allocator; const alloc = testing.allocator;
const buf = try alloc.alloc(u8, Map.layoutForCapacity(cap).total_size); const layout = Map.layoutForCapacity(cap);
const buf = try alloc.alignedAlloc(u8, Map.base_align, layout.total_size);
defer alloc.free(buf); defer alloc.free(buf);
var map = Map.init(cap, buf); var map = Map.init(OffsetBuf.init(buf), layout);
var i: u32 = 0; var i: u32 = 0;
while (i < 16) : (i += 1) { while (i < 16) : (i += 1) {
@ -1075,9 +1084,10 @@ test "HashMap multiple removes on same metadata" {
const cap = 32; const cap = 32;
const alloc = testing.allocator; const alloc = testing.allocator;
const buf = try alloc.alloc(u8, Map.layoutForCapacity(cap).total_size); const layout = Map.layoutForCapacity(cap);
const buf = try alloc.alignedAlloc(u8, Map.base_align, layout.total_size);
defer alloc.free(buf); defer alloc.free(buf);
var map = Map.init(cap, buf); var map = Map.init(OffsetBuf.init(buf), layout);
var i: u32 = 0; var i: u32 = 0;
while (i < 16) : (i += 1) { while (i < 16) : (i += 1) {
@ -1117,9 +1127,10 @@ test "HashMap put and remove loop in random order" {
const cap = 64; const cap = 64;
const alloc = testing.allocator; const alloc = testing.allocator;
const buf = try alloc.alloc(u8, Map.layoutForCapacity(cap).total_size); const layout = Map.layoutForCapacity(cap);
const buf = try alloc.alignedAlloc(u8, Map.base_align, layout.total_size);
defer alloc.free(buf); defer alloc.free(buf);
var map = Map.init(cap, buf); var map = Map.init(OffsetBuf.init(buf), layout);
var keys = std.ArrayList(u32).init(alloc); var keys = std.ArrayList(u32).init(alloc);
defer keys.deinit(); defer keys.deinit();
@ -1154,9 +1165,10 @@ test "HashMap put" {
const cap = 32; const cap = 32;
const alloc = testing.allocator; const alloc = testing.allocator;
const buf = try alloc.alloc(u8, Map.layoutForCapacity(cap).total_size); const layout = Map.layoutForCapacity(cap);
const buf = try alloc.alignedAlloc(u8, Map.base_align, layout.total_size);
defer alloc.free(buf); defer alloc.free(buf);
var map = Map.init(cap, buf); var map = Map.init(OffsetBuf.init(buf), layout);
var i: u32 = 0; var i: u32 = 0;
while (i < 16) : (i += 1) { while (i < 16) : (i += 1) {
@ -1184,13 +1196,10 @@ test "HashMap put full load" {
const cap = 16; const cap = 16;
const alloc = testing.allocator; const alloc = testing.allocator;
const buf = try alloc.alignedAlloc( const layout = Map.layoutForCapacity(cap);
u8, const buf = try alloc.alignedAlloc(u8, Map.base_align, layout.total_size);
Map.base_align,
Map.layoutForCapacity(cap).total_size,
);
defer alloc.free(buf); defer alloc.free(buf);
var map = Map.init(cap, buf); var map = Map.init(OffsetBuf.init(buf), layout);
for (0..cap) |i| try map.put(i, i); for (0..cap) |i| try map.put(i, i);
for (0..cap) |i| try expectEqual(map.get(i).?, i); for (0..cap) |i| try expectEqual(map.get(i).?, i);
@ -1203,9 +1212,10 @@ test "HashMap putAssumeCapacity" {
const cap = 32; const cap = 32;
const alloc = testing.allocator; const alloc = testing.allocator;
const buf = try alloc.alloc(u8, Map.layoutForCapacity(cap).total_size); const layout = Map.layoutForCapacity(cap);
const buf = try alloc.alignedAlloc(u8, Map.base_align, layout.total_size);
defer alloc.free(buf); defer alloc.free(buf);
var map = Map.init(cap, buf); var map = Map.init(OffsetBuf.init(buf), layout);
var i: u32 = 0; var i: u32 = 0;
while (i < 20) : (i += 1) { while (i < 20) : (i += 1) {
@ -1237,9 +1247,10 @@ test "HashMap repeat putAssumeCapacity/remove" {
const cap = 32; const cap = 32;
const alloc = testing.allocator; const alloc = testing.allocator;
const buf = try alloc.alloc(u8, Map.layoutForCapacity(cap).total_size); const layout = Map.layoutForCapacity(cap);
const buf = try alloc.alignedAlloc(u8, Map.base_align, layout.total_size);
defer alloc.free(buf); defer alloc.free(buf);
var map = Map.init(cap, buf); var map = Map.init(OffsetBuf.init(buf), layout);
const limit = cap; const limit = cap;
@ -1272,9 +1283,10 @@ test "HashMap getOrPut" {
const cap = 32; const cap = 32;
const alloc = testing.allocator; const alloc = testing.allocator;
const buf = try alloc.alloc(u8, Map.layoutForCapacity(cap).total_size); const layout = Map.layoutForCapacity(cap);
const buf = try alloc.alignedAlloc(u8, Map.base_align, layout.total_size);
defer alloc.free(buf); defer alloc.free(buf);
var map = Map.init(cap, buf); var map = Map.init(OffsetBuf.init(buf), layout);
var i: u32 = 0; var i: u32 = 0;
while (i < 10) : (i += 1) { while (i < 10) : (i += 1) {
@ -1300,9 +1312,10 @@ test "HashMap basic hash map usage" {
const cap = 32; const cap = 32;
const alloc = testing.allocator; const alloc = testing.allocator;
const buf = try alloc.alloc(u8, Map.layoutForCapacity(cap).total_size); const layout = Map.layoutForCapacity(cap);
const buf = try alloc.alignedAlloc(u8, Map.base_align, layout.total_size);
defer alloc.free(buf); defer alloc.free(buf);
var map = Map.init(cap, buf); var map = Map.init(OffsetBuf.init(buf), layout);
try testing.expect((try map.fetchPut(1, 11)) == null); try testing.expect((try map.fetchPut(1, 11)) == null);
try testing.expect((try map.fetchPut(2, 22)) == null); try testing.expect((try map.fetchPut(2, 22)) == null);
@ -1350,9 +1363,10 @@ test "HashMap ensureUnusedCapacity" {
const cap = 64; const cap = 64;
const alloc = testing.allocator; const alloc = testing.allocator;
const buf = try alloc.alloc(u8, Map.layoutForCapacity(cap).total_size); const layout = Map.layoutForCapacity(cap);
const buf = try alloc.alignedAlloc(u8, Map.base_align, layout.total_size);
defer alloc.free(buf); defer alloc.free(buf);
var map = Map.init(cap, buf); var map = Map.init(OffsetBuf.init(buf), layout);
try map.ensureUnusedCapacity(32); try map.ensureUnusedCapacity(32);
try testing.expectError(error.OutOfMemory, map.ensureUnusedCapacity(cap + 1)); try testing.expectError(error.OutOfMemory, map.ensureUnusedCapacity(cap + 1));
@ -1363,9 +1377,10 @@ test "HashMap removeByPtr" {
const cap = 64; const cap = 64;
const alloc = testing.allocator; const alloc = testing.allocator;
const buf = try alloc.alloc(u8, Map.layoutForCapacity(cap).total_size); const layout = Map.layoutForCapacity(cap);
const buf = try alloc.alignedAlloc(u8, Map.base_align, layout.total_size);
defer alloc.free(buf); defer alloc.free(buf);
var map = Map.init(cap, buf); var map = Map.init(OffsetBuf.init(buf), layout);
var i: i32 = undefined; var i: i32 = undefined;
i = 0; i = 0;
@ -1393,9 +1408,10 @@ test "HashMap removeByPtr 0 sized key" {
const cap = 64; const cap = 64;
const alloc = testing.allocator; const alloc = testing.allocator;
const buf = try alloc.alloc(u8, Map.layoutForCapacity(cap).total_size); const layout = Map.layoutForCapacity(cap);
const buf = try alloc.alignedAlloc(u8, Map.base_align, layout.total_size);
defer alloc.free(buf); defer alloc.free(buf);
var map = Map.init(cap, buf); var map = Map.init(OffsetBuf.init(buf), layout);
try map.put(0, 0); try map.put(0, 0);
@ -1416,9 +1432,10 @@ test "HashMap repeat fetchRemove" {
const cap = 64; const cap = 64;
const alloc = testing.allocator; const alloc = testing.allocator;
const buf = try alloc.alloc(u8, Map.layoutForCapacity(cap).total_size); const layout = Map.layoutForCapacity(cap);
const buf = try alloc.alignedAlloc(u8, Map.base_align, layout.total_size);
defer alloc.free(buf); defer alloc.free(buf);
var map = Map.init(cap, buf); var map = Map.init(OffsetBuf.init(buf), layout);
map.putAssumeCapacity(0, {}); map.putAssumeCapacity(0, {});
map.putAssumeCapacity(1, {}); map.putAssumeCapacity(1, {});
@ -1440,13 +1457,13 @@ test "HashMap repeat fetchRemove" {
test "OffsetHashMap basic usage" { test "OffsetHashMap basic usage" {
const OffsetMap = AutoOffsetHashMap(u32, u32); const OffsetMap = AutoOffsetHashMap(u32, u32);
const cap = 16;
const alloc = testing.allocator; const alloc = testing.allocator;
const cap = 16; const layout = OffsetMap.layout(cap);
const buf = try alloc.alloc(u8, OffsetMap.Unmanaged.layoutForCapacity(cap).total_size); const buf = try alloc.alignedAlloc(u8, OffsetMap.base_align, layout.total_size);
defer alloc.free(buf); defer alloc.free(buf);
var offset_map = OffsetMap.init(OffsetBuf.init(buf), layout);
var offset_map = OffsetMap.init(cap, buf);
var map = offset_map.map(buf.ptr); var map = offset_map.map(buf.ptr);
const count = 5; const count = 5;
@ -1475,13 +1492,13 @@ test "OffsetHashMap basic usage" {
test "OffsetHashMap remake map" { test "OffsetHashMap remake map" {
const OffsetMap = AutoOffsetHashMap(u32, u32); const OffsetMap = AutoOffsetHashMap(u32, u32);
const cap = 16;
const alloc = testing.allocator; const alloc = testing.allocator;
const cap = 16; const layout = OffsetMap.layout(cap);
const buf = try alloc.alloc(u8, OffsetMap.Unmanaged.layoutForCapacity(cap).total_size); const buf = try alloc.alignedAlloc(u8, OffsetMap.base_align, layout.total_size);
defer alloc.free(buf); defer alloc.free(buf);
var offset_map = OffsetMap.init(OffsetBuf.init(buf), layout);
var offset_map = OffsetMap.init(cap, buf);
{ {
var map = offset_map.map(buf.ptr); var map = offset_map.map(buf.ptr);

View File

@ -36,16 +36,23 @@ pub fn Offset(comptime T: type) type {
}; };
} }
/// A type that is used to intitialize offset-based structures. /// Represents a buffer that is offset from some base pointer.
/// This allows for tracking the base pointer, the offset into /// Offset-based structures should use this as their initialization
/// the base pointer we're starting, and the memory layout of /// parameter so that they can know what segment of memory they own
/// components. /// while at the same time initializing their offset fields to be
/// against the true base.
///
/// The term "true base" is used to describe the base address of
/// the allocation, which i.e. can include memory that you do NOT
/// own and is used by some other structures. All offsets are against
/// this "true base" so that to determine addresses structures don't
/// need to add up all the intermediary offsets.
pub const OffsetBuf = struct { pub const OffsetBuf = struct {
/// The true base pointer to the backing memory. This is /// The true base pointer to the backing memory. This is
/// "byte zero" of the allocation. This plus the offset make /// "byte zero" of the allocation. This plus the offset make
/// it easy to pass in the base pointer in all usage to this /// it easy to pass in the base pointer in all usage to this
/// structure and the offsets are correct. /// structure and the offsets are correct.
base: [*]u8 = 0, base: [*]u8,
/// Offset from base where the beginning of /this/ data /// Offset from base where the beginning of /this/ data
/// structure is located. We use this so that we can slowly /// structure is located. We use this so that we can slowly
@ -53,11 +60,45 @@ pub const OffsetBuf = struct {
/// have the base pointer sent into functions be the true base. /// have the base pointer sent into functions be the true base.
offset: usize = 0, offset: usize = 0,
pub fn offsetBase(comptime T: type, self: OffsetBuf) [*]T { /// Initialize a zero-offset buffer from a base.
pub fn init(base: anytype) OffsetBuf {
return initOffset(base, 0);
}
/// Initialize from some base pointer and offset.
pub fn initOffset(base: anytype, offset: usize) OffsetBuf {
return .{
.base = @ptrFromInt(intFromBase(base)),
.offset = offset,
};
}
/// The base address for the start of the data for the user
/// of this OffsetBuf. This is where your data structure should
/// begin; anything before this is NOT your memory.
pub fn start(self: OffsetBuf) [*]u8 {
const ptr = self.base + self.offset; const ptr = self.base + self.offset;
assert(@intFromPtr(ptr) % @alignOf(T) == 0);
return @ptrCast(ptr); return @ptrCast(ptr);
} }
/// Returns an Offset calculation for some child member of
/// your struct. The offset is against the true base pointer
/// so that future callers can pass that in as the base.
pub fn member(
self: OffsetBuf,
comptime T: type,
len: usize,
) Offset(T) {
return .{ .offset = @intCast(self.offset + len) };
}
/// Add an offset to the current offset.
pub fn add(self: OffsetBuf, offset: usize) OffsetBuf {
return .{
.base = self.base,
.offset = self.offset + offset,
};
}
}; };
/// Get the offset for a given type from some base pointer to the /// Get the offset for a given type from some base pointer to the
@ -74,7 +115,8 @@ pub fn getOffset(
} }
fn intFromBase(base: anytype) usize { fn intFromBase(base: anytype) usize {
return switch (@typeInfo(@TypeOf(base))) { const T = @TypeOf(base);
return switch (@typeInfo(T)) {
.Pointer => |v| switch (v.size) { .Pointer => |v| switch (v.size) {
.One, .One,
.Many, .Many,
@ -84,7 +126,11 @@ fn intFromBase(base: anytype) usize {
.Slice => @intFromPtr(base.ptr), .Slice => @intFromPtr(base.ptr),
}, },
else => @compileError("invalid base type"), else => switch (T) {
OffsetBuf => @intFromPtr(base.base),
else => @compileError("invalid base type"),
},
}; };
} }

View File

@ -4,6 +4,7 @@ const color = @import("../color.zig");
const sgr = @import("../sgr.zig"); const sgr = @import("../sgr.zig");
const size = @import("size.zig"); const size = @import("size.zig");
const Offset = size.Offset; const Offset = size.Offset;
const OffsetBuf = size.OffsetBuf;
const hash_map = @import("hash_map.zig"); const hash_map = @import("hash_map.zig");
const AutoOffsetHashMap = hash_map.AutoOffsetHashMap; const AutoOffsetHashMap = hash_map.AutoOffsetHashMap;
@ -49,6 +50,8 @@ pub const Style = struct {
/// A set of styles. /// A set of styles.
pub const Set = struct { pub const Set = struct {
pub const base_align = @max(MetadataMap.base_align, IdMap.base_align);
/// The mapping of a style to associated metadata. This is /// The mapping of a style to associated metadata. This is
/// the map that contains the actual style definitions /// the map that contains the actual style definitions
/// (in the form of the key). /// (in the form of the key).
@ -74,42 +77,40 @@ pub const Set = struct {
/// determine how much memory to allocate, and the layout must /// determine how much memory to allocate, and the layout must
/// be used to initialize the set so that the set knows all /// be used to initialize the set so that the set knows all
/// the offsets for the various buffers. /// the offsets for the various buffers.
pub fn layoutForCapacity(base: usize, cap: usize) Layout { pub fn layout(cap: usize) Layout {
const md_start = std.mem.alignForward(usize, base, MetadataMap.base_align); const md_layout = MetadataMap.layout(@intCast(cap));
const md_end = md_start + MetadataMap.bufferSize(@intCast(cap)); const md_start = 0;
const md_end = md_start + md_layout.total_size;
const id_layout = IdMap.layout(@intCast(cap));
const id_start = std.mem.alignForward(usize, md_end, IdMap.base_align); const id_start = std.mem.alignForward(usize, md_end, IdMap.base_align);
const id_end = id_start + IdMap.bufferSize(@intCast(cap)); const id_end = id_start + id_layout.total_size;
const total_size = id_end - base; const total_size = id_end;
return .{ return .{
.cap = cap,
.md_start = md_start, .md_start = md_start,
.md_layout = md_layout,
.id_start = id_start, .id_start = id_start,
.id_layout = id_layout,
.total_size = total_size, .total_size = total_size,
}; };
} }
pub const Layout = struct { pub const Layout = struct {
cap: usize,
md_start: usize, md_start: usize,
md_layout: MetadataMap.Layout,
id_start: usize, id_start: usize,
id_layout: IdMap.Layout,
total_size: usize, total_size: usize,
}; };
pub fn init(base: []u8, layout: Layout) Set { pub fn init(base: OffsetBuf, l: Layout) Set {
assert(base.len >= layout.total_size); const styles_buf = base.add(l.md_start);
const id_buf = base.add(l.id_start);
var styles = MetadataMap.init(@intCast(layout.cap), base[layout.md_start..]);
styles.metadata.offset += @intCast(layout.md_start);
var id_map = IdMap.init(@intCast(layout.cap), base[layout.id_start..]);
id_map.metadata.offset += @intCast(layout.id_start);
return .{ return .{
.styles = styles, .styles = MetadataMap.init(styles_buf, l.md_layout),
.id_map = id_map, .id_map = IdMap.init(id_buf, l.id_layout),
}; };
} }
@ -179,13 +180,13 @@ test {
test "Set basic usage" { test "Set basic usage" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;
const layout = Set.layoutForCapacity(0, 16); const layout = Set.layout(16);
const buf = try alloc.alloc(u8, layout.total_size); const buf = try alloc.alignedAlloc(u8, Set.base_align, layout.total_size);
defer alloc.free(buf); defer alloc.free(buf);
const style: Style = .{ .flags = .{ .bold = true } }; const style: Style = .{ .flags = .{ .bold = true } };
var set = Set.init(buf, layout); var set = Set.init(OffsetBuf.init(buf), layout);
// Upsert // Upsert
const meta = try set.upsert(buf, style); const meta = try set.upsert(buf, style);