aboutsummaryrefslogtreecommitdiff
path: root/src/link/MachO/StringTable.zig
blob: 43770afdc1eeb8ee45ee6ab65e9c973bf02bb9be (plain)
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;
}