terminal: hash map and ref counted set can initialize from zeroed memory

This commit is contained in:
Mitchell Hashimoto
2026-09-03 09:13:36 -07:00
parent ffe015ee55
commit c0a4f80d80
2 changed files with 52 additions and 4 deletions

View File

@@ -152,8 +152,19 @@ pub fn OffsetHashMap(
/// memory. The backing memory must be aligned to base_align.
pub fn init(buf: OffsetBuf, l: Layout) Self {
assert(base_align.check(@intFromPtr(buf.start())));
return fromUnmanaged(buf, Unmanaged.init(buf, l));
}
const m = Unmanaged.init(buf, l);
/// Like `init`, but for backing memory that the caller guarantees
/// is already zero-filled (e.g. fresh OS pages). Only the header
/// is written: all-zero slot metadata already means every slot is
/// free, so the metadata array is left untouched.
pub fn initAssumeZeroed(buf: OffsetBuf, l: Layout) Self {
assert(base_align.check(@intFromPtr(buf.start())));
return fromUnmanaged(buf, Unmanaged.initAssumeZeroed(buf, l));
}
fn fromUnmanaged(buf: OffsetBuf, m: Unmanaged) Self {
return .{ .metadata = getOffset(
Unmanaged.Metadata,
buf,
@@ -344,6 +355,17 @@ fn HashMapUnmanaged(
/// Initialize a hash map with a given capacity and a buffer. The
/// buffer must fit within the size defined by `layoutForCapacity`.
pub fn init(buf: OffsetBuf, layout: Layout) Self {
var map = initAssumeZeroed(buf, layout);
map.initMetadatas();
return map;
}
/// Like `init`, but for a buffer that the caller guarantees is
/// already zero-filled. Only the header is written: an all-zero
/// metadata byte is a free slot (see `Metadata.isFree`), so the
/// slot metadata is left untouched. Behavior is undefined if the
/// metadata region is not zero.
pub fn initAssumeZeroed(buf: OffsetBuf, layout: Layout) Self {
assert(base_align.check(@intFromPtr(buf.start())));
// Get all our main pointers
@@ -351,13 +373,12 @@ fn HashMapUnmanaged(
const metadata_ptr: [*]Metadata = @ptrCast(metadata_buf.start());
// Build our map
var map: Self = .{ .metadata = metadata_ptr };
const map: Self = .{ .metadata = metadata_ptr };
const hdr = map.header();
hdr.capacity = layout.capacity;
hdr.size = 0;
if (@sizeOf([*]K) != 0) hdr.keys = metadata_buf.member(K, layout.keys_start);
if (@sizeOf([*]V) != 0) hdr.values = metadata_buf.member(V, layout.vals_start);
map.initMetadatas();
return map;
}

View File

@@ -212,6 +212,30 @@ pub fn RefCountedSet(
@memset(table.ptr(base)[0..l.table_cap], 0);
@memset(items.ptr(base)[0..l.cap], .{});
return initFromParts(table, items, l, context);
}
/// Like `init`, but for backing memory that the caller guarantees
/// is already zero-filled (e.g. fresh OS pages). This writes
/// nothing to the backing buffer, so the OS pages behind the table
/// and items stay untouched until the first `add`.
///
/// Behavior is undefined if the backing memory is not zero.
pub fn initAssumeZeroed(base: OffsetBuf, l: Layout, context: Context) Self {
return initFromParts(
base.member(Id, l.table_start),
base.member(Item, l.items_start),
l,
context,
);
}
fn initFromParts(
table: Offset(Id),
items: Offset(Item),
l: Layout,
context: Context,
) Self {
return .{
.table = table,
.items = items,
@@ -726,7 +750,9 @@ pub fn RefCountedSet(
var psl_stats: [32]Id = @splat(0);
for (items[0..self.layout.cap], 0..) |item, id| {
// Start at item 1 because item 0 is reserved and never
// assigned to. Its metadata doesn't matter.
for (items[1..self.next_id], 1..) |item, id| {
if (item.meta.bucket < std.math.maxInt(Id)) {
assert(table[item.meta.bucket] == id);
psl_stats[item.meta.psl] += 1;
@@ -740,6 +766,7 @@ pub fn RefCountedSet(
psl_stats = @splat(0);
for (table[0..self.layout.table_cap], 0..) |id, bucket| {
if (id == 0) continue;
const item = items[id];
if (item.meta.bucket < std.math.maxInt(Id)) {
assert(item.meta.bucket == bucket);