| 1 | buffer: std.ArrayList(u8) = .empty, |
| 2 | table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .empty, |
| 3 | |
| 4 | pub fn deinit(self: *Self, gpa: Allocator) void { |
| 5 | self.buffer.deinit(gpa); |
| 6 | self.table.deinit(gpa); |
| 7 | } |
| 8 | |
| 9 | pub fn insert(self: *Self, gpa: Allocator, string: []const u8) !u32 { |
| 10 | const gop = try self.table.getOrPutContextAdapted(gpa, @as([]const u8, string), StringIndexAdapter{ |
| 11 | .bytes = &self.buffer, |
| 12 | }, StringIndexContext{ |
| 13 | .bytes = &self.buffer, |
| 14 | }); |
| 15 | if (gop.found_existing) return gop.key_ptr.*; |
| 16 | |
| 17 | try self.buffer.ensureUnusedCapacity(gpa, string.len + 1); |
| 18 | const new_off: u32 = @intCast(self.buffer.items.len); |
| 19 | |
| 20 | self.buffer.appendSliceAssumeCapacity(string); |
| 21 | self.buffer.appendAssumeCapacity(0); |
| 22 | |
| 23 | gop.key_ptr.* = new_off; |
| 24 | |
| 25 | return new_off; |
| 26 | } |
| 27 | |
| 28 | pub fn getOffset(self: *Self, string: []const u8) ?u32 { |
| 29 | return self.table.getKeyAdapted(string, StringIndexAdapter{ |
| 30 | .bytes = &self.buffer, |
| 31 | }); |
| 32 | } |
| 33 | |
| 34 | pub fn get(self: Self, off: u32) ?[:0]const u8 { |
| 35 | if (off >= self.buffer.items.len) return null; |
| 36 | return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.buffer.items.ptr + off)), 0); |
| 37 | } |
| 38 | |
| 39 | pub fn getAssumeExists(self: Self, off: u32) [:0]const u8 { |
| 40 | return self.get(off) orelse unreachable; |
| 41 | } |
| 42 | |
| 43 | const std = @import("std"); |
| 44 | const mem = std.mem; |
| 45 | |
| 46 | const Allocator = mem.Allocator; |
| 47 | const Self = @This(); |
| 48 | const StringIndexAdapter = std.hash_map.StringIndexAdapter; |
| 49 | const StringIndexContext = std.hash_map.StringIndexContext; |