X-Git-Url: https://gitweb.ps.run/ziglmdb/blobdiff_plain/87c8242a16663bebfa1e2314f372ce4b4bccceaa..b093b3271004173309ae5f7c67f1cdafc2cd4b82:/src/lmdb.zig?ds=sidebyside diff --git a/src/lmdb.zig b/src/lmdb.zig new file mode 100644 index 0000000..8cc91f4 --- /dev/null +++ b/src/lmdb.zig @@ -0,0 +1,360 @@ +const std = @import("std"); +const lmdb = @cImport(@cInclude("lmdb.h")); + +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, + }; + + ptr: ?*lmdb.MDB_cursor = undefined, + + 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 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.* = @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 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}); + }, + } + } + + pub fn has(self: *Dbi, k: K, flags: Flags) bool { + return self.get(k, flags) != null; + } + }; +} + +pub fn Dbi(comptime K: type, comptime V: type) type { + return struct { + const Self = @This(); + + ptr: lmdb.MDB_dbi = undefined, + txn: *const Txn = undefined, + env: *const Env = undefined, + + pub fn close(self: Self) void { + + // TODO: necessary? + lmdb.mdb_dbi_close(self.env.ptr, self.ptr); + } + + pub fn cursor(self: Self) !Cursor(K, V) { + var result = Cursor(K, V){}; + + return switch (lmdb.mdb_cursor_open(self.txn.ptr, self.ptr, &result.ptr)) { + 0 => result, + else => error.CursorOpen, + }; + } + + 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}); + }, + } + } + + 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 => @as(?*align(1) V, @ptrCast(val.mv_data)).?.*, + else => |err| { + std.debug.print("get err: {}\n", .{err}); + return null; + }, + }; + } + + 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 has(self: Self, k: K) bool { + return self.get(k) != null; + } + }; +} + +pub const Txn = struct { + ptr: ?*lmdb.MDB_txn = undefined, + env: *const 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 }; + // TODO: lmdb.MDB_INTEGERKEY? + switch (lmdb.mdb_dbi_open(self.ptr, name, lmdb.MDB_CREATE, &result.ptr)) { + 0 => return result, + else => |err| { + std.debug.print("dbi err: {}\n", .{err}); + return error.DbiOpen; + }, + } + } + + 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); + }, + else => |err| { + std.debug.print("commit err: {}\n", .{err}); + }, + } + } + + pub fn abort(self: Txn) void { + lmdb.mdb_txn_abort(self.ptr); + } +}; + +pub const Env = struct { + ptr: ?*lmdb.MDB_env = undefined, + + pub fn open(name: [*c]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); + // _ = lmdb.mdb_env_open(result.ptr, name, lmdb.MDB_NOSYNC | lmdb.MDB_WRITEMAP, 0o664); + + return result; + } + + pub fn close(self: Env) void { + lmdb.mdb_env_close(self.ptr); + } + + pub fn txn(self: *const 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}); + return error.TxnOpen; + }, + } + } + + pub fn sync(self: *Env) void { + switch (lmdb.mdb_env_sync(self.ptr, 1)) { + 0 => {}, + else => |err| { + std.debug.print("sync err: {}\n", .{err}); + }, + } + } +}; + +test "basic" { + var env = Env.open("db", 1024 * 1024 * 1); + + var txn = try env.txn(); + + const Value = struct { + i: i64 = 123, + s: [16]u8 = undefined, + }; + + var dbi = try txn.dbi("abc", u64, Value); + + var val = dbi.get(1) orelse Value{ .i = 5 }; + std.debug.print("{}\n", .{val}); + + val.i += 1; + + dbi.put(1, val); + + txn.commit(); + dbi.close(); + env.sync(); + env.close(); +} + +test "cursor" { + var env = Env.open("db", 1024 * 1024 * 1); + + var txn = try env.txn(); + + const Value = struct { + i: i64 = 123, + s: [16]u8 = undefined, + }; + + var dbi = try txn.dbi("def", u64, Value); + + for (0..10) |i| { + dbi.put(i, Value{ .i = @intCast(i + 23) }); + } + + var cursor = try dbi.cursor(); + + var key: u64 = undefined; + { + const val = cursor.get(&key, .First); + std.debug.print("{}: {?}\n", .{ key, val }); + } + + while (cursor.get(&key, .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 { +// 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(txn, dbi, &key, &val)) { +// 0 => @as(?*align(1) T, @alignCast(@ptrCast(val.mv_data))).?.*, +// else => |err| { +// std.debug.print("get err: {}\n", .{err}); +// return null; +// }, +// }; +// } + +// pub fn put(txn: ?*lmdb.MDB_txn, dbi: lmdb.MDB_dbi, 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(txn, dbi, &key, &val, 0)) { +// 0 => {}, +// else => |err| { +// std.debug.print("put err: {}\n", .{err}); +// }, +// } +// } + +// pub fn del(txn: ?*lmdb.MDB_txn, dbi: lmdb.MDB_dbi, k: anytype) void { +// var key = lmdb.MDB_val{ +// .mv_size = @sizeOf(@TypeOf(k)), +// .mv_data = @constCast(@ptrCast(&k)), +// }; +// switch (lmdb.mdb_del(txn, dbi, &key, null)) { +// 0 => {}, +// else => |err| { +// std.debug.print("del err: {}\n", .{err}); +// }, +// } +// } + +// pub fn has(txn: ?*lmdb.MDB_txn, dbi: lmdb.MDB_dbi, 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(txn, dbi, &key, &val)) { +// 0 => true, +// lmdb.MDB_NOTFOUND => false, +// else => |err| { +// std.debug.print("has err: {}\n", .{err}); +// return false; +// }, +// }; +// }