1 /**
2 Copyright: Copyright (c) 2016-2017 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 module voxelman.world.db.lmdbworlddb;
7 
8 import voxelman.log;
9 
10 import std..string : fromStringz, toStringz;
11 struct LmdbWorldDb
12 {
13 	private MDB_env* env;
14 	private MDB_txn* txn;
15 	private uint txnFlags;
16 	private MDB_dbi dbi;
17 
18 	// filename needs to have /0 after end
19 	void open(string filename, size_t mapSize = 1 * 1024 * 1024 * 1024) {
20 		assert(env is null);
21 		assert(txn is null);
22 		checked!mdb_env_create(&env);
23 
24 		checked!mdb_env_set_mapsize(env, mapSize);
25 
26 		checked!mdb_env_open(env, filename.toStringz,
27 			MDB_NOSUBDIR|
28 			MDB_NOMETASYNC|
29 			MDB_NOSYNC|
30 			MDB_WRITEMAP|
31 			MDB_NOLOCK|
32 			MDB_NOMEMINIT,
33 			//rwx_rwx_rwx
34 			0b110_110_110);
35 	}
36 
37 	static string libVersion() @nogc {
38 		return cast(string)mdb_version(null, null, null).fromStringz;
39 	}
40 
41 	void close() @nogc {
42 		mdb_env_close(env);
43 		env = null;
44 		txn = null;
45 	}
46 
47 	void beginTxn(uint flags = 0) @nogc {
48 		checked!mdb_txn_begin(env, null, flags, &txn);
49 		checked!mdb_dbi_open(txn, null/*main DB*/, 0/*flags*/, &dbi);
50 		checked!mdb_set_compare(txn, dbi, &mdb_cmp_long);
51 	}
52 
53 	void abortTxn() @nogc {
54 		mdb_txn_abort(txn);
55 	}
56 
57 	void commitTxn() @nogc {
58 		checked!mdb_txn_commit(txn);
59 	}
60 
61 	void put(ubyte[] key, ubyte[] value) @nogc {
62 		checked!mdb_put(txn, dbi, &key, &value, 0);
63 	}
64 
65 	ubyte[] get(ubyte[] key) @nogc {
66 		ubyte[] value;
67 		checked!mdb_get(txn, dbi, &key, &value);
68 		return value;
69 	}
70 
71 	void del(ubyte[] key) @nogc {
72 		checked!mdb_del(txn, dbi, &key, null);
73 	}
74 
75 	void dropDB() @nogc {
76 		checked!mdb_drop(txn, dbi, 0);
77 		checked!mdb_drop(txn, dbi, 1);
78 	}
79 }
80 
81 template checked(alias func)
82 {
83 	import std.traits;
84 	debug auto checked(string file = __FILE__, int line = __LINE__)(auto ref Parameters!func args)
85 	{
86 		int rc = func(args);
87 		checkCode(rc, file, line);
88 		return rc;
89 	} else
90 		alias checked = func;
91 }
92 
93 void checkCode(int code, string file = __FILE__, int line = __LINE__) @nogc
94 {
95 	if (code != MDB_SUCCESS && code != MDB_NOTFOUND)
96 	{
97 		//errorf("%s@%s: MDB error: %s", file, line, mdb_strerror(code).fromStringz);
98 		import core.stdc.stdio;
99 		printf("%s@%s: MDB error: %s", file.ptr, line, mdb_strerror(code));
100 		assert(false);
101 	}
102 }
103 
104 extern(C):
105 nothrow:
106 @nogc:
107 
108 static int mdb_cmp_long(const MDB_val *a, const MDB_val *b)
109 {
110 	immutable ulong a0 = (cast(ulong*)a.ptr)[0];
111 	immutable ulong a1 = (cast(ulong*)a.ptr)[1];
112 	immutable ulong b0 = (cast(ulong*)b.ptr)[0];
113 	immutable ulong b1 = (cast(ulong*)b.ptr)[1];
114 
115 	if (a1 == b1)
116 	{
117 		if (a0 == b0)
118 			return 0;
119 		else
120 			return (a0 < b0) ? -1 : 1;
121 	}
122 	else
123 	{
124 		return (a1 < b1) ? -1 : 1;
125 	}
126 }
127 
128 void mdb_load_libs();
129 
130 alias mdb_mode_t = uint;
131 struct mdb_filehandle_ts {}
132 alias mdb_filehandle_t = mdb_filehandle_ts*;
133 
134 struct MDB_env {}
135 struct MDB_txn {}
136 alias MDB_dbi = uint;
137 struct MDB_cursor {}
138 
139 alias MDB_val = ubyte[];
140 
141 enum {
142   MDB_FIXEDMAP = 0x01,
143   MDB_NOSUBDIR = 0x4000,
144   MDB_NOSYNC = 0x10000,
145   MDB_RDONLY = 0x20000,
146   MDB_NOMETASYNC = 0x40000,
147   MDB_WRITEMAP = 0x80000,
148   MDB_MAPASYNC = 0x100000,
149   MDB_NOTLS = 0x200000,
150   MDB_NOLOCK = 0x400000,
151   MDB_NORDAHEAD = 0x800000,
152   MDB_NOMEMINIT = 0x1000000
153 }
154 
155 enum {
156   MDB_REVERSEKEY = 0x02,
157   MDB_DUPSORT = 0x04,
158   MDB_INTEGERKEY = 0x08,
159   MDB_DUPFIXED = 0x10,
160   MDB_INTEGERDUP = 0x20,
161   MDB_REVERSEDUP = 0x40,
162   MDB_CREATE = 0x40000
163 }
164 
165 enum {
166   MDB_NOOVERWRITE = 0x10,
167   MDB_NODUPDATA = 0x20,
168   MDB_RESERVE = 0x10000,
169   MDB_APPEND = 0x20000,
170   MDB_APPENDDUP = 0x40000,
171   MDB_MULTIPLE = 0x80000
172 }
173 
174 enum /*MDB_cursor_op*/ {
175   MDB_FIRST,
176   MDB_FIRST_DUP,
177   MDB_GET_BOTH,
178   MDB_GET_BOTH_RANGE,
179   MDB_GET_CURRENT,
180   MDB_GET_MULTIPLE,
181   MDB_LAST,
182   MDB_LAST_DUP,
183   MDB_NEXT,
184   MDB_NEXT_DUP,
185   MDB_NEXT_MULTIPLE,
186   MDB_NEXT_NODUP,
187   MDB_PREV,
188   MDB_PREV_DUP,
189   MDB_PREV_NODUP,
190   MDB_SET,
191   MDB_SET_KEY,
192   MDB_SET_RANGE,
193 }
194 
195 enum {
196   MDB_SUCCESS = 0,
197   MDB_KEYEXIST = (-30799),
198   MDB_NOTFOUND = (-30798),
199   MDB_PAGE_NOTFOUND = (-30797),
200   MDB_CORRUPTED = (-30796),
201   MDB_PANIC = (-30795),
202   MDB_VERSION_MISMATCH = (-30794),
203   MDB_INVALID = (-30793),
204   MDB_MAP_FULL = (-30792),
205   MDB_DBS_FULL = (-30791),
206   MDB_READERS_FULL = (-30790),
207   MDB_TLS_FULL = (-30789),
208   MDB_TXN_FULL = (-30788),
209   MDB_CURSOR_FULL = (-30787),
210   MDB_PAGE_FULL = (-30786),
211   MDB_MAP_RESIZED = (-30785),
212   MDB_INCOMPATIBLE = (-30784),
213   MDB_BAD_RSLOT = (-30783),
214   MDB_BAD_TXN = (-30782),
215   MDB_BAD_VALSIZE = (-30781),
216   MDB_BAD_DBI = (-30780),
217   MDB_LAST_ERRCODE = MDB_BAD_DBI
218 }
219 
220 struct MDB_stat {
221   uint ms_psize;
222   uint ms_depth;
223   size_t ms_branch_pages;
224   size_t ms_leaf_pages;
225   size_t ms_overflow_pages;
226   size_t ms_entries;
227 }
228 
229 struct MDB_envinfo {
230   void* me_mapaddr;
231   size_t me_mapsize;
232   size_t me_last_pgno;
233   size_t me_last_txnid;
234   uint me_maxreaders;
235   uint me_numreaders;
236 }
237 
238 const(char)* mdb_version (int* major, int* minor, int* patch);
239 const(char)* mdb_strerror (int err);
240 
241 int mdb_env_create (MDB_env** env);
242 int mdb_env_open (MDB_env* env, const(char)* path, uint flags, mdb_mode_t mode);
243 int mdb_env_copy (MDB_env* env, const(char)* path);
244 int mdb_env_copyfd (MDB_env* env, mdb_filehandle_t fd);
245 int mdb_env_stat (MDB_env* env, MDB_stat* stat);
246 int mdb_env_info (MDB_env* env, MDB_envinfo* stat);
247 int mdb_env_sync (MDB_env* env, int force);
248 void mdb_env_close (MDB_env* env);
249 int mdb_env_set_flags (MDB_env* env, uint flags, int onoff);
250 int mdb_env_get_flags (MDB_env* env, uint* flags);
251 int mdb_env_get_path (MDB_env* env, const(char)** path);
252 int mdb_env_get_fd (MDB_env* env, mdb_filehandle_t* fd);
253 int mdb_env_set_mapsize (MDB_env* env, size_t size);
254 int mdb_env_set_maxreaders (MDB_env* env, uint readers);
255 int mdb_env_get_maxreaders (MDB_env* env, uint* readers);
256 int mdb_env_set_maxdbs (MDB_env* env, MDB_dbi dbs);
257 int mdb_env_get_maxkeysize (MDB_env* env);
258 int mdb_env_set_userctx (MDB_env* env, void* ctx);
259 void* mdb_env_get_userctx (MDB_env* env);
260 int mdb_env_set_assert (MDB_env* env, void function (MDB_env* env, const(char)* msg) func);
261 
262 int mdb_txn_begin (MDB_env* env, MDB_txn* parent, uint flags, MDB_txn** txn);
263 MDB_env* mdb_txn_env (MDB_txn* txn);
264 size_t mdb_txn_id (MDB_txn* txn);
265 int mdb_txn_commit (MDB_txn* txn);
266 void mdb_txn_abort (MDB_txn* txn);
267 void mdb_txn_reset (MDB_txn* txn);
268 int mdb_txn_renew (MDB_txn* txn);
269 
270 int mdb_dbi_open (MDB_txn* txn, const(char)* name, uint flags, MDB_dbi* dbi);
271 int mdb_stat (MDB_txn* txn, MDB_dbi dbi, MDB_stat* stat);
272 int mdb_dbi_flags (MDB_txn* txn, MDB_dbi dbi, uint* flags);
273 void mdb_dbi_close (MDB_env* env, MDB_dbi dbi);
274 int mdb_drop (MDB_txn* txn, MDB_dbi dbi, int del);
275 int mdb_set_compare (MDB_txn* txn, MDB_dbi dbi, int function (const MDB_val* a, const MDB_val* b) cmp);
276 int mdb_set_dupsort (MDB_txn* txn, MDB_dbi dbi, int function (MDB_val* a, MDB_val* b) cmp);
277 int mdb_set_relfunc (MDB_txn* txn, MDB_dbi dbi, void function (MDB_val* item, void* oldptr, void* newptr, void* relctx) rel);
278 int mdb_set_relctx (MDB_txn* txn, MDB_dbi dbi, void* ctx);
279 int mdb_get (MDB_txn* txn, MDB_dbi dbi, MDB_val* key, MDB_val* data);
280 int mdb_put (MDB_txn* txn, MDB_dbi dbi, MDB_val* key, MDB_val* data, uint flags);
281 int mdb_del (MDB_txn* txn, MDB_dbi dbi, MDB_val* key, MDB_val* data);
282 int mdb_cursor_open (MDB_txn* txn, MDB_dbi dbi, MDB_cursor** cursor);
283 void mdb_cursor_close (MDB_cursor* cursor);
284 int mdb_cursor_renew (MDB_txn* txn, MDB_cursor* cursor);
285 MDB_txn* mdb_cursor_txn (MDB_cursor* cursor);
286 MDB_dbi mdb_cursor_dbi (MDB_cursor* cursor);
287 int mdb_cursor_get (MDB_cursor* cursor, MDB_val* key, MDB_val* data, /*MDB_cursor_op*/uint op);
288 int mdb_cursor_put (MDB_cursor* cursor, MDB_val* key, MDB_val* data, uint flags);
289 int mdb_cursor_del (MDB_cursor* cursor, uint flags);
290 int mdb_cursor_count (MDB_cursor* cursor, size_t* countp);
291 int mdb_cmp (MDB_txn* txn, MDB_dbi dbi, MDB_val* a, MDB_val* b);
292 int mdb_dcmp (MDB_txn* txn, MDB_dbi dbi, MDB_val* a, MDB_val* b);
293 int mdb_reader_list (MDB_env* env, int function (const(char)* msg, void* ctx) func, void* ctx);
294 int mdb_reader_check (MDB_env* env, int* dead);