lib/trace/src/store/writer/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_manifest_io_bytes: usize = 8 * 1024;
7 pub const path_buffer_count: usize = 2;
8
9 pub const Limits = struct {
10 root_path_bytes: usize,
11 target_triple_bytes: usize = "unknown".len,
12 build_id_bytes: usize = "unknown".len,
13 mode_bytes: usize = "record".len,
14 endian_bytes: usize = "little".len,
15 event_bytes: usize = (format.Limits{}).max_event_bytes,
16 manifest_io_bytes: usize = default_manifest_io_bytes,
17
18 pub fn fromOpen(
19 root_path: []const u8,
20 manifest: format.Manifest,
21 trace_limits: format.Limits,
22 ) Limits {
23 return .{
24 .root_path_bytes = root_path.len,
25 .target_triple_bytes = manifest.target_triple.len,
26 .build_id_bytes = manifest.build_id.len,
27 .mode_bytes = manifest.mode.len,
28 .endian_bytes = manifest.endian.len,
29 .event_bytes = trace_limits.max_event_bytes,
30 };
31 }
32 };
33
34 pub const DeriveError = error{
35 CapacityOverflow,
36 RootPathStorageTooSmall,
37 EventStorageTooSmall,
38 EventStorageTooLarge,
39 ManifestIoStorageTooSmall,
40 };
41
42 pub const Capacity = struct {
43 root_path_bytes: usize,
44 root_path_offset: usize,
45 target_triple_bytes: usize,
46 target_triple_offset: usize,
47 build_id_bytes: usize,
48 build_id_offset: usize,
49 mode_bytes: usize,
50 mode_offset: usize,
51 endian_bytes: usize,
52 endian_offset: usize,
53 scratch_bytes: usize,
54 scratch_offset: usize,
55 event_bytes: usize,
56 block_bytes: usize,
57 block_offset: usize,
58 manifest_io_bytes: usize,
59 manifest_io_offset: usize,
60 manifest_bytes: usize,
61 manifest_offset: usize,
62 database: database.Capacity,
63 sql_bytes: usize,
64 sql_offset: usize,
65 path_bytes: usize,
66 path_offsets: [path_buffer_count]usize,
67 storage_bytes: usize,
68
69 pub fn derive(limits: Limits) DeriveError!Capacity {
70 if (limits.root_path_bytes == 0) return error.RootPathStorageTooSmall;
71 if (limits.event_bytes == 0) return error.EventStorageTooSmall;
72 if (limits.event_bytes > format.Limits.maximum_event_bytes) return error.EventStorageTooLarge;
73 if (limits.manifest_io_bytes == 0) return error.ManifestIoStorageTooSmall;
74 const block_bytes = block.capacity(limits.event_bytes) catch return error.CapacityOverflow;
75 const database_capacity = database.writerCapacity(block_bytes) catch return error.CapacityOverflow;
76
77 var offset: usize = 0;
78 const root_path_offset = offset;
79 offset = try add(offset, limits.root_path_bytes);
80 const target_triple_offset = offset;
81 offset = try add(offset, limits.target_triple_bytes);
82 const build_id_offset = offset;
83 offset = try add(offset, limits.build_id_bytes);
84 const mode_offset = offset;
85 offset = try add(offset, limits.mode_bytes);
86 const endian_offset = offset;
87 offset = try add(offset, limits.endian_bytes);
88 const scratch_offset = offset;
89 const scratch_bytes = @max(block_bytes, format.max_manifest_bytes);
90 offset = try add(offset, scratch_bytes);
91 const manifest_io_offset = offset;
92 offset = try add(offset, limits.manifest_io_bytes);
93 const sql_offset = offset;
94 offset = try add(offset, database_capacity.heap_bytes);
95 const path_bytes = try add(
96 try add(limits.root_path_bytes, 1),
97 format.max_relative_path_bytes,
98 );
99 var path_offsets: [path_buffer_count]usize = undefined;
100 for (&path_offsets) |*path_offset| {
101 path_offset.* = offset;
102 offset = try add(offset, path_bytes);
103 }
104 return .{
105 .root_path_bytes = limits.root_path_bytes,
106 .root_path_offset = root_path_offset,
107 .target_triple_bytes = limits.target_triple_bytes,
108 .target_triple_offset = target_triple_offset,
109 .build_id_bytes = limits.build_id_bytes,
110 .build_id_offset = build_id_offset,
111 .mode_bytes = limits.mode_bytes,
112 .mode_offset = mode_offset,
113 .endian_bytes = limits.endian_bytes,
114 .endian_offset = endian_offset,
115 .scratch_bytes = scratch_bytes,
116 .scratch_offset = scratch_offset,
117 .event_bytes = limits.event_bytes,
118 .block_bytes = block_bytes,
119 .block_offset = scratch_offset,
120 .manifest_io_bytes = limits.manifest_io_bytes,
121 .manifest_io_offset = manifest_io_offset,
122 .manifest_bytes = format.max_manifest_bytes,
123 .manifest_offset = scratch_offset,
124 .database = database_capacity,
125 .sql_bytes = database_capacity.heap_bytes,
126 .sql_offset = sql_offset,
127 .path_bytes = path_bytes,
128 .path_offsets = path_offsets,
129 .storage_bytes = offset,
130 };
131 }
132 };
133
134 fn add(left: usize, right: usize) DeriveError!usize {
135 return std.math.add(usize, left, right) catch error.CapacityOverflow;
136 }
137
138 test "trace writer capacity matches independent byte arithmetic" {
139 comptime {
140 @stardustClaim(
141 @import("alloc_phase").capacity.witness(@import("./root.zig").Storage, "trace_trace_writer_capacity"),
142 null,
143 null,
144 null,
145 null,
146 null,
147 null,
148 );
149 }
150
151 const capacity = try Capacity.derive(.{
152 .root_path_bytes = 13,
153 .target_triple_bytes = 17,
154 .build_id_bytes = 19,
155 .mode_bytes = 23,
156 .endian_bytes = 29,
157 .event_bytes = 31,
158 .manifest_io_bytes = 37,
159 });
160 const path_bytes = 13 + 1 + format.max_relative_path_bytes;
161 const block_bytes = try block.capacity(31);
162 const independent = 13 + 17 + 19 + 23 + 29 + @max(block_bytes, format.max_manifest_bytes) +
163 37 + capacity.database.heap_bytes + path_buffer_count * path_bytes;
164 try std.testing.expectEqual(path_bytes, capacity.path_bytes);
165 try std.testing.expectEqual(block_bytes, capacity.scratch_bytes);
166 try std.testing.expectEqual(capacity.block_offset, capacity.manifest_offset);
167 try std.testing.expectEqual(capacity.database.heap_bytes, capacity.sql_bytes);
168 try std.testing.expectEqual(independent, capacity.storage_bytes);
169 try std.testing.expectEqual(capacity.storage_bytes, capacity.path_offsets[1] + capacity.path_bytes);
170 }
171
172 test "trace writer capacity rejects unusable and overflowing limits" {
173 try std.testing.expectError(error.RootPathStorageTooSmall, Capacity.derive(.{ .root_path_bytes = 0 }));
174 try std.testing.expectError(error.EventStorageTooSmall, Capacity.derive(.{ .root_path_bytes = 1, .event_bytes = 0 }));
175 try std.testing.expectError(error.EventStorageTooLarge, Capacity.derive(.{
176 .root_path_bytes = 1,
177 .event_bytes = format.Limits.maximum_event_bytes + 1,
178 }));
179 try std.testing.expectError(error.ManifestIoStorageTooSmall, Capacity.derive(.{ .root_path_bytes = 1, .manifest_io_bytes = 0 }));
180 try std.testing.expectError(error.CapacityOverflow, Capacity.derive(.{
181 .root_path_bytes = std.math.maxInt(usize),
182 }));
183 }