]> gitweb.ps.run Git - ziglmdb/blob - src/db.zig
add reverse iterator to DB
[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("SetList");
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 item_idx(self: Self, k: K) ItemIndex {
148             return .{ self.idx, k };
149         }
150         fn item_get(self: Self, k: K) !Item {
151             return try self.dbi.get(self.item_idx(k), Item);
152         }
153         fn item_put(self: Self, k: K, item: Item) !void {
154             try self.dbi.put(self.item_idx(k), item);
155         }
156         fn head_update(self: Self) !void {
157             try self.dbi.put(self.idx, self.head);
158         }
159         pub fn del(self: *Self, k: K) !void {
160             const item = try self.item_get(k);
161
162             if (item.prev != null) {
163                 var prev = try self.item_get(item.prev.?);
164                 prev.next = item.next;
165                 try self.item_put(item.prev.?, prev);
166             }
167
168             if (item.next != null) {
169                 var next = try self.item_get(item.next.?);
170                 next.prev = item.prev;
171                 try self.item_put(item.next.?, next);
172             }
173
174             if (self.head.first == k) self.head.first = item.next;
175             if (self.head.last == k) self.head.last = item.prev;
176             self.head.len -= 1;
177             try self.head_update();
178
179             try self.dbi.del(self.item_idx(k));
180         }
181         pub fn clear(self: *Self) !void {
182             var it = self.iterator();
183             while (it.next()) |kv| {
184                 try self.del(kv.key);
185             }
186         }
187         pub fn len(self: Self) usize {
188             return self.head.len;
189         }
190         pub fn append(self: *Self, key: Key, val: Val) !void {
191             if (self.head.len == 0) {
192                 const item = Item{ .data = val };
193                 try self.item_put(key, item);
194
195                 self.head.len = 1;
196                 self.head.first = key;
197                 self.head.last = key;
198                 try self.head_update();
199             } else {
200                 const prev_idx = self.head.last.?;
201                 var prev = try self.item_get(prev_idx);
202
203                 const item = Item{ .prev = prev_idx, .data = val };
204                 try self.item_put(key, item);
205
206                 prev.next = key;
207                 try self.item_put(prev_idx, prev);
208
209                 self.head.last = key;
210                 self.head.len += 1;
211                 try self.head_update();
212             }
213         }
214         pub fn get(self: Self, key: Key) !Val {
215             const item = try self.item_get(key);
216             return item.data;
217         }
218         pub fn has(self: Self, key: Key) !bool {
219             return self.dbi.has(self.item_idx(key));
220         }
221         pub const Iterator = struct {
222             pub const Result = struct { key: K, val: V };
223
224             slv: SetListViewBase(K, V),
225             idx: ?K,
226             dir: enum { Forward, Backward },
227
228             pub fn next(self: *Iterator) ?Result {
229                 if (self.idx != null) {
230                     const k = self.idx.?;
231                     const item = self.slv.item_get(k) catch return null;
232                     self.idx = switch (self.dir) {
233                         .Forward => item.next,
234                         .Backward => item.prev,
235                     };
236                     return .{ .key = k, .val = item.data };
237                 } else {
238                     return null;
239                 }
240             }
241         };
242         pub fn iterator(self: Self) Iterator {
243             return .{
244                 .slv = self,
245                 .idx = self.head.first,
246                 .dir = .Forward,
247             };
248         }
249         pub fn reverse_iterator(self: Self) Iterator {
250             return .{
251                 .slv = self,
252                 .idx = self.head.last,
253                 .dir = .Backward,
254             };
255         }
256     };
257 }
258
259 pub fn Set(comptime K: type) type {
260     return struct {
261         pub const Key = K;
262         pub const Val = void;
263
264         pub const Base = SetListBase(Key, Val);
265         pub const View = struct {
266             const ViewBase = SetListViewBase(Key, Val);
267
268             base: ViewBase,
269
270             pub fn del(self: *@This(), key: Key) !void {
271                 try self.base.del(key);
272             }
273             pub fn clear(self: *@This()) !void {
274                 try self.base.clear();
275             }
276             pub fn len(self: @This()) usize {
277                 return self.base.len();
278             }
279             pub fn append(self: *@This(), key: Key) !void {
280                 try self.base.append(key, {});
281             }
282             pub fn has(self: @This(), key: Key) !bool {
283                 return try self.base.has(key);
284             }
285             pub fn iterator(self: @This()) ViewBase.Iterator {
286                 return self.base.iterator();
287             }
288             pub fn reverse_iterator(self: @This()) ViewBase.Iterator {
289                 return self.base.reverse_iterator();
290             }
291         };
292
293         base: Base,
294
295         pub fn init(txn: lmdb.Txn) !@This() {
296             return .{ .base = try Base.init(txn) };
297         }
298         pub fn open(self: @This(), txn: lmdb.Txn) !View {
299             return .{ .base = try self.base.open(txn) };
300         }
301     };
302 }
303
304 pub fn List(comptime V: type) type {
305     return struct {
306         pub const Key = u64;
307         pub const Val = V;
308
309         pub const Base = SetListBase(Key, Val);
310         pub const View = struct {
311             const ViewBase = SetListViewBase(Key, Val);
312
313             base: ViewBase,
314
315             fn gen(self: @This()) !Key {
316                 // TODO: limit loop
317                 while (true) {
318                     const key = try Prng.gen(self.base.dbi, Key);
319                     if (!try self.base.dbi.has(self.base.item_idx(key))) {
320                         return key;
321                     }
322                 }
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.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 }