mdbx++: provide copy content for move-assignment of mdbx::buffer for non-equal allocators case.

2026-07-13 mdbx: Update ChangeLog.
2026-07-12 mdbx-tools: fix/remove leftover usage of float point in `mdbx_stat`.
2026-07-12 mdbx: don't fail transaction on `MDBX_UNABLE_EXTEND_MAPSIZE`.
2026-07-12 mdbx: replace `reasonable_db_maxsize()` with `globals.assume_ram_pages` and `globals.reasonable_db_maxsize`.
2026-07-12 mdbx: avoid false-positive warning from Coverity.
2026-07-12 mdbx-make: more MDBX_CHECKING for `test-*` targets.
2026-07-12 mdbx-doc: more minor typos fixing.
2026-07-12 mdbx++: provide copy content for move-assignment of `mdbx::buffer` for non-equal allocators case.
2026-07-11 mdbx: introduce `globals.mmap_limit` and adjusting mmap-limit for ASAN/Valgrind.
2026-07-11 mdbx-tools: refine `mdbx_load`.
2026-07-11 mdbx-tools: fix 2Gb ram consumption by `mdbx_load` due leak of debug changes.
2026-07-11 mdbx-make: refine `test-*` and `smoke-*` targets.
2026-07-10 mdbx: more `LOG_IFERR(err)` into the opening path.
2026-07-10 mdbx-test: refine `copy-db` testcases, add assertions for `copy_pathname.empty()`.
2026-07-10 mdbx: fix dummy `EINVAL` inside `mdbx_chk`.
2026-07-10 mdbx: introduce `osal_fseek_shut()` with workaround for `EINVAL` from `lseek()` with huge offsets.
2026-07-10 mdbx: fix ODR-warnings from modern GCC while both LTO and UBSAN enabled.
2026-07-10 mdbx-make: fix `CTEST_OPT` for `test-memcheck` target.
2026-07-10 mdbx-test: move `valgrind_suppress.txt` and corresponding cmake-definitions.
This commit is contained in:
Леонид Юрьев (Leonid Yuriev)
2026-07-13 02:41:02 +03:00
parent 435d766267
commit 81eaf88944
20 changed files with 561 additions and 427 deletions
+20
View File
@@ -125,6 +125,26 @@ if(NOT CMAKE_VERSION VERSION_LESS 3.8.2)
source_group("markdown" FILES REGULAR_EXPRESSION ".*[.]md")
endif()
# Memcheck-options should be defined before including CTest.
# Otherwise CTest just ignore it.
if(NOT WIN32)
if(NOT MEMORYCHECK_COMMAND)
find_program(MEMORYCHECK_COMMAND valgrind)
endif()
if(MEMORYCHECK_COMMAND)
set(MEMORYCHECK_TYPE Valgrind)
set(MEMORYCHECK_SUPPRESSIONS_FILE
"${CMAKE_CURRENT_SOURCE_DIR}/valgrind_suppress.txt"
CACHE FILEPATH "Suppressions file for Valgrind" FORCE)
set(MEMORYCHECK_COMMAND_OPTIONS
"--trace-children=yes --leak-check=full --track-origins=yes --expensive-definedness-checks=yes --error-exitcode=42 --error-markers=@VALGRIND --errors-for-leak-kinds=definite --fair-sched=yes --suppressions=${MEMORYCHECK_SUPPRESSIONS_FILE}"
CACHE STRING "Valgrind options" FORCE)
set(MEMORYCHECK_COMMAND_OPTIONS
"${MEMORYCHECK_COMMAND_OPTIONS}"
CACHE STRING "Valgrind options" FORCE)
endif()
endif()
if(NOT ((DEFINED BUILD_TESTING AND NOT BUILD_TESTING) OR (DEFINED MDBX_ENABLE_TESTS AND NOT MDBX_ENABLE_TESTS)))
include(CTest)
option(MDBX_ENABLE_TESTS "Build MDBX tests" ${BUILD_TESTING})
+2 -2
View File
@@ -117,7 +117,7 @@ OpenLDAP, совершенно без намерения как-либо зад
3. Последствия и актуальные требования
Всё очень просто. Потребуется обеспечить требования новой лицензии в
соответствии с 4-м пунктом лицензции Apache 2.0.
соответствии с 4-м пунктом лицензии Apache 2.0.
В частности, при использовании/распространении libmdbx потребуется
обеспечить наличие файлов с текстом лицензии и файла NOTICE, а также
@@ -143,7 +143,7 @@ A copy of this license is available in the file LICENSE in the
top-level directory of the distribution or, alternatively, at
<http://www.OpenLDAP.org/license.html>.
LMDB itself devived code from btree.c written by Martin Hedenfalk:
LMDB itself derived code from btree.c written by Martin Hedenfalk:
Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se>
Permission to use, copy, modify, and distribute this software for any
+71 -59
View File
@@ -21,50 +21,6 @@ The supporting release of a stable branch with bug fixes.
- [Cosmin Apreutesei](https://github.com/capr) for bugs reporting.
- [stslam](https://github.com/stslam) for Embarcadero C++ Builder support.
### Improvements:
- Deferred invalidation of the dbi-handles of dropped tables has been implemented until the corresponding transactions are committed.
Previously, libmdbx implemented the behavior historically inherited from LMDB, when handles of a dropped tables were immediately closed, regardless of the possible subsequent abortion of such transactions.
Now, when tables are dropped, both ones associated handles and data remain available for other transactions running in parallel within the current process.
This improvement has been asking for a long time, but it required a lot of preparation and refactoring which are done step-by-step during a few last releases.
- Embarcadero C++ Builder now could be used to build libmdbx on Windows.
- Allowed to use cursors binded to the same table/DBI, but to different read-only transaction, in an API with multiple cursors in the parameters.
- Added the missing recipe for Conan to an amalgamated source code.
- Added check-and-retry of presync-to-disk condition to avoid latency spikes in commit path during asynchronous calls of `mdbx_env_sync()`, `mdbx_env_sync_ex()`, `mdbx_env_sync_poll()`.
- Added the `MDBX_opt_presync_threshold` option.
### Fixes:
- Fixed assertions triggering in a specific scenarios of creating and renaming tables within nested transactions.
- Fixed the [issue](https://github.com/Mithril-mine/libmdbx/issues/361) of loosing a table content after abortion the nested transaction where such table was dropped.
- Fixed `ERROR_LOCK_VIOLATION` during defrag on Windows in operation modes using overlapped I/O.
- Fixed a lot of typos and other minors by AI suggestions.
- Fixed off-by-one bugs in the `mdbx::from_base64` and `mdbx::slice::is_printable()`.
- Fixed major typo in condition inside `latch_maindb_locked()`.
However, despite the severity of the error, the scenario of its manifestation could not be found due to a combination of other checks in the code.
- Fixed possibility of infinite loop inside `mdbx_txn_abort()` because of `memcmp()`/`memcpy()` typo.
- Fixed `env_owned_wrtxn()` to avoid by-pass locking in the `MDBX_NOSTICKYTHREADS` mode.
- Fixed missing `return` statement in an one of error paths inside `mdbx_cursor_bind()`.
- Fixed potential buffer overread by `fgets()` in `mdbx_load` utility.
- Fixed a lot of typos and a few bugs detected by CodeQL.
### Backward compatibility breaks:
- Now API functions that do not receive a transaction in arguments, but require a write lock, always checks that the current thread owns (launched) the writing transaction.
@@ -88,11 +44,67 @@ The supporting release of a stable branch with bug fixes.
- On Windows platform the Windows-10 API is now used by default.
Previous versions are still supported, but now they should be explicitly requested during library build by defining `_WIN32_WINNT`
### Improvements:
- Deferred invalidation of the dbi-handles of dropped tables has been implemented until the corresponding transactions are committed.
Previously, libmdbx implemented the behavior historically inherited from LMDB, when handles of a dropped tables were immediately closed, regardless of the possible subsequent abortion of such transactions.
Now, when tables are dropped, both ones associated handles and data remain available for other transactions running in parallel within the current process.
This improvement has been asking for a long time, but it required a lot of preparation and refactoring which are done step-by-step during a few last releases.
- Embarcadero C++ Builder now could be used to build libmdbx on Windows.
- Allowed to use cursors binded to the same table/DBI, but to different read-only transaction, in an API with multiple cursors in the parameters.
- Added the missing recipe for Conan to an amalgamated source code.
- Added check-and-retry of presync-to-disk condition to avoid latency spikes in commit path during asynchronous calls of `mdbx_env_sync()`, `mdbx_env_sync_ex()`, `mdbx_env_sync_poll()`.
- Added the `MDBX_opt_presync_threshold` option.
- In the C++ API the move assignment operator of the `mdbx::buffer<>` template now supports the case of unequal allocators by copying the contents of a source.
- Added adjustment of the maximum size of the database and memory mapping in the modes of using Valgrind or AddressSanitizer, which greatly simplifies the use of these tools.
### Fixes:
- Fixed assertions triggering in a specific scenarios of creating and renaming tables within nested transactions.
- Fixed the [issue](https://github.com/Mithril-mine/libmdbx/issues/361) of loosing a table content after abortion the nested transaction where such table was dropped.
- Fixed `ERROR_LOCK_VIOLATION` during defrag on Windows in operation modes using overlapped I/O.
- Fixed a lot of typos and other minors by AI suggestions.
- Fixed off-by-one bugs in the `mdbx::from_base64` and `mdbx::slice::is_printable()`.
- Fixed major typo in condition inside `latch_maindb_locked()`.
However, despite the severity of the error, the scenario of its manifestation could not be found due to a combination of other checks in the code.
- Fixed possibility of infinite loop inside `mdbx_txn_abort()` because of `memcmp()`/`memcpy()` typo.
- Fixed `env_owned_wrtxn()` to avoid by-pass locking in the `MDBX_NOSTICKYTHREADS` mode.
- Fixed missing `return` statement in one of the error paths inside `mdbx_cursor_bind()`.
- Fixed potential buffer overread by `fgets()` in `mdbx_load` utility.
- Fixed a lot of typos and a few bugs detected by CodeQL.
- Fixed ODR-violations warnings from modern GCC while both LTO and UBSAN are enabled.
- Fixed unreasonably high memory 2GB consumption in `mdbx_load` utility due to leftover debug changes.
- Fixed running `ctest -T memcheck` by adding workaround of CTest/CMake bugs for Valgrind parameters.
- Fixed/removed leftover usage of float point in `mdbx_stat` utility.
--------------------------------------------------------------------------------
## v0.14.2 "Буревестник" (stormy petrel, aka Bourevestnik) at 2026-05-14
The frontward release with new major features and internal refactoring.
The forward-looking release with new major features and internal refactoring.
### Important:
@@ -185,7 +197,7 @@ The frontward release with new major features and internal refactoring.
- The command-line options `-b number`, `-L megabytes`, `-d percent` and `-G geometry` have been added to the `mdbx_load` utility, allowing you to set the size of batch inserts, limit the volume of transactions, set the desired page filling density and redefine the geometry of the database when loading data from a dump.
- Search was accelerated by using a branchless algorithm and embedding code of built-in/default comparators.
- Search was accelerated by using a branchless algorithm and embedding code of built-in/default comparators.
- Redesigned internal verification statements and related build options.
At the same time, `NDEBUG` no longer affects checks in the main engine code, which eliminates the causes of unexpected performance drops due to the lack of a definition of `NDEBUG` in non-debugging builds of users.
@@ -280,7 +292,7 @@ The frontward release with new major features and internal refactoring.
- The `mdbx_load` utility has fixed errors in loading zero-length values and exchanging shrink/growth parameters in the database geometry.
- Fixed a crash in the `SIGSEGV` software when all meta pages are not fully usable.
- Fixed a `SIGSEGV` crash when all meta pages are not fully usable.
- Fixed a typo in the condition for determining a change in the size of the database when rolling back a nested transaction.
@@ -288,7 +300,7 @@ The frontward release with new major features and internal refactoring.
- Fixed a typo in the `ST_EXPORTED` processing path that broke the build on platforms where the mentioned flag is defined for `fstatvfs()`.
- Fixed the crash of the `SIGSEGV` software due to an attempt to clean/overwrite a corrupted meta page when opening the database in read-only mode.
- Fixed a `SIGSEGV` crash due to an attempt to clean/overwrite a corrupted meta page when opening the database in read-only mode.
--------------------------------------------------------------------------------
@@ -523,7 +535,7 @@ English version [by liar Google](https://libmdbx-dqdkfa-ru.translate.goog/md__ch
- Кратное сокращение итераций тестов в зависимости от конфигурации Valgrind/Debug/CI.
- Устранены предупреждения UBASN о невыравненном доступе в тесте extra/close-dbi.
- Устранены предупреждения UBSAN о невыравненном доступе в тесте extra/close-dbi.
- Добавлен перехват и логирование исключений в extra-тестах на C++.
@@ -886,7 +898,7 @@ Other:
- Дополнен тест курсоров extra/cursor-closing.
- В `NOTICE` обновлена информация о Github.
- В `NOTICE` обновлена информация о GitHub.
--------------------------------------------------------------------------------
@@ -1004,7 +1016,7 @@ Other:
- Кратное сокращение итераций тестов в зависимости от конфигурации Valgrind/Debug/CI.
- Устранены предупреждения UBASN о невыравненном доступе в тесте extra/close-dbi.
- Устранены предупреждения UBSAN о невыравненном доступе в тесте extra/close-dbi.
- Добавлен перехват и логирование исключений в extra-тестах на C++.
@@ -1460,7 +1472,7 @@ Other:
лет после выпуска 0.12.1.
```
git diff' stat: 14 commits, 7 files changed, 256 insertions(+), 103 deletions(-)
git diff --stat: 14 commits, 7 files changed, 256 insertions(+), 103 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
```
@@ -1521,7 +1533,7 @@ Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
рекомендуется использовать ветку `master`.
```
git diff' stat: 6 commits, 5 files changed, 239 insertions(+), 6 deletions(-)
git diff --stat: 6 commits, 5 files changed, 239 insertions(+), 6 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
```
@@ -1563,7 +1575,7 @@ Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
военных спутников США.
```
git diff' stat: 29 commits, 14 files changed, 379 insertions(+), 151 deletions(-)
git diff --stat: 29 commits, 14 files changed, 379 insertions(+), 151 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
```
@@ -1622,7 +1634,7 @@ Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
в память Героя России гвардии майора Дмитрия Семёнова с позывным "СЭМ".
```
git diff' stat: 19 commits, 57 files changed, 751 insertions(+), 331 deletions(-)
git diff --stat: 19 commits, 57 files changed, 751 insertions(+), 331 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
```
@@ -1700,7 +1712,7 @@ Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением недочетов.
```
git diff' stat: 32 commits, 8 files changed, 667 insertions(+), 401 deletions(-)
git diff --stat: 32 commits, 8 files changed, 667 insertions(+), 401 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
```
@@ -1764,7 +1776,7 @@ Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
в день 100-летия со дня рождения выдающегося советского и российского ученого и конструктора [Влади́мира Фёдоровича У́ткина](https://ru.wikipedia.org/wiki/Уткин,_Владимир_Фёдорович).
```
git diff' stat: 24 commits, 18 files changed, 624 insertions(+), 94 deletions(-)
git diff --stat: 24 commits, 18 files changed, 624 insertions(+), 94 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
```
@@ -2054,7 +2066,7 @@ Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
Новое:
- Использование адреса [https://libmdbx.dqdkfa.ru/dead-github](https://libmdbx.dqdkfa.ru/dead-github)
для отсылки к сохранённым в web.archive.org копиям ресурсов, уничтоженных администрацией Github.
для отсылки к сохранённым в web.archive.org копиям ресурсов, уничтоженных администрацией GitHub.
- Реализована prefault-запись при выделении страниц для read-write отображений.
Это приводит к кратному снижению системных издержек и существенному увеличению
@@ -2344,7 +2356,7 @@ Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
## v0.12.1 "Positive Proxima" at 2022-08-24
The planned frontward release with new superior features on the day of 20 anniversary of [Positive Technologies](https://ptsecurty.com).
The planned frontward release with new superior features on the day of 20 anniversary of [Positive Technologies](https://ptsecurity.com).
```
37 files changed, 7604 insertions(+), 7417 deletions(-)
+21 -14
View File
@@ -116,7 +116,7 @@ define uname2ldflags
esac
endef
# TIP: try add the'-Wl, --no-as-needed,-lrt' for ability to built with modern glibc, but then use with the old.
# TIP: try adding '-Wl,--no-as-needed,-lrt' for the ability to build with modern glibc, and then use with the old.
define uname2libs
case "$(UNAME)" in
CYGWIN*|MINGW*|MSYS*|Windows*)
@@ -197,6 +197,7 @@ help:
@echo " make test-asan - build with AddressSanitizer and run basic test"
@echo " make test-leak - build with LeakSanitizer and run basic test"
@echo " make test-ubsan - build with UndefinedBehaviourSanitizer and run basic test"
@echo " make test-memcheck - build with Valgrind support and run basic test under memcheck tool"
show-options:
@echo " MDBX_BUILD_OPTIONS = $(MDBX_BUILD_OPTIONS)"
@@ -299,24 +300,30 @@ TEST_TARGETS += mdbx_legacy_example $(call select_by,MDBX_BUILD_CXX,mdbx_modern_
test: $(TEST_TARGETS)
build-test: $(TEST_BUILD_TARGETS)
test-assertion: MDBX_BUILD_OPTIONS += -DMDBX_CHECKING=2
test-assertion: CMAKE_OPT += -DMDBX_CHECKING=2
test-assertion: smoke
test-valgrind: test-memcheck
test-memcheck: CFLAGS_EXTRA += -Ofast -DENABLE_MEMCHECK
test-memcheck: CMAKE_OPT += -DENABLE_MEMCHECK=ON
test-memcheck: build-test build-stochastic
smoke-valgrind: smoke-memcheck
smoke-memcheck test-memcheck: CFLAGS_EXTRA += -Ofast -DENABLE_MEMCHECK -DMDBX_CHECKING=1
smoke-memcheck test-memcheck: CMAKE_OPT += -DENABLE_UBSAN:BOOL=OFF -DENABLE_ASAN:BOOL=OFF -DENABLE_MEMCHECK:BOOL=ON -DMDBX_CHECKING=1
smoke-memcheck test-memcheck: CTEST_OPT += -T memcheck
test-memcheck: build-test build-stochastic ctest
@echo ' RUNNING `test/stochastic.sh --with-valgrind --loops 2`...'
$(QUIET)test/stochastic.sh --with-valgrind --loops 2 --db-upto-mb 256 --skip-make >$(TEST_LOG) || (cat $(TEST_LOG) && false)
smoke-memcheck: smoke
test-ubsan:
@echo ' RE-TEST with `-fsanitize=undefined` option...'
$(QUIET)$(MAKE) IOARENA=false CXXSTD=$(CXXSTD) CMAKE_OPT="-DENABLE_UBSAN=ON" CFLAGS_EXTRA="-DENABLE_UBSAN -Ofast -fsanitize=undefined -fsanitize-undefined-trap-on-error" build-test test-stochastic
smoke-assertion test-assertion: MDBX_BUILD_OPTIONS += -DMDBX_CHECKING=2
smoke-assertion test-assertion: CMAKE_OPT += -DMDBX_CHECKING=2
test-assertion: test
smoke-assertion: smoke
test-asan:
@echo ' RE-TEST with `-fsanitize=address` option...'
$(QUIET)$(MAKE) IOARENA=false CXXSTD=$(CXXSTD) CMAKE_OPT="-DENABLE_ASAN=ON" CFLAGS_EXTRA="-Os -fsanitize=address" build-test test-stochastic
smoke-ubsan test-ubsan: CFLAGS_EXTRA += -DENABLE_UBSAN -Ofast -fsanitize=undefined -fsanitize-undefined-trap-on-error -DMDBX_CHECKING=2
smoke-ubsan test-ubsan: CMAKE_OPT += -DENABLE_UBSAN:BOOL=ON -DENABLE_ASAN:BOOL=OFF -DENABLE_MEMCHECK:BOOL=OFF -DMDBX_CHECKING=2
test-ubsan: test
smoke-ubsan: smoke
smoke-asan test-asan: CFLAGS_EXTRA += -Os -fsanitize=address -DMDBX_CHECKING=2
smoke-asan test-asan: CMAKE_OPT += -DENABLE_UBSAN:BOOL=OFF -DENABLE_ASAN:BOOL=ON -DENABLE_MEMCHECK:BOOL=OFF -DMDBX_CHECKING=2
test-asan: test
smoke-asan: smoke
test-leak:
@echo ' RE-TEST with `-fsanitize=leak` option...'
+7 -7
View File
@@ -20,24 +20,24 @@ please refer to the COPYRIGHT file within libmdbx source.
---
On 2022-04-15, without any warnings or following explanations, the
Github administration deleted _libmdbx_, my account and all other
GitHub administration deleted _libmdbx_, my account and all other
projects (status 404). A few months later, without any involvement or
notification from/to me, the projects were restored/opened in the "public
read-only archive" status from some kind of incomplete backup. I regard
these actions of Github as malicious sabotage, and I consider the Github
these actions of GitHub as malicious sabotage, and I consider the GitHub
service itself to have lost trust forever.
As a result of what has happened, I will never, under any circumstances,
post the primary sources (aka origins) of my projects on Github, or rely
in any way on the Github infrastructure.
post the primary sources (aka origins) of my projects on GitHub, or rely
in any way on the GitHub infrastructure.
Nonetheless, taking into account that it is more convenient for users of
my projects to access them on Github, I did not want to restrict their
my projects to access them on GitHub, I did not want to restrict their
freedom or create inconvenience, and therefore I posted mirrors on
Github. However, it was noticed that despite the development of
GitHub. However, it was noticed that despite the development of
_libmdbx_, free support and consultations, many projects and users
deviated from the rules of a fair deal and instead of helping and
building relationships, they began to adjust links and delete references
in violation of the license. Therefore, in protest against such unworthy
actions, on December 10, 2025, I decided to abandon the placement of
mirrors on Github.
mirrors on GitHub.
+4 -4
View File
@@ -67,7 +67,7 @@ However, _MithrilDB_ will not be available for countries unfriendly to Russia (i
Nonetheless, I try not to make any promises regarding _MithrilDB_ until release.
Contrary to _MithrilDB_, _libmdbx_ will forever free and open source. Moreover with high-quality support whenever possible. Tu deviens responsible pour toujours de ce que tu as apprivois. So I will continue to comply with the original open license and the principles of constructive cooperation, in spite of outright Github sabotage and sanctions. I will also try to keep (not drop) Windows support, despite it is an unused obsolete technology for us.
Contrary to _MithrilDB_, _libmdbx_ will forever free and open source. Moreover with high-quality support whenever possible. Tu deviens responsible pour toujours de ce que tu as apprivois. So I will continue to comply with the original open license and the principles of constructive cooperation, in spite of outright GitHub sabotage and sanctions. I will also try to keep (not drop) Windows support, despite it is an unused obsolete technology for us.
<!-- section-end -->
@@ -266,8 +266,8 @@ Historically, _libmdbx_ is a deeply revised and extended descendant of the [Ligh
Since 2017 _libmdbx_ is used in [Fast Positive Tables](https://sourcecraft.dev/dqdkfa/libfpta), and until 2025 development was funded by [Positive Technologies](https://www.ptsecurity.com). Since 2020 _libmdbx_ is used in Ethereum: [Erigon](https://github.com/erigontech/erigon), [Akula](https://github.com/akula-bft/akula), [Silkworm](https://github.com/erigontech/silkworm), [Reth](https://github.com/paradigmxyz/reth), etc.
On 2022-04-15 the Github administration, without any warning nor explanation, deleted _libmdbx_ along with a lot of other projects, simultaneously blocking access for many developers. Therefore on 2022-04-21 I have migrated to a reliable trusted infrastructure.
The origin for now is at [SourceCraft](https://sourcecraft.dev/dqdkfa/libmdbx) and ~~Github~~ is blacklisted forever to play this role.
On 2022-04-15 the GitHub administration, without any warning nor explanation, deleted _libmdbx_ along with a lot of other projects, simultaneously blocking access for many developers. Therefore on 2022-04-21 I have migrated to a reliable trusted infrastructure.
The origin for now is at [SourceCraft](https://sourcecraft.dev/dqdkfa/libmdbx) and ~~GitHub~~ is blacklisted forever to play this role.
Since May 2024 and version 0.13 _libmdbx_ was re-licensed under Apache-2.0 license. Please refer to the [`COPYRIGHT` file](https://sourcecraft.dev/dqdkfa/libmdbx/blob/raw?file=COPYRIGHT) for license change explanation.
@@ -287,7 +287,7 @@ Usage
Since December 2025 _libmdbx_ is available only in an amalgamated source code form like [SQLite](https://www.sqlite.org/amalgamation.html), without additional dependencies and internal resources needed only for development of _libmdbx_ itself. Packages support for common Linux distributions is planned in the future, since release the version `1.0`.
The source code is available on [SourceCraft](https://sourcecraft.dev/dqdkfa/libmdbx) and mirror on [Github](https://github.com/Mithril-mine/libmdbx).
The source code is available on [SourceCraft](https://sourcecraft.dev/dqdkfa/libmdbx) and mirror on [GitHub](https://github.com/Mithril-mine/libmdbx).
Please use the `stable` branch or the latest release for production environment through staging and the `master` branch for development a derivative projects.
## Building and Testing
+1 -1
View File
@@ -1 +1 @@
{ "git_describe": "v0.14.2-274-g58ea7f56", "git_timestamp": "2026-07-09T20:41:59+03:00", "git_tree": "e3fc5c7ae25788a022756ea18b9bc9d5ac994568", "git_commit": "58ea7f567ce6a1373b1cac66944d119f91530012", "semver": "0.14.2.274" }
{ "git_describe": "v0.14.2-293-g43618122", "git_timestamp": "2026-07-13T02:40:29+03:00", "git_tree": "afa87f6901647422f0649a54e112ca17aeeb66df", "git_commit": "43618122476a638b9b8690bd095e8f98c31f967c", "semver": "0.14.2.293" }
+1 -1
View File
@@ -1,4 +1,4 @@
/* This file is part of the libmdbx amalgamated source code (v0.14.2-274-g58ea7f56 at 2026-07-09T20:41:59+03:00),
/* This file is part of the libmdbx amalgamated source code (v0.14.2-293-g43618122 at 2026-07-13T02:40:29+03:00),
* it is the template for libmdbx's config.h
******************************************************************************/
+21 -2
View File
@@ -1,4 +1,4 @@
/* This file is part of the libmdbx amalgamated source code (v0.14.2-274-g58ea7f56 at 2026-07-09T20:41:59+03:00).
/* This file is part of the libmdbx amalgamated source code (v0.14.2-293-g43618122 at 2026-07-13T02:40:29+03:00).
*
* libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable, transactional key-value storage engine with
* open-source code. MDBX has a specific set of properties and capabilities, focused on creating unique lightweight
@@ -24,7 +24,7 @@
#define xMDBX_ALLOY 1 /* alloyed build */
#define MDBX_BUILD_SOURCERY 80096459d7ca2ce7f10e9e82a01b71791d3e155dea887a83a0afe1e5babc1a98_v0_14_2_274_g58ea7f56
#define MDBX_BUILD_SOURCERY c66fe3bf2ba78557c9b02e51851ce8bbbccb58a96dc20c81a5bc591858a8a1f2_v0_14_2_293_g43618122
#define LIBMDBX_INTERNALS
#define MDBX_DEPRECATED
@@ -924,12 +924,17 @@ __extern_C key_t ftok(const char *, int);
#endif /* ENABLE_MEMCHECK */
#ifdef __SANITIZE_ADDRESS__
#define RUNNING_ON_ASAN (1)
#include <sanitizer/asan_interface.h>
#elif !defined(ASAN_POISON_MEMORY_REGION)
#define ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
#define ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
#endif /* __SANITIZE_ADDRESS__ */
#ifndef RUNNING_ON_ASAN
#define RUNNING_ON_ASAN (0)
#endif
/*----------------------------------------------------------------------------*/
/* DTrace dynamic tracing framework */
@@ -1593,6 +1598,7 @@ enum osal_syncmode_bits {
MDBX_INTERNAL int osal_fsync(mdbx_filehandle_t fd, const enum osal_syncmode_bits mode_bits);
MDBX_INTERNAL int osal_fsetsize(mdbx_filehandle_t fd, const uint64_t length);
MDBX_INTERNAL int osal_fseek(mdbx_filehandle_t fd, uint64_t pos);
MDBX_INTERNAL int osal_fseek_shut(mdbx_filehandle_t fd, uint64_t *safe_parking_lot_offset);
MDBX_INTERNAL int osal_filesize(mdbx_filehandle_t fd, uint64_t *length);
enum osal_openfile_purpose {
@@ -3066,6 +3072,7 @@ typedef struct shared_lck {
#define MDBX_GOLD_RATIO_DBL 1.6180339887498948482
#define MEGABYTE ((size_t)1 << 20)
#define GIGABYTE ((size_t)1 << 30)
/*----------------------------------------------------------------------------*/
@@ -3078,6 +3085,7 @@ union logger_union {
struct libmdbx_globals {
bin128_t bootid;
unsigned sys_pagesize, sys_allocation_granularity;
size_t assume_ram_pages, mmap_limit, reasonable_db_maxsize;
#ifdef AT_UCACHEBSIZE
unsigned sys_unified_cache_block;
#endif /* AT_UCACHEBSIZE */
@@ -3088,6 +3096,9 @@ struct libmdbx_globals {
bool running_under_Wine;
#elif defined(__linux__) || defined(__gnu_linux__)
bool running_on_WSL1 /* Windows Subsystem 1 for Linux */;
#ifdef ENABLE_MEMCHECK
uint8_t running_on_Valgrind;
#endif /* ENABLE_MEMCHECK */
uint32_t linux_kernel_version;
#endif /* Linux */
union logger_union logger;
@@ -3106,6 +3117,14 @@ extern struct libmdbx_globals globals;
extern struct libmdbx_imports imports;
#endif /* Windows */
static inline unsigned mdbx_running_on_Valgrind(void) {
#ifdef ENABLE_MEMCHECK
return globals.running_on_Valgrind;
#else
return 0;
#endif /* ENABLE_MEMCHECK */
}
#ifndef __Wpedantic_format_voidptr
MDBX_MAYBE_UNUSED static inline const void *__Wpedantic_format_voidptr(const void *ptr) { return ptr; }
#define __Wpedantic_format_voidptr(ARG) __Wpedantic_format_voidptr(ARG)
+197 -135
View File
@@ -1,4 +1,4 @@
/* This file is part of the libmdbx amalgamated source code (v0.14.2-274-g58ea7f56 at 2026-07-09T20:41:59+03:00).
/* This file is part of the libmdbx amalgamated source code (v0.14.2-293-g43618122 at 2026-07-13T02:40:29+03:00).
*
* libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable, transactional key-value storage engine with
* open-source code. MDBX has a specific set of properties and capabilities, focused on creating unique lightweight
@@ -1989,7 +1989,7 @@ MDBX_NOTHROW_CONST_FUNCTION static inline size_t valsize_max(size_t pagesize, MD
STATIC_ASSERT(PAGELIST_LIMIT <= MAX_PAGENO);
const size_t pages_limit = PAGELIST_LIMIT / 4;
const size_t limit = (hard_pages < pages_limit) ? hard : (pages_limit << page_ln2);
return (limit < MAX_MAPSIZE / 2) ? limit : MAX_MAPSIZE / 2;
return (limit < globals.mmap_limit / 2) ? limit : globals.mmap_limit / 2;
}
MDBX_NOTHROW_CONST_FUNCTION static inline size_t env_valsize_max(const MDBX_env *env, MDBX_db_flags_t flags) {
@@ -2004,7 +2004,7 @@ MDBX_NOTHROW_CONST_FUNCTION static inline size_t env_valsize_max(const MDBX_env
STATIC_ASSERT(PAGELIST_LIMIT <= MAX_PAGENO);
const size_t pages_limit = PAGELIST_LIMIT / 4;
const size_t limit = (hard_pages < pages_limit) ? hard : (pages_limit << env->ps2ln);
size_max = (limit < MAX_MAPSIZE / 2) ? limit : MAX_MAPSIZE / 2;
size_max = (limit < globals.mmap_limit / 2) ? limit : globals.mmap_limit / 2;
}
eASSERT0(env, size_max == valsize_max(env->ps, flags));
return size_max;
@@ -4208,9 +4208,8 @@ __cold intptr_t mdbx_limits_dbsize_max(intptr_t pagesize) {
!is_powerof2((size_t)pagesize)))
return -1;
STATIC_ASSERT(MAX_MAPSIZE < INTPTR_MAX);
const uint64_t limit = (1 + (uint64_t)MAX_PAGENO) * pagesize;
return (limit < MAX_MAPSIZE) ? (intptr_t)limit : (intptr_t)MAX_MAPSIZE;
return (limit < globals.mmap_limit) ? (intptr_t)limit : (intptr_t)globals.mmap_limit;
}
__cold intptr_t mdbx_limits_txnsize_max(intptr_t pagesize) {
@@ -4220,9 +4219,8 @@ __cold intptr_t mdbx_limits_txnsize_max(intptr_t pagesize) {
!is_powerof2((size_t)pagesize)))
return -1;
STATIC_ASSERT(MAX_MAPSIZE < INTPTR_MAX);
const uint64_t pgl_limit = pagesize * (uint64_t)(PAGELIST_LIMIT / MDBX_GOLD_RATIO_DBL);
const uint64_t map_limit = (uint64_t)(MAX_MAPSIZE / MDBX_GOLD_RATIO_DBL);
const uint64_t map_limit = (uint64_t)(globals.mmap_limit / MDBX_GOLD_RATIO_DBL);
return (pgl_limit < map_limit) ? (intptr_t)pgl_limit : (intptr_t)map_limit;
}
@@ -6997,41 +6995,6 @@ bailout:
return LOG_IFERR(rc);
}
__cold static intptr_t reasonable_db_maxsize(void) {
static intptr_t cached_result;
if (cached_result == 0) {
intptr_t pagesize, total_ram_pages;
if (unlikely(mdbx_get_sysraminfo(&pagesize, &total_ram_pages, nullptr) != MDBX_SUCCESS))
/* the 32-bit limit is good enough for fallback */
return cached_result = MAX_MAPSIZE32 / 4;
#if defined(__SANITIZE_ADDRESS__)
total_ram_pages >>= 4;
#endif /* __SANITIZE_ADDRESS__ */
if (RUNNING_ON_VALGRIND)
total_ram_pages >>= 4;
if (unlikely((size_t)total_ram_pages > MAX_MAPSIZE / (size_t)pagesize))
return cached_result = MAX_MAPSIZE / 2;
ASSERT(MAX_MAPSIZE >= (size_t)(total_ram_pages * pagesize * 2));
/* Suggesting should not be more than golden ratio of the size of RAM. */
cached_result = (intptr_t)((size_t)total_ram_pages * 207 >> 7) * pagesize;
/* Round to the nearest human-readable granulation. */
for (size_t unit = MEGABYTE; unit; unit <<= 5) {
const size_t floor = floor_powerof2(cached_result, unit);
const size_t ceil = ceil_powerof2(cached_result, unit);
const size_t threshold = (size_t)cached_result >> 4;
const bool down = cached_result - floor < ceil - cached_result || ceil > MAX_MAPSIZE / 4;
if (threshold < (down ? cached_result - floor : ceil - cached_result))
break;
cached_result = down ? floor : ceil;
}
}
return cached_result;
}
__cold static int check_alternative_lck_absent(const pathchar_t *lck_pathname) {
int err = osal_fileexists(lck_pathname);
if (unlikely(err != MDBX_RESULT_FALSE)) {
@@ -8009,7 +7972,7 @@ __cold int mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, intptr_t si
shrink_threshold = pgno2bytes(env, pv2pages(geo->shrink_pv));
const size_t usedbytes = pgno_ceil2os_bytes(env, mvcc_snapshot_largest(env, geo->first_unallocated));
if ((size_t)size_upper < usedbytes) {
if ((size_t)size_upper < usedbytes || usedbytes > globals.mmap_limit) {
rc = MDBX_MAP_FULL;
goto bailout;
}
@@ -8036,11 +7999,11 @@ __cold int mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, intptr_t si
if (size_upper > top)
top = size_upper;
if (top < 0 /* default */)
top = reasonable_db_maxsize();
top = globals.reasonable_db_maxsize;
else if (top == 0 /* minimal */)
top = MIN_MAPSIZE;
else if (top >= (intptr_t)MAX_MAPSIZE /* maximal */)
top = MAX_MAPSIZE;
else if (top >= (intptr_t)globals.mmap_limit /* maximal */)
top = globals.mmap_limit;
while (top > pagesize * (int64_t)(MAX_PAGENO + 1) && pagesize < MDBX_MAX_PAGESIZE)
pagesize <<= 1;
@@ -8059,13 +8022,13 @@ __cold int mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, intptr_t si
size_lower = MIN_PAGENO * pagesize;
}
if (size_lower >= INTPTR_MAX) {
size_lower = reasonable_db_maxsize();
size_lower = globals.reasonable_db_maxsize;
if ((size_t)size_lower / pagesize > MAX_PAGENO + 1)
size_lower = pagesize * (MAX_PAGENO + 1);
}
if (size_now >= INTPTR_MAX) {
size_now = reasonable_db_maxsize();
size_now = globals.reasonable_db_maxsize;
if ((size_t)size_now / pagesize > MAX_PAGENO + 1)
size_now = pagesize * (MAX_PAGENO + 1);
}
@@ -8073,22 +8036,25 @@ __cold int mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, intptr_t si
if (size_upper <= 0) {
if ((growth_step == 0 || size_upper == 0) && size_now >= size_lower)
size_upper = size_now;
else if (size_now <= 0 || size_now >= reasonable_db_maxsize() / 2)
size_upper = reasonable_db_maxsize();
else if (size_now <= 0 || size_now >= (intptr_t)globals.reasonable_db_maxsize / 2)
size_upper = globals.reasonable_db_maxsize;
else if ((size_t)size_now >= MAX_MAPSIZE32 / 2 && (size_t)size_now <= MAX_MAPSIZE32 / 4 * 3)
size_upper = MAX_MAPSIZE32;
else {
size_upper = ceil_powerof2(((size_t)size_now < MAX_MAPSIZE / 4) ? size_now + size_now : size_now + size_now / 2,
MEGABYTE * MDBX_WORDBITS * MDBX_WORDBITS / 32);
if ((size_t)size_upper > MAX_MAPSIZE)
size_upper = MAX_MAPSIZE;
size_upper =
ceil_powerof2(((size_t)size_now < globals.mmap_limit / 4) ? size_now + size_now : size_now + size_now / 2,
MEGABYTE * MDBX_WORDBITS * MDBX_WORDBITS / 32);
}
if ((size_t)size_upper / pagesize > (MAX_PAGENO + 1))
size_upper = pagesize * (MAX_PAGENO + 1);
if ((size_t)size_upper > globals.mmap_limit)
size_upper = globals.mmap_limit;
} else if (size_upper >= INTPTR_MAX) {
size_upper = reasonable_db_maxsize();
size_upper = globals.reasonable_db_maxsize;
if ((size_t)size_upper / pagesize > MAX_PAGENO + 1)
size_upper = pagesize * (MAX_PAGENO + 1);
if ((size_t)size_upper > globals.mmap_limit)
size_upper = globals.mmap_limit;
}
if (unlikely(size_lower < (intptr_t)MIN_MAPSIZE || size_lower > size_upper)) {
@@ -8114,7 +8080,7 @@ __cold int mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, intptr_t si
size_now = size_lower;
}
if (unlikely((size_t)size_upper > MAX_MAPSIZE || (uint64_t)size_upper / pagesize > MAX_PAGENO + 1)) {
if (unlikely((size_t)size_upper > globals.mmap_limit || (uint64_t)size_upper / pagesize > MAX_PAGENO + 1)) {
rc = MDBX_TOO_LARGE;
goto bailout;
}
@@ -8131,7 +8097,7 @@ __cold int mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, intptr_t si
/* LY: подбираем значение size_upper:
* - кратное размеру unit_ag (размеру страницы БД и системному размеру выделения)
* - без нарушения MAX_MAPSIZE и MAX_PAGENO */
while (unlikely((size_t)size_upper > MAX_MAPSIZE || (uint64_t)size_upper / pagesize > MAX_PAGENO + 1)) {
while (unlikely((size_t)size_upper > globals.mmap_limit || (uint64_t)size_upper / pagesize > MAX_PAGENO + 1)) {
if ((size_t)size_upper < unit_ag + MIN_MAPSIZE || (size_t)size_upper < (size_t)pagesize * (MIN_PAGENO + 1)) {
/* паранойа на случай переполнения при невероятных значениях */
rc = MDBX_EINVAL;
@@ -9356,7 +9322,7 @@ __cold int mdbx_is_readahead_reasonable(size_t volume, intptr_t redundancy) {
if (unlikely(err != MDBX_SUCCESS))
return LOG_IFERR(err);
const int log2page = log2n_powerof2(pagesize);
const int log2page = globals.sys_pagesize_ln2;
const intptr_t volume_pages = (volume + pagesize - 1) >> log2page;
const intptr_t redundancy_pages = (redundancy < 0) ? -(intptr_t)((-redundancy + pagesize - 1) >> log2page)
: (intptr_t)(redundancy + pagesize - 1) >> log2page;
@@ -9632,7 +9598,8 @@ const char *mdbx_strerror_ANSI2OEM(int errnum) {
#endif /* Bit of madness for Windows */
static pgno_t env_max_pgno(const MDBX_env *env) {
return env->ps ? bytes2pgno(env, env->geo_in_bytes.upper ? env->geo_in_bytes.upper : MAX_MAPSIZE) : PAGELIST_LIMIT;
return env->ps ? bytes2pgno(env, env->geo_in_bytes.upper ? env->geo_in_bytes.upper : globals.mmap_limit)
: PAGELIST_LIMIT;
}
__cold pgno_t default_dp_limit(const MDBX_env *env) {
@@ -20187,10 +20154,8 @@ bailout:
}
#if defined(ENABLE_MEMCHECK) || defined(__SANITIZE_ADDRESS__)
void dxb_sanitize_tail(MDBX_env *env, MDBX_txn *txn) {
#if !defined(__SANITIZE_ADDRESS__)
if (!RUNNING_ON_VALGRIND)
if (!RUNNING_ON_ASAN && !mdbx_running_on_Valgrind())
return;
#endif
if (txn) { /* transaction start */
if (env->poison_edge < txn->geo.first_unallocated)
env->poison_edge = txn->geo.first_unallocated;
@@ -20346,7 +20311,7 @@ __cold int dxb_setup(MDBX_env *env, const int lck_rc, const mdbx_mode_t mode_bit
if (unlikely(err != MDBX_SUCCESS)) {
if (lck_rc != /* lck exclusive */ MDBX_RESULT_TRUE || err != MDBX_ENODATA || (env->flags & MDBX_RDONLY) != 0 ||
/* recovery mode */ env->stuck_meta >= 0)
return err;
return LOG_IFERR(err);
DEBUG("%s", "create new database");
rc = /* new database */ MDBX_RESULT_TRUE;
@@ -20355,26 +20320,26 @@ __cold int dxb_setup(MDBX_env *env, const int lck_rc, const mdbx_mode_t mode_bit
/* set defaults if not configured */
err = mdbx_env_set_geometry(env, 0, -1, -1, -1, -1, -1);
if (unlikely(err != MDBX_SUCCESS))
return err;
return LOG_IFERR(err);
}
err = env_page_auxbuffer(env);
if (unlikely(err != MDBX_SUCCESS))
return err;
return LOG_IFERR(err);
header = *meta_init_triplet(env, env->page_auxbuf);
err = osal_pwrite(env->lazy_fd, env->page_auxbuf, env->ps * (size_t)NUM_METAS, 0);
if (unlikely(err != MDBX_SUCCESS))
return err;
return LOG_IFERR(err);
err = osal_fsetsize(env->lazy_fd, env->dxb_mmap.filesize = env->dxb_mmap.current = env->geo_in_bytes.now);
if (unlikely(err != MDBX_SUCCESS))
return err;
return LOG_IFERR(err);
#if MDBX_CHECKING > 0 || MDBX_DEBUG > 0
err = dxb_read_header(env, &header, lck_rc, mode_bits);
if (unlikely(err != MDBX_SUCCESS))
return err;
return LOG_IFERR(err);
#endif /* MDBX_CHECKING > 0 || MDBX_DEBUG > 0 */
}
@@ -20402,7 +20367,7 @@ __cold int dxb_setup(MDBX_env *env, const int lck_rc, const mdbx_mode_t mode_bit
if ((env->flags & MDBX_RDONLY) == 0) {
err = env_page_auxbuffer(env);
if (unlikely(err != MDBX_SUCCESS))
return err;
return LOG_IFERR(err);
}
size_t expected_filesize = 0;
@@ -20519,13 +20484,13 @@ __cold int dxb_setup(MDBX_env *env, const int lck_rc, const mdbx_mode_t mode_bit
err = osal_mmap(env->flags, &env->dxb_mmap, env->geo_in_bytes.now, env->geo_in_bytes.upper,
(lck_rc && env->stuck_meta < 0) ? MMAP_OPTION_SETLENGTH : 0, env->pathname.dxb);
if (unlikely(err != MDBX_SUCCESS))
return err;
return LOG_IFERR(err);
#if defined(MADV_DONTDUMP)
err =
madvise(env->dxb_mmap.base, env->dxb_mmap.limit, MADV_DONTDUMP) ? ignore_enosys_and_eagain(errno) : MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err)))
return err;
return LOG_IFERR(err);
#endif /* MADV_DONTDUMP */
#if defined(MADV_DODUMP)
if (globals.runtime_flags & MDBX_DBG_DUMP) {
@@ -20533,7 +20498,7 @@ __cold int dxb_setup(MDBX_env *env, const int lck_rc, const mdbx_mode_t mode_bit
err = madvise(env->dxb_mmap.base, meta_length_aligned2os, MADV_DODUMP) ? ignore_enosys_and_eagain(errno)
: MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err)))
return err;
return LOG_IFERR(err);
}
#endif /* MADV_DODUMP */
@@ -20780,7 +20745,7 @@ __cold int dxb_setup(MDBX_env *env, const int lck_rc, const mdbx_mode_t mode_bit
? ignore_enosys_and_eagain(errno)
: MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err)))
return err;
return LOG_IFERR(err);
}
#endif /* MADV_REMOVE */
#if defined(MADV_DONTNEED)
@@ -20790,23 +20755,23 @@ __cold int dxb_setup(MDBX_env *env, const int lck_rc, const mdbx_mode_t mode_bit
? ignore_enosys_and_eagain(errno)
: MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err)))
return err;
return LOG_IFERR(err);
#elif defined(POSIX_MADV_DONTNEED)
err = ignore_enosys(posix_madvise(ptr_disp(env->dxb_mmap.base, allocated_aligned2os_bytes),
env->dxb_mmap.current - allocated_aligned2os_bytes, POSIX_MADV_DONTNEED));
if (unlikely(MDBX_IS_ERROR(err)))
return err;
return LOG_IFERR(err);
#elif defined(POSIX_FADV_DONTNEED)
err = ignore_enosys(posix_fadvise(env->lazy_fd, allocated_aligned2os_bytes,
env->dxb_mmap.current - allocated_aligned2os_bytes, POSIX_FADV_DONTNEED));
if (unlikely(MDBX_IS_ERROR(err)))
return err;
return LOG_IFERR(err);
#endif /* MADV_DONTNEED */
}
err = dxb_set_readahead(env, bytes2pgno(env, allocated_bytes), readahead, true);
if (unlikely(err != MDBX_SUCCESS))
return err;
return LOG_IFERR(err);
return rc;
}
@@ -21144,7 +21109,7 @@ int dxb_sync_locked(MDBX_env *env, unsigned flags, meta_t *const pending, troika
VERBOSE("shrink to %" PRIaPGNO " pages (-%" PRIaPGNO ")", pending->geometry.now, shrink);
rc = dxb_resize(env, pending->geometry.first_unallocated, pending->geometry.now, pending->geometry.upper,
implicit_shrink);
if (rc != MDBX_SUCCESS && rc != MDBX_EPERM)
if (rc != MDBX_SUCCESS && rc != MDBX_EPERM && rc != MDBX_UNABLE_EXTEND_MAPSIZE)
goto fail;
eASSERT1(env, coherency_check_meta(env, target, true));
}
@@ -21445,18 +21410,22 @@ __cold int env_open(MDBX_env *env, mdbx_mode_t mode) {
int rc = osal_openfile((env->flags & MDBX_RDONLY) ? MDBX_OPEN_DXB_READ : MDBX_OPEN_DXB_LAZY, env, env->pathname.dxb,
&env->lazy_fd, mode);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
return LOG_IFERR(rc);
#if MDBX_LOCKING == MDBX_LOCKING_SYSV
env->me_sysv_ipc.key = ftok(env->pathname.dxb, 42);
if (unlikely(env->me_sysv_ipc.key == -1))
return errno;
if (unlikely(env->me_sysv_ipc.key == -1)) {
rc = errno;
return LOG_IFERR(rc);
}
#endif /* MDBX_LOCKING */
/* Set the position in files outside of the data to avoid corruption
* due to erroneous use of file descriptors in the application code. */
const uint64_t safe_parking_lot_offset = UINT64_C(0x7fffFFFF80000000);
osal_fseek(env->lazy_fd, safe_parking_lot_offset);
uint64_t safe_parking_lot_offset = UINT64_C(0x7fffFFFF80000000);
rc = osal_fseek_shut(env->lazy_fd, &safe_parking_lot_offset);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
env->fd4meta = env->lazy_fd;
#if IS_WINDOWS
@@ -21506,15 +21475,19 @@ __cold int env_open(MDBX_env *env, mdbx_mode_t mode) {
rc = osal_openfile(ior_direct ? MDBX_OPEN_DXB_OVERLAPPED_DIRECT : MDBX_OPEN_DXB_OVERLAPPED, env, env->pathname.dxb,
&env->ioring.overlapped_fd, 0);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
osal_fseek(env->ioring.overlapped_fd, safe_parking_lot_offset);
return LOG_IFERR(rc);
rc = osal_fseek(env->ioring.overlapped_fd, safe_parking_lot_offset);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
}
#else
if (mode == 0) {
/* pickup mode for lck-file */
struct stat st;
if (unlikely(fstat(env->lazy_fd, &st)))
return errno;
if (unlikely(fstat(env->lazy_fd, &st))) {
rc = errno;
return LOG_IFERR(rc);
}
mode = st.st_mode;
}
mode = (/* inherit read permissions for group and others */ mode & (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) |
@@ -21524,9 +21497,12 @@ __cold int env_open(MDBX_env *env, mdbx_mode_t mode) {
#endif /* !Windows */
const int lck_rc = lck_setup(env, mode);
if (unlikely(MDBX_IS_ERROR(lck_rc)))
return lck_rc;
if (env->lck_mmap.fd != INVALID_HANDLE_VALUE)
osal_fseek(env->lck_mmap.fd, safe_parking_lot_offset);
return LOG_IFERR(lck_rc);
if (env->lck_mmap.fd != INVALID_HANDLE_VALUE) {
rc = osal_fseek(env->lck_mmap.fd, safe_parking_lot_offset);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
}
eASSERT0(env, env->dsync_fd == INVALID_HANDLE_VALUE);
if (!(env->flags & (MDBX_RDONLY | MDBX_SAFE_NOSYNC | DEPRECATED_MAPASYNC
@@ -21536,11 +21512,13 @@ __cold int env_open(MDBX_env *env, mdbx_mode_t mode) {
))) {
rc = osal_openfile(MDBX_OPEN_DXB_DSYNC, env, env->pathname.dxb, &env->dsync_fd, 0);
if (unlikely(MDBX_IS_ERROR(rc)))
return rc;
return LOG_IFERR(rc);
if (env->dsync_fd != INVALID_HANDLE_VALUE) {
if ((env->flags & MDBX_NOMETASYNC) == 0)
env->fd4meta = env->dsync_fd;
osal_fseek(env->dsync_fd, safe_parking_lot_offset);
rc = osal_fseek(env->dsync_fd, safe_parking_lot_offset);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
}
}
@@ -21610,7 +21588,7 @@ __cold int env_open(MDBX_env *env, mdbx_mode_t mode) {
env_clear_incore_cache(env);
const int dxb_rc = dxb_setup(env, lck_rc, mode);
if (MDBX_IS_ERROR(dxb_rc))
return dxb_rc;
return LOG_IFERR(dxb_rc);
rc = osal_check_fs_incore(env->lazy_fd);
env->incore = false;
@@ -21620,7 +21598,7 @@ __cold int env_open(MDBX_env *env, mdbx_mode_t mode) {
rc = MDBX_SUCCESS;
} else if (unlikely(rc != MDBX_SUCCESS)) {
ERROR("check_fs_incore(), err %d", rc);
return rc;
return LOG_IFERR(rc);
}
if (unlikely(/* recovery mode */ env->stuck_meta >= 0) &&
@@ -21641,11 +21619,11 @@ __cold int env_open(MDBX_env *env, mdbx_mode_t mode) {
rc = lck_downgrade(env);
DEBUG("lck-downgrade-%s: rc %i", (env->flags & MDBX_EXCLUSIVE) ? "partial" : "full", rc);
if (rc != MDBX_SUCCESS)
return rc;
return LOG_IFERR(rc);
} else {
rc = mvcc_cleanup_dead(env, false, nullptr);
if (MDBX_IS_ERROR(rc))
return rc;
return LOG_IFERR(rc);
}
}
@@ -21656,7 +21634,7 @@ __cold int env_open(MDBX_env *env, mdbx_mode_t mode) {
ior_direct, env->ioring.overlapped_fd
#endif /* Windows */
);
return rc;
return LOG_IFERR(rc);
}
__cold int env_close(MDBX_env *env, bool resurrect_after_fork) {
@@ -24973,7 +24951,68 @@ static bool getenv_bool(const char *name, bool default_value) {
return default_value;
}
__cold static size_t assume_ram_pages(void) {
intptr_t total_ram_pages, avail_ram_pages;
int err = mdbx_get_sysraminfo(nullptr, &total_ram_pages, &avail_ram_pages);
if (unlikely(err != MDBX_SUCCESS)) {
/* the 32-bit limit is good enough for fallback */
total_ram_pages = MAX_MAPSIZE32 / 3 >> globals.sys_pagesize_ln2;
avail_ram_pages = total_ram_pages / 2;
}
size_t result = (size_t)(total_ram_pages + avail_ram_pages) / 2;
if (RUNNING_ON_ASAN)
result = avail_ram_pages >> 1;
if (mdbx_running_on_Valgrind())
result = avail_ram_pages >> 4;
return result;
}
__cold static size_t mmap_limit(void) {
STATIC_ASSERT(MAX_MAPSIZE < INTPTR_MAX);
size_t limit = MAX_MAPSIZE;
const uint64_t asan_limit = UINT64_C(16384) * GIGABYTE;
if (RUNNING_ON_ASAN && limit > asan_limit)
limit = asan_limit;
const size_t valgrind_limit = (MDBX_WORDBITS < 64) ? 512 * MEGABYTE : (size_t)(32 * GIGABYTE);
if (mdbx_running_on_Valgrind() && limit > valgrind_limit)
limit = valgrind_limit;
if (RUNNING_ON_ASAN || mdbx_running_on_Valgrind()) {
ASSERT(globals.assume_ram_pages > 0);
limit = min_unsigned(limit, globals.assume_ram_pages << globals.sys_pagesize_ln2);
}
return limit;
}
__cold static size_t reasonable_db_maxsize(void) {
ASSERT(globals.assume_ram_pages > 0 && globals.sys_pagesize_ln2 && globals.mmap_limit);
/* Suggesting should not be more than golden ratio of the size of RAM. */
size_t result = (globals.assume_ram_pages * 207 >> 7) << globals.sys_pagesize_ln2;
if (result > globals.mmap_limit / 2)
return globals.mmap_limit / 2;
/* Round to the nearest human-readable granulation. */
for (size_t unit = MEGABYTE; unit; unit <<= 5) {
const size_t floor = floor_powerof2(result, unit);
const size_t ceil = ceil_powerof2(result, unit);
const size_t threshold = (size_t)result >> 4;
const bool down = result - floor < ceil - result || ceil > globals.mmap_limit / 4;
if (threshold < (down ? result - floor : ceil - result))
break;
result = down ? floor : ceil;
}
ASSERT(result <= globals.mmap_limit);
return result;
}
__cold static void mdbx_init(void) {
#ifdef ENABLE_MEMCHECK
globals.running_on_Valgrind = RUNNING_ON_VALGRIND;
#endif /* ENABLE_MEMCHECK */
globals.runtime_flags = (getenv_bool("MDBX_DBG_ASSERT", (MDBX_CHECKING) > 1) ? MDBX_DBG_ASSERT : 0) |
(getenv_bool("MDBX_DBG_AUDIT", (MDBX_CHECKING) > 2) ? MDBX_DBG_AUDIT : 0) |
(getenv_bool("MDBX_DBG_JITTER", false) ? MDBX_DBG_JITTER : 0) |
@@ -24990,6 +25029,10 @@ __cold static void mdbx_init(void) {
ENSURE(troika_verify_fsm());
ENSURE(pv2pages_verify());
#endif /* MDBX_CHECKING > 0 */
globals.assume_ram_pages = assume_ram_pages();
globals.mmap_limit = mmap_limit();
globals.reasonable_db_maxsize = reasonable_db_maxsize();
}
MDBX_EXCLUDE_FOR_GPROF
@@ -28008,7 +28051,7 @@ __cold int meta_validate(MDBX_env *env, meta_t *const meta, const page_t *const
return MDBX_CORRUPTED;
}
}
if (unlikely(meta->geometry.first_unallocated - 1 > MAX_PAGENO || allocated_bytes > MAX_MAPSIZE)) {
if (unlikely(meta->geometry.first_unallocated - 1 > MAX_PAGENO || allocated_bytes > globals.mmap_limit)) {
WARNING("meta[%u] has too large allocated-space (%" PRIu64 "), skip it", meta_number, allocated_bytes);
return MDBX_TOO_LARGE;
}
@@ -30565,6 +30608,31 @@ int osal_fseek(mdbx_filehandle_t fd, uint64_t pos) {
#endif
}
int osal_fseek_shut(mdbx_filehandle_t fd, uint64_t *safe_parking_lot_offset) {
/* Set the position in files outside of the data to avoid corruption
* due to erroneous use of file descriptors in the application code. */
int rc = osal_fseek(fd, *safe_parking_lot_offset);
if (unlikely(rc == MDBX_EINVAL)) {
uint64_t offset = *safe_parking_lot_offset;
unsigned shift_min = 1, shift_max = 32;
while (shift_min <= shift_max) {
unsigned shift = (shift_min + shift_max) >> 1;
*safe_parking_lot_offset = offset >> shift;
int err = osal_fseek(fd, *safe_parking_lot_offset);
if (err == MDBX_SUCCESS) {
shift_max = shift - 1;
rc = err;
} else if (err == MDBX_EINVAL)
shift_min = shift + 1;
else {
rc = err;
break;
}
}
}
return rc;
}
/*----------------------------------------------------------------------------*/
int osal_thread_create(osal_thread_t *thread, THREAD_RESULT(THREAD_CALL *start_routine)(void *), void *arg) {
@@ -30938,28 +31006,11 @@ int osal_check_fs_local(mdbx_filehandle_t handle, int flags) {
}
static int check_mmap_limit(const size_t limit) {
const bool should_check =
#if defined(__SANITIZE_ADDRESS__)
true;
#else
RUNNING_ON_VALGRIND;
#endif /* __SANITIZE_ADDRESS__ */
if (should_check) {
intptr_t pagesize, total_ram_pages, avail_ram_pages;
int err = mdbx_get_sysraminfo(&pagesize, &total_ram_pages, &avail_ram_pages);
if (unlikely(err != MDBX_SUCCESS))
return err;
const int log2page = log2n_powerof2(pagesize);
if ((limit >> (log2page + 7)) > (size_t)total_ram_pages || (limit >> (log2page + 6)) > (size_t)avail_ram_pages) {
ERROR("%s (%zu pages) is too large for available (%zu pages) or total "
"(%zu pages) system RAM",
"database upper size limit", limit >> log2page, avail_ram_pages, total_ram_pages);
return MDBX_TOO_LARGE;
}
if ((RUNNING_ON_ASAN || mdbx_running_on_Valgrind()) && limit > globals.mmap_limit) {
ERROR("%s (%zu bytes) is too large for %s (%zu)", "database upper size limit", limit,
RUNNING_ON_ASAN ? "AddressSanitizer" : "Valgrind", globals.mmap_limit);
return MDBX_TOO_LARGE;
}
return MDBX_SUCCESS;
}
@@ -30993,7 +31044,7 @@ int osal_mmap(const int flags, osal_mmap_t *map, size_t size, const size_t limit
ERROR("%" MDBX_PRIsPATH " is on a remote file system, the %s is required", pathname4logging, "MDBX_EXCLUSIVE");
__fallthrough /* fall through */;
default:
return err;
return LOG_IFERR(err);
}
}
@@ -31005,7 +31056,7 @@ int osal_mmap(const int flags, osal_mmap_t *map, size_t size, const size_t limit
err = osal_fsetsize(map->fd, size);
VERBOSE("osal_fsetsize %zu, err %d", size, err);
if (err != MDBX_SUCCESS)
return err;
return LOG_IFERR(err);
map->filesize = size;
#if !IS_WINDOWS
map->current = size;
@@ -31014,7 +31065,7 @@ int osal_mmap(const int flags, osal_mmap_t *map, size_t size, const size_t limit
err = osal_filesize(map->fd, &map->filesize);
VERBOSE("filesize %" PRIu64 ", err %d", map->filesize, err);
if (err != MDBX_SUCCESS)
return err;
return LOG_IFERR(err);
#if IS_WINDOWS
if (map->filesize < size) {
WARNING("file size (%zu) less than requested for mapping (%zu)", (size_t)map->filesize, size);
@@ -31087,23 +31138,36 @@ int osal_mmap(const int flags, osal_mmap_t *map, size_t size, const size_t limit
#define MAP_NORESERVE 0
#endif
map->base = mmap(nullptr, limit, (flags & MDBX_WRITEMAP) ? PROT_READ | PROT_WRITE : PROT_READ,
map->limit = limit;
retry:
map->base = mmap(nullptr, map->limit, (flags & MDBX_WRITEMAP) ? PROT_READ | PROT_WRITE : PROT_READ,
MAP_SHARED | MAP_FILE | MAP_NORESERVE | (F_ISSET(flags, MDBX_UTTERLY_NOSYNC) ? MAP_NOSYNC : 0) |
((options & MMAP_OPTION_SEMAPHORE) ? MAP_HASSEMAPHORE | MAP_NOSYNC : MAP_CONCEAL),
map->fd, 0);
if (unlikely(map->base == MAP_FAILED)) {
err = errno;
ASSERT(err != 0);
if ((RUNNING_ON_ASAN || mdbx_running_on_Valgrind()) && err == EINVAL && map->limit - map->current > MEGABYTE) {
map->limit -= (map->limit - map->current) / 2;
map->limit = max_unsigned(size, floor_powerof2(map->limit, MEGABYTE));
goto retry;
}
map->limit = 0;
map->current = 0;
map->base = nullptr;
ASSERT(errno != 0);
return errno;
return LOG_IFERR(err);
}
map->limit = limit;
if (unlikely(map->limit < limit) && (RUNNING_ON_ASAN || mdbx_running_on_Valgrind()))
NOTICE("Mapping size has been reduced from %zu to %zu for compatibility with %s", limit, map->limit,
RUNNING_ON_VALGRIND ? "Valgrind" : "AddressSanitizer");
#ifdef MADV_DONTFORK
if (unlikely(madvise(map->base, map->limit, MADV_DONTFORK) != 0))
return errno;
if (unlikely(madvise(map->base, map->limit, MADV_DONTFORK) != 0)) {
err = errno;
ASSERT(err != 0);
return LOG_IFERR(err);
}
#endif /* MADV_DONTFORK */
#ifdef MADV_NOHUGEPAGE
(void)madvise(map->base, map->limit, MADV_NOHUGEPAGE);
@@ -31467,9 +31531,7 @@ retry_mapview:;
VALGRIND_MAKE_MEM_NOACCESS(map->base, map->current);
/* Unpoisoning is required for ASAN to avoid false-positive diagnostic
* when this memory will re-used by malloc or another mmapping.
* See
* https://libmdbx.dqdkfa.ru/dead-github/pull/93#issuecomment-613687203
*/
* See https://libmdbx.dqdkfa.ru/dead-github/pull/93#issuecomment-613687203 */
MDBX_ASAN_UNPOISON_MEMORY_REGION(map->base, (map->current < map->limit) ? map->current : map->limit);
map->limit = 0;
map->current = 0;
@@ -32180,7 +32242,7 @@ __cold int mdbx_get_sysraminfo(intptr_t *page_size, intptr_t *total_pages, intpt
if (unlikely(pagesize < MDBX_MIN_PAGESIZE || !is_powerof2(pagesize)))
return LOG_IFERR(MDBX_INCOMPATIBLE);
const int log2page = log2n_powerof2(pagesize);
const int log2page = globals.sys_pagesize_ln2;
ASSERT(pagesize == (INT64_C(1) << log2page));
(void)log2page;
@@ -42047,10 +42109,10 @@ __dll_export
0,
14,
2,
274,
293,
"", /* pre-release suffix of SemVer
0.14.2.274 */
{"2026-07-09T20:41:59+03:00", "e3fc5c7ae25788a022756ea18b9bc9d5ac994568", "58ea7f567ce6a1373b1cac66944d119f91530012", "v0.14.2-274-g58ea7f56"},
0.14.2.293 */
{"2026-07-13T02:40:29+03:00", "afa87f6901647422f0649a54e112ca17aeeb66df", "43618122476a638b9b8690bd095e8f98c31f967c", "v0.14.2-293-g43618122"},
sourcery};
__dll_export
+1 -1
View File
@@ -1,4 +1,4 @@
/* This file is part of the libmdbx amalgamated source code (v0.14.2-274-g58ea7f56 at 2026-07-09T20:41:59+03:00).
/* This file is part of the libmdbx amalgamated source code (v0.14.2-293-g43618122 at 2026-07-13T02:40:29+03:00).
*
* libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable, transactional key-value storage engine with
* open-source code. MDBX has a specific set of properties and capabilities, focused on creating unique lightweight
+11 -13
View File
@@ -1,4 +1,4 @@
/** This file is part of the libmdbx amalgamated source code (v0.14.2-274-g58ea7f56 at 2026-07-09T20:41:59+03:00).
/** This file is part of the libmdbx amalgamated source code (v0.14.2-293-g43618122 at 2026-07-13T02:40:29+03:00).
\file mdbx.h
\brief The libmdbx C API header file.
@@ -882,6 +882,9 @@ enum MDBX_constants {
*
* \see mdbx_setup_debug() \see MDBX_log_level_t */
typedef enum MDBX_log_level {
/** for \ref mdbx_setup_debug() only: Don't change current settings */
MDBX_LOG_DONTCHANGE = -1,
/** Critical conditions, i.e. assertion failures.
* \note libmdbx always produces such messages regardless
* of \ref MDBX_DEBUG build option. */
@@ -925,12 +928,8 @@ typedef enum MDBX_log_level {
* \note Requires build libmdbx with \ref MDBX_DEBUG option. */
MDBX_LOG_EXTRA = 7,
#ifdef ENABLE_UBSAN
MDBX_LOG_MAX = 7 /* avoid UBSAN false-positive trap by a tests */,
#endif /* ENABLE_UBSAN */
/** for \ref mdbx_setup_debug() only: Don't change current settings */
MDBX_LOG_DONTCHANGE = -1
/** Avoids UBSAN false-positive issues/traps. */
MDBX_LOG_MAX = 7
} MDBX_log_level_t;
/** \brief Runtime debug flags
@@ -941,6 +940,9 @@ typedef enum MDBX_log_level {
*
* \see mdbx_setup_debug() \see MDBX_debug_flags_t */
typedef enum MDBX_debug_flags {
/** for mdbx_setup_debug() only: Don't change current settings */
MDBX_DBG_DONTCHANGE = -1,
MDBX_DBG_NONE = 0,
/** Enables costly check of debug-like assertions.
@@ -970,12 +972,8 @@ typedef enum MDBX_debug_flags {
* \note Nonetheless a new write transactions will use and store the last signature regardless this flag */
MDBX_DBG_DONT_UPGRADE = 64,
#ifdef ENABLE_UBSAN
MDBX_DBG_MAX = ((unsigned)MDBX_LOG_MAX) << 16 | 127 /* avoid UBSAN false-positive trap by a tests */,
#endif /* ENABLE_UBSAN */
/** for mdbx_setup_debug() only: Don't change current settings */
MDBX_DBG_DONTCHANGE = -1
/** Avoids UBSAN false-positive issues/traps. */
MDBX_DBG_MAX = ((unsigned)MDBX_LOG_MAX) << 16 | 127
} MDBX_debug_flags_t;
DEFINE_ENUM_FLAG_OPERATORS(MDBX_debug_flags)
+107 -89
View File
@@ -1,4 +1,4 @@
/// This file is part of the libmdbx amalgamated source code (v0.14.2-274-g58ea7f56 at 2026-07-09T20:41:59+03:00).
/// This file is part of the libmdbx amalgamated source code (v0.14.2-294-g9e57e21f at 2026-07-13T02:32:30+03:00).
/// \file mdbx.h++
/// \brief The libmdbx C++ API header file.
///
@@ -847,6 +847,12 @@ struct LIBMDBX_API_TYPE slice : public ::MDBX_val {
}
#endif /* __cpp_lib_string_view >= 201606L */
MDBX_CXX20_CONSTEXPR void swap(slice &other) noexcept {
auto temp = other;
other = *this;
*this = temp;
}
/// \brief Returns casted to pointer to byte an address of data.
MDBX_CXX11_CONSTEXPR const byte *byte_ptr() const noexcept;
MDBX_CXX11_CONSTEXPR byte *byte_ptr() noexcept;
@@ -1411,15 +1417,16 @@ struct move_assign_alloc;
template <typename T, typename A> struct move_assign_alloc<T, A, false> {
static constexpr bool is_nothrow() noexcept { return allocator_is_always_equal<A>(); }
static MDBX_CXX20_CONSTEXPR bool is_moveable(T *target, T &source) noexcept {
return allocator_is_always_equal<A>() || target->get_allocator() == source.get_allocator();
if MDBX_IF_CONSTEXPR (allocator_is_always_equal<A>())
return true;
else
return target->get_allocator() == source.get_allocator();
}
static MDBX_CXX20_CONSTEXPR void propagate(T *, T &) noexcept {}
};
template <typename T, typename A> struct move_assign_alloc<T, A, true> {
static constexpr bool is_nothrow() noexcept {
return allocator_is_always_equal<A>() || ::std::is_nothrow_move_assignable<A>::value;
}
static constexpr bool is_nothrow() noexcept { return ::std::is_nothrow_move_assignable<A>::value; }
static constexpr bool is_moveable(T *, T &) noexcept { return true; }
static MDBX_CXX20_CONSTEXPR void propagate(T *target, T &source) noexcept {
target->get_allocator() = ::std::move(source.get_allocator());
@@ -1479,10 +1486,9 @@ template <typename T, typename A> struct swap_alloc<T, A, true> {
(::std::is_nothrow_move_constructible<A>::value && ::std::is_nothrow_move_assignable<A>::value);
}
static MDBX_CXX20_CONSTEXPR void propagate(T &left, T &right) noexcept(is_nothrow()) {
if MDBX_IF_CONSTEXPR (!allocator_is_always_equal<A>()) {
if (MDBX_UNLIKELY(left.get_allocator() != right.get_allocator()))
MDBX_CXX20_UNLIKELY ::std::swap(left.get_allocator(), right.get_allocator());
} else {
if MDBX_IF_CONSTEXPR (!allocator_is_always_equal<A>())
MDBX_CXX20_UNLIKELY ::std::swap(left.get_allocator(), right.get_allocator());
else {
/* gag for buggy compilers */
(void)left;
(void)right;
@@ -1555,6 +1561,9 @@ public:
pettiness_threshold = reservation_policy::pettiness_threshold
};
/// \brief Data storage modality.
enum class modality { reference, inplace, allocated };
private:
friend class txn;
struct silo /* Empty Base Class Optimization */ : public allocator_type {
@@ -1647,14 +1656,14 @@ private:
return capacity_bytes <= inplace_capacity();
}
constexpr bool is_inplace() const noexcept {
MDBX_NOTHROW_PURE_FUNCTION constexpr bool is_inplace() const noexcept {
static_assert(size_t(inplace_signature_limit) > size_t(max_capacity), "WTF?");
static_assert(std::numeric_limits<size_t>::max() - (std::numeric_limits<size_t>::max() >> CHAR_BIT) ==
inplace_signature_limit,
"WTF?");
return inplace_.lastbyte_ == lastbyte_inplace_signature;
}
constexpr bool is_allocated() const noexcept { return !is_inplace(); }
MDBX_NOTHROW_PURE_FUNCTION constexpr bool is_allocated() const noexcept { return !is_inplace(); }
template <bool destroy_ptr> MDBX_CXX17_CONSTEXPR byte *make_inplace() noexcept {
if (destroy_ptr) {
@@ -1831,46 +1840,44 @@ private:
}
}
MDBX_CXX17_CONSTEXPR bool move(silo &&other) noexcept {
if (other.bin_.is_inplace()) {
MDBX_CXX17_CONSTEXPR bool move_content(silo &other, const modality other_modality) noexcept {
switch (other_modality) {
case modality::inplace:
memcpy(&bin_, &other.bin_, sizeof(bin));
MDBX_CONSTEXPR_ASSERT(bin_.is_inplace());
return /* buffer's slice fixup is needed */ true;
case modality::allocated:
new (&bin_.allocated_ptr_) allocator_pointer(::std::move(other.bin_.allocated_ptr_));
bin_.capacity_.bytes_ = other.bin_.capacity_.bytes_;
/* properly destroy allocator::pointer.
*
* CoverityScan issues an erroneous warning here about using an uninitialized object. Which is not true,
* since in C++ (unlike Rust) an object remains initialized after a move-assignment operation; Moreover,
* a destructor will be called for such an object (this is explicitly stated in all C++ standards, starting
* from the 11th). */
/* coverity[use_after_move] */
other.bin_.allocated_ptr_.~allocator_pointer();
other.bin_.inplace_.lastbyte_ = bin::lastbyte_inplace_signature;
MDBX_CONSTEXPR_ASSERT(bin_.is_allocated() && other.bin_.is_inplace());
return /* buffer's slice fixup is not needed */ false;
default:
MDBX_CONSTEXPR_ASSERT(other_modality == modality::reference);
return /* buffer's slice fixup is not needed */ false;
}
new (&bin_.allocated_ptr_) allocator_pointer(::std::move(other.bin_.allocated_ptr_));
bin_.capacity_.bytes_ = other.bin_.capacity_.bytes_;
/* properly destroy allocator::pointer.
*
* CoverityScan issues an erroneous warning here about using an uninitialized object. Which is not true,
* since in C++ (unlike Rust) an object remains initialized after a move-assignment operation; Moreover,
* a destructor will be called for such an object (this is explicitly stated in all C++ standards, starting
* from the 11th). */
/* coverity[use_after_move] */
other.bin_.allocated_ptr_.~allocator_pointer();
other.bin_.inplace_.lastbyte_ = bin::lastbyte_inplace_signature;
MDBX_CONSTEXPR_ASSERT(bin_.is_allocated() && other.bin_.is_inplace());
return false;
}
MDBX_CXX17_CONSTEXPR bool assign_move(silo &&other, bool is_reference) noexcept {
release();
if (allocation_aware_details::move_assign_alloc<silo, allocator_type>::is_moveable(this, other))
allocation_aware_details::move_assign_alloc<silo, allocator_type>::propagate(this, other);
return is_reference ? false : move(std::move(other));
}
static MDBX_CXX20_CONSTEXPR std::pair<bool, bool>
exchange(silo &left, const bool left_is_reference, silo &right, const bool right_is_reference) noexcept(
exchange(silo &left, const modality left_modality, silo &right, const modality right_modality) noexcept(
allocation_aware_details::swap_alloc<silo, allocator_type>::is_nothrow()) {
allocation_aware_details::swap_alloc<silo, allocator_type>::propagate(left, right);
bool left_need_fixup = false, right_need_fixup = false;
if (left_is_reference || right_is_reference) {
left_need_fixup = !right_is_reference && left.move(std::move(right));
right_need_fixup = !left_is_reference && right.move(std::move(left));
if (left_modality == modality::reference || right_modality == modality::reference) {
left_need_fixup = left.move_content(right, right_modality);
right_need_fixup = right.move_content(left, left_modality);
} else {
silo temp(std::move(left), false);
left_need_fixup = left.move(std::move(right));
right_need_fixup = right.move(std::move(temp));
left_need_fixup = left.move_content(right, right_modality);
right_need_fixup = right.move_content(temp, left_modality);
}
return std::make_pair(left_need_fixup, right_need_fixup);
}
@@ -1912,7 +1919,7 @@ private:
MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX11_CONSTEXPR void *data(size_t offset = 0) noexcept { return get(offset); }
};
MDBX_CXX14_CONSTEXPR void fixup_import(const typename silo::bin &src) noexcept {
MDBX_CXX14_CONSTEXPR void fixup_imported_inplace(const typename silo::bin &src) noexcept {
auto ptr = inherited::byte_ptr();
if (src.is_inplace(ptr)) {
MDBX_CONSTEXPR_ASSERT(silo_.bin_.is_inplace());
@@ -1985,23 +1992,25 @@ public:
return silo_.is_inplace(inherited::data());
}
/// \brief Checks whether the buffer just refers to data located outside
/// the buffer, rather than stores it.
/// \brief Checks whether the buffer just refers to data located outside the buffer, rather than stores it.
MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX20_CONSTEXPR bool is_reference() const noexcept { return !is_freestanding(); }
/// \brief Returns current data storage modality.
MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX20_CONSTEXPR modality asset() const noexcept {
return is_freestanding() ? (is_inplace() ? modality::inplace : modality::allocated) : modality::reference;
}
/// \brief Returns the number of bytes that can be held in currently allocated storage.
MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX20_CONSTEXPR size_t capacity() const noexcept {
return is_freestanding() ? silo_.capacity() : 0;
}
/// \brief Returns the number of bytes that available in currently allocated
/// storage ahead the currently beginning of data.
/// \brief Returns the number of bytes available in the currently allocated storage in front of the data.
MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX20_CONSTEXPR size_t headroom() const noexcept {
return is_freestanding() ? inherited::byte_ptr() - silo_begin() : 0;
}
/// \brief Returns the number of bytes that available in currently allocated
/// storage after the currently data end.
/// \brief Returns the number of bytes that available in currently allocated storage after the data.
MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX20_CONSTEXPR size_t tailroom() const noexcept {
return is_freestanding() ? silo_end() - inherited::end_byte_ptr() : 0;
}
@@ -2159,14 +2168,14 @@ public:
inline buffer(const txn &transaction, const slice &src, const allocator_type &alloc = allocator_type());
buffer(buffer &&src) noexcept(move_assign_alloc::is_nothrow())
buffer(buffer &&src) noexcept(noexcept(::std::is_nothrow_move_constructible<allocator_type>::value))
: inherited(/* no move here */ src), silo_(::std::move(src.silo_), src.is_reference()) {
/* CoverityScan issues an erroneous warning here about using an uninitialized object. Which is not true,
* since in C++ (unlike Rust) an object remains initialized after a move-assignment operation; Moreover,
* a destructor will be called for such an object (this is explicitly stated in all C++ standards, starting from the
* 11th). */
/* coverity[USE_AFTER_MOVE] */
fixup_import(src.silo_.bin_);
fixup_imported_inplace(src.silo_.bin_);
src.invalidate();
}
@@ -2306,13 +2315,12 @@ public:
}
MDBX_CXX20_CONSTEXPR void swap(buffer &other) noexcept(swap_alloc::is_nothrow()) {
const auto pair = silo::exchange(silo_, is_reference(), other.silo_, other.is_reference());
std::swap(iov_base, other.iov_base);
std::swap(iov_len, other.iov_len);
const auto pair = silo::exchange(silo_, asset(), other.silo_, other.asset());
inherited::swap(other);
if (pair.first)
fixup_import(other.silo_.bin_);
fixup_imported_inplace(other.silo_.bin_);
if (pair.second)
other.fixup_import(silo_.bin_);
other.fixup_imported_inplace(silo_.bin_);
}
MDBX_CXX20_CONSTEXPR friend void swap(buffer &left, buffer &right) noexcept(buffer::is_swap_nothrow()) {
@@ -2330,55 +2338,65 @@ public:
MDBX_CXX20_CONSTEXPR buffer &assign(size_t headroom, const buffer &src, size_t tailroom) {
const size_t whole_capacity = check_length(headroom, src.length(), tailroom);
if (MDBX_LIKELY(this != &src)) {
invalidate();
if MDBX_IF_CONSTEXPR (!allocation_aware_details::template allocator_is_always_equal<allocator_type>()) {
if (MDBX_UNLIKELY(silo_.get_allocator() != src.silo_.get_allocator()))
MDBX_CXX20_UNLIKELY {
silo_.release();
allocation_aware_details::copy_assign_alloc<silo, allocator_type>::propagate(&silo_, src.silo_);
}
}
if (MDBX_LIKELY(this != &src))
MDBX_CXX20_LIKELY {
invalidate();
if MDBX_IF_CONSTEXPR (!allocation_aware_details::template allocator_is_always_equal<allocator_type>()) {
if (MDBX_UNLIKELY(silo_.get_allocator() != src.silo_.get_allocator()))
MDBX_CXX20_UNLIKELY {
silo_.release();
allocation_aware_details::copy_assign_alloc<silo, allocator_type>::propagate(&silo_, src.silo_);
}
}
iov_base = silo_.template reshape<true>(whole_capacity, headroom, src.data(), src.length());
iov_len = src.length();
} else {
iov_base = silo_.template reshape<true>(whole_capacity, headroom, src.data(), src.length());
iov_len = src.length();
}
else {
iov_base = silo_.template reshape<false>(whole_capacity, headroom, src.data(), src.length());
}
return *this;
}
MDBX_CXX20_CONSTEXPR buffer &assign(const buffer &src, bool make_reference = false) {
if (MDBX_LIKELY(this != &src)) {
invalidate();
if MDBX_IF_CONSTEXPR (!allocation_aware_details::template allocator_is_always_equal<allocator_type>()) {
if (MDBX_UNLIKELY(silo_.get_allocator() != src.silo_.get_allocator()))
MDBX_CXX20_UNLIKELY {
silo_.release();
allocation_aware_details::copy_assign_alloc<silo, allocator_type>::propagate(&silo_, src.silo_);
}
}
if (MDBX_LIKELY(this != &src))
MDBX_CXX20_LIKELY {
invalidate();
if MDBX_IF_CONSTEXPR (!allocation_aware_details::template allocator_is_always_equal<allocator_type>()) {
if (MDBX_UNLIKELY(silo_.get_allocator() != src.silo_.get_allocator()))
MDBX_CXX20_UNLIKELY {
silo_.release();
allocation_aware_details::copy_assign_alloc<silo, allocator_type>::propagate(&silo_, src.silo_);
}
}
if (make_reference) {
silo_.release();
iov_base = src.iov_base;
} else {
iov_base = silo_.template reshape<true>(src.length(), 0, src.data(), src.length());
if (make_reference) {
silo_.release();
iov_base = src.iov_base;
} else {
iov_base = silo_.template reshape<true>(src.length(), 0, src.data(), src.length());
}
iov_len = src.length();
}
iov_len = src.length();
} else if (!make_reference && is_reference())
else if (!make_reference && is_reference())
insulate();
return *this;
}
MDBX_CXX20_CONSTEXPR buffer &
assign(buffer &&src) noexcept(allocation_aware_details::move_assign_alloc<silo, allocator_type>::is_nothrow()) {
if (MDBX_LIKELY(this != &src)) {
const bool is_reference = src.is_reference();
inherited::assign(std::move(src));
if (silo_.assign_move(std::move(src.silo_), is_reference))
fixup_import(src.silo_.bin_);
}
MDBX_CXX20_CONSTEXPR buffer &assign(buffer &&src) noexcept(move_assign_alloc::is_nothrow()) {
if (MDBX_LIKELY(this != &src))
MDBX_CXX20_LIKELY {
const auto kind = src.asset();
inherited::assign(std::move(src));
if (!move_assign_alloc::is_moveable(&silo_, src.silo_) && kind == modality::allocated) {
iov_base = silo_.template reshape<true>(src.silo_.capacity(), src.headroom(), src.data(), src.length());
return *this;
}
silo_.release();
move_assign_alloc::propagate(&silo_, src.silo_);
if (silo_.move_content(src.silo_, kind))
fixup_imported_inplace(src.silo_.bin_);
}
return *this;
}
+8 -8
View File
@@ -1,4 +1,4 @@
/* This file is part of the libmdbx amalgamated source code (v0.14.2-274-g58ea7f56 at 2026-07-09T20:41:59+03:00).
/* This file is part of the libmdbx amalgamated source code (v0.14.2-293-g43618122 at 2026-07-13T02:40:29+03:00).
*
* libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable, transactional key-value storage engine with
* open-source code. MDBX has a specific set of properties and capabilities, focused on creating unique lightweight
@@ -192,7 +192,7 @@ static FILE *MDBX_PRINTF_ARGS(2, 3) print_ln(enum MDBX_chk_severity severity, co
}
static void logger(MDBX_log_level_t level, const char *function, int line, const char *fmt, va_list args) {
if (level <= MDBX_LOG_ERROR)
if (level <= MDBX_LOG_ERROR && chk.internal)
mdbx_env_chk_encount_problem(&chk);
const unsigned kind =
@@ -328,15 +328,15 @@ static void print_done(MDBX_chk_line_t *line) {
}
static void print_chars(MDBX_chk_line_t *line, const char *str, size_t len) {
if (line->empty)
prefix(line->severity);
fwrite(str, 1, len, line_output);
FILE *const out = line->empty ? prefix(line->severity) : line_output;
if (out)
fwrite(str, 1, len, out);
}
static void print_format(MDBX_chk_line_t *line, const char *fmt, va_list args) {
if (line->empty)
prefix(line->severity);
vfprintf(line_output, fmt, args);
FILE *const out = line->empty ? prefix(line->severity) : line_output;
if (out)
vfprintf(out, fmt, args);
}
static const MDBX_chk_callbacks_t cb = {.check_break = check_break,
+1 -1
View File
@@ -1,4 +1,4 @@
/* This file is part of the libmdbx amalgamated source code (v0.14.2-274-g58ea7f56 at 2026-07-09T20:41:59+03:00).
/* This file is part of the libmdbx amalgamated source code (v0.14.2-293-g43618122 at 2026-07-13T02:40:29+03:00).
*
* libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable, transactional key-value storage engine with
* open-source code. MDBX has a specific set of properties and capabilities, focused on creating unique lightweight
+1 -1
View File
@@ -1,4 +1,4 @@
/* This file is part of the libmdbx amalgamated source code (v0.14.2-274-g58ea7f56 at 2026-07-09T20:41:59+03:00).
/* This file is part of the libmdbx amalgamated source code (v0.14.2-293-g43618122 at 2026-07-13T02:40:29+03:00).
*
* libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable, transactional key-value storage engine with
* open-source code. MDBX has a specific set of properties and capabilities, focused on creating unique lightweight
+1 -1
View File
@@ -1,4 +1,4 @@
/* This file is part of the libmdbx amalgamated source code (v0.14.2-274-g58ea7f56 at 2026-07-09T20:41:59+03:00).
/* This file is part of the libmdbx amalgamated source code (v0.14.2-293-g43618122 at 2026-07-13T02:40:29+03:00).
*
* libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable, transactional key-value storage engine with
* open-source code. MDBX has a specific set of properties and capabilities, focused on creating unique lightweight
+1 -1
View File
@@ -1,4 +1,4 @@
/* This file is part of the libmdbx amalgamated source code (v0.14.2-274-g58ea7f56 at 2026-07-09T20:41:59+03:00).
/* This file is part of the libmdbx amalgamated source code (v0.14.2-293-g43618122 at 2026-07-13T02:40:29+03:00).
*
* libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable, transactional key-value storage engine with
* open-source code. MDBX has a specific set of properties and capabilities, focused on creating unique lightweight
+78 -82
View File
@@ -1,4 +1,4 @@
/* This file is part of the libmdbx amalgamated source code (v0.14.2-274-g58ea7f56 at 2026-07-09T20:41:59+03:00).
/* This file is part of the libmdbx amalgamated source code (v0.14.2-293-g43618122 at 2026-07-13T02:40:29+03:00).
*
* libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable, transactional key-value storage engine with
* open-source code. MDBX has a specific set of properties and capabilities, focused on creating unique lightweight
@@ -139,7 +139,7 @@ static MDBX_envinfo envinfo;
#define GLOBAL 4
static int mode = GLOBAL;
static MDBX_val kbuf, dbuf;
static MDBX_val key_buf, data_buf;
#define STRLENOF(s) (sizeof(s) - 1)
@@ -167,7 +167,7 @@ static int readhdr(void) {
while (true) {
errno = 0;
if (fgets(dbuf.iov_base, (int)dbuf.iov_len, stdin) == nullptr)
if (fgets(data_buf.iov_base, (int)data_buf.iov_len, stdin) == nullptr)
return errno ? errno : EOF;
if (user_break)
return MDBX_EINTR;
@@ -175,7 +175,7 @@ static int readhdr(void) {
lineno++;
uint64_t u64;
if (valnum(dbuf.iov_base, "VERSION", &u64)) {
if (valnum(data_buf.iov_base, "VERSION", &u64)) {
if (u64 != 3) {
if (!quiet)
fprintf(stderr, "%s: line %" PRIiSIZE ": unsupported value %" PRIu64 " for %s\n", prog, lineno, u64,
@@ -185,7 +185,7 @@ static int readhdr(void) {
continue;
}
if (valnum(dbuf.iov_base, "db_pagesize", &u64)) {
if (valnum(data_buf.iov_base, "db_pagesize", &u64)) {
if (!(mode & GLOBAL) && envinfo.mi_dxb_pagesize != u64) {
if (!quiet)
fprintf(stderr, "%s: line %" PRIiSIZE ": ignore value %" PRIu64 " for '%s' in non-global context\n", prog,
@@ -199,7 +199,7 @@ static int readhdr(void) {
continue;
}
char *str = valstr(dbuf.iov_base, "format");
char *str = valstr(data_buf.iov_base, "format");
if (str) {
if (strcmp(str, "print") == 0) {
mode |= PLAINTEXT;
@@ -214,7 +214,7 @@ static int readhdr(void) {
exit(EXIT_FAILURE);
}
str = valstr(dbuf.iov_base, "database");
str = valstr(data_buf.iov_base, "database");
if (str) {
if (*str) {
free(subname);
@@ -228,7 +228,7 @@ static int readhdr(void) {
continue;
}
str = valstr(dbuf.iov_base, "type");
str = valstr(data_buf.iov_base, "type");
if (str) {
if (strcmp(str, "btree") != 0) {
if (!quiet)
@@ -239,7 +239,7 @@ static int readhdr(void) {
continue;
}
if (valnum(dbuf.iov_base, "mapaddr", &u64)) {
if (valnum(data_buf.iov_base, "mapaddr", &u64)) {
if (u64) {
if (!quiet)
fprintf(stderr, "%s: line %" PRIiSIZE ": ignore unsupported value 0x%" PRIx64 " for %s\n", prog, lineno, u64,
@@ -248,7 +248,7 @@ static int readhdr(void) {
continue;
}
if (valnum(dbuf.iov_base, "mapsize", &u64)) {
if (valnum(data_buf.iov_base, "mapsize", &u64)) {
if (!(mode & GLOBAL)) {
if (!quiet)
fprintf(stderr, "%s: line %" PRIiSIZE ": ignore value %" PRIu64 " for '%s' in non-global context\n", prog,
@@ -262,7 +262,7 @@ static int readhdr(void) {
continue;
}
if (valnum(dbuf.iov_base, "maxreaders", &u64)) {
if (valnum(data_buf.iov_base, "maxreaders", &u64)) {
if (!(mode & GLOBAL)) {
if (!quiet)
fprintf(stderr, "%s: line %" PRIiSIZE ": ignore value %" PRIu64 " for '%s' in non-global context\n", prog,
@@ -276,7 +276,7 @@ static int readhdr(void) {
continue;
}
if (valnum(dbuf.iov_base, "txnid", &u64)) {
if (valnum(data_buf.iov_base, "txnid", &u64)) {
if (u64 < MIN_TXNID || u64 > MAX_TXNID) {
if (!quiet)
fprintf(stderr, "%s: line %" PRIiSIZE ": ignore unsupported value 0x%" PRIx64 " for %s\n", prog, lineno, u64,
@@ -285,12 +285,12 @@ static int readhdr(void) {
continue;
}
if (valnum(dbuf.iov_base, "sequence", &u64)) {
if (valnum(data_buf.iov_base, "sequence", &u64)) {
sequence = u64;
continue;
}
str = valstr(dbuf.iov_base, "geometry");
str = valstr(data_buf.iov_base, "geometry");
if (str) {
if (!(mode & GLOBAL)) {
if (!quiet)
@@ -311,7 +311,7 @@ static int readhdr(void) {
continue;
}
str = valstr(dbuf.iov_base, "canary");
str = valstr(data_buf.iov_base, "canary");
if (str) {
if (!(mode & GLOBAL)) {
if (!quiet)
@@ -330,7 +330,7 @@ static int readhdr(void) {
for (int i = 0; dbflags[i].bit; i++) {
bool value = false;
if (valbool(dbuf.iov_base, dbflags[i].name, &value)) {
if (valbool(data_buf.iov_base, dbflags[i].name, &value)) {
if (value)
dbi_flags |= dbflags[i].bit;
else
@@ -339,7 +339,7 @@ static int readhdr(void) {
}
}
str = valstr(dbuf.iov_base, "HEADER");
str = valstr(data_buf.iov_base, "HEADER");
if (str) {
if (strcmp(str, "END") == 0)
return MDBX_SUCCESS;
@@ -347,7 +347,7 @@ static int readhdr(void) {
if (!quiet)
fprintf(stderr, "%s: line %" PRIiSIZE ": unrecognized keyword ignored: %s\n", prog, lineno,
(char *)dbuf.iov_base);
(char *)data_buf.iov_base);
next:;
}
return EOF;
@@ -359,34 +359,29 @@ static int badend(void) {
return errno ? errno : MDBX_ENODATA;
}
static inline int unhex(unsigned char *c2) {
int8_t hi = c2[0];
static inline uint8_t unhex(const char *hex) {
int8_t hi = hex[0];
hi = (hi | 0x20) - 'a';
hi += 10 + ((hi >> 7) & 39);
int8_t lo = c2[1];
int8_t lo = hex[1];
lo = (lo | 0x20) - 'a';
lo += 10 + ((lo >> 7) & 39);
return hi << 4 | lo;
return (uint8_t)(hi << 4 | lo);
}
__hot static int readline(MDBX_val *out, MDBX_val *buf) {
unsigned char *c1, *c2, *end;
size_t len, l2;
int c;
if (user_break)
return MDBX_EINTR;
errno = 0;
if (!(mode & NOHDR)) {
c = fgetc(stdin);
int c = fgetc(stdin);
if (c == EOF)
return errno ? errno : EOF;
if (c != ' ') {
lineno++;
errno = 0;
if (fgets(buf->iov_base, (int)buf->iov_len, stdin)) {
if (c == 'D' && !strncmp(buf->iov_base, "ATA=END", STRLENOF("ATA=END")))
return EOF;
@@ -396,74 +391,75 @@ __hot static int readline(MDBX_val *out, MDBX_val *buf) {
}
/* modern concise mode, where space in second position mean the same (previously) value */
c = fgetc(stdin);
int c = fgetc(stdin);
if (c == EOF)
return errno ? errno : EOF;
if (c == ' ')
return (ungetc(c, stdin) == c) ? MDBX_SUCCESS : (errno ? errno : EOF);
((char *)buf->iov_base)[0] = c;
((char *)buf->iov_base)[1] = 0;
if (c != '\n' && fgets((char *)buf->iov_base + 1, (int)buf->iov_len - 1, stdin) == nullptr)
char *line = buf->iov_base;
line[0] = c;
line[1] = 0;
if (c != '\n' && fgets(line + 1, (int)buf->iov_len - 1, stdin) == nullptr)
return errno ? errno : EOF;
lineno++;
c1 = buf->iov_base;
len = strlen((char *)c1);
l2 = len;
size_t len = strlen(line);
if (len > 0) {
/* Is buffer too short? */
while (line[len - 1] != '\n') {
/* double buffer size */
line = osal_realloc(buf->iov_base, buf->iov_len * 2);
if (!line) {
if (!quiet)
fprintf(stderr, "%s: line %" PRIiSIZE ": out of memory, line too long\n", prog, lineno);
return MDBX_ENOMEM;
}
buf->iov_base = line;
buf->iov_len *= 2;
/* Is buffer too short? */
while (len == 0 || c1[len - 1] != '\n') {
buf->iov_base = osal_realloc(buf->iov_base, buf->iov_len * 2);
if (!buf->iov_base) {
if (!quiet)
fprintf(stderr, "%s: line %" PRIiSIZE ": out of memory, line too long\n", prog, lineno);
return MDBX_ENOMEM;
/* continue read line */
errno = 0;
if (fgets(line + len, (int)buf->iov_len - len, stdin) == nullptr)
return errno ? errno : EOF;
len += strlen(line + len);
}
c1 = buf->iov_base;
c1 += l2;
errno = 0;
if (fgets((char *)c1, (int)(buf->iov_len - l2), stdin) == nullptr)
return errno ? errno : EOF;
buf->iov_len *= 2;
len = strlen((char *)c1);
l2 += len;
/* strip '\n' at the end */
line[--len] = '\0';
}
c1 = c2 = buf->iov_base;
len = l2;
c1[--len] = '\0';
end = c1 + len;
char *w = line, *r = line;
char *const end = r + len;
if (mode & PLAINTEXT) {
while (c2 < end) {
if (unlikely(*c2 == '\\')) {
if (c2[1] == '\\') {
*c1++ = '\\';
while (r < end) {
if (unlikely(r[0] == '\\')) {
if (r[1] == '\\') {
*w++ = '\\';
} else {
if (c2 + 3 > end || !isxdigit(c2[1]) || !isxdigit(c2[2]))
if (r + 3 > end || !isxdigit(r[1]) || !isxdigit(r[2]))
return badend();
*c1++ = (char)unhex(++c2);
*w++ = unhex(++r);
}
c2 += 2;
r += 2;
} else {
/* copies are redundant when no escapes were used */
*c1++ = *c2++;
*w++ = *r++;
}
}
} else {
/* odd length not allowed */
if (len & 1)
return badend();
while (c2 < end) {
if (!isxdigit(*c2) || !isxdigit(c2[1]))
while (r < end) {
if (!isxdigit(r[0]) || !isxdigit(r[1]))
return badend();
*c1++ = (char)unhex(c2);
c2 += 2;
*w++ = unhex(r);
r += 2;
}
}
c2 = out->iov_base = buf->iov_base;
out->iov_len = c1 - c2;
out->iov_base = line;
out->iov_len = w - line;
return MDBX_SUCCESS;
}
@@ -643,9 +639,9 @@ int main(int argc, char *argv[]) {
mdbx_setup_debug(MDBX_LOG_NOTICE, MDBX_DBG_DONTCHANGE, logger);
}
dbuf.iov_len = 4096;
dbuf.iov_base = osal_malloc(dbuf.iov_len);
if (!dbuf.iov_base) {
data_buf.iov_len = 4096;
data_buf.iov_base = osal_malloc(data_buf.iov_len);
if (!data_buf.iov_base) {
err = MDBX_ENOMEM;
error("value-buffer", err);
goto bailout;
@@ -693,12 +689,12 @@ int main(int argc, char *argv[]) {
(intptr_t)envinfo.mi_geo.shrink,
envinfo.mi_dxb_pagesize ? (intptr_t)envinfo.mi_dxb_pagesize : -1);
} else if (envinfo.mi_mapsize) {
if (envinfo.mi_mapsize > MAX_MAPSIZE) {
if (envinfo.mi_mapsize > globals.mmap_limit) {
if (!quiet)
fprintf(stderr,
"Database size is too large for current system (mapsize=%" PRIu64
" is greater than system-limit %zu)\n",
envinfo.mi_mapsize, (size_t)MAX_MAPSIZE);
envinfo.mi_mapsize, globals.mmap_limit);
goto bailout;
}
err = mdbx_env_set_geometry(env, (intptr_t)envinfo.mi_mapsize, (intptr_t)envinfo.mi_mapsize,
@@ -723,15 +719,15 @@ int main(int argc, char *argv[]) {
goto bailout;
}
kbuf.iov_len = mdbx_env_get_maxvalsize_ex(env, 0) + (size_t)1;
if (kbuf.iov_len >= INTPTR_MAX / 2) {
key_buf.iov_len = mdbx_env_get_maxkeysize_ex(env, 0) + (size_t)1;
if (key_buf.iov_len >= INTPTR_MAX / 2) {
if (!quiet)
fprintf(stderr, "mdbx_env_get_maxvalsize_ex() failed, returns %zu\n", kbuf.iov_len);
fprintf(stderr, "mdbx_env_get_maxvalsize_ex() failed, returns %zu\n", key_buf.iov_len);
goto bailout;
}
kbuf.iov_base = malloc(kbuf.iov_len);
if (!kbuf.iov_base) {
key_buf.iov_base = malloc(key_buf.iov_len);
if (!key_buf.iov_base) {
err = MDBX_ENOMEM;
error("key-buffer", err);
goto bailout;
@@ -811,7 +807,7 @@ int main(int argc, char *argv[]) {
size_t count = 0;
MDBX_val key = {.iov_base = nullptr, .iov_len = 0}, data = {.iov_base = nullptr, .iov_len = 0};
while (err == MDBX_SUCCESS) {
err = readline(&key, &kbuf);
err = readline(&key, &key_buf);
if (err == EOF)
break;
if (err) {
@@ -820,7 +816,7 @@ int main(int argc, char *argv[]) {
goto bailout;
}
err = readline(&data, &dbuf);
err = readline(&data, &data_buf);
if (err) {
if (!quiet)
fprintf(stderr, "%s: line %" PRIiSIZE ": failed to read %s\n", prog, lineno, "value");
@@ -916,8 +912,8 @@ bailout:
mdbx_txn_abort(txn);
if (env)
mdbx_env_close(env);
free(kbuf.iov_base);
free(dbuf.iov_base);
free(key_buf.iov_base);
free(data_buf.iov_base);
return err ? EXIT_FAILURE : EXIT_SUCCESS;
}
+7 -5
View File
@@ -1,4 +1,4 @@
/* This file is part of the libmdbx amalgamated source code (v0.14.2-274-g58ea7f56 at 2026-07-09T20:41:59+03:00).
/* This file is part of the libmdbx amalgamated source code (v0.14.2-293-g43618122 at 2026-07-13T02:40:29+03:00).
*
* libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable, transactional key-value storage engine with
* open-source code. MDBX has a specific set of properties and capabilities, focused on creating unique lightweight
@@ -95,10 +95,12 @@ static int reader_list_func(void *ctx, int num, int slot, mdbx_pid_t pid, mdbx_t
printf(" %3d)\t[%d]\t%10" PRIdSIZE " %*s", num, slot, (size_t)pid, (int)sizeof(size_t) * 2, thread_str);
if (txnid)
printf(" %20" PRIu64 " %10" PRIu64 " %12.1fM %12.1fM\n", txnid, lag, bytes_used / 1048576.0,
bytes_retained / 1048576.0);
else
if (txnid) {
char buf_bytes_used[42], buf_bytes_retained[42];
printf(" %20" PRIu64 " %10" PRIu64 " %12sM %12sM\n", txnid, lag,
mdbx_ratio2digits(bytes_used, 1048576, 1, buf_bytes_used, sizeof(buf_bytes_used)),
mdbx_ratio2digits(bytes_retained, 1048576, 1, buf_bytes_retained, sizeof(buf_bytes_retained)));
} else
printf(" %20s %10s %13s %13s\n", "-", "0", "0", "0");
return user_break ? MDBX_RESULT_TRUE : MDBX_RESULT_FALSE;