libertaria-stack/l1-identity/qvl/storage.zig

131 lines
4.0 KiB
Zig

//! QVL Storage Layer - Stub Implementation
//!
//! This is a stub/mock implementation for testing without libmdbx.
//! Replace with real libmdbx implementation when available.
const std = @import("std");
const types = @import("types.zig");
const NodeId = types.NodeId;
const RiskEdge = types.RiskEdge;
const RiskGraph = types.RiskGraph;
/// Mock persistent storage using in-memory HashMap
pub const PersistentGraph = struct {
allocator: std.mem.Allocator,
nodes: std.AutoHashMap(NodeId, void),
edges: std.AutoHashMap(EdgeKey, RiskEdge),
adjacency: std.AutoHashMap(NodeId, std.ArrayList(NodeId)),
path: []const u8,
const EdgeKey = struct {
from: NodeId,
to: NodeId,
pub fn hash(self: EdgeKey) u64 {
return @as(u64, self.from) << 32 | self.to;
}
pub fn eql(self: EdgeKey, other: EdgeKey) bool {
return self.from == other.from and self.to == other.to;
}
};
const Self = @This();
/// Open or create persistent graph (mock: in-memory)
pub fn open(path: []const u8, config: DBConfig, allocator: std.mem.Allocator) !Self {
_ = config;
return Self{
.allocator = allocator,
.nodes = std.AutoHashMap(NodeId, void).init(allocator),
.edges = std.AutoHashMap(EdgeKey, RiskEdge).init(allocator),
.adjacency = std.AutoHashMap(NodeId, std.ArrayList(NodeId)).init(allocator),
.path = try allocator.dupe(u8, path),
};
}
/// Close database
pub fn close(self: *Self) void {
// Clean up adjacency lists
var it = self.adjacency.valueIterator();
while (it.next()) |list| {
list.deinit(self.allocator);
}
self.adjacency.deinit();
self.edges.deinit();
self.nodes.deinit();
self.allocator.free(self.path);
}
/// Add node
pub fn addNode(self: *Self, node: NodeId) !void {
try self.nodes.put(node, {});
}
/// Add edge
pub fn addEdge(self: *Self, edge: RiskEdge) !void {
// Register nodes first
try self.nodes.put(edge.from, {});
try self.nodes.put(edge.to, {});
const key = EdgeKey{ .from = edge.from, .to = edge.to };
try self.edges.put(key, edge);
// Update adjacency
const entry = try self.adjacency.getOrPut(edge.from);
if (!entry.found_existing) {
entry.value_ptr.* = .{}; // Empty ArrayList, allocator passed on append
}
try entry.value_ptr.append(self.allocator, edge.to);
}
/// Get outgoing neighbors
pub fn getOutgoing(self: *Self, node: NodeId, allocator: std.mem.Allocator) ![]NodeId {
if (self.adjacency.get(node)) |list| {
// Copy to new slice with provided allocator
return allocator.dupe(NodeId, list.items);
}
return allocator.dupe(NodeId, &[_]NodeId{});
}
/// Get specific edge
pub fn getEdge(self: *Self, from: NodeId, to: NodeId) !?RiskEdge {
const key = EdgeKey{ .from = from, .to = to };
return self.edges.get(key);
}
/// Load in-memory RiskGraph
pub fn toRiskGraph(self: *Self, allocator: std.mem.Allocator) !RiskGraph {
var graph = RiskGraph.init(allocator);
errdefer graph.deinit();
// First add all nodes
var node_it = self.nodes.keyIterator();
while (node_it.next()) |node| {
try graph.addNode(node.*);
}
// Then add all edges
var edge_it = self.edges.valueIterator();
while (edge_it.next()) |edge| {
try graph.addEdge(edge.*);
}
return graph;
}
};
/// Database configuration (mock accepts same config for API compatibility)
pub const DBConfig = struct {
max_readers: u32 = 64,
max_dbs: u32 = 8,
map_size: usize = 10 * 1024 * 1024,
page_size: u32 = 4096,
};
// Re-export for integration.zig
pub const lmdb = struct {
// Stub exports
};