tiny.profiling.ingest.inspection
Defined in ingest.
API (6)
Actions
Public operations.
Source
Source: src/profiling/ingest/inspection.zig
zig
const std = @import("std");const sys = @import("sys");const ingest = @import("root.zig");const model = ingest.model;const LineInspection = struct { bytes: usize = 0, first_significant: ?u8 = null, last_significant: ?u8 = null, json_depth: usize = 0, max_json_depth: usize = 0, key_count: usize = 0, in_string: bool = false, escaped: bool = false, fn observe(self: *LineInspection, byte: u8) error{CapacityOverflow}!void { const previous_bytes = self.bytes; self.bytes = std.math.add(usize, self.bytes, 1) catch return error.CapacityOverflow; std.debug.assert(self.bytes == previous_bytes + 1); std.debug.assert(self.json_depth <= self.max_json_depth); if (!isTrimByte(byte)) { if (self.first_significant == null) self.first_significant = byte; self.last_significant = byte; } if (self.in_string) { if (self.escaped) { self.escaped = false; } else if (byte == '\\') { self.escaped = true; } else if (byte == '"') { self.in_string = false; } std.debug.assert(self.json_depth <= self.max_json_depth); return; } if (byte == '"') { self.in_string = true; } else if (byte == ':') { self.key_count = std.math.add(usize, self.key_count, 1) catch return error.CapacityOverflow; } else if (byte == '{' or byte == '[') { self.json_depth = std.math.add(usize, self.json_depth, 1) catch return error.CapacityOverflow; self.max_json_depth = @max(self.max_json_depth, self.json_depth); std.debug.assert(self.json_depth <= self.max_json_depth); } else if ((byte == '}' or byte == ']') and self.json_depth > 0) { self.json_depth -= 1; } std.debug.assert(self.key_count <= self.bytes); } fn isCandidate(self: LineInspection) bool { const result = self.first_significant == '{' and self.last_significant == '}'; if (result) { std.debug.assert(self.first_significant == '{'); std.debug.assert(self.last_significant == '}'); std.debug.assert(self.bytes >= 2); } return result; }};const ScanState = struct { facts: model.SourceFacts, hasher: std.crypto.hash.sha2.Sha256, line: LineInspection = .{}, line_number: usize = 0,};pub fn inspect(paths: model.Paths) !model.Limits { const sources = paths.sources(); std.debug.assert(sources.len == model.source_count); var facts: model.Facts = undefined; for (sources, 0..) |source, index| { std.debug.assert(index < model.source_count); facts.sources[index] = try inspectSource(source); } std.debug.assert(facts.sources.len == model.source_count); return .{ .paths = paths, .facts = facts };}pub fn inspectSource(source: model.Source) !model.SourceFacts { std.debug.assert(source.kind.name().len > 0); return scanSource(source, null, {}, ignoreCandidate);}pub fn visitSource( source: model.Source, line_storage: []u8, context: anytype, comptime visitCandidate: anytype,) !model.SourceFacts { std.debug.assert(source.kind.name().len > 0); return scanSource(source, line_storage, context, visitCandidate);}pub fn metadataMatches(paths: model.Paths, facts: model.Facts) !bool { const sources = paths.sources(); std.debug.assert(sources.len == facts.sources.len); for (sources, facts.sources) |source, expected| { var file = sys.fs.cwd().openFile( sys.fs.debugIo(), source.path, .{}, ) catch |err| switch (err) { error.FileNotFound => { if (expected.present) return false; continue; }, else => return err, }; defer file.close(sys.fs.debugIo()); if (!expected.present) return false; std.debug.assert(expected.present); const actual = model.Identity.fromStat(try file.stat(sys.fs.debugIo())); if (!std.meta.eql(expected.identity, actual)) return false; } return true;}pub fn admitSourceBytes(bytes: usize) error{StreamTooLong}!void { std.debug.assert(model.max_source_bytes > 0); if (bytes >= model.max_source_bytes) return error.StreamTooLong; std.debug.assert(bytes < model.max_source_bytes);}pub fn admitAllocationLine(bytes: usize, terminated: bool) error{StreamTooLong}!void { std.debug.assert(model.allocation_reader_bytes > 0); if (bytes > model.allocation_reader_bytes) return error.StreamTooLong; if (terminated and bytes == model.allocation_reader_bytes) return error.StreamTooLong; std.debug.assert(bytes <= model.allocation_reader_bytes);}fn scanSource( source: model.Source, line_storage: ?[]u8, context: anytype, comptime visitCandidate: anytype,) !model.SourceFacts { std.debug.assert(source.kind.name().len > 0); var file = sys.fs.cwd().openFile(sys.fs.debugIo(), source.path, .{}) catch |err| switch (err) { error.FileNotFound => return .{}, else => return err, }; defer file.close(sys.fs.debugIo()); const identity = model.Identity.fromStat(try file.stat(sys.fs.debugIo())); std.debug.assert(identity.size <= std.math.maxInt(u64)); const expected_bytes = std.math.cast(usize, identity.size) orelse return error.CapacityOverflow; if (!source.kind.isAllocation()) { try admitSourceBytes(expected_bytes); } return scanContents( &file, source, identity, expected_bytes, line_storage, context, visitCandidate, );}fn scanContents( file: *std.Io.File, source: model.Source, identity: model.Identity, expected_bytes: usize, line_storage: ?[]u8, context: anytype, comptime visitCandidate: anytype,) !model.SourceFacts { std.debug.assert(source.kind.name().len > 0); var state = ScanState{ .facts = .{ .present = true, .identity = identity }, .hasher = std.crypto.hash.sha2.Sha256.init(.{}), }; std.debug.assert(state.facts.present); try scanChunks( file, source, expected_bytes, line_storage, context, visitCandidate, &state, ); if (state.facts.bytes != expected_bytes) return error.InputChanged; if (state.line.bytes > 0) { std.debug.assert(state.line_number <= state.facts.lines); if (source.kind.isAllocation()) try admitAllocationLine(state.line.bytes, false); state.line_number = std.math.add(usize, state.line_number, 1) catch return error.CapacityOverflow; try finishLine( &state.facts, &state.line, line_storage, state.line_number, context, visitCandidate, ); } state.hasher.final(&state.facts.digest); const final_identity = model.Identity.fromStat(try file.stat(sys.fs.debugIo())); if (!std.meta.eql(identity, final_identity)) return error.InputChanged; if (identity.size != state.facts.bytes) return error.InputChanged; std.debug.assert(state.facts.present); std.debug.assert(state.facts.candidates <= state.facts.lines); std.debug.assert(state.facts.max_json_depth <= state.facts.max_line_bytes); std.debug.assert(state.facts.max_key_count <= state.facts.max_line_bytes); return state.facts;}fn scanChunks( file: *std.Io.File, source: model.Source, expected_bytes: usize, line_storage: ?[]u8, context: anytype, comptime visitCandidate: anytype, state: *ScanState,) !void { var reader_buffer: [16 * 1024]u8 = undefined; var chunk: [16 * 1024]u8 = undefined; var reader = file.reader(sys.fs.debugIo(), &reader_buffer); const read_bound = std.math.add(usize, expected_bytes, 1) catch return error.CapacityOverflow; for (0..read_bound) |_| { std.debug.assert(state.facts.bytes <= expected_bytes); const count = reader.interface.readSliceShort(&chunk) catch |err| switch (err) { error.ReadFailed => return reader.err orelse error.ReadFailed, }; std.debug.assert(count <= chunk.len); if (count == 0) return; if (count > expected_bytes - state.facts.bytes) return error.InputChanged; const bytes = chunk[0..count]; std.debug.assert(bytes.len > 0); state.hasher.update(bytes); state.facts.bytes = std.math.add(usize, state.facts.bytes, count) catch return error.CapacityOverflow; if (!source.kind.isAllocation()) try admitSourceBytes(state.facts.bytes); for (bytes) |byte| { std.debug.assert(state.line.bytes <= state.facts.bytes); if (byte == '\n') { if (source.kind.isAllocation()) try admitAllocationLine(state.line.bytes, true); state.line_number = std.math.add(usize, state.line_number, 1) catch return error.CapacityOverflow; try finishLine( &state.facts, &state.line, line_storage, state.line_number, context, visitCandidate, ); state.line = .{}; std.debug.assert(state.line.bytes == 0); continue; } if (line_storage) |storage| { if (state.line.bytes >= storage.len) return error.InputChanged; storage[state.line.bytes] = byte; } try state.line.observe(byte); std.debug.assert(state.line.bytes <= state.facts.bytes); if (source.kind.isAllocation() and state.line.bytes > model.allocation_reader_bytes) { return error.StreamTooLong; } } } return error.InputChanged;}fn finishLine( facts: *model.SourceFacts, line: *const LineInspection, line_storage: ?[]u8, line_number: usize, context: anytype, comptime visitCandidate: anytype,) !void { std.debug.assert(line.bytes <= facts.bytes); std.debug.assert(facts.candidates <= facts.lines); facts.lines = std.math.add(usize, facts.lines, 1) catch return error.CapacityOverflow; facts.max_line_bytes = @max(facts.max_line_bytes, line.bytes); if (!line.isCandidate()) return; facts.candidates = std.math.add(usize, facts.candidates, 1) catch return error.CapacityOverflow; facts.max_json_depth = @max(facts.max_json_depth, line.max_json_depth); facts.max_key_count = @max(facts.max_key_count, line.key_count); std.debug.assert(facts.candidates <= facts.lines); std.debug.assert(facts.max_json_depth <= facts.max_line_bytes); std.debug.assert(facts.max_key_count <= facts.max_line_bytes); if (line_storage) |storage| { std.debug.assert(line.bytes <= storage.len); const trimmed = std.mem.trim(u8, storage[0..line.bytes], " \t\r"); std.debug.assert(trimmed.len >= 2); try visitCandidate(context, trimmed, line_number); }}fn ignoreCandidate(_: void, _: []const u8, _: usize) error{}!void {}fn isTrimByte(byte: u8) bool { const result = byte == ' ' or byte == '\t' or byte == '\r'; if (result) std.debug.assert(byte != '\n'); return result;}test "ingestion source and allocation line boundaries match the legacy readers" { try admitSourceBytes(model.max_source_bytes - 1); try std.testing.expectError(error.StreamTooLong, admitSourceBytes(model.max_source_bytes)); try admitAllocationLine(model.allocation_reader_bytes - 1, true); try admitAllocationLine(model.allocation_reader_bytes, false); try std.testing.expectError( error.StreamTooLong, admitAllocationLine(model.allocation_reader_bytes, true), ); try std.testing.expectError( error.StreamTooLong, admitAllocationLine(model.allocation_reader_bytes + 1, false), );}Source: src/profiling/ingest/root.zig:2
zig
pub const inspection = @import("inspection.zig");Audit
| Definitions | 7 |
|---|---|
| Public names | 7 |
| Members | 0 |
| Version | 26.7.0 |
| Revision | daab053ee433 |