lib/zen/src/frontmatter/storage.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 const plan_mod = @import("plan.zig");
  6 
  7 pub const Regions = struct {
  8     plan: plan_mod.Plan,
  9     pairs: []model.Pair,
 10     continuation: []u8,
 11 };
 12 
 13 pub const Status = struct {
 14     phase: alloc_phase.capacity.Phase,
 15     in_use: bool,
 16     storage_bytes: usize,
 17     max_pairs: usize,
 18     max_continuation_bytes: usize,
 19 };
 20 
 21 pub const Storage = struct {
 22     phase: alloc_phase.capacity.Phase,
 23     capacity: capacity_mod.Capacity,
 24     bytes: []align(capacity_mod.storage_alignment) u8,
 25     pairs: []model.Pair,
 26     continuation: []u8,
 27     in_use: bool = false,
 28 
 29     pub const Limits: type = capacity_mod.Limits;
 30     pub const Capacity: type = capacity_mod.Capacity;
 31     pub const Exhaustion: type = model.Exhaustion;
 32     pub const InitError = std.mem.Allocator.Error || capacity_mod.DeriveError;
 33     pub const AcquireError: type = model.Error;
 34 
 35     pub const claim: alloc_phase.capacity.Declaration = .{
 36         .source = .{
 37             .id = "zen.frontmatter_storage",
 38             .kind = .phase_static,
 39             .limit_source = .caller,
 40             .storage = .{
 41                 .covered = &.{
 42                     .{
 43                         .id = "frontmatter_pair_descriptors",
 44                         .lifetime = .steady,
 45                         .detail = "frontmatter pair descriptors",
 46                     },
 47                     .{
 48                         .id = "joined_continuation_values",
 49                         .lifetime = .steady,
 50                         .detail = "joined continuation values",
 51                     },
 52                 },
 53                 .excluded = &.{
 54                     "caller-owned Markdown source and borrowed ordinary metadata values",
 55                     "site catalog, rendered page, theme, and filesystem output owners",
 56                 },
 57             },
 58             .capacity = .{
 59                 .inputs = &.{
 60                     alloc_phase.capacity.bindInput(Limits, "max_pairs", "max_pairs"),
 61                     alloc_phase.capacity.bindInput(Limits, "max_continuation_bytes", "max_continuation_bytes"),
 62                 },
 63                 .type_selectors = &.{
 64                     alloc_phase.capacity.bindType(model.Pair, "pair"),
 65                 },
 66                 .nodes = &.{
 67                     .{ .input = 0 },
 68                     .{ .scale = .{ .node = 0, .coefficient = .{ .size_of_concrete_type = 0 } } },
 69                     .{ .alignment = .{ .node = 1, .alignment = .{ .literal = 16 } } },
 70                     .{ .input = 1 },
 71                     .{ .add = .{ .left = 2, .right = 3 } },
 72                 },
 73                 .assertions = &.{.{
 74                     .scope = .closure_total,
 75                     .measure = .retained,
 76                     .relation = .exact,
 77                     .expression = 4,
 78                 }},
 79             },
 80             .overload = .{
 81                 .kind = .reject_before_mutation,
 82                 .detail = "unclosed input, pair overflow, continuation overflow, and concurrent acquisition fail before storage mutation",
 83             },
 84             .risks = .{
 85                 .transitive = .{
 86                     .status = .witnessed,
 87                     .detail = "section discovery, field scanning, and continuation joining use only borrowed input and acquired slices",
 88                 },
 89                 .foreign = .{
 90                     .status = .excluded,
 91                     .detail = "frontmatter parsing crosses no operating-system or foreign callback boundary",
 92                 },
 93             },
 94             .obligations = &.{
 95                 .{ .key = "zen_frontmatter_capacity", .role = .capacity_model },
 96                 .{ .key = "zen_frontmatter_acquisition", .role = .custom },
 97                 .{ .key = "zen_frontmatter_oom", .role = .custom },
 98                 .{ .key = "zen_frontmatter_boundaries", .role = .overload },
 99                 .{ .key = "zen_frontmatter_reuse", .role = .overload },
100                 .{ .key = "zen_frontmatter_sealed", .role = .transitive_risk },
101                 .{ .key = "zen_frontmatter_root", .role = .custom },
102                 .{ .key = "zen_frontmatter_consumer", .role = .foreign_risk },
103             },
104         },
105         .bindings = .{
106             .owner = @This(),
107             .seal = .{
108                 .family = alloc_phase.capacity.selector(@This().activate),
109                 .premise = .{
110                     .class = .checked_semantic_fact,
111                     .authority = .checker,
112                 },
113             },
114             .teardown = .{
115                 .family = alloc_phase.capacity.selector(@This().deinit),
116                 .premise = .{
117                     .class = .checked_semantic_fact,
118                     .authority = .checker,
119                 },
120             },
121         },
122     };
123 
124     pub fn init(allocator: std.mem.Allocator, limits: Limits) InitError!Storage {
125         const capacity = try Capacity.derive(limits);
126         const bytes = try allocator.alignedAlloc(
127             u8,
128             .fromByteUnits(capacity_mod.storage_alignment),
129             capacity.storage_bytes,
130         );
131         return .{
132             .phase = .initialization,
133             .capacity = capacity,
134             .bytes = bytes,
135             .pairs = typedSlice(
136                 model.Pair,
137                 bytes,
138                 capacity.pairs_offset,
139                 limits.max_pairs,
140             ),
141             .continuation = bytes[capacity.continuation_offset..][0..limits.max_continuation_bytes],
142         };
143     }
144 
145     pub fn activate(self: *Storage) void {
146         std.debug.assert(self.phase == .initialization);
147         std.debug.assert(self.bytes.len == self.capacity.storage_bytes);
148         self.phase = .steady;
149     }
150 
151     pub fn acquire(self: *Storage, markdown: []const u8) AcquireError!Regions {
152         std.debug.assert(self.phase == .steady);
153         if (self.in_use) return error.FrontmatterStorageInUse;
154         const plan = try plan_mod.Plan.inspect(markdown, self.capacity.limits);
155         self.in_use = true;
156         return .{
157             .plan = plan,
158             .pairs = self.pairs[0..plan.pairs],
159             .continuation = self.continuation[0..plan.continuation_bytes],
160         };
161     }
162 
163     pub fn reset(self: *Storage) void {
164         std.debug.assert(self.phase == .steady);
165         std.debug.assert(self.in_use);
166         self.in_use = false;
167     }
168 
169     pub fn status(self: *const Storage) Status {
170         return .{
171             .phase = self.phase,
172             .in_use = self.in_use,
173             .storage_bytes = self.capacity.storage_bytes,
174             .max_pairs = self.capacity.limits.max_pairs,
175             .max_continuation_bytes = self.capacity.limits.max_continuation_bytes,
176         };
177     }
178 
179     pub fn deinit(self: *Storage, allocator: std.mem.Allocator) void {
180         std.debug.assert(self.phase != .teardown);
181         std.debug.assert(!self.in_use);
182         std.debug.assert(self.bytes.len == self.capacity.storage_bytes);
183         self.phase = .teardown;
184         allocator.free(self.bytes);
185         self.bytes = &.{};
186         self.pairs = &.{};
187         self.continuation = &.{};
188     }
189 };
190 
191 fn typedSlice(
192     comptime T: type,
193     bytes: []align(capacity_mod.storage_alignment) u8,
194     offset: usize,
195     count: usize,
196 ) []T {
197     const byte_count = count * @sizeOf(T);
198     const region: []align(@alignOf(T)) u8 = @alignCast(bytes[offset..][0..byte_count]);
199     return std.mem.bytesAsSlice(T, region);
200 }
201 
202 fn checkInitFailures(allocator: std.mem.Allocator) !void {
203     var storage = try Storage.init(allocator, .{
204         .max_pairs = 5,
205         .max_continuation_bytes = 38,
206     });
207     storage.deinit(allocator);
208 }
209 
210 test "frontmatter storage acquires one exact aligned region" {
211     comptime {
212         @stardustClaim(
213             @import("alloc_phase").capacity.witness(Storage, "zen_frontmatter_acquisition"),
214             null,
215             null,
216             null,
217             null,
218             null,
219             null,
220         );
221     }
222 
223     var counting = std.testing.FailingAllocator.init(std.testing.allocator, .{});
224     const limits = capacity_mod.Limits{
225         .max_pairs = 5,
226         .max_continuation_bytes = 38,
227     };
228     const capacity = try capacity_mod.Capacity.derive(limits);
229     var storage = try Storage.init(counting.allocator(), limits);
230     defer storage.deinit(counting.allocator());
231 
232     try std.testing.expectEqual(@as(usize, 1), counting.alloc_index);
233     try std.testing.expectEqual(capacity.storage_bytes, counting.allocated_bytes);
234     try std.testing.expectEqual(alloc_phase.capacity.Phase.initialization, storage.status().phase);
235     storage.activate();
236     const source = "---\ndescription:\n  bounded\n---\n# Body\n";
237     const regions = try storage.acquire(source);
238     defer storage.reset();
239     const base = @intFromPtr(storage.bytes.ptr);
240     try std.testing.expectEqual(base + capacity.pairs_offset, @intFromPtr(regions.pairs.ptr));
241     try std.testing.expectEqual(base + capacity.continuation_offset, @intFromPtr(regions.continuation.ptr));
242 }
243 
244 test "frontmatter storage retries after every allocation failure" {
245     comptime {
246         @stardustClaim(
247             @import("alloc_phase").capacity.witness(Storage, "zen_frontmatter_oom"),
248             null,
249             null,
250             null,
251             null,
252             null,
253             null,
254         );
255     }
256 
257     try std.testing.checkAllAllocationFailures(std.testing.allocator, checkInitFailures, .{});
258 }
259 
260 comptime {
261     alloc_phase.capacity.requireAllocatorRejectingOwnerShape(Storage);
262 }