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 }