]> gitweb.ps.run Git - ziglmdb/blob - src/db.zig
5d7a8242c83bf0b2f0a58b609d2dfbb9bb3a1bb2
[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 has(self: Self, k: K) !bool {
187             return self.dbi.has(self.item_idx(k));
188         }
189         pub fn len(self: Self) usize {
190             return self.head.len;
191         }
192         pub const Iterator = struct {
193             sv: SetView(K),
194             idx: ?K,
195             dir: enum { Forward, Backward },
196
197             pub fn next(self: *Iterator) ?K {
198                 if (self.idx != null) {
199                     const k = self.idx.?;
200                     const item = self.sv.item_get(k) catch return null;
201                     self.idx = switch (self.dir) {
202                         .Forward => item.next,
203                         .Backward => item.prev,
204                     };
205                     return k;
206                 } else {
207                     return null;
208                 }
209             }
210         };
211         pub fn iterator(self: Self) Iterator {
212             return .{
213                 .sv = self,
214                 .idx = self.head.first,
215                 .dir = .Forward,
216             };
217         }
218         pub fn reverse_iterator(self: Self) Iterator {
219             return .{
220                 .sv = self,
221                 .idx = self.head.last,
222                 .dir = .Backward,
223             };
224         }
225     };
226 }
227 pub fn List(comptime V: type) type {
228     return struct {
229         idx: ?Index = null,
230
231         const Self = @This();
232         pub const Index = u64;
233         pub const View = ListView(V);
234
235         fn open_dbi(txn: lmdb.Txn) !lmdb.Dbi {
236             return try txn.dbi("SetList");
237         }
238         pub fn init(txn: lmdb.Txn) !Self {
239             const head = View.Head{};
240             const dbi = try open_dbi(txn);
241             const idx = try Prng.gen(dbi, Index);
242             try dbi.put(idx, head);
243             return .{ .idx = idx };
244         }
245         pub fn open(self: Self, txn: lmdb.Txn) !View {
246             // create new head
247             if (self.idx == null) {
248                 return error.NotInitialized;
249             }
250             // get head from dbi
251             const dbi = try open_dbi(txn);
252             const head = try dbi.get(self.idx.?, View.Head);
253             return .{
254                 .dbi = dbi,
255                 .idx = self.idx.?,
256                 .head = head,
257             };
258         }
259     };
260 }
261
262 pub fn ListView(comptime V: type) type {
263     return struct {
264         const Self = @This();
265         const K = u64;
266         const ItemIndex = struct { List(V).Index, K };
267
268         pub const Head = struct {
269             len: usize = 0,
270             first: ?K = null,
271             last: ?K = null,
272         };
273         pub const Item = struct {
274             next: ?K = null,
275             prev: ?K = null,
276             data: V,
277         };
278
279         dbi: lmdb.Dbi,
280         idx: List(V).Index,
281         head: Head,
282
283         fn item_idx(self: Self, k: K) ItemIndex {
284             return .{ self.idx, k };
285         }
286         fn item_get(self: Self, k: K) !Item {
287             return try self.dbi.get(self.item_idx(k), Item);
288         }
289         fn item_put(self: Self, k: K, item: Item) !void {
290             try self.dbi.put(self.item_idx(k), item);
291         }
292         fn head_update(self: Self) !void {
293             try self.dbi.put(self.idx, self.head);
294         }
295         fn gen(self: Self) !K {
296             // TODO: limit loop
297             while (true) {
298                 const k = try Prng.gen(self.dbi, K);
299                 if (!try self.dbi.has(self.item_idx(k))) {
300                     return k;
301                 }
302             }
303         }
304         pub fn append(self: *Self, v: V) !K {
305             if (self.head.len == 0) {
306                 const k = try self.gen();
307                 const item = Item{ .data = v };
308                 try self.item_put(k, item);
309
310                 self.head.len = 1;
311                 self.head.first = k;
312                 self.head.last = k;
313                 try self.head_update();
314
315                 return k;
316             } else {
317                 const prev_idx = self.head.last.?;
318                 var prev = try self.item_get(prev_idx);
319
320                 const k = try self.gen();
321                 const item = Item{ .prev = prev_idx, .data = v };
322                 try self.item_put(k, item);
323
324                 prev.next = k;
325                 try self.item_put(prev_idx, prev);
326
327                 self.head.last = k;
328                 self.head.len += 1;
329                 try self.head_update();
330
331                 return k;
332             }
333         }
334         pub fn get(self: Self, k: K) !V {
335             const item = try self.item_get(k);
336             return item.data;
337         }
338         pub fn del(self: *Self, k: K) !void {
339             const item = try self.item_get(k);
340
341             if (item.prev != null) {
342                 var prev = try self.item_get(item.prev.?);
343                 prev.next = item.next;
344                 try self.item_put(item.prev.?, prev);
345             }
346
347             if (item.next != null) {
348                 var next = try self.item_get(item.next.?);
349                 next.prev = item.prev;
350                 try self.item_put(item.next.?, next);
351             }
352
353             if (self.head.first == k) self.head.first = item.next;
354             if (self.head.last == k) self.head.last = item.prev;
355             self.head.len -= 1;
356             try self.head_update();
357
358             try self.dbi.del(self.item_idx(k));
359         }
360         pub fn len(self: Self) usize {
361             return self.head.len;
362         }
363         pub const Iterator = struct {
364             lv: ListView(V),
365             idx: ?K,
366             dir: enum { Forward, Backward },
367
368             pub fn next(self: *Iterator) ?struct { key: K, val: V } {
369                 if (self.idx != null) {
370                     const k = self.idx.?;
371                     const item = self.lv.item_get(k) catch return null;
372                     self.idx = switch (self.dir) {
373                         .Forward => item.next,
374                         .Backward => item.prev,
375                     };
376                     return .{ .key = k, .val = item.data };
377                 } else {
378                     return null;
379                 }
380             }
381         };
382         pub fn iterator(self: Self) Iterator {
383             return .{
384                 .lv = self,
385                 .idx = self.head.first,
386                 .dir = .Forward,
387             };
388         }
389         pub fn reverse_iterator(self: Self) Iterator {
390             return .{
391                 .lv = self,
392                 .idx = self.head.last,
393                 .dir = .Backward,
394             };
395         }
396     };
397 }
398
399 pub fn SetList(comptime K: type, comptime V: type) type {
400     return struct {
401         idx: ?Index = null,
402
403         const Self = @This();
404         pub const Index = u64;
405         pub const View = SetListView(K, V);
406
407         fn open_dbi(txn: lmdb.Txn) !lmdb.Dbi {
408             return try txn.dbi("SetList");
409         }
410         pub fn init(txn: lmdb.Txn) !Self {
411             const head = View.Head{};
412             const dbi = try open_dbi(txn);
413             const idx = try Prng.gen(dbi, Index);
414             try dbi.put(idx, head);
415             return .{ .idx = idx };
416         }
417         pub fn open(self: Self, txn: lmdb.Txn) !View {
418             // create new head
419             if (self.idx == null) {
420                 return error.NotInitialized;
421             }
422             // get head from dbi
423             const dbi = try open_dbi(txn);
424             const head = try dbi.get(self.idx.?, View.Head);
425             return .{
426                 .dbi = dbi,
427                 .idx = self.idx.?,
428                 .head = head,
429             };
430         }
431     };
432 }
433
434 pub fn SetListView(comptime K: type, comptime V: type) type {
435     return struct {
436         const Self = @This();
437         const ItemIndex = struct { SetList(K, V).Index, K };
438
439         pub const Head = struct {
440             len: usize = 0,
441             first: ?K = null,
442             last: ?K = null,
443         };
444         pub const Item = struct {
445             next: ?K = null,
446             prev: ?K = null,
447             data: V,
448         };
449
450         dbi: lmdb.Dbi,
451         idx: SetList(K, V).Index,
452         head: Head,
453
454         fn item_idx(self: Self, k: K) ItemIndex {
455             return .{ self.idx, k };
456         }
457         fn item_get(self: Self, k: K) !Item {
458             return try self.dbi.get(self.item_idx(k), Item);
459         }
460         fn item_put(self: Self, k: K, item: Item) !void {
461             try self.dbi.put(self.item_idx(k), item);
462         }
463         fn head_update(self: Self) !void {
464             try self.dbi.put(self.idx, self.head);
465         }
466         pub fn append(self: *Self, k: K, v: V) !void {
467             if (self.head.len == 0) {
468                 const item = Item{ .data = v };
469                 try self.item_put(k, item);
470
471                 self.head.len = 1;
472                 self.head.first = k;
473                 self.head.last = k;
474                 try self.head_update();
475             } else {
476                 const prev_idx = self.head.last.?;
477                 var prev = try self.item_get(prev_idx);
478
479                 const item = Item{ .prev = prev_idx, .data = v };
480                 try self.item_put(k, item);
481
482                 prev.next = k;
483                 try self.item_put(prev_idx, prev);
484
485                 self.head.last = k;
486                 self.head.len += 1;
487                 try self.head_update();
488             }
489         }
490         pub fn get(self: Self, k: K) !V {
491             const item = try self.item_get(k);
492             return item.data;
493         }
494         pub fn del(self: *Self, k: K) !void {
495             const item = try self.item_get(k);
496
497             if (item.prev != null) {
498                 var prev = try self.item_get(item.prev.?);
499                 prev.next = item.next;
500                 try self.item_put(item.prev.?, prev);
501             }
502
503             if (item.next != null) {
504                 var next = try self.item_get(item.next.?);
505                 next.prev = item.prev;
506                 try self.item_put(item.next.?, next);
507             }
508
509             if (self.head.first == k) self.head.first = item.next;
510             if (self.head.last == k) self.head.last = item.prev;
511             self.head.len -= 1;
512             try self.head_update();
513
514             try self.dbi.del(self.item_idx(k));
515         }
516         pub fn has(self: Self, k: K) !bool {
517             return self.dbi.has(self.item_idx(k));
518         }
519         pub fn len(self: Self) usize {
520             return self.head.len;
521         }
522         pub const Iterator = struct {
523             slv: SetListView(K, V),
524             idx: ?K,
525             dir: enum { Forward, Backward },
526
527             pub fn next(self: *Iterator) ?struct { key: K, val: V } {
528                 if (self.idx != null) {
529                     const k = self.idx.?;
530                     const item = self.slv.item_get(k) catch return null;
531                     self.idx = switch (self.dir) {
532                         .Forward => item.next,
533                         .Backward => item.prev,
534                     };
535                     return .{ .key = k, .val = item.data };
536                 } else {
537                     return null;
538                 }
539             }
540         };
541         pub fn iterator(self: Self) Iterator {
542             return .{
543                 .slv = self,
544                 .idx = self.head.first,
545                 .dir = .Forward,
546             };
547         }
548         pub fn reverse_iterator(self: Self) Iterator {
549             return .{
550                 .slv = self,
551                 .idx = self.head.last,
552                 .dir = .Backward,
553             };
554         }
555     };
556 }
557
558 const DB_SIZE = 1024 * 1024 * 1;
559
560 test "db" {
561     const env = try lmdb.Env.open("db", DB_SIZE);
562     defer env.close();
563
564     const txn = try env.txn();
565     defer txn.commit() catch {};
566
567     var db = try Db(u32, u32).init(txn, "123");
568     var n: u32 = 456;
569     if (try db.has(123)) {
570         n = try db.get(123);
571         n += 1;
572     }
573     try db.put(123, n);
574     std.debug.print("n: {}\n", .{n});
575 }
576
577 // test "list" {
578 //     const env = try lmdb.Env.open("db", DB_SIZE);
579 //     defer env.close();
580
581 //     const txn = try env.txn();
582 //     defer txn.commit();
583
584 //     const db = List.init(txn, "b", u32);
585 // }
586
587 test "set" {
588     var env = try lmdb.Env.open("db", 1024 * 1024 * 1);
589     // env.sync();
590     defer env.close();
591
592     var txn = try env.txn();
593     defer txn.commit() catch {};
594
595     var dbi = try txn.dbi("abc");
596
597     const A = struct {
598         ml: Set(usize),
599     };
600
601     var a: A = undefined;
602     const a_idx: u64 = 27;
603     if (try dbi.has(a_idx)) {
604         a = try dbi.get(a_idx, A);
605     } else {
606         a = A{ .ml = try Set(usize).init(txn) };
607         try dbi.put(a_idx, a);
608     }
609
610     var ml = try a.ml.open(txn);
611
612     const len = ml.len();
613     std.debug.print("{}\n", .{len});
614     try ml.append(len);
615     std.debug.print("{}\n", .{try ml.has(len)});
616     var it = ml.iterator();
617     while (it.next()) |i| {
618         std.debug.print("{}\n", .{i});
619     }
620 }
621
622 test "list" {
623     var env = try lmdb.Env.open("db", 1024 * 1024 * 1);
624     // env.sync();
625     defer env.close();
626
627     var txn = try env.txn();
628     defer txn.commit() catch {};
629
630     var dbi = try txn.dbi("def");
631
632     const A = struct {
633         ml: List(usize),
634     };
635
636     var a: A = undefined;
637     const a_idx: u64 = 27;
638     if (try dbi.has(a_idx)) {
639         a = try dbi.get(a_idx, A);
640     } else {
641         a = A{ .ml = try List(usize).init(txn) };
642         try dbi.put(a_idx, a);
643     }
644
645     var ml = try a.ml.open(txn);
646
647     const len = ml.len();
648     std.debug.print("{}\n", .{len});
649     const newest = try ml.append(len * 10);
650     std.debug.print("{}: {}\n", .{ newest, try ml.get(newest) });
651     var it = ml.iterator();
652     while (it.next()) |i| {
653         std.debug.print("{}: {}\n", .{ i.key, i.val });
654     }
655 }
656
657 test "setlist" {
658     var env = try lmdb.Env.open("db", 1024 * 1024 * 1);
659     // env.sync();
660     defer env.close();
661
662     var txn = try env.txn();
663     defer txn.commit() catch {};
664
665     var dbi = try txn.dbi("ghi");
666
667     const A = struct {
668         ml: SetList(usize, usize),
669     };
670
671     var a: A = undefined;
672     const a_idx: u64 = 27;
673     if (try dbi.has(a_idx)) {
674         a = try dbi.get(a_idx, A);
675     } else {
676         a = A{ .ml = try SetList(usize, usize).init(txn) };
677         try dbi.put(a_idx, a);
678     }
679
680     var ml = try a.ml.open(txn);
681
682     const len = ml.len();
683     std.debug.print("{}\n", .{len});
684     try ml.append(len, len * 10);
685     std.debug.print("{}\n", .{try ml.get(len)});
686     var it = ml.iterator();
687     while (it.next()) |i| {
688         std.debug.print("{}: {}\n", .{ i.key, i.val });
689     }
690 }