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 }