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 }