ona/source/coral/map.zig

275 lines
5.9 KiB
Zig
Raw Normal View History

2023-07-10 02:10:56 +02:00
const debug = @import("./debug.zig");
const io = @import("./io.zig");
const list = @import("./list.zig");
const math = @import("./math.zig");
pub fn StringTable(comptime Value: type) type {
2023-07-29 17:04:27 +02:00
return Table([]const io.Byte, Value, struct {
const Self = @This();
2023-08-12 13:57:50 +02:00
fn equals(key_a: []const io.Byte, key_b: []const io.Byte) bool {
2023-08-13 14:54:31 +02:00
return io.are_equal(key_a, key_b);
2023-07-29 17:04:27 +02:00
}
2023-08-12 13:57:50 +02:00
fn hash(key: []const io.Byte) usize {
2023-07-29 17:04:27 +02:00
return io.djb2_hash(@typeInfo(usize).Int, key);
}
});
}
2023-07-29 17:04:27 +02:00
pub fn Table(comptime Key: type, comptime Value: type, comptime Traits: type) type {
2023-07-10 02:10:56 +02:00
const load_max = 0.75;
2023-08-12 13:57:50 +02:00
const max_int = math.max_int(@typeInfo(usize).Int);
2023-07-10 02:10:56 +02:00
return struct {
allocator: io.Allocator,
2023-07-29 17:04:27 +02:00
traits: Traits,
2023-07-10 02:10:56 +02:00
count: usize,
entries: []?Entry,
pub const Entry = struct {
key: Key,
value: Value,
2023-07-29 17:04:27 +02:00
fn write_into(self: Entry, table: *Self) bool {
const hash_max = @min(max_int, table.entries.len);
2023-08-12 13:57:50 +02:00
var hashed_key = table.hash_key(self.key) % hash_max;
2023-07-10 02:10:56 +02:00
var iterations = @as(usize, 0);
while (true) : (iterations += 1) {
2023-07-29 17:04:27 +02:00
debug.assert(iterations < table.entries.len);
2023-07-10 02:10:56 +02:00
2023-07-29 17:04:27 +02:00
const table_entry = &(table.entries[hashed_key] orelse {
table.entries[hashed_key] = .{
2023-07-10 02:10:56 +02:00
.key = self.key,
.value = self.value,
};
2023-07-29 17:04:27 +02:00
table.count += 1;
2023-07-10 02:10:56 +02:00
return true;
});
2023-08-12 13:57:50 +02:00
if (table.keys_equal(table_entry.key, self.key)) {
2023-07-10 02:10:56 +02:00
return false;
}
hashed_key = (hashed_key +% 1) % hash_max;
}
}
};
pub const Iterable = struct {
table: *Self,
iterations: usize,
2023-07-10 02:10:56 +02:00
pub fn next(self: *Iterable) ?Entry {
while (self.iterations < self.table.entries.len) {
defer self.iterations += 1;
if (self.table.entries[self.iterations]) |entry| {
return entry;
}
}
return null;
}
};
const Self = @This();
pub fn as_iterable(self: *Self) Iterable {
return .{
.table = self,
.iterations = 0,
};
}
2023-08-12 13:57:50 +02:00
fn hash_key(self: Self, key: Key) usize {
return if (@sizeOf(Traits) == 0) Traits.hash(key) else self.traits.hash(key);
}
pub fn remove(self: *Self, key: Key) ?Entry {
2023-07-22 14:57:39 +02:00
const hash_max = @min(max_int, self.entries.len);
2023-08-12 13:57:50 +02:00
var hashed_key = self.hash_key(key) % hash_max;
while (true) {
const entry = &(self.entries[hashed_key] orelse continue);
2023-08-12 13:57:50 +02:00
if (self.keys_equal(entry.key, key)) {
const original_entry = entry.*;
self.entries[hashed_key] = null;
return original_entry;
}
hashed_key = (hashed_key +% 1) % hash_max;
}
}
2023-07-10 02:10:56 +02:00
pub fn replace(self: *Self, key: Key, value: Value) io.AllocationError!?Entry {
try self.rehash(load_max);
debug.assert(self.entries.len > self.count);
{
2023-07-22 14:57:39 +02:00
const hash_max = @min(max_int, self.entries.len);
2023-08-12 13:57:50 +02:00
const has_context = @sizeOf(Traits) != 0;
var hashed_key = (if (has_context) self.traits.hash(key) else Traits.hash(key)) % hash_max;
2023-07-10 02:10:56 +02:00
while (true) {
const entry = &(self.entries[hashed_key] orelse {
self.entries[hashed_key] = .{
.key = key,
.value = value,
};
self.count += 1;
return null;
});
2023-08-12 13:57:50 +02:00
if (has_context) {
if (self.traits.equals(entry.key, key)) {
const original_entry = entry.*;
2023-07-10 02:10:56 +02:00
2023-08-12 13:57:50 +02:00
entry.* = .{
.key = key,
.value = value,
};
return original_entry;
}
} else {
if (Traits.equals(entry.key, key)) {
const original_entry = entry.*;
2023-07-10 02:10:56 +02:00
2023-08-12 13:57:50 +02:00
entry.* = .{
.key = key,
.value = value,
};
return original_entry;
}
2023-07-10 02:10:56 +02:00
}
hashed_key = (hashed_key +% 1) % hash_max;
}
}
}
pub fn calculate_load_factor(self: Self) f32 {
return if (self.entries.len == 0) 1 else @as(f32, @floatFromInt(self.count)) / @as(f32, @floatFromInt(self.entries.len));
}
pub fn clear(self: *Self) void {
for (self.entries) |*entry| {
entry.* = null;
}
self.count = 0;
}
pub fn free(self: *Self) void {
if (self.entries.len == 0) {
return;
}
self.allocator.deallocate(self.entries);
self.entries = &.{};
self.count = 0;
}
pub fn insert(self: *Self, key: Key, value: Value) io.AllocationError!bool {
try self.rehash(load_max);
debug.assert(self.entries.len > self.count);
const entry = Entry{
.key = key,
.value = value,
};
2023-07-29 17:04:27 +02:00
return entry.write_into(self);
2023-07-10 02:10:56 +02:00
}
2023-08-12 13:57:50 +02:00
fn keys_equal(self: Self, key_a: Key, key_b: Key) bool {
if (@sizeOf(Traits) == 0) {
return Traits.equals(key_a, key_b);
} else {
return self.traits.equals(key_a, key_b);
}
}
2023-07-10 02:10:56 +02:00
pub fn lookup(self: Self, key: Key) ?Value {
if (self.count == 0) {
return null;
}
2023-07-22 14:57:39 +02:00
const hash_max = @min(max_int, self.entries.len);
2023-08-12 13:57:50 +02:00
const has_context = @sizeOf(Traits) != 0;
var hashed_key = (if (has_context) self.traits.hash(key) else Traits.hash(key)) % hash_max;
2023-07-10 02:10:56 +02:00
var iterations = @as(usize, 0);
while (iterations < self.count) : (iterations += 1) {
const entry = &(self.entries[hashed_key] orelse return null);
2023-08-12 13:57:50 +02:00
if (has_context) {
if (self.traits.equals(entry.key, key)) {
return entry.value;
}
} else {
if (Traits.equals(entry.key, key)) {
return entry.value;
}
2023-07-10 02:10:56 +02:00
}
hashed_key = (hashed_key +% 1) % hash_max;
}
return null;
}
2023-07-29 17:04:27 +02:00
pub fn make(allocator: io.Allocator, traits: Traits) Self {
2023-07-10 02:10:56 +02:00
return .{
.allocator = allocator,
.count = 0,
.entries = &.{},
2023-07-29 17:04:27 +02:00
.traits = traits,
2023-07-10 02:10:56 +02:00
};
}
pub fn rehash(self: *Self, max_load: f32) io.AllocationError!void {
if (self.calculate_load_factor() <= max_load) {
return;
}
2023-07-29 17:04:27 +02:00
var table = make(self.allocator, self.traits);
2023-07-10 02:10:56 +02:00
2023-07-29 17:04:27 +02:00
errdefer table.free();
2023-07-10 02:10:56 +02:00
2023-07-29 17:04:27 +02:00
table.entries = allocate: {
const min_count = @max(1, self.count);
const table_size = min_count * 2;
2023-07-10 02:10:56 +02:00
2023-07-29 17:04:27 +02:00
break: allocate try io.allocate_many(self.allocator, table_size, @as(?Entry, null));
};
2023-07-10 02:10:56 +02:00
2023-07-29 17:04:27 +02:00
for (self.entries) |maybe_entry| {
if (maybe_entry) |entry| {
debug.assert(entry.write_into(&table));
2023-07-10 02:10:56 +02:00
}
2023-07-29 17:04:27 +02:00
}
2023-07-10 02:10:56 +02:00
2023-07-29 17:04:27 +02:00
self.free();
2023-07-10 02:10:56 +02:00
2023-07-29 17:04:27 +02:00
self.* = table;
}
2023-07-10 02:10:56 +02:00
};
}