MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1const std = @import("std");
2
3pub const InternedString = struct {
4 ptr: [*]const u8,
5 len: u32,
6
7 pub fn slice(self: InternedString) []const u8 {
8 return self.ptr[0..self.len];
9 }
10
11 pub fn eql(a: InternedString, b: InternedString) bool {
12 return a.ptr == b.ptr and a.len == b.len;
13 }
14
15 pub fn hash(self: InternedString) u64 {
16 return @intFromPtr(self.ptr);
17 }
18
19 pub const empty: InternedString = .{ .ptr = "", .len = 0 };
20};
21
22pub const StringPool = struct {
23 allocator: std.mem.Allocator,
24 strings: std.StringHashMap(InternedString),
25 storage: std.ArrayListUnmanaged([]const u8),
26
27 pub fn init(allocator: std.mem.Allocator) StringPool {
28 return .{
29 .allocator = allocator,
30 .strings = std.StringHashMap(InternedString).init(allocator),
31 .storage = std.ArrayListUnmanaged([]const u8){},
32 };
33 }
34
35 pub fn deinit(self: *StringPool) void {
36 for (self.storage.items) |s| {
37 self.allocator.free(s);
38 }
39 self.storage.deinit(self.allocator);
40 self.strings.deinit();
41 }
42
43 pub fn intern(self: *StringPool, str: []const u8) !InternedString {
44 if (str.len == 0) return InternedString.empty;
45 if (self.strings.get(str)) |interned| return interned;
46
47 const owned = try self.allocator.dupe(u8, str);
48 errdefer self.allocator.free(owned);
49
50 try self.storage.append(self.allocator, owned);
51
52 const interned = InternedString{
53 .ptr = owned.ptr,
54 .len = @intCast(owned.len),
55 };
56
57 try self.strings.put(owned, interned);
58 return interned;
59 }
60
61 pub fn internOwned(self: *StringPool, owned: []const u8) !InternedString {
62 if (owned.len == 0) return InternedString.empty;
63
64 if (self.strings.get(owned)) |interned| {
65 self.allocator.free(@constCast(owned));
66 return interned;
67 }
68
69 try self.storage.append(self.allocator, owned);
70
71 const interned = InternedString{
72 .ptr = owned.ptr,
73 .len = @intCast(owned.len),
74 };
75
76 try self.strings.put(owned, interned);
77 return interned;
78 }
79
80 pub fn stats(self: *const StringPool) Stats {
81 var total_bytes: usize = 0;
82 for (self.storage.items) |s| {
83 total_bytes += s.len;
84 }
85 return .{
86 .string_count = self.storage.items.len,
87 .total_bytes = total_bytes,
88 };
89 }
90
91 pub const Stats = struct {
92 string_count: usize,
93 total_bytes: usize,
94 };
95};
96
97pub const CommonStrings = struct {
98 pool: *StringPool,
99
100 lodash: InternedString = InternedString.empty,
101 react: InternedString = InternedString.empty,
102 typescript: InternedString = InternedString.empty,
103 webpack: InternedString = InternedString.empty,
104 babel: InternedString = InternedString.empty,
105 eslint: InternedString = InternedString.empty,
106 jest: InternedString = InternedString.empty,
107 express: InternedString = InternedString.empty,
108
109 caret: InternedString = InternedString.empty, // ^
110 tilde: InternedString = InternedString.empty, // ~
111
112 pub fn init(pool: *StringPool) !CommonStrings {
113 return .{
114 .pool = pool,
115 .lodash = try pool.intern("lodash"),
116 .react = try pool.intern("react"),
117 .typescript = try pool.intern("typescript"),
118 .webpack = try pool.intern("webpack"),
119 .babel = try pool.intern("@babel/core"),
120 .eslint = try pool.intern("eslint"),
121 .jest = try pool.intern("jest"),
122 .express = try pool.intern("express"),
123 .caret = try pool.intern("^"),
124 .tilde = try pool.intern("~"),
125 };
126 }
127};