pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
-
const optimize = b.standardOptimizeOption(.{});
- const mod = b.addModule("lmdb", .{
- .root_source_file = .{ .src_path = .{ .owner = b, .sub_path = "src/lmdb.zig" } },
- // .root_source_file = .{ .cwd_relative = "src/lmdb.zig" },
+ const lmdb = b.addModule("lmdb", .{
+ .root_source_file = b.path("src/lmdb.zig"),
.target = target,
.optimize = optimize,
});
-
- mod.addIncludePath(.{ .src_path = .{ .owner = b, .sub_path = "lmdb/libraries/liblmdb" } });
-
- mod.addCSourceFiles(.{ .files = &.{
+ lmdb.addIncludePath(b.path("lmdb/libraries/liblmdb"));
+ lmdb.addCSourceFiles(.{ .files = &.{
"./lmdb/libraries/liblmdb/midl.c",
"./lmdb/libraries/liblmdb/mdb.c",
} });
+ lmdb.link_libc = true;
+
+ const db = b.addModule("db", .{
+ .root_source_file = b.path("src/db.zig"),
+ });
+ db.addImport("lmdb", lmdb);
- const unit_tests = b.addTest(.{
- .root_source_file = .{ .cwd_relative = "src/lmdb.zig" },
+ const lmdb_tests = b.addTest(.{
+ .root_source_file = b.path("src/lmdb.zig"),
.target = target,
.optimize = optimize,
});
-
- unit_tests.addIncludePath(.{ .src_path = .{ .owner = b, .sub_path = "lmdb/libraries/liblmdb" } });
- unit_tests.addCSourceFiles(.{ .files = &.{
+ lmdb_tests.addIncludePath(b.path("lmdb/libraries/liblmdb"));
+ lmdb_tests.addCSourceFiles(.{ .files = &.{
"./lmdb/libraries/liblmdb/midl.c",
"./lmdb/libraries/liblmdb/mdb.c",
} });
- unit_tests.linkLibC();
+ lmdb_tests.linkLibC();
+
+ const db_tests = b.addTest(.{
+ .root_source_file = b.path("src/db.zig"),
+ .target = target,
+ .optimize = optimize,
+ });
+ db_tests.root_module.addImport("lmdb", lmdb);
- const test_bin = b.addInstallBinFile(unit_tests.getEmittedBin(), "./lmdb_test");
+ const lmdb_test_bin = b.addInstallBinFile(lmdb_tests.getEmittedBin(), "./lmdb_test");
+ const db_test_bin = b.addInstallBinFile(db_tests.getEmittedBin(), "./db_test");
- const run_unit_tests = b.addRunArtifact(unit_tests);
+ const run_lmdb_tests = b.addRunArtifact(lmdb_tests);
+ const run_db_tests = b.addRunArtifact(db_tests);
const test_step = b.step("test", "Run unit tests");
- test_step.dependOn(&run_unit_tests.step);
- test_step.dependOn(&unit_tests.step);
- test_step.dependOn(&test_bin.step);
+ test_step.result_cached = false;
+ test_step.dependOn(&run_lmdb_tests.step);
+ test_step.dependOn(&lmdb_tests.step);
+ test_step.dependOn(&run_db_tests.step);
+ test_step.dependOn(&db_tests.step);
+ test_step.dependOn(&lmdb_test_bin.step);
+ test_step.dependOn(&db_test_bin.step);
}
.version = "0.0.0",
.paths = .{
"src/lmdb.zig",
+ "src/db.zig",
"build.zig",
"build.zig.zon",
},
--- /dev/null
+const std = @import("std");
+const lmdb = @import("lmdb");
+
+const PRNG_SEED = 0;
+
+pub fn Db(comptime K: type, comptime V: type) type {
+ return struct {
+ const Self = @This();
+
+ dbi: lmdb.Dbi,
+
+ pub fn init(txn: lmdb.Txn, name: [:0]const u8) !Self {
+ return .{
+ .dbi = try txn.dbi(name),
+ };
+ }
+ pub fn put(self: Self, k: K, v: V) !void {
+ try self.dbi.put(k, v);
+ }
+ pub fn get(self: Self, k: K) !V {
+ return try self.dbi.get(k, V);
+ }
+ pub fn del(self: Self, k: K) !void {
+ try self.dbi.del(k);
+ }
+ pub fn has(self: Self, k: K) !bool {
+ return try self.dbi.has(k);
+ }
+ pub const Iterator = struct {
+ cursor: lmdb.Cursor,
+ k: ?K,
+ v: ?V,
+
+ pub fn next(self: *Iterator) ?struct { key: K, val: V } {
+ if (self.k != null and self.v != null) {
+ const result = .{ .key = self.k.?, .val = self.v.? };
+
+ var k = self.k.?;
+ self.v = self.cursor.get(&k, V, .Next) catch return null;
+ if (self.v != null) {
+ self.k = k;
+ }
+ return result;
+ } else {
+ return null;
+ }
+ }
+ };
+ pub fn iterator(self: Self) !Iterator {
+ var cursor = try self.dbi.cursor();
+
+ var k: K = undefined;
+ const v = try cursor.get(&k, V, .First);
+ return .{ .cursor = cursor, .k = k, .v = v };
+ }
+ };
+}
+
+pub const Prng = struct {
+ var prng = std.Random.DefaultPrng.init(PRNG_SEED);
+
+ pub fn gen(dbi: lmdb.Dbi, comptime T: type) !T {
+ var buf: [@sizeOf(T)]u8 = undefined;
+ // TODO: limit loop
+ while (true) {
+ prng.fill(&buf);
+ const t = std.mem.bytesToValue(T, &buf);
+ if (!try dbi.has(t)) {
+ return t;
+ }
+ }
+ }
+};
+pub fn Set(comptime K: type) type {
+ return struct {
+ idx: ?Index = null,
+
+ const Self = @This();
+ pub const Index = u64;
+ pub const View = SetView(K);
+
+ fn open_dbi(txn: lmdb.Txn) !lmdb.Dbi {
+ return try txn.dbi("SetList");
+ }
+ pub fn init(txn: lmdb.Txn) !Self {
+ const head = View.Head{};
+ const dbi = try open_dbi(txn);
+ const idx = try Prng.gen(dbi, Index);
+ try dbi.put(idx, head);
+ return .{ .idx = idx };
+ }
+ pub fn open(self: Self, txn: lmdb.Txn) !View {
+ // create new head
+ if (self.idx == null) {
+ return error.NotInitialized;
+ }
+ // get head from dbi
+ const dbi = try open_dbi(txn);
+ const head = try dbi.get(self.idx.?, View.Head);
+ return .{
+ .dbi = dbi,
+ .idx = self.idx.?,
+ .head = head,
+ };
+ }
+ };
+}
+
+pub fn SetView(comptime K: type) type {
+ return struct {
+ const Self = @This();
+ const ItemIndex = struct { Set(K).Index, K };
+
+ pub const Head = struct {
+ len: usize = 0,
+ first: ?K = null,
+ last: ?K = null,
+ };
+ pub const Item = struct {
+ next: ?K = null,
+ prev: ?K = null,
+ };
+
+ dbi: lmdb.Dbi,
+ idx: Set(K).Index,
+ head: Head,
+
+ fn item_idx(self: Self, k: K) ItemIndex {
+ return .{ self.idx, k };
+ }
+ fn item_get(self: Self, k: K) !Item {
+ return try self.dbi.get(self.item_idx(k), Item);
+ }
+ fn item_put(self: Self, k: K, item: Item) !void {
+ try self.dbi.put(self.item_idx(k), item);
+ }
+ fn head_update(self: Self) !void {
+ try self.dbi.put(self.idx, self.head);
+ }
+ pub fn append(self: *Self, k: K) !void {
+ if (self.head.len == 0) {
+ const item = Item{};
+ try self.item_put(k, item);
+
+ self.head.len = 1;
+ self.head.first = k;
+ self.head.last = k;
+ try self.head_update();
+ } else {
+ const prev_idx = self.head.last.?;
+ var prev = try self.item_get(prev_idx);
+
+ const item = Item{ .prev = prev_idx };
+ try self.item_put(k, item);
+
+ prev.next = k;
+ try self.item_put(prev_idx, prev);
+
+ self.head.last = k;
+ self.head.len += 1;
+ try self.head_update();
+ }
+ }
+ pub fn del(self: *Self, k: K) !void {
+ const item = try self.item_get(k);
+
+ if (item.prev != null) {
+ var prev = try self.item_get(item.prev.?);
+ prev.next = item.next;
+ try self.item_put(item.prev.?, prev);
+ }
+
+ if (item.next != null) {
+ var next = try self.item_get(item.next.?);
+ next.prev = item.prev;
+ try self.item_put(item.next.?, next);
+ }
+
+ if (self.head.first == k) self.head.first = item.next;
+ if (self.head.last == k) self.head.last = item.prev;
+ self.head.len -= 1;
+ try self.head_update();
+
+ try self.dbi.del(self.item_idx(k));
+ }
+ pub fn has(self: Self, k: K) !bool {
+ return self.dbi.has(self.item_idx(k));
+ }
+ pub fn len(self: Self) usize {
+ return self.head.len;
+ }
+ pub const Iterator = struct {
+ sv: SetView(K),
+ idx: ?K,
+ dir: enum { Forward, Backward },
+
+ pub fn next(self: *Iterator) ?K {
+ if (self.idx != null) {
+ const k = self.idx.?;
+ const item = self.sv.item_get(k) catch return null;
+ self.idx = switch (self.dir) {
+ .Forward => item.next,
+ .Backward => item.prev,
+ };
+ return k;
+ } else {
+ return null;
+ }
+ }
+ };
+ pub fn iterator(self: Self) Iterator {
+ return .{
+ .sv = self,
+ .idx = self.head.first,
+ .dir = .Forward,
+ };
+ }
+ pub fn reverse_iterator(self: Self) Iterator {
+ return .{
+ .sv = self,
+ .idx = self.head.last,
+ .dir = .Backward,
+ };
+ }
+ };
+}
+pub fn List(comptime V: type) type {
+ return struct {
+ idx: ?Index = null,
+
+ const Self = @This();
+ pub const Index = u64;
+ pub const View = ListView(V);
+
+ fn open_dbi(txn: lmdb.Txn) !lmdb.Dbi {
+ return try txn.dbi("SetList");
+ }
+ pub fn init(txn: lmdb.Txn) !Self {
+ const head = View.Head{};
+ const dbi = try open_dbi(txn);
+ const idx = try Prng.gen(dbi, Index);
+ try dbi.put(idx, head);
+ return .{ .idx = idx };
+ }
+ pub fn open(self: Self, txn: lmdb.Txn) !View {
+ // create new head
+ if (self.idx == null) {
+ return error.NotInitialized;
+ }
+ // get head from dbi
+ const dbi = try open_dbi(txn);
+ const head = try dbi.get(self.idx.?, View.Head);
+ return .{
+ .dbi = dbi,
+ .idx = self.idx.?,
+ .head = head,
+ };
+ }
+ };
+}
+
+pub fn ListView(comptime V: type) type {
+ return struct {
+ const Self = @This();
+ const K = u64;
+ const ItemIndex = struct { List(V).Index, K };
+
+ pub const Head = struct {
+ len: usize = 0,
+ first: ?K = null,
+ last: ?K = null,
+ };
+ pub const Item = struct {
+ next: ?K = null,
+ prev: ?K = null,
+ data: V,
+ };
+
+ dbi: lmdb.Dbi,
+ idx: List(V).Index,
+ head: Head,
+
+ fn item_idx(self: Self, k: K) ItemIndex {
+ return .{ self.idx, k };
+ }
+ fn item_get(self: Self, k: K) !Item {
+ return try self.dbi.get(self.item_idx(k), Item);
+ }
+ fn item_put(self: Self, k: K, item: Item) !void {
+ try self.dbi.put(self.item_idx(k), item);
+ }
+ fn head_update(self: Self) !void {
+ try self.dbi.put(self.idx, self.head);
+ }
+ fn gen(self: Self) !K {
+ // TODO: limit loop
+ while (true) {
+ const k = try Prng.gen(self.dbi, K);
+ if (!try self.dbi.has(self.item_idx(k))) {
+ return k;
+ }
+ }
+ }
+ pub fn append(self: *Self, v: V) !K {
+ if (self.head.len == 0) {
+ const k = try self.gen();
+ const item = Item{ .data = v };
+ try self.item_put(k, item);
+
+ self.head.len = 1;
+ self.head.first = k;
+ self.head.last = k;
+ try self.head_update();
+
+ return k;
+ } else {
+ const prev_idx = self.head.last.?;
+ var prev = try self.item_get(prev_idx);
+
+ const k = try self.gen();
+ const item = Item{ .prev = prev_idx, .data = v };
+ try self.item_put(k, item);
+
+ prev.next = k;
+ try self.item_put(prev_idx, prev);
+
+ self.head.last = k;
+ self.head.len += 1;
+ try self.head_update();
+
+ return k;
+ }
+ }
+ pub fn get(self: Self, k: K) !V {
+ const item = try self.item_get(k);
+ return item.data;
+ }
+ pub fn del(self: *Self, k: K) !void {
+ const item = try self.item_get(k);
+
+ if (item.prev != null) {
+ var prev = try self.item_get(item.prev.?);
+ prev.next = item.next;
+ try self.item_put(item.prev.?, prev);
+ }
+
+ if (item.next != null) {
+ var next = try self.item_get(item.next.?);
+ next.prev = item.prev;
+ try self.item_put(item.next.?, next);
+ }
+
+ if (self.head.first == k) self.head.first = item.next;
+ if (self.head.last == k) self.head.last = item.prev;
+ self.head.len -= 1;
+ try self.head_update();
+
+ try self.dbi.del(self.item_idx(k));
+ }
+ pub fn len(self: Self) usize {
+ return self.head.len;
+ }
+ pub const Iterator = struct {
+ lv: ListView(V),
+ idx: ?K,
+ dir: enum { Forward, Backward },
+
+ pub fn next(self: *Iterator) ?struct { key: K, val: V } {
+ if (self.idx != null) {
+ const k = self.idx.?;
+ const item = self.lv.item_get(k) catch return null;
+ self.idx = switch (self.dir) {
+ .Forward => item.next,
+ .Backward => item.prev,
+ };
+ return .{ .key = k, .val = item.data };
+ } else {
+ return null;
+ }
+ }
+ };
+ pub fn iterator(self: Self) Iterator {
+ return .{
+ .lv = self,
+ .idx = self.head.first,
+ .dir = .Forward,
+ };
+ }
+ pub fn reverse_iterator(self: Self) Iterator {
+ return .{
+ .lv = self,
+ .idx = self.head.last,
+ .dir = .Backward,
+ };
+ }
+ };
+}
+
+pub fn SetList(comptime K: type, comptime V: type) type {
+ return struct {
+ idx: ?Index = null,
+
+ const Self = @This();
+ pub const Index = u64;
+ pub const View = SetListView(K, V);
+
+ fn open_dbi(txn: lmdb.Txn) !lmdb.Dbi {
+ return try txn.dbi("SetList");
+ }
+ pub fn init(txn: lmdb.Txn) !Self {
+ const head = View.Head{};
+ const dbi = try open_dbi(txn);
+ const idx = try Prng.gen(dbi, Index);
+ try dbi.put(idx, head);
+ return .{ .idx = idx };
+ }
+ pub fn open(self: Self, txn: lmdb.Txn) !View {
+ // create new head
+ if (self.idx == null) {
+ return error.NotInitialized;
+ }
+ // get head from dbi
+ const dbi = try open_dbi(txn);
+ const head = try dbi.get(self.idx.?, View.Head);
+ return .{
+ .dbi = dbi,
+ .idx = self.idx.?,
+ .head = head,
+ };
+ }
+ };
+}
+
+pub fn SetListView(comptime K: type, comptime V: type) type {
+ return struct {
+ const Self = @This();
+ const ItemIndex = struct { SetList(K, V).Index, K };
+
+ pub const Head = struct {
+ len: usize = 0,
+ first: ?K = null,
+ last: ?K = null,
+ };
+ pub const Item = struct {
+ next: ?K = null,
+ prev: ?K = null,
+ data: V,
+ };
+
+ dbi: lmdb.Dbi,
+ idx: SetList(K, V).Index,
+ head: Head,
+
+ fn item_idx(self: Self, k: K) ItemIndex {
+ return .{ self.idx, k };
+ }
+ fn item_get(self: Self, k: K) !Item {
+ return try self.dbi.get(self.item_idx(k), Item);
+ }
+ fn item_put(self: Self, k: K, item: Item) !void {
+ try self.dbi.put(self.item_idx(k), item);
+ }
+ fn head_update(self: Self) !void {
+ try self.dbi.put(self.idx, self.head);
+ }
+ pub fn append(self: *Self, k: K, v: V) !void {
+ if (self.head.len == 0) {
+ const item = Item{ .data = v };
+ try self.item_put(k, item);
+
+ self.head.len = 1;
+ self.head.first = k;
+ self.head.last = k;
+ try self.head_update();
+ } else {
+ const prev_idx = self.head.last.?;
+ var prev = try self.item_get(prev_idx);
+
+ const item = Item{ .prev = prev_idx, .data = v };
+ try self.item_put(k, item);
+
+ prev.next = k;
+ try self.item_put(prev_idx, prev);
+
+ self.head.last = k;
+ self.head.len += 1;
+ try self.head_update();
+ }
+ }
+ pub fn get(self: Self, k: K) !V {
+ const item = try self.item_get(k);
+ return item.data;
+ }
+ pub fn del(self: *Self, k: K) !void {
+ const item = try self.item_get(k);
+
+ if (item.prev != null) {
+ var prev = try self.item_get(item.prev.?);
+ prev.next = item.next;
+ try self.item_put(item.prev.?, prev);
+ }
+
+ if (item.next != null) {
+ var next = try self.item_get(item.next.?);
+ next.prev = item.prev;
+ try self.item_put(item.next.?, next);
+ }
+
+ if (self.head.first == k) self.head.first = item.next;
+ if (self.head.last == k) self.head.last = item.prev;
+ self.head.len -= 1;
+ try self.head_update();
+
+ try self.dbi.del(self.item_idx(k));
+ }
+ pub fn has(self: Self, k: K) !bool {
+ return self.dbi.has(self.item_idx(k));
+ }
+ pub fn len(self: Self) usize {
+ return self.head.len;
+ }
+ pub const Iterator = struct {
+ slv: SetListView(K, V),
+ idx: ?K,
+ dir: enum { Forward, Backward },
+
+ pub fn next(self: *Iterator) ?struct { key: K, val: V } {
+ if (self.idx != null) {
+ const k = self.idx.?;
+ const item = self.slv.item_get(k) catch return null;
+ self.idx = switch (self.dir) {
+ .Forward => item.next,
+ .Backward => item.prev,
+ };
+ return .{ .key = k, .val = item.data };
+ } else {
+ return null;
+ }
+ }
+ };
+ pub fn iterator(self: Self) Iterator {
+ return .{
+ .slv = self,
+ .idx = self.head.first,
+ .dir = .Forward,
+ };
+ }
+ pub fn reverse_iterator(self: Self) Iterator {
+ return .{
+ .slv = self,
+ .idx = self.head.last,
+ .dir = .Backward,
+ };
+ }
+ };
+}
+
+const DB_SIZE = 1024 * 1024 * 1;
+
+test "db" {
+ const env = try lmdb.Env.open("db", DB_SIZE);
+ defer env.close();
+
+ const txn = try env.txn();
+ defer txn.commit() catch {};
+
+ var db = try Db(u32, u32).init(txn, "123");
+ var n: u32 = 456;
+ if (try db.has(123)) {
+ n = try db.get(123);
+ n += 1;
+ }
+ try db.put(123, n);
+ std.debug.print("n: {}\n", .{n});
+}
+
+// test "list" {
+// const env = try lmdb.Env.open("db", DB_SIZE);
+// defer env.close();
+
+// const txn = try env.txn();
+// defer txn.commit();
+
+// const db = List.init(txn, "b", u32);
+// }
+
+test "maplist" {
+ var env = try lmdb.Env.open("db", 1024 * 1024 * 1);
+ // env.sync();
+ defer env.close();
+
+ var txn = try env.txn();
+ defer txn.commit() catch {};
+
+ var dbi = try txn.dbi("abc");
+
+ const A = struct {
+ ml: SetList(usize, usize),
+ };
+
+ var a: A = undefined;
+ const a_idx: u64 = 27;
+ if (try dbi.has(a_idx)) {
+ a = try dbi.get(a_idx, A);
+ } else {
+ a = A{ .ml = try SetList(usize, usize).init(txn) };
+ try dbi.put(a_idx, a);
+ }
+
+ var ml = try a.ml.open(txn);
+
+ const len = ml.len();
+ std.debug.print("{}\n", .{len});
+ try ml.append(len, len * 10);
+ std.debug.print("{}\n", .{try ml.get(len)});
+ var it = ml.iterator();
+ while (it.next()) |i| {
+ std.debug.print("{}: {}\n", .{ i.key, i.val });
+ }
+}
pub usingnamespace lmdb;
-pub fn Cursor(comptime K: type, comptime V: type) type {
- return struct {
- const Self = @This();
-
- pub const Flags = enum(c_uint) {
- First = lmdb.MDB_FIRST,
- FirstDup = lmdb.MDB_FIRST_DUP,
- GetBoth = lmdb.MDB_GET_BOTH,
- GetBothRange = lmdb.MDB_GET_BOTH_RANGE,
- GetCurrent = lmdb.MDB_GET_CURRENT,
- GetMultiple = lmdb.MDB_GET_MULTIPLE,
- Last = lmdb.MDB_LAST,
- LastDup = lmdb.MDB_LAST_DUP,
- Next = lmdb.MDB_NEXT,
- NextDup = lmdb.MDB_NEXT_DUP,
- NextMultiple = lmdb.MDB_NEXT_MULTIPLE,
- NextNodup = lmdb.MDB_NEXT_NODUP,
- Prev = lmdb.MDB_PREV,
- PrevDup = lmdb.MDB_PREV_DUP,
- PrevNodup = lmdb.MDB_PREV_NODUP,
- Set = lmdb.MDB_SET,
- SetKey = lmdb.MDB_SET_KEY,
- SetRange = lmdb.MDB_SET_RANGE,
- };
+pub const Cursor = struct {
+ const Self = @This();
+
+ pub const Flags = enum(c_uint) {
+ First = lmdb.MDB_FIRST,
+ FirstDup = lmdb.MDB_FIRST_DUP,
+ GetBoth = lmdb.MDB_GET_BOTH,
+ GetBothRange = lmdb.MDB_GET_BOTH_RANGE,
+ GetCurrent = lmdb.MDB_GET_CURRENT,
+ GetMultiple = lmdb.MDB_GET_MULTIPLE,
+ Last = lmdb.MDB_LAST,
+ LastDup = lmdb.MDB_LAST_DUP,
+ Next = lmdb.MDB_NEXT,
+ NextDup = lmdb.MDB_NEXT_DUP,
+ NextMultiple = lmdb.MDB_NEXT_MULTIPLE,
+ NextNodup = lmdb.MDB_NEXT_NODUP,
+ Prev = lmdb.MDB_PREV,
+ PrevDup = lmdb.MDB_PREV_DUP,
+ PrevNodup = lmdb.MDB_PREV_NODUP,
+ Set = lmdb.MDB_SET,
+ SetKey = lmdb.MDB_SET_KEY,
+ SetRange = lmdb.MDB_SET_RANGE,
+ };
- ptr: ?*lmdb.MDB_cursor = undefined,
+ ptr: ?*lmdb.MDB_cursor = undefined,
- pub fn close(self: *Self) void {
- _ = lmdb.mdb_cursor_close(self.ptr);
- }
+ pub fn close(self: Self) void {
+ lmdb.mdb_cursor_close(self.ptr);
+ }
- pub fn put(self: *Self, k: K, v: V) void {
- var key = lmdb.MDB_val{
- .mv_size = @sizeOf(@TypeOf(k)),
- .mv_data = @constCast(@ptrCast(&k)),
- };
- var val = lmdb.MDB_val{
- .mv_size = @sizeOf(@TypeOf(v)),
- .mv_data = @constCast(@ptrCast(&v)),
- };
- switch (lmdb.mdb_cursor_put(self.ptr, &key, &val, 0)) {
- 0 => {},
- else => |err| {
- std.debug.print("put err: {}\n", .{err});
- },
- }
+ pub fn put(self: Self, k: anytype, v: anytype) !void {
+ var key = lmdb.MDB_val{
+ .mv_size = @sizeOf(@TypeOf(k)),
+ .mv_data = @constCast(@ptrCast(&k)),
+ };
+ var val = lmdb.MDB_val{
+ .mv_size = @sizeOf(@TypeOf(v)),
+ .mv_data = @constCast(@ptrCast(&v)),
+ };
+ switch (lmdb.mdb_cursor_put(self.ptr, &key, &val, 0)) {
+ 0 => {},
+ else => |err| {
+ _ = err;
+ return error.CursorPut;
+ },
}
+ }
- pub fn get(self: *Self, k: *K, flags: Flags) ?V {
- var key = lmdb.MDB_val{
- .mv_size = @sizeOf(K),
- .mv_data = @constCast(@ptrCast(k)),
- };
- var val: lmdb.MDB_val = undefined;
- return switch (lmdb.mdb_cursor_get(self.ptr, &key, &val, @intFromEnum(flags))) {
- 0 => {
- k.* = std.mem.bytesToValue(K, key.mv_data.?);
- return std.mem.bytesToValue(V, val.mv_data.?);
- //k.* = @as(*align(1) K, @ptrCast(key.mv_data)).*;
- //return @as(?*align(1) V, @ptrCast(val.mv_data)).?.*;
- },
- lmdb.MDB_NOTFOUND => null,
- else => |err| {
- std.debug.print("get err: {}\n", .{err});
- return null;
- },
- };
- }
+ pub fn get(self: Self, k: anytype, comptime V: type, flags: Flags) !?V {
+ const k_ti = @typeInfo(@TypeOf(k));
+ const K = k_ti.Pointer.child;
- pub fn del(self: *Self, k: K) void {
- var key = lmdb.MDB_val{
- .mv_size = @sizeOf(@TypeOf(k)),
- .mv_data = @constCast(@ptrCast(&k)),
- };
- switch (lmdb.mdb_cursor_del(self.ptr, &key, 0)) {
- 0 => {},
- else => |err| {
- std.debug.print("del err: {}\n", .{err});
- },
- }
- }
+ var key = lmdb.MDB_val{
+ .mv_size = @sizeOf(K),
+ .mv_data = @constCast(@ptrCast(k)),
+ };
+ var val: lmdb.MDB_val = undefined;
+ return switch (lmdb.mdb_cursor_get(self.ptr, &key, &val, @intFromEnum(flags))) {
+ 0 => {
+ const ptr = @as(*K, @constCast(k));
+ ptr.* = std.mem.bytesToValue(K, key.mv_data.?);
+ return std.mem.bytesToValue(V, val.mv_data.?);
+ },
+ lmdb.MDB_NOTFOUND => null,
+ else => |err| {
+ _ = err;
+ return error.CursorGet;
+ },
+ };
+ }
- pub fn has(self: *Dbi, k: K, flags: Flags) bool {
- return self.get(k, flags) != null;
+ pub fn del(self: Self, k: anytype) !void {
+ var key = lmdb.MDB_val{
+ .mv_size = @sizeOf(@TypeOf(k)),
+ .mv_data = @constCast(@ptrCast(&k)),
+ };
+ switch (lmdb.mdb_cursor_del(self.ptr, &key, 0)) {
+ 0 => {},
+ else => |err| {
+ _ = err;
+ return error.CursorDel;
+ },
}
- };
-}
+ }
-pub fn Dbi(comptime K: type, comptime V: type) type {
- return struct {
- const Self = @This();
+ pub fn has(self: Self, k: anytype, flags: Flags) !bool {
+ const k_ti = @typeInfo(@TypeOf(k));
+ const K = k_ti.Pointer.child;
- ptr: lmdb.MDB_dbi = undefined,
- txn: *const Txn = undefined,
- env: *const Env = undefined,
+ var key = lmdb.MDB_val{
+ .mv_size = @sizeOf(K),
+ .mv_data = @constCast(@ptrCast(k)),
+ };
+ var val: lmdb.MDB_val = undefined;
+ return switch (lmdb.mdb_cursor_get(self.ptr, &key, &val, @intFromEnum(flags))) {
+ 0 => {
+ return true;
+ },
+ lmdb.MDB_NOTFOUND => {
+ return false;
+ },
+ else => {
+ return error.CursorHas;
+ },
+ };
+ }
+};
- pub fn close(self: Self) void {
+pub const Dbi = struct {
+ const Self = @This();
- // TODO: necessary?
- lmdb.mdb_dbi_close(self.env.ptr, self.ptr);
- }
+ ptr: lmdb.MDB_dbi = undefined,
+ txn: Txn = undefined,
+ env: Env = undefined,
- pub fn cursor(self: Self) !Cursor(K, V) {
- var result = Cursor(K, V){};
+ pub fn close(self: Self) void {
+ // TODO: necessary?
+ lmdb.mdb_dbi_close(self.env.ptr, self.ptr);
+ }
- return switch (lmdb.mdb_cursor_open(self.txn.ptr, self.ptr, &result.ptr)) {
- 0 => result,
- else => error.CursorOpen,
- };
- }
+ pub fn cursor(self: Self) !Cursor {
+ var result = Cursor{};
- pub fn put(self: Self, k: K, v: V) void {
- var key = lmdb.MDB_val{
- .mv_size = @sizeOf(@TypeOf(k)),
- .mv_data = @constCast(@ptrCast(&k)),
- };
- var val = lmdb.MDB_val{
- .mv_size = @sizeOf(@TypeOf(v)),
- .mv_data = @constCast(@ptrCast(&v)),
- };
- switch (lmdb.mdb_put(self.txn.ptr, self.ptr, &key, &val, 0)) {
- 0 => {},
- else => |err| {
- std.debug.print("put err: {}\n", .{err});
- },
- }
- }
+ return switch (lmdb.mdb_cursor_open(self.txn.ptr, self.ptr, &result.ptr)) {
+ 0 => result,
+ else => error.DbiCursor,
+ };
+ }
- pub fn get(self: Self, k: K) ?V {
- var key = lmdb.MDB_val{
- .mv_size = @sizeOf(@TypeOf(k)),
- .mv_data = @constCast(@ptrCast(&k)),
- };
- var val: lmdb.MDB_val = undefined;
- return switch (lmdb.mdb_get(self.txn.ptr, self.ptr, &key, &val)) {
- 0 => {
- return std.mem.bytesToValue(V, val.mv_data.?);
- //@as(?*align(1) V, @ptrCast(val.mv_data)).?.*
- },
- else => |err| {
- std.debug.print("get err: {}\n", .{err});
- return null;
- },
- };
+ pub fn put(self: Self, k: anytype, v: anytype) !void {
+ var key = lmdb.MDB_val{
+ .mv_size = @sizeOf(@TypeOf(k)),
+ .mv_data = @constCast(@ptrCast(&k)),
+ };
+ var val = lmdb.MDB_val{
+ .mv_size = @sizeOf(@TypeOf(v)),
+ .mv_data = @constCast(@ptrCast(&v)),
+ };
+ switch (lmdb.mdb_put(self.txn.ptr, self.ptr, &key, &val, 0)) {
+ 0 => {},
+ else => |err| {
+ _ = err;
+ return error.DbiPut;
+ },
}
+ }
- pub fn del(self: Self, k: K) void {
- var key = lmdb.MDB_val{
- .mv_size = @sizeOf(@TypeOf(k)),
- .mv_data = @constCast(@ptrCast(&k)),
- };
- switch (lmdb.mdb_del(self.txn.ptr, self.ptr, &key, null)) {
- 0 => {},
- else => |err| {
- std.debug.print("del err: {}\n", .{err});
- },
- }
- }
+ pub fn get(self: Self, k: anytype, comptime V: type) !V {
+ var key = lmdb.MDB_val{
+ .mv_size = @sizeOf(@TypeOf(k)),
+ .mv_data = @constCast(@ptrCast(&k)),
+ };
+ var val: lmdb.MDB_val = undefined;
+ return switch (lmdb.mdb_get(self.txn.ptr, self.ptr, &key, &val)) {
+ 0 => {
+ return std.mem.bytesToValue(V, val.mv_data.?);
+ },
+ lmdb.MDB_NOTFOUND => error.NotFound,
+ else => |err| {
+ _ = err;
+ return error.DbiGet;
+ },
+ };
+ }
- pub fn has(self: Self, k: K) bool {
- return self.get(k) != null;
+ pub fn del(self: Self, k: anytype) !void {
+ var key = lmdb.MDB_val{
+ .mv_size = @sizeOf(@TypeOf(k)),
+ .mv_data = @constCast(@ptrCast(&k)),
+ };
+ switch (lmdb.mdb_del(self.txn.ptr, self.ptr, &key, null)) {
+ 0 => {},
+ else => |err| {
+ _ = err;
+ return error.DbiDel;
+ },
}
- };
-}
+ }
+
+ pub fn has(self: Self, k: anytype) !bool {
+ var key = lmdb.MDB_val{
+ .mv_size = @sizeOf(@TypeOf(k)),
+ .mv_data = @constCast(@ptrCast(&k)),
+ };
+ var val: lmdb.MDB_val = undefined;
+ return switch (lmdb.mdb_get(self.txn.ptr, self.ptr, &key, &val)) {
+ 0 => {
+ return true;
+ },
+ lmdb.MDB_NOTFOUND => {
+ return false;
+ },
+ else => |err| {
+ std.debug.print("[{}]\n", .{err});
+ return error.DbiHas;
+ },
+ };
+ }
+};
pub const Txn = struct {
ptr: ?*lmdb.MDB_txn = undefined,
- env: *const Env = undefined,
+ env: Env = undefined,
- pub fn dbi(self: *const Txn, name: [*c]const u8, comptime K: type, comptime V: type) !Dbi(K, V) {
- var result = Dbi(K, V){ .env = self.env, .txn = self };
+ pub fn dbi(self: Txn, name: [:0]const u8) !Dbi {
+ var result = Dbi{ .env = self.env, .txn = self };
// TODO: lmdb.MDB_INTEGERKEY?
- switch (lmdb.mdb_dbi_open(self.ptr, name, lmdb.MDB_CREATE, &result.ptr)) {
+ switch (lmdb.mdb_dbi_open(self.ptr, @ptrCast(name), lmdb.MDB_CREATE, &result.ptr)) {
0 => return result,
else => |err| {
- std.debug.print("dbi err: {}\n", .{err});
+ _ = err;
return error.DbiOpen;
},
}
}
- pub fn commit(self: Txn) void {
+ pub fn commit(self: Txn) !void {
switch (lmdb.mdb_txn_commit(self.ptr)) {
0 => {},
lmdb.MDB_MAP_FULL => {
- std.debug.print("resize\n", .{});
_ = lmdb.mdb_env_set_mapsize(self.env.ptr, 0);
+ return error.TxnCommitMapFull;
},
else => |err| {
- std.debug.print("commit err: {}\n", .{err});
+ _ = err;
+ return error.TxnCommit;
},
}
}
pub const Env = struct {
ptr: ?*lmdb.MDB_env = undefined,
- pub fn open(name: [*c]const u8, size: lmdb.mdb_size_t) Env {
+ pub fn open(name: [:0]const u8, size: lmdb.mdb_size_t) !Env {
var result = Env{};
_ = lmdb.mdb_env_create(&result.ptr);
_ = lmdb.mdb_env_set_maxdbs(result.ptr, 10);
_ = lmdb.mdb_env_set_mapsize(result.ptr, size);
- _ = lmdb.mdb_env_open(result.ptr, name, lmdb.MDB_WRITEMAP, 0o664);
+ const res = lmdb.mdb_env_open(result.ptr, name, lmdb.MDB_WRITEMAP, 0o664);
// _ = lmdb.mdb_env_open(result.ptr, name, lmdb.MDB_NOSYNC | lmdb.MDB_WRITEMAP, 0o664);
- return result;
+ if (res != 0) {
+ return error.EnvOpen;
+ } else {
+ return result;
+ }
}
pub fn close(self: Env) void {
lmdb.mdb_env_close(self.ptr);
}
- pub fn txn(self: *const Env) !Txn {
+ pub fn txn(self: Env) !Txn {
var result = Txn{ .env = self };
switch (lmdb.mdb_txn_begin(self.ptr, null, 0, &result.ptr)) {
0 => return result,
else => |err| {
- std.debug.print("txn err: {}\n", .{err});
+ _ = err;
return error.TxnOpen;
},
}
}
- pub fn sync(self: *Env) void {
+ pub fn sync(self: Env) !void {
switch (lmdb.mdb_env_sync(self.ptr, 1)) {
0 => {},
else => |err| {
- std.debug.print("sync err: {}\n", .{err});
+ _ = err;
+ return error.EnvSync;
},
}
}
};
test "basic" {
- var env = Env.open("db", 1024 * 1024 * 1);
+ var env = try Env.open("db", 1024 * 1024 * 1);
+ // env.sync();
+ defer env.close();
var txn = try env.txn();
+ defer txn.commit() catch {};
const Value = struct {
i: i64 = 123,
s: [16]u8 = undefined,
};
- var dbi = try txn.dbi("abc", u64, Value);
+ var dbi = try txn.dbi("abc");
+
+ const idx: u64 = 1;
- var val = dbi.get(1) orelse Value{ .i = 5 };
+ std.debug.print("has?: {}\n", .{try dbi.has(idx)});
+
+ var val = dbi.get(idx, Value) catch Value{ .i = 5 };
std.debug.print("{}\n", .{val});
val.i += 1;
- dbi.put(1, val);
-
- txn.commit();
- dbi.close();
- env.sync();
- env.close();
+ try dbi.put(idx, val);
}
test "cursor" {
- var env = Env.open("db", 1024 * 1024 * 1);
+ var env = try Env.open("db", 1024 * 1024 * 1);
+ // env.sync();
+ defer env.close();
var txn = try env.txn();
+ defer txn.commit() catch {};
const Value = struct {
i: i64 = 123,
s: [16]u8 = undefined,
};
- var dbi = try txn.dbi("def", u64, Value);
+ var dbi = try txn.dbi("def");
for (0..10) |i| {
- dbi.put(i, Value{ .i = @intCast(i + 23) });
+ try dbi.put(@as(u64, i), Value{ .i = @intCast(i + 23) });
}
var cursor = try dbi.cursor();
+ defer cursor.close();
var key: u64 = undefined;
{
- const val = cursor.get(&key, .First);
+ const val = try cursor.get(&key, Value, .First);
std.debug.print("{}: {?}\n", .{ key, val });
}
- while (cursor.get(&key, .Next)) |val| {
+ while (try cursor.get(&key, Value, .Next)) |val| {
std.debug.print("{}: {?}\n", .{ key, val });
}
-
- cursor.close();
-
- txn.commit();
- dbi.close();
- env.sync();
- env.close();
}
// pub fn get(txn: ?*lmdb.MDB_txn, dbi: lmdb.MDB_dbi, k: anytype, comptime T: type) ?T {