Skip to documentation
SLOP

tiny.smg.scan.fragment

Reference tiny.smg scan fragment

Defined in scan.

API (18)

Actions

Public operations.

Types and contracts

Public types and contracts.

No direct callersNo direct callsscanfragment
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallsNo direct callsscan.fragmentloadManifesttest; no linktools.smg.src.scan.fragmenttest: manifest identifies stable chan...scan.fragment.Manifestempty
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsprivate; no linktools.smg.src.scan.pipeline.scanscanProjectSourceprivate; no linktools.smg.src.scan.fragmentelapsedNanosecondsprivate; no linktools.smg.src.scan.fragmentfragmentPathscan.fragmentmergeDeferredEdgescan.fragmentmergeEvidencescan.fragmentmergeNode+6 morescan.fragmentloadInto
Static calls · unresolved targets: 0 · external targets: 6.
Called byCallsscanscanProjectCachedscan.fragment.Manifestemptyprivate; no linktools.smg.src.scan.fragment.Readerbyteprivate; no linktools.smg.src.scan.fragment.Readercountprivate; no linktools.smg.src.scan.fragment.Readerdigestprivate; no linktools.smg.src.scan.fragment.Readerexpect+6 morescan.fragmentloadManifest
Static calls · unresolved targets: 0 · external targets: 4.
Called byCallsNo direct callsscan.fragmentloadIntoprivate; no linktools.smg.src.scan.pipeline.scanmergeSourceFragmentscan.fragmentmergeDeferredEdge
Static calls · unresolved targets: 1 · external targets: 3.
Called byCallsscan.fragmentloadIntoprivate; no linktools.smg.src.scan.pipeline.scanmergeSourceFragmentprivate; no linktools.smg.src.scan.fragmentmergeCountsscan.fragmentmergeEvidence
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsscan.fragmentloadIntoprivate; no linktools.smg.src.scan.pipeline.scanmergeSourceFragmentscan.fragmentmergeNode
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callersprivate; no linktools.smg.src.scan.fragmentexecutableDirectoryprivate; no linktools.smg.src.scan.fragmentfragmentPathprivate; no linktools.smg.src.scan.fragmentwriteCountprivate; no linktools.smg.src.scan.fragmentwriteEdgeprivate; no linktools.smg.src.scan.fragmentwriteEvidenceprivate; no linktools.smg.src.scan.fragmentwriteNodescan.fragmentwrite
Static calls · unresolved targets: 1 · external targets: 7.
Called byCallsscanscanProjectCachedprivate; no linktools.smg.src.scan.fragmentcacheDirectoryprivate; no linktools.smg.src.scan.fragmentcachePathprivate; no linktools.smg.src.scan.fragmentwriteBytesprivate; no linktools.smg.src.scan.fragmentwriteCountscan.fragmentwriteManifest
Static calls · unresolved targets: 2 · external targets: 15.

Source: tools/smg/src/scan/fragment.zig

