feat: add difference and ListMeta to collections
This commit is contained in:
1 parent
9f0ee59bf0
commit
40712f5cdd
1 file changed
+160
@@ -474,3 +474,163 @@ pub const Set = struct {
|
|||||||
results.deinit();
|
results.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (names) |name| {
|
||||||
|
const ms = try members(db, allocator, name);
|
||||||
|
defer {
|
||||||
|
for (ms) |m| allocator.free(m);
|
||||||
|
allocator.free(ms);
|
||||||
|
}
|
||||||
|
for (ms) |m| {
|
||||||
|
var seen = false;
|
||||||
|
for (results.items) |existing| {
|
||||||
|
if (std.mem.eql(u8, existing, m)) {
|
||||||
|
seen = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!seen) try results.append(try allocator.dupe(u8, m));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results.toOwnedSlice();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Members in `name` that are not in any set of `others`. Caller owns the result.
|
||||||
|
pub fn difference(db: *Db, allocator: Allocator, name: []const u8, others: []const []const u8) ![][]u8 {
|
||||||
|
var results = std.ArrayList([]u8).init(allocator);
|
||||||
|
errdefer {
|
||||||
|
for (results.items) |m| allocator.free(m);
|
||||||
|
results.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
const base = try members(db, allocator, name);
|
||||||
|
defer {
|
||||||
|
for (base) |m| allocator.free(m);
|
||||||
|
allocator.free(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
outer: for (base) |candidate| {
|
||||||
|
for (others) |other| {
|
||||||
|
if (try isMember(db, allocator, other, candidate)) continue :outer;
|
||||||
|
}
|
||||||
|
try results.append(try allocator.dupe(u8, candidate));
|
||||||
|
}
|
||||||
|
return results.toOwnedSlice();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// List (double-ended queue keyed by a monotonic sequence range)
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
const ListMeta = struct {
|
||||||
|
// Elements occupy sequence numbers (head+1 .. tail-1) inclusive; an
|
||||||
|
// empty list has tail == head + 1. Pushing left decrements head and
|
||||||
|
// writes at the new head; pushing right increments tail and writes at
|
||||||
|
// the slot just below the new tail.
|
||||||
|
head: i64,
|
||||||
|
tail: i64,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const List = struct {
|
||||||
|
fn metaKey(allocator: Allocator, name: []const u8) ![]u8 {
|
||||||
|
var out = std.ArrayList(u8).init(allocator);
|
||||||
|
errdefer out.deinit();
|
||||||
|
try out.append(nsTag);
|
||||||
|
try appendSegment(&out, "L");
|
||||||
|
try appendSegment(&out, name);
|
||||||
|
try out.append('M');
|
||||||
|
return out.toOwnedSlice();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn elementPrefix(allocator: Allocator, name: []const u8) ![]u8 {
|
||||||
|
var out = std.ArrayList(u8).init(allocator);
|
||||||
|
errdefer out.deinit();
|
||||||
|
try out.append(nsTag);
|
||||||
|
try appendSegment(&out, "L");
|
||||||
|
try appendSegment(&out, name);
|
||||||
|
try appendSegment(&out, "E");
|
||||||
|
return out.toOwnedSlice();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn elementKey(allocator: Allocator, name: []const u8, seq: i64) ![]u8 {
|
||||||
|
var out = std.ArrayList(u8).init(allocator);
|
||||||
|
errdefer out.deinit();
|
||||||
|
try out.append(nsTag);
|
||||||
|
try appendSegment(&out, "L");
|
||||||
|
try appendSegment(&out, name);
|
||||||
|
try appendSegment(&out, "E");
|
||||||
|
try row.appendOrdered(&out, .{ .int = seq });
|
||||||
|
return out.toOwnedSlice();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn readMeta(txn: *Transaction, allocator: Allocator, k: []const u8) !ListMeta {
|
||||||
|
const raw = try txn.get(allocator, k) orelse return ListMeta{ .head = 0, .tail = 1 };
|
||||||
|
defer allocator.free(raw);
|
||||||
|
if (raw.len != 16) return error.Malformed;
|
||||||
|
return .{
|
||||||
|
.head = std.mem.readInt(i64, raw[0..8], .big),
|
||||||
|
.tail = std.mem.readInt(i64, raw[8..16], .big),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writeMeta(txn: *Transaction, k: []const u8, meta: ListMeta) !void {
|
||||||
|
var buf: [16]u8 = undefined;
|
||||||
|
std.mem.writeInt(i64, buf[0..8], meta.head, .big);
|
||||||
|
std.mem.writeInt(i64, buf[8..16], meta.tail, .big);
|
||||||
|
try txn.put(k, &buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pushLeft(db: *Db, allocator: Allocator, name: []const u8, value: []const u8) !void {
|
||||||
|
const mk = try metaKey(allocator, name);
|
||||||
|
defer allocator.free(mk);
|
||||||
|
|
||||||
|
var attempt: usize = 0;
|
||||||
|
while (attempt < maxConflictRetries) : (attempt += 1) {
|
||||||
|
var txn = Transaction.begin(db);
|
||||||
|
defer txn.deinit();
|
||||||
|
|
||||||
|
var meta = try readMeta(&txn, allocator, mk);
|
||||||
|
const seq = meta.head;
|
||||||
|
meta.head -= 1;
|
||||||
|
|
||||||
|
const ek = try elementKey(allocator, name, seq);
|
||||||
|
defer allocator.free(ek);
|
||||||
|
try txn.put(ek, value);
|
||||||
|
try writeMeta(&txn, mk, meta);
|
||||||
|
|
||||||
|
txn.commit() catch |err| {
|
||||||
|
if (err == error.Conflict) continue;
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return error.TooManyConflicts;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pushRight(db: *Db, allocator: Allocator, name: []const u8, value: []const u8) !void {
|
||||||
|
const mk = try metaKey(allocator, name);
|
||||||
|
defer allocator.free(mk);
|
||||||
|
|
||||||
|
var attempt: usize = 0;
|
||||||
|
while (attempt < maxConflictRetries) : (attempt += 1) {
|
||||||
|
var txn = Transaction.begin(db);
|
||||||
|
defer txn.deinit();
|
||||||
|
|
||||||
|
var meta = try readMeta(&txn, allocator, mk);
|
||||||
|
const seq = meta.tail;
|
||||||
|
meta.tail += 1;
|
||||||
|
|
||||||
|
const ek = try elementKey(allocator, name, seq);
|
||||||
|
defer allocator.free(ek);
|
||||||
|
try txn.put(ek, value);
|
||||||
|
try writeMeta(&txn, mk, meta);
|
||||||
|
|
||||||
|
txn.commit() catch |err| {
|
||||||
|
if (err == error.Conflict) continue;
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return error.TooManyConflicts;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user