96 lines
3.4 KiB
Zig
96 lines
3.4 KiB
Zig
const std = @import("std");
|
|
|
|
const ctx = @import("../AppContext.zig");
|
|
|
|
/// Interned string ID. A string can be converted to a stable integer constant,
|
|
/// called an *atom*. The value of an atom for a given string is guaranteed to
|
|
/// be stable throughout a program's runtime, but not across different runs.
|
|
/// There can be no more than 2¹⁶ atoms.
|
|
pub const Atom = enum(u16) {
|
|
// VOLATILE When modifying the list of explicitly defined atoms (i.e. any
|
|
// explicit enum value), we need to update `Atoms.init` implementation.
|
|
|
|
/// Atom representing an empty string, i.e. `""`.
|
|
empty,
|
|
_,
|
|
|
|
/// Cast an integer into an atom. This can produce an invalid atom.
|
|
pub fn fromInt(value: u16) Atom {
|
|
return @enumFromInt(value);
|
|
}
|
|
|
|
/// Cast an index into an atom. This can produce an invalid atom. The caller
|
|
/// asserts that the index is not greater than the max atom value.
|
|
pub fn fromIndex(index: usize) Atom {
|
|
return @enumFromInt(@as(u16, @intCast(index)));
|
|
}
|
|
|
|
/// Cast an index into an atom. This can produce an invalid atom. Returns an
|
|
/// error if the index is greater than the max atom value.
|
|
pub fn fromIndexSafe(index: usize) error{Overflow}!Atom {
|
|
return @enumFromInt(std.math.cast(u16, index) orelse return error.Overflow);
|
|
}
|
|
|
|
/// Turn a string into an atom. Returns either an existing atom or makes a
|
|
/// new one, if necessary. This will always produce a valid atom. Will not
|
|
/// return any error if the atom already exists.
|
|
pub fn fromString(string: []const u8) error{ OutOfMemory, OutOfAtoms }!Atom {
|
|
const allocator_general = ctx.allocator_general;
|
|
const allocator_persistent = ctx.allocator_persistent;
|
|
const io = ctx.io;
|
|
const atoms = ctx.atoms;
|
|
|
|
atoms.mutex.lockUncancelable(io);
|
|
defer atoms.mutex.unlock(io);
|
|
|
|
const entry = try atoms.map.getOrPut(allocator_general, string);
|
|
|
|
if (entry.found_existing) {
|
|
return entry.value_ptr.*;
|
|
} else {
|
|
errdefer _ = atoms.map.remove(string);
|
|
const atom = Atom.fromIndexSafe(atoms.array.items.len) catch |err| switch (err) {
|
|
error.Overflow => return error.OutOfAtoms,
|
|
};
|
|
|
|
try atoms.array.ensureUnusedCapacity(allocator_general, 1);
|
|
const owned_string = try allocator_persistent.dupeZ(u8, string);
|
|
|
|
entry.key_ptr.* = owned_string;
|
|
entry.value_ptr.* = atom;
|
|
|
|
atoms.array.appendAssumeCapacity(owned_string);
|
|
return atom;
|
|
}
|
|
}
|
|
|
|
/// Turn a string into an atom, if the string has been already registered as
|
|
/// an atom. Returns `null` otherwise. This will always produce a valid
|
|
/// atom.
|
|
pub fn fromStringIfExists(string: []const u8) ?Atom {
|
|
const io = ctx.io;
|
|
const atoms = ctx.atoms;
|
|
|
|
atoms.mutex.lockUncancelable(io);
|
|
defer atoms.mutex.unlock(io);
|
|
|
|
return atoms.map.get(string);
|
|
}
|
|
|
|
/// Cast an atom into an integer.
|
|
pub fn toInt(self: Atom) u16 {
|
|
return @intFromEnum(self);
|
|
}
|
|
|
|
/// Cast an atom into a string. The caller asserts that the atom is valid.
|
|
pub fn toString(self: Atom) [:0]const u8 {
|
|
const io = ctx.io;
|
|
const atoms = ctx.atoms;
|
|
|
|
atoms.mutex.lockUncancelable(io);
|
|
defer atoms.mutex.unlock(io);
|
|
|
|
return atoms.array.items[self.toInt()];
|
|
}
|
|
};
|