]> gitweb.ps.run Git - ziglmdb/blob - src/lmdb.zig
update
[ziglmdb] / src / lmdb.zig
1 const std = @import("std");
2 const lmdb = @cImport(@cInclude("lmdb.h"));
3
4 pub usingnamespace lmdb;
5
6 pub fn Cursor(comptime K: type, comptime V: type) type {
7     return struct {
8         const Self = @This();
9
10         pub const Flags = enum(c_uint) {
11             First = lmdb.MDB_FIRST,
12             FirstDup = lmdb.MDB_FIRST_DUP,
13             GetBoth = lmdb.MDB_GET_BOTH,
14             GetBothRange = lmdb.MDB_GET_BOTH_RANGE,
15             GetCurrent = lmdb.MDB_GET_CURRENT,
16             GetMultiple = lmdb.MDB_GET_MULTIPLE,
17             Last = lmdb.MDB_LAST,
18             LastDup = lmdb.MDB_LAST_DUP,
19             Next = lmdb.MDB_NEXT,
20             NextDup = lmdb.MDB_NEXT_DUP,
21             NextMultiple = lmdb.MDB_NEXT_MULTIPLE,
22             NextNodup = lmdb.MDB_NEXT_NODUP,
23             Prev = lmdb.MDB_PREV,
24             PrevDup = lmdb.MDB_PREV_DUP,
25             PrevNodup = lmdb.MDB_PREV_NODUP,
26             Set = lmdb.MDB_SET,
27             SetKey = lmdb.MDB_SET_KEY,
28             SetRange = lmdb.MDB_SET_RANGE,
29         };
30
31         ptr: ?*lmdb.MDB_cursor = undefined,
32
33         pub fn close(self: *Self) void {
34             _ = lmdb.mdb_cursor_close(self.ptr);
35         }
36
37         pub fn put(self: *Self, k: K, v: V) void {
38             var key = lmdb.MDB_val{
39                 .mv_size = @sizeOf(@TypeOf(k)),
40                 .mv_data = @constCast(@ptrCast(&k)),
41             };
42             var val = lmdb.MDB_val{
43                 .mv_size = @sizeOf(@TypeOf(v)),
44                 .mv_data = @constCast(@ptrCast(&v)),
45             };
46             switch (lmdb.mdb_cursor_put(self.ptr, &key, &val, 0)) {
47                 0 => {},
48                 else => |err| {
49                     std.debug.print("put err: {}\n", .{err});
50                 },
51             }
52         }
53
54         pub fn get(self: *Self, k: *K, flags: Flags) ?V {
55             var key = lmdb.MDB_val{
56                 .mv_size = @sizeOf(K),
57                 .mv_data = @constCast(@ptrCast(k)),
58             };
59             var val: lmdb.MDB_val = undefined;
60             return switch (lmdb.mdb_cursor_get(self.ptr, &key, &val, @intFromEnum(flags))) {
61                 0 => {
62                     k.* = @as(*align(1) K, @ptrCast(key.mv_data)).*;
63                     return @as(?*align(1) V, @ptrCast(val.mv_data)).?.*;
64                 },
65                 lmdb.MDB_NOTFOUND => null,
66                 else => |err| {
67                     std.debug.print("get err: {}\n", .{err});
68                     return null;
69                 },
70             };
71         }
72
73         pub fn del(self: *Self, k: K) void {
74             var key = lmdb.MDB_val{
75                 .mv_size = @sizeOf(@TypeOf(k)),
76                 .mv_data = @constCast(@ptrCast(&k)),
77             };
78             switch (lmdb.mdb_cursor_del(self.ptr, &key, 0)) {
79                 0 => {},
80                 else => |err| {
81                     std.debug.print("del err: {}\n", .{err});
82                 },
83             }
84         }
85
86         pub fn has(self: *Dbi, k: K, flags: Flags) bool {
87             return self.get(k, flags) != null;
88         }
89     };
90 }
91
92 pub fn Dbi(comptime K: type, comptime V: type) type {
93     return struct {
94         const Self = @This();
95
96         ptr: lmdb.MDB_dbi = undefined,
97         txn: *const Txn = undefined,
98         env: *const Env = undefined,
99
100         pub fn close(self: Self) void {
101
102             // TODO: necessary?
103             lmdb.mdb_dbi_close(self.env.ptr, self.ptr);
104         }
105
106         pub fn cursor(self: Self) !Cursor(K, V) {
107             var result = Cursor(K, V){};
108
109             return switch (lmdb.mdb_cursor_open(self.txn.ptr, self.ptr, &result.ptr)) {
110                 0 => result,
111                 else => error.CursorOpen,
112             };
113         }
114
115         pub fn put(self: Self, k: K, v: V) void {
116             var key = lmdb.MDB_val{
117                 .mv_size = @sizeOf(@TypeOf(k)),
118                 .mv_data = @constCast(@ptrCast(&k)),
119             };
120             var val = lmdb.MDB_val{
121                 .mv_size = @sizeOf(@TypeOf(v)),
122                 .mv_data = @constCast(@ptrCast(&v)),
123             };
124             switch (lmdb.mdb_put(self.txn.ptr, self.ptr, &key, &val, 0)) {
125                 0 => {},
126                 else => |err| {
127                     std.debug.print("put err: {}\n", .{err});
128                 },
129             }
130         }
131
132         pub fn get(self: Self, k: K) ?V {
133             var key = lmdb.MDB_val{
134                 .mv_size = @sizeOf(@TypeOf(k)),
135                 .mv_data = @constCast(@ptrCast(&k)),
136             };
137             var val: lmdb.MDB_val = undefined;
138             return switch (lmdb.mdb_get(self.txn.ptr, self.ptr, &key, &val)) {
139                 0 => @as(?*align(1) V, @ptrCast(val.mv_data)).?.*,
140                 else => |err| {
141                     std.debug.print("get err: {}\n", .{err});
142                     return null;
143                 },
144             };
145         }
146
147         pub fn del(self: Self, k: K) void {
148             var key = lmdb.MDB_val{
149                 .mv_size = @sizeOf(@TypeOf(k)),
150                 .mv_data = @constCast(@ptrCast(&k)),
151             };
152             switch (lmdb.mdb_del(self.txn.ptr, self.ptr, &key, null)) {
153                 0 => {},
154                 else => |err| {
155                     std.debug.print("del err: {}\n", .{err});
156                 },
157             }
158         }
159
160         pub fn has(self: Self, k: K) bool {
161             return self.get(k) != null;
162         }
163     };
164 }
165
166 pub const Txn = struct {
167     ptr: ?*lmdb.MDB_txn = undefined,
168     env: *const Env = undefined,
169
170     pub fn dbi(self: *const Txn, name: [*c]const u8, comptime K: type, comptime V: type) !Dbi(K, V) {
171         var result = Dbi(K, V){ .env = self.env, .txn = self };
172         // TODO: lmdb.MDB_INTEGERKEY?
173         switch (lmdb.mdb_dbi_open(self.ptr, name, lmdb.MDB_CREATE, &result.ptr)) {
174             0 => return result,
175             else => |err| {
176                 std.debug.print("dbi err: {}\n", .{err});
177                 return error.DbiOpen;
178             },
179         }
180     }
181
182     pub fn commit(self: Txn) void {
183         switch (lmdb.mdb_txn_commit(self.ptr)) {
184             0 => {},
185             lmdb.MDB_MAP_FULL => {
186                 std.debug.print("resize\n", .{});
187                 _ = lmdb.mdb_env_set_mapsize(self.env.ptr, 0);
188             },
189             else => |err| {
190                 std.debug.print("commit err: {}\n", .{err});
191             },
192         }
193     }
194
195     pub fn abort(self: Txn) void {
196         lmdb.mdb_txn_abort(self.ptr);
197     }
198 };
199
200 pub const Env = struct {
201     ptr: ?*lmdb.MDB_env = undefined,
202
203     pub fn open(name: [*c]const u8, size: lmdb.mdb_size_t) Env {
204         var result = Env{};
205
206         _ = lmdb.mdb_env_create(&result.ptr);
207         _ = lmdb.mdb_env_set_maxdbs(result.ptr, 10);
208         _ = lmdb.mdb_env_set_mapsize(result.ptr, size);
209         _ = lmdb.mdb_env_open(result.ptr, name, lmdb.MDB_WRITEMAP, 0o664);
210         // _ = lmdb.mdb_env_open(result.ptr, name, lmdb.MDB_NOSYNC | lmdb.MDB_WRITEMAP, 0o664);
211
212         return result;
213     }
214
215     pub fn close(self: Env) void {
216         lmdb.mdb_env_close(self.ptr);
217     }
218
219     pub fn txn(self: *const Env) !Txn {
220         var result = Txn{ .env = self };
221         switch (lmdb.mdb_txn_begin(self.ptr, null, 0, &result.ptr)) {
222             0 => return result,
223             else => |err| {
224                 std.debug.print("txn err: {}\n", .{err});
225                 return error.TxnOpen;
226             },
227         }
228     }
229
230     pub fn sync(self: *Env) void {
231         switch (lmdb.mdb_env_sync(self.ptr, 1)) {
232             0 => {},
233             else => |err| {
234                 std.debug.print("sync err: {}\n", .{err});
235             },
236         }
237     }
238 };
239
240 test "basic" {
241     var env = Env.open("db", 1024 * 1024 * 1);
242
243     var txn = try env.txn();
244
245     const Value = struct {
246         i: i64 = 123,
247         s: [16]u8 = undefined,
248     };
249
250     var dbi = try txn.dbi("abc", u64, Value);
251
252     var val = dbi.get(1) orelse Value{ .i = 5 };
253     std.debug.print("{}\n", .{val});
254
255     val.i += 1;
256
257     dbi.put(1, val);
258
259     txn.commit();
260     dbi.close();
261     env.sync();
262     env.close();
263 }
264
265 test "cursor" {
266     var env = Env.open("db", 1024 * 1024 * 1);
267
268     var txn = try env.txn();
269
270     const Value = struct {
271         i: i64 = 123,
272         s: [16]u8 = undefined,
273     };
274
275     var dbi = try txn.dbi("def", u64, Value);
276
277     for (0..10) |i| {
278         dbi.put(i, Value{ .i = @intCast(i + 23) });
279     }
280
281     var cursor = try dbi.cursor();
282
283     var key: u64 = undefined;
284     {
285         const val = cursor.get(&key, .First);
286         std.debug.print("{}: {?}\n", .{ key, val });
287     }
288
289     while (cursor.get(&key, .Next)) |val| {
290         std.debug.print("{}: {?}\n", .{ key, val });
291     }
292
293     cursor.close();
294
295     txn.commit();
296     dbi.close();
297     env.sync();
298     env.close();
299 }
300
301 // pub fn get(txn: ?*lmdb.MDB_txn, dbi: lmdb.MDB_dbi, k: anytype, comptime T: type) ?T {
302 //     var key = lmdb.MDB_val{
303 //         .mv_size = @sizeOf(@TypeOf(k)),
304 //         .mv_data = @constCast(@ptrCast(&k)),
305 //     };
306 //     var val: lmdb.MDB_val = undefined;
307 //     return switch (lmdb.mdb_get(txn, dbi, &key, &val)) {
308 //         0 => @as(?*align(1) T, @alignCast(@ptrCast(val.mv_data))).?.*,
309 //         else => |err| {
310 //             std.debug.print("get err: {}\n", .{err});
311 //             return null;
312 //         },
313 //     };
314 // }
315
316 // pub fn put(txn: ?*lmdb.MDB_txn, dbi: lmdb.MDB_dbi, k: anytype, v: anytype) void {
317 //     var key = lmdb.MDB_val{
318 //         .mv_size = @sizeOf(@TypeOf(k)),
319 //         .mv_data = @constCast(@ptrCast(&k)),
320 //     };
321 //     var val = lmdb.MDB_val{
322 //         .mv_size = @sizeOf(@TypeOf(v)),
323 //         .mv_data = @constCast(@ptrCast(&v)),
324 //     };
325 //     switch (lmdb.mdb_put(txn, dbi, &key, &val, 0)) {
326 //         0 => {},
327 //         else => |err| {
328 //             std.debug.print("put err: {}\n", .{err});
329 //         },
330 //     }
331 // }
332
333 // pub fn del(txn: ?*lmdb.MDB_txn, dbi: lmdb.MDB_dbi, k: anytype) void {
334 //     var key = lmdb.MDB_val{
335 //         .mv_size = @sizeOf(@TypeOf(k)),
336 //         .mv_data = @constCast(@ptrCast(&k)),
337 //     };
338 //     switch (lmdb.mdb_del(txn, dbi, &key, null)) {
339 //         0 => {},
340 //         else => |err| {
341 //             std.debug.print("del err: {}\n", .{err});
342 //         },
343 //     }
344 // }
345
346 // pub fn has(txn: ?*lmdb.MDB_txn, dbi: lmdb.MDB_dbi, k: anytype) bool {
347 //     var key = lmdb.MDB_val{
348 //         .mv_size = @sizeOf(@TypeOf(k)),
349 //         .mv_data = @constCast(@ptrCast(&k)),
350 //     };
351 //     var val: lmdb.MDB_val = undefined;
352 //     return switch (lmdb.mdb_get(txn, dbi, &key, &val)) {
353 //         0 => true,
354 //         lmdb.MDB_NOTFOUND => false,
355 //         else => |err| {
356 //             std.debug.print("has err: {}\n", .{err});
357 //             return false;
358 //         },
359 //     };
360 // }