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