1 /* This is free and unencumbered software released into the public domain.
2
3 Original repository:
4 https://github.com/bendiken/lmdbxx
5 Adoptation for NCBI (Dmitrienko, Dmitry):
6 https://bitbucket.ncbi.nlm.nih.gov/projects/ID/repos/lmdbxx/browse
7 Adaptation for NCBI/VS2013 (Denis Vakatov):
8 here
9 Original version is available here:
10 src/util/lmdbxx/lmdb++.h.orig
11 */
12
13 #ifndef LMDBXX_H
14 #define LMDBXX_H
15
16 /**
17 * <lmdb++.h> - C++11 wrapper for LMDB.
18 *
19 * @author Arto Bendiken <arto@bendiken.net>
20 * @see https://sourceforge.net/projects/lmdbxx/
21 */
22
23 #ifndef __cplusplus
24 #error "<lmdb++.h> requires a C++ compiler"
25 #endif
26
27 #if __cplusplus < 201103L
28 #if !defined(_MSC_VER) || _MSC_VER < 1800
29 #error "<lmdb++.h> requires a C++11 compiler (CXXFLAGS='-std=c++11')"
30 #endif // _MSC_VER check
31 #endif
32
33 ////////////////////////////////////////////////////////////////////////////////
34
35 #include <lmdb.h> /* for MDB_*, mdb_*() */
36
37 #ifdef LMDBXX_DEBUG
38 #include <cassert> /* for assert() */
39 #endif
40 #include <cstddef> /* for std::size_t */
41 #include <cstdio> /* for std::snprintf() */
42 #include <cstring> /* for std::strlen() */
43 #include <stdexcept> /* for std::runtime_error */
44 #include <string> /* for std::string */
45 #include <type_traits> /* for std::is_pod<> */
46
47 #if defined(_MSC_VER) && _MSC_VER < 1900
48 # define NCBI_VS2013
49 #endif
50
51 #ifdef NCBI_VS2013
52 # define LMDB_NOEXCEPT
53 # define LMDB_CONSTEXPR const
54 # define LMDB_SNPRINTF _snprintf
55 #else
56 # define LMDB_NOEXCEPT noexcept
57 # define LMDB_CONSTEXPR constexpr
58 # define LMDB_SNPRINTF snprintf
59 #endif
60
61 namespace lmdb {
62 using mode = mdb_mode_t;
63 }
64
65 ////////////////////////////////////////////////////////////////////////////////
66 /* Error Handling */
67
68 namespace lmdb {
69 class error;
70 class logic_error;
71 class fatal_error;
72 class runtime_error;
73 class key_exist_error;
74 class not_found_error;
75 class corrupted_error;
76 class panic_error;
77 class version_mismatch_error;
78 class map_full_error;
79 class bad_dbi_error;
80 }
81
82 /**
83 * Base class for LMDB exception conditions.
84 *
85 * @see http://symas.com/mdb/doc/group__errors.html
86 */
87 class lmdb::error : public std::runtime_error {
88 protected:
89 const int _code;
90
91 #ifdef NCBI_VS2013
92 private:
93 mutable char buffer[1024];
94 #endif
95
96 public:
97 /**
98 * Throws an error based on the given LMDB return code.
99 */
100 #ifndef NCBI_VS2013
101 [[noreturn]]
102 #endif
103 static inline void raise(const char* origin, int rc);
104
105 /**
106 * Constructor.
107 */
error(const char * const origin,const int rc)108 error(const char* const origin,
109 const int rc) LMDB_NOEXCEPT
110 : runtime_error{origin},
111 _code{rc} {}
112
113 /**
114 * Returns the underlying LMDB error code.
115 */
code()116 int code() const LMDB_NOEXCEPT {
117 return _code;
118 }
119
120 /**
121 * Returns the origin of the LMDB error.
122 */
origin()123 const char* origin() const LMDB_NOEXCEPT {
124 return runtime_error::what();
125 }
126
127 /**
128 * Returns the underlying LMDB error code.
129 */
what()130 virtual const char* what() const LMDB_NOEXCEPT {
131 #ifndef NCBI_VS2013
132 static thread_local char buffer[1024];
133 #endif
134 #ifndef _MSC_VER
135 std::
136 #endif
137 LMDB_SNPRINTF(buffer, sizeof(buffer),
138 "%s: %s", origin(), ::mdb_strerror(code()));
139 return buffer;
140 }
141 };
142
143
144 /**
145 * Base class for logic error conditions.
146 */
147 class lmdb::logic_error : public lmdb::error {
148 public:
149 using error::error;
150 #ifdef NCBI_VS2013
logic_error(const char * const origin,const int rc)151 logic_error(const char* const origin,
152 const int rc) : lmdb::error(origin, rc) {}
153 #endif
154 };
155
156 /**
157 * Base class for fatal error conditions.
158 */
159 class lmdb::fatal_error : public lmdb::error {
160 public:
161 using error::error;
162 #ifdef NCBI_VS2013
fatal_error(const char * const origin,const int rc)163 fatal_error(const char* const origin,
164 const int rc) : lmdb::error(origin, rc) {}
165 #endif
166 };
167
168 /**
169 * Base class for runtime error conditions.
170 */
171 class lmdb::runtime_error : public lmdb::error {
172 public:
173 using error::error;
174 #ifdef NCBI_VS2013
runtime_error(const char * const origin,const int rc)175 runtime_error(const char* const origin,
176 const int rc) : lmdb::error(origin, rc) {}
177 #endif
178 };
179
180 /**
181 * Exception class for `MDB_KEYEXIST` errors.
182 *
183 * @see http://symas.com/mdb/doc/group__errors.html#ga05dc5bbcc7da81a7345bd8676e8e0e3b
184 */
185 class lmdb::key_exist_error final : public lmdb::runtime_error {
186 public:
187 using runtime_error::runtime_error;
188 #ifdef NCBI_VS2013
key_exist_error(const char * const origin,const int rc)189 key_exist_error(const char* const origin,
190 const int rc) : lmdb::runtime_error(origin, rc) {}
191 #endif
192 };
193
194 /**
195 * Exception class for `MDB_NOTFOUND` errors.
196 *
197 * @see http://symas.com/mdb/doc/group__errors.html#gabeb52e4c4be21b329e31c4add1b71926
198 */
199 class lmdb::not_found_error final : public lmdb::runtime_error {
200 public:
201 using runtime_error::runtime_error;
202 #ifdef NCBI_VS2013
not_found_error(const char * const origin,const int rc)203 not_found_error(const char* const origin,
204 const int rc) : lmdb::runtime_error(origin, rc) {}
205 #endif
206 };
207
208 /**
209 * Exception class for `MDB_CORRUPTED` errors.
210 *
211 * @see http://symas.com/mdb/doc/group__errors.html#gaf8148bf1b85f58e264e57194bafb03ef
212 */
213 class lmdb::corrupted_error final : public lmdb::fatal_error {
214 public:
215 using fatal_error::fatal_error;
216 #ifdef NCBI_VS2013
corrupted_error(const char * const origin,const int rc)217 corrupted_error(const char* const origin,
218 const int rc) : lmdb::fatal_error(origin, rc) {}
219 #endif
220 };
221
222 /**
223 * Exception class for `MDB_PANIC` errors.
224 *
225 * @see http://symas.com/mdb/doc/group__errors.html#gae37b9aedcb3767faba3de8c1cf6d3473
226 */
227 class lmdb::panic_error final : public lmdb::fatal_error {
228 public:
229 using fatal_error::fatal_error;
230 #ifdef NCBI_VS2013
panic_error(const char * const origin,const int rc)231 panic_error(const char* const origin,
232 const int rc) : lmdb::fatal_error(origin, rc) {}
233 #endif
234 };
235
236 /**
237 * Exception class for `MDB_VERSION_MISMATCH` errors.
238 *
239 * @see http://symas.com/mdb/doc/group__errors.html#ga909b2db047fa90fb0d37a78f86a6f99b
240 */
241 class lmdb::version_mismatch_error final : public lmdb::fatal_error {
242 public:
243 using fatal_error::fatal_error;
244 #ifdef NCBI_VS2013
version_mismatch_error(const char * const origin,const int rc)245 version_mismatch_error(const char* const origin,
246 const int rc) : lmdb::fatal_error(origin, rc) {}
247 #endif
248 };
249
250 /**
251 * Exception class for `MDB_MAP_FULL` errors.
252 *
253 * @see http://symas.com/mdb/doc/group__errors.html#ga0a83370402a060c9175100d4bbfb9f25
254 */
255 class lmdb::map_full_error final : public lmdb::runtime_error {
256 public:
257 using runtime_error::runtime_error;
258 #ifdef NCBI_VS2013
map_full_error(const char * const origin,const int rc)259 map_full_error(const char* const origin,
260 const int rc) : lmdb::runtime_error(origin, rc) {}
261 #endif
262 };
263
264 /**
265 * Exception class for `MDB_BAD_DBI` errors.
266 *
267 * @since 0.9.14 (2014/09/20)
268 * @see http://symas.com/mdb/doc/group__errors.html#gab4c82e050391b60a18a5df08d22a7083
269 */
270 class lmdb::bad_dbi_error final : public lmdb::runtime_error {
271 public:
272 using runtime_error::runtime_error;
273 #ifdef NCBI_VS2013
bad_dbi_error(const char * const origin,const int rc)274 bad_dbi_error(const char* const origin,
275 const int rc) : lmdb::runtime_error(origin, rc) {}
276 #endif
277 };
278
279 inline void
raise(const char * const origin,const int rc)280 lmdb::error::raise(const char* const origin,
281 const int rc) {
282 switch (rc) {
283 case MDB_KEYEXIST: throw key_exist_error{origin, rc};
284 case MDB_NOTFOUND: throw not_found_error{origin, rc};
285 case MDB_CORRUPTED: throw corrupted_error{origin, rc};
286 case MDB_PANIC: throw panic_error{origin, rc};
287 case MDB_VERSION_MISMATCH: throw version_mismatch_error{origin, rc};
288 case MDB_MAP_FULL: throw map_full_error{origin, rc};
289 #ifdef MDB_BAD_DBI
290 case MDB_BAD_DBI: throw bad_dbi_error{origin, rc};
291 #endif
292 default: throw lmdb::runtime_error{origin, rc};
293 }
294 }
295
296 ////////////////////////////////////////////////////////////////////////////////
297 /* Procedural Interface: Metadata */
298
299 namespace lmdb {
300 // TODO: mdb_version()
301 // TODO: mdb_strerror()
302 }
303
304 ////////////////////////////////////////////////////////////////////////////////
305 /* Procedural Interface: Environment */
306
307 namespace lmdb {
308 static inline void env_create(MDB_env** env);
309 static inline void env_open(MDB_env* env,
310 const char* path, unsigned int flags, mode mode);
311 #if MDB_VERSION_FULL >= MDB_VERINT(0, 9, 14)
312 static inline void env_copy(MDB_env* env, const char* path, unsigned int flags);
313 static inline void env_copy_fd(MDB_env* env, mdb_filehandle_t fd, unsigned int flags);
314 #else
315 static inline void env_copy(MDB_env* env, const char* path);
316 static inline void env_copy_fd(MDB_env* env, mdb_filehandle_t fd);
317 #endif
318 static inline void env_stat(MDB_env* env, MDB_stat* stat);
319 static inline void env_info(MDB_env* env, MDB_envinfo* stat);
320 static inline void env_sync(MDB_env* env, bool force);
321 static inline void env_close(MDB_env* env) LMDB_NOEXCEPT;
322 static inline void env_set_flags(MDB_env* env, unsigned int flags, bool onoff);
323 static inline void env_get_flags(MDB_env* env, unsigned int* flags);
324 static inline void env_get_path(MDB_env* env, const char** path);
325 static inline void env_get_fd(MDB_env* env, mdb_filehandle_t* fd);
326 static inline void env_set_mapsize(MDB_env* env, std::size_t size);
327 static inline void env_set_max_readers(MDB_env* env, unsigned int count);
328 static inline void env_get_max_readers(MDB_env* env, unsigned int* count);
329 static inline void env_set_max_dbs(MDB_env* env, MDB_dbi count);
330 static inline unsigned int env_get_max_keysize(MDB_env* env);
331 #if MDB_VERSION_FULL >= MDB_VERINT(0, 9, 11)
332 static inline void env_set_userctx(MDB_env* env, void* ctx);
333 static inline void* env_get_userctx(MDB_env* env);
334 #endif
335 // TODO: mdb_env_set_assert()
336 // TODO: mdb_reader_list()
337 // TODO: mdb_reader_check()
338 }
339
340 /**
341 * @throws lmdb::error on failure
342 * @see http://symas.com/mdb/doc/group__mdb.html#gaad6be3d8dcd4ea01f8df436f41d158d4
343 */
344 static inline void
env_create(MDB_env ** env)345 lmdb::env_create(MDB_env** env) {
346 const int rc = ::mdb_env_create(env);
347 if (rc != MDB_SUCCESS) {
348 error::raise("mdb_env_create", rc);
349 }
350 }
351
352 /**
353 * @throws lmdb::error on failure
354 * @see http://symas.com/mdb/doc/group__mdb.html#ga32a193c6bf4d7d5c5d579e71f22e9340
355 */
356 static inline void
env_open(MDB_env * const env,const char * const path,const unsigned int flags,const mode mode)357 lmdb::env_open(MDB_env* const env,
358 const char* const path,
359 const unsigned int flags,
360 const mode mode) {
361 const int rc = ::mdb_env_open(env, path, flags, mode);
362 if (rc != MDB_SUCCESS) {
363 error::raise("mdb_env_open", rc);
364 }
365 }
366
367 /**
368 * @throws lmdb::error on failure
369 * @see http://symas.com/mdb/doc/group__mdb.html#ga3bf50d7793b36aaddf6b481a44e24244
370 * @see http://symas.com/mdb/doc/group__mdb.html#ga5d51d6130325f7353db0955dbedbc378
371 */
372 static inline void
373 lmdb::env_copy(MDB_env* const env,
374 #if MDB_VERSION_FULL >= MDB_VERINT(0, 9, 14)
375 const char* const path,
376 const unsigned int flags = 0) {
377 const int rc = ::mdb_env_copy2(env, path, flags);
378 #else
379 const char* const path) {
380 const int rc = ::mdb_env_copy(env, path);
381 #endif
382 if (rc != MDB_SUCCESS) {
383 error::raise("mdb_env_copy2", rc);
384 }
385 }
386
387 /**
388 * @throws lmdb::error on failure
389 * @see http://symas.com/mdb/doc/group__mdb.html#ga5040d0de1f14000fa01fc0b522ff1f86
390 * @see http://symas.com/mdb/doc/group__mdb.html#ga470b0bcc64ac417de5de5930f20b1a28
391 */
392 static inline void
393 lmdb::env_copy_fd(MDB_env* const env,
394 #if MDB_VERSION_FULL >= MDB_VERINT(0, 9, 14)
395 const mdb_filehandle_t fd,
396 const unsigned int flags = 0) {
397 const int rc = ::mdb_env_copyfd2(env, fd, flags);
398 #else
399 const mdb_filehandle_t fd) {
400 const int rc = ::mdb_env_copyfd(env, fd);
401 #endif
402 if (rc != MDB_SUCCESS) {
403 error::raise("mdb_env_copyfd2", rc);
404 }
405 }
406
407 /**
408 * @throws lmdb::error on failure
409 * @see http://symas.com/mdb/doc/group__mdb.html#gaf881dca452050efbd434cd16e4bae255
410 */
411 static inline void
env_stat(MDB_env * const env,MDB_stat * const stat)412 lmdb::env_stat(MDB_env* const env,
413 MDB_stat* const stat) {
414 const int rc = ::mdb_env_stat(env, stat);
415 if (rc != MDB_SUCCESS) {
416 error::raise("mdb_env_stat", rc);
417 }
418 }
419
420 /**
421 * @throws lmdb::error on failure
422 * @see http://symas.com/mdb/doc/group__mdb.html#ga18769362c7e7d6cf91889a028a5c5947
423 */
424 static inline void
env_info(MDB_env * const env,MDB_envinfo * const stat)425 lmdb::env_info(MDB_env* const env,
426 MDB_envinfo* const stat) {
427 const int rc = ::mdb_env_info(env, stat);
428 if (rc != MDB_SUCCESS) {
429 error::raise("mdb_env_info", rc);
430 }
431 }
432
433 /**
434 * @throws lmdb::error on failure
435 * @see http://symas.com/mdb/doc/group__mdb.html#ga85e61f05aa68b520cc6c3b981dba5037
436 */
437 static inline void
438 lmdb::env_sync(MDB_env* const env,
439 const bool force = true) {
440 const int rc = ::mdb_env_sync(env, force);
441 if (rc != MDB_SUCCESS) {
442 error::raise("mdb_env_sync", rc);
443 }
444 }
445
446 /**
447 * @see http://symas.com/mdb/doc/group__mdb.html#ga4366c43ada8874588b6a62fbda2d1e95
448 */
449 static inline void
env_close(MDB_env * const env)450 lmdb::env_close(MDB_env* const env) LMDB_NOEXCEPT {
451 ::mdb_env_close(env);
452 }
453
454 /**
455 * @throws lmdb::error on failure
456 * @see http://symas.com/mdb/doc/group__mdb.html#ga83f66cf02bfd42119451e9468dc58445
457 */
458 static inline void
459 lmdb::env_set_flags(MDB_env* const env,
460 const unsigned int flags,
461 const bool onoff = true) {
462 const int rc = ::mdb_env_set_flags(env, flags, onoff ? 1 : 0);
463 if (rc != MDB_SUCCESS) {
464 error::raise("mdb_env_set_flags", rc);
465 }
466 }
467
468 /**
469 * @throws lmdb::error on failure
470 * @see http://symas.com/mdb/doc/group__mdb.html#ga2733aefc6f50beb49dd0c6eb19b067d9
471 */
472 static inline void
env_get_flags(MDB_env * const env,unsigned int * const flags)473 lmdb::env_get_flags(MDB_env* const env,
474 unsigned int* const flags) {
475 const int rc = ::mdb_env_get_flags(env, flags);
476 if (rc != MDB_SUCCESS) {
477 error::raise("mdb_env_get_flags", rc);
478 }
479 }
480
481 /**
482 * @throws lmdb::error on failure
483 * @see http://symas.com/mdb/doc/group__mdb.html#gac699fdd8c4f8013577cb933fb6a757fe
484 */
485 static inline void
env_get_path(MDB_env * const env,const char ** path)486 lmdb::env_get_path(MDB_env* const env,
487 const char** path) {
488 const int rc = ::mdb_env_get_path(env, path);
489 if (rc != MDB_SUCCESS) {
490 error::raise("mdb_env_get_path", rc);
491 }
492 }
493
494 /**
495 * @throws lmdb::error on failure
496 * @see http://symas.com/mdb/doc/group__mdb.html#gaf1570e7c0e5a5d860fef1032cec7d5f2
497 */
498 static inline void
env_get_fd(MDB_env * const env,mdb_filehandle_t * const fd)499 lmdb::env_get_fd(MDB_env* const env,
500 mdb_filehandle_t* const fd) {
501 const int rc = ::mdb_env_get_fd(env, fd);
502 if (rc != MDB_SUCCESS) {
503 error::raise("mdb_env_get_fd", rc);
504 }
505 }
506
507 /**
508 * @throws lmdb::error on failure
509 * @see http://symas.com/mdb/doc/group__mdb.html#gaa2506ec8dab3d969b0e609cd82e619e5
510 */
511 static inline void
env_set_mapsize(MDB_env * const env,const std::size_t size)512 lmdb::env_set_mapsize(MDB_env* const env,
513 const std::size_t size) {
514 const int rc = ::mdb_env_set_mapsize(env, size);
515 if (rc != MDB_SUCCESS) {
516 error::raise("mdb_env_set_mapsize", rc);
517 }
518 }
519
520 /**
521 * @throws lmdb::error on failure
522 * @see http://symas.com/mdb/doc/group__mdb.html#gae687966c24b790630be2a41573fe40e2
523 */
524 static inline void
env_set_max_readers(MDB_env * const env,const unsigned int count)525 lmdb::env_set_max_readers(MDB_env* const env,
526 const unsigned int count) {
527 const int rc = ::mdb_env_set_maxreaders(env, count);
528 if (rc != MDB_SUCCESS) {
529 error::raise("mdb_env_set_maxreaders", rc);
530 }
531 }
532
533 /**
534 * @throws lmdb::error on failure
535 * @see http://symas.com/mdb/doc/group__mdb.html#ga70e143cf11760d869f754c9c9956e6cc
536 */
537 static inline void
env_get_max_readers(MDB_env * const env,unsigned int * const count)538 lmdb::env_get_max_readers(MDB_env* const env,
539 unsigned int* const count) {
540 const int rc = ::mdb_env_get_maxreaders(env, count);
541 if (rc != MDB_SUCCESS) {
542 error::raise("mdb_env_get_maxreaders", rc);
543 }
544 }
545
546 /**
547 * @throws lmdb::error on failure
548 * @see http://symas.com/mdb/doc/group__mdb.html#gaa2fc2f1f37cb1115e733b62cab2fcdbc
549 */
550 static inline void
env_set_max_dbs(MDB_env * const env,const MDB_dbi count)551 lmdb::env_set_max_dbs(MDB_env* const env,
552 const MDB_dbi count) {
553 const int rc = ::mdb_env_set_maxdbs(env, count);
554 if (rc != MDB_SUCCESS) {
555 error::raise("mdb_env_set_maxdbs", rc);
556 }
557 }
558
559 /**
560 * @see http://symas.com/mdb/doc/group__mdb.html#gaaf0be004f33828bf2fb09d77eb3cef94
561 */
562 static inline unsigned int
env_get_max_keysize(MDB_env * const env)563 lmdb::env_get_max_keysize(MDB_env* const env) {
564 const int rc = ::mdb_env_get_maxkeysize(env);
565 #ifdef LMDBXX_DEBUG
566 assert(rc >= 0);
567 #endif
568 return static_cast<unsigned int>(rc);
569 }
570
571 #if MDB_VERSION_FULL >= MDB_VERINT(0, 9, 11)
572 /**
573 * @throws lmdb::error on failure
574 * @since 0.9.11 (2014/01/15)
575 * @see http://symas.com/mdb/doc/group__mdb.html#gaf2fe09eb9c96eeb915a76bf713eecc46
576 */
577 static inline void
env_set_userctx(MDB_env * const env,void * const ctx)578 lmdb::env_set_userctx(MDB_env* const env,
579 void* const ctx) {
580 const int rc = ::mdb_env_set_userctx(env, ctx);
581 if (rc != MDB_SUCCESS) {
582 error::raise("mdb_env_set_userctx", rc);
583 }
584 }
585 #endif
586
587 #if MDB_VERSION_FULL >= MDB_VERINT(0, 9, 11)
588 /**
589 * @since 0.9.11 (2014/01/15)
590 * @see http://symas.com/mdb/doc/group__mdb.html#ga45df6a4fb150cda2316b5ae224ba52f1
591 */
592 static inline void*
env_get_userctx(MDB_env * const env)593 lmdb::env_get_userctx(MDB_env* const env) {
594 return ::mdb_env_get_userctx(env);
595 }
596 #endif
597
598 ////////////////////////////////////////////////////////////////////////////////
599 /* Procedural Interface: Transactions */
600
601 namespace lmdb {
602 static inline void txn_begin(
603 MDB_env* env, MDB_txn* parent, unsigned int flags, MDB_txn** txn);
604 static inline MDB_env* txn_env(MDB_txn* txn) LMDB_NOEXCEPT;
605 #ifdef LMDBXX_TXN_ID
606 static inline std::size_t txn_id(MDB_txn* txn) LMDB_NOEXCEPT;
607 #endif
608 static inline void txn_commit(MDB_txn* txn);
609 static inline void txn_abort(MDB_txn* txn) LMDB_NOEXCEPT;
610 static inline void txn_reset(MDB_txn* txn) LMDB_NOEXCEPT;
611 static inline void txn_renew(MDB_txn* txn);
612 }
613
614 /**
615 * @throws lmdb::error on failure
616 * @see http://symas.com/mdb/doc/group__mdb.html#gad7ea55da06b77513609efebd44b26920
617 */
618 static inline void
txn_begin(MDB_env * const env,MDB_txn * const parent,const unsigned int flags,MDB_txn ** txn)619 lmdb::txn_begin(MDB_env* const env,
620 MDB_txn* const parent,
621 const unsigned int flags,
622 MDB_txn** txn) {
623 const int rc = ::mdb_txn_begin(env, parent, flags, txn);
624 if (rc != MDB_SUCCESS) {
625 error::raise("mdb_txn_begin", rc);
626 }
627 }
628
629 /**
630 * @see http://symas.com/mdb/doc/group__mdb.html#gaeb17735b8aaa2938a78a45cab85c06a0
631 */
632 static inline MDB_env*
txn_env(MDB_txn * const txn)633 lmdb::txn_env(MDB_txn* const txn) LMDB_NOEXCEPT {
634 return ::mdb_txn_env(txn);
635 }
636
637 #ifdef LMDBXX_TXN_ID
638 /**
639 * @note Only available in HEAD, not yet in any 0.9.x release (as of 0.9.16).
640 */
641 static inline std::size_t
txn_id(MDB_txn * const txn)642 lmdb::txn_id(MDB_txn* const txn) LMDB_NOEXCEPT {
643 return ::mdb_txn_id(txn);
644 }
645 #endif
646
647 /**
648 * @throws lmdb::error on failure
649 * @see http://symas.com/mdb/doc/group__mdb.html#ga846fbd6f46105617ac9f4d76476f6597
650 */
651 static inline void
txn_commit(MDB_txn * const txn)652 lmdb::txn_commit(MDB_txn* const txn) {
653 const int rc = ::mdb_txn_commit(txn);
654 if (rc != MDB_SUCCESS) {
655 error::raise("mdb_txn_commit", rc);
656 }
657 }
658
659 /**
660 * @see http://symas.com/mdb/doc/group__mdb.html#ga73a5938ae4c3239ee11efa07eb22b882
661 */
662 static inline void
txn_abort(MDB_txn * const txn)663 lmdb::txn_abort(MDB_txn* const txn) LMDB_NOEXCEPT {
664 ::mdb_txn_abort(txn);
665 }
666
667 /**
668 * @see http://symas.com/mdb/doc/group__mdb.html#ga02b06706f8a66249769503c4e88c56cd
669 */
670 static inline void
txn_reset(MDB_txn * const txn)671 lmdb::txn_reset(MDB_txn* const txn) LMDB_NOEXCEPT {
672 ::mdb_txn_reset(txn);
673 }
674
675 /**
676 * @throws lmdb::error on failure
677 * @see http://symas.com/mdb/doc/group__mdb.html#ga6c6f917959517ede1c504cf7c720ce6d
678 */
679 static inline void
txn_renew(MDB_txn * const txn)680 lmdb::txn_renew(MDB_txn* const txn) {
681 const int rc = ::mdb_txn_renew(txn);
682 if (rc != MDB_SUCCESS) {
683 error::raise("mdb_txn_renew", rc);
684 }
685 }
686
687 ////////////////////////////////////////////////////////////////////////////////
688 /* Procedural Interface: Databases */
689
690 namespace lmdb {
691 static inline void dbi_open(
692 MDB_txn* txn, const char* name, unsigned int flags, MDB_dbi* dbi);
693 static inline void dbi_stat(MDB_txn* txn, MDB_dbi dbi, MDB_stat* stat);
694 static inline void dbi_flags(MDB_txn* txn, MDB_dbi dbi, unsigned int* flags);
695 static inline void dbi_close(MDB_env* env, MDB_dbi dbi) LMDB_NOEXCEPT;
696 static inline void dbi_drop(MDB_txn* txn, MDB_dbi dbi, bool del);
697 static inline void dbi_set_compare(MDB_txn* txn, MDB_dbi dbi, MDB_cmp_func* cmp);
698 static inline void dbi_set_dupsort(MDB_txn* txn, MDB_dbi dbi, MDB_cmp_func* cmp);
699 static inline void dbi_set_relfunc(MDB_txn* txn, MDB_dbi dbi, MDB_rel_func* rel);
700 static inline void dbi_set_relctx(MDB_txn* txn, MDB_dbi dbi, void* ctx);
701 static inline bool dbi_get(MDB_txn* txn, MDB_dbi dbi, const MDB_val* key, MDB_val* data);
702 static inline bool dbi_put(MDB_txn* txn, MDB_dbi dbi, const MDB_val* key, const MDB_val* data, unsigned int flags);
703 static inline bool dbi_del(MDB_txn* txn, MDB_dbi dbi, const MDB_val* key, const MDB_val* data);
704 // TODO: mdb_cmp()
705 // TODO: mdb_dcmp()
706 }
707
708 /**
709 * @throws lmdb::error on failure
710 * @see http://symas.com/mdb/doc/group__mdb.html#gac08cad5b096925642ca359a6d6f0562a
711 */
712 static inline void
dbi_open(MDB_txn * const txn,const char * const name,const unsigned int flags,MDB_dbi * const dbi)713 lmdb::dbi_open(MDB_txn* const txn,
714 const char* const name,
715 const unsigned int flags,
716 MDB_dbi* const dbi) {
717 const int rc = ::mdb_dbi_open(txn, name, flags, dbi);
718 if (rc != MDB_SUCCESS) {
719 error::raise("mdb_dbi_open", rc);
720 }
721 }
722
723 /**
724 * @throws lmdb::error on failure
725 * @see http://symas.com/mdb/doc/group__mdb.html#gae6c1069febe94299769dbdd032fadef6
726 */
727 static inline void
dbi_stat(MDB_txn * const txn,const MDB_dbi dbi,MDB_stat * const result)728 lmdb::dbi_stat(MDB_txn* const txn,
729 const MDB_dbi dbi,
730 MDB_stat* const result) {
731 const int rc = ::mdb_stat(txn, dbi, result);
732 if (rc != MDB_SUCCESS) {
733 error::raise("mdb_stat", rc);
734 }
735 }
736
737 /**
738 * @throws lmdb::error on failure
739 * @see http://symas.com/mdb/doc/group__mdb.html#ga95ba4cb721035478a8705e57b91ae4d4
740 */
741 static inline void
dbi_flags(MDB_txn * const txn,const MDB_dbi dbi,unsigned int * const flags)742 lmdb::dbi_flags(MDB_txn* const txn,
743 const MDB_dbi dbi,
744 unsigned int* const flags) {
745 const int rc = ::mdb_dbi_flags(txn, dbi, flags);
746 if (rc != MDB_SUCCESS) {
747 error::raise("mdb_dbi_flags", rc);
748 }
749 }
750
751 /**
752 * @see http://symas.com/mdb/doc/group__mdb.html#ga52dd98d0c542378370cd6b712ff961b5
753 */
754 static inline void
dbi_close(MDB_env * const env,const MDB_dbi dbi)755 lmdb::dbi_close(MDB_env* const env,
756 const MDB_dbi dbi) LMDB_NOEXCEPT {
757 ::mdb_dbi_close(env, dbi);
758 }
759
760 /**
761 * @see http://symas.com/mdb/doc/group__mdb.html#gab966fab3840fc54a6571dfb32b00f2db
762 */
763 static inline void
764 lmdb::dbi_drop(MDB_txn* const txn,
765 const MDB_dbi dbi,
766 const bool del = false) {
767 const int rc = ::mdb_drop(txn, dbi, del ? 1 : 0);
768 if (rc != MDB_SUCCESS) {
769 error::raise("mdb_drop", rc);
770 }
771 }
772
773 /**
774 * @throws lmdb::error on failure
775 * @see http://symas.com/mdb/doc/group__mdb.html#ga68e47ffcf72eceec553c72b1784ee0fe
776 */
777 static inline void
778 lmdb::dbi_set_compare(MDB_txn* const txn,
779 const MDB_dbi dbi,
780 MDB_cmp_func* const cmp = nullptr) {
781 const int rc = ::mdb_set_compare(txn, dbi, cmp);
782 if (rc != MDB_SUCCESS) {
783 error::raise("mdb_set_compare", rc);
784 }
785 }
786
787 /**
788 * @throws lmdb::error on failure
789 * @see http://symas.com/mdb/doc/group__mdb.html#gacef4ec3dab0bbd9bc978b73c19c879ae
790 */
791 static inline void
792 lmdb::dbi_set_dupsort(MDB_txn* const txn,
793 const MDB_dbi dbi,
794 MDB_cmp_func* const cmp = nullptr) {
795 const int rc = ::mdb_set_dupsort(txn, dbi, cmp);
796 if (rc != MDB_SUCCESS) {
797 error::raise("mdb_set_dupsort", rc);
798 }
799 }
800
801 /**
802 * @throws lmdb::error on failure
803 * @see http://symas.com/mdb/doc/group__mdb.html#ga697d82c7afe79f142207ad5adcdebfeb
804 */
805 static inline void
dbi_set_relfunc(MDB_txn * const txn,const MDB_dbi dbi,MDB_rel_func * const rel)806 lmdb::dbi_set_relfunc(MDB_txn* const txn,
807 const MDB_dbi dbi,
808 MDB_rel_func* const rel) {
809 const int rc = ::mdb_set_relfunc(txn, dbi, rel);
810 if (rc != MDB_SUCCESS) {
811 error::raise("mdb_set_relfunc", rc);
812 }
813 }
814
815 /**
816 * @throws lmdb::error on failure
817 * @see http://symas.com/mdb/doc/group__mdb.html#ga7c34246308cee01724a1839a8f5cc594
818 */
819 static inline void
dbi_set_relctx(MDB_txn * const txn,const MDB_dbi dbi,void * const ctx)820 lmdb::dbi_set_relctx(MDB_txn* const txn,
821 const MDB_dbi dbi,
822 void* const ctx) {
823 const int rc = ::mdb_set_relctx(txn, dbi, ctx);
824 if (rc != MDB_SUCCESS) {
825 error::raise("mdb_set_relctx", rc);
826 }
827 }
828
829 /**
830 * @retval true if the key/value pair was retrieved
831 * @retval false if the key wasn't found
832 * @see http://symas.com/mdb/doc/group__mdb.html#ga8bf10cd91d3f3a83a34d04ce6b07992d
833 */
834 static inline bool
dbi_get(MDB_txn * const txn,const MDB_dbi dbi,const MDB_val * const key,MDB_val * const data)835 lmdb::dbi_get(MDB_txn* const txn,
836 const MDB_dbi dbi,
837 const MDB_val* const key,
838 MDB_val* const data) {
839 const int rc = ::mdb_get(txn, dbi, const_cast<MDB_val*>(key), data);
840 if (rc != MDB_SUCCESS && rc != MDB_NOTFOUND) {
841 error::raise("mdb_get", rc);
842 }
843 return (rc == MDB_SUCCESS);
844 }
845
846 /**
847 * @retval true if the key/value pair was inserted
848 * @retval false if the key already existed
849 * @see http://symas.com/mdb/doc/group__mdb.html#ga4fa8573d9236d54687c61827ebf8cac0
850 */
851 static inline bool
852 lmdb::dbi_put(MDB_txn* const txn,
853 const MDB_dbi dbi,
854 const MDB_val* const key,
855 const MDB_val* const data,
856 const unsigned int flags = 0) {
857 const int rc = ::mdb_put(txn, dbi, const_cast<MDB_val*>(key), const_cast<MDB_val*>(data), flags);
858 if (rc != MDB_SUCCESS && rc != MDB_KEYEXIST) {
859 error::raise("mdb_put", rc);
860 }
861 return (rc == MDB_SUCCESS);
862 }
863
864 /**
865 * @retval true if the key/value pair was removed
866 * @retval false if the key wasn't found
867 * @see http://symas.com/mdb/doc/group__mdb.html#gab8182f9360ea69ac0afd4a4eaab1ddb0
868 */
869 static inline bool
870 lmdb::dbi_del(MDB_txn* const txn,
871 const MDB_dbi dbi,
872 const MDB_val* const key,
873 const MDB_val* const data = nullptr) {
874 const int rc = ::mdb_del(txn, dbi, const_cast<MDB_val*>(key), const_cast<MDB_val*>(data));
875 if (rc != MDB_SUCCESS && rc != MDB_NOTFOUND) {
876 error::raise("mdb_del", rc);
877 }
878 return (rc == MDB_SUCCESS);
879 }
880
881 ////////////////////////////////////////////////////////////////////////////////
882 /* Procedural Interface: Cursors */
883
884 namespace lmdb {
885 static inline void cursor_open(MDB_txn* txn, MDB_dbi dbi, MDB_cursor** cursor);
886 static inline void cursor_close(MDB_cursor* cursor) LMDB_NOEXCEPT;
887 static inline void cursor_renew(MDB_txn* txn, MDB_cursor* cursor);
888 static inline MDB_txn* cursor_txn(MDB_cursor* cursor) LMDB_NOEXCEPT;
889 static inline MDB_dbi cursor_dbi(MDB_cursor* cursor) LMDB_NOEXCEPT;
890 static inline bool cursor_get(MDB_cursor* cursor, MDB_val* key, MDB_val* data, MDB_cursor_op op);
891 static inline void cursor_put(MDB_cursor* cursor, MDB_val* key, MDB_val* data, unsigned int flags);
892 static inline void cursor_del(MDB_cursor* cursor, unsigned int flags);
893 static inline void cursor_count(MDB_cursor* cursor, std::size_t& count);
894 }
895
896 /**
897 * @throws lmdb::error on failure
898 * @see http://symas.com/mdb/doc/group__mdb.html#ga9ff5d7bd42557fd5ee235dc1d62613aa
899 */
900 static inline void
cursor_open(MDB_txn * const txn,const MDB_dbi dbi,MDB_cursor ** const cursor)901 lmdb::cursor_open(MDB_txn* const txn,
902 const MDB_dbi dbi,
903 MDB_cursor** const cursor) {
904 const int rc = ::mdb_cursor_open(txn, dbi, cursor);
905 if (rc != MDB_SUCCESS) {
906 error::raise("mdb_cursor_open", rc);
907 }
908 }
909
910 /**
911 * @see http://symas.com/mdb/doc/group__mdb.html#gad685f5d73c052715c7bd859cc4c05188
912 */
913 static inline void
cursor_close(MDB_cursor * const cursor)914 lmdb::cursor_close(MDB_cursor* const cursor) LMDB_NOEXCEPT {
915 ::mdb_cursor_close(cursor);
916 }
917
918 /**
919 * @throws lmdb::error on failure
920 * @see http://symas.com/mdb/doc/group__mdb.html#gac8b57befb68793070c85ea813df481af
921 */
922 static inline void
cursor_renew(MDB_txn * const txn,MDB_cursor * const cursor)923 lmdb::cursor_renew(MDB_txn* const txn,
924 MDB_cursor* const cursor) {
925 const int rc = ::mdb_cursor_renew(txn, cursor);
926 if (rc != MDB_SUCCESS) {
927 error::raise("mdb_cursor_renew", rc);
928 }
929 }
930
931 /**
932 * @see http://symas.com/mdb/doc/group__mdb.html#ga7bf0d458f7f36b5232fcb368ebda79e0
933 */
934 static inline MDB_txn*
cursor_txn(MDB_cursor * const cursor)935 lmdb::cursor_txn(MDB_cursor* const cursor) LMDB_NOEXCEPT {
936 return ::mdb_cursor_txn(cursor);
937 }
938
939 /**
940 * @see http://symas.com/mdb/doc/group__mdb.html#ga2f7092cf70ee816fb3d2c3267a732372
941 */
942 static inline MDB_dbi
cursor_dbi(MDB_cursor * const cursor)943 lmdb::cursor_dbi(MDB_cursor* const cursor) LMDB_NOEXCEPT {
944 return ::mdb_cursor_dbi(cursor);
945 }
946
947 /**
948 * @throws lmdb::error on failure
949 * @see http://symas.com/mdb/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0
950 */
951 static inline bool
cursor_get(MDB_cursor * const cursor,MDB_val * const key,MDB_val * const data,const MDB_cursor_op op)952 lmdb::cursor_get(MDB_cursor* const cursor,
953 MDB_val* const key,
954 MDB_val* const data,
955 const MDB_cursor_op op) {
956 const int rc = ::mdb_cursor_get(cursor, key, data, op);
957 if (rc != MDB_SUCCESS && rc != MDB_NOTFOUND) {
958 error::raise("mdb_cursor_get", rc);
959 }
960 return (rc == MDB_SUCCESS);
961 }
962
963 /**
964 * @throws lmdb::error on failure
965 * @see http://symas.com/mdb/doc/group__mdb.html#ga1f83ccb40011837ff37cc32be01ad91e
966 */
967 static inline void
968 lmdb::cursor_put(MDB_cursor* const cursor,
969 MDB_val* const key,
970 MDB_val* const data,
971 const unsigned int flags = 0) {
972 const int rc = ::mdb_cursor_put(cursor, key, data, flags);
973 if (rc != MDB_SUCCESS) {
974 error::raise("mdb_cursor_put", rc);
975 }
976 }
977
978 /**
979 * @throws lmdb::error on failure
980 * @see http://symas.com/mdb/doc/group__mdb.html#ga26a52d3efcfd72e5bf6bd6960bf75f95
981 */
982 static inline void
983 lmdb::cursor_del(MDB_cursor* const cursor,
984 const unsigned int flags = 0) {
985 const int rc = ::mdb_cursor_del(cursor, flags);
986 if (rc != MDB_SUCCESS) {
987 error::raise("mdb_cursor_del", rc);
988 }
989 }
990
991 /**
992 * @throws lmdb::error on failure
993 * @see http://symas.com/mdb/doc/group__mdb.html#ga4041fd1e1862c6b7d5f10590b86ffbe2
994 */
995 static inline void
cursor_count(MDB_cursor * const cursor,std::size_t & count)996 lmdb::cursor_count(MDB_cursor* const cursor,
997 std::size_t& count) {
998 const int rc = ::mdb_cursor_count(cursor, &count);
999 if (rc != MDB_SUCCESS) {
1000 error::raise("mdb_cursor_count", rc);
1001 }
1002 }
1003
1004 ////////////////////////////////////////////////////////////////////////////////
1005 /* Resource Interface: Values */
1006
1007 namespace lmdb {
1008 class val;
1009 }
1010
1011 /**
1012 * Wrapper class for `MDB_val` structures.
1013 *
1014 * @note Instances of this class are movable and copyable both.
1015 * @see http://symas.com/mdb/doc/group__mdb.html#structMDB__val
1016 */
1017 class lmdb::val {
1018 protected:
1019 MDB_val _val;
1020
1021 public:
1022 /**
1023 * Default constructor.
1024 */
1025 val() LMDB_NOEXCEPT = default;
1026
1027 /**
1028 * Constructor.
1029 */
val(const std::string & data)1030 val(const std::string& data) LMDB_NOEXCEPT
1031 : val{data.data(), data.size()} {}
1032
1033 /**
1034 * Constructor.
1035 */
val(const std::string::const_iterator & data,std::size_t len)1036 val(const std::string::const_iterator& data, std::size_t len) LMDB_NOEXCEPT
1037 : val{&*data, len} {}
1038
1039 /**
1040 * Template Constructor.
1041 */
1042 template<typename T>
val(const T & t)1043 val(const T& t) LMDB_NOEXCEPT
1044 : val{&t, sizeof(T)} {}
1045
1046 /**
1047 * Constructor.
1048 */
val(const char * const data)1049 val(const char* const data) LMDB_NOEXCEPT
1050 : val{data, std::strlen(data)} {}
1051
1052 /**
1053 * Constructor.
1054 */
val(const void * const data,const std::size_t size)1055 val(const void* const data,
1056 const std::size_t size) LMDB_NOEXCEPT
1057 #ifndef NCBI_VS2013
1058 : _val{size, const_cast<void*>(data)} {}
1059 #else
1060 { _val.mv_size = size; _val.mv_data = const_cast<void*>(data); }
1061 #endif
1062
1063 #ifndef NCBI_VS2013
1064 /**
1065 * Move constructor.
1066 */
1067 val(val&& other) LMDB_NOEXCEPT = default;
1068 #endif
1069
1070 /**
1071 * Copy constructor.
1072 */
1073 val(const val& other) LMDB_NOEXCEPT = default;
1074
1075 #ifndef NCBI_VS2013
1076 /**
1077 * Move assignment operator.
1078 */
1079 val& operator=(val&& other) LMDB_NOEXCEPT = default;
1080 #endif
1081
1082 /**
1083 * Destructor.
1084 */
1085 ~val() LMDB_NOEXCEPT = default;
1086
1087 /**
1088 * Returns an `MDB_val*` pointer.
1089 */
1090 operator MDB_val*() LMDB_NOEXCEPT {
1091 return &_val;
1092 }
1093
1094 /**
1095 * Returns an `MDB_val*` pointer.
1096 */
1097 operator const MDB_val*() const LMDB_NOEXCEPT {
1098 return &_val;
1099 }
1100
1101 /**
1102 * Determines whether this value is empty.
1103 */
empty()1104 bool empty() const LMDB_NOEXCEPT {
1105 return size() == 0;
1106 }
1107
1108 /**
1109 * Returns the size of the data.
1110 */
size()1111 std::size_t size() const LMDB_NOEXCEPT {
1112 return _val.mv_size;
1113 }
1114
1115 /**
1116 * Returns a pointer to the data.
1117 */
1118 template<typename T>
data()1119 T* data() LMDB_NOEXCEPT {
1120 return reinterpret_cast<T*>(_val.mv_data);
1121 }
1122
1123 /**
1124 * Returns a pointer to the data.
1125 */
1126 template<typename T>
data()1127 const T* data() const LMDB_NOEXCEPT {
1128 return reinterpret_cast<T*>(_val.mv_data);
1129 }
1130
1131 /**
1132 * Returns a pointer to the data.
1133 */
data()1134 char* data() LMDB_NOEXCEPT {
1135 return reinterpret_cast<char*>(_val.mv_data);
1136 }
1137
1138 /**
1139 * Returns a pointer to the data.
1140 */
data()1141 const char* data() const LMDB_NOEXCEPT {
1142 return reinterpret_cast<char*>(_val.mv_data);
1143 }
1144
1145 /**
1146 * Assigns the value.
1147 */
1148 template<typename T>
assign(const T * const data,const std::size_t size)1149 val& assign(const T* const data,
1150 const std::size_t size) LMDB_NOEXCEPT {
1151 _val.mv_size = size;
1152 _val.mv_data = const_cast<void*>(reinterpret_cast<const void*>(data));
1153 return *this;
1154 }
1155
1156 /**
1157 * Assigns the value.
1158 */
1159 template<typename T>
assign(const T & data)1160 val& assign(const T& data) LMDB_NOEXCEPT {
1161 _val.mv_size = sizeof(T);
1162 _val.mv_data = const_cast<void*>(reinterpret_cast<const void*>(&data));
1163 return *this;
1164 }
1165
1166 /**
1167 * Assigns the value.
1168 */
assign(const char * const data)1169 val& assign(const char* const data) LMDB_NOEXCEPT {
1170 return assign(data, std::strlen(data));
1171 }
1172
1173 /**
1174 * Assigns the value.
1175 */
assign(const std::string & data)1176 val& assign(const std::string& data) LMDB_NOEXCEPT {
1177 return assign(data.data(), data.size());
1178 }
1179
1180 /**
1181 * Assigns the value.
1182 */
assign(const std::string::const_iterator & data,const std::size_t len)1183 val& assign(const std::string::const_iterator& data,
1184 const std::size_t len) LMDB_NOEXCEPT {
1185 return assign(&*data, len);
1186 }
1187
1188 /**
1189 * Convert value to T
1190 */
1191 template<typename T>
assign_to(T & t)1192 void assign_to(T& t) {
1193 t = *data<const T>();
1194 }
1195
1196 /**
1197 * Convert value to string
1198 */
assign_to(std::string & str)1199 void assign_to(std::string& str) {
1200 str.assign(data(), size());
1201 }
1202
1203 };
1204
1205 #if !(defined(__COVERITY__) || defined(_MSC_VER) || defined(__INTEL_COMPILER))
1206 static_assert(std::is_pod<lmdb::val>::value, "lmdb::val must be a POD type");
1207 static_assert(sizeof(lmdb::val) == sizeof(MDB_val), "sizeof(lmdb::val) != sizeof(MDB_val)");
1208 #endif
1209
1210 ////////////////////////////////////////////////////////////////////////////////
1211 /* Resource Interface: Environment */
1212
1213 namespace lmdb {
1214 class env;
1215 }
1216
1217 /**
1218 * Resource class for `MDB_env*` handles.
1219 *
1220 * @note Instances of this class are movable, but not copyable.
1221 * @see http://symas.com/mdb/doc/group__internal.html#structMDB__env
1222 */
1223 class lmdb::env {
1224 protected:
1225 MDB_env* _handle{nullptr};
1226
1227 public:
1228 static LMDB_CONSTEXPR unsigned int default_flags = 0;
1229 static LMDB_CONSTEXPR mode default_mode = 0644; /* -rw-r--r-- */
1230
1231 /**
1232 * Creates a new LMDB environment.
1233 *
1234 * @param flags
1235 * @throws lmdb::error on failure
1236 */
1237 static env create(const unsigned int flags = default_flags) {
1238 MDB_env* handle{nullptr};
1239 lmdb::env_create(&handle);
1240 #ifdef LMDBXX_DEBUG
1241 assert(handle != nullptr);
1242 #endif
1243 if (flags) {
1244 try {
1245 lmdb::env_set_flags(handle, flags);
1246 }
catch(const lmdb::error &)1247 catch (const lmdb::error&) {
1248 lmdb::env_close(handle);
1249 throw;
1250 }
1251 }
1252 return env{handle};
1253 }
1254
1255 /**
1256 * Constructor.
1257 *
1258 * @param handle a valid `MDB_env*` handle
1259 */
env(MDB_env * const handle)1260 env(MDB_env* const handle) LMDB_NOEXCEPT
1261 : _handle{handle} {}
1262
1263 /**
1264 * Move constructor.
1265 */
env(env && other)1266 env(env&& other) LMDB_NOEXCEPT {
1267 std::swap(_handle, other._handle);
1268 }
1269
1270 /**
1271 * Move assignment operator.
1272 */
1273 env& operator=(env&& other) LMDB_NOEXCEPT {
1274 if (this != &other) {
1275 std::swap(_handle, other._handle);
1276 }
1277 return *this;
1278 }
1279
1280 /**
1281 * Destructor.
1282 */
~env()1283 ~env() LMDB_NOEXCEPT {
1284 try { close(); } catch (...) {}
1285 }
1286
1287 /**
1288 * Returns the underlying `MDB_env*` handle.
1289 */
1290 operator MDB_env*() const LMDB_NOEXCEPT {
1291 return _handle;
1292 }
1293
1294 /**
1295 * Returns the underlying `MDB_env*` handle.
1296 */
handle()1297 MDB_env* handle() const LMDB_NOEXCEPT {
1298 return _handle;
1299 }
1300
1301 /**
1302 * Flushes data buffers to disk.
1303 *
1304 * @param force
1305 * @throws lmdb::error on failure
1306 */
1307 void sync(const bool force = true) {
1308 lmdb::env_sync(handle(), force);
1309 }
1310
1311 /**
1312 * Closes this environment, releasing the memory map.
1313 *
1314 * @note this method is idempotent
1315 * @post `handle() == nullptr`
1316 */
close()1317 void close() LMDB_NOEXCEPT {
1318 if (handle()) {
1319 lmdb::env_close(handle());
1320 _handle = nullptr;
1321 }
1322 }
1323
1324 /**
1325 * Opens this environment.
1326 *
1327 * @param path
1328 * @param flags
1329 * @param mode
1330 * @throws lmdb::error on failure
1331 */
1332 env& open(const char* const path,
1333 const unsigned int flags = default_flags,
1334 const mode mode = default_mode) {
1335 lmdb::env_open(handle(), path, flags, mode);
1336 return *this;
1337 }
1338
1339 /**
1340 * @param flags
1341 * @param onoff
1342 * @throws lmdb::error on failure
1343 */
1344 env& set_flags(const unsigned int flags,
1345 const bool onoff = true) {
1346 lmdb::env_set_flags(handle(), flags, onoff);
1347 return *this;
1348 }
1349
1350 /**
1351 * @param size
1352 * @throws lmdb::error on failure
1353 */
set_mapsize(const std::size_t size)1354 env& set_mapsize(const std::size_t size) {
1355 lmdb::env_set_mapsize(handle(), size);
1356 return *this;
1357 }
1358
1359 /**
1360 * @param count
1361 * @throws lmdb::error on failure
1362 */
set_max_readers(const unsigned int count)1363 env& set_max_readers(const unsigned int count) {
1364 lmdb::env_set_max_readers(handle(), count);
1365 return *this;
1366 }
1367
1368 /**
1369 * @param count
1370 * @throws lmdb::error on failure
1371 */
set_max_dbs(const MDB_dbi count)1372 env& set_max_dbs(const MDB_dbi count) {
1373 lmdb::env_set_max_dbs(handle(), count);
1374 return *this;
1375 }
1376 };
1377
1378 ////////////////////////////////////////////////////////////////////////////////
1379 /* Resource Interface: Transactions */
1380
1381 namespace lmdb {
1382 class txn;
1383 }
1384
1385 /**
1386 * Resource class for `MDB_txn*` handles.
1387 *
1388 * @note Instances of this class are movable, but not copyable.
1389 * @see http://symas.com/mdb/doc/group__internal.html#structMDB__txn
1390 */
1391 class lmdb::txn {
1392 protected:
1393 MDB_txn* _handle{nullptr};
1394
1395 public:
1396 static LMDB_CONSTEXPR unsigned int default_flags = 0;
1397
1398 /**
1399 * Creates a new LMDB transaction.
1400 *
1401 * @param env the environment handle
1402 * @param parent
1403 * @param flags
1404 * @throws lmdb::error on failure
1405 */
1406 static txn begin(MDB_env* const env,
1407 MDB_txn* const parent = nullptr,
1408 const unsigned int flags = default_flags) {
1409 MDB_txn* handle{nullptr};
1410 lmdb::txn_begin(env, parent, flags, &handle);
1411 #ifdef LMDBXX_DEBUG
1412 assert(handle != nullptr);
1413 #endif
1414 return txn{handle};
1415 }
1416
1417 /**
1418 * Constructor.
1419 *
1420 * @param handle a valid `MDB_txn*` handle
1421 */
txn(MDB_txn * const handle)1422 txn(MDB_txn* const handle) LMDB_NOEXCEPT
1423 : _handle{handle} {}
1424
1425 /**
1426 * Move constructor.
1427 */
txn(txn && other)1428 txn(txn&& other) LMDB_NOEXCEPT {
1429 std::swap(_handle, other._handle);
1430 }
1431
1432 /**
1433 * Move assignment operator.
1434 */
1435 txn& operator=(txn&& other) LMDB_NOEXCEPT {
1436 if (this != &other) {
1437 std::swap(_handle, other._handle);
1438 }
1439 return *this;
1440 }
1441
1442 /**
1443 * Destructor.
1444 */
~txn()1445 ~txn() LMDB_NOEXCEPT {
1446 if (_handle) {
1447 try { abort(); } catch (...) {}
1448 _handle = nullptr;
1449 }
1450 }
1451
1452 /**
1453 * Returns the underlying `MDB_txn*` handle.
1454 */
1455 operator MDB_txn*() const LMDB_NOEXCEPT {
1456 return _handle;
1457 }
1458
1459 /**
1460 * Returns the underlying `MDB_txn*` handle.
1461 */
handle()1462 MDB_txn* handle() const LMDB_NOEXCEPT {
1463 return _handle;
1464 }
1465
1466 /**
1467 * Returns the transaction's `MDB_env*` handle.
1468 */
env()1469 MDB_env* env() const LMDB_NOEXCEPT {
1470 return lmdb::txn_env(handle());
1471 }
1472
1473 /**
1474 * Commits this transaction.
1475 *
1476 * @throws lmdb::error on failure
1477 * @post `handle() == nullptr`
1478 */
commit()1479 void commit() {
1480 lmdb::txn_commit(_handle);
1481 _handle = nullptr;
1482 }
1483
1484 /**
1485 * Aborts this transaction.
1486 *
1487 * @post `handle() == nullptr`
1488 */
abort()1489 void abort() LMDB_NOEXCEPT {
1490 lmdb::txn_abort(_handle);
1491 _handle = nullptr;
1492 }
1493
1494 /**
1495 * Resets this read-only transaction.
1496 */
reset()1497 void reset() LMDB_NOEXCEPT {
1498 lmdb::txn_reset(_handle);
1499 }
1500
1501 /**
1502 * Renews this read-only transaction.
1503 *
1504 * @throws lmdb::error on failure
1505 */
renew()1506 void renew() {
1507 lmdb::txn_renew(_handle);
1508 }
1509 };
1510
1511 ////////////////////////////////////////////////////////////////////////////////
1512 /* Resource Interface: Databases */
1513
1514 namespace lmdb {
1515 class dbi;
1516 }
1517
1518 /**
1519 * Resource class for `MDB_dbi` handles.
1520 *
1521 * @note Instances of this class are movable, but not copyable.
1522 * @see http://symas.com/mdb/doc/group__mdb.html#gadbe68a06c448dfb62da16443d251a78b
1523 */
1524 class lmdb::dbi {
1525 protected:
1526 MDB_dbi _handle{0};
1527
1528 public:
1529 static LMDB_CONSTEXPR unsigned int default_flags = 0;
1530 static LMDB_CONSTEXPR unsigned int default_put_flags = 0;
1531
1532 /**
1533 * Opens a database handle.
1534 *
1535 * @param txn the transaction handle
1536 * @param name
1537 * @param flags
1538 * @throws lmdb::error on failure
1539 */
1540 static dbi
1541 open(MDB_txn* const txn,
1542 const char* const name = nullptr,
1543 const unsigned int flags = default_flags) {
1544 MDB_dbi handle{};
1545 lmdb::dbi_open(txn, name, flags, &handle);
1546 return dbi{handle};
1547 }
1548
1549 /**
1550 * Constructor.
1551 *
1552 * @param handle a valid `MDB_dbi` handle
1553 */
dbi(const MDB_dbi handle)1554 dbi(const MDB_dbi handle) LMDB_NOEXCEPT
1555 : _handle{handle} {}
1556
1557 /**
1558 * Move constructor.
1559 */
dbi(dbi && other)1560 dbi(dbi&& other) LMDB_NOEXCEPT {
1561 std::swap(_handle, other._handle);
1562 }
1563
1564 /**
1565 * Move assignment operator.
1566 */
1567 dbi& operator=(dbi&& other) LMDB_NOEXCEPT {
1568 if (this != &other) {
1569 std::swap(_handle, other._handle);
1570 }
1571 return *this;
1572 }
1573
1574 /**
1575 * Destructor.
1576 */
~dbi()1577 ~dbi() LMDB_NOEXCEPT {
1578 if (_handle) {
1579 /* No need to call close() here. */
1580 }
1581 }
1582
1583 /**
1584 * Closes DB handle
1585 */
close(MDB_env * const env)1586 void close(MDB_env* const env) {
1587 if (_handle) {
1588 mdb_dbi_close(env, _handle);
1589 _handle = 0;
1590 }
1591 }
1592
1593 /**
1594 * Returns the underlying `MDB_dbi` handle.
1595 */
MDB_dbi()1596 operator MDB_dbi() const LMDB_NOEXCEPT {
1597 return _handle;
1598 }
1599
1600 /**
1601 * Returns the underlying `MDB_dbi` handle.
1602 */
handle()1603 MDB_dbi handle() const LMDB_NOEXCEPT {
1604 return _handle;
1605 }
1606
1607 /**
1608 * Returns statistics for this database.
1609 *
1610 * @param txn a transaction handle
1611 * @throws lmdb::error on failure
1612 */
stat(MDB_txn * const txn)1613 MDB_stat stat(MDB_txn* const txn) const {
1614 MDB_stat result;
1615 lmdb::dbi_stat(txn, handle(), &result);
1616 return result;
1617 }
1618
1619 /**
1620 * Retrieves the flags for this database handle.
1621 *
1622 * @param txn a transaction handle
1623 * @throws lmdb::error on failure
1624 */
flags(MDB_txn * const txn)1625 unsigned int flags(MDB_txn* const txn) const {
1626 unsigned int result{};
1627 lmdb::dbi_flags(txn, handle(), &result);
1628 return result;
1629 }
1630
1631 /**
1632 * Returns the number of records in this database.
1633 *
1634 * @param txn a transaction handle
1635 * @throws lmdb::error on failure
1636 */
size(MDB_txn * const txn)1637 std::size_t size(MDB_txn* const txn) const {
1638 return stat(txn).ms_entries;
1639 }
1640
1641 /**
1642 * @param txn a transaction handle
1643 * @param del
1644 * @throws lmdb::error on failure
1645 */
1646 void drop(MDB_txn* const txn,
1647 const bool del = false) {
1648 lmdb::dbi_drop(txn, handle(), del);
1649 }
1650
1651 /**
1652 * Sets a custom key comparison function for this database.
1653 *
1654 * @param txn a transaction handle
1655 * @param cmp the comparison function
1656 * @throws lmdb::error on failure
1657 */
1658 dbi& set_compare(MDB_txn* const txn,
1659 MDB_cmp_func* const cmp = nullptr) {
1660 lmdb::dbi_set_compare(txn, handle(), cmp);
1661 return *this;
1662 }
1663
1664 /**
1665 * Retrieves a key/value pair from this database.
1666 *
1667 * @param txn a transaction handle
1668 * @param key
1669 * @param data
1670 * @throws lmdb::error on failure
1671 */
get(MDB_txn * const txn,const val & key,val & data)1672 bool get(MDB_txn* const txn,
1673 const val& key,
1674 val& data) const {
1675 return lmdb::dbi_get(txn, handle(), key, data);
1676 }
1677
1678 /**
1679 * Retrieves a key from this database.
1680 *
1681 * @param txn a transaction handle
1682 * @param key
1683 * @throws lmdb::error on failure
1684 */
1685 template<typename K>
get(MDB_txn * const txn,const K & key)1686 bool get(MDB_txn* const txn,
1687 const K& key) const {
1688 const lmdb::val k{key};
1689 lmdb::val v{};
1690 return lmdb::dbi_get(txn, handle(), k, v);
1691 }
1692
1693 /**
1694 * Retrieves a key/value pair from this database.
1695 *
1696 * @param txn a transaction handle
1697 * @param key
1698 * @param val
1699 * @throws lmdb::error on failure
1700 */
1701 template<typename K, typename V>
get(MDB_txn * const txn,const K & key,V & val)1702 bool get(MDB_txn* const txn,
1703 const K& key,
1704 V& val) const {
1705 const lmdb::val k{key};
1706 lmdb::val v{};
1707 const bool result = lmdb::dbi_get(txn, handle(), k, v);
1708 if (result)
1709 v.assign_to(val);
1710 return result;
1711 }
1712
1713 /**
1714 * Retrieves a key/value pair from this database.
1715 *
1716 * @param txn a transaction handle
1717 * @param key a NUL-terminated string key
1718 * @param val
1719 * @throws lmdb::error on failure
1720 */
1721 template<typename V>
get(MDB_txn * const txn,const char * const key,V & val)1722 bool get(MDB_txn* const txn,
1723 const char* const key,
1724 V& val) const {
1725 const lmdb::val k{key};
1726 lmdb::val v{};
1727 const bool result = lmdb::dbi_get(txn, handle(), k, v);
1728 if (result)
1729 v.assign_to(val);
1730 return result;
1731 }
1732
1733 /**
1734 * Stores a key/value pair into this database.
1735 *
1736 * @param txn a transaction handle
1737 * @param key
1738 * @param data
1739 * @param flags
1740 * @throws lmdb::error on failure
1741 */
1742 bool put(MDB_txn* const txn,
1743 const val& key,
1744 const val& data,
1745 const unsigned int flags = default_put_flags) {
1746 return lmdb::dbi_put(txn, handle(), key, data, flags);
1747 }
1748
1749 /**
1750 * Stores a key into this database.
1751 *
1752 * @param txn a transaction handle
1753 * @param key
1754 * @param flags
1755 * @throws lmdb::error on failure
1756 */
1757 template<typename K>
1758 bool put(MDB_txn* const txn,
1759 const K& key,
1760 const unsigned int flags = default_put_flags) {
1761 const lmdb::val k{key};
1762 lmdb::val v{};
1763 return lmdb::dbi_put(txn, handle(), k, v, flags);
1764 }
1765
1766 /**
1767 * Stores a key/value pair into this database.
1768 *
1769 * @param txn a transaction handle
1770 * @param key
1771 * @param val
1772 * @param flags
1773 * @throws lmdb::error on failure
1774 */
1775 template<typename K, typename V>
1776 bool put(MDB_txn* const txn,
1777 const K& key,
1778 const V& val,
1779 const unsigned int flags = default_put_flags) {
1780 const lmdb::val k{key};
1781 lmdb::val v{val};
1782 return lmdb::dbi_put(txn, handle(), k, v, flags);
1783 }
1784
1785 /**
1786 * Stores a key/value pair into this database.
1787 *
1788 * @param txn a transaction handle
1789 * @param key a NUL-terminated string key
1790 * @param val
1791 * @param flags
1792 * @throws lmdb::error on failure
1793 */
1794 template<typename V>
1795 bool put(MDB_txn* const txn,
1796 const char* const key,
1797 const V& val,
1798 const unsigned int flags = default_put_flags) {
1799 const lmdb::val k{key};
1800 lmdb::val v{val};
1801 return lmdb::dbi_put(txn, handle(), k, v, flags);
1802 }
1803
1804 /**
1805 * Stores a key/value pair into this database.
1806 *
1807 * @param txn a transaction handle
1808 * @param key a NUL-terminated string key
1809 * @param val a NUL-terminated string key
1810 * @param flags
1811 * @throws lmdb::error on failure
1812 */
1813 bool put(MDB_txn* const txn,
1814 const char* const key,
1815 const char* const val,
1816 const unsigned int flags = default_put_flags) {
1817 const lmdb::val k{key};
1818 lmdb::val v{val};
1819 return lmdb::dbi_put(txn, handle(), k, v, flags);
1820 }
1821
1822 /**
1823 * Removes a key/value pair from this database.
1824 *
1825 * @param txn a transaction handle
1826 * @param key
1827 * @throws lmdb::error on failure
1828 */
del(MDB_txn * const txn,const val & key)1829 bool del(MDB_txn* const txn,
1830 const val& key) {
1831 return lmdb::dbi_del(txn, handle(), key);
1832 }
1833
1834 /**
1835 * Removes a key/value pair from this database.
1836 *
1837 * @param txn a transaction handle
1838 * @param key
1839 * @throws lmdb::error on failure
1840 */
1841 template<typename K>
del(MDB_txn * const txn,const K & key)1842 bool del(MDB_txn* const txn,
1843 const K& key) {
1844 const lmdb::val k{key};
1845 return lmdb::dbi_del(txn, handle(), k);
1846 }
1847 };
1848
1849 ////////////////////////////////////////////////////////////////////////////////
1850 /* Resource Interface: Cursors */
1851
1852 namespace lmdb {
1853 class cursor;
1854 }
1855
1856 /**
1857 * Resource class for `MDB_cursor*` handles.
1858 *
1859 * @note Instances of this class are movable, but not copyable.
1860 * @see http://symas.com/mdb/doc/group__internal.html#structMDB__cursor
1861 */
1862 class lmdb::cursor {
1863 protected:
1864 MDB_cursor* _handle{nullptr};
1865
1866 public:
1867 static LMDB_CONSTEXPR unsigned int default_flags = 0;
1868
1869 /**
1870 * Creates an LMDB cursor.
1871 *
1872 * @param txn the transaction handle
1873 * @param dbi the database handle
1874 * @throws lmdb::error on failure
1875 */
1876 static cursor
open(MDB_txn * const txn,const MDB_dbi dbi)1877 open(MDB_txn* const txn,
1878 const MDB_dbi dbi) {
1879 MDB_cursor* handle{};
1880 lmdb::cursor_open(txn, dbi, &handle);
1881 #ifdef LMDBXX_DEBUG
1882 assert(handle != nullptr);
1883 #endif
1884 return cursor{handle};
1885 }
1886
1887 /**
1888 * Constructor.
1889 *
1890 * @param handle a valid `MDB_cursor*` handle
1891 */
cursor(MDB_cursor * const handle)1892 cursor(MDB_cursor* const handle) LMDB_NOEXCEPT
1893 : _handle{handle} {}
1894
1895 /**
1896 * Move constructor.
1897 */
cursor(cursor && other)1898 cursor(cursor&& other) LMDB_NOEXCEPT {
1899 std::swap(_handle, other._handle);
1900 }
1901
1902 /**
1903 * Move assignment operator.
1904 */
1905 cursor& operator=(cursor&& other) LMDB_NOEXCEPT {
1906 if (this != &other) {
1907 std::swap(_handle, other._handle);
1908 }
1909 return *this;
1910 }
1911
1912 /**
1913 * Destructor.
1914 */
~cursor()1915 ~cursor() LMDB_NOEXCEPT {
1916 try { close(); } catch (...) {}
1917 }
1918
1919 /**
1920 * Returns the underlying `MDB_cursor*` handle.
1921 */
1922 operator MDB_cursor*() const LMDB_NOEXCEPT {
1923 return _handle;
1924 }
1925
1926 /**
1927 * Returns the underlying `MDB_cursor*` handle.
1928 */
handle()1929 MDB_cursor* handle() const LMDB_NOEXCEPT {
1930 return _handle;
1931 }
1932
1933 /**
1934 * Closes this cursor.
1935 *
1936 * @note this method is idempotent
1937 * @post `handle() == nullptr`
1938 */
close()1939 void close() LMDB_NOEXCEPT {
1940 if (_handle) {
1941 lmdb::cursor_close(_handle);
1942 _handle = nullptr;
1943 }
1944 }
1945
1946 /**
1947 * Renews this cursor.
1948 *
1949 * @param txn the transaction scope
1950 * @throws lmdb::error on failure
1951 */
renew(MDB_txn * const txn)1952 void renew(MDB_txn* const txn) {
1953 lmdb::cursor_renew(txn, handle());
1954 }
1955
1956 /**
1957 * Returns the cursor's transaction handle.
1958 */
txn()1959 MDB_txn* txn() const LMDB_NOEXCEPT {
1960 return lmdb::cursor_txn(handle());
1961 }
1962
1963 /**
1964 * Returns the cursor's database handle.
1965 */
dbi()1966 MDB_dbi dbi() const LMDB_NOEXCEPT {
1967 return lmdb::cursor_dbi(handle());
1968 }
1969
1970 /**
1971 * Retrieves a key from the database.
1972 *
1973 * @param key
1974 * @param op
1975 * @throws lmdb::error on failure
1976 */
get(MDB_val * const key,const MDB_cursor_op op)1977 bool get(MDB_val* const key,
1978 const MDB_cursor_op op) {
1979 return get(key, nullptr, op);
1980 }
1981
1982 /**
1983 * Retrieves a key from the database.
1984 *
1985 * @param key
1986 * @param op
1987 * @throws lmdb::error on failure
1988 */
get(lmdb::val & key,const MDB_cursor_op op)1989 bool get(lmdb::val& key,
1990 const MDB_cursor_op op) {
1991 return get(key, nullptr, op);
1992 }
1993
1994 /**
1995 * Retrieves a key/value pair from the database.
1996 *
1997 * @param key
1998 * @param val (may be `nullptr`)
1999 * @param op
2000 * @throws lmdb::error on failure
2001 */
get(MDB_val * const key,MDB_val * const val,const MDB_cursor_op op)2002 bool get(MDB_val* const key,
2003 MDB_val* const val,
2004 const MDB_cursor_op op) {
2005 return lmdb::cursor_get(handle(), key, val, op);
2006 }
2007
2008 /**
2009 * Retrieves a key/value pair from the database.
2010 *
2011 * @param key
2012 * @param val
2013 * @param op
2014 * @throws lmdb::error on failure
2015 */
get(lmdb::val & key,lmdb::val & val,const MDB_cursor_op op)2016 bool get(lmdb::val& key,
2017 lmdb::val& val,
2018 const MDB_cursor_op op) {
2019 return lmdb::cursor_get(handle(), key, val, op);
2020 }
2021
2022 /**
2023 * Retrieves a key/value pair from the database.
2024 *
2025 * @param key
2026 * @param val
2027 * @param op
2028 * @throws lmdb::error on failure
2029 */
2030 template<typename K, typename V>
get(K & key,V & val,const MDB_cursor_op op)2031 bool get(K& key,
2032 V& val,
2033 const MDB_cursor_op op) {
2034 lmdb::val k{key}, v{val};
2035 const bool found = get(k, v, op);
2036 if (found) {
2037 k.assign_to(key);
2038 v.assign_to(val);
2039 }
2040 return found;
2041 }
2042
2043 /**
2044 * Positions this cursor at the given key.
2045 *
2046 * @param key
2047 * @param op
2048 * @throws lmdb::error on failure
2049 */
2050 template<typename K>
2051 bool find(const K& key,
2052 const MDB_cursor_op op = MDB_SET) {
2053 lmdb::val k{key};
2054 return get(k, nullptr, op);
2055 }
2056 };
2057
2058 ////////////////////////////////////////////////////////////////////////////////
2059
2060 #undef LMDB_NOEXCEPT
2061 #undef LMDB_CONSTEXPR
2062 #undef LMDB_SNPRINTF
2063
2064
2065 #endif /* LMDBXX_H */
2066