diff --git a/ChangeLog.md b/ChangeLog.md index 5a8f0af6..6978e6b1 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -5,9 +5,53 @@ English version [by Google](https://gitflic-ru.translate.goog/project/erthink/li and [by Yandex](https://translated.turbopages.org/proxy_u/ru-en.en/https/gitflic.ru/project/erthink/libmdbx/blob?file=ChangeLog.md). -## v0.12.8 (сопровождение и подготовка к релизу) +## v0.12.9 "Ясень-4" от 2023-12-11 -Поддержка стабильной ветки. +Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением недочетов. + +``` +git diff' stat: 32 commits, 8 files changed, 667 insertions(+), 401 deletions(-) +Signed-off-by: Леонид Юрьев (Leonid Yuriev) +``` + +Исправления и доработки: + + - Ликвидация зависимости от ранее удаленной опции `MDBX_ENABLE_PREFAULT`, из-за + чего опция `MDBX_ENABLE_MINCORE` не включалась автоматически, что приводило + к не-активации соответствующего улучшения и не-достижению декларируемого уровня + производительности в сценариях использования в режиме `MDBX_WRITEMAP`. + + - Исправление авто-установки `MDBX_ENV_CHECKPID` при отключении использования + функционала `madvise()` посредством опции сборки `MDBX_ENABLE_MADVISE=0`. + Из-за чего при поддержке системой `madvise(MADV_DONTFORK)` не включался контроль pid. + + - Добавлена проверка переданного ключа на `NULL` при обработке `MDBX_GET_MULTIPLE`. + + - Добавлена проверка номеров корневых страниц в `coherency_check()`. + + - Обеспечен `const` для начала и конца диапазона в аргументах `mdbx_estimate_range()`. + + - Из разрабатываемой версии перенесены не-нарушающие совместимости доработки C++ API: + + - добавлен тип `mdbx::cursor::estimation_result`, а поведение методов + `cursor::estimate()` унифицировано с `cursor::move()`; + - для предотвращения незаметного неверного использования API, для инициализации + возвращаемых по ссылке срезов, вместо пустых срезов задействован `slice::invalid()`; + - добавлены дополнительные C++ операторы преобразования к типам C API; + - для совместимости со старыми стандартами C++ и старыми версиями STL перенесены + в public классы `buffer::move_assign_alloc` и `buffer::copy_assign_alloc`; + - добавлен тип `mdbx::default_buffer`; + - для срезов и буферов добавлены методы `hex_decode()`, `base64_decode()`, `base58_decode()`; + - добавлен тип `mdbx::comparator` и функций `mdbx::default_comparator()`; + - добавлены статические методы `buffer::hex()`, `base64()`, `base58()`; + - для транзакций и курсоров добавлены методы `get_/set_context`; + - добавлен метод `cursor::clone()`; + + - Поддержка base58 приведена в соответствии с черновиком RFC. + + - Переработка/исправление `to_hex()` и `from_hex()`. + + - Уменьшение `MDBX_opt_rp_augment_limit` по умолчанию до 1/3 от текущего количества страниц в БД. Мелочи: @@ -15,6 +59,12 @@ and [by Yandex](https://translated.turbopages.org/proxy_u/ru-en.en/https/gitflic - Корректировка определения `MDBX_LAST_ADDED_ERRCODE`. - Добавление в C++ API забытого исключения `mdbx::duplicated_lck_file`. - Обновление патча для старых версий buildroot. + - Использование в API `const MDBX_txn` где это возможно. + - Удаление устаревшего упоминания `MDBX_EAGAIN`. + - Проверка pid процесса только в функциях API требующих активной среды/env. + - Исправление опечаток в комментариях, в том числе в doxygen-описании. + - В тестах для совместимости с проблемными версиями glibc и glibc++ + устранено использование `std::stoull()`. -------------------------------------------------------------------------------- diff --git a/mdbx.h b/mdbx.h index ece77eda..15122715 100644 --- a/mdbx.h +++ b/mdbx.h @@ -2119,7 +2119,8 @@ enum MDBX_option_t { * growth, or/and to the inability of put long values. * * The `MDBX_opt_rp_augment_limit` controls described limit for the current - * process. Default is 262144, it is usually enough for most cases. */ + * process. By default this limit adjusted dynamically to 1/3 of current + * quantity of DB pages, which is usually enough for most cases. */ MDBX_opt_rp_augment_limit, /** \brief Controls the in-process limit to grow a cache of dirty @@ -2352,7 +2353,6 @@ LIBMDBX_API int mdbx_env_get_option(const MDBX_env *env, * doesn't exist. * \retval MDBX_EACCES The user didn't have permission to access * the environment files. - * \retval MDBX_EAGAIN The environment was locked by another process. * \retval MDBX_BUSY The \ref MDBX_EXCLUSIVE flag was specified and the * environment is in use by another process, * or the current process tries to open environment @@ -4293,8 +4293,8 @@ mdbx_int64_from_key(const MDBX_val); * \retval MDBX_THREAD_MISMATCH Given transaction is not owned * by current thread. * \retval MDBX_EINVAL An invalid parameter was specified. */ -LIBMDBX_API int mdbx_dbi_stat(MDBX_txn *txn, MDBX_dbi dbi, MDBX_stat *stat, - size_t bytes); +LIBMDBX_API int mdbx_dbi_stat(const MDBX_txn *txn, MDBX_dbi dbi, + MDBX_stat *stat, size_t bytes); /** \brief Retrieve depth (bitmask) information of nested dupsort (multi-value) * B+trees for given database. @@ -4311,7 +4311,7 @@ LIBMDBX_API int mdbx_dbi_stat(MDBX_txn *txn, MDBX_dbi dbi, MDBX_stat *stat, * by current thread. * \retval MDBX_EINVAL An invalid parameter was specified. * \retval MDBX_RESULT_TRUE The dbi isn't a dupsort (multi-value) database. */ -LIBMDBX_API int mdbx_dbi_dupsort_depthmask(MDBX_txn *txn, MDBX_dbi dbi, +LIBMDBX_API int mdbx_dbi_dupsort_depthmask(const MDBX_txn *txn, MDBX_dbi dbi, uint32_t *mask); /** \brief DBI state bits returted by \ref mdbx_dbi_flags_ex() @@ -4343,13 +4343,13 @@ DEFINE_ENUM_FLAG_OPERATORS(MDBX_dbi_state_t) * \param [out] state Address where the state will be returned. * * \returns A non-zero error value on failure and 0 on success. */ -LIBMDBX_API int mdbx_dbi_flags_ex(MDBX_txn *txn, MDBX_dbi dbi, unsigned *flags, - unsigned *state); +LIBMDBX_API int mdbx_dbi_flags_ex(const MDBX_txn *txn, MDBX_dbi dbi, + unsigned *flags, unsigned *state); /** \brief The shortcut to calling \ref mdbx_dbi_flags_ex() with `state=NULL` * for discarding it result. * \ingroup c_statinfo */ LIBMDBX_INLINE_API(int, mdbx_dbi_flags, - (MDBX_txn * txn, MDBX_dbi dbi, unsigned *flags)) { + (const MDBX_txn *txn, MDBX_dbi dbi, unsigned *flags)) { unsigned state; return mdbx_dbi_flags_ex(txn, dbi, flags, &state); } @@ -4425,7 +4425,7 @@ LIBMDBX_API int mdbx_drop(MDBX_txn *txn, MDBX_dbi dbi, bool del); * by current thread. * \retval MDBX_NOTFOUND The key was not in the database. * \retval MDBX_EINVAL An invalid parameter was specified. */ -LIBMDBX_API int mdbx_get(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, +LIBMDBX_API int mdbx_get(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data); /** \brief Get items from a database @@ -4458,7 +4458,7 @@ LIBMDBX_API int mdbx_get(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, * by current thread. * \retval MDBX_NOTFOUND The key was not in the database. * \retval MDBX_EINVAL An invalid parameter was specified. */ -LIBMDBX_API int mdbx_get_ex(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, +LIBMDBX_API int mdbx_get_ex(const MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, size_t *values_count); /** \brief Get equal or great item from a database. @@ -4489,7 +4489,7 @@ LIBMDBX_API int mdbx_get_ex(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, * by current thread. * \retval MDBX_NOTFOUND The key was not in the database. * \retval MDBX_EINVAL An invalid parameter was specified. */ -LIBMDBX_API int mdbx_get_equal_or_great(MDBX_txn *txn, MDBX_dbi dbi, +LIBMDBX_API int mdbx_get_equal_or_great(const MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data); /** \brief Store items into a database. @@ -4731,7 +4731,7 @@ mdbx_cursor_get_userctx(const MDBX_cursor *cursor); * \retval MDBX_THREAD_MISMATCH Given transaction is not owned * by current thread. * \retval MDBX_EINVAL An invalid parameter was specified. */ -LIBMDBX_API int mdbx_cursor_bind(MDBX_txn *txn, MDBX_cursor *cursor, +LIBMDBX_API int mdbx_cursor_bind(const MDBX_txn *txn, MDBX_cursor *cursor, MDBX_dbi dbi); /** \brief Create a cursor handle for the specified transaction and DBI handle. @@ -4764,7 +4764,7 @@ LIBMDBX_API int mdbx_cursor_bind(MDBX_txn *txn, MDBX_cursor *cursor, * \retval MDBX_THREAD_MISMATCH Given transaction is not owned * by current thread. * \retval MDBX_EINVAL An invalid parameter was specified. */ -LIBMDBX_API int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, +LIBMDBX_API int mdbx_cursor_open(const MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **cursor); /** \brief Close a cursor handle. @@ -4807,7 +4807,7 @@ LIBMDBX_API void mdbx_cursor_close(MDBX_cursor *cursor); * \retval MDBX_EINVAL An invalid parameter was specified. * \retval MDBX_BAD_DBI The cursor was not bound to a DBI-handle * or such a handle became invalid. */ -LIBMDBX_API int mdbx_cursor_renew(MDBX_txn *txn, MDBX_cursor *cursor); +LIBMDBX_API int mdbx_cursor_renew(const MDBX_txn *txn, MDBX_cursor *cursor); /** \brief Return the cursor's transaction handle. * \ingroup c_cursors @@ -5186,9 +5186,11 @@ LIBMDBX_API int mdbx_estimate_move(const MDBX_cursor *cursor, MDBX_val *key, * \param [out] distance_items A pointer to store range estimation result. * * \returns A non-zero error value on failure and 0 on success. */ -LIBMDBX_API int mdbx_estimate_range(MDBX_txn *txn, MDBX_dbi dbi, - MDBX_val *begin_key, MDBX_val *begin_data, - MDBX_val *end_key, MDBX_val *end_data, +LIBMDBX_API int mdbx_estimate_range(const MDBX_txn *txn, MDBX_dbi dbi, + const MDBX_val *begin_key, + const MDBX_val *begin_data, + const MDBX_val *end_key, + const MDBX_val *end_data, ptrdiff_t *distance_items); /** \brief The EPSILON value for mdbx_estimate_range() diff --git a/mdbx.h++ b/mdbx.h++ index d4cd7077..b77d851a 100644 --- a/mdbx.h++ +++ b/mdbx.h++ @@ -362,8 +362,11 @@ using default_allocator = polymorphic_allocator; using default_allocator = legacy_allocator; #endif /* __cpp_lib_memory_resource >= 201603L */ -/// \brief Default singe-byte string. -template +/// \brief Default buffer. +using default_buffer = buffer; + +/// \brief Default single-byte string. +template using string = ::std::basic_string, ALLOCATOR>; using filehandle = ::mdbx_filehandle_t; @@ -733,6 +736,8 @@ struct LIBMDBX_API_TYPE slice : public ::MDBX_val { slice &operator=(const slice &) noexcept = default; inline slice &operator=(slice &&src) noexcept; inline slice &operator=(::MDBX_val &&src); + operator MDBX_val *() noexcept { return this; } + operator const MDBX_val *() const noexcept { return this; } #if defined(DOXYGEN) || \ (defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L) @@ -1322,8 +1327,7 @@ struct LIBMDBX_API to_base58 { /// \brief Returns the buffer size in bytes needed for /// [Base58](https://en.wikipedia.org/wiki/Base58) dump of passed slice. MDBX_CXX11_CONSTEXPR size_t envisage_result_length() const noexcept { - const size_t bytes = - source.length() / 8 * 11 + (source.length() % 8 * 43 + 31) / 32; + const size_t bytes = (source.length() * 11 + 7) / 8; return wrap_width ? bytes + bytes / wrap_width : bytes; } @@ -1488,7 +1492,7 @@ struct LIBMDBX_API from_base58 { /// [Base58](https://en.wikipedia.org/wiki/Base58) dump from a passed slice to /// decoded data. MDBX_CXX11_CONSTEXPR size_t envisage_result_length() const noexcept { - return source.length() / 11 * 8 + source.length() % 11 * 32 / 43; + return source.length() /* могут быть все нули кодируемые один-к-одному */; } /// \brief Fills the destination with data decoded from @@ -1576,10 +1580,6 @@ public: private: friend class txn; struct silo; - using move_assign_alloc = - allocation_aware_details::move_assign_alloc; - using copy_assign_alloc = - allocation_aware_details::copy_assign_alloc; using swap_alloc = allocation_aware_details::swap_alloc; struct silo /* Empty Base Class Optimization */ : public allocator_type { MDBX_CXX20_CONSTEXPR const allocator_type &get_allocator() const noexcept { @@ -2071,6 +2071,11 @@ public: /// \todo buffer& operator>>(buffer&, ...) for reading (delegated to slice) /// \todo template key(X) for encoding keys while writing + using move_assign_alloc = + allocation_aware_details::move_assign_alloc; + using copy_assign_alloc = + allocation_aware_details::copy_assign_alloc; + /// \brief Returns the associated allocator. MDBX_CXX20_CONSTEXPR allocator_type get_allocator() const { return silo_.get_allocator(); @@ -2339,6 +2344,126 @@ public: return slice_.as_pod(); } + /// \brief Returns a new buffer with a hexadecimal dump of the slice content. + static buffer hex(const ::mdbx::slice &source, bool uppercase = false, + unsigned wrap_width = 0, + const allocator_type &allocator = allocator_type()) { + return source.template encode_hex( + uppercase, wrap_width, allocator); + } + + /// \brief Returns a new buffer with a + /// [Base58](https://en.wikipedia.org/wiki/Base58) dump of the slice content. + static buffer base58(const ::mdbx::slice &source, unsigned wrap_width = 0, + const allocator_type &allocator = allocator_type()) { + return source.template encode_base58(wrap_width, + allocator); + } + /// \brief Returns a new buffer with a + /// [Base64](https://en.wikipedia.org/wiki/Base64) dump of the slice content. + static buffer base64(const ::mdbx::slice &source, unsigned wrap_width = 0, + const allocator_type &allocator = allocator_type()) { + return source.template encode_base64(wrap_width, + allocator); + } + + /// \brief Returns a new buffer with a hexadecimal dump of the given pod. + template + static buffer hex(const POD &pod, bool uppercase = false, + unsigned wrap_width = 0, + const allocator_type &allocator = allocator_type()) { + return hex(mdbx::slice::wrap(pod), uppercase, wrap_width, allocator); + } + + /// \brief Returns a new buffer with a + /// [Base58](https://en.wikipedia.org/wiki/Base58) dump of the given pod. + template + static buffer base58(const POD &pod, unsigned wrap_width = 0, + const allocator_type &allocator = allocator_type()) { + return base58(mdbx::slice::wrap(pod), wrap_width, allocator); + } + + /// \brief Returns a new buffer with a + /// [Base64](https://en.wikipedia.org/wiki/Base64) dump of the given pod. + template + static buffer base64(const POD &pod, unsigned wrap_width = 0, + const allocator_type &allocator = allocator_type()) { + return base64(mdbx::slice::wrap(pod), wrap_width, allocator); + } + + /// \brief Returns a new buffer with a hexadecimal dump of the slice content. + buffer encode_hex(bool uppercase = false, unsigned wrap_width = 0, + const allocator_type &allocator = allocator_type()) const { + return slice().template encode_hex( + uppercase, wrap_width, allocator); + } + + /// \brief Returns a new buffer with a + /// [Base58](https://en.wikipedia.org/wiki/Base58) dump of the slice content. + buffer + encode_base58(unsigned wrap_width = 0, + const allocator_type &allocator = allocator_type()) const { + return slice().template encode_base58( + wrap_width, allocator); + } + /// \brief Returns a new buffer with a + /// [Base64](https://en.wikipedia.org/wiki/Base64) dump of the slice content. + buffer + encode_base64(unsigned wrap_width = 0, + const allocator_type &allocator = allocator_type()) const { + return slice().template encode_base64( + wrap_width, allocator); + } + + /// \brief Decodes hexadecimal dump from the slice content to returned buffer. + static buffer hex_decode(const ::mdbx::slice &source, + bool ignore_spaces = false, + const allocator_type &allocator = allocator_type()) { + return source.template hex_decode(ignore_spaces, + allocator); + } + + /// \brief Decodes [Base58](https://en.wikipedia.org/wiki/Base58) dump + /// from the slice content to returned buffer. + static buffer + base58_decode(const ::mdbx::slice &source, bool ignore_spaces = false, + const allocator_type &allocator = allocator_type()) { + return source.template base58_decode( + ignore_spaces, allocator); + } + + /// \brief Decodes [Base64](https://en.wikipedia.org/wiki/Base64) dump + /// from the slice content to returned buffer. + static buffer + base64_decode(const ::mdbx::slice &source, bool ignore_spaces = false, + const allocator_type &allocator = allocator_type()) { + return source.template base64_decode( + ignore_spaces, allocator); + } + + /// \brief Decodes hexadecimal dump + /// from the buffer content to new returned buffer. + buffer hex_decode(bool ignore_spaces = false, + const allocator_type &allocator = allocator_type()) const { + return hex_decode(slice(), ignore_spaces, allocator); + } + + /// \brief Decodes [Base58](https://en.wikipedia.org/wiki/Base58) dump + /// from the buffer content to new returned buffer. + buffer + base58_decode(bool ignore_spaces = false, + const allocator_type &allocator = allocator_type()) const { + return base58_decode(slice(), ignore_spaces, allocator); + } + + /// \brief Decodes [Base64](https://en.wikipedia.org/wiki/Base64) dump + /// from the buffer content to new returned buffer. + buffer + base64_decode(bool ignore_spaces = false, + const allocator_type &allocator = allocator_type()) const { + return base64_decode(slice(), ignore_spaces, allocator); + } + /// \brief Reserves storage space. void reserve(size_t wanna_headroom, size_t wanna_tailroom) { wanna_headroom = ::std::min(::std::max(headroom(), wanna_headroom), @@ -2983,6 +3108,7 @@ struct LIBMDBX_API_TYPE map_handle { map_handle(const map_handle &) noexcept = default; map_handle &operator=(const map_handle &) noexcept = default; operator bool() const noexcept { return dbi != 0; } + operator MDBX_dbi() const { return dbi; } using flags = ::MDBX_db_flags_t; using state = ::MDBX_dbi_state_t; @@ -3008,6 +3134,14 @@ struct LIBMDBX_API_TYPE map_handle { }; }; +using comparator = ::MDBX_cmp_func *; +inline comparator default_comparator(key_mode mode) noexcept { + return ::mdbx_get_keycmp(static_cast(mode)); +} +inline comparator default_comparator(value_mode mode) noexcept { + return ::mdbx_get_keycmp(static_cast(mode)); +} + /// \brief Key-value pairs put mode. enum put_mode { insert_unique = MDBX_NOOVERWRITE, ///< Insert only unique keys. @@ -3445,7 +3579,7 @@ public: inline void *get_context() const noexcept; /// \brief Sets the application context associated with the environment. - inline env &set_context(void *); + inline env &set_context(void *your_context); /// \brief Sets threshold to force flush the data buffers to disk, for /// non-sync durability modes. @@ -3803,6 +3937,12 @@ public: /// \brief Return the transaction's ID. inline uint64_t id() const; + /// \brief Returns the application context associated with the transaction. + inline void *get_context() const noexcept; + + /// \brief Sets the application context associated with the transaction. + inline txn &set_context(void *your_context); + /// \brief Checks whether the given data is on a dirty page. inline bool is_dirty(const void *ptr) const; @@ -3839,7 +3979,7 @@ public: txn_managed start_nested(); /// \brief Opens cursor for specified key-value map handle. - inline cursor_managed open_cursor(map_handle map); + inline cursor_managed open_cursor(map_handle map) const; /// \brief Open existing key-value map. inline map_handle open_map( @@ -4038,10 +4178,12 @@ public: put_multiple(map, key, vector.data(), vector.size(), mode); } - inline ptrdiff_t estimate(map_handle map, pair from, pair to) const; - inline ptrdiff_t estimate(map_handle map, slice from, slice to) const; - inline ptrdiff_t estimate_from_first(map_handle map, slice to) const; - inline ptrdiff_t estimate_to_last(map_handle map, slice from) const; + inline ptrdiff_t estimate(map_handle map, const pair &from, + const pair &to) const; + inline ptrdiff_t estimate(map_handle map, const slice &from, + const slice &to) const; + inline ptrdiff_t estimate_from_first(map_handle map, const slice &to) const; + inline ptrdiff_t estimate_to_last(map_handle map, const slice &from) const; }; /// \brief Managed database transaction. @@ -4122,6 +4264,7 @@ public: inline cursor &operator=(cursor &&other) noexcept; inline cursor(cursor &&other) noexcept; inline ~cursor() noexcept; + inline cursor_managed clone(void *your_context = nullptr) const; MDBX_CXX14_CONSTEXPR operator bool() const noexcept; MDBX_CXX14_CONSTEXPR operator const MDBX_cursor *() const; MDBX_CXX14_CONSTEXPR operator MDBX_cursor *(); @@ -4130,6 +4273,12 @@ public: friend MDBX_CXX11_CONSTEXPR bool operator!=(const cursor &a, const cursor &b) noexcept; + /// \brief Returns the application context associated with the cursor. + inline void *get_context() const noexcept; + + /// \brief Sets the application context associated with the cursor. + inline cursor &set_context(void *your_context); + enum move_operation { first = MDBX_FIRST, last = MDBX_LAST, @@ -4154,10 +4303,13 @@ public: struct move_result : public pair_result { inline move_result(const cursor &cursor, bool throw_notfound); - inline move_result(cursor &cursor, move_operation operation, - bool throw_notfound); - inline move_result(cursor &cursor, move_operation operation, - const slice &key, bool throw_notfound); + move_result(cursor &cursor, move_operation operation, bool throw_notfound) + : move_result(cursor, operation, slice::invalid(), slice::invalid(), + throw_notfound) {} + move_result(cursor &cursor, move_operation operation, const slice &key, + bool throw_notfound) + : move_result(cursor, operation, key, slice::invalid(), + throw_notfound) {} inline move_result(cursor &cursor, move_operation operation, const slice &key, const slice &value, bool throw_notfound); @@ -4165,6 +4317,20 @@ public: move_result &operator=(const move_result &) noexcept = default; }; + struct estimate_result : public pair { + ptrdiff_t approximate_quantity; + estimate_result(const cursor &cursor, move_operation operation) + : estimate_result(cursor, operation, slice::invalid(), + slice::invalid()) {} + estimate_result(const cursor &cursor, move_operation operation, + const slice &key) + : estimate_result(cursor, operation, key, slice::invalid()) {} + inline estimate_result(const cursor &cursor, move_operation operation, + const slice &key, const slice &value); + estimate_result(const estimate_result &) noexcept = default; + estimate_result &operator=(const estimate_result &) noexcept = default; + }; + protected: inline bool move(move_operation operation, MDBX_val *key, MDBX_val *value, bool throw_notfound) const @@ -4209,19 +4375,20 @@ public: inline bool eof() const; inline bool on_first() const; inline bool on_last() const; - inline ptrdiff_t estimate(slice key, slice value) const; - inline ptrdiff_t estimate(slice key) const; - inline ptrdiff_t estimate(move_operation operation) const; + inline estimate_result estimate(const slice &key, const slice &value) const; + inline estimate_result estimate(const slice &key) const; + inline estimate_result estimate(move_operation operation) const; + inline estimate_result estimate(move_operation operation, slice &key) const; //---------------------------------------------------------------------------- /// \brief Renew/bind a cursor with a new transaction and previously used /// key-value map handle. - inline void renew(::mdbx::txn &txn); + inline void renew(const ::mdbx::txn &txn); /// \brief Bind/renew a cursor with a new transaction and specified key-value /// map handle. - inline void bind(::mdbx::txn &txn, ::mdbx::map_handle map_handle); + inline void bind(const ::mdbx::txn &txn, ::mdbx::map_handle map_handle); /// \brief Returns the cursor's transaction. inline ::mdbx::txn txn() const; @@ -4275,7 +4442,8 @@ class LIBMDBX_API_TYPE cursor_managed : public cursor { public: /// \brief Creates a new managed cursor with underlying object. - cursor_managed() : cursor_managed(::mdbx_cursor_create(nullptr)) { + cursor_managed(void *your_context = nullptr) + : cursor_managed(::mdbx_cursor_create(your_context)) { if (MDBX_UNLIKELY(!handle_)) MDBX_CXX20_UNLIKELY error::throw_exception(MDBX_ENOMEM); } @@ -5417,6 +5585,15 @@ MDBX_CXX11_CONSTEXPR bool operator!=(const txn &a, const txn &b) noexcept { return a.handle_ != b.handle_; } +inline void *txn::get_context() const noexcept { + return mdbx_txn_get_userctx(handle_); +} + +inline txn &txn::set_context(void *ptr) { + error::success_or_throw(::mdbx_txn_set_userctx(handle_, ptr)); + return *this; +} + inline bool txn::is_dirty(const void *ptr) const { int err = ::mdbx_is_dirty(handle_, ptr); switch (err) { @@ -5457,7 +5634,7 @@ inline txn::info txn::get_info(bool scan_reader_lock_table) const { return r; } -inline cursor_managed txn::open_cursor(map_handle map) { +inline cursor_managed txn::open_cursor(map_handle map) const { MDBX_cursor *ptr; error::success_or_throw(::mdbx_cursor_open(handle_, map.dbi, &ptr)); return cursor_managed(ptr); @@ -5855,28 +6032,32 @@ inline size_t txn::put_multiple(map_handle map, const slice &key, return args[1].iov_len /* done item count */; } -inline ptrdiff_t txn::estimate(map_handle map, pair from, pair to) const { +inline ptrdiff_t txn::estimate(map_handle map, const pair &from, + const pair &to) const { ptrdiff_t result; error::success_or_throw(mdbx_estimate_range( handle_, map.dbi, &from.key, &from.value, &to.key, &to.value, &result)); return result; } -inline ptrdiff_t txn::estimate(map_handle map, slice from, slice to) const { +inline ptrdiff_t txn::estimate(map_handle map, const slice &from, + const slice &to) const { ptrdiff_t result; error::success_or_throw(mdbx_estimate_range(handle_, map.dbi, &from, nullptr, &to, nullptr, &result)); return result; } -inline ptrdiff_t txn::estimate_from_first(map_handle map, slice to) const { +inline ptrdiff_t txn::estimate_from_first(map_handle map, + const slice &to) const { ptrdiff_t result; error::success_or_throw(mdbx_estimate_range(handle_, map.dbi, nullptr, nullptr, &to, nullptr, &result)); return result; } -inline ptrdiff_t txn::estimate_to_last(map_handle map, slice from) const { +inline ptrdiff_t txn::estimate_to_last(map_handle map, + const slice &from) const { ptrdiff_t result; error::success_or_throw(mdbx_estimate_range(handle_, map.dbi, &from, nullptr, nullptr, nullptr, &result)); @@ -5887,6 +6068,21 @@ inline ptrdiff_t txn::estimate_to_last(map_handle map, slice from) const { MDBX_CXX11_CONSTEXPR cursor::cursor(MDBX_cursor *ptr) noexcept : handle_(ptr) {} +inline cursor_managed cursor::clone(void *your_context) const { + cursor_managed clone(your_context); + error::success_or_throw(::mdbx_cursor_copy(handle_, clone.handle_)); + return clone; +} + +inline void *cursor::get_context() const noexcept { + return mdbx_cursor_get_userctx(handle_); +} + +inline cursor &cursor::set_context(void *ptr) { + error::success_or_throw(::mdbx_cursor_set_userctx(handle_, ptr)); + return *this; +} + inline cursor &cursor::operator=(cursor &&other) noexcept { handle_ = other.handle_; other.handle_ = nullptr; @@ -5925,22 +6121,8 @@ MDBX_CXX11_CONSTEXPR bool operator!=(const cursor &a, inline cursor::move_result::move_result(const cursor &cursor, bool throw_notfound) - : pair_result(key, value, false) { - done = cursor.move(get_current, &key, &value, throw_notfound); -} - -inline cursor::move_result::move_result(cursor &cursor, - move_operation operation, - bool throw_notfound) - : pair_result(key, value, false) { - done = cursor.move(operation, &key, &value, throw_notfound); -} - -inline cursor::move_result::move_result(cursor &cursor, - move_operation operation, - const slice &key, bool throw_notfound) - : pair_result(key, slice(), false) { - this->done = cursor.move(operation, &this->key, &this->value, throw_notfound); + : pair_result(slice(), slice(), false) { + done = cursor.move(get_current, &this->key, &this->value, throw_notfound); } inline cursor::move_result::move_result(cursor &cursor, @@ -5967,6 +6149,14 @@ inline bool cursor::move(move_operation operation, MDBX_val *key, } } +inline cursor::estimate_result::estimate_result(const cursor &cursor, + move_operation operation, + const slice &key, + const slice &value) + : pair(key, value), approximate_quantity(PTRDIFF_MIN) { + approximate_quantity = cursor.estimate(operation, &this->key, &this->value); +} + inline ptrdiff_t cursor::estimate(move_operation operation, MDBX_val *key, MDBX_val *value) const { ptrdiff_t result; @@ -6089,24 +6279,26 @@ inline bool cursor::on_last() const { return error::boolean_or_throw(::mdbx_cursor_on_last(*this)); } -inline ptrdiff_t cursor::estimate(slice key, slice value) const { - return estimate(multi_exactkey_lowerboundvalue, &key, &value); +inline cursor::estimate_result cursor::estimate(const slice &key, + const slice &value) const { + return estimate_result(*this, multi_exactkey_lowerboundvalue, key, value); } -inline ptrdiff_t cursor::estimate(slice key) const { - return estimate(key_lowerbound, &key, nullptr); +inline cursor::estimate_result cursor::estimate(const slice &key) const { + return estimate_result(*this, key_lowerbound, key); } -inline ptrdiff_t cursor::estimate(move_operation operation) const { - slice unused_key; - return estimate(operation, &unused_key, nullptr); +inline cursor::estimate_result +cursor::estimate(move_operation operation) const { + return estimate_result(*this, operation); } -inline void cursor::renew(::mdbx::txn &txn) { +inline void cursor::renew(const ::mdbx::txn &txn) { error::success_or_throw(::mdbx_cursor_renew(txn, handle_)); } -inline void cursor::bind(::mdbx::txn &txn, ::mdbx::map_handle map_handle) { +inline void cursor::bind(const ::mdbx::txn &txn, + ::mdbx::map_handle map_handle) { error::success_or_throw(::mdbx_cursor_bind(txn, handle_, map_handle.dbi)); } diff --git a/src/core.c b/src/core.c index 899b65af..9d142343 100644 --- a/src/core.c +++ b/src/core.c @@ -3386,7 +3386,7 @@ static int __must_check_result cursor_first(MDBX_cursor *mc, MDBX_val *key, static int __must_check_result cursor_last(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data); -static int __must_check_result cursor_init(MDBX_cursor *mc, MDBX_txn *txn, +static int __must_check_result cursor_init(MDBX_cursor *mc, const MDBX_txn *txn, size_t dbi); static int __must_check_result cursor_xinit0(MDBX_cursor *mc); static int __must_check_result cursor_xinit1(MDBX_cursor *mc, MDBX_node *node, @@ -5979,8 +5979,8 @@ __cold static void munlock_all(const MDBX_env *env) { } __cold static unsigned default_rp_augment_limit(const MDBX_env *env) { - /* default rp_augment_limit = ceil(npages / gold_ratio) */ - const size_t augment = (env->me_dbgeo.now >> (env->me_psize2log + 10)) * 633u; + /* default rp_augment_limit = npages / 3 */ + const size_t augment = env->me_dbgeo.now / 3 >> env->me_psize2log; eASSERT(env, augment < MDBX_PGL_LIMIT); return pnl_bytes2size(pnl_size2bytes( (augment > MDBX_PNL_INITIAL) ? augment : MDBX_PNL_INITIAL)); @@ -7315,7 +7315,7 @@ static pgr_t page_alloc_slowpath(const MDBX_cursor *const mc, const size_t num, * простейших случаях (тривиальный бенчмарк) интегральная производительность * становится вдвое меньше. А на платформах без mincore() и с проблемной * подсистемой виртуальной памяти ситуация может быть многократно хуже. - * Поэтому избегаем затрат в ситуациях когда prefaukt-write скорее всего не + * Поэтому избегаем затрат в ситуациях когда prefault-write скорее всего не * нужна. */ const bool readahead_enabled = env->me_lck->mti_readahead_anchor & 1; const pgno_t readahead_edge = env->me_lck->mti_readahead_anchor >> 1; @@ -8258,17 +8258,16 @@ static __inline int check_env(const MDBX_env *env, const bool wanna_active) { if (unlikely(env->me_signature.weak != MDBX_ME_SIGNATURE)) return MDBX_EBADSIGN; -#if MDBX_ENV_CHECKPID - if (unlikely(env->me_pid != osal_getpid())) { - ((MDBX_env *)env)->me_flags |= MDBX_FATAL_ERROR; - return MDBX_PANIC; - } -#endif /* MDBX_ENV_CHECKPID */ - if (unlikely(env->me_flags & MDBX_FATAL_ERROR)) return MDBX_PANIC; if (wanna_active) { +#if MDBX_ENV_CHECKPID + if (unlikely(env->me_pid != osal_getpid())) { + ((MDBX_env *)env)->me_flags |= MDBX_FATAL_ERROR; + return MDBX_PANIC; + } +#endif /* MDBX_ENV_CHECKPID */ if (unlikely((env->me_flags & MDBX_ENV_ACTIVE) == 0)) return MDBX_EPERM; eASSERT(env, env->me_map != nullptr); @@ -8615,20 +8614,45 @@ static bool coherency_check(const MDBX_env *env, const txnid_t txnid, const volatile MDBX_meta *meta, bool report) { const txnid_t freedb_mod_txnid = dbs[FREE_DBI].md_mod_txnid; const txnid_t maindb_mod_txnid = dbs[MAIN_DBI].md_mod_txnid; + const pgno_t last_pgno = meta->mm_geo.now; const pgno_t freedb_root_pgno = dbs[FREE_DBI].md_root; - const MDBX_page *freedb_root = (env->me_map && freedb_root_pgno != P_INVALID) + const MDBX_page *freedb_root = (env->me_map && freedb_root_pgno < last_pgno) ? pgno2page(env, freedb_root_pgno) : nullptr; const pgno_t maindb_root_pgno = dbs[MAIN_DBI].md_root; - const MDBX_page *maindb_root = (env->me_map && maindb_root_pgno != P_INVALID) + const MDBX_page *maindb_root = (env->me_map && maindb_root_pgno < last_pgno) ? pgno2page(env, maindb_root_pgno) : nullptr; const uint64_t magic_and_version = unaligned_peek_u64_volatile(4, &meta->mm_magic_and_version); bool ok = true; + if (freedb_root_pgno != P_INVALID && + unlikely(freedb_root_pgno >= last_pgno)) { + if (report) + WARNING( + "catch invalid %sdb root %" PRIaPGNO " for meta_txnid %" PRIaTXN + " %s", + "free", freedb_root_pgno, txnid, + (env->me_stuck_meta < 0) + ? "(workaround for incoherent flaw of unified page/buffer cache)" + : "(wagering meta)"); + ok = false; + } + if (maindb_root_pgno != P_INVALID && + unlikely(maindb_root_pgno >= last_pgno)) { + if (report) + WARNING( + "catch invalid %sdb root %" PRIaPGNO " for meta_txnid %" PRIaTXN + " %s", + "main", maindb_root_pgno, txnid, + (env->me_stuck_meta < 0) + ? "(workaround for incoherent flaw of unified page/buffer cache)" + : "(wagering meta)"); + ok = false; + } if (unlikely(txnid < freedb_mod_txnid || (!freedb_mod_txnid && freedb_root && likely(magic_and_version == MDBX_DATA_MAGIC)))) { @@ -9579,7 +9603,7 @@ int mdbx_txn_flags(const MDBX_txn *txn) { } /* Check for misused dbi handles */ -static __inline bool dbi_changed(MDBX_txn *txn, size_t dbi) { +static __inline bool dbi_changed(const MDBX_txn *txn, size_t dbi) { if (txn->mt_dbiseqs == txn->mt_env->me_dbiseqs) return false; if (likely( @@ -11170,7 +11194,7 @@ static int txn_write(MDBX_txn *txn, iov_ctx_t *ctx) { } /* Check txn and dbi arguments to a function */ -static __always_inline bool check_dbi(MDBX_txn *txn, MDBX_dbi dbi, +static __always_inline bool check_dbi(const MDBX_txn *txn, MDBX_dbi dbi, unsigned validity) { if (likely(dbi < txn->mt_numdbs)) { if (likely(!dbi_changed(txn, dbi))) { @@ -11181,7 +11205,7 @@ static __always_inline bool check_dbi(MDBX_txn *txn, MDBX_dbi dbi, return false; } } - return dbi_import(txn, dbi); + return dbi_import((MDBX_txn *)txn, dbi); } /* Merge child txn into parent */ @@ -16078,7 +16102,8 @@ static __always_inline int node_read(MDBX_cursor *mc, const MDBX_node *node, return node_read_bigdata(mc, node, data, mp); } -int mdbx_get(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data) { +int mdbx_get(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, + MDBX_val *data) { DKBUF_DEBUG; DEBUG("===> get db %u key [%s]", dbi, DKEY_DEBUG(key)); @@ -16100,7 +16125,7 @@ int mdbx_get(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data) { return cursor_set(&cx.outer, (MDBX_val *)key, data, MDBX_SET).err; } -int mdbx_get_equal_or_great(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, +int mdbx_get_equal_or_great(const MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data) { int rc = check_txn(txn, MDBX_TXN_BLOCKED); if (unlikely(rc != MDBX_SUCCESS)) @@ -16123,8 +16148,8 @@ int mdbx_get_equal_or_great(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, return cursor_get(&cx.outer, key, data, MDBX_SET_LOWERBOUND); } -int mdbx_get_ex(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, - size_t *values_count) { +int mdbx_get_ex(const MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, + MDBX_val *data, size_t *values_count) { DKBUF_DEBUG; DEBUG("===> get db %u key [%s]", dbi, DKEY_DEBUG(key)); @@ -16883,24 +16908,30 @@ static __hot int cursor_get(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, return MDBX_EINVAL; if (unlikely((mc->mc_db->md_flags & MDBX_DUPFIXED) == 0)) return MDBX_INCOMPATIBLE; - rc = (mc->mc_flags & C_INITIALIZED) - ? MDBX_SUCCESS - : cursor_set(mc, key, data, MDBX_SET).err; - if ((mc->mc_xcursor->mx_cursor.mc_flags & (C_INITIALIZED | C_EOF)) != - C_INITIALIZED) + if ((mc->mc_flags & C_INITIALIZED) == 0) { + if (unlikely(!key)) + return MDBX_EINVAL; + rc = cursor_set(mc, key, data, MDBX_SET).err; + if (unlikely(rc != MDBX_SUCCESS)) + break; + } + rc = MDBX_SUCCESS; + if (unlikely(C_INITIALIZED != (mc->mc_xcursor->mx_cursor.mc_flags & + (C_INITIALIZED | C_EOF)))) { + rc = MDBX_NOTFOUND; break; - goto fetchm; + } + goto fetch_multiple; case MDBX_NEXT_MULTIPLE: - if (unlikely(data == NULL)) + if (unlikely(!data)) return MDBX_EINVAL; if (unlikely(!(mc->mc_db->md_flags & MDBX_DUPFIXED))) return MDBX_INCOMPATIBLE; rc = cursor_next(mc, key, data, MDBX_NEXT_DUP); if (rc == MDBX_SUCCESS) { if (mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) { - MDBX_cursor *mx; - fetchm: - mx = &mc->mc_xcursor->mx_cursor; + fetch_multiple:; + MDBX_cursor *mx = &mc->mc_xcursor->mx_cursor; data->iov_len = page_numkeys(mx->mc_pg[mx->mc_top]) * mx->mc_db->md_xsize; data->iov_base = page_data(mx->mc_pg[mx->mc_top]); @@ -16911,21 +16942,20 @@ static __hot int cursor_get(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, } break; case MDBX_PREV_MULTIPLE: - if (data == NULL) + if (unlikely(!data)) return MDBX_EINVAL; if (!(mc->mc_db->md_flags & MDBX_DUPFIXED)) return MDBX_INCOMPATIBLE; rc = MDBX_SUCCESS; - if (!(mc->mc_flags & C_INITIALIZED)) + if ((mc->mc_flags & C_INITIALIZED) == 0) rc = cursor_last(mc, key, data); if (rc == MDBX_SUCCESS) { MDBX_cursor *mx = &mc->mc_xcursor->mx_cursor; + rc = MDBX_NOTFOUND; if (mx->mc_flags & C_INITIALIZED) { rc = cursor_sibling(mx, SIBLING_LEFT); if (rc == MDBX_SUCCESS) - goto fetchm; - } else { - rc = MDBX_NOTFOUND; + goto fetch_multiple; } } break; @@ -18754,13 +18784,13 @@ static int cursor_xinit2(MDBX_cursor *mc, MDBX_xcursor *src_mx, } static __inline int couple_init(MDBX_cursor_couple *couple, const size_t dbi, - MDBX_txn *const txn, MDBX_db *const db, + const MDBX_txn *const txn, MDBX_db *const db, MDBX_dbx *const dbx, uint8_t *const dbstate) { couple->outer.mc_signature = MDBX_MC_LIVE; couple->outer.mc_next = NULL; couple->outer.mc_backup = NULL; couple->outer.mc_dbi = (MDBX_dbi)dbi; - couple->outer.mc_txn = txn; + couple->outer.mc_txn = (MDBX_txn *)txn; couple->outer.mc_db = db; couple->outer.mc_dbx = dbx; couple->outer.mc_dbistate = dbstate; @@ -18798,7 +18828,7 @@ static __inline int couple_init(MDBX_cursor_couple *couple, const size_t dbi, } /* Initialize a cursor for a given transaction and database. */ -static int cursor_init(MDBX_cursor *mc, MDBX_txn *txn, size_t dbi) { +static int cursor_init(MDBX_cursor *mc, const MDBX_txn *txn, size_t dbi) { STATIC_ASSERT(offsetof(MDBX_cursor_couple, outer) == 0); return couple_init(container_of(mc, MDBX_cursor_couple, outer), dbi, txn, &txn->mt_dbs[dbi], &txn->mt_dbxs[dbi], @@ -18841,7 +18871,7 @@ void *mdbx_cursor_get_userctx(const MDBX_cursor *mc) { return couple->mc_userctx; } -int mdbx_cursor_bind(MDBX_txn *txn, MDBX_cursor *mc, MDBX_dbi dbi) { +int mdbx_cursor_bind(const MDBX_txn *txn, MDBX_cursor *mc, MDBX_dbi dbi) { if (unlikely(!mc)) return MDBX_EINVAL; @@ -18913,7 +18943,7 @@ int mdbx_cursor_bind(MDBX_txn *txn, MDBX_cursor *mc, MDBX_dbi dbi) { return MDBX_SUCCESS; } -int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) { +int mdbx_cursor_open(const MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) { if (unlikely(!ret)) return MDBX_EINVAL; *ret = NULL; @@ -18932,7 +18962,7 @@ int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) { return MDBX_SUCCESS; } -int mdbx_cursor_renew(MDBX_txn *txn, MDBX_cursor *mc) { +int mdbx_cursor_renew(const MDBX_txn *txn, MDBX_cursor *mc) { return likely(mc) ? mdbx_cursor_bind(txn, mc, mc->mc_dbi) : MDBX_EINVAL; } @@ -22199,7 +22229,7 @@ __cold int mdbx_env_stat_ex(const MDBX_env *env, const MDBX_txn *txn, return rc; } -__cold int mdbx_dbi_dupsort_depthmask(MDBX_txn *txn, MDBX_dbi dbi, +__cold int mdbx_dbi_dupsort_depthmask(const MDBX_txn *txn, MDBX_dbi dbi, uint32_t *mask) { int rc = check_txn(txn, MDBX_TXN_BLOCKED); if (unlikely(rc != MDBX_SUCCESS)) @@ -22817,7 +22847,7 @@ int mdbx_dbi_open_ex2(MDBX_txn *txn, const MDBX_val *name, return dbi_open(txn, name, flags, dbi, keycmp, datacmp); } -__cold int mdbx_dbi_stat(MDBX_txn *txn, MDBX_dbi dbi, MDBX_stat *dest, +__cold int mdbx_dbi_stat(const MDBX_txn *txn, MDBX_dbi dbi, MDBX_stat *dest, size_t bytes) { int rc = check_txn(txn, MDBX_TXN_BLOCKED); if (unlikely(rc != MDBX_SUCCESS)) @@ -22837,7 +22867,7 @@ __cold int mdbx_dbi_stat(MDBX_txn *txn, MDBX_dbi dbi, MDBX_stat *dest, return MDBX_BAD_TXN; if (unlikely(txn->mt_dbistate[dbi] & DBI_STALE)) { - rc = fetch_sdb(txn, dbi); + rc = fetch_sdb((MDBX_txn *)txn, dbi); if (unlikely(rc != MDBX_SUCCESS)) return rc; } @@ -22898,7 +22928,7 @@ int mdbx_dbi_close(MDBX_env *env, MDBX_dbi dbi) { return rc; } -int mdbx_dbi_flags_ex(MDBX_txn *txn, MDBX_dbi dbi, unsigned *flags, +int mdbx_dbi_flags_ex(const MDBX_txn *txn, MDBX_dbi dbi, unsigned *flags, unsigned *state) { int rc = check_txn(txn, MDBX_TXN_BLOCKED - MDBX_TXN_ERROR); if (unlikely(rc != MDBX_SUCCESS)) @@ -24069,9 +24099,10 @@ int mdbx_estimate_move(const MDBX_cursor *cursor, MDBX_val *key, MDBX_val *data, return mdbx_estimate_distance(cursor, &next.outer, distance_items); } -int mdbx_estimate_range(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *begin_key, - MDBX_val *begin_data, MDBX_val *end_key, - MDBX_val *end_data, ptrdiff_t *size_items) { +int mdbx_estimate_range(const MDBX_txn *txn, MDBX_dbi dbi, + const MDBX_val *begin_key, const MDBX_val *begin_data, + const MDBX_val *end_key, const MDBX_val *end_data, + ptrdiff_t *size_items) { int rc = check_txn(txn, MDBX_TXN_BLOCKED); if (unlikely(rc != MDBX_SUCCESS)) return rc; @@ -24102,13 +24133,13 @@ int mdbx_estimate_range(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *begin_key, return MDBX_SUCCESS; } + MDBX_val stub; if (!begin_key) { if (unlikely(!end_key)) { /* LY: FIRST..LAST case */ *size_items = (ptrdiff_t)begin.outer.mc_db->md_entries; return MDBX_SUCCESS; } - MDBX_val stub = {0, 0}; rc = cursor_first(&begin.outer, &stub, &stub); if (unlikely(end_key == MDBX_EPSILON)) { /* LY: FIRST..+epsilon case */ @@ -24120,7 +24151,6 @@ int mdbx_estimate_range(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *begin_key, if (unlikely(begin_key == MDBX_EPSILON)) { if (end_key == NULL) { /* LY: -epsilon..LAST case */ - MDBX_val stub = {0, 0}; rc = cursor_last(&begin.outer, &stub, &stub); return (rc == MDBX_SUCCESS) ? mdbx_cursor_count(&begin.outer, (size_t *)size_items) @@ -24138,7 +24168,7 @@ int mdbx_estimate_range(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *begin_key, (begin_key == end_key || begin.outer.mc_dbx->md_cmp(begin_key, end_key) == 0)) { /* LY: single key case */ - rc = cursor_set(&begin.outer, begin_key, NULL, MDBX_SET).err; + rc = cursor_set(&begin.outer, (MDBX_val *)begin_key, NULL, MDBX_SET).err; if (unlikely(rc != MDBX_SUCCESS)) { *size_items = 0; return (rc == MDBX_NOTFOUND) ? MDBX_SUCCESS : rc; @@ -24159,10 +24189,14 @@ int mdbx_estimate_range(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *begin_key, } } return MDBX_SUCCESS; - } else { - rc = cursor_set(&begin.outer, begin_key, begin_data, - begin_data ? MDBX_GET_BOTH_RANGE : MDBX_SET_RANGE) + } else if (begin_data) { + stub = *begin_data; + rc = cursor_set(&begin.outer, (MDBX_val *)begin_key, &stub, + MDBX_GET_BOTH_RANGE) .err; + } else { + stub = *begin_key; + rc = cursor_set(&begin.outer, &stub, nullptr, MDBX_SET_RANGE).err; } } @@ -24175,13 +24209,15 @@ int mdbx_estimate_range(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *begin_key, rc = cursor_init(&end.outer, txn, dbi); if (unlikely(rc != MDBX_SUCCESS)) return rc; - if (!end_key) { - MDBX_val stub = {0, 0}; + if (!end_key) rc = cursor_last(&end.outer, &stub, &stub); - } else { - rc = cursor_set(&end.outer, end_key, end_data, - end_data ? MDBX_GET_BOTH_RANGE : MDBX_SET_RANGE) + else if (end_data) { + stub = *end_data; + rc = cursor_set(&end.outer, (MDBX_val *)end_key, &stub, MDBX_GET_BOTH_RANGE) .err; + } else { + stub = *end_key; + rc = cursor_set(&end.outer, &stub, nullptr, MDBX_SET_RANGE).err; } if (unlikely(rc != MDBX_SUCCESS)) { if (rc != MDBX_NOTFOUND || !(end.outer.mc_flags & C_INITIALIZED)) @@ -25411,7 +25447,8 @@ LIBMDBX_API __cold int mdbx_env_info(const MDBX_env *env, MDBX_envinfo *info, return __inline_mdbx_env_info(env, info, bytes); } -LIBMDBX_API int mdbx_dbi_flags(MDBX_txn *txn, MDBX_dbi dbi, unsigned *flags) { +LIBMDBX_API int mdbx_dbi_flags(const MDBX_txn *txn, MDBX_dbi dbi, + unsigned *flags) { return __inline_mdbx_dbi_flags(txn, dbi, flags); } diff --git a/src/mdbx.c++ b/src/mdbx.c++ index 9ac6cd71..31c1e6a8 100644 --- a/src/mdbx.c++ +++ b/src/mdbx.c++ @@ -207,6 +207,44 @@ __cold bug::~bug() noexcept {} #endif /* Unused*/ +struct line_wrapper { + char *line, *ptr; + line_wrapper(char *buf) noexcept : line(buf), ptr(buf) {} + void put(char c, size_t wrap_width) noexcept { + *ptr++ = c; + if (wrap_width && ptr >= wrap_width + line) { + *ptr++ = '\n'; + line = ptr; + } + } + void put(const ::mdbx::slice &chunk, size_t wrap_width) noexcept { + if (!wrap_width || wrap_width > (ptr - line) + chunk.length()) { + memcpy(ptr, chunk.data(), chunk.length()); + ptr += chunk.length(); + } else { + for (size_t i = 0; i < chunk.length(); ++i) + put(chunk.char_ptr()[i], wrap_width); + } + } +}; + +template +struct temp_buffer { + TYPE inplace[(INPLACE_BYTES + sizeof(TYPE) - 1) / sizeof(TYPE)]; + const size_t size; + TYPE *const area; + temp_buffer(size_t bytes) + : size((bytes + sizeof(TYPE) - 1) / sizeof(TYPE)), + area((bytes > sizeof(inplace)) ? new TYPE[size] : inplace) { + memset(area, 0, sizeof(TYPE) * size); + } + ~temp_buffer() { + if (area != inplace) + delete[] area; + } + TYPE *end() const { return area + size; } +}; + } // namespace //------------------------------------------------------------------------------ @@ -599,7 +637,7 @@ char *to_hex::write_bytes(char *__restrict const dest, size_t dest_size) const { auto ptr = dest; auto src = source.byte_ptr(); - const char alphabase = (uppercase ? 'A' : 'a') - 10; + const char alpha_shift = (uppercase ? 'A' : 'a') - '9' - 1; auto line = ptr; for (const auto end = source.end_byte_ptr(); src != end; ++src) { if (wrap_width && size_t(ptr - line) >= wrap_width) { @@ -608,8 +646,8 @@ char *to_hex::write_bytes(char *__restrict const dest, size_t dest_size) const { } const int8_t hi = *src >> 4; const int8_t lo = *src & 15; - ptr[0] = char(alphabase + hi + (((hi - 10) >> 7) & -7)); - ptr[1] = char(alphabase + lo + (((lo - 10) >> 7) & -7)); + ptr[0] = char('0' + hi + (((9 - hi) >> 7) & alpha_shift)); + ptr[1] = char('0' + lo + (((9 - lo) >> 7) & alpha_shift)); ptr += 2; assert(ptr <= dest + dest_size); } @@ -621,7 +659,7 @@ char *to_hex::write_bytes(char *__restrict const dest, size_t dest_size) const { MDBX_CXX20_LIKELY { ::std::ostream::sentry sentry(out); auto src = source.byte_ptr(); - const char alphabase = (uppercase ? 'A' : 'a') - 10; + const char alpha_shift = (uppercase ? 'A' : 'a') - '9' - 1; unsigned width = 0; for (const auto end = source.end_byte_ptr(); src != end; ++src) { if (wrap_width && width >= wrap_width) { @@ -630,8 +668,8 @@ char *to_hex::write_bytes(char *__restrict const dest, size_t dest_size) const { } const int8_t hi = *src >> 4; const int8_t lo = *src & 15; - out.put(char(alphabase + hi + (((hi - 10) >> 7) & -7))); - out.put(char(alphabase + lo + (((lo - 10) >> 7) & -7))); + out.put(char('0' + hi + (((9 - hi) >> 7) & alpha_shift))); + out.put(char('0' + lo + (((9 - lo) >> 7) & alpha_shift))); width += 2; } } @@ -662,11 +700,11 @@ char *from_hex::write_bytes(char *__restrict const dest, int8_t hi = src[0]; hi = (hi | 0x20) - 'a'; - hi += 10 + ((hi >> 7) & 7); + hi += 10 + ((hi >> 7) & 39); int8_t lo = src[1]; lo = (lo | 0x20) - 'a'; - lo += 10 + ((lo >> 7) & 7); + lo += 10 + ((lo >> 7) & 39); *ptr++ = hi << 4 | lo; src += 2; @@ -709,156 +747,135 @@ enum : signed char { IL /* invalid */ = -1 }; -static const byte b58_alphabet[58] = { - '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', - 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', - 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; - -#ifndef bswap64 -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -static inline uint64_t bswap64(uint64_t v) noexcept { -#if __GNUC_PREREQ(4, 4) || __CLANG_PREREQ(4, 0) || \ - __has_builtin(__builtin_bswap64) - return __builtin_bswap64(v); -#elif defined(_MSC_VER) && !defined(__clang__) - return _byteswap_uint64(v); -#elif defined(__bswap_64) - return __bswap_64(v); -#elif defined(bswap_64) - return bswap_64(v); +#if MDBX_WORDBITS > 32 +using b58_uint = uint_fast64_t; #else - return v << 56 | v >> 56 | ((v << 40) & UINT64_C(0x00ff000000000000)) | - ((v << 24) & UINT64_C(0x0000ff0000000000)) | - ((v << 8) & UINT64_C(0x000000ff00000000)) | - ((v >> 8) & UINT64_C(0x00000000ff000000)) | - ((v >> 24) & UINT64_C(0x0000000000ff0000)) | - ((v >> 40) & UINT64_C(0x000000000000ff00)); +using b58_uint = uint_fast32_t; #endif -} -#endif /* __BYTE_ORDER__ */ -#endif /* ifndef bswap64 */ -static inline char b58_8to11(uint64_t &v) noexcept { - const unsigned i = unsigned(v % 58); +struct b58_buffer : public temp_buffer { + b58_buffer(size_t bytes, size_t estimation_ratio_numerator, + size_t estimation_ratio_denominator, size_t extra = 0) + : temp_buffer((/* пересчитываем по указанной пропорции */ + bytes = (bytes * estimation_ratio_numerator + + estimation_ratio_denominator - 1) / + estimation_ratio_denominator, + /* учитываем резервный старший байт в каждом слове */ + ((bytes + sizeof(b58_uint) - 2) / (sizeof(b58_uint) - 1) * + sizeof(b58_uint) + + extra) * + sizeof(b58_uint))) {} +}; + +static byte b58_8to11(b58_uint &v) noexcept { + static const char b58_alphabet[58] = { + '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; + + const auto i = size_t(v % 58); v /= 58; return b58_alphabet[i]; } +static slice b58_encode(b58_buffer &buf, const byte *begin, const byte *end) { + auto high = buf.end(); + const auto modulo = + b58_uint((sizeof(b58_uint) > 4) ? UINT64_C(0x1A636A90B07A00) /* 58^9 */ + : UINT32_C(0xACAD10) /* 58^4 */); + static_assert(sizeof(modulo) == 4 || sizeof(modulo) == 8, "WTF?"); + while (begin < end) { + b58_uint carry = *begin++; + auto ptr = buf.end(); + do { + assert(ptr > buf.area); + carry += *--ptr << CHAR_BIT; + *ptr = carry % modulo; + carry /= modulo; + } while (carry || ptr > high); + high = ptr; + } + + byte *output = static_cast(static_cast(buf.area)); + auto ptr = output; + for (auto porous = high; porous < buf.end();) { + auto chunk = *porous++; + static_assert(sizeof(chunk) == 4 || sizeof(chunk) == 8, "WTF?"); + assert(chunk < modulo); + if (sizeof(chunk) > 4) { + ptr[8] = b58_8to11(chunk); + ptr[7] = b58_8to11(chunk); + ptr[6] = b58_8to11(chunk); + ptr[5] = b58_8to11(chunk); + ptr[4] = b58_8to11(chunk); + ptr[3] = b58_8to11(chunk); + ptr[2] = b58_8to11(chunk); + ptr[1] = b58_8to11(chunk); + ptr[0] = b58_8to11(chunk); + ptr += 9; + } else { + ptr[3] = b58_8to11(chunk); + ptr[2] = b58_8to11(chunk); + ptr[1] = b58_8to11(chunk); + ptr[0] = b58_8to11(chunk); + ptr += 4; + } + assert(static_cast(ptr) < static_cast(porous)); + } + + while (output < ptr && *output == '1') + ++output; + return slice(output, ptr); +} + char *to_base58::write_bytes(char *__restrict const dest, size_t dest_size) const { if (MDBX_UNLIKELY(envisage_result_length() > dest_size)) MDBX_CXX20_UNLIKELY throw_too_small_target_buffer(); - auto ptr = dest; - auto src = source.byte_ptr(); - size_t left = source.length(); - auto line = ptr; - while (MDBX_LIKELY(left > 7)) { - uint64_t v; - std::memcpy(&v, src, 8); - src += 8; -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - v = bswap64(v); -#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -#else -#error "FIXME: Unsupported byte order" -#endif /* __BYTE_ORDER__ */ - ptr[10] = b58_8to11(v); - ptr[9] = b58_8to11(v); - ptr[8] = b58_8to11(v); - ptr[7] = b58_8to11(v); - ptr[6] = b58_8to11(v); - ptr[5] = b58_8to11(v); - ptr[4] = b58_8to11(v); - ptr[3] = b58_8to11(v); - ptr[2] = b58_8to11(v); - ptr[1] = b58_8to11(v); - ptr[0] = b58_8to11(v); - assert(v == 0); - ptr += 11; - left -= 8; - if (wrap_width && size_t(ptr - line) >= wrap_width && left) { - *ptr = '\n'; - line = ++ptr; - } - assert(ptr <= dest + dest_size); + auto begin = source.byte_ptr(); + auto end = source.end_byte_ptr(); + line_wrapper wrapper(dest); + while (MDBX_LIKELY(begin < end) && *begin == 0) { + wrapper.put('1', wrap_width); + assert(wrapper.ptr <= dest + dest_size); + ++begin; } - if (left) { - uint64_t v = 0; - unsigned parrots = 31; - do { - v = (v << 8) + *src++; - parrots += 43; - } while (--left); - - auto tail = ptr += parrots >> 5; - assert(ptr <= dest + dest_size); - do { - *--tail = b58_8to11(v); - parrots -= 32; - } while (parrots > 31); - assert(v == 0); - } - - return ptr; + b58_buffer buf(end - begin, 11, 8); + wrapper.put(b58_encode(buf, begin, end), wrap_width); + return wrapper.ptr; } ::std::ostream &to_base58::output(::std::ostream &out) const { if (MDBX_LIKELY(!is_empty())) MDBX_CXX20_LIKELY { ::std::ostream::sentry sentry(out); - auto src = source.byte_ptr(); - size_t left = source.length(); + auto begin = source.byte_ptr(); + auto end = source.end_byte_ptr(); unsigned width = 0; - std::array buf; - - while (MDBX_LIKELY(left > 7)) { - uint64_t v; - std::memcpy(&v, src, 8); - src += 8; -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - v = bswap64(v); -#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -#else -#error "FIXME: Unsupported byte order" -#endif /* __BYTE_ORDER__ */ - buf[10] = b58_8to11(v); - buf[9] = b58_8to11(v); - buf[8] = b58_8to11(v); - buf[7] = b58_8to11(v); - buf[6] = b58_8to11(v); - buf[5] = b58_8to11(v); - buf[4] = b58_8to11(v); - buf[3] = b58_8to11(v); - buf[2] = b58_8to11(v); - buf[1] = b58_8to11(v); - buf[0] = b58_8to11(v); - assert(v == 0); - out.write(&buf.front(), 11); - left -= 8; - if (wrap_width && (width += 11) >= wrap_width && left) { + while (MDBX_LIKELY(begin < end) && *begin == 0) { + out.put('1'); + if (wrap_width && ++width >= wrap_width) { out << ::std::endl; width = 0; } + ++begin; } - if (left) { - uint64_t v = 0; - unsigned parrots = 31; - do { - v = (v << 8) + *src++; - parrots += 43; - } while (--left); - - auto ptr = buf.end(); - do { - *--ptr = b58_8to11(v); - parrots -= 32; - } while (parrots > 31); - assert(v == 0); - out.write(&*ptr, buf.end() - ptr); + b58_buffer buf(end - begin, 11, 8); + const auto chunk = b58_encode(buf, begin, end); + if (!wrap_width || wrap_width > width + chunk.length()) + out.write(chunk.char_ptr(), chunk.length()); + else { + for (size_t i = 0; i < chunk.length(); ++i) { + out.put(chunk.char_ptr()[i]); + if (wrap_width && ++width >= wrap_width) { + out << ::std::endl; + width = 0; + } + } } } return out; @@ -884,10 +901,46 @@ const signed char b58_map[256] = { IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, IL // f0 }; -static inline signed char b58_11to8(uint64_t &v, const byte c) noexcept { - const signed char m = b58_map[c]; - v = v * 58 + m; - return m; +static slice b58_decode(b58_buffer &buf, const byte *begin, const byte *end, + bool ignore_spaces) { + auto high = buf.end(); + while (begin < end) { + const auto c = b58_map[*begin++]; + if (MDBX_LIKELY(c >= 0)) { + b58_uint carry = c; + auto ptr = buf.end(); + do { + assert(ptr > buf.area); + carry += *--ptr * 58; + *ptr = carry & (~b58_uint(0) >> CHAR_BIT); + carry >>= CHAR_BIT * (sizeof(carry) - 1); + } while (carry || ptr > high); + high = ptr; + } else if (MDBX_UNLIKELY(!ignore_spaces || !isspace(begin[-1]))) + MDBX_CXX20_UNLIKELY + throw std::domain_error("mdbx::from_base58:: invalid base58 string"); + } + + byte *output = static_cast(static_cast(buf.area)); + auto ptr = output; + for (auto porous = high; porous < buf.end(); ++porous) { + auto chunk = *porous; + static_assert(sizeof(chunk) == 4 || sizeof(chunk) == 8, "WTF?"); + assert(chunk <= (~b58_uint(0) >> CHAR_BIT)); + if (sizeof(chunk) > 4) { + *ptr++ = byte(uint_fast64_t(chunk) >> CHAR_BIT * 6); + *ptr++ = byte(uint_fast64_t(chunk) >> CHAR_BIT * 5); + *ptr++ = byte(uint_fast64_t(chunk) >> CHAR_BIT * 4); + *ptr++ = byte(chunk >> CHAR_BIT * 3); + } + *ptr++ = byte(chunk >> CHAR_BIT * 2); + *ptr++ = byte(chunk >> CHAR_BIT * 1); + *ptr++ = byte(chunk >> CHAR_BIT * 0); + } + + while (output < ptr && *output == 0) + ++output; + return slice(output, ptr); } char *from_base58::write_bytes(char *__restrict const dest, @@ -896,98 +949,33 @@ char *from_base58::write_bytes(char *__restrict const dest, MDBX_CXX20_UNLIKELY throw_too_small_target_buffer(); auto ptr = dest; - auto src = source.byte_ptr(); - for (auto left = source.length(); left > 0;) { - if (MDBX_UNLIKELY(isspace(*src)) && ignore_spaces) { - ++src; - --left; - continue; - } - - if (MDBX_LIKELY(left > 10)) { - uint64_t v = 0; - if (MDBX_UNLIKELY((b58_11to8(v, src[0]) | b58_11to8(v, src[1]) | - b58_11to8(v, src[2]) | b58_11to8(v, src[3]) | - b58_11to8(v, src[4]) | b58_11to8(v, src[5]) | - b58_11to8(v, src[6]) | b58_11to8(v, src[7]) | - b58_11to8(v, src[8]) | b58_11to8(v, src[9]) | - b58_11to8(v, src[10])) < 0)) - MDBX_CXX20_UNLIKELY goto bailout; -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - v = bswap64(v); -#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -#else -#error "FIXME: Unsupported byte order" -#endif /* __BYTE_ORDER__ */ - std::memcpy(ptr, &v, 8); - ptr += 8; - src += 11; - left -= 11; - assert(ptr <= dest + dest_size); - continue; - } - - constexpr unsigned invalid_length_mask = 1 << 1 | 1 << 4 | 1 << 8; - if (MDBX_UNLIKELY(invalid_length_mask & (1 << left))) - MDBX_CXX20_UNLIKELY goto bailout; - - uint64_t v = 1; - unsigned parrots = 0; - do { - if (MDBX_UNLIKELY(b58_11to8(v, *src++) < 0)) - MDBX_CXX20_UNLIKELY goto bailout; - parrots += 32; - } while (--left); - - auto tail = ptr += parrots / 43; - assert(ptr <= dest + dest_size); - do { - *--tail = byte(v); - v >>= 8; - } while (v > 255); - break; + auto begin = source.byte_ptr(); + auto const end = source.end_byte_ptr(); + while (begin < end && *begin <= '1') { + if (MDBX_LIKELY(*begin == '1')) + MDBX_CXX20_LIKELY *ptr++ = 0; + else if (MDBX_UNLIKELY(!ignore_spaces || !isspace(*begin))) + MDBX_CXX20_UNLIKELY + throw std::domain_error("mdbx::from_base58:: invalid base58 string"); + ++begin; } - return ptr; -bailout: - throw std::domain_error("mdbx::from_base58:: invalid base58 string"); + b58_buffer buf(end - begin, 47, 64); + auto slice = b58_decode(buf, begin, end, ignore_spaces); + memcpy(ptr, slice.data(), slice.length()); + return ptr + slice.length(); } bool from_base58::is_erroneous() const noexcept { - bool got = false; - auto src = source.byte_ptr(); - for (auto left = source.length(); left > 0;) { - if (MDBX_UNLIKELY(*src <= ' ') && - MDBX_LIKELY(ignore_spaces && isspace(*src))) { - ++src; - --left; - continue; - } - - if (MDBX_LIKELY(left > 10)) { - if (MDBX_UNLIKELY((b58_map[src[0]] | b58_map[src[1]] | b58_map[src[2]] | - b58_map[src[3]] | b58_map[src[4]] | b58_map[src[5]] | - b58_map[src[6]] | b58_map[src[7]] | b58_map[src[8]] | - b58_map[src[9]] | b58_map[src[10]]) < 0)) - MDBX_CXX20_UNLIKELY return true; - src += 11; - left -= 11; - got = true; - continue; - } - - constexpr unsigned invalid_length_mask = 1 << 1 | 1 << 4 | 1 << 8; - if (invalid_length_mask & (1 << left)) - return false; - - do - if (MDBX_UNLIKELY(b58_map[*src++] < 0)) - MDBX_CXX20_UNLIKELY return true; - while (--left); - got = true; - break; + auto begin = source.byte_ptr(); + auto const end = source.end_byte_ptr(); + while (begin < end) { + if (MDBX_UNLIKELY(b58_map[*begin] < 0 && + !(ignore_spaces && isspace(*begin)))) + return true; + ++begin; } - return !got; + return false; } //------------------------------------------------------------------------------ diff --git a/src/options.h b/src/options.h index 0ef27e6f..19a7cb32 100644 --- a/src/options.h +++ b/src/options.h @@ -28,9 +28,17 @@ #define MDBX_OSX_SPEED_INSTEADOF_DURABILITY MDBX_OSX_WANNA_DURABILITY #endif /* MDBX_OSX_SPEED_INSTEADOF_DURABILITY */ +/** Controls using of POSIX' madvise() and/or similar hints. */ +#ifndef MDBX_ENABLE_MADVISE +#define MDBX_ENABLE_MADVISE 1 +#elif !(MDBX_ENABLE_MADVISE == 0 || MDBX_ENABLE_MADVISE == 1) +#error MDBX_ENABLE_MADVISE must be defined as 0 or 1 +#endif /* MDBX_ENABLE_MADVISE */ + /** Controls checking PID against reuse DB environment after the fork() */ #ifndef MDBX_ENV_CHECKPID -#if defined(MADV_DONTFORK) || defined(_WIN32) || defined(_WIN64) +#if (defined(MADV_DONTFORK) && MDBX_ENABLE_MADVISE) || defined(_WIN32) || \ + defined(_WIN64) /* PID check could be omitted: * - on Linux when madvise(MADV_DONTFORK) is available, i.e. after the fork() * mapped pages will not be available for child process. @@ -96,8 +104,7 @@ /** Controls using Unix' mincore() to determine whether DB-pages * are resident in memory. */ #ifndef MDBX_ENABLE_MINCORE -#if MDBX_ENABLE_PREFAULT && \ - (defined(MINCORE_INCORE) || !(defined(_WIN32) || defined(_WIN64))) +#if defined(MINCORE_INCORE) || !(defined(_WIN32) || defined(_WIN64)) #define MDBX_ENABLE_MINCORE 1 #else #define MDBX_ENABLE_MINCORE 0 @@ -118,13 +125,6 @@ #error MDBX_ENABLE_BIGFOOT must be defined as 0 or 1 #endif /* MDBX_ENABLE_BIGFOOT */ -/** Controls using of POSIX' madvise() and/or similar hints. */ -#ifndef MDBX_ENABLE_MADVISE -#define MDBX_ENABLE_MADVISE 1 -#elif !(MDBX_ENABLE_MADVISE == 0 || MDBX_ENABLE_MADVISE == 1) -#error MDBX_ENABLE_MADVISE must be defined as 0 or 1 -#endif /* MDBX_ENABLE_MADVISE */ - /** Disable some checks to reduce an overhead and detection probability of * database corruption to a values closer to the LMDB. */ #ifndef MDBX_DISABLE_VALIDATION diff --git a/test/config.c++ b/test/config.c++ index 31cf9395..922f7b37 100644 --- a/test/config.c++ +++ b/test/config.c++ @@ -667,7 +667,10 @@ bool actor_config::deserialize(const char *str, actor_config &config) { } str = slash + 1; - uint64_t verify = std::stoull(std::string(str)); + uint64_t verify = 0; + while (*str >= '0' && *str <= '9') + verify = verify * 10 + *str++ - '0'; + if (checksum.value != verify) { TRACE("<< actor_config::deserialize: checksum mismatch\n"); return false;