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