lib/filigree/src/fallback/workspace.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const alloc_phase = @import("alloc_phase");
3 const capacity_mod = @import("capacity.zig");
4 const model = @import("model.zig");
5
6 pub const Status = struct {
7 phase: alloc_phase.capacity.Phase,
8 storage_bytes: usize,
9 max_source_units: usize,
10 scalar_count: usize,
11 cluster_count: usize,
12 range_count: usize,
13 };
14
15 pub const Workspace = struct {
16 phase: alloc_phase.capacity.Phase,
17 capacity: capacity_mod.Capacity,
18 bytes: []align(capacity_mod.storage_alignment) u8,
19 scalars: std.ArrayList(model.Scalar),
20 clusters: std.ArrayList(model.SourceCluster),
21 ranges: std.ArrayList(model.SegmentRange),
22
23 pub const Limits: type = capacity_mod.Limits;
24 pub const Capacity: type = capacity_mod.Capacity;
25 pub const Exhaustion: type = model.Exhaustion;
26 pub const InitError = std.mem.Allocator.Error || capacity_mod.DeriveError;
27
28 pub const claim: alloc_phase.capacity.Declaration = .{
29 .source = .{
30 .id = "filigree.fallback_workspace",
31 .kind = .phase_static,
32 .limit_source = .caller,
33 .storage = .{
34 .covered = &.{
35 .{
36 .id = "decoded_fallback_scalars",
37 .lifetime = .steady,
38 .detail = "decoded scalar descriptors used for face coverage",
39 },
40 .{
41 .id = "grapheme_safe_fallback_clusters",
42 .lifetime = .steady,
43 .detail = "grapheme and joining-cluster descriptors",
44 },
45 .{
46 .id = "fallback_face_segment_ranges",
47 .lifetime = .steady,
48 .detail = "script and selected-face segment descriptors",
49 },
50 },
51 .excluded = &.{
52 "caller-owned source units and fallback candidates",
53 "shape engine, shape output, and script-run storage",
54 "caller-owned reported face segments",
55 },
56 },
57 .capacity = .{
58 .inputs = &.{
59 alloc_phase.capacity.bindInput(Limits, "max_source_units", "max_source_units"),
60 },
61 .type_selectors = &.{
62 alloc_phase.capacity.bindType(model.Scalar, "scalar"),
63 alloc_phase.capacity.bindType(model.SourceCluster, "sourcecluster"),
64 alloc_phase.capacity.bindType(model.SegmentRange, "segmentrange"),
65 },
66 .nodes = &.{
67 .{ .input = 0 },
68 .{ .scale = .{ .node = 0, .coefficient = .{ .size_of_concrete_type = 0 } } },
69 .{ .alignment = .{ .node = 1, .alignment = .{ .concrete_type = 1 } } },
70 .{ .scale = .{ .node = 0, .coefficient = .{ .size_of_concrete_type = 1 } } },
71 .{ .add = .{ .left = 2, .right = 3 } },
72 .{ .alignment = .{ .node = 4, .alignment = .{ .concrete_type = 2 } } },
73 .{ .scale = .{ .node = 0, .coefficient = .{ .size_of_concrete_type = 2 } } },
74 .{ .add = .{ .left = 5, .right = 6 } },
75 },
76 .assertions = &.{.{
77 .scope = .closure_total,
78 .measure = .retained,
79 .relation = .exact,
80 .expression = 7,
81 }},
82 },
83 .overload = .{
84 .kind = .reject_before_mutation,
85 .detail = "a max-plus-one source rejects before prior scalar, cluster, or face-range state changes",
86 },
87 .risks = .{
88 .transitive = .{
89 .status = .witnessed,
90 .detail = "fallback collection and segmentation use only activated typed regions",
91 },
92 .foreign = .{
93 .status = .excluded,
94 .detail = "workspace collection and segmentation cross no operating-system or foreign callback boundary",
95 },
96 },
97 .obligations = &.{
98 .{ .key = "filigree_fallback_workspace_capacity", .role = .capacity_model },
99 .{ .key = "filigree_fallback_workspace_acquisition", .role = .custom },
100 .{ .key = "filigree_fallback_workspace_oom", .role = .custom },
101 .{ .key = "filigree_fallback_workspace_boundaries", .role = .overload },
102 .{ .key = "filigree_fallback_workspace_reuse", .role = .overload },
103 .{ .key = "filigree_fallback_workspace_sealed_transitive_risk", .role = .transitive_risk },
104 .{ .key = "filigree_fallback_workspace_sealed_foreign_risk", .role = .foreign_risk },
105 .{ .key = "filigree_fallback_workspace_root", .role = .custom },
106 },
107 },
108 .bindings = .{
109 .owner = @This(),
110 .seal = .{
111 .family = alloc_phase.capacity.selector(@This().activate),
112 .premise = .{
113 .class = .checked_semantic_fact,
114 .authority = .checker,
115 },
116 },
117 .teardown = .{
118 .family = alloc_phase.capacity.selector(@This().deinit),
119 .premise = .{
120 .class = .checked_semantic_fact,
121 .authority = .checker,
122 },
123 },
124 },
125 };
126
127 pub fn init(allocator: std.mem.Allocator, limits: Limits) InitError!Workspace {
128 const capacity = try Capacity.derive(limits);
129 const bytes = try allocator.alignedAlloc(
130 u8,
131 .fromByteUnits(capacity_mod.storage_alignment),
132 capacity.storage_bytes,
133 );
134 return .{
135 .phase = .initialization,
136 .capacity = capacity,
137 .bytes = bytes,
138 .scalars = .initBuffer(typedSlice(model.Scalar, bytes, capacity.scalar_offset, limits.max_source_units)),
139 .clusters = .initBuffer(typedSlice(model.SourceCluster, bytes, capacity.cluster_offset, limits.max_source_units)),
140 .ranges = .initBuffer(typedSlice(model.SegmentRange, bytes, capacity.range_offset, limits.max_source_units)),
141 };
142 }
143
144 pub fn activate(self: *Workspace) void {
145 std.debug.assert(self.phase == .initialization);
146 self.assertStorage();
147 self.phase = .steady;
148 }
149
150 pub fn deinit(self: *Workspace, allocator: std.mem.Allocator) void {
151 std.debug.assert(self.phase != .teardown);
152 self.assertStorage();
153 self.phase = .teardown;
154 allocator.free(self.bytes);
155 self.bytes = &.{};
156 self.scalars = .empty;
157 self.clusters = .empty;
158 self.ranges = .empty;
159 }
160
161 pub fn begin(self: *Workspace, source_units: usize) Exhaustion!void {
162 std.debug.assert(self.phase == .steady);
163 if (source_units > self.capacity.limits.max_source_units) {
164 return error.SourceUnitCapacityExceeded;
165 }
166 self.reset();
167 }
168
169 pub fn reset(self: *Workspace) void {
170 std.debug.assert(self.phase == .steady);
171 self.scalars.clearRetainingCapacity();
172 self.clusters.clearRetainingCapacity();
173 self.ranges.clearRetainingCapacity();
174 }
175
176 pub fn status(self: *const Workspace) Status {
177 return .{
178 .phase = self.phase,
179 .storage_bytes = self.capacity.storage_bytes,
180 .max_source_units = self.capacity.limits.max_source_units,
181 .scalar_count = self.scalars.items.len,
182 .cluster_count = self.clusters.items.len,
183 .range_count = self.ranges.items.len,
184 };
185 }
186
187 fn assertStorage(self: *const Workspace) void {
188 std.debug.assert(self.bytes.len == self.capacity.storage_bytes);
189 std.debug.assert(self.scalars.capacity == self.capacity.limits.max_source_units);
190 std.debug.assert(self.clusters.capacity == self.capacity.limits.max_source_units);
191 std.debug.assert(self.ranges.capacity == self.capacity.limits.max_source_units);
192 std.debug.assert(self.scalars.items.len <= self.scalars.capacity);
193 std.debug.assert(self.clusters.items.len <= self.clusters.capacity);
194 std.debug.assert(self.ranges.items.len <= self.ranges.capacity);
195 }
196 };
197
198 comptime {
199 alloc_phase.capacity.requireAllocatorRejectingOwnerShape(Workspace);
200 }
201
202 fn typedSlice(
203 comptime T: type,
204 bytes: []align(capacity_mod.storage_alignment) u8,
205 offset: usize,
206 count: usize,
207 ) []T {
208 const byte_count = count * @sizeOf(T);
209 const region: []align(@alignOf(T)) u8 = @alignCast(bytes[offset..][0..byte_count]);
210 return std.mem.bytesAsSlice(T, region);
211 }
212
213 fn activatedWorkspace(allocator: std.mem.Allocator, max_source_units: usize) !Workspace {
214 var workspace = try Workspace.init(allocator, .{ .max_source_units = max_source_units });
215 workspace.activate();
216 return workspace;
217 }
218
219 fn checkInitFailures(allocator: std.mem.Allocator) !void {
220 var workspace = try Workspace.init(allocator, .{ .max_source_units = 13 });
221 workspace.deinit(allocator);
222 }
223
224 test "fallback workspace acquires one exact region" {
225 comptime {
226 alloc_phase.capacity.record(
227 alloc_phase.capacity.witness(Workspace, "filigree_fallback_workspace_acquisition"),
228 );
229 }
230
231 var counting = std.testing.FailingAllocator.init(std.testing.allocator, .{});
232 const limits = capacity_mod.Limits{ .max_source_units = 13 };
233 const capacity = try capacity_mod.Capacity.derive(limits);
234 var workspace = try Workspace.init(counting.allocator(), limits);
235 defer workspace.deinit(counting.allocator());
236
237 try std.testing.expectEqual(@as(usize, 1), counting.alloc_index);
238 try std.testing.expectEqual(capacity.storage_bytes, counting.allocated_bytes);
239 workspace.activate();
240 const base = @intFromPtr(workspace.bytes.ptr);
241 try std.testing.expectEqual(base + capacity.scalar_offset, @intFromPtr(workspace.scalars.allocatedSlice().ptr));
242 try std.testing.expectEqual(base + capacity.cluster_offset, @intFromPtr(workspace.clusters.allocatedSlice().ptr));
243 try std.testing.expectEqual(base + capacity.range_offset, @intFromPtr(workspace.ranges.allocatedSlice().ptr));
244 }
245
246 test "fallback workspace retries after its only allocation failure" {
247 comptime {
248 alloc_phase.capacity.record(
249 alloc_phase.capacity.witness(Workspace, "filigree_fallback_workspace_oom"),
250 );
251 }
252 try std.testing.checkAllAllocationFailures(std.testing.allocator, checkInitFailures, .{});
253 }
254
255 test "fallback workspace rejects max plus one transactionally and reuses storage" {
256 comptime {
257 alloc_phase.capacity.record(
258 alloc_phase.capacity.witness(Workspace, "filigree_fallback_workspace_boundaries"),
259 );
260 }
261 comptime {
262 alloc_phase.capacity.record(
263 alloc_phase.capacity.witness(Workspace, "filigree_fallback_workspace_reuse"),
264 );
265 }
266 comptime {
267 alloc_phase.capacity.record(
268 alloc_phase.capacity.witness(Workspace, "filigree_fallback_workspace_sealed_transitive_risk"),
269 );
270 }
271 comptime {
272 alloc_phase.capacity.record(
273 alloc_phase.capacity.witness(Workspace, "filigree_fallback_workspace_sealed_foreign_risk"),
274 );
275 }
276
277 var workspace = try activatedWorkspace(std.testing.allocator, 3);
278 defer workspace.deinit(std.testing.allocator);
279 try workspace.begin(3);
280 workspace.scalars.appendAssumeCapacity(.{ .codepoint = 'A', .start = 0, .end = 1 });
281 const prior = workspace.scalars.items[0];
282 const pointer = workspace.scalars.allocatedSlice().ptr;
283 const status = workspace.status();
284
285 try std.testing.expectError(error.SourceUnitCapacityExceeded, workspace.begin(4));
286 try std.testing.expectEqual(prior, workspace.scalars.items[0]);
287 try std.testing.expectEqual(status, workspace.status());
288 try std.testing.expectEqual(pointer, workspace.scalars.allocatedSlice().ptr);
289
290 try workspace.begin(3);
291 try std.testing.expectEqual(@as(usize, 0), workspace.status().scalar_count);
292 try std.testing.expectEqual(pointer, workspace.scalars.allocatedSlice().ptr);
293 }
294
295 test "fallback workspace is exported through its namespace root" {
296 comptime {
297 alloc_phase.capacity.record(
298 alloc_phase.capacity.witness(Workspace, "filigree_fallback_workspace_root"),
299 );
300 }
301 try std.testing.expect(@import("root.zig").Workspace == Workspace);
302 }