lib/zen/src/diagram/document/plan.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 
  3 const model = @import("model.zig");
  4 
  5 const diagram = @import("../root.zig");
  6 const diagram_limits = diagram.limits;
  7 
  8 pub const Limits = struct {
  9     max_line_scratch_bytes: usize,
 10     max_document_bytes: usize,
 11 };
 12 
 13 pub const Plan = struct {
 14     source_bytes: usize,
 15     source_hash: u64,
 16     line_scratch_bytes: usize,
 17     document_bytes: usize,
 18     surveyed: diagram_limits.Limits,
 19 
 20     pub fn inspect(line_scratch: []u8, jsonl: []const u8) model.Error!Plan {
 21         const line_scratch_bytes = try requiredLineScratchBytes(jsonl);
 22         if (line_scratch_bytes > line_scratch.len) {
 23             return error.DiagramLineScratchCapacityExceeded;
 24         }
 25         const surveyed = try diagram_limits.survey(
 26             line_scratch[0..line_scratch_bytes],
 27             jsonl,
 28         );
 29         const derived = try diagram_limits.Capacity.derive(surveyed);
 30         return .{
 31             .source_bytes = jsonl.len,
 32             .source_hash = std.hash.Wyhash.hash(0, jsonl),
 33             .line_scratch_bytes = line_scratch_bytes,
 34             .document_bytes = try regionLength(derived.document_bytes),
 35             .surveyed = surveyed,
 36         };
 37     }
 38 
 39     pub fn requiredLineScratchBytes(jsonl: []const u8) error{DiagramTooLarge}!usize {
 40         const measured = diagram_limits.measure(jsonl);
 41         return regionLength(try diagram_limits.lineScratchBytes(measured.max_line_bytes));
 42     }
 43 
 44     pub fn exactLimits(self: Plan) Limits {
 45         return .{
 46             .max_line_scratch_bytes = self.line_scratch_bytes,
 47             .max_document_bytes = self.document_bytes,
 48         };
 49     }
 50 
 51     pub fn require(self: Plan, limits: Limits) model.Exhaustion!void {
 52         if (self.line_scratch_bytes > limits.max_line_scratch_bytes) {
 53             return error.DiagramLineScratchCapacityExceeded;
 54         }
 55         if (self.document_bytes > limits.max_document_bytes) {
 56             return error.DiagramDocumentCapacityExceeded;
 57         }
 58     }
 59 
 60     pub fn matches(self: Plan, jsonl: []const u8) bool {
 61         return self.source_bytes == jsonl.len and
 62             self.source_hash == std.hash.Wyhash.hash(0, jsonl);
 63     }
 64 };
 65 
 66 fn regionLength(bytes: u64) error{DiagramTooLarge}!usize {
 67     return std.math.cast(usize, bytes) orelse error.DiagramTooLarge;
 68 }
 69 
 70 test "diagram Document plan exposes exact surveyed regions" {
 71     const jsonl =
 72         \\{"kind":"frame","title":"Plan"}
 73         \\{"kind":"bar","x":"a","y":1}
 74     ;
 75     const line_scratch_bytes = try Plan.requiredLineScratchBytes(jsonl);
 76     const line_scratch = try std.testing.allocator.alloc(u8, line_scratch_bytes);
 77     defer std.testing.allocator.free(line_scratch);
 78     const plan = try Plan.inspect(line_scratch, jsonl);
 79     try std.testing.expectEqual(jsonl.len, plan.source_bytes);
 80     try std.testing.expectEqual(line_scratch_bytes, plan.line_scratch_bytes);
 81     try std.testing.expect(plan.document_bytes > 0);
 82     try std.testing.expectEqual(@as(u64, 2), plan.surveyed.record_count);
 83     try std.testing.expect(plan.matches(jsonl));
 84     try std.testing.expectEqual(plan.exactLimits(), Limits{
 85         .max_line_scratch_bytes = plan.line_scratch_bytes,
 86         .max_document_bytes = plan.document_bytes,
 87     });
 88 }
 89 
 90 test "diagram Document plan rejects every resource limit" {
 91     const jsonl = "{\"kind\":\"point\",\"x\":1,\"y\":2}";
 92     const line_scratch_bytes = try Plan.requiredLineScratchBytes(jsonl);
 93     const line_scratch = try std.testing.allocator.alloc(u8, line_scratch_bytes);
 94     defer std.testing.allocator.free(line_scratch);
 95     const plan = try Plan.inspect(line_scratch, jsonl);
 96     try plan.require(plan.exactLimits());
 97     try std.testing.expectError(
 98         error.DiagramLineScratchCapacityExceeded,
 99         Plan.inspect(line_scratch[0 .. line_scratch.len - 1], jsonl),
100     );
101     try std.testing.expectError(error.DiagramLineScratchCapacityExceeded, plan.require(.{
102         .max_line_scratch_bytes = plan.line_scratch_bytes - 1,
103         .max_document_bytes = plan.document_bytes,
104     }));
105     try std.testing.expectError(error.DiagramDocumentCapacityExceeded, plan.require(.{
106         .max_line_scratch_bytes = plan.line_scratch_bytes,
107         .max_document_bytes = plan.document_bytes - 1,
108     }));
109 }