Skip to documentation
SLOP

tiny.smg.storage.nodes

Reference tiny.smg storage nodes

Defined in storage.

API (9)

Actions

Public operations.

Types and contracts

Public types and contracts.

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

Source

Source: tools/smg/src/storage/rows/types.zig:18

zig
pub const NodeRow = struct {    rowid: i64,    node: model.Node,};
Called byCallscommand.nodelisttest; no linktools.smg.src.storage.nodestest: bounded node listing keeps lexi...test; no linktools.smg.src.storage.nodestest: node projections load search ro...modeldeinitNodestorage.nodesfreeListed
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsprivate; no linktools.smg.src.searchselectStoredMatchesmodeldeinitNodestorage.nodesfreeRows
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallscommand.nodelisttest; no linktools.smg.src.storage.nodestest: bounded node listing keeps lexi...test; no linktools.smg.src.storage.nodestest: node projections load search ro...modeldeinitNodeprivate; no linktools.smg.src.storage.nodeslistedBoundedstorage.nodessortedRowsstorage.nodeslisted
Static calls · unresolved targets: 1 · external targets: 3.
Called byCallstest; no linktools.smg.src.storage.nodestest: node projections load search ro...storage.nodesrowsByIdFromReaderstorage.storeopenReadstorage.nodesrowsById
Static calls · unresolved targets: 1 · external targets: 0.
Called byCallsprivate; no linktools.smg.src.searchselectStoredMatchesstorage.nodesrowsByIdmodeldeinitNodeprivate; no linktools.smg.src.storage.nodesuniquePositiveRowidsstorage.nodesrowsByIdFromReader
Static calls · unresolved targets: 4 · external targets: 4.
Called byCallsNo direct callsstorage.nodessortedRowsstorage.nodessortRows
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsstorage.nodeslistedstorage.nodessortRowsstorage.storeopenReadstorage.nodessortedRows
Static calls · unresolved targets: 1 · external targets: 1.

Source: tools/smg/src/storage/nodes.zig

