lib/zen/src/footnote/capacity.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const model = @import("model.zig");
3 const plan_mod = @import("plan.zig");
4
5 pub const storage_alignment: usize = @alignOf(model.Definition);
6
7 pub const Limits = plan_mod.Limits;
8 pub const DeriveError = error{CapacityOverflow};
9
10 pub const Capacity = struct {
11 limits: Limits,
12 definitions_offset: usize,
13 definition_bytes: usize,
14 joined_text_offset: usize,
15 storage_bytes: usize,
16
17 pub fn derive(limits: Limits) DeriveError!Capacity {
18 const definition_bytes = std.math.mul(
19 usize,
20 limits.max_definitions,
21 @sizeOf(model.Definition),
22 ) catch return error.CapacityOverflow;
23 return .{
24 .limits = limits,
25 .definitions_offset = 0,
26 .definition_bytes = definition_bytes,
27 .joined_text_offset = definition_bytes,
28 .storage_bytes = try added(definition_bytes, limits.max_joined_text_bytes),
29 };
30 }
31 };
32
33 fn added(left: usize, right: usize) DeriveError!usize {
34 return std.math.add(usize, left, right) catch error.CapacityOverflow;
35 }
36
37 fn independent(limits: Limits) DeriveError!Capacity {
38 const definition_bytes = @as(u128, limits.max_definitions) * @sizeOf(model.Definition);
39 const storage_bytes = definition_bytes + limits.max_joined_text_bytes;
40 if (definition_bytes > std.math.maxInt(usize) or storage_bytes > std.math.maxInt(usize)) {
41 return error.CapacityOverflow;
42 }
43 return .{
44 .limits = limits,
45 .definitions_offset = 0,
46 .definition_bytes = @intCast(definition_bytes),
47 .joined_text_offset = @intCast(definition_bytes),
48 .storage_bytes = @intCast(storage_bytes),
49 };
50 }
51
52 test "footnote capacity matches an independent byte model" {
53 comptime {
54 @stardustClaim(
55 @import("alloc_phase").capacity.witness(@import("./root.zig").Storage, "zen_footnote_capacity"),
56 null,
57 null,
58 null,
59 null,
60 null,
61 null,
62 );
63 }
64
65 const limits = Limits{
66 .max_definitions = 3,
67 .max_joined_text_bytes = 42,
68 };
69 const capacity = try Capacity.derive(limits);
70 try std.testing.expectEqual(try independent(limits), capacity);
71 try std.testing.expectEqual(@as(usize, 210), capacity.storage_bytes);
72 try std.testing.expectEqual(
73 @as(usize, 94_208),
74 (try Capacity.derive(plan_mod.default_limits)).storage_bytes,
75 );
76 }
77
78 test "footnote capacity handles zero and rejects overflow" {
79 try std.testing.expectEqual(@as(usize, 0), (try Capacity.derive(.{
80 .max_definitions = 0,
81 .max_joined_text_bytes = 0,
82 })).storage_bytes);
83 try std.testing.expectError(error.CapacityOverflow, Capacity.derive(.{
84 .max_definitions = std.math.maxInt(usize),
85 .max_joined_text_bytes = 0,
86 }));
87 try std.testing.expectError(error.CapacityOverflow, Capacity.derive(.{
88 .max_definitions = 1,
89 .max_joined_text_bytes = std.math.maxInt(usize),
90 }));
91 }