1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
const StringTable = @This();
const std = @import("std");
const log = std.log.scoped(.strtab);
const mem = std.mem;
const Allocator = mem.Allocator;
allocator: *Allocator,
buffer: std.ArrayListUnmanaged(u8) = .{},
cache: std.StringHashMapUnmanaged(u32) = .{},
pub const Error = error{OutOfMemory};
pub fn init(allocator: *Allocator) Error!StringTable {
var strtab = StringTable{
.allocator = allocator,
};
try strtab.buffer.append(allocator, 0);
return strtab;
}
pub fn deinit(self: *StringTable) void {
{
var it = self.cache.keyIterator();
while (it.next()) |key| {
self.allocator.free(key.*);
}
}
self.cache.deinit(self.allocator);
self.buffer.deinit(self.allocator);
}
pub fn getOrPut(self: *StringTable, string: []const u8) Error!u32 {
if (self.cache.get(string)) |off| {
log.debug("reusing string '{s}' at offset 0x{x}", .{ string, off });
return off;
}
try self.buffer.ensureUnusedCapacity(self.allocator, string.len + 1);
const new_off = @intCast(u32, self.buffer.items.len);
log.debug("writing new string '{s}' at offset 0x{x}", .{ string, new_off });
self.buffer.appendSliceAssumeCapacity(string);
self.buffer.appendAssumeCapacity(0);
try self.cache.putNoClobber(self.allocator, try self.allocator.dupe(u8, string), new_off);
return new_off;
}
pub fn get(self: StringTable, off: u32) ?[]const u8 {
if (off >= self.buffer.items.len) return null;
return mem.spanZ(@ptrCast([*:0]const u8, self.buffer.items.ptr + off));
}
pub fn asSlice(self: StringTable) []const u8 {
return self.buffer.items;
}
pub fn size(self: StringTable) u64 {
return self.buffer.items.len;
}
|