lib/choir/src/repro/input/storage.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const alloc_phase = @import("alloc_phase");
  3 const capacity_mod = @import("capacity.zig");
  4 
  5 pub const Exhaustion = error{
  6     ReproducerInputStorageInUse,
  7     ReproducerInputCapacityExceeded,
  8 };
  9 
 10 pub const Status = struct {
 11     phase: alloc_phase.capacity.Phase,
 12     in_use: bool,
 13     terminal: bool,
 14     file_bytes: usize,
 15     storage_bytes: usize,
 16     loaded_file_bytes: usize,
 17     high_water_file_bytes: usize,
 18     rejected_file_count: u64,
 19 };
 20 
 21 pub fn exactLimitsForFile(
 22     file_bytes: u64,
 23     maximum: capacity_mod.Limits,
 24 ) Exhaustion!capacity_mod.Limits {
 25     const exact = std.math.cast(usize, file_bytes) orelse
 26         return error.ReproducerInputCapacityExceeded;
 27     if (exact > maximum.file_bytes) return error.ReproducerInputCapacityExceeded;
 28     return .{ .file_bytes = exact };
 29 }
 30 
 31 const Spec = struct {
 32     pub const Limits: type = capacity_mod.Limits;
 33     pub const Capacity: type = capacity_mod.Capacity;
 34     pub const Exhaustion: type = @import("storage.zig").Exhaustion;
 35     pub const Status: type = @import("storage.zig").Status;
 36     pub const DeriveError: type = capacity_mod.DeriveError;
 37     pub const acquisition: alloc_phase.input.Acquisition = .{ .file = true };
 38     pub const overload: alloc_phase.input.Overload = .terminal;
 39     pub const limit_field = "file_bytes";
 40     pub const loaded_field = "loaded_file_bytes";
 41     pub const high_water_field = "high_water_file_bytes";
 42     pub const rejected_field = "rejected_file_count";
 43     pub const in_use_error: @import("storage.zig").Exhaustion =
 44         error.ReproducerInputStorageInUse;
 45     pub const capacity_error: @import("storage.zig").Exhaustion =
 46         error.ReproducerInputCapacityExceeded;
 47 
 48     pub const claim: alloc_phase.capacity.Declaration = .{
 49         .source = .{
 50             .id = "choir.pass_reproducer_input_storage",
 51             .kind = .phase_static,
 52             .limit_source = .caller,
 53             .storage = .{
 54                 .covered = &.{
 55                     .{
 56                         .id = "one_pass_reproducer_file_and_overload_lookahead_region",
 57                         .lifetime = .steady,
 58                         .detail = "one pass reproducer file and overload-lookahead region",
 59                     },
 60                 },
 61                 .excluded = &.{
 62                     "reproducer parser output and owned field copies",
 63                     "Choir registry, IR context, pass manager, worker, and replay storage",
 64                     "process arguments, file path, report rendering, stdout, and stderr storage",
 65                     "filesystem implementation, kernel file state, and terminal teardown",
 66                 },
 67             },
 68             .capacity = .{
 69                 .inputs = &.{
 70                     alloc_phase.capacity.bindInput(Limits, "file_bytes", "file_bytes"),
 71                 },
 72                 .type_selectors = &.{},
 73                 .nodes = &.{
 74                     .{ .input = 0 },
 75                     .{ .constant = 1 },
 76                     .{ .add = .{ .left = 0, .right = 1 } },
 77                 },
 78                 .assertions = &.{.{
 79                     .scope = .closure_total,
 80                     .measure = .retained,
 81                     .relation = .exact,
 82                     .expression = 2,
 83                 }},
 84             },
 85             .overload = .{
 86                 .kind = .terminal,
 87                 .detail = "stat preflight or max plus one byte ends acquisition before reproducer parsing or replay",
 88             },
 89             .risks = .{
 90                 .transitive = .{
 91                     .status = .witnessed,
 92                     .detail = "activated file reads use only the preacquired region before synchronous parser consumption",
 93                 },
 94                 .foreign = .{
 95                     .status = .excluded,
 96                     .detail = "filesystem implementation and kernel file state remain outside pass reproducer input storage",
 97                 },
 98             },
 99             .obligations = &.{
100                 .{ .key = "choir_pass_reproducer_input_capacity", .role = .capacity_model },
101                 .{ .key = "choir_pass_reproducer_input_preflight", .role = .overload },
102                 .{ .key = "choir_pass_reproducer_input_file_boundary_overload", .role = .overload },
103                 .{ .key = "choir_pass_reproducer_input_file_boundary_transitive_risk", .role = .transitive_risk },
104                 .{ .key = "choir_pass_reproducer_input_file_boundary_foreign_risk", .role = .foreign_risk },
105                 .{ .key = "choir_pass_reproducer_input_byte_boundary", .role = .custom },
106                 .{ .key = "choir_pass_reproducer_input_command_composition", .role = .custom },
107                 .{ .key = "choir_pass_reproducer_input_root", .role = .custom },
108             },
109         },
110         .bindings = .{},
111     };
112 };
113 
114 pub const Storage = alloc_phase.input.OneRegion(Spec);
115 
116 test "Choir pass reproducer input maps files beyond lookahead to capacity exhaustion" {
117     comptime {
118         @stardustClaim(
119             @import("alloc_phase").capacity.witness(Storage, "choir_pass_reproducer_input_capacity"),
120             null,
121             null,
122             null,
123             null,
124             null,
125             null,
126         );
127     }
128 
129     comptime {
130         @stardustClaim(
131             @import("alloc_phase").capacity.witness(Storage, "choir_pass_reproducer_input_file_boundary_overload"),
132             null,
133             null,
134             null,
135             null,
136             null,
137             null,
138         );
139     }
140     comptime {
141         @stardustClaim(
142             @import("alloc_phase").capacity.witness(Storage, "choir_pass_reproducer_input_file_boundary_transitive_risk"),
143             null,
144             null,
145             null,
146             null,
147             null,
148             null,
149         );
150     }
151     comptime {
152         @stardustClaim(
153             @import("alloc_phase").capacity.witness(Storage, "choir_pass_reproducer_input_file_boundary_foreign_risk"),
154             null,
155             null,
156             null,
157             null,
158             null,
159             null,
160         );
161     }
162 
163     var temporary = std.testing.tmpDir(.{});
164     defer temporary.cleanup();
165     const io = std.Options.debug_io;
166     var storage = try Storage.init(std.testing.allocator, .{ .file_bytes = 5 });
167     defer storage.deinit(std.testing.allocator);
168     storage.activate();
169     try temporary.dir.writeFile(
170         io,
171         .{ .sub_path = "pass.choirrepro", .data = "abcde" },
172     );
173     const exact = try storage.readFile(temporary.dir, io, "pass.choirrepro");
174     try std.testing.expectEqualStrings("abcde", exact);
175     storage.release();
176     try temporary.dir.writeFile(
177         io,
178         .{ .sub_path = "pass.choirrepro", .data = "abcdefgh" },
179     );
180     try std.testing.expectError(
181         error.ReproducerInputCapacityExceeded,
182         storage.readFile(temporary.dir, io, "pass.choirrepro"),
183     );
184     try std.testing.expect(storage.status().terminal);
185 }
186 
187 comptime {
188     alloc_phase.capacity.requireAllocatorRejectingOwnerShape(Storage);
189 }