]> gitweb.ps.run Git - ziglmdb/blob - src/db.zig
6279a1ba6c834beed506a54efd80d5821c392a32
[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             cursor: lmdb.Cursor,
31             k: ?K,
32             v: ?V,
33
34             pub fn next(self: *Iterator) ?struct { key: K, val: V } {
35                 if (self.k != null and self.v != null) {
36                     const result = .{ .key = self.k.?, .val = self.v.? };
37
38                     var k = self.k.?;
39                     self.v = self.cursor.get(&k, V, .Next) catch return null;
40                     if (self.v != null) {
41                         self.k = k;
42                     }
43                     return result;
44                 } else {
45                     return null;
46                 }
47             }
48         };
49         pub fn iterator(self: Self) !Iterator {
50             var cursor = try self.dbi.cursor();
51
52             var k: K = undefined;
53             const v = try cursor.get(&k, V, .First);
54             return .{ .cursor = cursor, .k = k, .v = v };
55         }
56     };
57 }
58
59 pub const Prng = struct {
60     var prng = std.Random.DefaultPrng.init(PRNG_SEED);
61
62     pub fn gen(dbi: lmdb.Dbi, comptime T: type) !T {
63         var buf: [@sizeOf(T)]u8 = undefined;
64         // TODO: limit loop
65         while (true) {
66             prng.fill(&buf);
67             const t = std.mem.bytesToValue(T, &buf);
68             if (!try dbi.has(t)) {
69                 return t;
70             }
71         }
72     }
73 };
74 pub fn Set(comptime K: type) type {
75     return struct {
76         idx: ?Index = null,
77
78         const Self = @This();
79         pub const Index = u64;
80         pub const View = SetView(K);
81
82         fn open_dbi(txn: lmdb.Txn) !lmdb.Dbi {
83             return try txn.dbi("SetList");
84         }
85         pub fn init(txn: lmdb.Txn) !Self {
86             const head = View.Head{};
87             const dbi = try open_dbi(txn);
88             const idx = try Prng.gen(dbi, Index);
89             try dbi.put(idx, head);
90             return .{ .idx = idx };
91         }
92         pub fn open(self: Self, txn: lmdb.Txn) !View {
93             // create new head
94             if (self.idx == null) {
95                 return error.NotInitialized;
96             }
97             // get head from dbi
98             const dbi = try open_dbi(txn);
99             const head = try dbi.get(self.idx.?, View.Head);
100             return .{
101                 .dbi = dbi,
102                 .idx = self.idx.?,
103                 .head = head,
104             };
105         }
106     };
107 }
108
109 pub fn SetView(comptime K: type) type {
110     return struct {
111         const Self = @This();
112         const ItemIndex = struct { Set(K).Index, K };
113
114         pub const Head = struct {
115             len: usize = 0,
116             first: ?K = null,
117             last: ?K = null,
118         };
119         pub const Item = struct {
120             next: ?K = null,
121             prev: ?K = null,
122         };
123
124         dbi: lmdb.Dbi,
125         idx: Set(K).Index,
126         head: Head,
127
128         fn item_idx(self: Self, k: K) ItemIndex {
129             return .{ self.idx, k };
130         }
131         fn item_get(self: Self, k: K) !Item {
132             return try self.dbi.get(self.item_idx(k), Item);
133         }
134         fn item_put(self: Self, k: K, item: Item) !void {
135             try self.dbi.put(self.item_idx(k), item);
136         }
137         fn head_update(self: Self) !void {
138             try self.dbi.put(self.idx, self.head);
139         }
140         pub fn append(self: *Self, k: K) !void {
141             if (self.head.len == 0) {
142                 const item = Item{};
143                 try self.item_put(k, item);
144
145                 self.head.len = 1;
146                 self.head.first = k;
147                 self.head.last = k;
148                 try self.head_update();
149             } else {
150                 const prev_idx = self.head.last.?;
151                 var prev = try self.item_get(prev_idx);
152
153                 const item = Item{ .prev = prev_idx };
154                 try self.item_put(k, item);
155
156                 prev.next = k;
157                 try self.item_put(prev_idx, prev);
158
159                 self.head.last = k;
160                 self.head.len += 1;
161                 try self.head_update();
162             }
163         }
164         pub fn del(self: *Self, k: K) !void {
165             const item = try self.item_get(k);
166
167             if (item.prev != null) {
168                 var prev = try self.item_get(item.prev.?);
169                 prev.next = item.next;
170                 try self.item_put(item.prev.?, prev);
171             }
172
173             if (item.next != null) {
174                 var next = try self.item_get(item.next.?);
175                 next.prev = item.prev;
176                 try self.item_put(item.next.?, next);
177             }
178
179             if (self.head.first == k) self.head.first = item.next;
180             if (self.head.last == k) self.head.last = item.prev;
181             self.head.len -= 1;
182             try self.head_update();
183
184             try self.dbi.del(self.item_idx(k));
185         }
186         pub fn clear(self: *Self) !void {
187             var it = self.iterator();
188             while (it.next()) |i| {
189                 try self.del(i);
190             }
191         }
192         pub fn has(self: Self, k: K) !bool {
193             return self.dbi.has(self.item_idx(k));
194         }
195         pub fn len(self: Self) usize {
196             return self.head.len;
197         }
198         pub const Iterator = struct {
199             sv: SetView(K),
200             idx: ?K,
201             dir: enum { Forward, Backward },
202
203             pub fn next(self: *Iterator) ?K {
204                 if (self.idx != null) {
205                     const k = self.idx.?;
206                     const item = self.sv.item_get(k) catch return null;
207                     self.idx = switch (self.dir) {
208                         .Forward => item.next,
209                         .Backward => item.prev,
210                     };
211                     return k;
212                 } else {
213                     return null;
214                 }
215             }
216         };
217         pub fn iterator(self: Self) Iterator {
218             return .{
219                 .sv = self,
220                 .idx = self.head.first,
221                 .dir = .Forward,
222             };
223         }
224         pub fn reverse_iterator(self: Self) Iterator {
225             return .{
226                 .sv = self,
227                 .idx = self.head.last,
228                 .dir = .Backward,
229             };
230         }
231     };
232 }
233 pub fn List(comptime V: type) type {
234     return struct {
235         idx: ?Index = null,
236
237         const Self = @This();
238         pub const Index = u64;
239         pub const View = ListView(V);
240
241         fn open_dbi(txn: lmdb.Txn) !lmdb.Dbi {
242             return try txn.dbi("SetList");
243         }
244         pub fn init(txn: lmdb.Txn) !Self {
245             const head = View.Head{};
246             const dbi = try open_dbi(txn);
247             const idx = try Prng.gen(dbi, Index);
248             try dbi.put(idx, head);
249             return .{ .idx = idx };
250         }
251         pub fn open(self: Self, txn: lmdb.Txn) !View {
252             // create new head
253             if (self.idx == null) {
254                 return error.NotInitialized;
255             }
256             // get head from dbi
257             const dbi = try open_dbi(txn);
258             const head = try dbi.get(self.idx.?, View.Head);
259             return .{
260                 .dbi = dbi,
261                 .idx = self.idx.?,
262                 .head = head,
263             };
264         }
265     };
266 }
267
268 pub fn ListView(comptime V: type) type {
269     return struct {
270         const Self = @This();
271         const K = u64;
272         const ItemIndex = struct { List(V).Index, K };
273
274         pub const Head = struct {
275             len: usize = 0,
276             first: ?K = null,
277             last: ?K = null,
278         };
279         pub const Item = struct {
280             next: ?K = null,
281             prev: ?K = null,
282             data: V,
283         };
284
285         dbi: lmdb.Dbi,
286         idx: List(V).Index,
287         head: Head,
288
289         fn item_idx(self: Self, k: K) ItemIndex {
290             return .{ self.idx, k };
291         }
292         fn item_get(self: Self, k: K) !Item {
293             return try self.dbi.get(self.item_idx(k), Item);
294         }
295         fn item_put(self: Self, k: K, item: Item) !void {
296             try self.dbi.put(self.item_idx(k), item);
297         }
298         fn head_update(self: Self) !void {
299             try self.dbi.put(self.idx, self.head);
300         }
301         fn gen(self: Self) !K {
302             // TODO: limit loop
303             while (true) {
304                 const k = try Prng.gen(self.dbi, K);
305                 if (!try self.dbi.has(self.item_idx(k))) {
306                     return k;
307                 }
308             }
309         }
310         pub fn append(self: *Self, v: V) !K {
311             if (self.head.len == 0) {
312                 const k = try self.gen();
313                 const item = Item{ .data = v };
314                 try self.item_put(k, item);
315
316                 self.head.len = 1;
317                 self.head.first = k;
318                 self.head.last = k;
319                 try self.head_update();
320
321                 return k;
322             } else {
323                 const prev_idx = self.head.last.?;
324                 var prev = try self.item_get(prev_idx);
325
326                 const k = try self.gen();
327                 const item = Item{ .prev = prev_idx, .data = v };
328                 try self.item_put(k, item);
329
330                 prev.next = k;
331                 try self.item_put(prev_idx, prev);
332
333                 self.head.last = k;
334                 self.head.len += 1;
335                 try self.head_update();
336
337                 return k;
338             }
339         }
340         pub fn get(self: Self, k: K) !V {
341             const item = try self.item_get(k);
342             return item.data;
343         }
344         pub fn del(self: *Self, k: K) !void {
345             const item = try self.item_get(k);
346
347             if (item.prev != null) {
348                 var prev = try self.item_get(item.prev.?);
349                 prev.next = item.next;
350                 try self.item_put(item.prev.?, prev);
351             }
352
353             if (item.next != null) {
354                 var next = try self.item_get(item.next.?);
355                 next.prev = item.prev;
356                 try self.item_put(item.next.?, next);
357             }
358
359             if (self.head.first == k) self.head.first = item.next;
360             if (self.head.last == k) self.head.last = item.prev;
361             self.head.len -= 1;
362             try self.head_update();
363
364             try self.dbi.del(self.item_idx(k));
365         }
366         pub fn clear(self: *Self) !void {
367             var it = self.iterator();
368             while (it.next()) |kv| {
369                 try self.del(kv.key);
370             }
371         }
372         pub fn len(self: Self) usize {
373             return self.head.len;
374         }
375         pub const Iterator = struct {
376             lv: ListView(V),
377             idx: ?K,
378             dir: enum { Forward, Backward },
379
380             pub fn next(self: *Iterator) ?struct { key: K, val: V } {
381                 if (self.idx != null) {
382                     const k = self.idx.?;
383                     const item = self.lv.item_get(k) catch return null;
384                     self.idx = switch (self.dir) {
385                         .Forward => item.next,
386                         .Backward => item.prev,
387                     };
388                     return .{ .key = k, .val = item.data };
389                 } else {
390                     return null;
391                 }
392             }
393         };
394         pub fn iterator(self: Self) Iterator {
395             return .{
396                 .lv = self,
397                 .idx = self.head.first,
398                 .dir = .Forward,
399             };
400         }
401         pub fn reverse_iterator(self: Self) Iterator {
402             return .{
403                 .lv = self,
404                 .idx = self.head.last,
405                 .dir = .Backward,
406             };
407         }
408     };
409 }
410
411 pub fn SetList(comptime K: type, comptime V: type) type {
412     return struct {
413         idx: ?Index = null,
414
415         const Self = @This();
416         pub const Index = u64;
417         pub const View = SetListView(K, V);
418
419         fn open_dbi(txn: lmdb.Txn) !lmdb.Dbi {
420             return try txn.dbi("SetList");
421         }
422         pub fn init(txn: lmdb.Txn) !Self {
423             const head = View.Head{};
424             const dbi = try open_dbi(txn);
425             const idx = try Prng.gen(dbi, Index);
426             try dbi.put(idx, head);
427             return .{ .idx = idx };
428         }
429         pub fn open(self: Self, txn: lmdb.Txn) !View {
430             // create new head
431             if (self.idx == null) {
432                 return error.NotInitialized;
433             }
434             // get head from dbi
435             const dbi = try open_dbi(txn);
436             const head = try dbi.get(self.idx.?, View.Head);
437             return .{
438                 .dbi = dbi,
439                 .idx = self.idx.?,
440                 .head = head,
441             };
442         }
443     };
444 }
445
446 pub fn SetListView(comptime K: type, comptime V: type) type {
447     return struct {
448         const Self = @This();
449         const ItemIndex = struct { SetList(K, V).Index, K };
450
451         pub const Head = struct {
452             len: usize = 0,
453             first: ?K = null,
454             last: ?K = null,
455         };
456         pub const Item = struct {
457             next: ?K = null,
458             prev: ?K = null,
459             data: V,
460         };
461
462         dbi: lmdb.Dbi,
463         idx: SetList(K, V).Index,
464         head: Head,
465
466         fn item_idx(self: Self, k: K) ItemIndex {
467             return .{ self.idx, k };
468         }
469         fn item_get(self: Self, k: K) !Item {
470             return try self.dbi.get(self.item_idx(k), Item);
471         }
472         fn item_put(self: Self, k: K, item: Item) !void {
473             try self.dbi.put(self.item_idx(k), item);
474         }
475         fn head_update(self: Self) !void {
476             try self.dbi.put(self.idx, self.head);
477         }
478         pub fn append(self: *Self, k: K, v: V) !void {
479             if (self.head.len == 0) {
480                 const item = Item{ .data = v };
481                 try self.item_put(k, item);
482
483                 self.head.len = 1;
484                 self.head.first = k;
485                 self.head.last = k;
486                 try self.head_update();
487             } else {
488                 const prev_idx = self.head.last.?;
489                 var prev = try self.item_get(prev_idx);
490
491                 const item = Item{ .prev = prev_idx, .data = v };
492                 try self.item_put(k, item);
493
494                 prev.next = k;
495                 try self.item_put(prev_idx, prev);
496
497                 self.head.last = k;
498                 self.head.len += 1;
499                 try self.head_update();
500             }
501         }
502         pub fn get(self: Self, k: K) !V {
503             const item = try self.item_get(k);
504             return item.data;
505         }
506         pub fn del(self: *Self, k: K) !void {
507             const item = try self.item_get(k);
508
509             if (item.prev != null) {
510                 var prev = try self.item_get(item.prev.?);
511                 prev.next = item.next;
512                 try self.item_put(item.prev.?, prev);
513             }
514
515             if (item.next != null) {
516                 var next = try self.item_get(item.next.?);
517                 next.prev = item.prev;
518                 try self.item_put(item.next.?, next);
519             }
520
521             if (self.head.first == k) self.head.first = item.next;
522             if (self.head.last == k) self.head.last = item.prev;
523             self.head.len -= 1;
524             try self.head_update();
525
526             try self.dbi.del(self.item_idx(k));
527         }
528         pub fn clear(self: *Self) !void {
529             var it = self.iterator();
530             while (it.next()) |kv| {
531                 try self.del(kv.key);
532             }
533         }
534         pub fn has(self: Self, k: K) !bool {
535             return self.dbi.has(self.item_idx(k));
536         }
537         pub fn len(self: Self) usize {
538             return self.head.len;
539         }
540         pub const Iterator = struct {
541             slv: SetListView(K, V),
542             idx: ?K,
543             dir: enum { Forward, Backward },
544
545             pub fn next(self: *Iterator) ?struct { key: K, val: V } {
546                 if (self.idx != null) {
547                     const k = self.idx.?;
548                     const item = self.slv.item_get(k) catch return null;
549                     self.idx = switch (self.dir) {
550                         .Forward => item.next,
551                         .Backward => item.prev,
552                     };
553                     return .{ .key = k, .val = item.data };
554                 } else {
555                     return null;
556                 }
557             }
558         };
559         pub fn iterator(self: Self) Iterator {
560             return .{
561                 .slv = self,
562                 .idx = self.head.first,
563                 .dir = .Forward,
564             };
565         }
566         pub fn reverse_iterator(self: Self) Iterator {
567             return .{
568                 .slv = self,
569                 .idx = self.head.last,
570                 .dir = .Backward,
571             };
572         }
573     };
574 }
575
576 const DB_SIZE = 1024 * 1024 * 1;
577
578 test "db" {
579     const env = try lmdb.Env.open("db", DB_SIZE);
580     defer env.close();
581
582     const txn = try env.txn();
583     defer txn.commit() catch {};
584
585     var db = try Db(u32, u32).init(txn, "123");
586     var n: u32 = 456;
587     if (try db.has(123)) {
588         n = try db.get(123);
589         n += 1;
590     }
591     try db.put(123, n);
592     std.debug.print("n: {}\n", .{n});
593 }
594
595 // test "list" {
596 //     const env = try lmdb.Env.open("db", DB_SIZE);
597 //     defer env.close();
598
599 //     const txn = try env.txn();
600 //     defer txn.commit();
601
602 //     const db = List.init(txn, "b", u32);
603 // }
604
605 test "set" {
606     var env = try lmdb.Env.open("db", 1024 * 1024 * 1);
607     // env.sync();
608     defer env.close();
609
610     var txn = try env.txn();
611     defer txn.commit() catch {};
612
613     var dbi = try txn.dbi("abc");
614
615     const A = struct {
616         ml: Set(usize),
617     };
618
619     var a: A = undefined;
620     const a_idx: u64 = 27;
621     if (try dbi.has(a_idx)) {
622         a = try dbi.get(a_idx, A);
623     } else {
624         a = A{ .ml = try Set(usize).init(txn) };
625         try dbi.put(a_idx, a);
626     }
627
628     var ml = try a.ml.open(txn);
629
630     const len = ml.len();
631     std.debug.print("{}\n", .{len});
632     try ml.append(len);
633     std.debug.print("{}\n", .{try ml.has(len)});
634     var it = ml.iterator();
635     while (it.next()) |i| {
636         std.debug.print("{}\n", .{i});
637     }
638 }
639
640 test "list" {
641     var env = try lmdb.Env.open("db", 1024 * 1024 * 1);
642     // env.sync();
643     defer env.close();
644
645     var txn = try env.txn();
646     defer txn.commit() catch {};
647
648     var dbi = try txn.dbi("def");
649
650     const A = struct {
651         ml: List(usize),
652     };
653
654     var a: A = undefined;
655     const a_idx: u64 = 27;
656     if (try dbi.has(a_idx)) {
657         a = try dbi.get(a_idx, A);
658     } else {
659         a = A{ .ml = try List(usize).init(txn) };
660         try dbi.put(a_idx, a);
661     }
662
663     var ml = try a.ml.open(txn);
664
665     const len = ml.len();
666     std.debug.print("{}\n", .{len});
667     const newest = try ml.append(len * 10);
668     std.debug.print("{}: {}\n", .{ newest, try ml.get(newest) });
669     var it = ml.iterator();
670     while (it.next()) |i| {
671         std.debug.print("{}: {}\n", .{ i.key, i.val });
672     }
673 }
674
675 test "setlist" {
676     var env = try lmdb.Env.open("db", 1024 * 1024 * 1);
677     // env.sync();
678     defer env.close();
679
680     var txn = try env.txn();
681     defer txn.commit() catch {};
682
683     var dbi = try txn.dbi("ghi");
684
685     const A = struct {
686         ml: SetList(usize, usize),
687     };
688
689     var a: A = undefined;
690     const a_idx: u64 = 27;
691     if (try dbi.has(a_idx)) {
692         a = try dbi.get(a_idx, A);
693     } else {
694         a = A{ .ml = try SetList(usize, usize).init(txn) };
695         try dbi.put(a_idx, a);
696     }
697
698     var ml = try a.ml.open(txn);
699
700     const len = ml.len();
701     std.debug.print("{}\n", .{len});
702     try ml.append(len, len * 10);
703     std.debug.print("{}\n", .{try ml.get(len)});
704     var it = ml.iterator();
705     while (it.next()) |i| {
706         std.debug.print("{}: {}\n", .{ i.key, i.val });
707     }
708 }