]> gitweb.ps.run Git - ziglmdb/blob - src/db.zig
update result type to work with zig 0.14.0
[ziglmdb] / src / db.zig
1 const std = @import("std");
2 const lmdb = @import("lmdb");
3
4 const PRNG_SEED = 0;
5
6 pub fn Db(comptime K: type, comptime V: type) type {
7     return struct {
8         const Self = @This();
9
10         dbi: lmdb.Dbi,
11
12         pub fn init(txn: lmdb.Txn, name: [:0]const u8) !Self {
13             return .{
14                 .dbi = try txn.dbi(name),
15             };
16         }
17         pub fn put(self: Self, k: K, v: V) !void {
18             try self.dbi.put(k, v);
19         }
20         pub fn get(self: Self, k: K) !V {
21             return try self.dbi.get(k, V);
22         }
23         pub fn del(self: Self, k: K) !void {
24             try self.dbi.del(k);
25         }
26         pub fn has(self: Self, k: K) !bool {
27             return try self.dbi.has(k);
28         }
29         pub const Iterator = struct {
30             pub const Result = struct { key: K, val: V };
31             cursor: lmdb.Cursor,
32             k: ?K,
33             v: ?V,
34
35             pub fn next(self: *Iterator) ?Result {
36                 if (self.k != null and self.v != null) {
37                     const result = Result{ .key = self.k.?, .val = self.v.? };
38
39                     var k = self.k.?;
40                     self.v = self.cursor.get(&k, V, .Next) catch return null;
41                     if (self.v != null) {
42                         self.k = k;
43                     }
44                     return result;
45                 } else {
46                     return null;
47                 }
48             }
49         };
50         pub fn iterator(self: Self) !Iterator {
51             var cursor = try self.dbi.cursor();
52
53             var k: K = undefined;
54             const v = try cursor.get(&k, V, .First);
55             return .{ .cursor = cursor, .k = k, .v = v };
56         }
57     };
58 }
59
60 pub const Prng = struct {
61     var prng = std.Random.DefaultPrng.init(PRNG_SEED);
62
63     pub fn gen(dbi: lmdb.Dbi, comptime T: type) !T {
64         var buf: [@sizeOf(T)]u8 = undefined;
65         // TODO: limit loop
66         while (true) {
67             prng.fill(&buf);
68             const t = std.mem.bytesToValue(T, &buf);
69             if (!try dbi.has(t)) {
70                 return t;
71             }
72         }
73     }
74 };
75
76 fn SetListBase(comptime K: type, comptime V: type) type {
77     return struct {
78         const Self = @This();
79         pub const Index = u64;
80         pub const Key = K;
81         pub const Val = V;
82         pub const View = SetListViewBase(K, V);
83
84         idx: ?Index = null,
85
86         fn open_dbi(txn: lmdb.Txn) !lmdb.Dbi {
87             return try txn.dbi("SetList");
88         }
89         pub fn init(txn: lmdb.Txn) !Self {
90             const head = View.Head{};
91             const dbi = try open_dbi(txn);
92             const idx = try Prng.gen(dbi, Index);
93             try dbi.put(idx, head);
94             return .{ .idx = idx };
95         }
96         pub fn open(self: Self, txn: lmdb.Txn) !View {
97             // create new head
98             if (self.idx == null) {
99                 return error.NotInitialized;
100             }
101             // get head from dbi
102             const dbi = try open_dbi(txn);
103             const head = try dbi.get(self.idx.?, View.Head);
104             return .{
105                 .dbi = dbi,
106                 .idx = self.idx.?,
107                 .head = head,
108             };
109         }
110     };
111 }
112
113 fn SetListViewBase(comptime K: type, comptime V: type) type {
114     return struct {
115         const Self = @This();
116         pub const ItemIndex = struct { SetListBase(K, V).Index, Key };
117         pub const Key = K;
118         pub const Val = V;
119
120         pub const Head = struct {
121             len: usize = 0,
122             first: ?K = null,
123             last: ?K = null,
124         };
125         pub const Item = struct {
126             next: ?K = null,
127             prev: ?K = null,
128             data: V,
129         };
130
131         dbi: lmdb.Dbi,
132         idx: SetListBase(K, V).Index,
133         head: Head,
134
135         fn item_idx(self: Self, k: K) ItemIndex {
136             return .{ self.idx, k };
137         }
138         fn item_get(self: Self, k: K) !Item {
139             return try self.dbi.get(self.item_idx(k), Item);
140         }
141         fn item_put(self: Self, k: K, item: Item) !void {
142             try self.dbi.put(self.item_idx(k), item);
143         }
144         fn head_update(self: Self) !void {
145             try self.dbi.put(self.idx, self.head);
146         }
147         pub fn del(self: *Self, k: K) !void {
148             const item = try self.item_get(k);
149
150             if (item.prev != null) {
151                 var prev = try self.item_get(item.prev.?);
152                 prev.next = item.next;
153                 try self.item_put(item.prev.?, prev);
154             }
155
156             if (item.next != null) {
157                 var next = try self.item_get(item.next.?);
158                 next.prev = item.prev;
159                 try self.item_put(item.next.?, next);
160             }
161
162             if (self.head.first == k) self.head.first = item.next;
163             if (self.head.last == k) self.head.last = item.prev;
164             self.head.len -= 1;
165             try self.head_update();
166
167             try self.dbi.del(self.item_idx(k));
168         }
169         pub fn clear(self: *Self) !void {
170             var it = self.iterator();
171             while (it.next()) |kv| {
172                 try self.del(kv.key);
173             }
174         }
175         pub fn len(self: Self) usize {
176             return self.head.len;
177         }
178         pub fn append(self: *Self, key: Key, val: Val) !void {
179             if (self.head.len == 0) {
180                 const item = Item{ .data = val };
181                 try self.item_put(key, item);
182
183                 self.head.len = 1;
184                 self.head.first = key;
185                 self.head.last = key;
186                 try self.head_update();
187             } else {
188                 const prev_idx = self.head.last.?;
189                 var prev = try self.item_get(prev_idx);
190
191                 const item = Item{ .prev = prev_idx, .data = val };
192                 try self.item_put(key, item);
193
194                 prev.next = key;
195                 try self.item_put(prev_idx, prev);
196
197                 self.head.last = key;
198                 self.head.len += 1;
199                 try self.head_update();
200             }
201         }
202         pub fn get(self: Self, key: Key) !Val {
203             const item = try self.item_get(key);
204             return item.data;
205         }
206         pub fn has(self: Self, key: Key) !bool {
207             return self.dbi.has(self.item_idx(key));
208         }
209         pub const Iterator = struct {
210             pub const Result = struct { key: K, val: V };
211
212             slv: SetListViewBase(K, V),
213             idx: ?K,
214             dir: enum { Forward, Backward },
215
216             pub fn next(self: *Iterator) ?Result {
217                 if (self.idx != null) {
218                     const k = self.idx.?;
219                     const item = self.slv.item_get(k) catch return null;
220                     self.idx = switch (self.dir) {
221                         .Forward => item.next,
222                         .Backward => item.prev,
223                     };
224                     return .{ .key = k, .val = item.data };
225                 } else {
226                     return null;
227                 }
228             }
229         };
230         pub fn iterator(self: Self) Iterator {
231             return .{
232                 .slv = self,
233                 .idx = self.head.first,
234                 .dir = .Forward,
235             };
236         }
237         pub fn reverse_iterator(self: Self) Iterator {
238             return .{
239                 .slv = self,
240                 .idx = self.head.last,
241                 .dir = .Backward,
242             };
243         }
244     };
245 }
246
247 pub fn Set(comptime K: type) type {
248     return struct {
249         pub const Key = K;
250         pub const Val = void;
251
252         pub const Base = SetListBase(Key, Val);
253         pub const View = struct {
254             const ViewBase = SetListViewBase(Key, Val);
255
256             base: ViewBase,
257
258             pub fn del(self: *@This(), key: Key) !void {
259                 try self.base.del(key);
260             }
261             pub fn clear(self: *@This()) !void {
262                 try self.base.clear();
263             }
264             pub fn len(self: @This()) usize {
265                 return self.base.len();
266             }
267             pub fn append(self: *@This(), key: Key) !void {
268                 try self.base.append(key, {});
269             }
270             pub fn has(self: @This(), key: Key) !bool {
271                 return try self.base.has(key);
272             }
273             pub fn iterator(self: @This()) ViewBase.Iterator {
274                 return self.base.iterator();
275             }
276             pub fn reverse_iterator(self: @This()) ViewBase.Iterator {
277                 return self.base.reverse_iterator();
278             }
279         };
280
281         base: Base,
282
283         pub fn init(txn: lmdb.Txn) !@This() {
284             return .{ .base = try Base.init(txn) };
285         }
286         pub fn open(self: @This(), txn: lmdb.Txn) !View {
287             return .{ .base = try self.base.open(txn) };
288         }
289     };
290 }
291
292 pub fn List(comptime V: type) type {
293     return struct {
294         pub const Key = u64;
295         pub const Val = V;
296
297         pub const Base = SetListBase(Key, Val);
298         pub const View = struct {
299             const ViewBase = SetListViewBase(Key, Val);
300
301             base: ViewBase,
302
303             fn gen(self: @This()) !Key {
304                 // TODO: limit loop
305                 while (true) {
306                     const key = try Prng.gen(self.base.dbi, Key);
307                     if (!try self.base.dbi.has(self.base.item_idx(key))) {
308                         return key;
309                     }
310                 }
311             }
312             pub fn del(self: *@This(), key: Key) !void {
313                 try self.base.del(key);
314             }
315             pub fn clear(self: *@This()) !void {
316                 try self.base.clear();
317             }
318             pub fn len(self: @This()) usize {
319                 return self.base.len();
320             }
321             pub fn append(self: *@This(), val: Val) !Key {
322                 const key = try self.gen();
323                 try self.base.append(key, val);
324                 return key;
325             }
326             pub fn get(self: @This(), key: Key) !Val {
327                 return try self.base.get(key);
328             }
329             pub fn has(self: @This(), key: Key) !bool {
330                 return try self.base.has(key);
331             }
332             pub fn iterator(self: @This()) ViewBase.Iterator {
333                 return self.base.iterator();
334             }
335             pub fn reverse_iterator(self: @This()) ViewBase.Iterator {
336                 return self.base.reverse_iterator();
337             }
338         };
339
340         base: Base,
341
342         pub fn init(txn: lmdb.Txn) !@This() {
343             return .{ .base = try Base.init(txn) };
344         }
345         pub fn open(self: @This(), txn: lmdb.Txn) !View {
346             return .{ .base = try self.base.open(txn) };
347         }
348     };
349 }
350
351 pub fn SetList(comptime K: type, comptime V: type) type {
352     return struct {
353         pub const Key = K;
354         pub const Val = V;
355
356         pub const Base = SetListBase(Key, Val);
357         pub const View = struct {
358             const ViewBase = SetListViewBase(Key, Val);
359
360             base: ViewBase,
361
362             pub fn del(self: *@This(), key: Key) !void {
363                 try self.base.del(key);
364             }
365             pub fn clear(self: *@This()) !void {
366                 try self.base.clear();
367             }
368             pub fn len(self: @This()) usize {
369                 return self.base.len();
370             }
371             pub fn append(self: *@This(), key: Key, val: Val) !void {
372                 try self.base.append(key, val);
373             }
374             pub fn get(self: @This(), key: Key) !Val {
375                 return try self.base.get(key);
376             }
377             pub fn has(self: @This(), key: Key) !bool {
378                 return try self.base.has(key);
379             }
380             pub fn iterator(self: @This()) ViewBase.Iterator {
381                 return self.base.iterator();
382             }
383             pub fn reverse_iterator(self: @This()) ViewBase.Iterator {
384                 return self.base.reverse_iterator();
385             }
386         };
387
388         base: Base,
389
390         pub fn init(txn: lmdb.Txn) !@This() {
391             return .{ .base = try Base.init(txn) };
392         }
393         pub fn open(self: @This(), txn: lmdb.Txn) !View {
394             return .{ .base = try self.base.open(txn) };
395         }
396     };
397 }
398
399 const DB_SIZE = 1024 * 1024 * 1;
400
401 test "db" {
402     const env = try lmdb.Env.open("db", DB_SIZE);
403     defer env.close();
404
405     const txn = try env.txn();
406     defer txn.commit() catch {};
407
408     var db = try Db(u32, u32).init(txn, "123");
409     var n: u32 = 456;
410     if (try db.has(123)) {
411         n = try db.get(123);
412         n += 1;
413     }
414     try db.put(123, n);
415     std.debug.print("n: {}\n", .{n});
416 }
417
418 // test "list" {
419 //     const env = try lmdb.Env.open("db", DB_SIZE);
420 //     defer env.close();
421
422 //     const txn = try env.txn();
423 //     defer txn.commit();
424
425 //     const db = List.init(txn, "b", u32);
426 // }
427
428 test "set" {
429     var env = try lmdb.Env.open("db", 1024 * 1024 * 1);
430     // env.sync();
431     defer env.close();
432
433     var txn = try env.txn();
434     defer txn.commit() catch {};
435
436     var dbi = try txn.dbi("abc");
437
438     const A = struct {
439         ml: Set(usize),
440     };
441
442     var a: A = undefined;
443     const a_idx: u64 = 27;
444     if (try dbi.has(a_idx)) {
445         a = try dbi.get(a_idx, A);
446     } else {
447         a = A{ .ml = try Set(usize).init(txn) };
448         try dbi.put(a_idx, a);
449     }
450
451     var ml = try a.ml.open(txn);
452
453     const len = ml.len();
454     std.debug.print("{}\n", .{len});
455     try ml.append(len);
456     std.debug.print("{}\n", .{try ml.has(len)});
457     var it = ml.iterator();
458     while (it.next()) |i| {
459         std.debug.print("{}\n", .{i});
460     }
461 }
462
463 test "list" {
464     var env = try lmdb.Env.open("db", 1024 * 1024 * 1);
465     // env.sync();
466     defer env.close();
467
468     var txn = try env.txn();
469     defer txn.commit() catch {};
470
471     var dbi = try txn.dbi("def");
472
473     const A = struct {
474         ml: List(usize),
475     };
476
477     var a: A = undefined;
478     const a_idx: u64 = 27;
479     if (try dbi.has(a_idx)) {
480         a = try dbi.get(a_idx, A);
481     } else {
482         a = A{ .ml = try List(usize).init(txn) };
483         try dbi.put(a_idx, a);
484     }
485
486     var ml = try a.ml.open(txn);
487
488     const len = ml.len();
489     std.debug.print("{}\n", .{len});
490     const newest = try ml.append(len * 10);
491     std.debug.print("{}: {}\n", .{ newest, try ml.get(newest) });
492     var it = ml.iterator();
493     while (it.next()) |i| {
494         std.debug.print("{}: {}\n", .{ i.key, i.val });
495     }
496 }
497
498 test "setlist" {
499     var env = try lmdb.Env.open("db", 1024 * 1024 * 1);
500     // env.sync();
501     defer env.close();
502
503     var txn = try env.txn();
504     defer txn.commit() catch {};
505
506     var dbi = try txn.dbi("ghi");
507
508     const A = struct {
509         ml: SetList(usize, usize),
510     };
511
512     var a: A = undefined;
513     const a_idx: u64 = 27;
514     if (try dbi.has(a_idx)) {
515         a = try dbi.get(a_idx, A);
516     } else {
517         a = A{ .ml = try SetList(usize, usize).init(txn) };
518         try dbi.put(a_idx, a);
519     }
520
521     var ml = try a.ml.open(txn);
522
523     const len = ml.len();
524     std.debug.print("{}\n", .{len});
525     try ml.append(len, len * 10);
526     std.debug.print("{}\n", .{try ml.get(len)});
527     var it = ml.iterator();
528     while (it.next()) |i| {
529         std.debug.print("{}: {}\n", .{ i.key, i.val });
530     }
531 }