mdbx.h++ 232 KB
1 /// \file mdbx.h++
2 /// \brief The libmdbx C++ API header file.
3 ///
4 /// \author Copyright (c) 2020-2023, Leonid Yuriev <leo@yuriev.ru>.
5 /// \copyright SPDX-License-Identifier: Apache-2.0
6 ///
7 /// Tested with:
8 /// - Elbrus LCC >= 1.23 (http://www.mcst.ru/lcc);
9 /// - GNU C++ >= 4.8;
10 /// - clang >= 3.9;
11 /// - MSVC >= 14.0 (Visual Studio 2015),
12 /// but 19.2x could hang due optimizer bug;
13 /// - AppleClang, but without C++20 concepts.
14 ///
15
16 ///
17 /// The origin has been migrated to https://gitflic.ru/project/erthink/libmdbx
18 /// since on 2022-04-15 the Github administration, without any warning nor
19 /// explanation, deleted libmdbx along with a lot of other projects,
20 /// simultaneously blocking access for many developers.
21 /// For the same reason Github is blacklisted forever.
22 ///
23
24 #pragma once
25
26 /* Workaround for modern libstdc++ with CLANG < 4.x */
27 #if defined(__SIZEOF_INT128__) && !defined(__GLIBCXX_TYPE_INT_N_0) && \
28     defined(__clang__) && __clang_major__ < 4
29 #define __GLIBCXX_BITSIZE_INT_N_0 128
30 #define __GLIBCXX_TYPE_INT_N_0 __int128
31 #endif /* Workaround for modern libstdc++ with CLANG < 4.x */
32
33 #if !defined(__cplusplus) || __cplusplus < 201103L
34 #if !defined(_MSC_VER) || _MSC_VER < 1900
35 #error "C++11 compiler or better is required"
36 #elif _MSC_VER >= 1910
37 #error \
38     "Please add `/Zc:__cplusplus` to MSVC compiler options to enforce it conform ISO C++"
39 #endif /* MSVC is mad and don't define __cplusplus properly */
40 #endif /* __cplusplus < 201103L */
41
42 #if (defined(_WIN32) || defined(_WIN64)) && MDBX_WITHOUT_MSVC_CRT
43 #error \
44     "CRT is required for C++ API, the MDBX_WITHOUT_MSVC_CRT option must be disabled"
45 #endif /* Windows */
46
47 #ifndef __has_include
48 #define __has_include(header) (0)
49 #endif /* __has_include */
50
51 #if __has_include(<version>)
52 #include <version>
53 #endif /* <version> */
54
55 /* Disable min/max macros from C' headers */
56 #ifndef NOMINMAX
57 #define NOMINMAX
58 #endif
59
60 #include <algorithm> // for std::min/max
61 #include <cassert> // for assert()
62 #include <climits> // for CHAR_BIT
63 #include <cstring> // for std::strlen, str:memcmp
64 #include <exception> // for std::exception_ptr
65 #include <ostream> // for std::ostream
66 #include <sstream> // for std::ostringstream
67 #include <stdexcept> // for std::invalid_argument
68 #include <string> // for std::string
69 #include <type_traits> // for std::is_pod<>, etc.
70 #include <utility> // for std::make_pair
71 #include <vector> // for std::vector<> as template args
72
73 #if defined(__cpp_lib_memory_resource) && __cpp_lib_memory_resource >= 201603L
74 #include <memory_resource>
75 #endif
76
77 #if defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L
78 #include <string_view>
79 #endif
80
81 #if defined(__cpp_lib_filesystem) && __cpp_lib_filesystem >= 201703L
82 #include <filesystem>
83 #elif __has_include(<experimental/filesystem>)
84 #include <experimental/filesystem>
85 #endif
86
87 #include "mdbx.h"
88
89 #if (defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L) || \
90     (defined(__cpp_lib_endian) && __cpp_lib_endian >= 201907L) || \
91     (defined(__cpp_lib_bitops) && __cpp_lib_bitops >= 201907L) || \
92     (defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L)
93 #include <bit>
94 #elif !(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
95         defined(__ORDER_BIG_ENDIAN__))
96 #if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && defined(__BIG_ENDIAN)
97 #define __ORDER_LITTLE_ENDIAN__ __LITTLE_ENDIAN
98 #define __ORDER_BIG_ENDIAN__ __BIG_ENDIAN
99 #define __BYTE_ORDER__ __BYTE_ORDER
100 #elif defined(_BYTE_ORDER) && defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN)
101 #define __ORDER_LITTLE_ENDIAN__ _LITTLE_ENDIAN
102 #define __ORDER_BIG_ENDIAN__ _BIG_ENDIAN
103 #define __BYTE_ORDER__ _BYTE_ORDER
104 #else
105 #define __ORDER_LITTLE_ENDIAN__ 1234
106 #define __ORDER_BIG_ENDIAN__ 4321
107 #if defined(__LITTLE_ENDIAN__) || \
108     (defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) || \
109     defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || \
110     defined(__MIPSEL__) || defined(_MIPSEL) || defined(__MIPSEL) || \
111     defined(_M_ARM) || defined(_M_ARM64) || defined(__e2k__) || \
112     defined(__elbrus_4c__) || defined(__elbrus_8c__) || defined(__bfin__) || \
113     defined(__BFIN__) || defined(__ia64__) || defined(_IA64) || \
114     defined(__IA64__) || defined(__ia64) || defined(_M_IA64) || \
115     defined(__itanium__) || defined(__ia32__) || defined(__CYGWIN__) || \
116     defined(_WIN64) || defined(_WIN32) || defined(__TOS_WIN__) || \
117     defined(__WINDOWS__)
118 #define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
119 #elif defined(__BIG_ENDIAN__) || \
120     (defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)) || \
121     defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \
122     defined(__MIPSEB__) || defined(_MIPSEB) || defined(__MIPSEB) || \
123     defined(__m68k__) || defined(M68000) || defined(__hppa__) || \
124     defined(__hppa) || defined(__HPPA__) || defined(__sparc__) || \
125     defined(__sparc) || defined(__370__) || defined(__THW_370__) || \
126     defined(__s390__) || defined(__s390x__) || defined(__SYSC_ZARCH__)
127 #define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__
128 #endif
129 #endif
130 #endif /* Byte Order */
131
132 /** Workaround for old compilers without properly support for `C++17 constexpr`.
133  */
134 #if defined(DOXYGEN)
135 #define MDBX_CXX17_CONSTEXPR constexpr
136 #elif defined(__cpp_constexpr) && __cpp_constexpr >= 201603L && \
137     ((defined(_MSC_VER) && _MSC_VER >= 1915) || \
138      (defined(__clang__) && __clang_major__ > 5) || \
139      (defined(__GNUC__) && __GNUC__ > 7) || \
140      (!defined(__GNUC__) && !defined(__clang__) && !defined(_MSC_VER)))
141 #define MDBX_CXX17_CONSTEXPR constexpr
142 #else
143 #define MDBX_CXX17_CONSTEXPR inline
144 #endif /* MDBX_CXX17_CONSTEXPR */
145
146 /** Workaround for old compilers without properly support for C++20 `constexpr`.
147  */
148 #if defined(DOXYGEN)
149 #define MDBX_CXX20_CONSTEXPR constexpr
150 #elif defined(__cpp_lib_is_constant_evaluated) && \
151     __cpp_lib_is_constant_evaluated >= 201811L && \
152     defined(__cpp_lib_constexpr_string) && \
153     __cpp_lib_constexpr_string >= 201907L
154 #define MDBX_CXX20_CONSTEXPR constexpr
155 #else
156 #define MDBX_CXX20_CONSTEXPR inline
157 #endif /* MDBX_CXX20_CONSTEXPR */
158
159 /** Workaround for old compilers without support assertion inside `constexpr`
160  * functions. */
161 #if defined(CONSTEXPR_ASSERT)
162 #define MDBX_CONSTEXPR_ASSERT(expr) CONSTEXPR_ASSERT(expr)
163 #elif defined NDEBUG
164 #define MDBX_CONSTEXPR_ASSERT(expr) void(0)
165 #else
166 #define MDBX_CONSTEXPR_ASSERT(expr) \
167   ((expr) ? void(0) : [] { assert(!#expr); }())
168 #endif /* MDBX_CONSTEXPR_ASSERT */
169
170 #ifndef MDBX_LIKELY
171 #if defined(DOXYGEN) || \
172     (defined(__GNUC__) || __has_builtin(__builtin_expect)) && \
173         !defined(__COVERITY__)
174 #define MDBX_LIKELY(cond) __builtin_expect(!!(cond), 1)
175 #else
176 #define MDBX_LIKELY(x) (x)
177 #endif
178 #endif /* MDBX_LIKELY */
179
180 #ifndef MDBX_UNLIKELY
181 #if defined(DOXYGEN) || \
182     (defined(__GNUC__) || __has_builtin(__builtin_expect)) && \
183         !defined(__COVERITY__)
184 #define MDBX_UNLIKELY(cond) __builtin_expect(!!(cond), 0)
185 #else
186 #define MDBX_UNLIKELY(x) (x)
187 #endif
188 #endif /* MDBX_UNLIKELY */
189
190 /** Workaround for old compilers without properly support for C++20 `if
191  * constexpr`. */
192 #if defined(DOXYGEN)
193 #define MDBX_IF_CONSTEXPR constexpr
194 #elif defined(__cpp_if_constexpr) && __cpp_if_constexpr >= 201606L
195 #define MDBX_IF_CONSTEXPR constexpr
196 #else
197 #define MDBX_IF_CONSTEXPR
198 #endif /* MDBX_IF_CONSTEXPR */
199
200 #if defined(DOXYGEN) || \
201     (__has_cpp_attribute(fallthrough) && \
202      (!defined(__clang__) || __clang__ > 4)) || \
203     __cplusplus >= 201703L
204 #define MDBX_CXX17_FALLTHROUGH [[fallthrough]]
205 #else
206 #define MDBX_CXX17_FALLTHROUGH
207 #endif /* MDBX_CXX17_FALLTHROUGH */
208
209 #if defined(DOXYGEN) || (__has_cpp_attribute(likely) >= 201803L && \
210                          (!defined(__GNUC__) || __GNUC__ > 9))
211 #define MDBX_CXX20_LIKELY [[likely]]
212 #else
213 #define MDBX_CXX20_LIKELY
214 #endif /* MDBX_CXX20_LIKELY */
215
216 #ifndef MDBX_CXX20_UNLIKELY
217 #if defined(DOXYGEN) || (__has_cpp_attribute(unlikely) >= 201803L && \
218                          (!defined(__GNUC__) || __GNUC__ > 9))
219 #define MDBX_CXX20_UNLIKELY [[unlikely]]
220 #else
221 #define MDBX_CXX20_UNLIKELY
222 #endif
223 #endif /* MDBX_CXX20_UNLIKELY */
224
225 #ifndef MDBX_HAVE_CXX20_CONCEPTS
226 #if defined(DOXYGEN) || \
227     (defined(__cpp_lib_concepts) && __cpp_lib_concepts >= 202002L)
228 #include <concepts>
229 #define MDBX_HAVE_CXX20_CONCEPTS 1
230 #else
231 #define MDBX_HAVE_CXX20_CONCEPTS 0
232 #endif /* <concepts> */
233 #endif /* MDBX_HAVE_CXX20_CONCEPTS */
234
235 #ifndef MDBX_CXX20_CONCEPT
236 #if MDBX_HAVE_CXX20_CONCEPTS
237 #define MDBX_CXX20_CONCEPT(CONCEPT, NAME) CONCEPT NAME
238 #else
239 #define MDBX_CXX20_CONCEPT(CONCEPT, NAME) typename NAME
240 #endif
241 #endif /* MDBX_CXX20_CONCEPT */
242
243 #ifndef MDBX_ASSERT_CXX20_CONCEPT_SATISFIED
244 #if MDBX_HAVE_CXX20_CONCEPTS
245 #define MDBX_ASSERT_CXX20_CONCEPT_SATISFIED(CONCEPT, TYPE) \
246   static_assert(CONCEPT<TYPE>)
247 #else
248 #define MDBX_ASSERT_CXX20_CONCEPT_SATISFIED(CONCEPT, NAME) \
249   static_assert(true, MDBX_STRINGIFY(CONCEPT) "<" MDBX_STRINGIFY(TYPE) ">")
250 #endif
251 #endif /* MDBX_ASSERT_CXX20_CONCEPT_SATISFIED */
252
253 #ifdef _MSC_VER
254 #pragma warning(push, 4)
255 #pragma warning(disable : 4127) /* conditional expression is constant */
256 #pragma warning(disable : 4251) /* 'std::FOO' needs to have dll-interface to \
257                                    be used by clients of 'mdbx::BAR' */
258 #pragma warning(disable : 4275) /* non dll-interface 'std::FOO' used as \
259                                    base for dll-interface 'mdbx::BAR' */
260 /* MSVC is mad and can generate this warning for its own intermediate
261  * automatically generated code, which becomes unreachable after some kinds of
262  * optimization (copy elision, etc). */
263 #pragma warning(disable : 4702) /* unreachable code */
264 #endif /* _MSC_VER (warnings) */
265
266 #if defined(__LCC__) && __LCC__ >= 126
267 #pragma diagnostic push
268 #if __LCC__ < 127
269 #pragma diag_suppress 3058 /* workaround: call to is_constant_evaluated() \
270                               appearing in a constant expression `true` */
271 #pragma diag_suppress 3060 /* workaround: call to is_constant_evaluated() \
272                               appearing in a constant expression `false` */
273 #endif
274 #endif /* E2K LCC (warnings) */
275
276 //------------------------------------------------------------------------------
277 /// \brief The libmdbx C++ API namespace
278 /// \ingroup cxx_api
279 namespace mdbx {
280
281 /// \defgroup cxx_api C++ API
282 /// @{
283
284 // Functions whose signature depends on the `mdbx::byte` type
285 // must be strictly defined as inline!
286 #if defined(DOXYGEN) || (defined(__cpp_char8_t) && __cpp_char8_t >= 201811)
287 // To enable all kinds of an compiler optimizations we use a byte-like type
288 // that don't presumes aliases for pointers as does the `char` type and its
289 // derivatives/typedefs.
290 // Please see https://libmdbx.dqdkfa.ru/dead-github/issues/263
291 // for reasoning of the use of `char8_t` type and switching to `__restrict__`.
292 using byte = char8_t;
293 #else
294 // Avoid `std::byte` since it doesn't add features but inconvenient
295 // restrictions.
296 using byte = unsigned char;
297 #endif /* __cpp_char8_t >= 201811*/
298
299 #if defined(__cpp_lib_endian) && __cpp_lib_endian >= 201907L
300 using endian = ::std::endian;
301 #elif defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
302     defined(__ORDER_BIG_ENDIAN__)
303 enum class endian {
304   little = __ORDER_LITTLE_ENDIAN__,
305   big = __ORDER_BIG_ENDIAN__,
306   native = __BYTE_ORDER__
307 };
308 #else
309 #error \
310     "Please use a C++ compiler provides byte order information or C++20 support"
311 #endif /* Byte Order enum */
312
313 /// \copydoc MDBX_version_info
314 using version_info = ::MDBX_version_info;
315 /// \brief Returns libmdbx version information.
316 MDBX_CXX11_CONSTEXPR const version_info &get_version() noexcept;
317 /// \copydoc MDBX_build_info
318 using build_info = ::MDBX_build_info;
319 /// \brief Returns libmdbx build information.
320 MDBX_CXX11_CONSTEXPR const build_info &get_build() noexcept;
321
322 /// \brief constexpr-enabled strlen().
323 static MDBX_CXX17_CONSTEXPR size_t strlen(const char *c_str) noexcept;
324
325 /// \brief constexpr-enabled memcpy().
326 static MDBX_CXX20_CONSTEXPR void *memcpy(void *dest, const void *src,
327                                          size_t bytes) noexcept;
328 /// \brief constexpr-enabled memcmp().
329 static MDBX_CXX20_CONSTEXPR int memcmp(const void *a, const void *b,
330                                        size_t bytes) noexcept;
331
332 /// \brief Legacy default allocator
333 /// but it is recommended to use \ref polymorphic_allocator.
334 using legacy_allocator = ::std::string::allocator_type;
335
336 struct slice;
337 struct default_capacity_policy;
338 template <class ALLOCATOR = legacy_allocator,
339           class CAPACITY_POLICY = default_capacity_policy>
340 class buffer;
341 class env;
342 class env_managed;
343 class txn;
344 class txn_managed;
345 class cursor;
346 class cursor_managed;
347
348 #if defined(DOXYGEN) || \
349     (defined(__cpp_lib_memory_resource) && \
350      __cpp_lib_memory_resource >= 201603L && _GLIBCXX_USE_CXX11_ABI)
351 /// \brief Default polymorphic allocator for modern code.
352 using polymorphic_allocator = ::std::pmr::string::allocator_type;
353 #endif /* __cpp_lib_memory_resource >= 201603L */
354
355 /// \brief Default singe-byte string.
356 template <class ALLOCATOR = legacy_allocator>
357 using string = ::std::basic_string<char, ::std::char_traits<char>, ALLOCATOR>;
358
359 using filehandle = ::mdbx_filehandle_t;
360 #if defined(DOXYGEN) || \
361     (defined(__cpp_lib_filesystem) && __cpp_lib_filesystem >= 201703L && \
362      (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || \
363       __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) && \
364      (!defined(__IPHONE_OS_VERSION_MIN_REQUIRED) || \
365       __IPHONE_OS_VERSION_MIN_REQUIRED >= 130100))
366 namespace filesystem = ::std::filesystem;
367 /// \brief Defined if `mdbx::filesystem::path` is available.
368 /// \details If defined, it is always `mdbx::filesystem::path`,
369 /// which in turn can be refs to `std::filesystem::path`
370 /// or `std::experimental::filesystem::path`.
371 /// Nonetheless `MDBX_STD_FILESYSTEM_PATH` not defined if the `::mdbx::path`
372 /// is fallbacked to c `std::string` or `std::wstring`.
373 #define MDBX_STD_FILESYSTEM_PATH ::mdbx::filesystem::path
374 #elif defined(__cpp_lib_experimental_filesystem) && \
375     __cpp_lib_experimental_filesystem >= 201406L
376 namespace filesystem = ::std::experimental::filesystem;
377 #define MDBX_STD_FILESYSTEM_PATH ::mdbx::filesystem::path
378 #endif /* MDBX_STD_FILESYSTEM_PATH */
379
380 #ifdef MDBX_STD_FILESYSTEM_PATH
381 using path = MDBX_STD_FILESYSTEM_PATH;
382 #elif defined(_WIN32) || defined(_WIN64)
383 using path = ::std::wstring;
384 #else
385 using path = ::std::string;
386 #endif /* mdbx::path */
387
388 /// \defgroup cxx_exceptions exceptions and errors
389 /// @{
390
391 /// \brief Transfers C++ exceptions thru C callbacks.
392 /// \details Implements saving exceptions before returning
393 /// from an C++'s environment to the intermediate C code and re-throwing after
394 /// returning from C to the C++'s environment.
395 class LIBMDBX_API_TYPE exception_thunk {
396   ::std::exception_ptr captured_;
397
398 public:
399   exception_thunk() noexcept = default;
400   exception_thunk(const exception_thunk &) = delete;
401   exception_thunk(exception_thunk &&) = delete;
402   exception_thunk &operator=(const exception_thunk &) = delete;
403   exception_thunk &operator=(exception_thunk &&) = delete;
404   inline bool is_clean() const noexcept;
405   inline void capture() noexcept;
406   inline void rethrow_captured() const;
407 };
408
409 /// \brief Implements error information and throwing corresponding exceptions.
410 class LIBMDBX_API_TYPE error {
411   MDBX_error_t code_;
412   inline error &operator=(MDBX_error_t error_code) noexcept;
413
414 public:
415   MDBX_CXX11_CONSTEXPR error(MDBX_error_t error_code) noexcept;
416   error(const error &) = default;
417   error(error &&) = default;
418   error &operator=(const error &) = default;
419   error &operator=(error &&) = default;
420
421   MDBX_CXX11_CONSTEXPR friend bool operator==(const error &a,
422                                               const error &b) noexcept;
423   MDBX_CXX11_CONSTEXPR friend bool operator!=(const error &a,
424                                               const error &b) noexcept;
425
426   MDBX_CXX11_CONSTEXPR bool is_success() const noexcept;
427   MDBX_CXX11_CONSTEXPR bool is_result_true() const noexcept;
428   MDBX_CXX11_CONSTEXPR bool is_result_false() const noexcept;
429   MDBX_CXX11_CONSTEXPR bool is_failure() const noexcept;
430
431   /// \brief Returns error code.
432   MDBX_CXX11_CONSTEXPR MDBX_error_t code() const noexcept;
433
434   /// \brief Returns message for MDBX's errors only and "SYSTEM" for others.
435   const char *what() const noexcept;
436
437   /// \brief Returns message for any errors.
438   ::std::string message() const;
439
440   /// \brief Returns true for MDBX's errors.
441   MDBX_CXX11_CONSTEXPR bool is_mdbx_error() const noexcept;
442   /// \brief Panics on unrecoverable errors inside destructors etc.
443   [[noreturn]] void panic(const char *context_where_when,
444                           const char *func_who_what) const noexcept;
445   [[noreturn]] void throw_exception() const;
446   [[noreturn]] static inline void throw_exception(int error_code);
447   inline void throw_on_failure() const;
448   inline void success_or_throw() const;
449   inline void success_or_throw(const exception_thunk &) const;
450   inline void panic_on_failure(const char *context_where,
451                                const char *func_who) const noexcept;
452   inline void success_or_panic(const char *context_where,
453                                const char *func_who) const noexcept;
454   static inline void throw_on_nullptr(const void *ptr, MDBX_error_t error_code);
455   static inline void success_or_throw(MDBX_error_t error_code);
456   static void success_or_throw(int error_code) {
457     success_or_throw(static_cast<MDBX_error_t>(error_code));
458   }
459   static inline void throw_on_failure(int error_code);
460   static inline bool boolean_or_throw(int error_code);
461   static inline void success_or_throw(int error_code, const exception_thunk &);
462   static inline void panic_on_failure(int error_code, const char *context_where,
463                                       const char *func_who) noexcept;
464   static inline void success_or_panic(int error_code, const char *context_where,
465                                       const char *func_who) noexcept;
466 };
467
468 /// \brief Base class for all libmdbx's exceptions that are corresponds
469 /// to libmdbx errors.
470 ///
471 /// \see MDBX_error_t
472 class LIBMDBX_API_TYPE exception : public ::std::runtime_error {
473   using base = ::std::runtime_error;
474   ::mdbx::error error_;
475
476 public:
477   exception(const ::mdbx::error &) noexcept;
478   exception(const exception &) = default;
479   exception(exception &&) = default;
480   exception &operator=(const exception &) = default;
481   exception &operator=(exception &&) = default;
482   virtual ~exception() noexcept;
483   const ::mdbx::error error() const noexcept { return error_; }
484 };
485
486 /// \brief Fatal exception that lead termination anyway
487 /// in dangerous unrecoverable cases.
488 class LIBMDBX_API_TYPE fatal : public exception {
489   using base = exception;
490
491 public:
492   fatal(const ::mdbx::error &) noexcept;
493   fatal(const exception &src) noexcept : fatal(src.error()) {}
494   fatal(exception &&src) noexcept : fatal(src.error()) {}
495   fatal(const fatal &src) noexcept : fatal(src.error()) {}
496   fatal(fatal &&src) noexcept : fatal(src.error()) {}
497   fatal &operator=(fatal &&) = default;
498   fatal &operator=(const fatal &) = default;
499   virtual ~fatal() noexcept;
500 };
501
502 #define MDBX_DECLARE_EXCEPTION(NAME) \
503   struct LIBMDBX_API_TYPE NAME : public exception { \
504     NAME(const ::mdbx::error &); \
505     virtual ~NAME() noexcept; \
506   }
507 MDBX_DECLARE_EXCEPTION(bad_map_id);
508 MDBX_DECLARE_EXCEPTION(bad_transaction);
509 MDBX_DECLARE_EXCEPTION(bad_value_size);
510 MDBX_DECLARE_EXCEPTION(db_corrupted);
511 MDBX_DECLARE_EXCEPTION(db_full);
512 MDBX_DECLARE_EXCEPTION(db_invalid);
513 MDBX_DECLARE_EXCEPTION(db_too_large);
514 MDBX_DECLARE_EXCEPTION(db_unable_extend);
515 MDBX_DECLARE_EXCEPTION(db_version_mismatch);
516 MDBX_DECLARE_EXCEPTION(db_wanna_write_for_recovery);
517 MDBX_DECLARE_EXCEPTION(incompatible_operation);
518 MDBX_DECLARE_EXCEPTION(internal_page_full);
519 MDBX_DECLARE_EXCEPTION(internal_problem);
520 MDBX_DECLARE_EXCEPTION(key_exists);
521 MDBX_DECLARE_EXCEPTION(key_mismatch);
522 MDBX_DECLARE_EXCEPTION(max_maps_reached);
523 MDBX_DECLARE_EXCEPTION(max_readers_reached);
524 MDBX_DECLARE_EXCEPTION(multivalue);
525 MDBX_DECLARE_EXCEPTION(no_data);
526 MDBX_DECLARE_EXCEPTION(not_found);
527 MDBX_DECLARE_EXCEPTION(operation_not_permitted);
528 MDBX_DECLARE_EXCEPTION(permission_denied_or_not_writeable);
529 MDBX_DECLARE_EXCEPTION(reader_slot_busy);
530 MDBX_DECLARE_EXCEPTION(remote_media);
531 MDBX_DECLARE_EXCEPTION(something_busy);
532 MDBX_DECLARE_EXCEPTION(thread_mismatch);
533 MDBX_DECLARE_EXCEPTION(transaction_full);
534 MDBX_DECLARE_EXCEPTION(transaction_overlapping);
535 #undef MDBX_DECLARE_EXCEPTION
536
537 [[noreturn]] LIBMDBX_API void throw_too_small_target_buffer();
538 [[noreturn]] LIBMDBX_API void throw_max_length_exceeded();
539 [[noreturn]] LIBMDBX_API void throw_out_range();
540 [[noreturn]] LIBMDBX_API void throw_allocators_mismatch();
541 static MDBX_CXX14_CONSTEXPR size_t check_length(size_t bytes);
542 static MDBX_CXX14_CONSTEXPR size_t check_length(size_t headroom,
543                                                 size_t payload);
544 static MDBX_CXX14_CONSTEXPR size_t check_length(size_t headroom, size_t payload,
545                                                 size_t tailroom);
546
547 /// end of cxx_exceptions @}
548
549 //------------------------------------------------------------------------------
550
551 /// \defgroup cxx_data slices and buffers
552 /// @{
553
554 #if MDBX_HAVE_CXX20_CONCEPTS
555
556 template <typename T>
557 concept MutableByteProducer = requires(T a, char array[42]) {
558   { a.is_empty() } -> std::same_as<bool>;
559   { a.envisage_result_length() } -> std::same_as<size_t>;
560   { a.write_bytes(&array[0], size_t(42)) } -> std::same_as<char *>;
561 };
562
563 template <typename T>
564 concept ImmutableByteProducer = requires(const T &a, char array[42]) {
565   { a.is_empty() } -> std::same_as<bool>;
566   { a.envisage_result_length() } -> std::same_as<size_t>;
567   { a.write_bytes(&array[0], size_t(42)) } -> std::same_as<char *>;
568 };
569
570 template <typename T>
571 concept SliceTranscoder = ImmutableByteProducer<T> &&
572     requires(const slice &source, const T &a) {
573   T(source);
574   { a.is_erroneous() } -> std::same_as<bool>;
575 };
576
577 #endif /* MDBX_HAVE_CXX20_CONCEPTS */
578
579 template <class ALLOCATOR = legacy_allocator,
580           typename CAPACITY_POLICY = default_capacity_policy,
581           MDBX_CXX20_CONCEPT(MutableByteProducer, PRODUCER)>
582 inline buffer<ALLOCATOR, CAPACITY_POLICY>
583 make_buffer(PRODUCER &producer, const ALLOCATOR &allocator = ALLOCATOR());
584
585 template <class ALLOCATOR = legacy_allocator,
586           typename CAPACITY_POLICY = default_capacity_policy,
587           MDBX_CXX20_CONCEPT(ImmutableByteProducer, PRODUCER)>
588 inline buffer<ALLOCATOR, CAPACITY_POLICY>
589 make_buffer(const PRODUCER &producer, const ALLOCATOR &allocator = ALLOCATOR());
590
591 template <class ALLOCATOR = legacy_allocator,
592           MDBX_CXX20_CONCEPT(MutableByteProducer, PRODUCER)>
593 inline string<ALLOCATOR> make_string(PRODUCER &producer,
594                                      const ALLOCATOR &allocator = ALLOCATOR());
595
596 template <class ALLOCATOR = legacy_allocator,
597           MDBX_CXX20_CONCEPT(ImmutableByteProducer, PRODUCER)>
598 inline string<ALLOCATOR> make_string(const PRODUCER &producer,
599                                      const ALLOCATOR &allocator = ALLOCATOR());
600
601 /// \brief References a data located outside the slice.
602 ///
603 /// The `slice` is similar in many ways to `std::string_view`, but it
604 /// implements specific capabilities and manipulates with bytes but
605 /// not a characters.
606 ///
607 /// \copydetails MDBX_val
608 struct LIBMDBX_API_TYPE slice : public ::MDBX_val {
609   /// \todo slice& operator<<(slice&, ...) for reading
610   /// \todo key-to-value (parse/unpack) functions
611   /// \todo template<class X> key(X); for decoding keys while reading
612
613   enum { max_length = MDBX_MAXDATASIZE };
614
615   /// \brief Create an empty slice.
616   MDBX_CXX11_CONSTEXPR slice() noexcept;
617
618   /// \brief Create a slice that refers to [0,bytes-1] of memory bytes pointed
619   /// by ptr.
620   MDBX_CXX14_CONSTEXPR slice(const void *ptr, size_t bytes);
621
622   /// \brief Create a slice that refers to [begin,end] of memory bytes.
623   MDBX_CXX14_CONSTEXPR slice(const void *begin, const void *end);
624
625   /// \brief Create a slice that refers to text[0,strlen(text)-1].
626   template <size_t SIZE>
627   MDBX_CXX14_CONSTEXPR slice(const char (&text)[SIZE]) : slice(text, SIZE - 1) {
628     MDBX_CONSTEXPR_ASSERT(SIZE > 0 && text[SIZE - 1] == '\0');
629   }
630   /// \brief Create a slice that refers to c_str[0,strlen(c_str)-1].
631   explicit MDBX_CXX17_CONSTEXPR slice(const char *c_str);
632
633   /// \brief Create a slice that refers to the contents of "str".
634   /// \note 'explicit' to avoid reference to the temporary std::string instance.
635   template <class CHAR, class T, class A>
636   explicit MDBX_CXX20_CONSTEXPR
637   slice(const ::std::basic_string<CHAR, T, A> &str)
638       : slice(str.data(), str.length() * sizeof(CHAR)) {}
639
640   MDBX_CXX14_CONSTEXPR slice(const MDBX_val &src);
641   MDBX_CXX11_CONSTEXPR slice(const slice &) noexcept = default;
642   MDBX_CXX14_CONSTEXPR slice(MDBX_val &&src);
643   MDBX_CXX14_CONSTEXPR slice(slice &&src) noexcept;
644
645 #if defined(DOXYGEN) || \
646     (defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L)
647   /// \brief Create a slice that refers to the same contents as "string_view"
648   template <class CHAR, class T>
649   MDBX_CXX14_CONSTEXPR slice(const ::std::basic_string_view<CHAR, T> &sv)
650       : slice(sv.data(), sv.data() + sv.length()) {}
651
652   template <class CHAR, class T>
653   slice(::std::basic_string_view<CHAR, T> &&sv) : slice(sv) {
654     sv = {};
655   }
656 #endif /* __cpp_lib_string_view >= 201606L */
657
658   template <size_t SIZE>
659   static MDBX_CXX14_CONSTEXPR slice wrap(const char (&text)[SIZE]) {
660     return slice(text);
661   }
662
663   template <typename POD>
664   MDBX_CXX14_CONSTEXPR static slice wrap(const POD &pod) {
665     static_assert(::std::is_standard_layout<POD>::value &&
666                       !::std::is_pointer<POD>::value,
667                   "Must be a standard layout type!");
668     return slice(&pod, sizeof(pod));
669   }
670
671   inline slice &assign(const void *ptr, size_t bytes);
672   inline slice &assign(const slice &src) noexcept;
673   inline slice &assign(const ::MDBX_val &src);
674   inline slice &assign(slice &&src) noexcept;
675   inline slice &assign(::MDBX_val &&src);
676   inline slice &assign(const void *begin, const void *end);
677   template <class CHAR, class T, class ALLOCATOR>
678   slice &assign(const ::std::basic_string<CHAR, T, ALLOCATOR> &str) {
679     return assign(str.data(), str.length() * sizeof(CHAR));
680   }
681   inline slice &assign(const char *c_str);
682 #if defined(DOXYGEN) || \
683     (defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L)
684   template <class CHAR, class T>
685   slice &assign(const ::std::basic_string_view<CHAR, T> &view) {
686     return assign(view.begin(), view.end());
687   }
688   template <class CHAR, class T>
689   slice &assign(::std::basic_string_view<CHAR, T> &&view) {
690     assign(view);
691     view = {};
692     return *this;
693   }
694 #endif /* __cpp_lib_string_view >= 201606L */
695
696   slice &operator=(const slice &) noexcept = default;
697   inline slice &operator=(slice &&src) noexcept;
698   inline slice &operator=(::MDBX_val &&src);
699
700 #if defined(DOXYGEN) || \
701     (defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L)
702   template <class CHAR, class T>
703   slice &operator=(const ::std::basic_string_view<CHAR, T> &view) {
704     return assign(view);
705   }
706
707   template <class CHAR, class T>
708   slice &operator=(::std::basic_string_view<CHAR, T> &&view) {
709     return assign(view);
710   }
711
712   /// \brief Return a string_view that references the same data as this slice.
713   template <class CHAR = char, class T = ::std::char_traits<CHAR>>
714   MDBX_CXX11_CONSTEXPR ::std::basic_string_view<CHAR, T>
715   string_view() const noexcept {
716     static_assert(sizeof(CHAR) == 1, "Must be single byte characters");
717     return ::std::basic_string_view<CHAR, T>(char_ptr(), length());
718   }
719
720   /// \brief Return a string_view that references the same data as this slice.
721   template <class CHAR, class T>
722   MDBX_CXX11_CONSTEXPR explicit
723   operator ::std::basic_string_view<CHAR, T>() const noexcept {
724     return this->string_view<CHAR, T>();
725   }
726 #endif /* __cpp_lib_string_view >= 201606L */
727
728   template <class CHAR = char, class T = ::std::char_traits<CHAR>,
729             class ALLOCATOR = legacy_allocator>
730   MDBX_CXX20_CONSTEXPR ::std::basic_string<CHAR, T, ALLOCATOR>
731   as_string(const ALLOCATOR &allocator = ALLOCATOR()) const {
732     static_assert(sizeof(CHAR) == 1, "Must be single byte characters");
733     return ::std::basic_string<CHAR, T, ALLOCATOR>(char_ptr(), length(),
734                                                    allocator);
735   }
736
737   template <class CHAR, class T, class ALLOCATOR>
738   MDBX_CXX20_CONSTEXPR explicit
739   operator ::std::basic_string<CHAR, T, ALLOCATOR>() const {
740     return as_string<CHAR, T, ALLOCATOR>();
741   }
742
743   /// \brief Returns a string with a hexadecimal dump of the slice content.
744   template <class ALLOCATOR = legacy_allocator>
745   inline string<ALLOCATOR>
746   as_hex_string(bool uppercase = false, unsigned wrap_width = 0,
747                 const ALLOCATOR &allocator = ALLOCATOR()) const;
748
749   /// \brief Returns a string with a
750   /// [Base58](https://en.wikipedia.org/wiki/Base58) dump of the slice content.
751   template <class ALLOCATOR = legacy_allocator>
752   inline string<ALLOCATOR>
753   as_base58_string(unsigned wrap_width = 0,
754                    const ALLOCATOR &allocator = ALLOCATOR()) const;
755
756   /// \brief Returns a string with a
757   /// [Base58](https://en.wikipedia.org/wiki/Base64) dump of the slice content.
758   template <class ALLOCATOR = legacy_allocator>
759   inline string<ALLOCATOR>
760   as_base64_string(unsigned wrap_width = 0,
761                    const ALLOCATOR &allocator = ALLOCATOR()) const;
762
763   /// \brief Returns a buffer with a hexadecimal dump of the slice content.
764   template <class ALLOCATOR = legacy_allocator,
765             class CAPACITY_POLICY = default_capacity_policy>
766   inline buffer<ALLOCATOR, CAPACITY_POLICY>
767   encode_hex(bool uppercase = false, unsigned wrap_width = 0,
768              const ALLOCATOR &allocator = ALLOCATOR()) const;
769
770   /// \brief Returns a buffer with a
771   /// [Base58](https://en.wikipedia.org/wiki/Base58) dump of the slice content.
772   template <class ALLOCATOR = legacy_allocator,
773             class CAPACITY_POLICY = default_capacity_policy>
774   inline buffer<ALLOCATOR, CAPACITY_POLICY>
775   encode_base58(unsigned wrap_width = 0,
776                 const ALLOCATOR &allocator = ALLOCATOR()) const;
777
778   /// \brief Returns a buffer with a
779   /// [Base64](https://en.wikipedia.org/wiki/Base64) dump of the slice content.
780   template <class ALLOCATOR = legacy_allocator,
781             class CAPACITY_POLICY = default_capacity_policy>
782   inline buffer<ALLOCATOR, CAPACITY_POLICY>
783   encode_base64(unsigned wrap_width = 0,
784                 const ALLOCATOR &allocator = ALLOCATOR()) const;
785
786   /// \brief Decodes hexadecimal dump from the slice content to returned buffer.
787   template <class ALLOCATOR = legacy_allocator,
788             class CAPACITY_POLICY = default_capacity_policy>
789   inline buffer<ALLOCATOR, CAPACITY_POLICY>
790   hex_decode(bool ignore_spaces = false,
791              const ALLOCATOR &allocator = ALLOCATOR()) const;
792
793   /// \brief Decodes [Base58](https://en.wikipedia.org/wiki/Base58) dump
794   /// from the slice content to returned buffer.
795   template <class ALLOCATOR = legacy_allocator,
796             class CAPACITY_POLICY = default_capacity_policy>
797   inline buffer<ALLOCATOR, CAPACITY_POLICY>
798   base58_decode(bool ignore_spaces = false,
799                 const ALLOCATOR &allocator = ALLOCATOR()) const;
800
801   /// \brief Decodes [Base64](https://en.wikipedia.org/wiki/Base64) dump
802   /// from the slice content to returned buffer.
803   template <class ALLOCATOR = legacy_allocator,
804             class CAPACITY_POLICY = default_capacity_policy>
805   inline buffer<ALLOCATOR, CAPACITY_POLICY>
806   base64_decode(bool ignore_spaces = false,
807                 const ALLOCATOR &allocator = ALLOCATOR()) const;
808
809   /// \brief Checks whether the content of the slice is printable.
810   /// \param [in] disable_utf8 By default if `disable_utf8` is `false` function
811   /// checks that content bytes are printable ASCII-7 characters or a valid UTF8
812   /// sequences. Otherwise, if if `disable_utf8` is `true` function checks that
813   /// content bytes are printable extended 8-bit ASCII codes.
814   MDBX_NOTHROW_PURE_FUNCTION bool
815   is_printable(bool disable_utf8 = false) const noexcept;
816
817   /// \brief Checks whether the content of the slice is a hexadecimal dump.
818   /// \param [in] ignore_spaces If `true` function will skips spaces surrounding
819   /// (before, between and after) a encoded bytes. However, spaces should not
820   /// break a pair of characters encoding a single byte.
821   inline MDBX_NOTHROW_PURE_FUNCTION bool
822   is_hex(bool ignore_spaces = false) const noexcept;
823
824   /// \brief Checks whether the content of the slice is a
825   /// [Base58](https://en.wikipedia.org/wiki/Base58) dump.
826   /// \param [in] ignore_spaces If `true` function will skips spaces surrounding
827   /// (before, between and after) a encoded bytes. However, spaces should not
828   /// break a code group of characters.
829   inline MDBX_NOTHROW_PURE_FUNCTION bool
830   is_base58(bool ignore_spaces = false) const noexcept;
831
832   /// \brief Checks whether the content of the slice is a
833   /// [Base64](https://en.wikipedia.org/wiki/Base64) dump.
834   /// \param [in] ignore_spaces If `true` function will skips spaces surrounding
835   /// (before, between and after) a encoded bytes. However, spaces should not
836   /// break a code group of characters.
837   inline MDBX_NOTHROW_PURE_FUNCTION bool
838   is_base64(bool ignore_spaces = false) const noexcept;
839
840   inline void swap(slice &other) noexcept;
841
842 #if defined(DOXYGEN) || \
843     (defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L)
844   template <class CHAR, class T>
845   void swap(::std::basic_string_view<CHAR, T> &view) noexcept {
846     static_assert(sizeof(CHAR) == 1, "Must be single byte characters");
847     const auto temp = ::std::basic_string_view<CHAR, T>(*this);
848     *this = view;
849     view = temp;
850   }
851 #endif /* __cpp_lib_string_view >= 201606L */
852
853   /// \brief Returns casted to pointer to byte an address of data.
854   MDBX_CXX11_CONSTEXPR const byte *byte_ptr() const noexcept;
855   MDBX_CXX11_CONSTEXPR byte *byte_ptr() noexcept;
856
857   /// \brief Returns casted to pointer to byte an end of data.
858   MDBX_CXX11_CONSTEXPR const byte *end_byte_ptr() const noexcept;
859   MDBX_CXX11_CONSTEXPR byte *end_byte_ptr() noexcept;
860
861   /// \brief Returns casted to pointer to char an address of data.
862   MDBX_CXX11_CONSTEXPR const char *char_ptr() const noexcept;
863   MDBX_CXX11_CONSTEXPR char *char_ptr() noexcept;
864
865   /// \brief Returns casted to pointer to char an end of data.
866   MDBX_CXX11_CONSTEXPR const char *end_char_ptr() const noexcept;
867   MDBX_CXX11_CONSTEXPR char *end_char_ptr() noexcept;
868
869   /// \brief Return a pointer to the beginning of the referenced data.
870   MDBX_CXX11_CONSTEXPR const void *data() const noexcept;
871   MDBX_CXX11_CONSTEXPR void *data() noexcept;
872
873   /// \brief Return a pointer to the ending of the referenced data.
874   MDBX_CXX11_CONSTEXPR const void *end() const noexcept;
875   MDBX_CXX11_CONSTEXPR void *end() noexcept;
876
877   /// \brief Returns the number of bytes.
878   MDBX_CXX11_CONSTEXPR size_t length() const noexcept;
879
880   /// \brief Set slice length.
881   MDBX_CXX14_CONSTEXPR slice &set_length(size_t bytes);
882
883   /// \brief Sets the length by specifying the end of the slice data.
884   MDBX_CXX14_CONSTEXPR slice &set_end(const void *ptr);
885
886   /// \brief Checks whether the slice is empty.
887   MDBX_CXX11_CONSTEXPR bool empty() const noexcept;
888
889   /// \brief Checks whether the slice data pointer is nullptr.
890   MDBX_CXX11_CONSTEXPR bool is_null() const noexcept;
891
892   /// \brief Returns the number of bytes.
893   MDBX_CXX11_CONSTEXPR size_t size() const noexcept;
894
895   /// \brief Returns true if slice is not empty.
896   MDBX_CXX11_CONSTEXPR operator bool() const noexcept;
897
898   /// \brief Depletes content of slice and make it invalid.
899   MDBX_CXX14_CONSTEXPR void invalidate() noexcept;
900
901   /// \brief Makes the slice empty and referencing to nothing.
902   MDBX_CXX14_CONSTEXPR void clear() noexcept;
903
904   /// \brief Drops the first "n" bytes from this slice.
905   /// \pre REQUIRES: `n <= size()`
906   inline void remove_prefix(size_t n) noexcept;
907
908   /// \brief Drops the last "n" bytes from this slice.
909   /// \pre REQUIRES: `n <= size()`
910   inline void remove_suffix(size_t n) noexcept;
911
912   /// \brief Drops the first "n" bytes from this slice.
913   /// \throws std::out_of_range if `n > size()`
914   inline void safe_remove_prefix(size_t n);
915
916   /// \brief Drops the last "n" bytes from this slice.
917   /// \throws std::out_of_range if `n > size()`
918   inline void safe_remove_suffix(size_t n);
919
920   /// \brief Checks if the data starts with the given prefix.
921   MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX14_CONSTEXPR bool
922   starts_with(const slice &prefix) const noexcept;
923
924   /// \brief Checks if the data ends with the given suffix.
925   MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX14_CONSTEXPR bool
926   ends_with(const slice &suffix) const noexcept;
927
928   /// \brief Returns the nth byte in the referenced data.
929   /// \pre REQUIRES: `n < size()`
930   MDBX_CXX11_CONSTEXPR byte operator[](size_t n) const noexcept;
931
932   /// \brief Returns the nth byte in the referenced data with bounds checking.
933   /// \throws std::out_of_range if `n >= size()`
934   MDBX_CXX11_CONSTEXPR byte at(size_t n) const;
935
936   /// \brief Returns the first "n" bytes of the slice.
937   /// \pre REQUIRES: `n <= size()`
938   MDBX_CXX14_CONSTEXPR slice head(size_t n) const noexcept;
939
940   /// \brief Returns the last "n" bytes of the slice.
941   /// \pre REQUIRES: `n <= size()`
942   MDBX_CXX14_CONSTEXPR slice tail(size_t n) const noexcept;
943
944   /// \brief Returns the middle "n" bytes of the slice.
945   /// \pre REQUIRES: `from + n <= size()`
946   MDBX_CXX14_CONSTEXPR slice middle(size_t from, size_t n) const noexcept;
947
948   /// \brief Returns the first "n" bytes of the slice.
949   /// \throws std::out_of_range if `n >= size()`
950   MDBX_CXX14_CONSTEXPR slice safe_head(size_t n) const;
951
952   /// \brief Returns the last "n" bytes of the slice.
953   /// \throws std::out_of_range if `n >= size()`
954   MDBX_CXX14_CONSTEXPR slice safe_tail(size_t n) const;
955
956   /// \brief Returns the middle "n" bytes of the slice.
957   /// \throws std::out_of_range if `from + n >= size()`
958   MDBX_CXX14_CONSTEXPR slice safe_middle(size_t from, size_t n) const;
959
960   /// \brief Returns the hash value of referenced data.
961   /// \attention Function implementation and returned hash values may changed
962   /// version to version, and in future the t1ha3 will be used here. Therefore
963   /// values obtained from this function shouldn't be persisted anywhere.
964   MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX14_CONSTEXPR size_t
965   hash_value() const noexcept;
966
967   /// \brief Three-way fast non-lexicographically length-based comparison.
968   /// \details Firstly compares length and if it equal then compare content
969   /// lexicographically. \return value:
970   /// `== 0` if `a` the same as `b`;
971   /// `< 0` if `a` shorter than `b`,
972   /// or the same length and lexicographically less than `b`;
973   /// `> 0` if `a` longer than `b`,
974   /// or the same length and lexicographically great than `b`.
975   MDBX_NOTHROW_PURE_FUNCTION static MDBX_CXX14_CONSTEXPR intptr_t
976   compare_fast(const slice &a, const slice &b) noexcept;
977
978   /// \brief Three-way lexicographically comparison.
979   /// \return value:
980   /// `== 0` if `a` lexicographically equal `b`;
981   /// `< 0` if `a` lexicographically less than `b`;
982   /// `> 0` if `a` lexicographically great than `b`.
983   MDBX_NOTHROW_PURE_FUNCTION static MDBX_CXX14_CONSTEXPR intptr_t
984   compare_lexicographically(const slice &a, const slice &b) noexcept;
985   friend MDBX_CXX14_CONSTEXPR bool operator==(const slice &a,
986                                               const slice &b) noexcept;
987   friend MDBX_CXX14_CONSTEXPR bool operator<(const slice &a,
988                                              const slice &b) noexcept;
989   friend MDBX_CXX14_CONSTEXPR bool operator>(const slice &a,
990                                              const slice &b) noexcept;
991   friend MDBX_CXX14_CONSTEXPR bool operator<=(const slice &a,
992                                               const slice &b) noexcept;
993   friend MDBX_CXX14_CONSTEXPR bool operator>=(const slice &a,
994                                               const slice &b) noexcept;
995   friend MDBX_CXX14_CONSTEXPR bool operator!=(const slice &a,
996                                               const slice &b) noexcept;
997
998   /// \brief Checks the slice is not refers to null address or has zero length.
999   MDBX_CXX11_CONSTEXPR bool is_valid() const noexcept {
1,000     return !(iov_base == nullptr && iov_len != 0);
1,001   }
1,002
1,003   /// \brief Build an invalid slice which non-zero length and refers to null
1,004   /// address.
1,005   MDBX_CXX14_CONSTEXPR static slice invalid() noexcept {
1,006     return slice(size_t(-1));
1,007   }
1,008
1,009 protected:
1,010   MDBX_CXX11_CONSTEXPR slice(size_t invalid_length) noexcept
1,011       : ::MDBX_val({nullptr, invalid_length}) {}
1,012 };
1,013
1,014 //------------------------------------------------------------------------------
1,015
1,016 namespace allocation_aware_details {
1,017
1,018 template <typename A> constexpr bool allocator_is_always_equal() noexcept {
1,019 #if defined(__cpp_lib_allocator_traits_is_always_equal) && \
1,020     __cpp_lib_allocator_traits_is_always_equal >= 201411L
1,021   return ::std::allocator_traits<A>::is_always_equal::value;
1,022 #else
1,023   return ::std::is_empty<A>::value;
1,024 #endif /* __cpp_lib_allocator_traits_is_always_equal */
1,025 }
1,026
1,027 template <typename T, typename A = typename T::allocator_type,
1,028           bool PoCMA = ::std::allocator_traits<
1,029               A>::propagate_on_container_move_assignment::value>
1,030 struct move_assign_alloc;
1,031
1,032 template <typename T, typename A> struct move_assign_alloc<T, A, false> {
1,033   static constexpr bool is_nothrow() noexcept {
1,034     return allocator_is_always_equal<A>();
1,035   }
1,036   static MDBX_CXX20_CONSTEXPR bool is_moveable(T *target, T &source) noexcept {
1,037     return allocator_is_always_equal<A>() ||
1,038            target->get_allocator() == source.get_allocator();
1,039   }
1,040   static MDBX_CXX20_CONSTEXPR void propagate(T *target, T &source) noexcept {
1,041     assert(target->get_allocator() != source.get_allocator());
1,042     (void)target;
1,043     (void)source;
1,044   }
1,045 };
1,046
1,047 template <typename T, typename A> struct move_assign_alloc<T, A, true> {
1,048   static constexpr bool is_nothrow() noexcept {
1,049     return allocator_is_always_equal<A>() ||
1,050            ::std::is_nothrow_move_assignable<A>::value;
1,051   }
1,052   static constexpr bool is_moveable(T *, T &) noexcept { return true; }
1,053   static MDBX_CXX20_CONSTEXPR void propagate(T *target, T &source) {
1,054     assert(target->get_allocator() != source.get_allocator());
1,055     target->get_allocator() = ::std::move(source.get_allocator());
1,056   }
1,057 };
1,058
1,059 template <typename T, typename A = typename T::allocator_type,
1,060           bool PoCCA = ::std::allocator_traits<
1,061               A>::propagate_on_container_copy_assignment::value>
1,062 struct copy_assign_alloc;
1,063
1,064 template <typename T, typename A> struct copy_assign_alloc<T, A, false> {
1,065   static constexpr bool is_nothrow() noexcept { return false; }
1,066   static MDBX_CXX20_CONSTEXPR void propagate(T *target,
1,067                                              const T &source) noexcept {
1,068     assert(target->get_allocator() != source.get_allocator());
1,069     (void)target;
1,070     (void)source;
1,071   }
1,072 };
1,073
1,074 template <typename T, typename A> struct copy_assign_alloc<T, A, true> {
1,075   static constexpr bool is_nothrow() noexcept {
1,076     return allocator_is_always_equal<A>() ||
1,077            ::std::is_nothrow_copy_assignable<A>::value;
1,078   }
1,079   static MDBX_CXX20_CONSTEXPR void
1,080   propagate(T *target, const T &source) noexcept(is_nothrow()) {
1,081     if MDBX_IF_CONSTEXPR (!allocator_is_always_equal<A>()) {
1,082       if (MDBX_UNLIKELY(target->get_allocator() != source.get_allocator()))
1,083         MDBX_CXX20_UNLIKELY target->get_allocator() =
1,084             ::std::allocator_traits<A>::select_on_container_copy_construction(
1,085                 source.get_allocator());
1,086     } else {
1,087       /* gag for buggy compilers */
1,088       (void)target;
1,089       (void)source;
1,090     }
1,091   }
1,092 };
1,093
1,094 template <typename T, typename A = typename T::allocator_type,
1,095           bool PoCS =
1,096               ::std::allocator_traits<A>::propagate_on_container_swap::value>
1,097 struct swap_alloc;
1,098
1,099 template <typename T, typename A> struct swap_alloc<T, A, false> {
1,100   static constexpr bool is_nothrow() noexcept {
1,101     return allocator_is_always_equal<A>();
1,102   }
1,103   static MDBX_CXX20_CONSTEXPR void propagate(T *target,
1,104                                              T &source) noexcept(is_nothrow()) {
1,105     if MDBX_IF_CONSTEXPR (!allocator_is_always_equal<A>()) {
1,106       if (MDBX_UNLIKELY(target->get_allocator() != source.get_allocator()))
1,107         MDBX_CXX20_UNLIKELY throw_allocators_mismatch();
1,108     } else {
1,109       /* gag for buggy compilers */
1,110       (void)target;
1,111       (void)source;
1,112     }
1,113   }
1,114 };
1,115
1,116 template <typename T, typename A> struct swap_alloc<T, A, true> {
1,117   static constexpr bool is_nothrow() noexcept {
1,118     return allocator_is_always_equal<A>() ||
1,119 #if defined(__cpp_lib_is_swappable) && __cpp_lib_is_swappable >= 201603L
1,120            ::std::is_nothrow_swappable<A>() ||
1,121 #endif /* __cpp_lib_is_swappable >= 201603L */
1,122            (::std::is_nothrow_move_constructible<A>::value &&
1,123             ::std::is_nothrow_move_assignable<A>::value);
1,124   }
1,125   static MDBX_CXX20_CONSTEXPR void propagate(T *target,
1,126                                              T &source) noexcept(is_nothrow()) {
1,127     if MDBX_IF_CONSTEXPR (!allocator_is_always_equal<A>()) {
1,128       if (MDBX_UNLIKELY(target->get_allocator() != source.get_allocator()))
1,129         MDBX_CXX20_UNLIKELY ::std::swap(*target, source);
1,130     } else {
1,131       /* gag for buggy compilers */
1,132       (void)target;
1,133       (void)source;
1,134     }
1,135   }
1,136 };
1,137
1,138 } // namespace allocation_aware_details
1,139
1,140 struct default_capacity_policy {
1,141   enum : size_t {
1,142     extra_inplace_storage = 0,
1,143     pettiness_threshold = 64,
1,144     max_reserve = 65536
1,145   };
1,146
1,147   static MDBX_CXX11_CONSTEXPR size_t round(const size_t value) {
1,148     static_assert((pettiness_threshold & (pettiness_threshold - 1)) == 0,
1,149                   "pettiness_threshold must be a power of 2");
1,150     static_assert(pettiness_threshold % 2 == 0,
1,151                   "pettiness_threshold must be even");
1,152     static_assert(pettiness_threshold >= sizeof(uint64_t),
1,153                   "pettiness_threshold must be > 7");
1,154     constexpr const auto pettiness_mask = ~size_t(pettiness_threshold - 1);
1,155     return (value + pettiness_threshold - 1) & pettiness_mask;
1,156   }
1,157
1,158   static MDBX_CXX11_CONSTEXPR size_t advise(const size_t current,
1,159                                             const size_t wanna) {
1,160     static_assert(max_reserve % pettiness_threshold == 0,
1,161                   "max_reserve must be a multiple of pettiness_threshold");
1,162     static_assert(max_reserve / 3 > pettiness_threshold,
1,163                   "max_reserve must be > pettiness_threshold * 3");
1,164     if (wanna > current)
1,165       /* doubling capacity, but don't made reserve more than max_reserve */
1,166       return round(wanna + ::std::min(size_t(max_reserve), current));
1,167
1,168     if (current - wanna >
1,169         /* shrink if reserve will more than half of current or max_reserve,
1,170          * but not less than pettiness_threshold */
1,171         ::std::min(wanna + pettiness_threshold, size_t(max_reserve)))
1,172       return round(wanna);
1,173
1,174     /* keep unchanged */
1,175     return current;
1,176   }
1,177 };
1,178
1,179 /// \brief Hexadecimal encoder which satisfy \ref SliceTranscoder concept.
1,180 struct LIBMDBX_API to_hex {
1,181   const slice source;
1,182   const bool uppercase = false;
1,183   const unsigned wrap_width = 0;
1,184   MDBX_CXX11_CONSTEXPR to_hex(const slice &source, bool uppercase = false,
1,185                               unsigned wrap_width = 0) noexcept
1,186       : source(source), uppercase(uppercase), wrap_width(wrap_width) {
1,187     MDBX_ASSERT_CXX20_CONCEPT_SATISFIED(SliceTranscoder, to_hex);
1,188   }
1,189
1,190   /// \brief Returns a string with a hexadecimal dump of a passed slice.
1,191   template <class ALLOCATOR = legacy_allocator>
1,192   string<ALLOCATOR> as_string(const ALLOCATOR &allocator = ALLOCATOR()) const {
1,193     return make_string<ALLOCATOR>(*this, allocator);
1,194   }
1,195
1,196   /// \brief Returns a buffer with a hexadecimal dump of a passed slice.
1,197   template <class ALLOCATOR = legacy_allocator,
1,198             typename CAPACITY_POLICY = default_capacity_policy>
1,199   buffer<ALLOCATOR, CAPACITY_POLICY>
1,200   as_buffer(const ALLOCATOR &allocator = ALLOCATOR()) const {
1,201     return make_buffer<ALLOCATOR>(*this, allocator);
1,202   }
1,203
1,204   /// \brief Returns the buffer size in bytes needed for hexadecimal
1,205   /// dump of a passed slice.
1,206   MDBX_CXX11_CONSTEXPR size_t envisage_result_length() const noexcept {
1,207     const size_t bytes = source.length() << 1;
1,208     return wrap_width ? bytes + bytes / wrap_width : bytes;
1,209   }
1,210
1,211   /// \brief Fills the buffer by hexadecimal dump of a passed slice.
1,212   /// \throws std::length_error if given buffer is too small.
1,213   char *write_bytes(char *dest, size_t dest_size) const;
1,214
1,215   /// \brief Output hexadecimal dump of passed slice to the std::ostream.
1,216   /// \throws std::ios_base::failure corresponding to std::ostream::write()
1,217   /// behaviour.
1,218   ::std::ostream &output(::std::ostream &out) const;
1,219
1,220   /// \brief Checks whether a passed slice is empty,
1,221   /// and therefore there will be no output bytes.
1,222   bool is_empty() const noexcept { return source.empty(); }
1,223
1,224   /// \brief Checks whether the content of a passed slice is a valid data
1,225   /// and could be encoded or unexpectedly not.
1,226   bool is_erroneous() const noexcept { return false; }
1,227 };
1,228
1,229 /// \brief [Base58](https://en.wikipedia.org/wiki/Base58) encoder which satisfy
1,230 /// \ref SliceTranscoder concept.
1,231 struct LIBMDBX_API to_base58 {
1,232   const slice source;
1,233   const unsigned wrap_width = 0;
1,234   MDBX_CXX11_CONSTEXPR
1,235   to_base58(const slice &source, unsigned wrap_width = 0) noexcept
1,236       : source(source), wrap_width(wrap_width) {
1,237     MDBX_ASSERT_CXX20_CONCEPT_SATISFIED(SliceTranscoder, to_base58);
1,238   }
1,239
1,240   /// \brief Returns a string with a
1,241   /// [Base58](https://en.wikipedia.org/wiki/Base58) dump of a passed slice.
1,242   template <class ALLOCATOR = legacy_allocator>
1,243   string<ALLOCATOR> as_string(const ALLOCATOR &allocator = ALLOCATOR()) const {
1,244     return make_string<ALLOCATOR>(*this, allocator);
1,245   }
1,246
1,247   /// \brief Returns a buffer with a
1,248   /// [Base58](https://en.wikipedia.org/wiki/Base58) dump of a passed slice.
1,249   template <class ALLOCATOR = legacy_allocator,
1,250             typename CAPACITY_POLICY = default_capacity_policy>
1,251   buffer<ALLOCATOR, CAPACITY_POLICY>
1,252   as_buffer(const ALLOCATOR &allocator = ALLOCATOR()) const {
1,253     return make_buffer<ALLOCATOR>(*this, allocator);
1,254   }
1,255
1,256   /// \brief Returns the buffer size in bytes needed for
1,257   /// [Base58](https://en.wikipedia.org/wiki/Base58) dump of passed slice.
1,258   MDBX_CXX11_CONSTEXPR size_t envisage_result_length() const noexcept {
1,259     const size_t bytes =
1,260         source.length() / 8 * 11 + (source.length() % 8 * 43 + 31) / 32;
1,261     return wrap_width ? bytes + bytes / wrap_width : bytes;
1,262   }
1,263
1,264   /// \brief Fills the buffer by [Base58](https://en.wikipedia.org/wiki/Base58)
1,265   /// dump of passed slice.
1,266   /// \throws std::length_error if given buffer is too small.
1,267   char *write_bytes(char *dest, size_t dest_size) const;
1,268
1,269   /// \brief Output [Base58](https://en.wikipedia.org/wiki/Base58)
1,270   /// dump of passed slice to the std::ostream.
1,271   /// \throws std::ios_base::failure corresponding to std::ostream::write()
1,272   /// behaviour.
1,273   ::std::ostream &output(::std::ostream &out) const;
1,274
1,275   /// \brief Checks whether a passed slice is empty,
1,276   /// and therefore there will be no output bytes.
1,277   bool is_empty() const noexcept { return source.empty(); }
1,278
1,279   /// \brief Checks whether the content of a passed slice is a valid data
1,280   /// and could be encoded or unexpectedly not.
1,281   bool is_erroneous() const noexcept { return false; }
1,282 };
1,283
1,284 /// \brief [Base64](https://en.wikipedia.org/wiki/Base64) encoder which satisfy
1,285 /// \ref SliceTranscoder concept.
1,286 struct LIBMDBX_API to_base64 {
1,287   const slice source;
1,288   const unsigned wrap_width = 0;
1,289   MDBX_CXX11_CONSTEXPR
1,290   to_base64(const slice &source, unsigned wrap_width = 0) noexcept
1,291       : source(source), wrap_width(wrap_width) {
1,292     MDBX_ASSERT_CXX20_CONCEPT_SATISFIED(SliceTranscoder, to_base64);
1,293   }
1,294
1,295   /// \brief Returns a string with a
1,296   /// [Base64](https://en.wikipedia.org/wiki/Base64) dump of a passed slice.
1,297   template <class ALLOCATOR = legacy_allocator>
1,298   string<ALLOCATOR> as_string(const ALLOCATOR &allocator = ALLOCATOR()) const {
1,299     return make_string<ALLOCATOR>(*this, allocator);
1,300   }
1,301
1,302   /// \brief Returns a buffer with a
1,303   /// [Base64](https://en.wikipedia.org/wiki/Base64) dump of a passed slice.
1,304   template <class ALLOCATOR = legacy_allocator,
1,305             typename CAPACITY_POLICY = default_capacity_policy>
1,306   buffer<ALLOCATOR, CAPACITY_POLICY>
1,307   as_buffer(const ALLOCATOR &allocator = ALLOCATOR()) const {
1,308     return make_buffer<ALLOCATOR>(*this, allocator);
1,309   }
1,310
1,311   /// \brief Returns the buffer size in bytes needed for
1,312   /// [Base64](https://en.wikipedia.org/wiki/Base64) dump of passed slice.
1,313   MDBX_CXX11_CONSTEXPR size_t envisage_result_length() const noexcept {
1,314     const size_t bytes = (source.length() + 2) / 3 * 4;
1,315     return wrap_width ? bytes + bytes / wrap_width : bytes;
1,316   }
1,317
1,318   /// \brief Fills the buffer by [Base64](https://en.wikipedia.org/wiki/Base64)
1,319   /// dump of passed slice.
1,320   /// \throws std::length_error if given buffer is too small.
1,321   char *write_bytes(char *dest, size_t dest_size) const;
1,322
1,323   /// \brief Output [Base64](https://en.wikipedia.org/wiki/Base64)
1,324   /// dump of passed slice to the std::ostream.
1,325   /// \throws std::ios_base::failure corresponding to std::ostream::write()
1,326   /// behaviour.
1,327   ::std::ostream &output(::std::ostream &out) const;
1,328
1,329   /// \brief Checks whether a passed slice is empty,
1,330   /// and therefore there will be no output bytes.
1,331   bool is_empty() const noexcept { return source.empty(); }
1,332
1,333   /// \brief Checks whether the content of a passed slice is a valid data
1,334   /// and could be encoded or unexpectedly not.
1,335   bool is_erroneous() const noexcept { return false; }
1,336 };
1,337
1,338 inline ::std::ostream &operator<<(::std::ostream &out, const to_hex &wrapper) {
1,339   return wrapper.output(out);
1,340 }
1,341 inline ::std::ostream &operator<<(::std::ostream &out,
1,342                                   const to_base58 &wrapper) {
1,343   return wrapper.output(out);
1,344 }
1,345 inline ::std::ostream &operator<<(::std::ostream &out,
1,346                                   const to_base64 &wrapper) {
1,347   return wrapper.output(out);
1,348 }
1,349
1,350 /// \brief Hexadecimal decoder which satisfy \ref SliceTranscoder concept.
1,351 struct LIBMDBX_API from_hex {
1,352   const slice source;
1,353   const bool ignore_spaces = false;
1,354   MDBX_CXX11_CONSTEXPR from_hex(const slice &source,
1,355                                 bool ignore_spaces = false) noexcept
1,356       : source(source), ignore_spaces(ignore_spaces) {
1,357     MDBX_ASSERT_CXX20_CONCEPT_SATISFIED(SliceTranscoder, from_hex);
1,358   }
1,359
1,360   /// \brief Decodes hexadecimal dump from a passed slice to returned string.
1,361   template <class ALLOCATOR = legacy_allocator>
1,362   string<ALLOCATOR> as_string(const ALLOCATOR &allocator = ALLOCATOR()) const {
1,363     return make_string<ALLOCATOR>(*this, allocator);
1,364   }
1,365
1,366   /// \brief Decodes hexadecimal dump from a passed slice to returned buffer.
1,367   template <class ALLOCATOR = legacy_allocator,
1,368             typename CAPACITY_POLICY = default_capacity_policy>
1,369   buffer<ALLOCATOR, CAPACITY_POLICY>
1,370   as_buffer(const ALLOCATOR &allocator = ALLOCATOR()) const {
1,371     return make_buffer<ALLOCATOR>(*this, allocator);
1,372   }
1,373
1,374   /// \brief Returns the number of bytes needed for conversion
1,375   /// hexadecimal dump from a passed slice to decoded data.
1,376   MDBX_CXX11_CONSTEXPR size_t envisage_result_length() const noexcept {
1,377     return source.length() >> 1;
1,378   }
1,379
1,380   /// \brief Fills the destination with data decoded from hexadecimal dump
1,381   /// from a passed slice.
1,382   /// \throws std::length_error if given buffer is too small.
1,383   char *write_bytes(char *dest, size_t dest_size) const;
1,384
1,385   /// \brief Checks whether a passed slice is empty,
1,386   /// and therefore there will be no output bytes.
1,387   bool is_empty() const noexcept { return source.empty(); }
1,388
1,389   /// \brief Checks whether the content of a passed slice is a valid hexadecimal
1,390   /// dump, and therefore there could be decoded or not.
1,391   bool is_erroneous() const noexcept;
1,392 };
1,393
1,394 /// \brief [Base58](https://en.wikipedia.org/wiki/Base58) decoder which satisfy
1,395 /// \ref SliceTranscoder concept.
1,396 struct LIBMDBX_API from_base58 {
1,397   const slice source;
1,398   const bool ignore_spaces = false;
1,399   MDBX_CXX11_CONSTEXPR from_base58(const slice &source,
1,400                                    bool ignore_spaces = false) noexcept
1,401       : source(source), ignore_spaces(ignore_spaces) {
1,402     MDBX_ASSERT_CXX20_CONCEPT_SATISFIED(SliceTranscoder, from_base58);
1,403   }
1,404
1,405   /// \brief Decodes [Base58](https://en.wikipedia.org/wiki/Base58) dump from a
1,406   /// passed slice to returned string.
1,407   template <class ALLOCATOR = legacy_allocator>
1,408   string<ALLOCATOR> as_string(const ALLOCATOR &allocator = ALLOCATOR()) const {
1,409     return make_string<ALLOCATOR>(*this, allocator);
1,410   }
1,411
1,412   /// \brief Decodes [Base58](https://en.wikipedia.org/wiki/Base58) dump from a
1,413   /// passed slice to returned buffer.
1,414   template <class ALLOCATOR = legacy_allocator,
1,415             typename CAPACITY_POLICY = default_capacity_policy>
1,416   buffer<ALLOCATOR, CAPACITY_POLICY>
1,417   as_buffer(const ALLOCATOR &allocator = ALLOCATOR()) const {
1,418     return make_buffer<ALLOCATOR>(*this, allocator);
1,419   }
1,420
1,421   /// \brief Returns the number of bytes needed for conversion
1,422   /// [Base58](https://en.wikipedia.org/wiki/Base58) dump from a passed slice to
1,423   /// decoded data.
1,424   MDBX_CXX11_CONSTEXPR size_t envisage_result_length() const noexcept {
1,425     return source.length() / 11 * 8 + source.length() % 11 * 32 / 43;
1,426   }
1,427
1,428   /// \brief Fills the destination with data decoded from
1,429   /// [Base58](https://en.wikipedia.org/wiki/Base58) dump from a passed slice.
1,430   /// \throws std::length_error if given buffer is too small.
1,431   char *write_bytes(char *dest, size_t dest_size) const;
1,432
1,433   /// \brief Checks whether a passed slice is empty,
1,434   /// and therefore there will be no output bytes.
1,435   bool is_empty() const noexcept { return source.empty(); }
1,436
1,437   /// \brief Checks whether the content of a passed slice is a valid
1,438   /// [Base58](https://en.wikipedia.org/wiki/Base58) dump, and therefore there
1,439   /// could be decoded or not.
1,440   bool is_erroneous() const noexcept;
1,441 };
1,442
1,443 /// \brief [Base64](https://en.wikipedia.org/wiki/Base64) decoder which satisfy
1,444 /// \ref SliceTranscoder concept.
1,445 struct LIBMDBX_API from_base64 {
1,446   const slice source;
1,447   const bool ignore_spaces = false;
1,448   MDBX_CXX11_CONSTEXPR from_base64(const slice &source,
1,449                                    bool ignore_spaces = false) noexcept
1,450       : source(source), ignore_spaces(ignore_spaces) {
1,451     MDBX_ASSERT_CXX20_CONCEPT_SATISFIED(SliceTranscoder, from_base64);
1,452   }
1,453
1,454   /// \brief Decodes [Base64](https://en.wikipedia.org/wiki/Base64) dump from a
1,455   /// passed slice to returned string.
1,456   template <class ALLOCATOR = legacy_allocator>
1,457   string<ALLOCATOR> as_string(const ALLOCATOR &allocator = ALLOCATOR()) const {
1,458     return make_string<ALLOCATOR>(*this, allocator);
1,459   }
1,460
1,461   /// \brief Decodes [Base64](https://en.wikipedia.org/wiki/Base64) dump from a
1,462   /// passed slice to returned buffer.
1,463   template <class ALLOCATOR = legacy_allocator,
1,464             typename CAPACITY_POLICY = default_capacity_policy>
1,465   buffer<ALLOCATOR, CAPACITY_POLICY>
1,466   as_buffer(const ALLOCATOR &allocator = ALLOCATOR()) const {
1,467     return make_buffer<ALLOCATOR>(*this, allocator);
1,468   }
1,469
1,470   /// \brief Returns the number of bytes needed for conversion
1,471   /// [Base64](https://en.wikipedia.org/wiki/Base64) dump from a passed slice to
1,472   /// decoded data.
1,473   MDBX_CXX11_CONSTEXPR size_t envisage_result_length() const noexcept {
1,474     return (source.length() + 3) / 4 * 3;
1,475   }
1,476
1,477   /// \brief Fills the destination with data decoded from
1,478   /// [Base64](https://en.wikipedia.org/wiki/Base64) dump from a passed slice.
1,479   /// \throws std::length_error if given buffer is too small.
1,480   char *write_bytes(char *dest, size_t dest_size) const;
1,481
1,482   /// \brief Checks whether a passed slice is empty,
1,483   /// and therefore there will be no output bytes.
1,484   bool is_empty() const noexcept { return source.empty(); }
1,485
1,486   /// \brief Checks whether the content of a passed slice is a valid
1,487   /// [Base64](https://en.wikipedia.org/wiki/Base64) dump, and therefore there
1,488   /// could be decoded or not.
1,489   bool is_erroneous() const noexcept;
1,490 };
1,491
1,492 /// \brief The chunk of data stored inside the buffer or located outside it.
1,493 template <class ALLOCATOR, typename CAPACITY_POLICY> class buffer {
1,494 public:
1,495 #if !defined(_MSC_VER) || _MSC_VER > 1900
1,496   using allocator_type = typename ::std::allocator_traits<
1,497       ALLOCATOR>::template rebind_alloc<uint64_t>;
1,498 #else
1,499   using allocator_type = typename ALLOCATOR::template rebind<uint64_t>::other;
1,500 #endif /* MSVC is mad */
1,501   using allocator_traits = ::std::allocator_traits<allocator_type>;
1,502   using reservation_policy = CAPACITY_POLICY;
1,503   enum : size_t {
1,504     max_length = MDBX_MAXDATASIZE,
1,505     max_capacity = (max_length / 3u * 4u + 1023u) & ~size_t(1023),
1,506     extra_inplace_storage = reservation_policy::extra_inplace_storage,
1,507     pettiness_threshold = reservation_policy::pettiness_threshold
1,508   };
1,509
1,510 private:
1,511   friend class txn;
1,512   struct silo;
1,513   using move_assign_alloc =
1,514       allocation_aware_details::move_assign_alloc<silo, allocator_type>;
1,515   using copy_assign_alloc =
1,516       allocation_aware_details::copy_assign_alloc<silo, allocator_type>;
1,517   using swap_alloc = allocation_aware_details::swap_alloc<silo, allocator_type>;
1,518   struct silo /* Empty Base Class Optimization */ : public allocator_type {
1,519     MDBX_CXX20_CONSTEXPR const allocator_type &get_allocator() const noexcept {
1,520       return *this;
1,521     }
1,522     MDBX_CXX20_CONSTEXPR allocator_type &get_allocator() noexcept {
1,523       return *this;
1,524     }
1,525
1,526     using allocator_pointer = typename allocator_traits::pointer;
1,527     using allocator_const_pointer = typename allocator_traits::const_pointer;
1,528
1,529     MDBX_CXX20_CONSTEXPR ::std::pair<allocator_pointer, size_t>
1,530     allocate_storage(size_t bytes) {
1,531       assert(bytes >= sizeof(bin));
1,532       constexpr size_t unit = sizeof(typename allocator_type::value_type);
1,533       static_assert((unit & (unit - 1)) == 0,
1,534                     "size of ALLOCATOR::value_type should be a power of 2");
1,535       static_assert(unit > 0, "size of ALLOCATOR::value_type must be > 0");
1,536       const size_t n = (bytes + unit - 1) / unit;
1,537       return ::std::make_pair(allocator_traits::allocate(get_allocator(), n),
1,538                               n * unit);
1,539     }
1,540     MDBX_CXX20_CONSTEXPR void deallocate_storage(allocator_pointer ptr,
1,541                                                  size_t bytes) {
1,542       constexpr size_t unit = sizeof(typename allocator_type::value_type);
1,543       assert(ptr && bytes >= sizeof(bin) && bytes >= unit && bytes % unit == 0);
1,544       allocator_traits::deallocate(get_allocator(), ptr, bytes / unit);
1,545     }
1,546
1,547     static MDBX_CXX17_CONSTEXPR void *
1,548     to_address(allocator_pointer ptr) noexcept {
1,549 #if defined(__cpp_lib_to_address) && __cpp_lib_to_address >= 201711L
1,550       return static_cast<void *>(::std::to_address(ptr));
1,551 #else
1,552       return static_cast<void *>(::std::addressof(*ptr));
1,553 #endif /* __cpp_lib_to_address */
1,554     }
1,555     static MDBX_CXX17_CONSTEXPR const void *
1,556     to_address(allocator_const_pointer ptr) noexcept {
1,557 #if defined(__cpp_lib_to_address) && __cpp_lib_to_address >= 201711L
1,558       return static_cast<const void *>(::std::to_address(ptr));
1,559 #else
1,560       return static_cast<const void *>(::std::addressof(*ptr));
1,561 #endif /* __cpp_lib_to_address */
1,562     }
1,563
1,564     union bin {
1,565       struct allocated {
1,566         allocator_pointer ptr_;
1,567         size_t capacity_bytes_;
1,568         constexpr allocated(allocator_pointer ptr, size_t bytes) noexcept
1,569             : ptr_(ptr), capacity_bytes_(bytes) {}
1,570         constexpr allocated(const allocated &) noexcept = default;
1,571         constexpr allocated(allocated &&) noexcept = default;
1,572         MDBX_CXX17_CONSTEXPR allocated &
1,573         operator=(const allocated &) noexcept = default;
1,574         MDBX_CXX17_CONSTEXPR allocated &
1,575         operator=(allocated &&) noexcept = default;
1,576       };
1,577
1,578       allocated allocated_;
1,579       uint64_t align_hint_;
1,580       byte inplace_[(sizeof(allocated) + extra_inplace_storage + 7u) &
1,581                     ~size_t(7)];
1,582
1,583       static constexpr bool
1,584       is_suitable_for_inplace(size_t capacity_bytes) noexcept {
1,585         static_assert(sizeof(bin) == sizeof(inplace_), "WTF?");
1,586         return capacity_bytes < sizeof(bin);
1,587       }
1,588
1,589       enum : byte {
1,590         /* Little Endian:
1,591          * last byte is the most significant byte of u_.allocated.cap,
1,592          * so use higher bit of capacity as the inplace-flag */
1,593         le_lastbyte_mask = 0x80,
1,594         /* Big Endian:
1,595          * last byte is the least significant byte of u_.allocated.cap,
1,596          * so use lower bit of capacity as the inplace-flag. */
1,597         be_lastbyte_mask = 0x01
1,598       };
1,599
1,600       static constexpr byte inplace_lastbyte_mask() noexcept {
1,601         static_assert(
1,602             endian::native == endian::little || endian::native == endian::big,
1,603             "Only the little-endian or big-endian bytes order are supported");
1,604         return (endian::native == endian::little) ? le_lastbyte_mask
1,605                                                   : be_lastbyte_mask;
1,606       }
1,607       constexpr byte lastbyte() const noexcept {
1,608         return inplace_[sizeof(bin) - 1];
1,609       }
1,610       MDBX_CXX17_CONSTEXPR byte &lastbyte() noexcept {
1,611         return inplace_[sizeof(bin) - 1];
1,612       }
1,613
1,614       constexpr bool is_inplace() const noexcept {
1,615         return (lastbyte() & inplace_lastbyte_mask()) != 0;
1,616       }
1,617       constexpr bool is_allocated() const noexcept { return !is_inplace(); }
1,618
1,619       template <bool destroy_ptr>
1,620       MDBX_CXX17_CONSTEXPR byte *make_inplace() noexcept {
1,621         if (destroy_ptr) {
1,622           MDBX_CONSTEXPR_ASSERT(is_allocated());
1,623           /* properly destroy allocator::pointer */
1,624           allocated_.~allocated();
1,625         }
1,626         if (::std::is_trivial<allocator_pointer>::value)
1,627           /* workaround for "uninitialized" warning from some compilers */
1,628           ::std::memset(&allocated_.ptr_, 0, sizeof(allocated_.ptr_));
1,629         lastbyte() = inplace_lastbyte_mask();
1,630         MDBX_CONSTEXPR_ASSERT(is_inplace() && address() == inplace_ &&
1,631                               is_suitable_for_inplace(capacity()));
1,632         return address();
1,633       }
1,634
1,635       template <bool construct_ptr>
1,636       MDBX_CXX17_CONSTEXPR byte *
1,637       make_allocated(allocator_pointer ptr, size_t capacity_bytes) noexcept {
1,638         MDBX_CONSTEXPR_ASSERT(
1,639             (capacity_bytes & be_lastbyte_mask) == 0 &&
1,640             ((capacity_bytes >>
1,641               (sizeof(allocated_.capacity_bytes_) - 1) * CHAR_BIT) &
1,642              le_lastbyte_mask) == 0);
1,643         if (construct_ptr)
1,644           /* properly construct allocator::pointer */
1,645           new (&allocated_) allocated(ptr, capacity_bytes);
1,646         else {
1,647           MDBX_CONSTEXPR_ASSERT(is_allocated());
1,648           allocated_.ptr_ = ptr;
1,649           allocated_.capacity_bytes_ = capacity_bytes;
1,650         }
1,651         MDBX_CONSTEXPR_ASSERT(is_allocated() && address() == to_address(ptr) &&
1,652                               capacity() == capacity_bytes);
1,653         return address();
1,654       }
1,655
1,656       MDBX_CXX20_CONSTEXPR bin(size_t capacity_bytes = 0) noexcept {
1,657         MDBX_CONSTEXPR_ASSERT(is_suitable_for_inplace(capacity_bytes));
1,658         make_inplace<false>();
1,659         (void)capacity_bytes;
1,660       }
1,661       MDBX_CXX20_CONSTEXPR bin(allocator_pointer ptr,
1,662                                size_t capacity_bytes) noexcept {
1,663         MDBX_CONSTEXPR_ASSERT(!is_suitable_for_inplace(capacity_bytes));
1,664         make_allocated<true>(ptr, capacity_bytes);
1,665       }
1,666       MDBX_CXX20_CONSTEXPR ~bin() {
1,667         if (is_allocated())
1,668           /* properly destroy allocator::pointer */
1,669           allocated_.~allocated();
1,670       }
1,671       MDBX_CXX20_CONSTEXPR bin(bin &&ditto) noexcept {
1,672         if (ditto.is_inplace()) {
1,673           // micro-optimization: don't use make_inplace<> here
1,674           // since memcpy() will copy the flag.
1,675           memcpy(inplace_, ditto.inplace_, sizeof(inplace_));
1,676           MDBX_CONSTEXPR_ASSERT(is_inplace());
1,677         } else {
1,678           new (&allocated_) allocated(::std::move(ditto.allocated_));
1,679           ditto.make_inplace<true>();
1,680           MDBX_CONSTEXPR_ASSERT(is_allocated());
1,681         }
1,682       }
1,683
1,684       MDBX_CXX17_CONSTEXPR bin &operator=(const bin &ditto) noexcept {
1,685         if (ditto.is_inplace()) {
1,686           // micro-optimization: don't use make_inplace<> here
1,687           // since memcpy() will copy the flag.
1,688           if (is_allocated())
1,689             /* properly destroy allocator::pointer */
1,690             allocated_.~allocated();
1,691           memcpy(inplace_, ditto.inplace_, sizeof(inplace_));
1,692           MDBX_CONSTEXPR_ASSERT(is_inplace());
1,693         } else if (is_inplace())
1,694           make_allocated<true>(ditto.allocated_.ptr_,
1,695                                ditto.allocated_.capacity_bytes_);
1,696         else
1,697           make_allocated<false>(ditto.allocated_.ptr_,
1,698                                 ditto.allocated_.capacity_bytes_);
1,699         return *this;
1,700       }
1,701
1,702       MDBX_CXX17_CONSTEXPR bin &operator=(bin &&ditto) noexcept {
1,703         operator=(const_cast<const bin &>(ditto));
1,704         if (ditto.is_allocated())
1,705           ditto.make_inplace<true>();
1,706         return *this;
1,707       }
1,708
1,709       static MDBX_CXX20_CONSTEXPR size_t advise_capacity(const size_t current,
1,710                                                          const size_t wanna) {
1,711         if (MDBX_UNLIKELY(wanna > max_capacity))
1,712           MDBX_CXX20_UNLIKELY throw_max_length_exceeded();
1,713
1,714         const size_t advised = reservation_policy::advise(current, wanna);
1,715         assert(advised >= wanna);
1,716         return ::std::min(size_t(max_capacity),
1,717                           ::std::max(sizeof(bin) - 1, advised));
1,718       }
1,719
1,720       constexpr const byte *address() const noexcept {
1,721         return is_inplace()
1,722                    ? inplace_
1,723                    : static_cast<const byte *>(to_address(allocated_.ptr_));
1,724       }
1,725       MDBX_CXX17_CONSTEXPR byte *address() noexcept {
1,726         return is_inplace() ? inplace_
1,727                             : static_cast<byte *>(to_address(allocated_.ptr_));
1,728       }
1,729       constexpr size_t capacity() const noexcept {
1,730         return is_inplace() ? sizeof(bin) - 1 : allocated_.capacity_bytes_;
1,731       }
1,732     } bin_;
1,733
1,734     MDBX_CXX20_CONSTEXPR void *init(size_t capacity) {
1,735       capacity = bin::advise_capacity(0, capacity);
1,736       if (bin_.is_suitable_for_inplace(capacity))
1,737         new (&bin_) bin();
1,738       else {
1,739         const auto pair = allocate_storage(capacity);
1,740         assert(pair.second >= capacity);
1,741         new (&bin_) bin(pair.first, pair.second);
1,742       }
1,743       return bin_.address();
1,744     }
1,745
1,746     MDBX_CXX20_CONSTEXPR void release() noexcept {
1,747       if (bin_.is_allocated()) {
1,748         deallocate_storage(bin_.allocated_.ptr_,
1,749                            bin_.allocated_.capacity_bytes_);
1,750         bin_.template make_inplace<true>();
1,751       }
1,752     }
1,753
1,754     template <bool external_content>
1,755