lib/zen/src/document/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.Heading);
  6 
  7 pub const Limits = plan_mod.Limits;
  8 pub const DeriveError = error{CapacityOverflow};
  9 
 10 pub const Capacity = struct {
 11     limits: Limits,
 12     headings_offset: usize,
 13     heading_bytes: usize,
 14     text_offset: usize,
 15     id_offset: usize,
 16     storage_bytes: usize,
 17 
 18     pub fn derive(limits: Limits) DeriveError!Capacity {
 19         const heading_bytes = std.math.mul(
 20             usize,
 21             limits.max_headings,
 22             @sizeOf(model.Heading),
 23         ) catch return error.CapacityOverflow;
 24         const id_offset = try added(heading_bytes, limits.max_text_bytes);
 25         return .{
 26             .limits = limits,
 27             .headings_offset = 0,
 28             .heading_bytes = heading_bytes,
 29             .text_offset = heading_bytes,
 30             .id_offset = id_offset,
 31             .storage_bytes = try added(id_offset, limits.max_id_bytes),
 32         };
 33     }
 34 };
 35 
 36 fn added(left: usize, right: usize) DeriveError!usize {
 37     return std.math.add(usize, left, right) catch error.CapacityOverflow;
 38 }
 39 
 40 fn independent(limits: Limits) DeriveError!Capacity {
 41     const heading_bytes = @as(u128, limits.max_headings) * @sizeOf(model.Heading);
 42     const id_offset = heading_bytes + limits.max_text_bytes;
 43     const storage_bytes = id_offset + limits.max_id_bytes;
 44     if (heading_bytes > std.math.maxInt(usize) or
 45         id_offset > std.math.maxInt(usize) or
 46         storage_bytes > std.math.maxInt(usize))
 47     {
 48         return error.CapacityOverflow;
 49     }
 50     return .{
 51         .limits = limits,
 52         .headings_offset = 0,
 53         .heading_bytes = @intCast(heading_bytes),
 54         .text_offset = @intCast(heading_bytes),
 55         .id_offset = @intCast(id_offset),
 56         .storage_bytes = @intCast(storage_bytes),
 57     };
 58 }
 59 
 60 test "heading capacity matches an independent byte model" {
 61     comptime {
 62         @stardustClaim(
 63             @import("alloc_phase").capacity.witness(@import("./root.zig").Storage, "zen_heading_capacity"),
 64             null,
 65             null,
 66             null,
 67             null,
 68             null,
 69             null,
 70         );
 71     }
 72 
 73     const limits = Limits{
 74         .max_headings = 2,
 75         .max_text_bytes = 11,
 76         .max_id_bytes = 11,
 77     };
 78     const capacity = try Capacity.derive(limits);
 79     try std.testing.expectEqual(try independent(limits), capacity);
 80     try std.testing.expectEqual(@as(usize, 118), capacity.storage_bytes);
 81     try std.testing.expectEqual(
 82         @as(usize, 2_293_760),
 83         (try Capacity.derive(plan_mod.default_limits)).storage_bytes,
 84     );
 85 }
 86 
 87 test "heading capacity handles zero and rejects overflow" {
 88     try std.testing.expectEqual(@as(usize, 0), (try Capacity.derive(.{
 89         .max_headings = 0,
 90         .max_text_bytes = 0,
 91         .max_id_bytes = 0,
 92     })).storage_bytes);
 93     try std.testing.expectError(error.CapacityOverflow, Capacity.derive(.{
 94         .max_headings = std.math.maxInt(usize),
 95         .max_text_bytes = 0,
 96         .max_id_bytes = 0,
 97     }));
 98     try std.testing.expectError(error.CapacityOverflow, Capacity.derive(.{
 99         .max_headings = 0,
100         .max_text_bytes = std.math.maxInt(usize),
101         .max_id_bytes = 1,
102     }));
103 }