lib/chant/src/lexer/capacity.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

 1 const std = @import("std");
 2 const chant = @import("../root.zig");
 3 
 4 const Token = chant.token.Token;
 5 
 6 pub const storage_alignment: usize = @alignOf(Token);
 7 
 8 pub const Limits = struct {
 9     tokens: usize,
10 };
11 
12 pub const DeriveError = error{
13     CapacityOverflow,
14 };
15 
16 pub const Capacity = struct {
17     limits: Limits,
18     storage_bytes: usize,
19 
20     pub fn derive(limits: Limits) DeriveError!Capacity {
21         const storage_bytes = std.math.mul(
22             usize,
23             limits.tokens,
24             @sizeOf(Token),
25         ) catch return error.CapacityOverflow;
26         return .{
27             .limits = limits,
28             .storage_bytes = storage_bytes,
29         };
30     }
31 };
32 
33 fn modelCapacity(limits: Limits) DeriveError!Capacity {
34     const storage_bytes: u128 = @as(u128, limits.tokens) * @sizeOf(Token);
35     if (storage_bytes > std.math.maxInt(usize)) return error.CapacityOverflow;
36     return .{
37         .limits = limits,
38         .storage_bytes = @intCast(storage_bytes),
39     };
40 }
41 
42 test "token storage capacity matches an independent byte model" {
43     comptime {
44         @stardustClaim(
45             @import("alloc_phase").capacity.witness(@import("./root.zig").Storage, "chant_lexed_tokens_capacity"),
46             null,
47             null,
48             null,
49             null,
50             null,
51             null,
52         );
53     }
54 
55     for ([_]usize{ 0, 1, 37, std.math.maxInt(u16) }) |tokens| {
56         const limits = Limits{ .tokens = tokens };
57         try std.testing.expectEqual(try modelCapacity(limits), try Capacity.derive(limits));
58     }
59 }
60 
61 test "token storage capacity rejects overflowing limits" {
62     try std.testing.expectError(
63         error.CapacityOverflow,
64         Capacity.derive(.{ .tokens = std.math.maxInt(usize) }),
65     );
66 }