From 38dadb6d05de385f786f0c3d47a3b93e88d8a050 Mon Sep 17 00:00:00 2001 From: kayomn Date: Tue, 9 May 2023 20:59:26 +0000 Subject: [PATCH] Fix compile errors in slot map remove function --- source/coral/slots.zig | 114 ++++++++++++++++++++++++++++++++--------- 1 file changed, 89 insertions(+), 25 deletions(-) diff --git a/source/coral/slots.zig b/source/coral/slots.zig index c14ed76..22cfb24 100755 --- a/source/coral/slots.zig +++ b/source/coral/slots.zig @@ -6,6 +6,10 @@ const math = @import("./math.zig"); const stack = @import("./stack.zig"); +/// +/// Retruns a set of dense slots that may store `Element`s indexable by a [Slot], where `key` defines how many bits the +/// [Slot] used is made from. +/// pub fn Dense(comptime key: Key, comptime Element: type) type { const KeySlot = Slot(key); const Index = math.Unsigned(key.index_bits); @@ -19,20 +23,12 @@ pub fn Dense(comptime key: Key, comptime Element: type) type { const Self = @This(); - pub fn fetch(self: Self, slot: KeySlot) ?*Element { - if (slot.index >= self.values.len) { - return null; - } - - const redirect = &self.slots[slot.index]; - - if (slot.salt != redirect.salt) { - return null; - } - - return &self.values[redirect.index]; - } - + /// + /// Clears all elements from the slots in `self`. + /// + /// *Note* that clearing the slots is not the same as deinitializing them, as it does not deallocate any memory + /// that has already been allocated to the slots structure. + /// pub fn clear(self: *Self) void { self.next_free = 0; self.values = self.values[0 .. 0]; @@ -49,18 +45,66 @@ pub fn Dense(comptime key: Key, comptime Element: type) type { } } + /// + /// Frees all memory allocated by `allocator` to self. + /// + /// *Note*: if `self` already contains allocated memory then `allocator` must reference the same [io.Allocator] + /// that was used to create the already-allocated memory. + /// pub fn deinit(self: *Self, allocator: io.Allocator) void { io.deallocate(allocator, self.values.ptr); io.deallocate(allocator, self.slots); io.deallocate(allocator, self.erase); + + self.values = &.{}; + self.slots = null; + self.erase = null; } + /// + /// Attempts to fetch the element identified referenced by `slot` from `self`, returning it or `null` if `slot` + /// does not reference a valid element. + /// + pub fn fetch(self: Self, slot: KeySlot) ?*Element { + if (slot.index >= self.values.len) { + return null; + } + + const redirect = &self.slots[slot.index]; + + if (slot.salt != redirect.salt) { + return null; + } + + return &self.values[redirect.index]; + } + + /// + /// Attempts to transactionally grow `self` by `growth_amount` using `allocator`, returning a + /// [io.AllocationError] if it failed. + /// + /// Should growing fail, `self` is left in an unmodified state. + /// + /// *Note*: if `self` already contains allocated memory then `allocator` must reference the same [io.Allocator] + /// that was used to create the already-allocated memory. + /// pub fn grow(self: *Self, allocator: io.Allocator, growth_amount: usize) io.AllocationError!void { const grown_capacity = self.capacity + growth_amount; + const values = try io.allocate_many(Element, grown_capacity, allocator); - self.values = try io.reallocate(allocator, self.values, grown_capacity); - self.slots = (try io.reallocate(allocator, self.slots.?[0 .. self.values.len], grown_capacity)).ptr; - self.erase = (try io.reallocate(allocator, self.erase.?[0 .. self.values.len], grown_capacity)).ptr; + errdefer io.deallocate(allocator, values); + + const slots = try io.allocate_many(KeySlot, grown_capacity, allocator); + + errdefer io.deallocate(allocator, slots); + + const erase = try io.allocate_many(Index, grown_capacity, allocator); + + errdefer io.deallocate(allocator, slots); + + self.values = values; + self.slots = slots.ptr; + self.erase = erase.ptr; self.capacity = grown_capacity; // Add new values to the freelist @@ -76,6 +120,13 @@ pub fn Dense(comptime key: Key, comptime Element: type) type { } } + /// + /// Attempts to insert `value` into `self`, growing the internal buffer with `allocator` if it is full and + /// returning a `Slot` of `key` referencing the inserted element or a [io.AllocationError] if it failed. + /// + /// *Note*: if `self` already contains allocated memory then `allocator` must reference the same [io.Allocator] + /// that was used to create the already-allocated memory. + /// pub fn insert(self: *Self, allocator: io.Allocator, value: Element) io.AllocationError!KeySlot { if (self.values.len == self.capacity) { try self.grow(allocator, math.max(usize, 1, self.capacity)); @@ -97,8 +148,12 @@ pub fn Dense(comptime key: Key, comptime Element: type) type { }; } + /// + /// Attempts to remove the element referenced by `slot` from `self`, returning `true` if it was successful or + /// `false` if `slot` does not reference a valid slot. + /// pub fn remove(self: *Self, slot: KeySlot) bool { - const redirect = &self.slots[slot.index]; + const redirect = &self.slots.?[slot.index]; if (slot.salt != redirect.salt) { return false; @@ -109,17 +164,17 @@ pub fn Dense(comptime key: Key, comptime Element: type) type { self.values = self.values[0 .. (self.values.len - 1)]; if (self.values.len > 0) { - const free_data = &self.data[free_index]; - const free_erase = &self.erase[free_index]; - const last_data = &self.data[self.values.len]; - const last_erase = &self.erase[self.values.len]; + const free_value = &self.values[free_index]; + const free_erase = &self.erase.?[free_index]; + const last_value = &self.values[self.values.len]; + const last_erase = &self.erase.?[self.values.len]; - free_data.* = last_data.*; + free_value.* = last_value.*; free_erase.* = last_erase.*; - self.slots[free_erase.*].index = free_index; + self.slots.?[free_erase.*].index = free_index; } - redirect.salt = math.max(redirect.salt +% 1, 1); + redirect.salt = math.max(Index, redirect.salt +% 1, 1); redirect.index = self.next_free; self.next_free = slot.index; @@ -128,11 +183,17 @@ pub fn Dense(comptime key: Key, comptime Element: type) type { }; } +/// +/// Describes the memory layout of an element-slot mapping. +/// pub const Key = struct { index_bits: usize, salt_bits: usize, }; +/// +/// References a slot in a slot mapping. +/// pub fn Slot(comptime key: Key) type { return extern struct { index: math.Unsigned(key.index_bits), @@ -140,6 +201,9 @@ pub fn Slot(comptime key: Key) type { }; } +/// +/// [Key] that uses the same number of bits as a [usize]. +/// pub const addressable_key = Key{ .index_bits = (@bitSizeOf(usize) / 2), .salt_bits = (@bitSizeOf(usize) / 2),