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