handles.zig (2357B)
1 const std = @import("std"); 2 const Arena = @import("arena.zig").Arena; 3 4 pub const Handle = u64; 5 // A process-wide generation prevents cross-context aliases as well as stale 6 // slot reuse. Never wrap: exhaustion is an explicit error, not ABA resurrection. 7 var generation = std.atomic.Value(u32).init(1); 8 fn nextGeneration() !u32 { 9 var old = generation.load(.monotonic); 10 while (old != std.math.maxInt(i32)) { 11 old = generation.cmpxchgWeak(old, old + 1, .monotonic, .monotonic) orelse return old; 12 } 13 return error.HandleExhausted; 14 } 15 16 pub const Table = struct { 17 const Slot = struct { node: ?u32, generation: u32, next: ?u32 = null }; 18 slots: std.ArrayList(Slot) = .empty, 19 free: ?u32 = null, 20 21 pub fn deinit(self: *Table, allocator: std.mem.Allocator) void { 22 self.slots.deinit(allocator); 23 } 24 pub fn create(self: *Table, allocator: std.mem.Allocator, node: u32) !Handle { 25 const gen = try nextGeneration(); 26 const idx = if (self.free) |idx| blk: { 27 self.free = self.slots.items[idx].next; 28 self.slots.items[idx] = .{ .node = node, .generation = gen }; 29 break :blk idx; 30 } else blk: { 31 if (self.slots.items.len >= std.math.maxInt(u32)) return error.HandleExhausted; 32 const idx: u32 = @intCast(self.slots.items.len); 33 try self.slots.append(allocator, .{ .node = node, .generation = gen }); 34 break :blk idx; 35 }; 36 return (@as(u64, gen) << 32) | idx; 37 } 38 pub fn get(self: *const Table, handle: Handle) !u32 { 39 const idx: u32 = @truncate(handle); 40 const gen: u32 = @intCast(handle >> 32); 41 if (idx >= self.slots.items.len) return error.InvalidHandle; 42 const slot = self.slots.items[idx]; 43 if (gen != slot.generation) return error.InvalidHandle; 44 return slot.node orelse error.InvalidHandle; 45 } 46 pub fn release(self: *Table, handle: Handle) !void { 47 _ = try self.get(handle); 48 const idx: u32 = @truncate(handle); 49 self.slots.items[idx].node = null; 50 self.slots.items[idx].next = self.free; 51 self.free = idx; 52 } 53 pub fn appendRoots(self: *const Table, arena: *Arena) !void { 54 for (self.slots.items) |slot| { 55 if (slot.node) |node| try arena.roots.append(arena.allocator, node); 56 } 57 } 58 };