update table to use packed ID and limit size to fit in 32 bytes.

This commit is contained in:
Vivianne 2022-07-26 03:00:15 -07:00
parent 0aa55ae1a9
commit 742f966e14

View file

@ -18,9 +18,9 @@ pub fn Table(comptime K: type, comptime V: type) type {
const Self = @This();
/// The ID for the table (index + generation)
pub const Id = struct {
index: usize,
pub const Id = packed struct {
gen: u8,
index: u24,
};
// Collections sharing an index:
@ -35,19 +35,19 @@ pub fn Table(comptime K: type, comptime V: type) type {
// Other fields
/// List of indices which have previously been freed and are available to fill.
free_list: Queue(usize),
free_list: Queue(u24),
/// Used for mapping the key to its index.
lookup: std.AutoHashMap(K, usize),
lookup: std.AutoHashMap(K, u24),
/// The amount of items in the table (not the allocated size)
len: usize,
len: u24,
pub fn init(allocator: std.mem.Allocator) Self {
return Self{
.gens = std.ArrayList(u8).init(allocator),
.values = std.ArrayList(?V).init(allocator),
.keys = std.ArrayList(?K).init(allocator),
.free_list = Queue(usize).init(allocator),
.lookup = std.AutoHashMap(K, usize).init(allocator),
.free_list = Queue(u24).init(allocator),
.lookup = std.AutoHashMap(K, u24).init(allocator),
.len = 0,
};
}
@ -77,7 +77,7 @@ pub fn Table(comptime K: type, comptime V: type) type {
}
pub fn exists(self: *const Self, id: Id) bool {
return id.index < self.values.items.len and id.gen == self.gens.items[id.index];
return id.index < self.values.items.len and id.gen == self.gens.items[@as(usize, id.index)];
}
pub const AddResult = struct {
@ -94,9 +94,11 @@ pub fn Table(comptime K: type, comptime V: type) type {
}
if (self.free_list.popOrNull()) |index| {
const gen = self.gens.items[index];
self.keys.items[index] = key;
self.values.items[index] = val;
const uindex = @as(usize, index);
const gen = self.gens.items[uindex];
self.keys.items[uindex] = key;
self.values.items[uindex] = val;
try self.lookup.putNoClobber(key, index);
self.len += 1;
return AddResult{
@ -116,7 +118,7 @@ pub fn Table(comptime K: type, comptime V: type) type {
assert(self.keys.items.len == self.values.items.len);
assert(self.values.items.len == self.gens.items.len);
const index = self.keys.items.len - 1;
const index = @intCast(u24, self.keys.items.len - 1);
try self.lookup.putNoClobber(key, index);
return AddResult{
@ -132,29 +134,31 @@ pub fn Table(comptime K: type, comptime V: type) type {
pub fn remove(self: *Self, id: Id) !V {
assert(self.len > 0);
const index = id.index;
const uindex = @as(usize, id.index);
const key = self.keys.items[index] orelse unreachable;
const key = self.keys.items[uindex] orelse unreachable;
const removed = self.lookup.remove(key);
assert(removed);
self.keys.items[index] = null;
self.keys.items[uindex] = null;
self.gens.items[index] += 1;
const val = self.values.items[index] orelse unreachable;
self.values.items[index] = null;
try self.free_list.push(index);
self.gens.items[uindex] += 1;
const val = self.values.items[uindex] orelse unreachable;
self.values.items[uindex] = null;
try self.free_list.push(id.index);
self.len -= 1;
return val;
}
pub fn get(self: *Self, id: Id) ?*V {
return if (self.exists(id)) &(self.values.items[id.index] orelse unreachable) else null;
return if (self.exists(id)) &(self.values.items[@as(usize, id.index)] orelse unreachable) else null;
}
pub fn find(self: *Self, key: K) ?Id {
if (self.lookup.get(key)) |index| {
const gen = self.gens.items[index];
const uindex = @as(usize, index);
const gen = self.gens.items[uindex];
return Id{
.index = index,
.gen = gen,
@ -165,7 +169,7 @@ pub fn Table(comptime K: type, comptime V: type) type {
}
pub fn getKey(self: *Self, id: Id) ?K {
return if (self.exists(id)) (self.keys.items[id.index] orelse unreachable) else null;
return if (self.exists(id)) (self.keys.items[@as(usize, id.index)] orelse unreachable) else null;
}
};
}
@ -182,12 +186,12 @@ test "general table test" {
const first_result = try table.add(56, .{ .a = 42, .b = 87 });
try std.testing.expect(first_result.added);
try std.testing.expectEqual(@as(u8, 0), first_result.id.gen);
try std.testing.expectEqual(@as(usize, 0), first_result.id.index);
try std.testing.expectEqual(@as(u24, 0), first_result.id.index);
const second_result = try table.add(62, .{ .a = 1, .b = 12 });
try std.testing.expect(second_result.added);
try std.testing.expectEqual(@as(u8, 0), second_result.id.gen);
try std.testing.expectEqual(@as(usize, 1), second_result.id.index);
try std.testing.expectEqual(@as(u24, 1), second_result.id.index);
var second_id = table.find(62) orelse unreachable;
var second_val = table.get(second_id) orelse unreachable;
@ -272,7 +276,7 @@ pub fn RefTable(comptime K: type, comptime V: type) type {
}
pub fn remove(self: *Self, id: Id) !V {
self.ref_counts.items[id.index] = 0;
self.ref_counts.items[@as(usize, id.index)] = 0;
return self.table.remove(id);
}
@ -333,7 +337,7 @@ pub fn RefTable(comptime K: type, comptime V: type) type {
assert(result.id.index == self.ref_counts.items.len);
try self.ref_counts.append(1);
} else {
self.ref_counts.items[result.id.index] = 1;
self.ref_counts.items[@as(usize, result.id.index)] = 1;
}
return result;
@ -360,12 +364,12 @@ test "general ref table test" {
const first_result = try table.add(56, .{ .a = 42, .b = 87 });
try std.testing.expect(first_result.added);
try std.testing.expectEqual(@as(u8, 0), first_result.id.gen);
try std.testing.expectEqual(@as(usize, 0), first_result.id.index);
try std.testing.expectEqual(@as(u24, 0), first_result.id.index);
const second_result = try table.add(62, .{ .a = 1, .b = 12 });
try std.testing.expect(second_result.added);
try std.testing.expectEqual(@as(u8, 0), second_result.id.gen);
try std.testing.expectEqual(@as(usize, 1), second_result.id.index);
try std.testing.expectEqual(@as(u24, 1), second_result.id.index);
var second_id = table.find(62) orelse unreachable;
var second_val = table.get(second_id) orelse unreachable;
@ -406,7 +410,7 @@ test "ref counting" {
first_result = try table.add(12, .{ .a = 6, .b = 5 });
try std.testing.expect(first_result.added);
try std.testing.expectEqual(@as(usize, 0), first_result.id.index);
try std.testing.expectEqual(@as(u24, 0), first_result.id.index);
try std.testing.expectEqual(@as(u8, 1), first_result.id.gen);
const second_result = try table.add(12, .{ .a = 1, .b = 2 });
try std.testing.expect(!second_result.added);