lib/zen/src/frontmatter/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.Pair);
 6 pub const Limits = plan_mod.Limits;
 7 pub const DeriveError = error{CapacityOverflow};
 8 
 9 pub const Capacity = struct {
10     limits: Limits,
11     pairs_offset: usize,
12     pair_bytes: usize,
13     continuation_offset: usize,
14     storage_bytes: usize,
15 
16     pub fn derive(limits: Limits) DeriveError!Capacity {
17         const pairs = try placed(model.Pair, 0, limits.max_pairs);
18         const storage_bytes = try added(pairs.end, limits.max_continuation_bytes);
19         return .{
20             .limits = limits,
21             .pairs_offset = pairs.start,
22             .pair_bytes = pairs.bytes,
23             .continuation_offset = pairs.end,
24             .storage_bytes = storage_bytes,
25         };
26     }
27 };
28 
29 const Region = struct {
30     start: usize,
31     bytes: usize,
32     end: usize,
33 };
34 
35 fn placed(comptime T: type, offset: usize, count: usize) DeriveError!Region {
36     const mask: usize = @alignOf(T) - 1;
37     const start = (try added(offset, mask)) & ~mask;
38     const bytes = std.math.mul(usize, count, @sizeOf(T)) catch return error.CapacityOverflow;
39     return .{ .start = start, .bytes = bytes, .end = try added(start, bytes) };
40 }
41 
42 fn added(left: usize, right: usize) DeriveError!usize {
43     return std.math.add(usize, left, right) catch error.CapacityOverflow;
44 }
45 
46 fn modelCapacity(limits: Limits) DeriveError!Capacity {
47     const pair_bytes = @as(u128, limits.max_pairs) * @sizeOf(model.Pair);
48     const continuation_offset = pair_bytes;
49     const storage_bytes = continuation_offset + limits.max_continuation_bytes;
50     if (pair_bytes > std.math.maxInt(usize) or
51         continuation_offset > std.math.maxInt(usize) or
52         storage_bytes > std.math.maxInt(usize))
53     {
54         return error.CapacityOverflow;
55     }
56     return .{
57         .limits = limits,
58         .pairs_offset = 0,
59         .pair_bytes = @intCast(pair_bytes),
60         .continuation_offset = @intCast(continuation_offset),
61         .storage_bytes = @intCast(storage_bytes),
62     };
63 }
64 
65 test "frontmatter capacity matches an independent byte model" {
66     comptime {
67         @stardustClaim(
68             @import("alloc_phase").capacity.witness(@import("./root.zig").Storage, "zen_frontmatter_capacity"),
69             null,
70             null,
71             null,
72             null,
73             null,
74             null,
75         );
76     }
77 
78     const limits = Limits{ .max_pairs = 5, .max_continuation_bytes = 38 };
79     const capacity = try Capacity.derive(limits);
80     try std.testing.expectEqual(try modelCapacity(limits), capacity);
81     try std.testing.expectEqual(@as(usize, 198), capacity.storage_bytes);
82 }
83 
84 test "frontmatter capacity handles zero and rejects overflow" {
85     try std.testing.expectEqual(@as(usize, 0), (try Capacity.derive(.{
86         .max_pairs = 0,
87         .max_continuation_bytes = 0,
88     })).storage_bytes);
89     try std.testing.expectError(error.CapacityOverflow, Capacity.derive(.{
90         .max_pairs = std.math.maxInt(usize),
91         .max_continuation_bytes = 1,
92     }));
93 }