diff options
Diffstat (limited to 'src/allocators.zig')
-rw-r--r-- | src/allocators.zig | 381 |
1 files changed, 178 insertions, 203 deletions
diff --git a/src/allocators.zig b/src/allocators.zig index 8ebde9150..c9371426a 100644 --- a/src/allocators.zig +++ b/src/allocators.zig @@ -1,5 +1,8 @@ const std = @import("std"); +const hash_map = @import("hash_map.zig"); + +const HashMapUnmanaged = hash_map.HashMapUnmanaged; const Wyhash = std.hash.Wyhash; const FixedBufferAllocator = std.heap.FixedBufferAllocator; @@ -70,7 +73,7 @@ pub const IndexType = packed struct { }; const HashKeyType = u64; -const IndexMap = std.HashMapUnmanaged(HashKeyType, IndexType, hash_hashFn, hash_eqlFn, 80); +const IndexMap = HashMapUnmanaged(HashKeyType, IndexType, hash_hashFn, hash_eqlFn, 80); pub const Result = struct { hash: HashKeyType, index: IndexType, @@ -88,7 +91,7 @@ pub const Result = struct { return if (r.isOverflowing(count)) @intCast(IndexType, r.index - max_index) else r.index; } }; -const Seed = 999; +const Seed = 0; pub const NotFound = IndexType{ .index = std.math.maxInt(u31), @@ -233,246 +236,227 @@ pub fn BSSList(comptime ValueType: type, comptime count: anytype) type { } }; } -pub fn BSSStringList(comptime count: usize, comptime item_length: usize) type { - const max_index = count - 1; - const ValueType = []const u8; - return struct { - pub var slice_buf: [count][]const u8 = undefined; - pub var slice_buf_used: u16 = 0; - pub var backing_buf: [count * item_length]u8 = undefined; - pub var backing_buf_used: u64 = undefined; - const Allocator = std.mem.Allocator; - const Self = @This(); - pub const ListIndex = packed struct { - index: u31, - is_overflowing: bool = false, - }; - overflow_list: std.ArrayListUnmanaged(ValueType), - allocator: *Allocator, +// Like an ArrayList except: +// - It only grows +// - Pointer never invalidates +pub const ByteBuffer = struct { + allocator: *std.mem.Allocator, + ptr: [*]u8, + len: usize, - pub var instance: Self = undefined; + items: []u8, - pub fn init(allocator: *std.mem.Allocator) *Self { - instance = Self{ - .allocator = allocator, - .overflow_list = std.ArrayListUnmanaged(ValueType){}, - }; + pub fn init(allocator: *std.mem.Allocator, comptime min_length: usize) !ByteBuffer { + var items = if (min_length > 0) try allocator.alloc(u8, min_length) else &([_]u8{}); - return &instance; + return ByteBuffer{ .allocator = allocator, .items = items, .ptr = undefined, .len = min_length }; + } + + pub fn growIfNeeded(this: *ByteBuffer, min_length: usize) !void { + const len = std.math.ceilPowerOfTwo(usize, this.items.len + min_length) catch unreachable; + if (this.len >= len) { + return; } - pub fn isOverflowing() bool { - return slice_buf_used >= @as(u16, count); + if (this.len == 0) { + const items = try this.allocator.alloc(u8, len); + this.ptr = items.ptr; + this.len = items.len; + } else { + const items = try this.allocator.realloc(this.ptr[0..this.len], len); + this.ptr = items.ptr; + this.len = items.len; } - pub fn at(self: *const Self, index: IndexType) ?ValueType { - if (index.index == NotFound.index or index.index == Unassigned.index) return null; + this.items = this.ptr[0 .. this.items.len + min_length]; + } - if (index.is_overflowing) { - return &self.overflow_list.items[index.index]; - } else { - return &slice_buf[index.index]; - } - } + pub fn reset(this: *ByteBuffer) void { + this.items = this.items[0..0]; + } - pub fn exists(self: *Self, value: ValueType) bool { - return isSliceInBuffer(value, slice_buf); - } + pub fn slice(this: *ByteBuffer, len: usize) ![]u8 { + try this.growIfNeeded(len); - pub fn editableSlice(slice: []const u8) []u8 { - return constStrToU8(slice); + return this.items[this.items.len - len ..]; + } + + pub fn append(this: *ByteBuffer, items: anytype) ![]u8 { + var writable = try this.slice(items.len); + @memcpy(writable.ptr, items.ptr, items.len); + return writable; + } +}; + +// Like an ArrayList except: +// - It only grows +// - Pointer never invalidates + +pub fn OverflowList(comptime ValueType: type) type { + return struct { + const Self = @This(); + allocator: *std.mem.Allocator, + ptr: [*]ValueType, + len: usize, + + items: []ValueType, + + pub fn init(allocator: *std.mem.Allocator, comptime min_length: usize) !Self { + var items = if (min_length > 0) try allocator.alloc(ValueType, min_length) else &([_]ValueType{}); + + return Self{ .allocator = allocator, .items = items, .ptr = undefined, .len = min_length }; } - pub fn append(self: *Self, _value: anytype) ![]const u8 { - var value = _value; - if (value.len + backing_buf_used < backing_buf.len - 1) { - const start = backing_buf_used; - backing_buf_used += value.len; - std.mem.copy(u8, backing_buf[start..backing_buf_used], _value); - value = backing_buf[start..backing_buf_used]; - } else { - value = try self.allocator.dupe(u8, _value); + pub fn growIfNeeded(this: *Self, min_length: usize) !void { + const len = std.math.ceilPowerOfTwo(usize, this.items.len + min_length) catch unreachable; + if (this.len >= len) { + return; } - var result = ListIndex{ .index = std.math.maxInt(u31), .is_overflowing = slice_buf_used > max_index }; - - if (result.is_overflowing) { - result.index = @intCast(u31, self.overflow_list.items.len); + if (this.len == 0) { + const items = try this.allocator.alloc(ValueType, len); + this.ptr = items.ptr; + this.len = items.len; } else { - result.index = slice_buf_used; - slice_buf_used += 1; - if (slice_buf_used >= max_index) { - self.overflow_list = try @TypeOf(self.overflow_list).initCapacity(self.allocator, count); - } + const items = try this.allocator.realloc(this.ptr[0..this.len], len); + this.ptr = items.ptr; + this.len = items.len; } - if (result.is_overflowing) { - if (self.overflow_list.items.len == result.index) { - const real_index = self.overflow_list.items.len; - try self.overflow_list.append(self.allocator, value); - } else { - self.overflow_list.items[result.index] = value; - } + this.items = this.ptr[0 .. this.items.len + min_length]; + } - return self.overflow_list.items[result.index]; - } else { - slice_buf[result.index] = value; + pub fn reset(this: *Self) void { + this.items = this.items[0..0]; + } - return slice_buf[result.index]; - } + pub fn slice(this: *Self, len: usize) ![]ValueType { + try this.growIfNeeded(len); + + return this.items[this.items.len - len ..]; } - pub fn remove(self: *Self, index: ListIndex) void { - @compileError("Not implemented yet."); - // switch (index) { - // Unassigned.index => { - // self.index.remove(_key); - // }, - // NotFound.index => { - // self.index.remove(_key); - // }, - // 0...max_index => { - // if (hasDeinit(ValueType)) { - // slice_buf[index].deinit(); - // } - // slice_buf[index] = undefined; - // }, - // else => { - // const i = index - count; - // if (hasDeinit(ValueType)) { - // self.overflow_list.items[i].deinit(); - // } - // self.overflow_list.items[index - count] = undefined; - // }, - // } + pub fn append(this: *Self, value: ValueType) !*ValueType { + try this.growIfNeeded(1); + const index = this.items.len - 1; + this.items[index] = value; + return &this.items[index]; + } - // return index; + pub fn appendGetIndex(this: *Self, value: ValueType) !usize { + try this.growIfNeeded(1); + const index = this.items.len - 1; + this.items[index] = value; + return index; } }; } -pub fn TBSSStringList(comptime count: usize, comptime item_length: usize) type { +// Growable array of variable-length strings +// Copies the strings +pub const StringList = struct { + const DataType = [][]u8; + buffer: ByteBuffer, + ptr: [*][]u8, + len: usize = 0, + allocator: *std.mem.Allocator, + + items: DataType, + + pub fn init(allocator: *std.mem.Allocator) StringList { + return StringList{ + .ptr = undefined, + .allocator = allocator, + .len = 0, + .buffer = ByteBuffer.init(allocator, 0) catch unreachable, + .items = std.mem.zeroes(DataType), + }; + } + + pub fn reset(self: *StringList) void { + self.buffer.reset(); + self.items = self.ptr[0..0]; + } + + pub fn appendCopy(self: *StringList, str: anytype, comptime copy: bool) ![]const u8 { + const index = try self.appendCopyIndex(str, copy); + return self.items[index]; + } + + pub fn appendCopyIndex(self: *StringList, str: anytype, comptime copy: bool) !usize { + if (self.len == 0) { + var items = try self.allocator.alloc([]u8, 8); + self.ptr = items.ptr; + self.len = items.len; + self.items = items[0..1]; + } else if (self.items.len >= self.len) { + const end = self.len + 1; + const len = std.math.ceilPowerOfTwo(usize, self.len + 1) catch unreachable; + var items = try self.allocator.realloc(self.ptr[0..self.len], len); + self.ptr = items.ptr; + self.len = items.len; + self.items = self.ptr[0..end]; + } + + const index = self.items.len - 1; + self.items[index] = if (copy) try self.buffer.append(str) else str; + return index; + } + + pub fn append(self: *StringList, str: anytype) ![]const u8 { + return try self.appendCopy(str, true); + } +}; + +pub fn BSSStringList(comptime count: usize, comptime item_length: usize) type { const max_index = count - 1; const ValueType = []const u8; return struct { + pub var backing_buf: [count * item_length]u8 = undefined; + pub var backing_buf_used: u64 = undefined; + const Allocator = std.mem.Allocator; const Self = @This(); - - pub threadlocal var slice_buf: [count][]const u8 = undefined; - pub threadlocal var slice_buf_used: u16 = 0; - pub threadlocal var backing_buf: [count * item_length]u8 = undefined; - pub threadlocal var backing_buf_used: u64 = undefined; - pub threadlocal var instance: Self = undefined; pub const ListIndex = packed struct { index: u31, is_overflowing: bool = false, }; - overflow_list: std.ArrayListUnmanaged(ValueType), + list: StringList, allocator: *Allocator, + pub var instance: Self = undefined; + pub fn init(allocator: *std.mem.Allocator) *Self { instance = Self{ .allocator = allocator, - .overflow_list = std.ArrayListUnmanaged(ValueType){}, + .list = StringList.init(allocator), }; return &instance; } - pub fn isOverflowing() bool { - return slice_buf_used >= @as(u16, count); - } - - pub fn at(self: *const Self, index: IndexType) ?ValueType { - if (index.index == NotFound.index or index.index == Unassigned.index) return null; - - if (index.is_overflowing) { - return &self.overflow_list.items[index.index]; - } else { - return &slice_buf[index.index]; - } - } - - pub fn exists(self: *Self, value: ValueType) bool { - return isSliceInBuffer(value, slice_buf); - } - pub fn editableSlice(slice: []const u8) []u8 { return constStrToU8(slice); } - pub fn append(self: *Self, _value: anytype) ![]const u8 { - var value = _value; + pub fn append(self: *Self, value: anytype) ![]const u8 { if (value.len + backing_buf_used < backing_buf.len - 1) { const start = backing_buf_used; backing_buf_used += value.len; - std.mem.copy(u8, backing_buf[start..backing_buf_used], _value); - value = backing_buf[start..backing_buf_used]; - } else { - value = try self.allocator.dupe(u8, _value); + std.mem.copy(u8, backing_buf[start..backing_buf_used], value); + return try self.list.appendCopy(backing_buf[start..backing_buf_used], false); } - var result = ListIndex{ .index = std.math.maxInt(u31), .is_overflowing = slice_buf_used > max_index }; - - if (result.is_overflowing) { - result.index = @intCast(u31, self.overflow_list.items.len); - } else { - result.index = slice_buf_used; - slice_buf_used += 1; - if (slice_buf_used >= max_index) { - self.overflow_list = try @TypeOf(self.overflow_list).initCapacity(self.allocator, count); - } - } - - if (result.is_overflowing) { - if (self.overflow_list.items.len == result.index) { - const real_index = self.overflow_list.items.len; - try self.overflow_list.append(self.allocator, value); - } else { - self.overflow_list.items[result.index] = value; - } - - return self.overflow_list.items[result.index]; - } else { - slice_buf[result.index] = value; - - return slice_buf[result.index]; - } - } - - pub fn remove(self: *Self, index: ListIndex) void { - @compileError("Not implemented yet."); - // switch (index) { - // Unassigned.index => { - // self.index.remove(_key); - // }, - // NotFound.index => { - // self.index.remove(_key); - // }, - // 0...max_index => { - // if (hasDeinit(ValueType)) { - // slice_buf[index].deinit(); - // } - // slice_buf[index] = undefined; - // }, - // else => { - // const i = index - count; - // if (hasDeinit(ValueType)) { - // self.overflow_list.items[i].deinit(); - // } - // self.overflow_list.items[index - count] = undefined; - // }, - // } - - // return index; + return try self.list.appendCopy(value, true); } }; } pub fn BSSMap(comptime ValueType: type, comptime count: anytype, store_keys: bool, estimated_key_length: usize) type { const max_index = count - 1; + const OverflowListType = OverflowList(ValueType); const BSSMapType = struct { pub var backing_buf: [count]ValueType = undefined; pub var backing_buf_used: u16 = 0; @@ -480,7 +464,7 @@ pub fn BSSMap(comptime ValueType: type, comptime count: anytype, store_keys: boo const Self = @This(); index: IndexMap, - overflow_list: std.ArrayListUnmanaged(ValueType), + overflow_list: OverflowListType, allocator: *Allocator, pub var instance: Self = undefined; @@ -489,7 +473,7 @@ pub fn BSSMap(comptime ValueType: type, comptime count: anytype, store_keys: boo instance = Self{ .index = IndexMap{}, .allocator = allocator, - .overflow_list = std.ArrayListUnmanaged(ValueType){}, + .overflow_list = OverflowListType.init(allocator, 0) catch unreachable, }; return &instance; @@ -501,7 +485,7 @@ pub fn BSSMap(comptime ValueType: type, comptime count: anytype, store_keys: boo pub fn getOrPut(self: *Self, key: []const u8) !Result { const _key = Wyhash.hash(Seed, key); - var index = try self.index.getOrPut(self.allocator, _key); + var index = try self.index.getOrPutWithHash(self.allocator, _key, _key); if (index.found_existing) { return Result{ @@ -525,12 +509,12 @@ pub fn BSSMap(comptime ValueType: type, comptime count: anytype, store_keys: boo pub fn get(self: *const Self, key: []const u8) ?*ValueType { const _key = Wyhash.hash(Seed, key); - const index = self.index.get(_key) orelse return null; + const index = self.index.getWithHash(_key, _key) orelse return null; return self.atIndex(index); } pub fn markNotFound(self: *Self, result: Result) void { - self.index.put(self.allocator, result.hash, NotFound) catch unreachable; + self.index.putWithHash(self.allocator, result.hash, result.hash, NotFound) catch unreachable; } pub fn atIndex(self: *const Self, index: IndexType) ?*ValueType { @@ -551,23 +535,13 @@ pub fn BSSMap(comptime ValueType: type, comptime count: anytype, store_keys: boo } else { result.index.index = backing_buf_used; backing_buf_used += 1; - if (backing_buf_used >= max_index) { - self.overflow_list = try @TypeOf(self.overflow_list).initCapacity(self.allocator, count); - } } } - try self.index.put(self.allocator, result.hash, result.index); + try self.index.putWithHash(self.allocator, result.hash, result.hash, result.index); if (result.index.is_overflow) { - if (self.overflow_list.items.len == result.index.index) { - const real_index = self.overflow_list.items.len; - try self.overflow_list.append(self.allocator, value); - } else { - self.overflow_list.items[result.index.index] = value; - } - - return &self.overflow_list.items[result.index.index]; + return try self.overflow_list.append(value); } else { backing_buf[result.index.index] = value; @@ -577,7 +551,7 @@ pub fn BSSMap(comptime ValueType: type, comptime count: anytype, store_keys: boo pub fn remove(self: *Self, key: string) IndexType { const _key = Wyhash.hash(Seed, key); - const index = self.index.get(_key) orelse return; + const index = self.index.getWithHash(_key, _key) orelse return; switch (index) { Unassigned.index => { self.index.remove(_key); @@ -614,13 +588,13 @@ pub fn BSSMap(comptime ValueType: type, comptime count: anytype, store_keys: boo var key_list_buffer: [count * estimated_key_length]u8 = undefined; var key_list_buffer_used: usize = 0; var key_list_slices: [count][]u8 = undefined; - var key_list_overflow: std.ArrayListUnmanaged([]u8) = undefined; + var key_list_overflow: StringList = undefined; pub fn init(allocator: *std.mem.Allocator) *Self { instance = Self{ .map = BSSMapType.init(allocator), }; - + key_list_overflow = key_list_overflow.init(allocator); return &instance; } @@ -677,9 +651,10 @@ pub fn BSSMap(comptime ValueType: type, comptime count: anytype, store_keys: boo const start = key_list_buffer_used; key_list_buffer_used += key.len; slice = key_list_buffer[start..key_list_buffer_used]; - std.mem.copy(u8, slice, key); + @memcpy(slice.ptr, key.ptr, key.len); } else { - slice = try self.map.allocator.dupe(u8, key); + result.index = try key_list_overflow.appendCopyIndex(key, true); + return; } if (!result.index.is_overflow) { @@ -692,7 +667,7 @@ pub fn BSSMap(comptime ValueType: type, comptime count: anytype, store_keys: boo } key_list_overflow.items[result.index.index] = slice; } else { - try key_list_overflow.append(self.map.allocator, slice); + try key_list_overflow.appendCopy(self.map.allocator, slice, false); } } } |