71 lines
2.2 KiB
Zig
71 lines
2.2 KiB
Zig
//! Reference counted, owned, and interned strings
|
|
|
|
const std = @import("std");
|
|
const Allocator = std.mem.Allocator;
|
|
|
|
bytes: std.ArrayListUnmanaged(u8),
|
|
map: std.StringHashMapUnmanaged(void),
|
|
|
|
// Construct new empty string intern pool
|
|
pub const empty: @This() = .{ .bytes = .empty, .map = .empty };
|
|
|
|
// Free all memory allocated by the intern pool. Additionally, ensure
|
|
// all memory allocated by the pool in it's lifetime is also freed.
|
|
pub fn deinit(self: *@This(), gpa: Allocator) void {
|
|
self.bytes.deinit(gpa);
|
|
self.map.deinit(gpa);
|
|
self.* = undefined;
|
|
}
|
|
|
|
// Add a string to the intern pool, returning a stable pointer to the string.
|
|
// The pointer will last as far as the string is not removed with `remove()`.
|
|
pub fn add(self: *@This(), gpa: Allocator, str: []const u8) ![]const u8 {
|
|
if (self.map.getKey(str)) |entry| {
|
|
// Return pre-existing
|
|
return entry;
|
|
}
|
|
|
|
// Allocate memory for & copy the string
|
|
const str_start_idx = self.bytes.items.len;
|
|
try self.bytes.appendSlice(gpa, str);
|
|
const entry_str = self.bytes.items[str_start_idx..];
|
|
errdefer self.bytes.items.len = str_start_idx;
|
|
|
|
// Add the entry to our map, use the owned string
|
|
try self.map.putNoClobber(gpa, entry_str, void{});
|
|
|
|
// Return the stable pointer
|
|
return entry_str;
|
|
}
|
|
|
|
test "Intern Pool" {
|
|
const gpa = std.testing.allocator;
|
|
try generalWorkload(gpa);
|
|
|
|
try std.testing.checkAllAllocationFailures(gpa, generalWorkload, .{});
|
|
}
|
|
|
|
fn generalWorkload(gpa: Allocator) !void {
|
|
var pool: @This() = .empty;
|
|
defer pool.deinit(gpa);
|
|
|
|
// Inserting elements
|
|
const a = try pool.add(gpa, "x");
|
|
const b = try pool.add(gpa, "y");
|
|
const c = try pool.add(gpa, "z");
|
|
const d = try pool.add(gpa, "z");
|
|
const e = try pool.add(gpa, "y");
|
|
|
|
try std.testing.expectEqualSlices(u8, "x", a);
|
|
try std.testing.expectEqualSlices(u8, "y", b);
|
|
try std.testing.expectEqualSlices(u8, "z", c);
|
|
try std.testing.expectEqualSlices(u8, "z", d);
|
|
try std.testing.expectEqualSlices(u8, "y", e);
|
|
|
|
try std.testing.expectEqual(b.ptr, e.ptr);
|
|
try std.testing.expectEqual(c.ptr, d.ptr);
|
|
|
|
try std.testing.expectEqual(pool.map.size, 3);
|
|
try std.testing.expectEqualSlices(u8, "xyz", pool.bytes.items);
|
|
}
|