tiny.zen.diagram.limits
Defined in diagram.
API (20)
Actions
Public operations.
Capacity.deriveCapacity.totalBytesLimits.fanoutStringBytesLimits.marksUpperlineScratchBytesmeasuresurvey
Types and contracts
Public types and contracts.
Values and defaults
Public values and defaults.
axes_output_bytescategory_slot_bytesescape_factorfields_json_bytes_minline_scratch_factorline_scratch_slacklist_growth_factormark_output_bytesregion_slack_bytessvg_chrome_bytes
Source
Source: lib/zen/src/diagram/limits.zig
zig
const std = @import("std");const root = @import("root.zig");const spec = root.spec;pub const line_scratch_factor: u64 = 64;pub const line_scratch_slack: u64 = 4096;pub const list_growth_factor: u64 = 2;pub const fields_json_bytes_min: u64 = 8;pub const category_slot_bytes: u64 = 48;pub const mark_output_bytes: u64 = 4096;pub const axes_output_bytes: u64 = 16384;pub const svg_chrome_bytes: u64 = 4096;pub const escape_factor: u64 = 6;pub const region_slack_bytes: u64 = 4096;pub const Measure = struct { line_count: u64 = 0, max_line_bytes: u64 = 0, input_bytes: u64 = 0,};pub fn measure(jsonl: []const u8) Measure { var measured = Measure{}; var lines = spec.LineIterator.init(jsonl); while (lines.next()) |line| { measured.line_count += 1; measured.max_line_bytes = @max(measured.max_line_bytes, line.len); measured.input_bytes += line.len; } return measured;}pub fn lineScratchBytes(max_line_bytes: u64) error{DiagramTooLarge}!u64 { const scaled = std.math.mul(u64, max_line_bytes, line_scratch_factor) catch return error.DiagramTooLarge; return std.math.add(u64, scaled, line_scratch_slack) catch return error.DiagramTooLarge;}pub const Limits = struct { input_bytes: u64 = 0, max_line_bytes: u64 = 0, record_count: u64 = 0, frame_records: u64 = 0, scale_records: u64 = 0, transform_records: u64 = 0, data_records: u64 = 0, data_line_bytes: u64 = 0, channel_records: u64 = 0, explicit_mark_records: u64 = 0, scratch_high_water: u64 = 0, pub fn marksUpper(self: Limits) error{DiagramTooLarge}!u64 { const fanout = std.math.mul(u64, self.channel_records, self.data_records) catch return error.DiagramTooLarge; return std.math.add(u64, self.explicit_mark_records, fanout) catch return error.DiagramTooLarge; } pub fn fanoutStringBytes(self: Limits) error{DiagramTooLarge}!u64 { return std.math.mul(u64, self.channel_records, self.data_line_bytes) catch return error.DiagramTooLarge; }};pub fn survey(line_scratch: []u8, jsonl: []const u8) !Limits { var limits = Limits{}; var scratch = std.heap.FixedBufferAllocator.init(line_scratch); var lines = spec.LineIterator.init(jsonl); while (lines.next()) |line| { scratch.reset(); var parsed = std.json.parseFromSlice(std.json.Value, scratch.allocator(), line, .{}) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => return error.InvalidJsonl, }; defer parsed.deinit(); limits.record_count += 1; limits.input_bytes += line.len; limits.max_line_bytes = @max(limits.max_line_bytes, line.len); switch (try spec.recordKindOf(parsed.value)) { .frame => limits.frame_records += 1, .scale => limits.scale_records += 1, .transform => limits.transform_records += 1, .data => { limits.data_records += 1; limits.data_line_bytes += line.len; }, .mark => limits.channel_records += 1, .bar, .point, .rule, .text, .box, .edge => limits.explicit_mark_records += 1, } limits.scratch_high_water = @max(limits.scratch_high_water, scratch.end_index); } return limits;}pub const Capacity = struct { line_scratch_bytes: u64, document_bytes: u64, render_scratch_bytes: u64, output_bytes: u64, pub fn derive(limits: Limits) error{DiagramTooLarge}!Capacity { const marks_upper = try limits.marksUpper(); const fanout_string_bytes = try limits.fanoutStringBytes(); const retained_string_bytes = try add(limits.input_bytes, fanout_string_bytes); const mark_slots = try mul(marks_upper, @sizeOf(spec.Mark) * list_growth_factor); const data_slots = try mul(limits.data_records, @sizeOf(spec.DataRow) * list_growth_factor); const transform_slots = try mul(limits.transform_records, @sizeOf(spec.Transform) * list_growth_factor); const field_count = try add(limits.data_line_bytes / fields_json_bytes_min, limits.data_records); const field_slots = try mul(field_count, @sizeOf(spec.Field) * list_growth_factor); var document_bytes = try add(mark_slots, data_slots); document_bytes = try add(document_bytes, transform_slots); document_bytes = try add(document_bytes, field_slots); document_bytes = try add(document_bytes, retained_string_bytes); document_bytes = try add(document_bytes, region_slack_bytes); var render_scratch_bytes = try mul(marks_upper, category_slot_bytes); render_scratch_bytes = try add(render_scratch_bytes, retained_string_bytes); render_scratch_bytes = try add(render_scratch_bytes, limits.max_line_bytes); render_scratch_bytes = try add(render_scratch_bytes, region_slack_bytes); var output_bytes = try mul(marks_upper, mark_output_bytes); output_bytes = try add(output_bytes, try mul(retained_string_bytes, escape_factor)); output_bytes = try add(output_bytes, axes_output_bytes); output_bytes = try add(output_bytes, svg_chrome_bytes); output_bytes = try add(output_bytes, region_slack_bytes); return .{ .line_scratch_bytes = try lineScratchBytes(limits.max_line_bytes), .document_bytes = document_bytes, .render_scratch_bytes = render_scratch_bytes, .output_bytes = output_bytes, }; } pub fn totalBytes(self: Capacity) error{DiagramTooLarge}!u64 { var total = try add(self.line_scratch_bytes, self.document_bytes); total = try add(total, self.render_scratch_bytes); return try add(total, self.output_bytes); }};fn add(a: u64, b: u64) error{DiagramTooLarge}!u64 { return std.math.add(u64, a, b) catch error.DiagramTooLarge;}fn mul(a: u64, b: u64) error{DiagramTooLarge}!u64 { return std.math.mul(u64, a, b) catch error.DiagramTooLarge;}test "measure reports lines and bytes over trimmed nonempty lines" { const jsonl = " {\"kind\":\"frame\"} \n\n{\"kind\":\"point\",\"x\":1,\"y\":2}\n"; const measured = measure(jsonl); try std.testing.expectEqual(@as(u64, 2), measured.line_count); try std.testing.expectEqual(@as(u64, 16 + 28), measured.input_bytes); try std.testing.expectEqual(@as(u64, 28), measured.max_line_bytes);}test "survey counts records by kind and tracks scratch high water" { const jsonl = \\{"kind":"frame","title":"T"} \\{"kind":"scale","axis":"x","type":"log"} \\{"kind":"transform","op":"stack"} \\{"kind":"data","name":"d","x":"a","y":1} \\{"kind":"data","name":"d","x":"b","y":2} \\{"kind":"mark","type":"bar","data":"d","x":"x","y":"y"} \\{"kind":"bar","x":"a","y":1} \\{"kind":"edge","x1":0,"y1":0,"x2":1,"y2":1} ; var scratch: [8192]u8 = undefined; const limits = try survey(&scratch, jsonl); try std.testing.expectEqual(@as(u64, 8), limits.record_count); try std.testing.expectEqual(@as(u64, 1), limits.frame_records); try std.testing.expectEqual(@as(u64, 1), limits.scale_records); try std.testing.expectEqual(@as(u64, 1), limits.transform_records); try std.testing.expectEqual(@as(u64, 2), limits.data_records); try std.testing.expectEqual(@as(u64, 1), limits.channel_records); try std.testing.expectEqual(@as(u64, 2), limits.explicit_mark_records); try std.testing.expect(limits.scratch_high_water > 0); try std.testing.expect(limits.scratch_high_water <= scratch.len); try std.testing.expectEqual(@as(u64, 4), try limits.marksUpper());}test "survey rejects malformed lines exactly as parse admission" { var scratch: [8192]u8 = undefined; try std.testing.expectError(error.InvalidJsonl, survey(&scratch, "not json")); try std.testing.expectError(error.MissingKind, survey(&scratch, "{\"x\":1}")); try std.testing.expectError(error.UnknownRecordKind, survey(&scratch, "{\"kind\":\"nope\"}")); try std.testing.expectError(error.InvalidRecord, survey(&scratch, "[1,2]"));}test "capacity derivation is monotone in survey terms and rejects overflow" { var scratch: [8192]u8 = undefined; const small = try survey(&scratch, "{\"kind\":\"point\",\"x\":1,\"y\":2}"); const capacity = try Capacity.derive(small); try std.testing.expect(capacity.document_bytes >= small.input_bytes); try std.testing.expect(capacity.output_bytes >= mark_output_bytes); try std.testing.expect(try capacity.totalBytes() > 0); var overflowing = small; overflowing.channel_records = std.math.maxInt(u64) / 2; overflowing.data_records = std.math.maxInt(u64) / 2; try std.testing.expectError(error.DiagramTooLarge, Capacity.derive(overflowing));}Source: lib/zen/src/diagram/root.zig:8
zig
pub const limits = @import("limits.zig");Audit
| Definitions | 21 |
|---|---|
| Public names | 21 |
| Members | 18 |
| Version | 26.7.0 |
| Revision | daab053ee433 |