lib/trace/src/store/reader/capacity.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const format = @import("../format/root.zig");
3 const block = format.block;
4 const database = format.database;
5
6 pub const default_json_nesting: usize = 32;
7 pub const maximum_json_nesting: usize = 256;
8
9 pub const Limits = struct {
10 root_path_bytes: usize,
11 event_bytes: usize = (format.Limits{}).max_event_bytes,
12 chunk_bytes: usize = (format.Limits{}).max_chunk_bytes,
13 json_nesting: usize = default_json_nesting,
14
15 pub fn fromOpen(root_path: []const u8, trace_limits: format.Limits) Limits {
16 return .{
17 .root_path_bytes = root_path.len,
18 .event_bytes = trace_limits.max_event_bytes,
19 .chunk_bytes = trace_limits.max_chunk_bytes,
20 };
21 }
22 };
23
24 pub const DeriveError = error{
25 CapacityOverflow,
26 RootPathStorageTooSmall,
27 EventStorageTooSmall,
28 EventStorageTooLarge,
29 ChunkAdmissionTooSmall,
30 ChunkAdmissionTooLarge,
31 JsonNestingTooSmall,
32 JsonNestingTooLarge,
33 };
34
35 pub const Capacity = struct {
36 root_path_bytes: usize,
37 root_path_offset: usize,
38 event_bytes: usize,
39 chunk_bytes: usize,
40 block_bytes: usize,
41 input_bytes: usize,
42 input_offset: usize,
43 path_bytes: usize,
44 path_offset: usize,
45 json_nesting: usize,
46 json_stack_bytes: usize,
47 json_stack_offset: usize,
48 database: database.Capacity,
49 sql_bytes: usize,
50 sql_offset: usize,
51 storage_bytes: usize,
52
53 pub fn derive(limits: Limits) DeriveError!Capacity {
54 if (limits.root_path_bytes == 0) return error.RootPathStorageTooSmall;
55 if (limits.event_bytes == 0) return error.EventStorageTooSmall;
56 if (limits.event_bytes > format.Limits.maximum_event_bytes) return error.EventStorageTooLarge;
57 if (limits.chunk_bytes < limits.event_bytes) return error.ChunkAdmissionTooSmall;
58 if (limits.chunk_bytes > format.Limits.maximum_chunk_bytes) return error.ChunkAdmissionTooLarge;
59 if (limits.json_nesting < 2) return error.JsonNestingTooSmall;
60 if (limits.json_nesting > maximum_json_nesting) return error.JsonNestingTooLarge;
61 const block_bytes = block.capacity(limits.event_bytes) catch return error.CapacityOverflow;
62 const database_capacity = database.readerCapacity() catch return error.CapacityOverflow;
63
64 var offset: usize = 0;
65 const root_path_offset = offset;
66 offset = try add(offset, limits.root_path_bytes);
67 const input_offset = offset;
68 const input_bytes = @max(block_bytes, format.max_manifest_bytes);
69 offset = try add(offset, input_bytes);
70 const path_offset = offset;
71 const path_bytes = try add(
72 try add(limits.root_path_bytes, 1),
73 format.max_relative_path_bytes,
74 );
75 offset = try add(offset, path_bytes);
76 const json_stack_offset = offset;
77 const json_stack_bytes = (try add(limits.json_nesting, 7)) / 8;
78 offset = try add(offset, json_stack_bytes);
79 const sql_offset = offset;
80 offset = try add(offset, database_capacity.heap_bytes);
81 return .{
82 .root_path_bytes = limits.root_path_bytes,
83 .root_path_offset = root_path_offset,
84 .event_bytes = limits.event_bytes,
85 .chunk_bytes = limits.chunk_bytes,
86 .block_bytes = block_bytes,
87 .input_bytes = input_bytes,
88 .input_offset = input_offset,
89 .path_bytes = path_bytes,
90 .path_offset = path_offset,
91 .json_nesting = limits.json_nesting,
92 .json_stack_bytes = json_stack_bytes,
93 .json_stack_offset = json_stack_offset,
94 .database = database_capacity,
95 .sql_bytes = database_capacity.heap_bytes,
96 .sql_offset = sql_offset,
97 .storage_bytes = offset,
98 };
99 }
100 };
101
102 fn add(left: usize, right: usize) DeriveError!usize {
103 return std.math.add(usize, left, right) catch error.CapacityOverflow;
104 }
105
106 test "trace reader capacity matches independent byte arithmetic" {
107 comptime {
108 @stardustClaim(
109 @import("alloc_phase").capacity.witness(@import("./root.zig").TraceReaderStorage, "trace_trace_reader_capacity"),
110 null,
111 null,
112 null,
113 null,
114 null,
115 null,
116 );
117 }
118
119 const capacity = try Capacity.derive(.{
120 .root_path_bytes = 13,
121 .event_bytes = 31,
122 .chunk_bytes = 37,
123 .json_nesting = 17,
124 });
125 const block_bytes = try block.capacity(31);
126 const input_bytes = @max(block_bytes, format.max_manifest_bytes);
127 const path_bytes = 13 + 1 + format.max_relative_path_bytes;
128 const json_stack_bytes = 3;
129 const independent = 13 + input_bytes + path_bytes + json_stack_bytes + capacity.database.heap_bytes;
130 try std.testing.expectEqual(input_bytes, capacity.input_bytes);
131 try std.testing.expectEqual(block_bytes, capacity.block_bytes);
132 try std.testing.expectEqual(path_bytes, capacity.path_bytes);
133 try std.testing.expectEqual(json_stack_bytes, capacity.json_stack_bytes);
134 try std.testing.expectEqual(capacity.database.heap_bytes, capacity.sql_bytes);
135 try std.testing.expectEqual(independent, capacity.storage_bytes);
136 try std.testing.expectEqual(capacity.storage_bytes, capacity.sql_offset + capacity.sql_bytes);
137 }
138
139 test "trace reader capacity rejects unusable and overflowing limits" {
140 try std.testing.expectError(error.RootPathStorageTooSmall, Capacity.derive(.{ .root_path_bytes = 0 }));
141 try std.testing.expectError(error.EventStorageTooSmall, Capacity.derive(.{ .root_path_bytes = 1, .event_bytes = 0 }));
142 try std.testing.expectError(error.EventStorageTooLarge, Capacity.derive(.{
143 .root_path_bytes = 1,
144 .event_bytes = format.Limits.maximum_event_bytes + 1,
145 .chunk_bytes = format.Limits.maximum_chunk_bytes,
146 }));
147 try std.testing.expectError(error.ChunkAdmissionTooSmall, Capacity.derive(.{
148 .root_path_bytes = 1,
149 .event_bytes = 2,
150 .chunk_bytes = 1,
151 }));
152 try std.testing.expectError(error.ChunkAdmissionTooLarge, Capacity.derive(.{
153 .root_path_bytes = 1,
154 .chunk_bytes = format.Limits.maximum_chunk_bytes + 1,
155 }));
156 try std.testing.expectError(error.JsonNestingTooSmall, Capacity.derive(.{
157 .root_path_bytes = 1,
158 .json_nesting = 1,
159 }));
160 try std.testing.expectError(error.JsonNestingTooLarge, Capacity.derive(.{
161 .root_path_bytes = 1,
162 .json_nesting = maximum_json_nesting + 1,
163 }));
164 try std.testing.expectError(error.CapacityOverflow, Capacity.derive(.{
165 .root_path_bytes = std.math.maxInt(usize),
166 }));
167 }