zig
const std = @import("std");const sql = @import("sql");const sys = @import("sys");const smg = @import("../root.zig");const storage = smg.storage;const database_read = storage.database.read;const store = storage.store;const sync = storage.sync;const rows = storage.rows;const graph_mod = smg.graph;const model = smg.model;const view = smg.view;pub const NodeRow = rows.NodeRow;pub const ListedNodes = struct {    nodes: []const model.Node,    total: usize,};pub fn rowsById(    allocator: std.mem.Allocator,    root: []const u8,    rowids: []const i64,    limits: smg.StorageLimits,) ![]NodeRow {    var opened = try store.openRead(allocator, root, limits);    defer opened.close();    return try rowsByIdFromReader(allocator, &opened.reader, rowids);}pub fn rowsByIdFromReader(allocator: std.mem.Allocator, reader: *storage.database.Reader, rowids: []const i64) ![]NodeRow {    const requested = try uniquePositiveRowids(allocator, rowids);    defer allocator.free(requested);    if (requested.len == 0) return &.{};    var handle = try reader.catalog.openRelation(reader.allocator, rows.nodes_relation);    defer handle.deinit();    var selected_rows: std.ArrayList(NodeRow) = .empty;    errdefer {        for (selected_rows.items) |*row| model.deinitNode(&row.node, allocator);        selected_rows.deinit(allocator);    }    for (requested) |rowid| {        const bytes = (try handle.relation.get(allocator, rowid)) orelse continue;        defer allocator.free(bytes);        try selected_rows.append(allocator, try rows.decodeNodeRow(allocator, rowid, bytes));    }    return try selected_rows.toOwnedSlice(allocator);}pub fn listed(    allocator: std.mem.Allocator,    root: []const u8,    type_filter: ?[]const u8,    limit: usize,    limits: smg.StorageLimits,) !ListedNodes {    if (limit != 0) return try listedBounded(allocator, root, type_filter, limit, limits);    const sorted = try sortedRows(allocator, root, limits);    defer allocator.free(sorted);    var listed_nodes: std.ArrayList(model.Node) = .empty;    errdefer {        for (listed_nodes.items) |*node| model.deinitNode(node, allocator);        listed_nodes.deinit(allocator);    }    var total: usize = 0;    for (sorted) |loaded| {        var node = loaded.node;        if (type_filter) |filter| {            if (!std.mem.eql(u8, node.type, filter)) {                model.deinitNode(&node, allocator);                continue;            }        }        total += 1;        if (limit != 0 and listed_nodes.items.len == limit) {            model.deinitNode(&node, allocator);            continue;        }        try listed_nodes.append(allocator, node);    }    return .{ .nodes = try listed_nodes.toOwnedSlice(allocator), .total = total };}fn listedBounded(    allocator: std.mem.Allocator,    root: []const u8,    type_filter: ?[]const u8,    limit: usize,    limits: smg.StorageLimits,) !ListedNodes {    var opened = try store.openRead(allocator, root, limits);    defer opened.close();    var handle = opened.reader.catalog.openRelation(opened.reader.allocator, rows.nodes_relation) catch |err| switch (err) {        error.RelationNotFound => return .{ .nodes = &.{}, .total = 0 },        else => return err,    };    defer handle.deinit();    var scan: sql.TableScan = undefined;    try handle.relation.scan(&scan, opened.reader.allocator, null, null);    defer scan.deinit();    var listed_nodes: std.ArrayList(model.Node) = .empty;    errdefer {        for (listed_nodes.items) |*node| model.deinitNode(node, allocator);        listed_nodes.deinit(allocator);    }    var total: usize = 0;    while (try scan.next()) |entry| {        const node_type = try rows.nodeTypeView(entry.bytes);        if (type_filter) |filter| {            if (!std.mem.eql(u8, node_type, filter)) continue;        }        total += 1;        const name = try rows.nodeNameView(entry.bytes);        try retainBoundedNode(allocator, &listed_nodes, limit, name, entry.rowid, entry.bytes);    }    std.mem.sort(model.Node, listed_nodes.items, {}, nodeLess);    return .{ .nodes = try listed_nodes.toOwnedSlice(allocator), .total = total };}fn retainBoundedNode(allocator: std.mem.Allocator, listed_nodes: *std.ArrayList(model.Node), limit: usize, name: []const u8, rowid: i64, bytes: []const u8) !void {    if (listed_nodes.items.len < limit) {        var node = (try rows.decodeNodeRow(allocator, rowid, bytes)).node;        errdefer model.deinitNode(&node, allocator);        try listed_nodes.append(allocator, node);        return;    }    const worst_index = worstNodeIndex(listed_nodes.items);    if (!std.mem.lessThan(u8, name, listed_nodes.items[worst_index].name)) return;    const row = try rows.decodeNodeRow(allocator, rowid, bytes);    var old = listed_nodes.items[worst_index];    listed_nodes.items[worst_index] = row.node;    model.deinitNode(&old, allocator);}fn worstNodeIndex(nodes: []const model.Node) usize {    var worst: usize = 0;    for (nodes[1..], 1..) |node, index| {        if (std.mem.lessThan(u8, nodes[worst].name, node.name)) worst = index;    }    return worst;}pub fn freeRows(allocator: std.mem.Allocator, node_rows: []NodeRow) void {    for (node_rows) |*row| model.deinitNode(&row.node, allocator);    if (node_rows.len != 0) allocator.free(node_rows);}pub fn freeListed(allocator: std.mem.Allocator, loaded: ListedNodes) void {    for (loaded.nodes) |*node_const| {        var node = node_const.*;        model.deinitNode(&node, allocator);    }    if (loaded.nodes.len != 0) allocator.free(loaded.nodes);}pub fn sortedRows(    allocator: std.mem.Allocator,    root: []const u8,    limits: smg.StorageLimits,) ![]rows.NodeRow {    var opened = try store.openRead(allocator, root, limits);    defer opened.close();    const node_rows = try database_read.readerNodes(&opened.reader, allocator, allocator);    sortRows(node_rows);    return node_rows;}pub fn sortRows(node_rows: []rows.NodeRow) void {    std.mem.sort(rows.NodeRow, node_rows, {}, rowLess);}fn rowLess(_: void, left: rows.NodeRow, right: rows.NodeRow) bool {    return std.mem.lessThan(u8, left.node.name, right.node.name);}fn nodeLess(_: void, left: model.Node, right: model.Node) bool {    return std.mem.lessThan(u8, left.name, right.name);}fn uniquePositiveRowids(allocator: std.mem.Allocator, rowids: []const i64) ![]i64 {    var requested: std.ArrayList(i64) = .empty;    errdefer requested.deinit(allocator);    for (rowids) |rowid| if (rowid > 0) try requested.append(allocator, rowid);    std.mem.sort(i64, requested.items, {}, rowidLess);    var write: usize = 0;    for (requested.items) |rowid| {        if (write != 0 and requested.items[write - 1] == rowid) continue;        requested.items[write] = rowid;        write += 1;    }    requested.items.len = write;    return try requested.toOwnedSlice(allocator);}fn rowidLess(_: void, left: i64, right: i64) bool {    return left < right;}test "node projections load search rows listings and stable rowids" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    const root = try testRoot(allocator, 0);    defer sys.fs.deleteTree(root) catch {};    try initStore(allocator, root);    var stored = graph_mod.init(allocator);    const meta = try model.sourcePair(allocator, "scan");    try graph_mod.addNode(&stored, .{ .name = "a", .type = model.NodeType.module, .metadata = meta });    try graph_mod.addNode(&stored, .{ .name = "a.f", .type = model.NodeType.function, .file = "a.zig", .line = 3, .docstring = "search body" });    try graph_mod.addNode(&stored, .{ .name = "m.item", .type = model.NodeType.module, .file = "m.zig" });    try graph_mod.addNode(&stored, .{ .name = "z.item", .type = model.NodeType.function, .file = "z.zig", .line = 9 });    try graph_mod.addEdge(&stored, .{ .source = "a", .target = "a.f", .rel = model.RelType.contains });    try graph_mod.addEdge(&stored, .{ .source = "z.item", .target = "a.f", .rel = model.RelType.contains });    _ = try saveGraph(allocator, stored, root);    const loaded = try listed(allocator, root, null, 2, smg.default_limits.storage);    defer freeListed(allocator, loaded);    try std.testing.expectEqual(@as(usize, 4), loaded.total);    try std.testing.expectEqual(@as(usize, 2), loaded.nodes.len);    try std.testing.expectEqualStrings("a", loaded.nodes[0].name);    try std.testing.expectEqualStrings("a.f", loaded.nodes[1].name);    const filtered = try listed(        allocator,        root,        model.NodeType.function,        1,        smg.default_limits.storage,    );    defer freeListed(allocator, filtered);    try std.testing.expectEqual(@as(usize, 2), filtered.total);    try std.testing.expectEqual(@as(usize, 1), filtered.nodes.len);    try std.testing.expectEqualStrings("a.f", filtered.nodes[0].name);    var grown = graph_mod.init(allocator);    try graph_mod.addNode(&grown, .{ .name = "a", .type = model.NodeType.module });    try graph_mod.addNode(&grown, .{ .name = "a.f", .type = model.NodeType.function, .file = "a.zig", .line = 3, .docstring = "search body" });    try graph_mod.addNode(&grown, .{ .name = "c", .type = model.NodeType.module });    try graph_mod.addNode(&grown, .{ .name = "m.item", .type = model.NodeType.module, .file = "m.zig" });    try graph_mod.addNode(&grown, .{ .name = "z.item", .type = model.NodeType.function, .file = "z.zig", .line = 9 });    const saved_nodes = try saveGraph(allocator, grown, root);    try std.testing.expectEqual(@as(usize, 5), saved_nodes.len);    try std.testing.expectEqualStrings("c", saved_nodes[2].name);    const loaded_rows = try rowsById(        allocator,        root,        &.{ 3, 1, 3, -1 },        smg.default_limits.storage,    );    try std.testing.expectEqual(@as(usize, 2), loaded_rows.len);    try std.testing.expectEqual(@as(i64, 1), loaded_rows[0].rowid);    try std.testing.expectEqualStrings("a", loaded_rows[0].node.name);    try std.testing.expectEqual(@as(i64, 3), loaded_rows[1].rowid);    try std.testing.expectEqualStrings("m.item", loaded_rows[1].node.name);}test "bounded node listing keeps lexical order independent of rowid" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    const root = try testRoot(allocator, 3);    defer sys.fs.deleteTree(root) catch {};    try initStore(allocator, root);    {        var opened = try store.open(allocator, root, smg.default_limits.storage);        defer opened.close();        try storage.database.edits.apply(&opened.database, .{            .node_puts = &.{                .{ .rowid = 1, .node = .{ .name = "z", .type = model.NodeType.module } },                .{ .rowid = 2, .node = .{ .name = "a", .type = model.NodeType.function } },                .{ .rowid = 3, .node = .{ .name = "m", .type = model.NodeType.function } },                .{ .rowid = 4, .node = .{ .name = "b", .type = model.NodeType.type } },            },        });        try storage.database.commitIfDirty(&opened.database);    }    const loaded = try listed(allocator, root, null, 2, smg.default_limits.storage);    defer freeListed(allocator, loaded);    try std.testing.expectEqual(@as(usize, 4), loaded.total);    try std.testing.expectEqual(@as(usize, 2), loaded.nodes.len);    try std.testing.expectEqualStrings("a", loaded.nodes[0].name);    try std.testing.expectEqualStrings("b", loaded.nodes[1].name);    const filtered = try listed(        allocator,        root,        model.NodeType.function,        2,        smg.default_limits.storage,    );    defer freeListed(allocator, filtered);    try std.testing.expectEqual(@as(usize, 2), filtered.total);    try std.testing.expectEqual(@as(usize, 2), filtered.nodes.len);    try std.testing.expectEqualStrings("a", filtered.nodes[0].name);    try std.testing.expectEqualStrings("m", filtered.nodes[1].name);}fn initStore(allocator: std.mem.Allocator, root: []const u8) !void {    try sys.fs.createDirPath(root);    var opened = try store.open(allocator, root, smg.default_limits.storage);    opened.close();}fn saveGraph(allocator: std.mem.Allocator, graph: graph_mod.Graph, root: []const u8) ![]const model.Node {    const stored_nodes = try view.allNodes(graph, allocator, null);    var opened = try store.open(allocator, root, smg.default_limits.storage);    defer opened.close();    try sync.replaceGraph(allocator, &opened.database, graph);    return stored_nodes;}fn testRoot(allocator: std.mem.Allocator, offset: i64) ![]const u8 {    return try std.fmt.allocPrint(allocator, "/tmp/smg-storage-nodes-test-{x}", .{@as(u64, @intCast(@max(0, sys.time.realMilliTimestamp() + offset)))});}

Source: tools/smg/src/storage/root.zig:6

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

Audit

Definitions10
Public names11
Members4
Version26.7.0
Revisiondaab053ee433