tiny.smg.traversal
Defined in tiny.smg.
API (13)
Actions
Public operations.
EdgeIterator.deinitEdgeIterator.next: The returned strings remain valid until the next call to next.NodeIterator.deinitNodeIterator.nextSource.edgesSource.nodeSource.prefiximpact
Types and contracts
Public types and contracts.
Source
Source: tools/smg/src/root.zig:36
zig
pub const traversal = @import("traversal.zig");Source: tools/smg/src/traversal.zig
zig
const std = @import("std");const sql = @import("sql");const smg = @import("root.zig");const graph_mod = smg.graph;const model = smg.model;const storage = smg.storage;const row_bytes_max = @import("context/model.zig").row_bytes_max;pub const Direction = enum { incoming, outgoing };pub const Edge = struct { source: []const u8, rel: []const u8, target: []const u8,};pub const Source = union(enum) { graph: graph_mod.Graph, stored: *storage.database.Reader, pub fn node(self: Source, allocator: std.mem.Allocator, name: []const u8) !?model.Node { return switch (self) { .graph => |graph| if (graph_mod.getNode(&graph, name)) |found| found else null, .stored => |reader| blk: { const found = storage.context.loadFromReader( allocator, reader, name, false, ) catch |err| switch (err) { error.NodeNotFound => break :blk null, else => return err, }; break :blk found.node; }, }; } pub fn edges( self: Source, _: std.mem.Allocator, name: []const u8, direction: Direction, ) !EdgeIterator { return switch (self) { .graph => |graph| .{ .graph = .{ .graph = graph, .indices = if (direction == .incoming) graph_mod.incomingEdges(&graph, name) else graph_mod.outgoingEdges(&graph, name), } }, .stored => |reader| blk: { var handle = try reader.catalog.openRelation( reader.allocator, storage.rows.edges_relation, ); errdefer handle.deinit(); const index_name = if (direction == .incoming) storage.rows.edge_target_index else storage.rows.edge_source_index; const slot = indexSlot(&handle, index_name) orelse return error.StorageIndexMissing; var scan: sql.index.Scan = undefined; try handle.relation.lookup( &scan, reader.allocator, slot, &.{.{ .text = name }}, ); break :blk .{ .stored = .{ .handle = handle, .scan = scan } }; }, }; } pub fn prefix( self: Source, allocator: std.mem.Allocator, namespace: []const u8, ) !NodeIterator { const prefix_name = try std.fmt.allocPrint(allocator, "{s}.", .{namespace}); const end_name = try std.fmt.allocPrint(allocator, "{s}/", .{namespace}); return switch (self) { .graph => |graph| .{ .graph = .{ .graph = graph, .prefix_name = prefix_name, } }, .stored => |reader| blk: { var handle = try reader.catalog.openRelation( reader.allocator, storage.rows.nodes_relation, ); errdefer handle.deinit(); const slot = indexSlot(&handle, storage.rows.node_name_index) orelse return error.StorageIndexMissing; var scan: sql.index.Scan = undefined; try handle.relation.indexScan( &scan, reader.allocator, slot, &.{.{ .text = prefix_name }}, &.{.{ .text = end_name }}, ); break :blk .{ .stored = .{ .handle = handle, .scan = scan } }; }, }; }};pub const EdgeIterator = union(enum) { graph: struct { graph: graph_mod.Graph, indices: []const usize, cursor: usize = 0, }, stored: struct { handle: sql.catalog.ReadRelationHandle, scan: sql.index.Scan, buffer: [row_bytes_max]u8 = undefined, }, pub fn deinit(self: *EdgeIterator) void { switch (self.*) { .graph => {}, .stored => |*stored| { stored.scan.deinit(); stored.handle.deinit(); }, } } /// The returned strings remain valid until the next call to next. pub fn next(self: *EdgeIterator) !?Edge { return switch (self.*) { .graph => |*graph| blk: { if (graph.cursor == graph.indices.len) break :blk null; const edge = graph.graph.edges.items[graph.indices[graph.cursor]]; graph.cursor += 1; break :blk .{ .source = edge.source, .rel = edge.rel, .target = edge.target, }; }, .stored => |*stored| blk: { const entry = (try stored.scan.next()) orelse break :blk null; const bytes = (try stored.handle.relation.getInto( entry.rowid, &stored.buffer, )) orelse return error.StorageIndexCorrupt; break :blk .{ .source = try storage.rows.edgeSourceView(bytes), .rel = try storage.rows.edgeRelView(bytes), .target = try storage.rows.edgeTargetView(bytes), }; }, }; }};pub const NodeIterator = union(enum) { graph: struct { graph: graph_mod.Graph, prefix_name: []const u8, cursor: usize = 0, }, stored: struct { handle: sql.catalog.ReadRelationHandle, scan: sql.index.Scan, buffer: [row_bytes_max]u8 = undefined, }, pub fn deinit(self: *NodeIterator) void { switch (self.*) { .graph => {}, .stored => |*stored| { stored.scan.deinit(); stored.handle.deinit(); }, } } pub fn next(self: *NodeIterator, allocator: std.mem.Allocator) !?model.Node { return switch (self.*) { .graph => |*graph| blk: { while (graph.cursor < graph.graph.nodes.items.len) { const node = graph.graph.nodes.items[graph.cursor]; graph.cursor += 1; if (std.mem.startsWith(u8, node.name, graph.prefix_name)) break :blk node; } break :blk null; }, .stored => |*stored| blk: { const entry = (try stored.scan.next()) orelse break :blk null; const bytes = (try stored.handle.relation.getInto( entry.rowid, &stored.buffer, )) orelse return error.StorageIndexCorrupt; break :blk (try storage.rows.decodeNodeRow( allocator, entry.rowid, bytes, )).node; }, }; }};fn indexSlot(handle: *const sql.catalog.ReadRelationHandle, name: []const u8) ?usize { for (handle.index_definitions, 0..) |definition, slot| { if (std.mem.eql(u8, definition.name, name)) return slot; } return null;}pub fn impact( allocator: std.mem.Allocator, source: Source, name: []const u8, max_depth: ?usize, coupling_only: bool,) ![]const []const u8 { var seen = std.StringHashMap(usize).init(allocator); var queue: std.ArrayList([]const u8) = .empty; try seen.put(name, 0); try queue.append(allocator, name); var cursor: usize = 0; while (cursor < queue.items.len) : (cursor += 1) { const current = queue.items[cursor]; const depth = seen.get(current).?; if (max_depth != null and depth >= max_depth.?) continue; var edges = try source.edges(allocator, current, .incoming); defer edges.deinit(); while (try edges.next()) |edge| { if (coupling_only and !smg.exports.isCoupling(edge.rel)) continue; if (seen.contains(edge.source)) continue; const next = if (source == .stored) try allocator.dupe(u8, edge.source) else edge.source; try seen.put(next, depth + 1); try queue.append(allocator, next); } } var out: std.ArrayList([]const u8) = .empty; var iterator = seen.iterator(); while (iterator.next()) |entry| { if (!std.mem.eql(u8, entry.key_ptr.*, name)) { try out.append(allocator, entry.key_ptr.*); } } std.mem.sort([]const u8, out.items, {}, stringLess); return try out.toOwnedSlice(allocator);}fn stringLess(_: void, left: []const u8, right: []const u8) bool { return std.mem.lessThan(u8, left, right);}Audit
| Definitions | 14 |
|---|---|
| Public names | 14 |
| Members | 11 |
| Version | 26.7.0 |
| Revision | daab053ee433 |