tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

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 };