zig
const std = @import("std");const sys = @import("sys");const smg = @import("../root.zig");const core = @import("core/root.zig");const graph_mod = smg.graph;const model = smg.model;const scan_files = @import("files.zig");const text = smg.text;const fs_io = std.Options.debug_io;pub const Digest = scan_files.SourceDigest;const fragment_magic = "tiny.smg.fragment/v1\n";const manifest_magic = "tiny.smg.fragment-manifest/v2\n";const cache_directory_name = "fragments";const manifest_file_name = "fragment-state.bin";const manifest_temp_file_name = "fragment-state.bin.tmp";const checksum_bytes = std.crypto.hash.sha2.Sha256.digest_length;pub const Evidence = struct {    files: usize = 0,    nodes_added: usize = 0,    lang_counts: []const core.Count = &.{},    type_counts: []const core.Count = &.{},};pub const LoadTiming = struct {    read_ns: u64 = 0,    validation_ns: u64 = 0,    fold_ns: u64 = 0,};pub const ManifestEntry = struct {    key: Digest,    live: bool,    seen: bool = false,};pub const Manifest = struct {    project: ?scan_files.ProjectIdentity,    entries: std.StringHashMap(ManifestEntry),    pub fn empty(allocator: std.mem.Allocator) Manifest {        return .{            .project = null,            .entries = std.StringHashMap(ManifestEntry).init(allocator),        };    }    pub fn get(self: *const Manifest, path: []const u8, key: Digest) bool {        const entry = self.entries.get(path) orelse return false;        return entry.live and std.mem.eql(u8, &entry.key, &key);    }    pub fn tombstones(self: *const Manifest) usize {        var count: usize = 0;        var iterator = self.entries.valueIterator();        while (iterator.next()) |entry| if (!entry.live) {            count += 1;        };        return count;    }    pub fn matchesProject(self: *const Manifest, digest: Digest) bool {        const project = self.project orelse return false;        return std.mem.eql(u8, &project.digest, &digest);    }    pub fn changedPaths(        self: *Manifest,        allocator: std.mem.Allocator,        root: []const u8,        current: []const scan_files.SourceIdentity,    ) ![]const []const u8 {        var prior = self.entries.valueIterator();        while (prior.next()) |entry| entry.seen = false;        var changed_count: usize = 0;        for (current) |source| {            if (self.entries.getPtr(source.path)) |entry| {                entry.seen = true;                if (entry.live and std.mem.eql(u8, &entry.key, &source.digest)) continue;            }            changed_count = std.math.add(usize, changed_count, 1) catch                return error.FragmentManifestTooLarge;        }        var entries = self.entries.iterator();        while (entries.next()) |entry| {            if (!entry.value_ptr.live or entry.value_ptr.seen) continue;            changed_count = std.math.add(usize, changed_count, 1) catch                return error.FragmentManifestTooLarge;        }        const changed = try allocator.alloc([]const u8, changed_count);        var index: usize = 0;        for (current) |source| {            const entry = self.entries.get(source.path);            if (entry != null and entry.?.live and                std.mem.eql(u8, &entry.?.key, &source.digest)) continue;            changed[index] = try std.fs.path.join(allocator, &.{ root, source.path });            index += 1;        }        entries = self.entries.iterator();        while (entries.next()) |entry| {            if (!entry.value_ptr.live or entry.value_ptr.seen) continue;            changed[index] = try std.fs.path.join(allocator, &.{ root, entry.key_ptr.* });            index += 1;        }        std.debug.assert(index == changed.len);        std.mem.sort([]const u8, changed, {}, scan_files.lessThan);        return changed;    }    pub fn deinit(self: *Manifest) void {        self.entries.deinit();        self.* = undefined;    }};pub fn loadInto(    allocator: std.mem.Allocator,    scratch: std.mem.Allocator,    root: []const u8,    executable: Digest,    key: Digest,    graph: *graph_mod.Graph,    stats: *core.Stats,    timing: *LoadTiming,    limits: smg.ScanLimits,) !bool {    var phase_started = sys.time.nanoTimestamp();    const path = try fragmentPath(scratch, root, executable, key);    const encoded = text.readFile(        scratch,        path,        limits.max_fragment_bytes,    ) catch |err| {        timing.read_ns += sinceNanoseconds(phase_started);        return switch (err) {            error.StreamTooLong => error.MaxFragmentBytesExceeded,            else => false,        };    };    timing.read_ns += elapsedNanoseconds(&phase_started);    if (!validFragment(encoded, executable, key)) {        timing.validation_ns += sinceNanoseconds(phase_started);        return false;    }    timing.validation_ns += elapsedNanoseconds(&phase_started);    var reader = payloadReader(encoded, executable, key) orelse unreachable;    const evidence = (try readEvidence(allocator, &reader)) orelse unreachable;    try mergeEvidence(allocator, stats, evidence);    const node_count = reader.count() orelse unreachable;    for (0..node_count) |_| {        const node = try readNode(allocator, &reader) orelse unreachable;        try mergeNode(graph, node);    }    const edge_count = reader.count() orelse unreachable;    for (0..edge_count) |_| {        const edge = try readEdge(allocator, &reader) orelse unreachable;        if (try graph_mod.addEdgeTracked(graph, edge)) stats.edges_added += 1;    }    const deferred_count = reader.count() orelse unreachable;    stats.activateDeferred(allocator);    for (0..deferred_count) |_| {        const edge = try readEdge(allocator, &reader) orelse unreachable;        try mergeDeferredEdge(allocator, graph, stats, edge);    }    std.debug.assert(reader.remaining() == 0);    timing.fold_ns += sinceNanoseconds(phase_started);    return true;}pub fn write(    scratch: std.mem.Allocator,    root: []const u8,    executable: Digest,    key: Digest,    graph: graph_mod.Graph,    deferred: []const model.Edge,    evidence: Evidence,    limits: smg.ScanLimits,) !void {    var encoded: std.Io.Writer.Allocating = .init(scratch);    defer encoded.deinit();    const writer = &encoded.writer;    try writer.writeAll(fragment_magic);    try writer.writeAll(&executable);    try writer.writeAll(&key);    try writeEvidence(writer, evidence);    try writeCount(writer, graph.nodes.items.len);    for (graph.nodes.items) |node| try writeNode(writer, node);    try writeCount(writer, graph.edges.items.len);    for (graph.edges.items) |edge| try writeEdge(writer, edge);    try writeCount(writer, deferred.len);    for (deferred) |edge| try writeEdge(writer, edge);    if (encoded.written().len > limits.max_fragment_bytes - checksum_bytes) {        return error.MaxFragmentBytesExceeded;    }    var checksum: [checksum_bytes]u8 = undefined;    std.crypto.hash.sha2.Sha256.hash(encoded.written(), &checksum, .{});    try writer.writeAll(&checksum);    const directory = try executableDirectory(scratch, root, executable);    try sys.fs.createDirPath(directory);    const destination = try fragmentPath(scratch, root, executable, key);    const pid = try sys.process.currentProcessId();    const temporary = try std.fmt.allocPrint(scratch, "{s}.tmp.{d}", .{ destination, pid });    errdefer std.Io.Dir.deleteFileAbsolute(fs_io, temporary) catch {};    {        var file = try std.Io.Dir.createFileAbsolute(fs_io, temporary, .{ .truncate = true });        defer file.close(fs_io);        try file.writeStreamingAll(fs_io, encoded.written());    }    try sys.fs.rename(temporary, destination);}pub fn loadManifest(    allocator: std.mem.Allocator,    scratch: std.mem.Allocator,    root: []const u8,    executable: Digest,    limits: smg.ScanLimits,) !Manifest {    var manifest = Manifest.empty(allocator);    errdefer manifest.deinit();    const path = try cachePath(scratch, root, manifest_file_name);    const encoded = text.readFile(        scratch,        path,        limits.max_manifest_bytes,    ) catch return manifest;    const header_bytes = manifest_magic.len + 2 * @sizeOf(Digest) +        2 * @sizeOf(u64) + checksum_bytes;    if (!validChecksum(encoded) or encoded.len < header_bytes) return manifest;    var reader = Reader.init(encoded[0 .. encoded.len - checksum_bytes]);    if (!reader.expect(manifest_magic)) return manifest;    const stored_executable = reader.digest() orelse return manifest;    if (!std.mem.eql(u8, &stored_executable, &executable)) return manifest;    const project_digest = reader.digest() orelse return manifest;    const project_files = reader.count() orelse return manifest;    const count = reader.count() orelse return manifest;    if (count > reader.remaining() / (1 + @sizeOf(u32) + @sizeOf(Digest))) return manifest;    try manifest.entries.ensureTotalCapacity(@intCast(count));    for (0..count) |_| {        const live = switch (reader.byte() orelse return invalidManifest(&manifest)) {            0 => false,            1 => true,            else => return invalidManifest(&manifest),        };        const path_value = try reader.owned(allocator) orelse return invalidManifest(&manifest);        const key = reader.digest() orelse return invalidManifest(&manifest);        manifest.entries.putAssumeCapacity(path_value, .{ .key = key, .live = live });    }    if (reader.remaining() != 0) return invalidManifest(&manifest);    manifest.project = .{ .digest = project_digest, .files = project_files };    return manifest;}fn invalidManifest(manifest: *Manifest) Manifest {    manifest.project = null;    manifest.entries.clearRetainingCapacity();    return manifest.*;}pub fn writeManifest(    allocator: std.mem.Allocator,    scratch: std.mem.Allocator,    root: []const u8,    executable: Digest,    project: scan_files.ProjectIdentity,    previous: *const Manifest,    current: []const scan_files.SourceIdentity,    limits: smg.ScanLimits,) !usize {    var live = std.StringHashMap(void).init(scratch);    defer live.deinit();    try live.ensureTotalCapacity(@intCast(current.len));    for (current) |entry| live.putAssumeCapacity(entry.path, {});    var tombstones: std.ArrayList([]const u8) = .empty;    var prior = previous.entries.iterator();    while (prior.next()) |entry| {        if (live.contains(entry.key_ptr.*)) continue;        try tombstones.append(scratch, entry.key_ptr.*);    }    std.mem.sort([]const u8, tombstones.items, {}, scan_files.lessThan);    var encoded: std.Io.Writer.Allocating = .init(scratch);    defer encoded.deinit();    const writer = &encoded.writer;    try writer.writeAll(manifest_magic);    try writer.writeAll(&executable);    try writer.writeAll(&project.digest);    try writeCount(writer, project.files);    const entry_count = std.math.add(usize, current.len, tombstones.items.len) catch        return error.FragmentManifestTooLarge;    try writeCount(writer, entry_count);    for (current) |entry| {        try writer.writeByte(1);        try writeBytes(writer, entry.path);        try writer.writeAll(&entry.digest);    }    const empty_key: Digest = @splat(0);    for (tombstones.items) |path| {        try writer.writeByte(0);        try writeBytes(writer, path);        try writer.writeAll(&empty_key);    }    if (encoded.written().len > limits.max_manifest_bytes - checksum_bytes) {        return error.FragmentManifestTooLarge;    }    var checksum: [checksum_bytes]u8 = undefined;    std.crypto.hash.sha2.Sha256.hash(encoded.written(), &checksum, .{});    try writer.writeAll(&checksum);    const directory = try cacheDirectory(scratch, root);    try sys.fs.createDirPath(directory);    const destination = try cachePath(scratch, root, manifest_file_name);    const temporary_name = try std.fmt.allocPrint(        scratch,        "{s}.{d}",        .{ manifest_temp_file_name, try sys.process.currentProcessId() },    );    const temporary = try cachePath(scratch, root, temporary_name);    errdefer std.Io.Dir.deleteFileAbsolute(fs_io, temporary) catch {};    {        var file = try std.Io.Dir.createFileAbsolute(fs_io, temporary, .{ .truncate = true });        defer file.close(fs_io);        try file.writeStreamingAll(fs_io, encoded.written());        try file.sync(fs_io);    }    try sys.fs.rename(temporary, destination);    _ = allocator;    return tombstones.items.len;}fn writeEvidence(writer: *std.Io.Writer, evidence: Evidence) !void {    try writeCount(writer, evidence.files);    try writeCount(writer, evidence.nodes_added);    try writeCounts(writer, evidence.lang_counts);    try writeCounts(writer, evidence.type_counts);}fn readEvidence(allocator: std.mem.Allocator, reader: *Reader) !?Evidence {    const files = reader.count() orelse return null;    const nodes_added = reader.count() orelse return null;    const lang_counts = (try readCounts(allocator, reader)) orelse return null;    const type_counts = (try readCounts(allocator, reader)) orelse return null;    return .{        .files = files,        .nodes_added = nodes_added,        .lang_counts = lang_counts,        .type_counts = type_counts,    };}pub fn mergeEvidence(    allocator: std.mem.Allocator,    stats: *core.Stats,    evidence: Evidence,) !void {    stats.files += evidence.files;    stats.nodes_added += evidence.nodes_added;    try mergeCounts(allocator, &stats.lang_counts, evidence.lang_counts);    try mergeCounts(allocator, &stats.type_counts, evidence.type_counts);}pub fn mergeNode(graph: *graph_mod.Graph, node: model.Node) !void {    if (std.mem.eql(u8, node.type, model.NodeType.package) and        node.file == null and        graph_mod.getNode(graph, node.name) != null)    {        return;    }    try graph_mod.addNode(graph, node);}pub fn mergeDeferredEdge(    allocator: std.mem.Allocator,    graph: *graph_mod.Graph,    stats: *core.Stats,    edge: model.Edge,) !void {    if (std.mem.eql(u8, edge.rel, model.RelType.imports) and        graph_mod.getNode(graph, edge.target) != null)    {        const resolved = model.Edge{            .source = edge.source,            .rel = edge.rel,            .target = edge.target,            .metadata = try core.scanEdgeMetadata(allocator, edge.metadata),        };        if (try graph_mod.addEdgeTracked(graph, resolved)) stats.edges_added += 1;        return;    }    try stats.deferred.append(edge);}fn mergeCounts(    allocator: std.mem.Allocator,    target: *std.ArrayList(core.Count),    source: []const core.Count,) !void {    for (source) |incoming| {        for (target.items) |*existing| {            if (!std.mem.eql(u8, existing.name, incoming.name)) continue;            existing.count += incoming.count;            break;        } else {            try target.append(allocator, incoming);        }    }}fn writeCounts(writer: *std.Io.Writer, counts: []const core.Count) !void {    try writeCount(writer, counts.len);    for (counts) |count| {        try writeBytes(writer, count.name);        try writeCount(writer, count.count);    }}fn readCounts(allocator: std.mem.Allocator, reader: *Reader) !?[]const core.Count {    const count = reader.count() orelse return null;    if (count > reader.remaining() / (@sizeOf(u32) + @sizeOf(u64))) return null;    const values = try allocator.alloc(core.Count, count);    for (values) |*value| {        value.name = try reader.owned(allocator) orelse return null;        value.count = reader.count() orelse return null;    }    return values;}fn writeNode(writer: *std.Io.Writer, node: model.Node) !void {    try writeBytes(writer, node.name);    try writeBytes(writer, node.type);    try writeOptionalBytes(writer, node.file);    try writeOptionalInt(writer, node.line);    try writeOptionalInt(writer, node.end_line);    try writeOptionalBytes(writer, node.docstring);    try writePairs(writer, node.metadata);}fn readNode(allocator: std.mem.Allocator, reader: *Reader) !?model.Node {    const name = try reader.owned(allocator) orelse return null;    const node_type = try reader.owned(allocator) orelse return null;    const file = try reader.optionalOwned(allocator) orelse return null;    const line = reader.optionalInt() orelse return null;    const end_line = reader.optionalInt() orelse return null;    const docstring = try reader.optionalOwned(allocator) orelse return null;    const metadata = try readPairs(allocator, reader) orelse return null;    return .{        .name = name,        .type = node_type,        .file = file.value,        .line = line.value,        .end_line = end_line.value,        .docstring = docstring.value,        .metadata = metadata,    };}fn writeEdge(writer: *std.Io.Writer, edge: model.Edge) !void {    try writeBytes(writer, edge.source);    try writeBytes(writer, edge.rel);    try writeBytes(writer, edge.target);    try writePairs(writer, edge.metadata);}fn readEdge(allocator: std.mem.Allocator, reader: *Reader) !?model.Edge {    const source = try reader.owned(allocator) orelse return null;    const rel = try reader.owned(allocator) orelse return null;    const target = try reader.owned(allocator) orelse return null;    const metadata = try readPairs(allocator, reader) orelse return null;    return .{ .source = source, .rel = rel, .target = target, .metadata = metadata };}fn writePairs(writer: *std.Io.Writer, pairs: []const model.Pair) !void {    try writeCount(writer, pairs.len);    for (pairs) |pair| {        try writeBytes(writer, pair.key);        try writeBytes(writer, pair.value);        try writer.writeByte(@intFromBool(pair.json));    }}fn readPairs(allocator: std.mem.Allocator, reader: *Reader) !?[]const model.Pair {    const count = reader.count() orelse return null;    if (count > reader.remaining() / (2 * @sizeOf(u32) + 1)) return null;    const pairs = try allocator.alloc(model.Pair, count);    for (pairs) |*pair| {        pair.key = try reader.owned(allocator) orelse return null;        pair.value = try reader.owned(allocator) orelse return null;        pair.json = switch (reader.byte() orelse return null) {            0 => false,            1 => true,            else => return null,        };    }    return pairs;}fn writeOptionalBytes(writer: *std.Io.Writer, value: ?[]const u8) !void {    if (value) |bytes| {        try writer.writeByte(1);        try writeBytes(writer, bytes);    } else {        try writer.writeByte(0);    }}fn writeOptionalInt(writer: *std.Io.Writer, value: ?i64) !void {    if (value) |integer| {        try writer.writeByte(1);        try writeInteger(i64, writer, integer);    } else {        try writer.writeByte(0);    }}fn writeBytes(writer: *std.Io.Writer, bytes: []const u8) !void {    if (bytes.len > std.math.maxInt(u32)) return error.FragmentFieldTooLarge;    try writeInteger(u32, writer, @intCast(bytes.len));    try writer.writeAll(bytes);}fn writeCount(writer: *std.Io.Writer, count: usize) !void {    if (count > std.math.maxInt(u64)) return error.FragmentCountTooLarge;    try writeInteger(u64, writer, @intCast(count));}fn writeInteger(comptime T: type, writer: *std.Io.Writer, value: T) !void {    var encoded: [@sizeOf(T)]u8 = undefined;    std.mem.writeInt(T, &encoded, value, .big);    try writer.writeAll(&encoded);}const OptionalBytes = struct { value: ?[]const u8 };const OptionalInt = struct { value: ?i64 };const Reader = struct {    bytes: []const u8,    position: usize = 0,    fn init(bytes: []const u8) Reader {        return .{ .bytes = bytes };    }    fn remaining(self: Reader) usize {        return self.bytes.len - self.position;    }    fn expect(self: *Reader, expected: []const u8) bool {        const actual = self.take(expected.len) orelse return false;        return std.mem.eql(u8, actual, expected);    }    fn digest(self: *Reader) ?Digest {        const bytes = self.take(@sizeOf(Digest)) orelse return null;        var value: Digest = undefined;        @memcpy(&value, bytes);        return value;    }    fn count(self: *Reader) ?usize {        const value = self.integer(u64) orelse return null;        return std.math.cast(usize, value);    }    fn byte(self: *Reader) ?u8 {        return (self.take(1) orelse return null)[0];    }    fn owned(self: *Reader, allocator: std.mem.Allocator) !?[]const u8 {        const length = self.integer(u32) orelse return null;        const bytes = self.take(length) orelse return null;        return try allocator.dupe(u8, bytes);    }    fn optionalOwned(self: *Reader, allocator: std.mem.Allocator) !?OptionalBytes {        return switch (self.byte() orelse return null) {            0 => .{ .value = null },            1 => .{ .value = try self.owned(allocator) orelse return null },            else => null,        };    }    fn optionalInt(self: *Reader) ?OptionalInt {        return switch (self.byte() orelse return null) {            0 => .{ .value = null },            1 => .{ .value = self.integer(i64) orelse return null },            else => null,        };    }    fn skipBytes(self: *Reader) bool {        const length = self.integer(u32) orelse return false;        _ = self.take(length) orelse return false;        return true;    }    fn skipOptionalBytes(self: *Reader) bool {        return switch (self.byte() orelse return false) {            0 => true,            1 => self.skipBytes(),            else => false,        };    }    fn skipOptionalInt(self: *Reader) bool {        return switch (self.byte() orelse return false) {            0 => true,            1 => self.integer(i64) != null,            else => false,        };    }    fn integer(self: *Reader, comptime T: type) ?T {        const bytes = self.take(@sizeOf(T)) orelse return null;        return std.mem.readInt(T, bytes[0..@sizeOf(T)], .big);    }    fn take(self: *Reader, length: usize) ?[]const u8 {        if (length > self.remaining()) return null;        const start = self.position;        self.position += length;        return self.bytes[start..self.position];    }};fn validChecksum(encoded: []const u8) bool {    if (encoded.len < checksum_bytes) return false;    var computed: [checksum_bytes]u8 = undefined;    std.crypto.hash.sha2.Sha256.hash(encoded[0 .. encoded.len - checksum_bytes], &computed, .{});    return std.mem.eql(u8, &computed, encoded[encoded.len - checksum_bytes ..]);}fn cacheDirectory(allocator: std.mem.Allocator, root: []const u8) ![]const u8 {    return try std.fs.path.join(allocator, &.{ root, ".smg", cache_directory_name });}fn validFragment(encoded: []const u8, executable: Digest, key: Digest) bool {    if (!validChecksum(encoded)) return false;    var reader = payloadReader(encoded, executable, key) orelse return false;    if (!skipEvidence(&reader)) return false;    const node_count = reader.count() orelse return false;    if (node_count > reader.remaining() / 3) return false;    for (0..node_count) |_| if (!skipNode(&reader)) return false;    const edge_count = reader.count() orelse return false;    if (edge_count > reader.remaining() / 3) return false;    for (0..edge_count) |_| if (!skipEdge(&reader)) return false;    const deferred_count = reader.count() orelse return false;    if (deferred_count > reader.remaining() / 3) return false;    for (0..deferred_count) |_| if (!skipEdge(&reader)) return false;    return reader.remaining() == 0;}fn payloadReader(encoded: []const u8, executable: Digest, key: Digest) ?Reader {    if (encoded.len < fragment_magic.len + 2 * @sizeOf(Digest) + checksum_bytes) return null;    var reader = Reader.init(encoded[0 .. encoded.len - checksum_bytes]);    if (!reader.expect(fragment_magic)) return null;    const stored_executable = reader.digest() orelse return null;    if (!std.mem.eql(u8, &stored_executable, &executable)) return null;    const stored_key = reader.digest() orelse return null;    if (!std.mem.eql(u8, &stored_key, &key)) return null;    return reader;}fn skipEvidence(reader: *Reader) bool {    _ = reader.count() orelse return false;    _ = reader.count() orelse return false;    return skipCounts(reader) and skipCounts(reader);}fn skipCounts(reader: *Reader) bool {    const count = reader.count() orelse return false;    if (count > reader.remaining() / (@sizeOf(u32) + @sizeOf(u64))) return false;    for (0..count) |_| {        if (!reader.skipBytes()) return false;        _ = reader.count() orelse return false;    }    return true;}fn skipNode(reader: *Reader) bool {    return reader.skipBytes() and        reader.skipBytes() and        reader.skipOptionalBytes() and        reader.skipOptionalInt() and        reader.skipOptionalInt() and        reader.skipOptionalBytes() and        skipPairs(reader);}fn skipEdge(reader: *Reader) bool {    return reader.skipBytes() and        reader.skipBytes() and        reader.skipBytes() and        skipPairs(reader);}fn skipPairs(reader: *Reader) bool {    const count = reader.count() orelse return false;    if (count > reader.remaining() / (2 * @sizeOf(u32) + 1)) return false;    for (0..count) |_| {        if (!reader.skipBytes() or !reader.skipBytes()) return false;        const json = reader.byte() orelse return false;        if (json > 1) return false;    }    return true;}fn executableDirectory(    allocator: std.mem.Allocator,    root: []const u8,    executable: Digest,) ![]const u8 {    const hex = std.fmt.bytesToHex(executable, .lower);    return try std.fs.path.join(allocator, &.{ root, ".smg", cache_directory_name, &hex });}fn fragmentPath(    allocator: std.mem.Allocator,    root: []const u8,    executable: Digest,    key: Digest,) ![]const u8 {    const hex = std.fmt.bytesToHex(key, .lower);    const file_name = try std.fmt.allocPrint(allocator, "{s}.bin", .{&hex});    return try std.fs.path.join(        allocator,        &.{ try executableDirectory(allocator, root, executable), file_name },    );}fn cachePath(allocator: std.mem.Allocator, root: []const u8, name: []const u8) ![]const u8 {    return try std.fs.path.join(allocator, &.{ root, ".smg", name });}fn elapsedNanoseconds(started: *i128) u64 {    const finished = sys.time.nanoTimestamp();    std.debug.assert(finished >= started.*);    const elapsed: u64 = @intCast(finished - started.*);    started.* = finished;    return elapsed;}fn sinceNanoseconds(started: i128) u64 {    const finished = sys.time.nanoTimestamp();    std.debug.assert(finished >= started);    return @intCast(finished - started);}test "manifest identifies stable changed added and removed sources" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    const first: Digest = @splat(1);    const second: Digest = @splat(2);    var manifest = Manifest.empty(allocator);    defer manifest.deinit();    const empty = try manifest.changedPaths(allocator, "/repo", &.{});    try std.testing.expectEqual(@as(usize, 0), empty.len);    manifest.project = .{ .digest = first, .files = 4 };    try manifest.entries.put("same.py", .{ .key = first, .live = true });    try manifest.entries.put("edit.py", .{ .key = first, .live = true });    try manifest.entries.put("gone.py", .{ .key = first, .live = true });    try manifest.entries.put("dead.py", .{ .key = @splat(0), .live = false });    const current = [_]scan_files.SourceIdentity{        .{ .path = "added.py", .digest = first },        .{ .path = "edit.py", .digest = second },        .{ .path = "same.py", .digest = first },    };    const changed = try manifest.changedPaths(allocator, "/repo", &current);    try std.testing.expect(manifest.matchesProject(first));    try std.testing.expect(!manifest.matchesProject(second));    try std.testing.expectEqual(@as(usize, 3), changed.len);    try std.testing.expectEqualStrings("/repo/added.py", changed[0]);    try std.testing.expectEqualStrings("/repo/edit.py", changed[1]);    try std.testing.expectEqualStrings("/repo/gone.py", changed[2]);}

Source: tools/smg/src/scan/root.zig:5

zig
pub const fragment = @import("fragment.zig");

Complete call list for scan.fragment.loadInto

11 direct calls.

Complete call list for scan.fragment.loadManifest

11 direct calls.

Audit

Definitions18
Public names18
Members12
Version26.7.0
Revisiondaab053ee433