Skip to documentation
SLOP

tiny.smg.context.query

Reference tiny.smg context query

Defined in context.

API (1)

Actions

Public operations.

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

Source

Called byCallsprivate; no linktools.smg.src.context.inspectcollectprivate; no linktools.smg.src.context.queryappendprivate; no linktools.smg.src.context.queryneighborhoodprivate; no linktools.smg.src.context.queryresolveprivate; no linktools.smg.src.context.queryunsupportedcontext.queryrun
Static calls · unresolved targets: 0 · external targets: 4.

Source: tools/smg/src/context/query.zig

zig
const std = @import("std");const sql = @import("sql");const repo = @import("repo");const smg = @import("../root.zig");const context = @import("root.zig");const model = context.model;const rows = smg.storage.rows;const Visit = struct { name: []const u8, depth: u8 };pub fn run(builder: *model.Builder, reader: *smg.storage.database.Reader) !void {    std.debug.assert(builder.target_count != 0);    std.debug.assert(builder.target_count <= model.targets_max);    var nodes = try reader.catalog.openRelation(reader.allocator, rows.nodes_relation);    defer nodes.deinit();    var edges = try reader.catalog.openRelation(reader.allocator, rows.edges_relation);    defer edges.deinit();    for (builder.targets[0..builder.target_count]) |*target| {        if (!builder.active()) break;        if (unsupported(target.input)) {            target.state = .unsupported;            builder.report.coverage.unsupported += 1;            continue;        }        const node = try resolve(builder, &nodes, target) orelse continue;        target.state = .resolved;        target.name = node.name;        builder.report.coverage.resolved += 1;        _ = try append(builder, node, target.input, .target, "target");        try neighborhood(builder, &nodes, &edges, target.input, node.name);    }    std.mem.sort(        model.Entry,        builder.entries[0..builder.entry_count],        builder.targets[0..builder.target_count],        entryLess,    );}fn resolve(builder: *model.Builder, nodes: anytype, target: *model.Target) !?smg.Node {    std.debug.assert(target.state == .unqueried);    std.debug.assert(target.input.len != 0);    if (try exact(builder, nodes, target.input)) |node| return node;    const translated = try translatedPath(builder, target.input);    if (translated) |name| if (try exact(builder, nodes, name)) |node| return node;    const query = translated orelse target.input;    const slot = indexSlot(nodes, rows.node_name_index) orelse return error.StorageIndexMissing;    var scan: sql.IndexScan = undefined;    try nodes.relation.indexScan(&scan, builder.allocator, slot, null, null);    defer scan.deinit();    var matches: [model.targets_max][]const u8 = undefined;    var count: usize = 0;    var complete = false;    while (builder.row()) {        const row = (try scan.next()) orelse {            complete = true;            break;        };        builder.report.work.visited_index_rows += 1;        var values: [1]sql.row.Value = undefined;        var key_buffer: [sql.page.size]u8 = undefined;        const name = try smg.storage.names.indexedName(&values, &key_buffer, row.key);        if (!smg.name.matchesSuffix(name, query)) continue;        if (contains(matches[0..count], name)) continue;        if (count == matches.len) {            builder.exhausted();            break;        }        matches[count] = try builder.allocator.dupe(u8, name);        count += 1;    }    target.candidates = try builder.allocator.dupe([]const u8, matches[0..count]);    if (count > 1) {        target.state = .ambiguous;        builder.report.coverage.ambiguous += 1;    } else if (complete and count == 1) {        return exact(builder, nodes, matches[0]);    } else if (complete) {        target.state = .missing;        builder.report.coverage.missing += 1;    }    return null;}fn exact(builder: *model.Builder, nodes: anytype, name: []const u8) !?smg.Node {    std.debug.assert(name.len != 0);    std.debug.assert(builder.report.work.index_steps <= builder.options.limits.rows);    if (!builder.row()) return null;    const slot = indexSlot(nodes, rows.node_name_index) orelse return error.StorageIndexMissing;    var lookup: sql.IndexScan = undefined;    try nodes.relation.lookup(&lookup, builder.allocator, slot, &.{.{ .text = name }});    defer lookup.deinit();    const found = (try lookup.next()) orelse return null;    builder.report.work.visited_index_rows += 1;    var buffer: [model.row_bytes_max]u8 = undefined;    const bytes = (try nodes.relation.getInto(found.rowid, &buffer)) orelse        return error.StorageIndexCorrupt;    const node = (try rows.decodeNodeRow(builder.allocator, found.rowid, bytes)).node;    builder.report.work.loaded_nodes += 1;    try validNode(node);    return node;}fn neighborhood(    builder: *model.Builder,    nodes: anytype,    edges: anytype,    target: []const u8,    name: []const u8,) !void {    std.debug.assert(name.len != 0);    std.debug.assert(builder.options.limits.depth <= 2);    var queue: [model.entries_max]Visit = undefined;    queue[0] = .{ .name = name, .depth = 0 };    var count: usize = 1;    var cursor: usize = 0;    while (cursor < count and builder.active()) : (cursor += 1) {        const visit = queue[cursor];        builder.report.work.depth_reached = @max(builder.report.work.depth_reached, visit.depth);        if (visit.depth == builder.options.limits.depth) {            builder.report.omissions.depth = true;            builder.exhausted();            continue;        }        try adjacency(builder, nodes, edges, target, visit, true, &queue, &count);        try adjacency(builder, nodes, edges, target, visit, false, &queue, &count);    }}fn adjacency(    builder: *model.Builder,    nodes: anytype,    edges: anytype,    target: []const u8,    visit: Visit,    incoming: bool,    queue: []Visit,    count: *usize,) !void {    std.debug.assert(visit.depth < builder.options.limits.depth);    std.debug.assert(count.* != 0);    std.debug.assert(count.* <= queue.len);    const index = if (incoming) rows.edge_target_index else rows.edge_source_index;    const slot = indexSlot(edges, index) orelse return error.StorageIndexMissing;    var lookup: sql.IndexScan = undefined;    try edges.relation.lookup(&lookup, builder.allocator, slot, &.{.{ .text = visit.name }});    defer lookup.deinit();    while (builder.row()) {        const found = (try lookup.next()) orelse break;        builder.report.work.visited_index_rows += 1;        builder.report.work.visited_edges += 1;        var buffer: [model.row_bytes_max]u8 = undefined;        const bytes = (try edges.relation.getInto(found.rowid, &buffer)) orelse            return error.StorageIndexCorrupt;        const edge = (try rows.decodeEdgeRow(builder.allocator, found.rowid, bytes)).edge;        try validEdge(edge);        const name = if (incoming) edge.source else edge.target;        if (std.mem.eql(u8, name, queue[0].name)) continue;        const node = try exact(builder, nodes, name) orelse continue;        const section = classify(node, edge, incoming, visit.depth);        if (!try append(builder, node, target, section, edge.rel)) continue;        enqueue(builder, queue, count, .{ .name = node.name, .depth = visit.depth + 1 });    }}fn classify(node: smg.Node, edge: smg.Edge, incoming: bool, depth: u8) model.Section {    std.debug.assert(node.name.len != 0);    std.debug.assert(depth < 2);    if (smg.api.isTestDeclaration(node)) return .tests;    if (incoming and std.mem.eql(u8, edge.rel, smg.model.RelType.contains)) return .owner;    if (incoming) return if (depth == 0) .usages else .impact;    return .dependencies;}fn append(    builder: *model.Builder,    node: smg.Node,    target: []const u8,    section: model.Section,    relation: []const u8,) !bool {    std.debug.assert(node.name.len != 0);    std.debug.assert(target.len != 0);    var entry = model.Entry{        .target = target,        .section = section,        .relation = relation,        .name = node.name,        .path = node.file,        .line = node.line,    };    if (!builder.add(entry)) return false;    try context.source.inspect(builder, &entry, node);    builder.entries[builder.entry_count - 1] = entry;    return true;}fn enqueue(builder: *model.Builder, queue: []Visit, count: *usize, visit: Visit) void {    std.debug.assert(count.* <= queue.len);    std.debug.assert(visit.depth <= builder.options.limits.depth);    for (queue[0..count.*]) |existing| if (std.mem.eql(u8, existing.name, visit.name)) return;    if (count.* == queue.len) {        builder.exhausted();        return;    }    queue[count.*] = visit;    count.* += 1;}fn indexSlot(handle: anytype, name: []const u8) ?usize {    for (handle.index_definitions, 0..) |definition, slot| {        if (std.mem.eql(u8, definition.name, name)) return slot;    }    return null;}fn contains(names: []const []const u8, name: []const u8) bool {    std.debug.assert(names.len <= model.targets_max);    for (names) |entry| if (std.mem.eql(u8, entry, name)) return true;    return false;}fn translatedPath(builder: *model.Builder, input: []const u8) !?[]const u8 {    const path = std.mem.indexOfScalar(u8, input, '/') != null or        smg.scan.scan_files.supported(input);    if (!path) return null;    const relative = try repo.work.resolvedPathAlloc(builder.allocator, builder.root, input);    const prefixed = try std.fmt.allocPrint(builder.allocator, "./{s}", .{relative});    return smg.name.dottedFromPath(builder.allocator, prefixed);}fn unsupported(input: []const u8) bool {    if (std.mem.indexOfScalar(u8, input, '/') == null) return false;    if (std.fs.path.extension(input).len == 0) return false;    return !smg.scan.scan_files.supported(input);}fn validEdge(edge: smg.Edge) !void {    for ([_][]const u8{ edge.source, edge.rel, edge.target }) |value| {        if (value.len > 4096 or !std.unicode.utf8ValidateSlice(value)) {            return error.InvalidInspectionEdge;        }    }}fn validNode(node: smg.Node) !void {    if (node.name.len > 4096 or !std.unicode.utf8ValidateSlice(node.name)) {        return error.InvalidInspectionNode;    }    if (node.file) |path| if (path.len > 4096 or !std.unicode.utf8ValidateSlice(path)) {        return error.InvalidInspectionNode;    };}fn entryLess(targets: []model.Target, left: model.Entry, right: model.Entry) bool {    std.debug.assert(targets.len != 0);    std.debug.assert(targets.len <= model.targets_max);    if (!std.mem.eql(u8, left.target, right.target)) {        for (targets) |target| {            if (std.mem.eql(u8, target.input, left.target)) return true;            if (std.mem.eql(u8, target.input, right.target)) return false;        }        unreachable;    }    if (left.section != right.section) {        return @backingInt(left.section) < @backingInt(right.section);    }    const name = std.mem.order(u8, left.name, right.name);    if (name != .eq) return name == .lt;    return std.mem.lessThan(u8, left.relation, right.relation);}

Source: tools/smg/src/context/root.zig:7

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

Audit

Definitions2
Public names2
Members0
Version26.7.0
Revisiondaab053ee433