mirror of
https://github.com/isar/libmdbx.git
synced 2025-12-15 16:42:22 +08:00
Compare commits
116 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf603bdffc | ||
|
|
cd73caac1c | ||
|
|
aec884f0d1 | ||
|
|
e3ce6d645a | ||
|
|
d2cba98f70 | ||
|
|
217e951e68 | ||
|
|
99b75b5004 | ||
|
|
42d545e579 | ||
|
|
e3300259ff | ||
|
|
68273acc2a | ||
|
|
e20664fe55 | ||
|
|
8c761f5774 | ||
|
|
b6ffec12e4 | ||
|
|
5d4d02617d | ||
|
|
327b283136 | ||
|
|
4bb0c57e29 | ||
|
|
a9cae5e314 | ||
|
|
b9ac607a5e | ||
|
|
7e035115bb | ||
|
|
099dd68630 | ||
|
|
5d7ef50054 | ||
|
|
2395564c17 | ||
|
|
5e7a685ba8 | ||
|
|
f7cbf41f03 | ||
|
|
70933d81a8 | ||
|
|
a8115267a6 | ||
|
|
a9e99241b7 | ||
|
|
bca4adcdc7 | ||
|
|
79281d59c7 | ||
|
|
e6a83654a8 | ||
|
|
f4f166a66d | ||
|
|
65fa0cf8ed | ||
|
|
046dc02f73 | ||
|
|
c2fa453725 | ||
|
|
e731260056 | ||
|
|
9a04c9a350 | ||
|
|
13912be35d | ||
|
|
2e68adefb3 | ||
|
|
bf9e6146df | ||
|
|
2c190cfb56 | ||
|
|
5fa2e30709 | ||
|
|
faafa21480 | ||
|
|
3f929e3766 | ||
|
|
9a1dffc7dc | ||
|
|
c81ab53eb2 | ||
|
|
7759e52850 | ||
|
|
e7336e1d5e | ||
|
|
68aef96f0a | ||
|
|
0b120b8fa9 | ||
|
|
28c36af67c | ||
|
|
d67b9eaf17 | ||
|
|
6034985686 | ||
|
|
7da64b725d | ||
|
|
ba22ae9ab3 | ||
|
|
8aaf5d071b | ||
|
|
6a6ead6cfb | ||
|
|
10fefe87a6 | ||
|
|
de3c028f0d | ||
|
|
b6233ae2e5 | ||
|
|
fe5f008d39 | ||
|
|
c8743cb9c4 | ||
|
|
1995754bc3 | ||
|
|
f749b3deee | ||
|
|
ebfffe3f2b | ||
|
|
de4a6baf80 | ||
|
|
584326e9b6 | ||
|
|
a7becdc6b3 | ||
|
|
4de2dcebb5 | ||
|
|
678a80dd19 | ||
|
|
55d1f5e9c0 | ||
|
|
c18bf4f898 | ||
|
|
728e7f92b2 | ||
|
|
1740043678 | ||
|
|
891d68838a | ||
|
|
108398c213 | ||
|
|
8bdee27248 | ||
|
|
00c6dc9788 | ||
|
|
fb67682a79 | ||
|
|
5ed50a4739 | ||
|
|
c30c3def8b | ||
|
|
2f74f405ae | ||
|
|
c7e05f63e6 | ||
|
|
d65305564f | ||
|
|
660c302525 | ||
|
|
d7aad3a7cf | ||
|
|
15ed0f6a84 | ||
|
|
ffb8d23632 | ||
|
|
682632756f | ||
|
|
39c5e66ed1 | ||
|
|
b66780633e | ||
|
|
bd2bb51f0f | ||
|
|
ac69464143 | ||
|
|
62889b5b7f | ||
|
|
c4a696be1d | ||
|
|
cf5f31c577 | ||
|
|
fa49e5a57b | ||
|
|
750a17e41e | ||
|
|
5d4281fbbe | ||
|
|
2c18225654 | ||
|
|
cc6610f42c | ||
|
|
77a1f32e2a | ||
|
|
63e7276c7d | ||
|
|
a5e10b4ea1 | ||
|
|
b8e621cb2f | ||
|
|
68a164da2b | ||
|
|
5db855d728 | ||
|
|
06aa596519 | ||
|
|
d47864dedf | ||
|
|
581ca4fdf4 | ||
|
|
6771b7526c | ||
|
|
d7c06b1337 | ||
|
|
18bc28bea2 | ||
|
|
93e6e64eb0 | ||
|
|
0cd1eac6a8 | ||
|
|
0e83a8e5ef | ||
|
|
f951f246b8 |
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@@ -9,4 +9,4 @@ community_bridge: # Replace with a single Community Bridge project-name e.g., cl
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: ['https://sobe.ru/na/libmdbx', 'https://paypal.me/erthink', 'https://etherscan.io/address/0xe8a741a2a0a4850c5bdbb415aaaaf8fb46f06fb7']
|
||||
custom: ['https://sobe.ru/na/libmdbx', 'https://paypal.me/erthink', 'https://etherscan.io/address/0x19291d8658f762f3baceae1700c0b9466572ceab']
|
||||
|
||||
1
.github/actions/spelling/excludes.txt
vendored
1
.github/actions/spelling/excludes.txt
vendored
@@ -8,3 +8,4 @@
|
||||
^\.gitignore$
|
||||
^\.travis\.yml$
|
||||
^packages/buildroot/
|
||||
^CMakeSettings\.json$
|
||||
|
||||
32
.github/actions/spelling/expect.txt
vendored
32
.github/actions/spelling/expect.txt
vendored
@@ -9,6 +9,7 @@ acision
|
||||
AClass
|
||||
adata
|
||||
addprefix
|
||||
addressof
|
||||
addsuffix
|
||||
addtogroup
|
||||
advapi
|
||||
@@ -22,6 +23,7 @@ alevel
|
||||
alexey
|
||||
Alfke
|
||||
alignas
|
||||
alignof
|
||||
alldbs
|
||||
ALLDUPS
|
||||
ALLEXTERNALS
|
||||
@@ -103,6 +105,7 @@ bitfield
|
||||
bitmaps
|
||||
bitmask
|
||||
bitness
|
||||
bitops
|
||||
blogger
|
||||
blogs
|
||||
blogspot
|
||||
@@ -126,6 +129,7 @@ buflen
|
||||
bufsize
|
||||
BUGLIST
|
||||
bugzilla
|
||||
buildflags
|
||||
buildpack
|
||||
buildroot
|
||||
builtins
|
||||
@@ -170,6 +174,7 @@ cifs
|
||||
cinttypes
|
||||
circleci
|
||||
claude
|
||||
climits
|
||||
clockid
|
||||
CLOEXEC
|
||||
closefile
|
||||
@@ -283,6 +288,8 @@ ddl
|
||||
deadread
|
||||
deadwrite
|
||||
debian
|
||||
Debroux
|
||||
debrouxl
|
||||
debruijn
|
||||
DECC
|
||||
declspec
|
||||
@@ -471,6 +478,7 @@ exactkey
|
||||
exactp
|
||||
excpt
|
||||
exe
|
||||
executables
|
||||
exename
|
||||
exherbo
|
||||
exitcode
|
||||
@@ -489,6 +497,7 @@ FCXX
|
||||
fd
|
||||
fdatasync
|
||||
featuredarticles
|
||||
fedotov
|
||||
FEEDNAME
|
||||
feof
|
||||
ferror
|
||||
@@ -511,6 +520,7 @@ Firefox
|
||||
firstvalue
|
||||
fixedpoint
|
||||
FIXME
|
||||
fjvallarino
|
||||
flagbit
|
||||
flg
|
||||
flipcoin
|
||||
@@ -608,9 +618,11 @@ grep
|
||||
gtags
|
||||
gz
|
||||
gzip
|
||||
hackage
|
||||
HAGL
|
||||
hallvard
|
||||
hardcoded
|
||||
haskell
|
||||
HASSEMAPHORE
|
||||
hdiutil
|
||||
hdr
|
||||
@@ -681,6 +693,7 @@ img
|
||||
impl
|
||||
IMPLIB
|
||||
inblock
|
||||
INCLUDEDIR
|
||||
indx
|
||||
INDXSIZE
|
||||
ini
|
||||
@@ -775,6 +788,7 @@ klen
|
||||
KMGTPEZY
|
||||
knipp
|
||||
kp
|
||||
kriszyp
|
||||
ks
|
||||
ksize
|
||||
kstat
|
||||
@@ -785,6 +799,7 @@ kval
|
||||
Lanfranchi
|
||||
largedata
|
||||
largepage
|
||||
lastbyte
|
||||
lastest
|
||||
lastvalue
|
||||
lastword
|
||||
@@ -830,6 +845,7 @@ LLV
|
||||
llvm
|
||||
lmb
|
||||
lmdb
|
||||
lmdbx
|
||||
LMEM
|
||||
LNK
|
||||
lnps
|
||||
@@ -875,6 +891,7 @@ MACROFILE
|
||||
MADV
|
||||
madvise
|
||||
mahlonsmith
|
||||
mailto
|
||||
maindb
|
||||
mainpage
|
||||
maj
|
||||
@@ -960,6 +977,7 @@ microsoft
|
||||
Mikkelson
|
||||
minflt
|
||||
MINGW
|
||||
minimalistic
|
||||
minkeys
|
||||
minlen
|
||||
MINSIZEREL
|
||||
@@ -1097,6 +1115,7 @@ nordahead
|
||||
NOREPLACE
|
||||
NORESERVE
|
||||
noreturn
|
||||
NOSANITIZE
|
||||
nospill
|
||||
nosubdir
|
||||
nosync
|
||||
@@ -1107,8 +1126,10 @@ notls
|
||||
notracking
|
||||
NOWAIT
|
||||
npages
|
||||
npmjs
|
||||
npos
|
||||
npr
|
||||
nproc
|
||||
nptl
|
||||
nreaders
|
||||
nrepeat
|
||||
@@ -1272,7 +1293,6 @@ ppc
|
||||
pragma
|
||||
pread
|
||||
prealloc
|
||||
prealloc'd
|
||||
PREDEF
|
||||
prefetch
|
||||
Prefetcher
|
||||
@@ -1286,7 +1306,7 @@ PRIa
|
||||
PRId
|
||||
PRIi
|
||||
printf
|
||||
Prioritization
|
||||
prioritization
|
||||
PRIu
|
||||
prng
|
||||
prno
|
||||
@@ -1322,6 +1342,7 @@ putflags
|
||||
pv
|
||||
pvalue
|
||||
PVOID
|
||||
pwd
|
||||
PWIM
|
||||
PWIN
|
||||
PWOF
|
||||
@@ -1466,6 +1487,7 @@ semop
|
||||
sems
|
||||
sendfile
|
||||
sepkey
|
||||
sergey
|
||||
SETALL
|
||||
SETFD
|
||||
setlen
|
||||
@@ -1648,6 +1670,7 @@ svg
|
||||
svnweb
|
||||
svr
|
||||
swait
|
||||
swappable
|
||||
symas
|
||||
SYMLINKS
|
||||
syncbytes
|
||||
@@ -1716,6 +1739,7 @@ toolset
|
||||
tooltip
|
||||
torquem
|
||||
TOUPPER
|
||||
transcoder
|
||||
transcoding
|
||||
treeview
|
||||
tribudubois
|
||||
@@ -1771,6 +1795,7 @@ underfilled
|
||||
UNDOC
|
||||
unicode
|
||||
UNIFORUM
|
||||
uninit
|
||||
uninstall
|
||||
UNINSTALLING
|
||||
uniq
|
||||
@@ -1789,6 +1814,7 @@ updation
|
||||
upsert
|
||||
UPSERTING
|
||||
upsertion
|
||||
upto
|
||||
URG
|
||||
url
|
||||
usec
|
||||
@@ -1814,6 +1840,7 @@ Vaefnrs
|
||||
valbool
|
||||
valgrind
|
||||
validator
|
||||
Vallarino
|
||||
valnum
|
||||
valsize
|
||||
valstr
|
||||
@@ -1948,3 +1975,4 @@ zi
|
||||
ZLIB
|
||||
zu
|
||||
zx
|
||||
Zyp
|
||||
|
||||
4
.github/workflows/release-assets.yml
vendored
4
.github/workflows/release-assets.yml
vendored
@@ -24,8 +24,8 @@ jobs:
|
||||
run: |
|
||||
echo "::set-output name=tarball::$(ls *.tar.gz)"
|
||||
echo "::set-output name=zip::$(ls *.zip)"
|
||||
echo "::set-output name=suffix_unix::$(sed 's|^\([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\)\.\(.*\)$|\1|' dist/VERSION)"
|
||||
echo "::set-output name=suffix_dos::$(sed 's|^\([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\)\.\(.*\)$|\1|' dist/VERSION | tr . _)"
|
||||
echo "::set-output name=suffix_unix::$(sed 's|^\([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\)\.\(.*\)$|\1|' dist/VERSION.txt)"
|
||||
echo "::set-output name=suffix_dos::$(sed 's|^\([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\)\.\(.*\)$|\1|' dist/VERSION.txt | tr . _)"
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -15,11 +15,13 @@
|
||||
.idea
|
||||
.le.ini
|
||||
.vs/
|
||||
.vscode/
|
||||
cmake-build-*
|
||||
@*
|
||||
core
|
||||
mdbx_example
|
||||
libmdbx.creator.user
|
||||
CMakeLists.txt.user
|
||||
mdbx_chk
|
||||
mdbx_copy
|
||||
mdbx_drop
|
||||
@@ -36,6 +38,9 @@ valgrind.*
|
||||
src/version.c
|
||||
src/config.h
|
||||
dist/
|
||||
dist-check/
|
||||
dist-checked.tag
|
||||
*.tar*
|
||||
docs/Doxyfile
|
||||
docs/html/
|
||||
buildflags.tag
|
||||
|
||||
@@ -34,10 +34,14 @@
|
||||
## The Future will (be) Positive. Всё будет хорошо.
|
||||
##
|
||||
|
||||
cmake_minimum_required(VERSION 3.8.2)
|
||||
if(CMAKE_VERSION VERSION_LESS 3.12)
|
||||
cmake_minimum_required(VERSION 3.8.2)
|
||||
else()
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
endif()
|
||||
|
||||
cmake_policy(PUSH)
|
||||
cmake_policy(VERSION 3.8.2)
|
||||
cmake_policy(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION})
|
||||
if(NOT CMAKE_VERSION VERSION_LESS 3.15)
|
||||
cmake_policy(SET CMP0091 NEW)
|
||||
endif()
|
||||
@@ -70,7 +74,7 @@ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git" AND
|
||||
message(SEND_ERROR "Git command-line tool not found")
|
||||
endif()
|
||||
set(MDBX_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src")
|
||||
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" AND
|
||||
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION.txt" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/mdbx.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/mdbx.c++" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" AND
|
||||
@@ -228,8 +232,13 @@ else()
|
||||
endif()
|
||||
|
||||
if(CMAKE_INTERPROCEDURAL_OPTIMIZATION_AVAILABLE
|
||||
OR GCC_LTO_AVAILABLE OR MSVC_LTO_AVAILABLE OR CLANG_LTO_AVAILABLE)
|
||||
OR GCC_LTO_AVAILABLE OR MSVC_LTO_AVAILABLE OR
|
||||
(CLANG_LTO_AVAILABLE AND
|
||||
((DEFINED MDBX_ENABLE_TESTS AND NOT MDBX_ENABLE_TESTS)
|
||||
OR NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.0)))
|
||||
option(INTERPROCEDURAL_OPTIMIZATION "Enable interprocedural/LTO optimization" ${INTERPROCEDURAL_OPTIMIZATION_DEFAULT})
|
||||
else()
|
||||
set(INTERPROCEDURAL_OPTIMIZATION OFF)
|
||||
endif()
|
||||
|
||||
if(INTERPROCEDURAL_OPTIMIZATION)
|
||||
@@ -328,9 +337,11 @@ list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_20 HAS_CXX20)
|
||||
if(NOT DEFINED MDBX_CXX_STANDARD)
|
||||
if(DEFINED CMAKE_CXX_STANDARD)
|
||||
set(MDBX_CXX_STANDARD ${CMAKE_CXX_STANDARD})
|
||||
elseif(NOT HAS_CXX20 LESS 0)
|
||||
elseif(NOT HAS_CXX20 LESS 0
|
||||
AND NOT (CMAKE_COMPILER_IS_CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9))
|
||||
set(MDBX_CXX_STANDARD 20)
|
||||
elseif(NOT HAS_CXX17 LESS 0)
|
||||
elseif(NOT HAS_CXX17 LESS 0
|
||||
AND NOT (CMAKE_COMPILER_IS_CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5))
|
||||
set(MDBX_CXX_STANDARD 17)
|
||||
elseif(NOT HAS_CXX14 LESS 0)
|
||||
set(MDBX_CXX_STANDARD 14)
|
||||
@@ -483,7 +494,7 @@ if(CMAKE_CXX_COMPILER_LOADED AND MDBX_CXX_STANDARD GREATER_EQUAL 11 AND MDBX_CXX
|
||||
endif()
|
||||
if(NOT MDBX_WITHOUT_MSVC_CRT
|
||||
AND NOT (CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8)
|
||||
AND NOT (CMAKE_COMPILER_IS_CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4)
|
||||
AND NOT (CMAKE_COMPILER_IS_CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.9)
|
||||
AND NOT (MSVC AND MSVC_VERSION LESS 1900))
|
||||
option(MDBX_BUILD_CXX "Build C++ portion" ON)
|
||||
else()
|
||||
@@ -539,6 +550,13 @@ else()
|
||||
message(STATUS "Use C${MDBX_C_STANDARD} for libmdbx but C++ portion is disabled")
|
||||
endif()
|
||||
|
||||
if(SUBPROJECT AND MSVC)
|
||||
if(MSVC_VERSION LESS 1900)
|
||||
message(FATAL_ERROR "At least \"Microsoft C/C++ Compiler\" version 19.0.24234.1 (Visual Studio 2015 Update 3) is required.")
|
||||
endif()
|
||||
add_compile_options("/utf-8")
|
||||
endif()
|
||||
|
||||
macro(target_setup_options TARGET)
|
||||
if(DEFINED INTERPROCEDURAL_OPTIMIZATION)
|
||||
set_target_properties(${TARGET} PROPERTIES
|
||||
@@ -549,15 +567,19 @@ macro(target_setup_options TARGET)
|
||||
if(MDBX_BUILD_CXX)
|
||||
set_target_properties(${TARGET} PROPERTIES
|
||||
CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON)
|
||||
if(MSVC AND NOT MSVC_VERSION LESS 1910)
|
||||
target_compile_options(${TARGET} INTERFACE "/Zc:__cplusplus")
|
||||
endif()
|
||||
endif()
|
||||
if(CC_HAS_FASTMATH)
|
||||
if(CC_HAS_FASTMATH
|
||||
AND NOT (CMAKE_COMPILER_IS_CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10))
|
||||
target_compile_options(${TARGET} PRIVATE "-ffast-math")
|
||||
endif()
|
||||
if(CC_HAS_VISIBILITY)
|
||||
target_compile_options(${TARGET} PRIVATE "-fvisibility=hidden")
|
||||
endif()
|
||||
if(BUILD_FOR_NATIVE_CPU AND CC_HAS_ARCH_NATIVE)
|
||||
target_compile_options(${TARGET} PUBLIC "-march=native")
|
||||
target_compile_options(${TARGET} PRIVATE "-march=native")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
@@ -573,7 +595,7 @@ macro(libmdbx_setup_libs TARGET MODE)
|
||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Android")
|
||||
target_link_libraries(${TARGET} ${MODE} log)
|
||||
endif()
|
||||
if(LIBCXX_FILESYSTEM AND MDBX_BUILD_CXX)
|
||||
if(MDBX_CXX_STANDARD GREATER_EQUAL 17 AND LIBCXX_FILESYSTEM AND MDBX_BUILD_CXX)
|
||||
target_link_libraries(${TARGET} ${MODE} ${LIBCXX_FILESYSTEM})
|
||||
endif()
|
||||
endmacro()
|
||||
@@ -670,34 +692,34 @@ endif()
|
||||
# mdbx-shared-lib installation
|
||||
if(NOT DEFINED MDBX_DLL_INSTALL_DESTINATION)
|
||||
if(WIN32)
|
||||
set(MDBX_DLL_INSTALL_DESTINATION bin)
|
||||
set(MDBX_DLL_INSTALL_DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
else()
|
||||
set(MDBX_DLL_INSTALL_DESTINATION lib)
|
||||
set(MDBX_DLL_INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
endif()
|
||||
endif()
|
||||
if(MDBX_BUILD_SHARED_LIBRARY)
|
||||
if(CMAKE_VERSION VERSION_LESS 3.12)
|
||||
install(TARGETS mdbx EXPORT libmdbx
|
||||
LIBRARY DESTINATION ${MDBX_DLL_INSTALL_DESTINATION} COMPONENT runtime
|
||||
OBJECTS DESTINATION lib COMPONENT devel
|
||||
ARCHIVE DESTINATION lib COMPONENT devel
|
||||
PUBLIC_HEADER DESTINATION include COMPONENT devel
|
||||
INCLUDES DESTINATION include COMPONENT devel)
|
||||
OBJECTS DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT devel
|
||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT devel)
|
||||
else()
|
||||
install(TARGETS mdbx EXPORT libmdbx
|
||||
LIBRARY DESTINATION ${MDBX_DLL_INSTALL_DESTINATION} COMPONENT runtime
|
||||
NAMELINK_COMPONENT devel
|
||||
OBJECTS DESTINATION lib COMPONENT devel
|
||||
ARCHIVE DESTINATION lib COMPONENT devel
|
||||
PUBLIC_HEADER DESTINATION include COMPONENT devel
|
||||
INCLUDES DESTINATION include COMPONENT devel)
|
||||
OBJECTS DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT devel
|
||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT devel)
|
||||
endif()
|
||||
endif(MDBX_BUILD_SHARED_LIBRARY)
|
||||
|
||||
# mdbx-tools installation
|
||||
if(MDBX_BUILD_TOOLS)
|
||||
if(NOT DEFINED MDBX_TOOLS_INSTALL_DESTINATION)
|
||||
set(MDBX_TOOLS_INSTALL_DESTINATION bin)
|
||||
set(MDBX_TOOLS_INSTALL_DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
endif()
|
||||
install(
|
||||
TARGETS
|
||||
@@ -712,7 +734,7 @@ if(MDBX_BUILD_TOOLS)
|
||||
COMPONENT runtime)
|
||||
if(MDBX_INSTALL_MANPAGES)
|
||||
if(NOT DEFINED MDBX_MAN_INSTALL_DESTINATION)
|
||||
set(MDBX_MAN_INSTALL_DESTINATION man/man1)
|
||||
set(MDBX_MAN_INSTALL_DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
|
||||
endif()
|
||||
install(
|
||||
FILES
|
||||
@@ -731,26 +753,28 @@ endif(MDBX_BUILD_TOOLS)
|
||||
if(MDBX_INSTALL_STATIC)
|
||||
if(CMAKE_VERSION VERSION_LESS 3.12)
|
||||
install(TARGETS mdbx-static EXPORT libmdbx
|
||||
LIBRARY DESTINATION lib COMPONENT devel
|
||||
OBJECTS DESTINATION lib COMPONENT devel
|
||||
ARCHIVE DESTINATION lib COMPONENT devel
|
||||
PUBLIC_HEADER DESTINATION include COMPONENT devel
|
||||
INCLUDES DESTINATION include COMPONENT devel)
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel
|
||||
OBJECTS DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT devel
|
||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT devel)
|
||||
else()
|
||||
install(TARGETS mdbx-static EXPORT libmdbx
|
||||
LIBRARY DESTINATION lib COMPONENT devel
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel
|
||||
NAMELINK_COMPONENT devel
|
||||
OBJECTS DESTINATION lib COMPONENT devel
|
||||
ARCHIVE DESTINATION lib COMPONENT devel
|
||||
PUBLIC_HEADER DESTINATION include COMPONENT devel
|
||||
INCLUDES DESTINATION include COMPONENT devel)
|
||||
OBJECTS DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT devel
|
||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT devel)
|
||||
endif()
|
||||
endif(MDBX_INSTALL_STATIC)
|
||||
|
||||
################################################################################
|
||||
|
||||
# collect options & build info
|
||||
string(TIMESTAMP MDBX_BUILD_TIMESTAMP UTC)
|
||||
if(NOT DEFINED MDBX_BUILD_TIMESTAMP)
|
||||
string(TIMESTAMP MDBX_BUILD_TIMESTAMP UTC)
|
||||
endif()
|
||||
set(MDBX_BUILD_FLAGS ${CMAKE_C_FLAGS})
|
||||
if(MDBX_BUILD_CXX)
|
||||
set(MDBX_BUILD_FLAGS ${CMAKE_CXX_FLAGS})
|
||||
|
||||
26
CMakeSettings.json
Normal file
26
CMakeSettings.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "x64-Debug",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "Debug",
|
||||
"inheritEnvironments": [ "msvc_x64_x64" ],
|
||||
"buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\libmdbx\\build\\${name}",
|
||||
"installRoot": "${env.USERPROFILE}\\CMakeBuilds\\libmdbx\\install\\${name}",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": ""
|
||||
},
|
||||
{
|
||||
"name": "x64-Release",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "Release",
|
||||
"inheritEnvironments": [ "msvc_x64_x64" ],
|
||||
"buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\libmdbx\\build\\${name}",
|
||||
"installRoot": "${env.USERPROFILE}\\CMakeBuilds\\libmdbx\\install\\${name}",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
85
ChangeLog.md
85
ChangeLog.md
@@ -1,23 +1,100 @@
|
||||
ChangeLog
|
||||
---------
|
||||
|
||||
## v0.10.2 (in development)
|
||||
## v0.11.x (in development)
|
||||
|
||||
### TODO
|
||||
|
||||
- [Engage an "overlapped I/O" on Windows](https://github.com/erthink/libmdbx/issues/224).
|
||||
- [Simple careful mode for working with corrupted DB](https://github.com/erthink/libmdbx/issues/223).
|
||||
- [Move most of `mdbx_chk` functional to the library API](https://github.com/erthink/libmdbx/issues/204).
|
||||
- [Replace SRW-lock on Windows to allow shrink DB with `MDBX_NOTLS` option](https://github.com/erthink/libmdbx/issues/210).
|
||||
- [More flexible support of asynchronous runtime/framework(s)](https://github.com/erthink/libmdbx/issues/200).
|
||||
- [Migration guide from LMDB to MDBX](https://github.com/erthink/libmdbx/issues/199).
|
||||
- [Get rid of dirty-pages list in MDBX_WRITEMAP mode](https://github.com/erthink/libmdbx/issues/193).
|
||||
- [Large/Overflow pages accounting for dirty-room](https://github.com/erthink/libmdbx/issues/192).
|
||||
- [C++ Buffer issue](https://github.com/erthink/libmdbx/issues/191).
|
||||
- [Support for RAW devices](https://github.com/erthink/libmdbx/issues/124).
|
||||
- [Test framework issue](https://github.com/erthink/libmdbx/issues/127).
|
||||
- [Support MessagePack for Keys & Values](https://github.com/erthink/libmdbx/issues/115).
|
||||
- [Engage new terminology](https://github.com/erthink/libmdbx/issues/137).
|
||||
- Finalize C++ API (few typos and trivia bugs are still likely for now).
|
||||
- Finalize C++ API (few typos and bugs are still maybe for now).
|
||||
- Packages for [Astra Linux](https://astralinux.ru/), [ALT Linux](https://www.altlinux.org/), [ROSA Linux](https://www.rosalinux.ru/), etc.
|
||||
|
||||
|
||||
## v0.10.3 at 2021-08-27
|
||||
|
||||
Acknowledgements:
|
||||
|
||||
- [Francisco Vallarino](https://github.com/fjvallarino) for [Haskell bindings for libmdbx](https://hackage.haskell.org/package/libmdbx).
|
||||
- [Alex Sharov](https://github.com/AskAlexSharov) for reporting and testing.
|
||||
- [Andrea Lanfranchi](https://github.com/AndreaLanfranchi) for contributing.
|
||||
|
||||
Extensions and improvements:
|
||||
|
||||
- Added `cursor::erase()` overloads for `key` and for `key-value`.
|
||||
- Resolve minor Coverity Scan issues (no fixes but some hint/comment were added).
|
||||
- Resolve minor UndefinedBehaviorSanitizer issues (no fixes but some workaround were added).
|
||||
|
||||
Fixes:
|
||||
|
||||
- Always setup `madvise` while opening DB (fixes https://github.com/erthink/libmdbx/issues/231).
|
||||
- Fixed checking legacy `P_DIRTY` flag (`0x10`) for nested/sub-pages.
|
||||
|
||||
Minors:
|
||||
|
||||
- Fixed getting revision number from middle of history during amalgamation (GNU Makefile).
|
||||
- Fixed search GCC tools for LTO (CMake scripts).
|
||||
- Fixed/reorder dirs list for search CLANG tools for LTO (CMake scripts).
|
||||
- Fixed/workarounds for CLANG < 9.x
|
||||
- Fixed CMake warning about compatibility with 3.8.2
|
||||
|
||||
|
||||
## v0.10.2 at 2021-07-26
|
||||
|
||||
Acknowledgements:
|
||||
|
||||
- [Alex Sharov](https://github.com/AskAlexSharov) for reporting and testing.
|
||||
- [Andrea Lanfranchi](https://github.com/AndreaLanfranchi) for reporting bugs.
|
||||
- [Lionel Debroux](https://github.com/debrouxl) for fuzzing tests and reporting bugs.
|
||||
- [Sergey Fedotov](https://github.com/SergeyFromHell/) for [`node-mdbx` NodeJS bindings](https://www.npmjs.com/package/node-mdbx).
|
||||
- [Kris Zyp](https://github.com/kriszyp) for [`lmdbx-store` NodeJS bindings](https://github.com/kriszyp/lmdbx-store).
|
||||
- [Noel Kuntze](https://github.com/Thermi) for [draft Python bindings](https://github.com/erthink/libmdbx/commits/python-bindings).
|
||||
|
||||
New features, extensions and improvements:
|
||||
|
||||
- [Allow to predefine/override `MDBX_BUILD_TIMESTAMP` for builds reproducibility](https://github.com/erthink/libmdbx/issues/201).
|
||||
- Added options support for `long-stochastic` script.
|
||||
- Avoided `MDBX_TXN_FULL` error for large transactions when possible.
|
||||
- The `MDBX_READERS_LIMIT` increased to `32767`.
|
||||
- Raise `MDBX_TOO_LARGE` under Valgrind/ASAN if being opened DB is 100 larger than RAM (to avoid hangs and OOM).
|
||||
- Minimized the size of poisoned/unpoisoned regions to avoid Valgrind/ASAN stuck.
|
||||
- Added more workarounds for QEMU for testing builds for 32-bit platforms, Alpha and Sparc architectures.
|
||||
- `mdbx_chk` now skips iteration & checking of DB' records if corresponding page-tree is corrupted (to avoid `SIGSEGV`, ASAN failures, etc).
|
||||
- Added more checks for [rare/fuzzing corruption cases](https://github.com/erthink/libmdbx/issues/217).
|
||||
|
||||
Backward compatibility break:
|
||||
|
||||
- Use file `VERSION.txt` for version information instead of `VERSION` to avoid collision with `#include <version>`.
|
||||
- Rename `slice::from/to_FOO_bytes()` to `slice::envisage_from/to_FOO_length()'.
|
||||
- Rename `MDBX_TEST_EXTRA` make's variable to `MDBX_SMOKE_EXTRA`.
|
||||
- Some details of the C++ API have been changed for subsequent freezing.
|
||||
|
||||
Fixes:
|
||||
|
||||
- Fixed excess meta-pages checks in case `mdbx_chk` is called to check the DB for a specific meta page and thus could prevent switching to the selected meta page, even if the check passed without errors.
|
||||
- Fixed [recursive use of SRW-lock on Windows cause by `MDBX_NOTLS` option](https://github.com/erthink/libmdbx/issues/203).
|
||||
- Fixed [log a warning during a new DB creation](https://github.com/erthink/libmdbx/issues/205).
|
||||
- Fixed [false-negative `mdbx_cursor_eof()` result](https://github.com/erthink/libmdbx/issues/207).
|
||||
- Fixed [`make install` with non-GNU `install` utility (OSX, BSD)](https://github.com/erthink/libmdbx/issues/208).
|
||||
- Fixed [installation by `CMake` in special cases by complete use `GNUInstallDirs`'s variables](https://github.com/erthink/libmdbx/issues/209).
|
||||
- Fixed [C++ Buffer issue with `std::string` and alignment](https://github.com/erthink/libmdbx/issues/191).
|
||||
- Fixed `safe64_reset()` for platforms without atomic 64-bit compare-and-swap.
|
||||
- Fixed hang/shutdown on big-endian platforms without `__cxa_thread_atexit()`.
|
||||
- Fixed [using bad meta-pages if DB was partially/recoverable corrupted](https://github.com/erthink/libmdbx/issues/217).
|
||||
- Fixed extra `noexcept` for `buffer::&assign_reference()`.
|
||||
- Fixed `bootid` generation on Windows for case of change system' time.
|
||||
- Fixed [test framework keygen-related issue](https://github.com/erthink/libmdbx/issues/127).
|
||||
|
||||
|
||||
## v0.10.1 at 2021-06-01
|
||||
|
||||
Acknowledgements:
|
||||
|
||||
259
GNUmakefile
259
GNUmakefile
@@ -29,7 +29,8 @@ INSTALL ?= install
|
||||
CC ?= gcc
|
||||
CFLAGS_EXTRA ?=
|
||||
LD ?= ld
|
||||
MDBX_BUILD_OPTIONS ?= -DNDEBUG=1
|
||||
MDBX_BUILD_OPTIONS ?=-DNDEBUG=1
|
||||
MDBX_BUILD_TIMESTAMP ?=$(shell date +%Y-%m-%dT%H:%M:%S%z)
|
||||
CFLAGS ?= -std=gnu11 -O2 -g -Wall -Werror -Wextra -Wpedantic -ffunction-sections -fPIC -fvisibility=hidden -pthread -Wno-error=attributes $(CFLAGS_EXTRA)
|
||||
# -Wno-tautological-compare
|
||||
CXX ?= g++
|
||||
@@ -38,9 +39,9 @@ CXXSTD ?= $(eval CXXSTD := $$(shell PROBE=$$$$([ -f mdbx.c++ ] && echo mdbx.c++
|
||||
CXXFLAGS = $(CXXSTD) $(filter-out -std=gnu11,$(CFLAGS))
|
||||
|
||||
# TIP: Try append '--no-as-needed,-lrt' for ability to built with modern glibc, but then use with the old.
|
||||
LIBS ?= $(shell uname | grep -qi SunOS && echo "-lkstat") $(shell uname | grep -qi -e Darwin -e OpenBSD || echo "-lrt") $(shell uname | grep -qi Windows && echo "-lntdll")
|
||||
LIBS ?= $(strip -lm $(shell uname | grep -qi SunOS && echo "-lkstat") $(shell uname | grep -qi -e Darwin -e OpenBSD || echo "-lrt") $(shell uname | grep -qi Windows && echo "-lntdll"))
|
||||
|
||||
LDFLAGS ?= $(shell $(LD) --help 2>/dev/null | grep -q -- --gc-sections && echo '-Wl,--gc-sections,-z,relro,-O1')$(shell $(LD) --help 2>/dev/null | grep -q -- -dead_strip && echo '-Wl,-dead_strip')
|
||||
LDFLAGS ?= $(strip $(shell $(LD) --help 2>/dev/null | grep -q -- --gc-sections && echo '-Wl,--gc-sections,-z,relro,-O1')$(shell $(LD) --help 2>/dev/null | grep -q -- -dead_strip && echo '-Wl,-dead_strip'))
|
||||
EXE_LDFLAGS ?= -pthread
|
||||
|
||||
################################################################################
|
||||
@@ -58,8 +59,9 @@ HEADERS := mdbx.h mdbx.h++
|
||||
LIBRARIES := libmdbx.a libmdbx.$(SO_SUFFIX)
|
||||
TOOLS := mdbx_stat mdbx_copy mdbx_dump mdbx_load mdbx_chk mdbx_drop
|
||||
MANPAGES := mdbx_stat.1 mdbx_copy.1 mdbx_dump.1 mdbx_load.1 mdbx_chk.1 mdbx_drop.1
|
||||
TIP := // TIP:
|
||||
|
||||
.PHONY: all help options lib tools clean install uninstall
|
||||
.PHONY: all help options lib tools clean install uninstall check_buildflags_tag
|
||||
.PHONY: install-strip install-no-strip strip libmdbx mdbx show-options
|
||||
|
||||
ifeq ("$(origin V)", "command line")
|
||||
@@ -72,11 +74,11 @@ endif
|
||||
ifeq ($(MDBX_BUILD_VERBOSE),1)
|
||||
QUIET :=
|
||||
HUSH :=
|
||||
$(info ## TIP: Use `make V=0` for quiet.)
|
||||
$(info $(TIP) Use `make V=0` for quiet.)
|
||||
else
|
||||
QUIET := @
|
||||
HUSH := >/dev/null
|
||||
$(info ## TIP: Use `make V=1` for verbose.)
|
||||
$(info $(TIP) Use `make V=1` for verbose.)
|
||||
endif
|
||||
|
||||
all: show-options $(LIBRARIES) $(TOOLS)
|
||||
@@ -102,19 +104,22 @@ help:
|
||||
@echo " make bench-clean - remove temp database(s) after benchmark"
|
||||
#> dist-cutoff-begin
|
||||
@echo ""
|
||||
@echo " make smoke - fast smoke test"
|
||||
@echo " make test - basic test"
|
||||
@echo " make check - basic test"
|
||||
@echo " make memcheck - build with Valgrind's and test with memcheck tool"
|
||||
@echo " make test-valgrind - build with Valgrind's and test with memcheck tool"
|
||||
@echo " make test-asan - build with AddressSanitizer and test"
|
||||
@echo " make test-leak - build with LeakSanitizer and test"
|
||||
@echo " make test-ubsan - build with UndefinedBehaviourSanitizer and test"
|
||||
@echo " make cross-gcc - run cross-compilation testset"
|
||||
@echo " make cross-qemu - run cross-compilation and execution testset with QEMU"
|
||||
@echo " make check - smoke test with amalgamation and installation checking"
|
||||
@echo " make long-test - execute long test which runs for several weeks, or until you interrupt it"
|
||||
@echo " make memcheck - build with Valgrind's and smoke test with memcheck tool"
|
||||
@echo " make test-valgrind - build with Valgrind's and basic test with memcheck tool"
|
||||
@echo " make test-asan - build with AddressSanitizer and basic test"
|
||||
@echo " make test-leak - build with LeakSanitizer and basic test"
|
||||
@echo " make test-ubsan - build with UndefinedBehaviourSanitizer and basic test"
|
||||
@echo " make cross-gcc - check cross-compilation without test execution"
|
||||
@echo " make cross-qemu - run cross-compilation and execution basic test with QEMU"
|
||||
@echo " make gcc-analyzer - run gcc-analyzer (mostly useless for now)"
|
||||
@echo " make build-test - build test executable(s)"
|
||||
@echo " make test-fault - run transaction owner failure testcase"
|
||||
@echo " make test-singleprocess - run single-process test (used by make cross-qemu)"
|
||||
@echo " make smoke-fault - execute transaction owner failure smoke testcase"
|
||||
@echo " make smoke-singleprocess - execute single-process smoke test"
|
||||
@echo " make test-singleprocess - execute single-process basic test (also used by make cross-qemu)"
|
||||
@echo ""
|
||||
@echo " make dist - build amalgamated source code"
|
||||
@echo " make doxygen - build HTML documentation"
|
||||
@@ -123,12 +128,14 @@ help:
|
||||
#< dist-cutoff-end
|
||||
|
||||
show-options:
|
||||
@echo " MDBX_BUILD_OPTIONS =$(MDBX_BUILD_OPTIONS)"
|
||||
@echo '## TIP: Use `make options` to listing available build options.'
|
||||
@echo " MDBX_BUILD_OPTIONS = $(MDBX_BUILD_OPTIONS)"
|
||||
@echo " MDBX_BUILD_TIMESTAMP = $(MDBX_BUILD_TIMESTAMP)"
|
||||
@echo '$(TIP) Use `make options` to listing available build options.'
|
||||
@echo " CC =`which $(CC)` | `$(CC) --version | head -1`"
|
||||
@echo " CFLAGS =$(CFLAGS)"
|
||||
@echo " CXXFLAGS =$(CXXFLAGS)"
|
||||
@echo " LDFLAGS =$(LDFLAGS) $(LIBS) $(EXE_LDFLAGS)"
|
||||
@echo '## TIP: Use `make help` to listing available targets.'
|
||||
@echo '$(TIP) Use `make help` to listing available targets.'
|
||||
|
||||
options:
|
||||
@echo " INSTALL =$(INSTALL)"
|
||||
@@ -149,7 +156,8 @@ options:
|
||||
@echo " EXE_LDFLAGS =$(EXE_LDFLAGS)"
|
||||
@echo " LIBS =$(LIBS)"
|
||||
@echo ""
|
||||
@echo " MDBX_BUILD_OPTIONS =$(MDBX_BUILD_OPTIONS)"
|
||||
@echo " MDBX_BUILD_OPTIONS = $(MDBX_BUILD_OPTIONS)"
|
||||
@echo " MDBX_BUILD_TIMESTAMP = $(MDBX_BUILD_TIMESTAMP)"
|
||||
@echo ""
|
||||
@echo "## Assortment items for MDBX_BUILD_OPTIONS:"
|
||||
@echo "## Note that the defaults should already be correct for most platforms;"
|
||||
@@ -178,7 +186,17 @@ clean:
|
||||
@echo ' REMOVE ...'
|
||||
$(QUIET)rm -rf $(TOOLS) mdbx_test @* *.[ao] *.[ls]o *.$(SO_SUFFIX) *.dSYM *~ tmp.db/* \
|
||||
*.gcov *.log *.err src/*.o test/*.o mdbx_example dist \
|
||||
config.h src/config.h src/version.c *.tar*
|
||||
config.h src/config.h src/version.c *.tar* buildflags.tag
|
||||
|
||||
MDBX_BUILD_FLAGS =$(strip $(MDBX_BUILD_OPTIONS) $(CXXSTD) $(CFLAGS) $(LDFLAGS) $(LIBS))
|
||||
check_buildflags_tag:
|
||||
$(QUIET)if [ "$(MDBX_BUILD_FLAGS)" != "$$(cat buildflags.tag 2>&1)" ]; then \
|
||||
echo -n " CLEAN for build with specified flags..." && \
|
||||
$(MAKE) IOARENA=false CXXSTD= -s clean >/dev/null && echo " Ok" && \
|
||||
echo '$(MDBX_BUILD_FLAGS)' > buildflags.tag; \
|
||||
fi
|
||||
|
||||
buildflags.tag: check_buildflags_tag
|
||||
|
||||
libmdbx.a: mdbx-static.o mdbx++-static.o
|
||||
@echo ' AR $@'
|
||||
@@ -196,13 +214,13 @@ ifeq ($(wildcard mdbx.c),mdbx.c)
|
||||
# Amalgamated source code, i.e. distributed after `make dist`
|
||||
MAN_SRCDIR := man1/
|
||||
|
||||
config.h: mdbx.c $(lastword $(MAKEFILE_LIST))
|
||||
config.h: buildflags.tag mdbx.c $(lastword $(MAKEFILE_LIST))
|
||||
@echo ' MAKE $@'
|
||||
$(QUIET)(echo '#define MDBX_BUILD_TIMESTAMP "$(shell date +%Y-%m-%dT%H:%M:%S%z)"' \
|
||||
&& echo '#define MDBX_BUILD_FLAGS "$(CXXSTD) $(CFLAGS) $(LDFLAGS) $(LIBS)"' \
|
||||
$(QUIET)(echo '#define MDBX_BUILD_TIMESTAMP "$(MDBX_BUILD_TIMESTAMP)"' \
|
||||
&& echo "#define MDBX_BUILD_FLAGS \"$$(cat buildflags.tag)\"" \
|
||||
&& echo '#define MDBX_BUILD_COMPILER "$(shell (LC_ALL=C $(CC) --version || echo 'Please use GCC or CLANG compatible compiler') | head -1)"' \
|
||||
&& echo '#define MDBX_BUILD_TARGET "$(shell set -o pipefail; (LC_ALL=C $(CC) -v 2>&1 | grep -i '^Target:' | cut -d ' ' -f 2- || (LC_ALL=C $(CC) --version | grep -qi e2k && echo E2K) || echo 'Please use GCC or CLANG compatible compiler') | head -1)"' \
|
||||
) > $@
|
||||
) >$@
|
||||
|
||||
mdbx-dylib.o: config.h mdbx.c mdbx.h $(lastword $(MAKEFILE_LIST))
|
||||
@echo ' CC $@'
|
||||
@@ -229,9 +247,9 @@ else
|
||||
################################################################################
|
||||
# Plain (non-amalgamated) sources with test
|
||||
|
||||
.PHONY: build-test check cross-gcc cross-qemu dist doxygen gcc-analyzer
|
||||
.PHONY: reformat release-assets tags test test-asan test-fault test-leak
|
||||
.PHONY: test-singleprocess test-ubsan test-valgrind memcheck
|
||||
.PHONY: build-test build-test-with-valgrind check cross-gcc cross-qemu dist doxygen gcc-analyzer long-test
|
||||
.PHONY: reformat release-assets tags smoke test test-asan smoke-fault test-leak
|
||||
.PHONY: smoke-singleprocess test-singleprocess test-ubsan test-valgrind memcheck
|
||||
|
||||
define uname2osal
|
||||
case "$(UNAME)" in
|
||||
@@ -247,7 +265,7 @@ define uname2titer
|
||||
esac
|
||||
endef
|
||||
|
||||
DIST_EXTRA := LICENSE README.md CMakeLists.txt GNUmakefile Makefile ChangeLog.md VERSION config.h.in ntdll.def \
|
||||
DIST_EXTRA := LICENSE README.md CMakeLists.txt GNUmakefile Makefile ChangeLog.md VERSION.txt config.h.in ntdll.def \
|
||||
$(addprefix man1/, $(MANPAGES)) cmake/compiler.cmake cmake/profile.cmake cmake/utils.cmake
|
||||
DIST_SRC := mdbx.h mdbx.h++ mdbx.c mdbx.c++ $(addsuffix .c, $(TOOLS))
|
||||
|
||||
@@ -271,72 +289,90 @@ reformat:
|
||||
fi
|
||||
|
||||
MAN_SRCDIR := src/man1/
|
||||
ALLOY_DEPS := $(wildcard src/*)
|
||||
ALLOY_DEPS := $(shell git ls-files src/)
|
||||
git_DIR := $(shell if [ -d .git ]; then echo .git; elif [ -s .git -a -f .git ]; then grep '^gitdir: ' .git | cut -d ':' -f 2; else echo git_directory_is_absent; fi)
|
||||
MDBX_GIT_VERSION = $(shell set -o pipefail; git describe --tags 2>&- | sed -n 's|^v*\([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\)\(.*\)|\1|p' || echo 'Please fetch tags and/or use non-obsolete git version')
|
||||
MDBX_GIT_REVISION = $(shell set -o pipefail; git rev-list --count HEAD ^`git tag --sort=-version:refname 2>&- | sed -n '/^\(v[0-9]\+\.[0-9]\+\.[0-9]\+\)*/p;q' || echo 'git_tag_with_sort_was_failed'` 2>&- || echo 'Please use non-obsolete git version')
|
||||
MDBX_GIT_REVISION = $(shell set -o pipefail; git rev-list `git describe --tags --abbrev=0`..HEAD --count 2>&- || echo 'Please fetch tags and/or use non-obsolete git version')
|
||||
MDBX_GIT_TIMESTAMP = $(shell git show --no-patch --format=%cI HEAD 2>&- || echo 'Please install latest get version')
|
||||
MDBX_GIT_DESCRIBE = $(shell git describe --tags --long --dirty=-dirty 2>&- || echo 'Please fetch tags and/or install non-obsolete git version')
|
||||
MDBX_VERSION_SUFFIX = $(shell set -o pipefail; echo -n '$(MDBX_GIT_DESCRIBE)' | tr -c -s '[a-zA-Z0-9]' _)
|
||||
MDBX_BUILD_SOURCERY = $(shell set -o pipefail; $(MAKE) CXXSTD= -s src/version.c >/dev/null && (openssl dgst -r -sha256 src/version.c || sha256sum src/version.c || shasum -a 256 src/version.c) 2>/dev/null | cut -d ' ' -f 1 || (echo 'Please install openssl or sha256sum or shasum' >&2 && echo sha256sum_is_no_available))_$(MDBX_VERSION_SUFFIX)
|
||||
MDBX_BUILD_SOURCERY = $(shell set -o pipefail; $(MAKE) IOARENA=false CXXSTD= -s src/version.c >/dev/null && (openssl dgst -r -sha256 src/version.c || sha256sum src/version.c || shasum -a 256 src/version.c) 2>/dev/null | cut -d ' ' -f 1 || (echo 'Please install openssl or sha256sum or shasum' >&2 && echo sha256sum_is_no_available))_$(MDBX_VERSION_SUFFIX)
|
||||
MDBX_DIST_DIR = libmdbx-$(MDBX_VERSION_SUFFIX)
|
||||
|
||||
# Extra options mdbx_test utility
|
||||
MDBX_TEST_EXTRA ?=
|
||||
MDBX_SMOKE_EXTRA ?=
|
||||
|
||||
check: test dist
|
||||
check: DESTDIR = $(shell pwd)/@check-install
|
||||
check: test dist install
|
||||
|
||||
test: build-test
|
||||
@echo ' RUNNING `mdbx_test basic`...'
|
||||
smoke: build-test
|
||||
@echo ' SMOKE `mdbx_test basic`...'
|
||||
$(QUIET)rm -f $(TEST_DB) $(TEST_LOG).gz && (set -o pipefail; \
|
||||
(./mdbx_test --table=+data.integer --keygen.split=29 --datalen.min=min --datalen.max=max --progress --console=no --repeat=$(TEST_ITER) --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_TEST_EXTRA) basic && \
|
||||
./mdbx_test --mode=-writemap,-nosync-safe,-lifo --progress --console=no --repeat=12 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_TEST_EXTRA) basic) \
|
||||
| tee >(gzip --stdout > $(TEST_LOG).gz) | tail -n 42) \
|
||||
(./mdbx_test --table=+data.integer --keygen.split=29 --datalen.min=min --datalen.max=max --progress --console=no --repeat=$(TEST_ITER) --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_SMOKE_EXTRA) basic && \
|
||||
./mdbx_test --mode=-writemap,-nosync-safe,-lifo --progress --console=no --repeat=12 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_SMOKE_EXTRA) basic) \
|
||||
| tee >(gzip --stdout >$(TEST_LOG).gz) | tail -n 42) \
|
||||
&& ./mdbx_chk -vvn $(TEST_DB) && ./mdbx_chk -vvn $(TEST_DB)-copy
|
||||
|
||||
test-singleprocess: all mdbx_test
|
||||
@echo ' RUNNING `mdbx_test --nested`...'
|
||||
smoke-singleprocess: build-test
|
||||
@echo ' SMOKE `mdbx_test --nested`...'
|
||||
$(QUIET)rm -f $(TEST_DB) $(TEST_LOG).gz && (set -o pipefail; \
|
||||
(./mdbx_test --table=+data.integer --keygen.split=29 --datalen.min=min --datalen.max=max --progress --console=no --repeat=42 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_TEST_EXTRA) --hill && \
|
||||
(./mdbx_test --table=+data.integer --keygen.split=29 --datalen.min=min --datalen.max=max --progress --console=no --repeat=42 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_SMOKE_EXTRA) --hill && \
|
||||
./mdbx_test --progress --console=no --repeat=2 --pathname=$(TEST_DB) --dont-cleanup-before --dont-cleanup-after --copy && \
|
||||
./mdbx_test --mode=-writemap,-nosync-safe,-lifo --progress --console=no --repeat=42 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_TEST_EXTRA) --nested) \
|
||||
| tee >(gzip --stdout > $(TEST_LOG).gz) | tail -n 42) \
|
||||
./mdbx_test --mode=-writemap,-nosync-safe,-lifo --progress --console=no --repeat=42 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_SMOKE_EXTRA) --nested) \
|
||||
| tee >(gzip --stdout >$(TEST_LOG).gz) | tail -n 42) \
|
||||
&& ./mdbx_chk -vvn $(TEST_DB) && ./mdbx_chk -vvn $(TEST_DB)-copy
|
||||
|
||||
test-fault: all mdbx_test
|
||||
@echo ' RUNNING `mdbx_test --inject-writefault=42 basic`...'
|
||||
$(QUIET)rm -f $(TEST_DB) $(TEST_LOG).gz && (set -o pipefail; ./mdbx_test --progress --console=no --pathname=$(TEST_DB) --inject-writefault=42 --dump-config --dont-cleanup-after $(MDBX_TEST_EXTRA) basic \
|
||||
| tee >(gzip --stdout > $(TEST_LOG).gz) | tail -n 42) \
|
||||
smoke-fault: build-test
|
||||
@echo ' SMOKE `mdbx_test --inject-writefault=42 basic`...'
|
||||
$(QUIET)rm -f $(TEST_DB) $(TEST_LOG).gz && (set -o pipefail; ./mdbx_test --progress --console=no --pathname=$(TEST_DB) --inject-writefault=42 --dump-config --dont-cleanup-after $(MDBX_SMOKE_EXTRA) basic \
|
||||
| tee >(gzip --stdout >$(TEST_LOG).gz) | tail -n 42) \
|
||||
; ./mdbx_chk -vvnw $(TEST_DB) && ([ ! -e $(TEST_DB)-copy ] || ./mdbx_chk -vvn $(TEST_DB)-copy)
|
||||
|
||||
VALGRIND=valgrind --trace-children=yes --log-file=valgrind-%p.log --leak-check=full --track-origins=yes --error-exitcode=42 --suppressions=test/valgrind_suppress.txt
|
||||
memcheck test-valgrind:
|
||||
@echo " RUNNING \`mdbx_test basic\` under Valgrind's memcheck..."
|
||||
$(QUIET)$(MAKE) CXXSTD= clean && $(MAKE) CXXSTD=$(CXXSTD) CFLAGS_EXTRA="-Ofast -DMDBX_USE_VALGRIND" build-test && \
|
||||
rm -f valgrind-*.log $(TEST_DB) $(TEST_LOG).gz && (set -o pipefail; ( \
|
||||
$(VALGRIND) ./mdbx_test --table=+data.integer --keygen.split=29 --datalen.min=min --datalen.max=max --progress --console=no --repeat=2 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_TEST_EXTRA) basic && \
|
||||
test: build-test
|
||||
@echo ' RUNNING `test/long_stochastic.sh --loops 2`...'
|
||||
$(QUIET)test/long_stochastic.sh --loops 2 --db-upto-mb 256 --skip-make >$(TEST_LOG)
|
||||
|
||||
long-test: build-test
|
||||
@echo ' RUNNING `test/long_stochastic.sh --loops 42`...'
|
||||
$(QUIET)test/long_stochastic.sh --loops 42 --db-upto-mb 1024 --skip-make
|
||||
|
||||
test-singleprocess: build-test
|
||||
@echo ' RUNNING `test/long_stochastic.sh --single --loops 2`...'
|
||||
$(QUIET)test/long_stochastic.sh --single --loops 2 --db-upto-mb 256 --skip-make >$(TEST_LOG)
|
||||
|
||||
test-valgrind: CFLAGS_EXTRA=-Ofast -DMDBX_USE_VALGRIND
|
||||
test-valgrind: build-test
|
||||
@echo ' RUNNING `test/long_stochastic.sh --with-valgrind --loops 2`...'
|
||||
$(QUIET)test/long_stochastic.sh --with-valgrind --loops 2 --db-upto-mb 256 --skip-make >$(TEST_LOG)
|
||||
|
||||
memcheck: VALGRIND=valgrind --trace-children=yes --log-file=valgrind-%p.log --leak-check=full --track-origins=yes --error-exitcode=42 --suppressions=test/valgrind_suppress.txt
|
||||
memcheck: CFLAGS_EXTRA=-Ofast -DMDBX_USE_VALGRIND
|
||||
memcheck: build-test
|
||||
@echo " SMOKE \`mdbx_test basic\` under Valgrind's memcheck..."
|
||||
$(QUIET)rm -f valgrind-*.log $(TEST_DB) $(TEST_LOG).gz && (set -o pipefail; ( \
|
||||
$(VALGRIND) ./mdbx_test --table=+data.integer --keygen.split=29 --datalen.min=min --datalen.max=max --progress --console=no --repeat=2 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_SMOKE_EXTRA) basic && \
|
||||
$(VALGRIND) ./mdbx_test --progress --console=no --pathname=$(TEST_DB) --dont-cleanup-before --dont-cleanup-after --copy && \
|
||||
$(VALGRIND) ./mdbx_test --mode=-writemap,-nosync-safe,-lifo --progress --console=no --repeat=4 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_TEST_EXTRA) basic && \
|
||||
$(VALGRIND) ./mdbx_test --mode=-writemap,-nosync-safe,-lifo --progress --console=no --repeat=4 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_SMOKE_EXTRA) basic && \
|
||||
$(VALGRIND) ./mdbx_chk -vvn $(TEST_DB) && \
|
||||
$(VALGRIND) ./mdbx_chk -vvn $(TEST_DB)-copy \
|
||||
) | tee >(gzip --stdout > $(TEST_LOG).gz) | tail -n 42)
|
||||
) | tee >(gzip --stdout >$(TEST_LOG).gz) | tail -n 42)
|
||||
|
||||
gcc-analyzer:
|
||||
@echo ' RE-BUILD with `-fanalyzer` option...'
|
||||
@echo "NOTE: There a lot of false-positive warnings at 2020-05-01 by pre-release GCC-10 (20200328, Red Hat 10.0.1-0.11)"
|
||||
$(QUIET)$(MAKE) CXXSTD=$(CXXSTD) --always-make CFLAGS_EXTRA="-Og -fanalyzer -Wno-error" build-test
|
||||
$(QUIET)$(MAKE) IOARENA=false CXXSTD=$(CXXSTD) CFLAGS_EXTRA="-Og -fanalyzer -Wno-error" build-test
|
||||
|
||||
test-ubsan:
|
||||
@echo ' RE-CHECK with `-fsanitize=undefined` option...'
|
||||
$(QUIET)$(MAKE) CXXSTD= clean && $(MAKE) CXXSTD=$(CXXSTD) CFLAGS_EXTRA="-Ofast -fsanitize=undefined -fsanitize-undefined-trap-on-error" check
|
||||
@echo ' RE-TEST with `-fsanitize=undefined` option...'
|
||||
$(QUIET)$(MAKE) IOARENA=false CXXSTD=$(CXXSTD) CFLAGS_EXTRA="-Ofast -fsanitize=undefined -fsanitize-undefined-trap-on-error" test
|
||||
|
||||
test-asan:
|
||||
@echo ' RE-CHECK with `-fsanitize=address` option...'
|
||||
$(QUIET)$(MAKE) CXXSTD= clean && $(MAKE) CXXSTD=$(CXXSTD) CFLAGS_EXTRA="-Os -fsanitize=address" check
|
||||
@echo ' RE-TEST with `-fsanitize=address` option...'
|
||||
$(QUIET)$(MAKE) IOARENA=false CXXSTD=$(CXXSTD) CFLAGS_EXTRA="-Os -fsanitize=address" test
|
||||
|
||||
test-leak:
|
||||
@echo ' RE-CHECK with `-fsanitize=leak` option...'
|
||||
$(QUIET)$(MAKE) CXXSTD= clean && $(MAKE) CXXSTD=$(CXXSTD) CFLAGS_EXTRA="-fsanitize=leak" check
|
||||
@echo ' RE-TEST with `-fsanitize=leak` option...'
|
||||
$(QUIET)$(MAKE) IOARENA=false CXXSTD=$(CXXSTD) CFLAGS_EXTRA="-fsanitize=leak" test
|
||||
|
||||
mdbx_example: mdbx.h example/example-mdbx.c libmdbx.$(SO_SUFFIX)
|
||||
@echo ' CC+LD $@'
|
||||
@@ -345,7 +381,7 @@ mdbx_example: mdbx.h example/example-mdbx.c libmdbx.$(SO_SUFFIX)
|
||||
build-test: all mdbx_example mdbx_test
|
||||
|
||||
define test-rule
|
||||
$(patsubst %.cc,%.o,$(1)): $(1) $(TEST_INC) mdbx.h $(lastword $(MAKEFILE_LIST))
|
||||
$(patsubst %.cc,%.o,$(1)): $(1) $(TEST_INC) $(HEADERS) $(lastword $(MAKEFILE_LIST))
|
||||
@echo ' CC $$@'
|
||||
$(QUIET)$$(CXX) $$(CXXFLAGS) $$(MDBX_BUILD_OPTIONS) -c $(1) -o $$@
|
||||
|
||||
@@ -384,16 +420,16 @@ src/version.c: src/version.c.in $(lastword $(MAKEFILE_LIST)) $(git_DIR)/HEAD $(g
|
||||
-e "s|\$${MDBX_VERSION_MINOR}|$(shell echo '$(MDBX_GIT_VERSION)' | cut -d . -f 2)|" \
|
||||
-e "s|\$${MDBX_VERSION_RELEASE}|$(shell echo '$(MDBX_GIT_VERSION)' | cut -d . -f 3)|" \
|
||||
-e "s|\$${MDBX_VERSION_REVISION}|$(MDBX_GIT_REVISION)|" \
|
||||
src/version.c.in > $@
|
||||
src/version.c.in >$@
|
||||
|
||||
src/config.h: src/version.c $(lastword $(MAKEFILE_LIST))
|
||||
src/config.h: buildflags.tag src/version.c $(lastword $(MAKEFILE_LIST))
|
||||
@echo ' MAKE $@'
|
||||
$(QUIET)(echo '#define MDBX_BUILD_TIMESTAMP "$(shell date +%Y-%m-%dT%H:%M:%S%z)"' \
|
||||
&& echo '#define MDBX_BUILD_FLAGS "$(CXXSTD) $(CFLAGS) $(LDFLAGS) $(LIBS)"' \
|
||||
$(QUIET)(echo '#define MDBX_BUILD_TIMESTAMP "$(MDBX_BUILD_TIMESTAMP)"' \
|
||||
&& echo "#define MDBX_BUILD_FLAGS \"$$(cat buildflags.tag)\"" \
|
||||
&& echo '#define MDBX_BUILD_COMPILER "$(shell (LC_ALL=C $(CC) --version || echo 'Please use GCC or CLANG compatible compiler') | head -1)"' \
|
||||
&& echo '#define MDBX_BUILD_TARGET "$(shell set -o pipefail; (LC_ALL=C $(CC) -v 2>&1 | grep -i '^Target:' | cut -d ' ' -f 2- || (LC_ALL=C $(CC) --version | grep -qi e2k && echo E2K) || echo 'Please use GCC or CLANG compatible compiler') | head -1)"' \
|
||||
&& echo '#define MDBX_BUILD_SOURCERY $(MDBX_BUILD_SOURCERY)' \
|
||||
) > $@
|
||||
) >$@
|
||||
|
||||
mdbx-dylib.o: src/config.h src/version.c src/alloy.c $(ALLOY_DEPS) $(lastword $(MAKEFILE_LIST))
|
||||
@echo ' CC $@'
|
||||
@@ -414,32 +450,32 @@ docs/Doxyfile: docs/Doxyfile.in src/version.c
|
||||
-e "s|\$${MDBX_VERSION_MINOR}|$(shell echo '$(MDBX_GIT_VERSION)' | cut -d . -f 2)|" \
|
||||
-e "s|\$${MDBX_VERSION_RELEASE}|$(shell echo '$(MDBX_GIT_VERSION)' | cut -d . -f 3)|" \
|
||||
-e "s|\$${MDBX_VERSION_REVISION}|$(MDBX_GIT_REVISION)|" \
|
||||
docs/Doxyfile.in > $@
|
||||
docs/Doxyfile.in >$@
|
||||
|
||||
define md-extract-section
|
||||
docs/__$(1).md: $(2)
|
||||
@echo ' EXTRACT $1'
|
||||
$(QUIET)sed -n '/<!-- section-begin $(1) -->/,/<!-- section-end -->/p' $$< > $$@ && test -s $$@
|
||||
$(QUIET)sed -n '/<!-- section-begin $(1) -->/,/<!-- section-end -->/p' $$< >$$@ && test -s $$@
|
||||
|
||||
endef
|
||||
$(foreach section,overview mithril characteristics improvements history usage performance bindings,$(eval $(call md-extract-section,$(section),README.md)))
|
||||
|
||||
docs/overall.md: docs/__overview.md docs/_toc.md docs/__mithril.md docs/__history.md AUTHORS LICENSE
|
||||
@echo ' MAKE $@'
|
||||
$(QUIET)echo -e "\\mainpage Overall\n\\section brief Brief" | cat - $(filter %.md, $^) > $@ && echo -e "\n\n\nLicense\n=======\n" | cat AUTHORS - LICENSE >> $@
|
||||
$(QUIET)echo -e "\\mainpage Overall\n\\section brief Brief" | cat - $(filter %.md, $^) >$@ && echo -e "\n\n\nLicense\n=======\n" | cat AUTHORS - LICENSE >>$@
|
||||
|
||||
docs/intro.md: docs/_preface.md docs/__characteristics.md docs/__improvements.md docs/_restrictions.md docs/__performance.md
|
||||
@echo ' MAKE $@'
|
||||
$(QUIET)cat $^ | sed 's/^Performance comparison$$/Performance comparison {#performance}/' > $@
|
||||
$(QUIET)cat $^ | sed 's/^Performance comparison$$/Performance comparison {#performance}/' >$@
|
||||
|
||||
docs/usage.md: docs/__usage.md docs/_starting.md docs/__bindings.md
|
||||
@echo ' MAKE $@'
|
||||
$(QUIET)echo -e "\\page usage Usage\n\\section getting Building & Embedding" | cat - $^ | sed 's/^Bindings$$/Bindings {#bindings}/' > $@
|
||||
$(QUIET)echo -e "\\page usage Usage\n\\section getting Building & Embedding" | cat - $^ | sed 's/^Bindings$$/Bindings {#bindings}/' >$@
|
||||
|
||||
doxygen: docs/Doxyfile docs/overall.md docs/intro.md docs/usage.md mdbx.h mdbx.h++ src/options.h ChangeLog.md AUTHORS LICENSE
|
||||
@echo ' RUNNING doxygen...'
|
||||
$(QUIET)rm -rf docs/html && \
|
||||
cat mdbx.h | tr '\n' '\r' | sed -e 's/LIBMDBX_INLINE_API\s*(\s*\([^,]\+\),\s*\([^,]\+\),\s*(\s*\([^)]\+\)\s*)\s*)\s*{/inline \1 \2(\3) {/g' | tr '\r' '\n' > docs/mdbx.h && \
|
||||
cat mdbx.h | tr '\n' '\r' | sed -e 's/LIBMDBX_INLINE_API\s*(\s*\([^,]\+\),\s*\([^,]\+\),\s*(\s*\([^)]\+\)\s*)\s*)\s*{/inline \1 \2(\3) {/g' | tr '\r' '\n' >docs/mdbx.h && \
|
||||
cp mdbx.h++ src/options.h ChangeLog.md docs/ && (cd docs && doxygen Doxyfile $(HUSH)) && cp AUTHORS LICENSE docs/html/
|
||||
|
||||
mdbx++-dylib.o: src/config.h src/mdbx.c++ mdbx.h mdbx.h++ $(lastword $(MAKEFILE_LIST))
|
||||
@@ -462,15 +498,14 @@ release-assets: libmdbx-sources-$(MDBX_VERSION_SUFFIX).tar.gz libmdbx-sources-$(
|
||||
|
||||
dist-checked.tag: $(addprefix dist/, $(DIST_SRC) $(DIST_EXTRA))
|
||||
@echo -n ' VERIFY amalgamated sources...'
|
||||
$(QUIET)rm -rf $@ \
|
||||
$(QUIET)rm -rf $@ dist/@tmp-shared_internals.inc \
|
||||
&& if grep -R "define xMDBX_ALLOY" dist | grep -q MDBX_BUILD_SOURCERY; then echo "sed output is WRONG!" >&2; exit 2; fi \
|
||||
&& rm -rf dist-check && cp -r -p dist dist-check && ($(MAKE) -C dist-check > dist-check/build.log 2> dist-check/build.err || (cat dist-check/build.err && exit 1)) \
|
||||
&& touch $@ || (echo " FAILED! See dist-check/build.err" >&2; exit 2) && echo " Ok" \
|
||||
&& rm dist/@tmp-shared_internals.inc
|
||||
&& rm -rf dist-check && cp -r -p dist dist-check && ($(MAKE) IOARENA=false CXXSTD=$(CXXSTD) -C dist-check >dist-check.log 2>dist-check.err || (cat dist-check.err && exit 1)) \
|
||||
&& touch $@ || (echo " FAILED! See dist-check.log and dist-check.err" >&2; exit 2) && echo " Ok"
|
||||
|
||||
libmdbx-sources-$(MDBX_VERSION_SUFFIX).tar.gz: dist-checked.tag
|
||||
@echo ' CREATE $@'
|
||||
$(QUIET)$(TAR) -c $(shell LC_ALL=C $(TAR) --help | grep -q -- '--owner' && echo '--owner=0 --group=0') -f - -C dist $(DIST_SRC) $(DIST_EXTRA) | gzip -c -9 > $@
|
||||
$(QUIET)$(TAR) -c $(shell LC_ALL=C $(TAR) --help | grep -q -- '--owner' && echo '--owner=0 --group=0') -f - -C dist $(DIST_SRC) $(DIST_EXTRA) | gzip -c -9 >$@
|
||||
|
||||
libmdbx-sources-$(MDBX_VERSION_SUFFIX).zip: dist-checked.tag
|
||||
@echo ' CREATE $@'
|
||||
@@ -487,26 +522,26 @@ dist/mdbx.h++: mdbx.h++ src/version.c $(lastword $(MAKEFILE_LIST))
|
||||
dist/@tmp-shared_internals.inc: src/version.c $(ALLOY_DEPS) $(lastword $(MAKEFILE_LIST))
|
||||
@echo ' ALLOYING...'
|
||||
$(QUIET)mkdir -p dist \
|
||||
&& echo '#define xMDBX_ALLOY 1' > dist/@tmp-sed.inc && echo '#define MDBX_BUILD_SOURCERY $(MDBX_BUILD_SOURCERY)' >> dist/@tmp-sed.inc \
|
||||
&& echo '#define xMDBX_ALLOY 1' >dist/@tmp-sed.inc && echo '#define MDBX_BUILD_SOURCERY $(MDBX_BUILD_SOURCERY)' >>dist/@tmp-sed.inc \
|
||||
&& sed \
|
||||
-e '/#pragma once/r dist/@tmp-sed.inc' \
|
||||
-e 's|#include "../mdbx.h"|@INCLUDE "mdbx.h"|' \
|
||||
-e '/#include "defs.h"/r src/defs.h' \
|
||||
-e '/#include "osal.h"/r src/osal.h' \
|
||||
-e '/#include "options.h"/r src/options.h' \
|
||||
src/internals.h > $@ \
|
||||
src/internals.h >$@ \
|
||||
&& rm -rf dist/@tmp-sed.inc
|
||||
|
||||
dist/mdbx.c: dist/@tmp-shared_internals.inc $(lastword $(MAKEFILE_LIST))
|
||||
@echo ' MAKE $@'
|
||||
$(QUIET)mkdir -p dist && (cat dist/@tmp-shared_internals.inc \
|
||||
&& cat src/core.c src/osal.c src/version.c src/lck-windows.c src/lck-posix.c \
|
||||
) | grep -v -e '#include "' -e '#pragma once' | sed 's|@INCLUDE|#include|' > $@
|
||||
) | grep -v -e '#include "' -e '#pragma once' | sed 's|@INCLUDE|#include|' >$@
|
||||
|
||||
dist/mdbx.c++: dist/@tmp-shared_internals.inc src/mdbx.c++ $(lastword $(MAKEFILE_LIST))
|
||||
@echo ' MAKE $@'
|
||||
$(QUIET)mkdir -p dist && (cat dist/@tmp-shared_internals.inc && cat src/mdbx.c++) \
|
||||
| grep -v -e '#include "' -e '#pragma once' | sed 's|@INCLUDE|#include|;s|"mdbx.h"|"mdbx.h++"|' > $@
|
||||
| grep -v -e '#include "' -e '#pragma once' | sed 's|@INCLUDE|#include|;s|"mdbx.h"|"mdbx.h++"|' >$@
|
||||
|
||||
define dist-tool-rule
|
||||
dist/$(1).c: src/$(1).c src/wingetopt.h src/wingetopt.c \
|
||||
@@ -517,7 +552,7 @@ dist/$(1).c: src/$(1).c src/wingetopt.h src/wingetopt.c \
|
||||
-e '/#include "wingetopt.h"/r src/wingetopt.c' \
|
||||
src/$(1).c \
|
||||
| grep -v -e '#include "' -e '#pragma once' -e '#define xMDBX_ALLOY' \
|
||||
| sed 's|@INCLUDE|#include|' > $$@
|
||||
| sed 's|@INCLUDE|#include|' >$$@
|
||||
|
||||
endef
|
||||
$(foreach file,$(TOOLS),$(eval $(call dist-tool-rule,$(file))))
|
||||
@@ -525,14 +560,14 @@ $(foreach file,$(TOOLS),$(eval $(call dist-tool-rule,$(file))))
|
||||
define dist-extra-rule
|
||||
dist/$(1): $(1)
|
||||
@echo ' REFINE $$@'
|
||||
$(QUIET)mkdir -p $$(dir $$@) && sed -e '/^#> dist-cutoff-begin/,/^#< dist-cutoff-end/d' $$< > $$@
|
||||
$(QUIET)mkdir -p $$(dir $$@) && sed -e '/^#> dist-cutoff-begin/,/^#< dist-cutoff-end/d' $$< >$$@
|
||||
|
||||
endef
|
||||
$(foreach file,$(filter-out man1/% VERSION %.in ntdll.def,$(DIST_EXTRA)),$(eval $(call dist-extra-rule,$(file))))
|
||||
$(foreach file,$(filter-out man1/% VERSION.txt %.in ntdll.def,$(DIST_EXTRA)),$(eval $(call dist-extra-rule,$(file))))
|
||||
|
||||
dist/VERSION: src/version.c
|
||||
dist/VERSION.txt: src/version.c
|
||||
@echo ' MAKE $@'
|
||||
$(QUIET)mkdir -p dist/ && echo "$(MDBX_GIT_VERSION).$(MDBX_GIT_REVISION)" > $@
|
||||
$(QUIET)mkdir -p dist/ && echo "$(MDBX_GIT_VERSION).$(MDBX_GIT_REVISION)" >$@
|
||||
|
||||
dist/ntdll.def: src/ntdll.def
|
||||
@echo ' COPY $@'
|
||||
@@ -551,7 +586,7 @@ endif
|
||||
################################################################################
|
||||
# Cross-compilation simple test
|
||||
|
||||
CROSS_LIST = sparc64-linux-gnu-gcc alpha-linux-gnu-gcc mips-linux-gnu-gcc \
|
||||
CROSS_LIST = mips-linux-gnu-gcc \
|
||||
powerpc64-linux-gnu-gcc powerpc-linux-gnu-gcc \
|
||||
arm-linux-gnueabihf-gcc aarch64-linux-gnu-gcc \
|
||||
sh4-linux-gnu-gcc mips64-linux-gnuabi64-gcc \
|
||||
@@ -562,7 +597,7 @@ CROSS_LIST = sparc64-linux-gnu-gcc alpha-linux-gnu-gcc mips-linux-gnu-gcc \
|
||||
# s390x-linux-gnu-gcc - works (previously: qemu hang/abort)
|
||||
# sparc64-linux-gnu-gcc - coredump (qemu mmap-troubles, previously: qemu fails fcntl for F_SETLK/F_GETLK)
|
||||
# alpha-linux-gnu-gcc - coredump (qemu mmap-troubles)
|
||||
CROSS_LIST_NOQEMU =
|
||||
CROSS_LIST_NOQEMU = sparc64-linux-gnu-gcc alpha-linux-gnu-gcc
|
||||
|
||||
cross-gcc:
|
||||
@echo ' Re-building by cross-compiler for: $(CROSS_LIST_NOQEMU) $(CROSS_LIST)'
|
||||
@@ -570,7 +605,7 @@ cross-gcc:
|
||||
@echo "FOR INSTANCE: apt install g++-aarch64-linux-gnu g++-alpha-linux-gnu g++-arm-linux-gnueabihf g++-hppa-linux-gnu g++-mips-linux-gnu g++-mips64-linux-gnuabi64 g++-powerpc-linux-gnu g++-powerpc64-linux-gnu g++-s390x-linux-gnu g++-sh4-linux-gnu g++-sparc64-linux-gnu"
|
||||
$(QUIET)for CC in $(CROSS_LIST_NOQEMU) $(CROSS_LIST); do \
|
||||
echo "===================== $$CC"; \
|
||||
$(MAKE) CXXSTD= clean && CC=$$CC CXX=$$(echo $$CC | sed 's/-gcc/-g++/') EXE_LDFLAGS=-static $(MAKE) all || exit $$?; \
|
||||
$(MAKE) IOARENA=false CXXSTD= clean && CC=$$CC CXX=$$(echo $$CC | sed 's/-gcc/-g++/') EXE_LDFLAGS=-static $(MAKE) IOARENA=false all || exit $$?; \
|
||||
done
|
||||
|
||||
# Unfortunately qemu don't provide robust support for futexes.
|
||||
@@ -583,21 +618,25 @@ cross-qemu:
|
||||
@echo " 2) apt install binfmt-support qemu-user-static qemu-user qemu-system-arm qemu-system-mips qemu-system-misc qemu-system-ppc qemu-system-sparc"
|
||||
$(QUIET)for CC in $(CROSS_LIST); do \
|
||||
echo "===================== $$CC + qemu"; \
|
||||
$(MAKE) CXXSTD= clean && \
|
||||
MDBX_TEST_EXTRA="$(MDBX_TEST_EXTRA)$$(echo $$CC | grep -q -e alpha -e sparc && echo ' --size-upper=64M --size-lower=64M')" \
|
||||
$(MAKE) IOARENA=false CXXSTD= clean && \
|
||||
CC=$$CC CXX=$$(echo $$CC | sed 's/-gcc/-g++/') EXE_LDFLAGS=-static MDBX_BUILD_OPTIONS="-DMDBX_SAFE4QEMU $(MDBX_BUILD_OPTIONS)" \
|
||||
$(MAKE) test-singleprocess || exit $$?; \
|
||||
$(MAKE) IOARENA=false smoke-singleprocess test-singleprocess || exit $$?; \
|
||||
done
|
||||
|
||||
#< dist-cutoff-end
|
||||
|
||||
install: $(LIBRARIES) $(TOOLS) $(HEADERS)
|
||||
@echo ' INSTALLING...'
|
||||
$(INSTALL) -D -p $(EXE_INSTALL_FLAGS) -t $(DESTDIR)$(prefix)/bin$(suffix) $(TOOLS) && \
|
||||
$(INSTALL) -D -p $(EXE_INSTALL_FLAGS) -t $(DESTDIR)$(prefix)/lib$(suffix) $(filter-out libmdbx.a,$(LIBRARIES)) && \
|
||||
$(INSTALL) -D -p -t $(DESTDIR)$(prefix)/lib$(suffix) libmdbx.a && \
|
||||
$(INSTALL) -D -p -m 444 -t $(DESTDIR)$(prefix)/include $(HEADERS) && \
|
||||
$(INSTALL) -D -p -m 444 -t $(DESTDIR)$(mandir)/man1 $(addprefix $(MAN_SRCDIR), $(MANPAGES))
|
||||
$(QUIET)mkdir -p $(DESTDIR)$(prefix)/bin$(suffix) && \
|
||||
$(INSTALL) -p $(EXE_INSTALL_FLAGS) $(TOOLS) $(DESTDIR)$(prefix)/bin$(suffix)/ && \
|
||||
mkdir -p $(DESTDIR)$(prefix)/lib$(suffix)/ && \
|
||||
$(INSTALL) -p $(EXE_INSTALL_FLAGS) $(filter-out libmdbx.a,$(LIBRARIES)) $(DESTDIR)$(prefix)/lib$(suffix)/ && \
|
||||
mkdir -p $(DESTDIR)$(prefix)/lib$(suffix)/ && \
|
||||
$(INSTALL) -p libmdbx.a $(DESTDIR)$(prefix)/lib$(suffix)/ && \
|
||||
mkdir -p $(DESTDIR)$(prefix)/include/ && \
|
||||
$(INSTALL) -p -m 444 $(HEADERS) $(DESTDIR)$(prefix)/include/ && \
|
||||
mkdir -p $(DESTDIR)$(mandir)/man1/ && \
|
||||
$(INSTALL) -p -m 444 $(addprefix $(MAN_SRCDIR), $(MANPAGES)) $(DESTDIR)$(mandir)/man1/
|
||||
|
||||
install-strip: EXE_INSTALL_FLAGS = -s
|
||||
install-strip: install
|
||||
@@ -615,24 +654,32 @@ uninstall:
|
||||
################################################################################
|
||||
# Benchmarking by ioarena
|
||||
|
||||
IOARENA ?= $(shell \
|
||||
ifeq ($(origin IOARENA),undefined)
|
||||
IOARENA := $(shell \
|
||||
(test -x ../ioarena/@BUILD/src/ioarena && echo ../ioarena/@BUILD/src/ioarena) || \
|
||||
(test -x ../../@BUILD/src/ioarena && echo ../../@BUILD/src/ioarena) || \
|
||||
(test -x ../../src/ioarena && echo ../../src/ioarena) || which ioarena 2>&- || \
|
||||
echo '\#\# TIP: Clone and build the https://github.com/pmwkaa/ioarena.git within a neighbouring directory for availability of benchmarking.' >&2)
|
||||
(echo false && echo '$(TIP) Clone and build the https://github.com/pmwkaa/ioarena.git within a neighbouring directory for availability of benchmarking.' >&2))
|
||||
endif
|
||||
NN ?= 25000000
|
||||
BENCH_CRUD_MODE ?= nosync
|
||||
|
||||
ifneq ($(wildcard $(IOARENA)),)
|
||||
|
||||
.PHONY: bench bench-clean bench-couple re-bench bench-quartet bench-triplet
|
||||
|
||||
bench-clean:
|
||||
@echo ' REMOVE bench-*.txt _ioarena/*'
|
||||
$(QUIET)rm -rf bench-*.txt _ioarena/*
|
||||
|
||||
re-bench: bench-clean bench
|
||||
|
||||
ifeq ($(or $(IOARENA),false),false)
|
||||
bench bench-quartet bench-triplet bench-couple:
|
||||
$(QUIET)echo 'The `ioarena` benchmark is required.' >&2 && \
|
||||
echo 'Please clone and build the https://github.com/pmwkaa/ioarena.git within a neighbouring `ioarena` directory.' >&2 && \
|
||||
false
|
||||
|
||||
else
|
||||
|
||||
.PHONY: bench bench-clean bench-couple re-bench bench-quartet bench-triplet
|
||||
|
||||
define bench-rule
|
||||
bench-$(1)_$(2).txt: $(3) $(IOARENA) $(lastword $(MAKEFILE_LIST))
|
||||
@echo ' RUNNING ioarena for $1/$2...'
|
||||
|
||||
4
Makefile
4
Makefile
@@ -5,8 +5,8 @@ clean install install-no-strip install-strip strip tools uninstall \
|
||||
bench bench-clean bench-couple bench-quartet bench-triplet re-bench \
|
||||
lib libmdbx mdbx mdbx_chk mdbx_copy mdbx_drop mdbx_dump mdbx_load mdbx_stat \
|
||||
check dist memcheck cross-gcc cross-qemu doxygen gcc-analyzer reformat \
|
||||
release-assets tags test build-test mdbx_test \
|
||||
test-asan test-fault test-leak test-singleprocess test-ubsan test-valgrind:
|
||||
release-assets tags test build-test mdbx_test smoke smoke-fault smoke-singleprocess \
|
||||
test-asan test-leak test-singleprocess test-ubsan test-valgrind:
|
||||
@CC=$(CC) \
|
||||
CXX=`if test -n "$(CXX)" && which "$(CXX)" > /dev/null; then echo "$(CXX)"; elif test -n "$(CCC)" && which "$(CCC)" > /dev/null; then echo "$(CCC)"; else echo "c++"; fi` \
|
||||
`which gmake || which gnumake || echo 'echo "GNU Make 3.80 or above is required"; exit 2;'` \
|
||||
|
||||
86
README.md
86
README.md
@@ -91,7 +91,7 @@ _MithrilDB_ is a rightly relevant name.
|
||||
- [Improvements beyond LMDB](#improvements-beyond-lmdb)
|
||||
- [History & Acknowledgments](#history)
|
||||
- [Usage](#usage)
|
||||
- [Building](#building)
|
||||
- [Building and Testing](#building-and-testing)
|
||||
- [API description](#api-description)
|
||||
- [Bindings](#bindings)
|
||||
- [Performance comparison](#performance-comparison)
|
||||
@@ -167,9 +167,11 @@ transaction journal. No crash recovery needed. No maintenance is required.
|
||||
2. _libmdbx_ is based on [B+ tree](https://en.wikipedia.org/wiki/B%2B_tree), so access to database pages is mostly random.
|
||||
Thus SSDs provide a significant performance boost over spinning disks for large databases.
|
||||
|
||||
3. _libmdbx_ uses [shadow paging](https://en.wikipedia.org/wiki/Shadow_paging) instead of [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging). Thus syncing data to disk might be a bottleneck for write intensive workload.
|
||||
3. _libmdbx_ uses [shadow paging](https://en.wikipedia.org/wiki/Shadow_paging) instead of [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging).
|
||||
Thus syncing data to disk might be a bottleneck for write intensive workload.
|
||||
|
||||
4. _libmdbx_ uses [copy-on-write](https://en.wikipedia.org/wiki/Copy-on-write) for [snapshot isolation](https://en.wikipedia.org/wiki/Snapshot_isolation) during updates, but read transactions prevents recycling an old retired/freed pages, since it read ones. Thus altering of data during a parallel
|
||||
4. _libmdbx_ uses [copy-on-write](https://en.wikipedia.org/wiki/Copy-on-write) for [snapshot isolation](https://en.wikipedia.org/wiki/Snapshot_isolation) during updates,
|
||||
but read transactions prevents recycling an old retired/freed pages, since it read ones. Thus altering of data during a parallel
|
||||
long-lived read operation will increase the process work set, may exhaust entire free database space,
|
||||
the database can grow quickly, and result in performance degradation.
|
||||
Try to avoid long running read transactions.
|
||||
@@ -369,7 +371,7 @@ The amalgamated source code could be created from the original clone of git
|
||||
repository on Linux by executing `make dist`. As a result, the desired
|
||||
set of files will be formed in the `dist` subdirectory.
|
||||
|
||||
## Building
|
||||
## Building and Testing
|
||||
|
||||
Both amalgamated and original source code provides build through the use
|
||||
[CMake](https://cmake.org/) or [GNU
|
||||
@@ -383,10 +385,76 @@ target platform. Obviously you need building tools itself, i.e. `git`,
|
||||
and `make options` are also available for listing existing targets
|
||||
and build options respectively.
|
||||
|
||||
The only significant specificity is that git' tags are required
|
||||
to build from complete (not amalgamated) source codes.
|
||||
Executing **`git fetch --tags --force --prune`** is enough to get ones,
|
||||
or `git fetch --unshallow --tags --prune --force` after the Github's
|
||||
[`actions/checkout@v2`](https://github.com/actions/checkout) either set **`fetch-depth: 0`** for it.
|
||||
|
||||
So just using CMake or GNU Make in your habitual manner and feel free to
|
||||
fill an issue or make pull request in the case something will be
|
||||
unexpected or broken down.
|
||||
|
||||
### Testing
|
||||
The amalgamated source code does not contain any tests for or several reasons.
|
||||
Please read [the explanation](https://github.com/erthink/libmdbx/issues/214#issuecomment-870717981) and don't ask to alter this.
|
||||
So for testing _libmdbx_ itself you need a full source code, i.e. the clone of a git repository, there is no option.
|
||||
|
||||
The full source code of _libmdbx_ has a [`test` subdirectory](https://github.com/erthink/libmdbx/tree/master/test) with minimalistic test "framework".
|
||||
Actually yonder is a source code of the `mdbx_test` – console utility which has a set of command-line options that allow construct and run a reasonable enough test scenarios.
|
||||
This test utility is intended for _libmdbx_'s developers for testing library itself, but not for use by users.
|
||||
Therefore, only basic information is provided:
|
||||
|
||||
- There are few CRUD-based test cases (hill, TTL, nested, append, jitter, etc),
|
||||
which can be combined to test the concurrent operations within shared database in a multi-processes environment.
|
||||
This is the `basic` test scenario.
|
||||
- The `Makefile` provide several self-described targets for testing: `smoke`, `test`, `check`, `memcheck`, `test-valgrind`,
|
||||
`test-asan`, `test-leak`, `test-ubsan`, `cross-gcc`, `cross-qemu`, `gcc-analyzer`, `smoke-fault`, `smoke-singleprocess`,
|
||||
`test-singleprocess`, 'long-test'. Please run `make --help` if doubt.
|
||||
- In addition to the `mdbx_test` utility, there is the script [`long_stochastic.sh`](https://github.com/erthink/libmdbx/blob/master/test/long_stochastic.sh),
|
||||
which calls `mdbx_test` by going through set of modes and options, with gradually increasing the number of operations and the size of transactions.
|
||||
This script is used for mostly of all automatic testing, including `Makefile` targets and Continuous Integration.
|
||||
- Brief information of available command-line options is available by `--help`.
|
||||
However, you should dive into source code to get all, there is no option.
|
||||
|
||||
Anyway, no matter how thoroughly the _libmdbx_ is tested, you should rely only on your own tests for a few reasons:
|
||||
|
||||
1. Mostly of all use cases are unique.
|
||||
So it is no warranty that your use case was properly tested, even the _libmdbx_'s tests engages stochastic approach.
|
||||
2. If there are problems, then your test on the one hand will help to verify whether you are using _libmdbx_ correctly,
|
||||
on the other hand it will allow to reproduce the problem and insure against regression in a future.
|
||||
3. Actually you should rely on than you checked by yourself or take a risk.
|
||||
|
||||
### Common important details
|
||||
|
||||
#### Build reproducibility
|
||||
By default _libmdbx_ track build time via `MDBX_BUILD_TIMESTAMP` build option and macro.
|
||||
So for a [reproducible builds](https://en.wikipedia.org/wiki/Reproducible_builds) you should predefine/override it to known fixed string value.
|
||||
For instance:
|
||||
|
||||
- for reproducible build with make: `make MDBX_BUILD_TIMESTAMP=unknown ` ...
|
||||
- or during configure by CMake: `cmake -DMDBX_BUILD_TIMESTAMP:STRING=unknown ` ...
|
||||
|
||||
Of course, in addition to this, your toolchain must ensure the reproducibility of builds.
|
||||
For more information please refer to [reproducible-builds.org](https://reproducible-builds.org/).
|
||||
|
||||
#### Containers
|
||||
There are no special traits nor quirks if you use libmdbx ONLY inside the single container.
|
||||
But in a cross-container cases or with a host-container(s) mix the two major things MUST be
|
||||
guaranteed:
|
||||
|
||||
1. Coherence of memory mapping content and unified page cache inside OS kernel for host and all container(s) operated with a some DB.
|
||||
Basically this means must be only a single physical copy of each memory mapped DB' page in the system memory.
|
||||
|
||||
2. Uniqueness of PID values and/or a common space for ones:
|
||||
- for POSIX systems: PID uniqueness for all processes operated with a DB.
|
||||
I.e. the `--pid=host` is required for run DB-aware processes inside Docker,
|
||||
either without host interaction a `--pid=container:<name|id>` with the same name/id.
|
||||
- for non-POSIX (i.e. Windows) systems: inter-visibility of processes handles.
|
||||
I.e. the `OpenProcess(SYNCHRONIZE, ..., PID)` must return reasonable error,
|
||||
including `ERROR_ACCESS_DENIED`,
|
||||
but not the `ERROR_INVALID_PARAMETER` as for an invalid/non-existent PID.
|
||||
|
||||
#### DSO/DLL unloading and destructors of Thread-Local-Storage objects
|
||||
When building _libmdbx_ as a shared library or use static _libmdbx_ as a
|
||||
part of another dynamic library, it is advisable to make sure that your
|
||||
@@ -479,7 +547,7 @@ Please refer to the [official guide](https://developer.android.com/studio/projec
|
||||
|
||||
### iOS
|
||||
To build _libmdbx_ for iOS, we recommend using CMake with the
|
||||
"[toolchain file](https://cmake.org/cmake/help/latest/variable/CMAKE_TOOLCHAIN_FILE.html)"
|
||||
["toolchain file"](https://cmake.org/cmake/help/latest/variable/CMAKE_TOOLCHAIN_FILE.html)
|
||||
from the [ios-cmake](https://github.com/leetal/ios-cmake) project.
|
||||
|
||||
<!-- section-end -->
|
||||
@@ -496,6 +564,10 @@ Bindings
|
||||
|
||||
| Runtime | Repo | Author |
|
||||
| ------- | ------ | ------ |
|
||||
| Haskell | [libmdbx-hs](https://hackage.haskell.org/package/libmdbx) | [Francisco Vallarino](https://github.com/fjvallarino) |
|
||||
| Python (draft) | [python-bindings](https://github.com/erthink/libmdbx/commits/python-bindings) branch | [Noel Kuntze](https://github.com/Thermi)
|
||||
| NodeJS | [lmdbx-store](https://github.com/kriszyp/lmdbx-store) | [Kris Zyp](https://github.com/kriszyp/)
|
||||
| NodeJS | [node-mdbx](https://www.npmjs.com/package/node-mdbx/) | [Сергей Федотов](mailto:sergey.fedotov@corp.mail.ru) |
|
||||
| Ruby | [ruby-mdbx](https://rubygems.org/gems/mdbx/) | [Mahlon E. Smith](https://github.com/mahlonsmith) |
|
||||
| Go | [mdbx-go](https://github.com/torquem-ch/mdbx-go) | [Alex Sharov](https://github.com/AskAlexSharov) |
|
||||
| [Nim](https://en.wikipedia.org/wiki/Nim_(programming_language)) | [NimDBX](https://github.com/snej/nimdbx) | [Jens Alfke](https://github.com/snej)
|
||||
@@ -610,7 +682,9 @@ records.
|
||||
execution time of transactions. Each interval shows minimal and maximum
|
||||
execution time, cross marks standard deviation.
|
||||
|
||||
**1,000,000 transactions in async-write mode**. In case of a crash all data is consistent and conforms to the one of last successful transactions, but lost transaction count is much higher than in
|
||||
**1,000,000 transactions in async-write mode**.
|
||||
In case of a crash all data is consistent and conforms to the one of last successful transactions,
|
||||
but lost transaction count is much higher than in
|
||||
lazy-write mode. All DB engines in this mode do as little writes as
|
||||
possible on persistent storage. _libmdbx_ uses
|
||||
[msync(MS_ASYNC)](https://linux.die.net/man/2/msync) in this mode.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: 0.10.1.{build}
|
||||
version: 0.10.3.{build}
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
@@ -52,8 +52,8 @@ matrix:
|
||||
configuration: Release
|
||||
|
||||
# Enable RDP for troubleshooting
|
||||
init:
|
||||
- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
#init:
|
||||
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
|
||||
before_build:
|
||||
- git clean -x -f -d
|
||||
|
||||
@@ -13,9 +13,14 @@
|
||||
## limitations under the License.
|
||||
##
|
||||
|
||||
cmake_minimum_required(VERSION 3.8.2)
|
||||
if(CMAKE_VERSION VERSION_LESS 3.12)
|
||||
cmake_minimum_required(VERSION 3.8.2)
|
||||
else()
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
endif()
|
||||
|
||||
cmake_policy(PUSH)
|
||||
cmake_policy(VERSION 3.8.2)
|
||||
cmake_policy(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION})
|
||||
|
||||
if(CMAKE_VERSION MATCHES ".*MSVC.*" AND CMAKE_VERSION VERSION_LESS 3.16)
|
||||
message(FATAL_ERROR "CMake from MSVC kit is unfit! "
|
||||
@@ -286,21 +291,21 @@ if(CMAKE_COMPILER_IS_GNU${CMAKE_PRIMARY_LANG})
|
||||
|
||||
set(gcc_suffix "")
|
||||
if(gcc_collect_valid AND gcc_collect)
|
||||
string(REGEX MATCH "^(.*cc)(-.+)$" gcc_suffix_valid ${gcc_collect})
|
||||
string(REGEX MATCH "^(.*(cc|\\+\\+))(-.+)$" gcc_suffix_valid ${gcc_collect})
|
||||
if(gcc_suffix_valid)
|
||||
string(REGEX MATCH "^(.*cc)(-.+)$" "\\2" gcc_suffix ${gcc_collect})
|
||||
string(REGEX REPLACE "^(.*(cc|\\+\\+))(-.+)$" "\\3" gcc_suffix ${gcc_collect})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
get_filename_component(gcc_dir ${CMAKE_${CMAKE_PRIMARY_LANG}_COMPILER} DIRECTORY)
|
||||
if(NOT CMAKE_GCC_AR)
|
||||
find_program(CMAKE_GCC_AR NAMES gcc${gcc_suffix}-ar gcc-ar${gcc_suffix} PATHS ${gcc_dir} NO_DEFAULT_PATH)
|
||||
find_program(CMAKE_GCC_AR NAMES "gcc${gcc_suffix}-ar" "gcc-ar${gcc_suffix}" PATHS "${gcc_dir}" NO_DEFAULT_PATH)
|
||||
endif()
|
||||
if(NOT CMAKE_GCC_NM)
|
||||
find_program(CMAKE_GCC_NM NAMES gcc${gcc_suffix}-nm gcc-nm${gcc_suffix} PATHS ${gcc_dir} NO_DEFAULT_PATH)
|
||||
find_program(CMAKE_GCC_NM NAMES "gcc${gcc_suffix}-nm" "gcc-nm${gcc_suffix}" PATHS "${gcc_dir}" NO_DEFAULT_PATH)
|
||||
endif()
|
||||
if(NOT CMAKE_GCC_RANLIB)
|
||||
find_program(CMAKE_GCC_RANLIB NAMES gcc${gcc_suffix}-ranlib gcc-ranlib${gcc_suffix} PATHS ${gcc_dir} NO_DEFAULT_PATH)
|
||||
find_program(CMAKE_GCC_RANLIB NAMES "gcc${gcc_suffix}-ranlib" "gcc-ranlib${gcc_suffix}" PATHS "${gcc_dir}" NO_DEFAULT_PATH)
|
||||
endif()
|
||||
|
||||
unset(gcc_dir)
|
||||
@@ -342,30 +347,40 @@ if(CMAKE_COMPILER_IS_CLANG)
|
||||
OUTPUT_VARIABLE clang_search_dirs RESULT_VARIABLE clang_probe_result ERROR_QUIET)
|
||||
|
||||
unset(clang_bindirs)
|
||||
unset(clang_bindirs_x)
|
||||
unset(clang_libdirs)
|
||||
unset(clang_libdirs_x)
|
||||
if(clang_probe_result EQUAL 0)
|
||||
string(REGEX MATCH "(^|\n.*)(.*programs: =)([^\n]+)((\n.*)|$)" regexp_valid ${clang_search_dirs})
|
||||
if(regexp_valid)
|
||||
string(REGEX REPLACE "(^|\n.*)(.*programs: =)([^\n]+)((\n.*)|$)" "\\3" list ${clang_search_dirs})
|
||||
string(REPLACE ":" ";" list "${list}")
|
||||
#set(clang_bindirs "")
|
||||
foreach(dir IN LISTS list)
|
||||
get_filename_component(dir "${dir}" REALPATH)
|
||||
string(REGEX MATCH "(^|\n.*)(.*programs: =)([^\n]+)((\n.*)|$)" regexp_valid ${clang_search_dirs})
|
||||
if(regexp_valid)
|
||||
string(REGEX REPLACE "(^|\n.*)(.*programs: =)([^\n]+)((\n.*)|$)" "\\3" list ${clang_search_dirs})
|
||||
string(REPLACE ":" ";" list "${list}")
|
||||
foreach(dir IN LISTS list)
|
||||
get_filename_component(dir "${dir}" REALPATH)
|
||||
if(dir MATCHES ".*llvm.*" OR dir MATCHES ".*clang.*")
|
||||
list(APPEND clang_bindirs "${dir}")
|
||||
endforeach()
|
||||
list(REMOVE_DUPLICATES clang_bindirs)
|
||||
endif()
|
||||
string(REGEX MATCH "(^|\n.*)(.*libraries: =)([^\n]+)((\n.*)|$)" regexp_valid ${clang_search_dirs})
|
||||
if(regexp_valid)
|
||||
string(REGEX REPLACE "(^|\n.*)(.*libraries: =)([^\n]+)((\n.*)|$)" "\\3" list ${clang_search_dirs})
|
||||
string(REPLACE ":" ";" list "${list}")
|
||||
#set(clang_libdirs "")
|
||||
foreach(dir IN LISTS list)
|
||||
get_filename_component(dir "${dir}" REALPATH)
|
||||
else()
|
||||
list(APPEND clang_bindirs_x "${dir}")
|
||||
endif()
|
||||
endforeach()
|
||||
list(APPEND clang_bindirs "${clang_bindirs_x}")
|
||||
list(REMOVE_DUPLICATES clang_bindirs)
|
||||
endif()
|
||||
string(REGEX MATCH "(^|\n.*)(.*libraries: =)([^\n]+)((\n.*)|$)" regexp_valid ${clang_search_dirs})
|
||||
if(regexp_valid)
|
||||
string(REGEX REPLACE "(^|\n.*)(.*libraries: =)([^\n]+)((\n.*)|$)" "\\3" list ${clang_search_dirs})
|
||||
string(REPLACE ":" ";" list "${list}")
|
||||
foreach(dir IN LISTS list)
|
||||
get_filename_component(dir "${dir}" REALPATH)
|
||||
if(dir MATCHES ".*llvm.*" OR dir MATCHES ".*clang.*")
|
||||
list(APPEND clang_libdirs "${dir}")
|
||||
endforeach()
|
||||
list(REMOVE_DUPLICATES clang_libdirs)
|
||||
endif()
|
||||
else()
|
||||
list(APPEND clang_libdirs_x "${dir}")
|
||||
endif()
|
||||
endforeach()
|
||||
list(APPEND clang_libdirs "${clang_libdirs_x}")
|
||||
list(REMOVE_DUPLICATES clang_libdirs)
|
||||
endif()
|
||||
else()
|
||||
get_filename_component(clang_bindirs ${CMAKE_${CMAKE_PRIMARY_LANG}_COMPILER} DIRECTORY)
|
||||
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
|
||||
|
||||
@@ -13,9 +13,14 @@
|
||||
## limitations under the License.
|
||||
##
|
||||
|
||||
cmake_minimum_required(VERSION 3.8.2)
|
||||
if(CMAKE_VERSION VERSION_LESS 3.12)
|
||||
cmake_minimum_required(VERSION 3.8.2)
|
||||
else()
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
endif()
|
||||
|
||||
cmake_policy(PUSH)
|
||||
cmake_policy(VERSION 3.8.2)
|
||||
cmake_policy(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION})
|
||||
|
||||
include(CheckLibraryExists)
|
||||
check_library_exists(gcov __gcov_flush "" HAVE_GCOV)
|
||||
|
||||
@@ -13,9 +13,14 @@
|
||||
## limitations under the License.
|
||||
##
|
||||
|
||||
cmake_minimum_required(VERSION 3.8.2)
|
||||
if(CMAKE_VERSION VERSION_LESS 3.12)
|
||||
cmake_minimum_required(VERSION 3.8.2)
|
||||
else()
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
endif()
|
||||
|
||||
cmake_policy(PUSH)
|
||||
cmake_policy(VERSION 3.8.2)
|
||||
cmake_policy(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION})
|
||||
|
||||
macro(add_compile_flags languages)
|
||||
foreach(_lang ${languages})
|
||||
@@ -173,7 +178,10 @@ macro(fetch_version name source_root_directory parent_scope)
|
||||
set(${name}_GIT_REVISION 0)
|
||||
|
||||
# Try to get version from VERSION file
|
||||
set(version_file "${source_root_directory}/VERSION")
|
||||
set(version_file "${source_root_directory}/VERSION.txt")
|
||||
if(NOT EXISTS "${version_file}")
|
||||
set(version_file "${source_root_directory}/VERSION")
|
||||
endif()
|
||||
if(EXISTS "${version_file}")
|
||||
file(STRINGS "${version_file}" ${name}_VERSION LIMIT_COUNT 1 LIMIT_INPUT 42)
|
||||
endif()
|
||||
|
||||
102
mdbx.h
102
mdbx.h
@@ -361,6 +361,12 @@ typedef mode_t mdbx_mode_t;
|
||||
#define LIBMDBX_INLINE_API(TYPE, NAME, ARGS) static __inline TYPE NAME ARGS
|
||||
#endif /* LIBMDBX_INLINE_API */
|
||||
|
||||
/** \brief Converts a macro argument into a string constant. */
|
||||
#ifndef MDBX_STRINGIFY
|
||||
#define MDBX_STRINGIFY_HELPER(x) #x
|
||||
#define MDBX_STRINGIFY(x) MDBX_STRINGIFY_HELPER(x)
|
||||
#endif /* MDBX_STRINGIFY */
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef __cplusplus
|
||||
@@ -454,8 +460,12 @@ typedef mode_t mdbx_mode_t;
|
||||
#endif
|
||||
#endif /* MDBX_PRINTF_ARGS */
|
||||
|
||||
#if defined(DOXYGEN) || (__has_cpp_attribute(maybe_unused) && \
|
||||
(defined(__cplusplus) || __STDC_VERSION__ > 202005L))
|
||||
#if defined(DOXYGEN) || \
|
||||
(defined(__cplusplus) && __cplusplus >= 201603 && \
|
||||
__has_cpp_attribute(maybe_unused) && \
|
||||
__has_cpp_attribute(maybe_unused) >= 201603) || \
|
||||
(!defined(__cplusplus) && defined(__STDC_VERSION__) && \
|
||||
__STDC_VERSION__ > 202005L)
|
||||
#define MDBX_MAYBE_UNUSED [[maybe_unused]]
|
||||
#elif defined(__GNUC__) || __has_attribute(__unused__)
|
||||
#define MDBX_MAYBE_UNUSED __attribute__((__unused__))
|
||||
@@ -463,6 +473,12 @@ typedef mode_t mdbx_mode_t;
|
||||
#define MDBX_MAYBE_UNUSED
|
||||
#endif /* MDBX_MAYBE_UNUSED */
|
||||
|
||||
#if __has_attribute(no_sanitize)
|
||||
#define MDBX_NOSANITIZE_ENUM __attribute((__no_sanitize__("enum")))
|
||||
#else
|
||||
#define MDBX_NOSANITIZE_ENUM
|
||||
#endif /* MDBX_NOSANITIZE_ENUM */
|
||||
|
||||
/* Oh, below are some songs and dances since:
|
||||
* - C++ requires explicit definition of the necessary operators.
|
||||
* - the proper implementation of DEFINE_ENUM_FLAG_OPERATORS for C++ required
|
||||
@@ -488,28 +504,40 @@ typedef mode_t mdbx_mode_t;
|
||||
/// used to define flags (based on Microsoft's DEFINE_ENUM_FLAG_OPERATORS).
|
||||
#define DEFINE_ENUM_FLAG_OPERATORS(ENUM) \
|
||||
extern "C++" { \
|
||||
MDBX_CXX01_CONSTEXPR ENUM operator|(ENUM a, ENUM b) { \
|
||||
MDBX_NOSANITIZE_ENUM MDBX_CXX01_CONSTEXPR ENUM operator|(ENUM a, ENUM b) { \
|
||||
return ENUM(unsigned(a) | unsigned(b)); \
|
||||
} \
|
||||
MDBX_CXX14_CONSTEXPR ENUM &operator|=(ENUM &a, ENUM b) { return a = a | b; } \
|
||||
MDBX_CXX01_CONSTEXPR ENUM operator&(ENUM a, ENUM b) { \
|
||||
MDBX_NOSANITIZE_ENUM MDBX_CXX14_CONSTEXPR ENUM &operator|=(ENUM &a, \
|
||||
ENUM b) { \
|
||||
return a = a | b; \
|
||||
} \
|
||||
MDBX_NOSANITIZE_ENUM MDBX_CXX01_CONSTEXPR ENUM operator&(ENUM a, ENUM b) { \
|
||||
return ENUM(unsigned(a) & unsigned(b)); \
|
||||
} \
|
||||
MDBX_CXX01_CONSTEXPR ENUM operator&(ENUM a, unsigned b) { \
|
||||
MDBX_NOSANITIZE_ENUM MDBX_CXX01_CONSTEXPR ENUM operator&(ENUM a, \
|
||||
unsigned b) { \
|
||||
return ENUM(unsigned(a) & b); \
|
||||
} \
|
||||
MDBX_CXX01_CONSTEXPR ENUM operator&(unsigned a, ENUM b) { \
|
||||
MDBX_NOSANITIZE_ENUM MDBX_CXX01_CONSTEXPR ENUM operator&(unsigned a, \
|
||||
ENUM b) { \
|
||||
return ENUM(a & unsigned(b)); \
|
||||
} \
|
||||
MDBX_CXX14_CONSTEXPR ENUM &operator&=(ENUM &a, ENUM b) { return a = a & b; } \
|
||||
MDBX_CXX14_CONSTEXPR ENUM &operator&=(ENUM &a, unsigned b) { \
|
||||
MDBX_NOSANITIZE_ENUM MDBX_CXX14_CONSTEXPR ENUM &operator&=(ENUM &a, \
|
||||
ENUM b) { \
|
||||
return a = a & b; \
|
||||
} \
|
||||
MDBX_NOSANITIZE_ENUM MDBX_CXX14_CONSTEXPR ENUM &operator&=(ENUM &a, \
|
||||
unsigned b) { \
|
||||
return a = a & b; \
|
||||
} \
|
||||
MDBX_CXX01_CONSTEXPR unsigned operator~(ENUM a) { return ~unsigned(a); } \
|
||||
MDBX_CXX01_CONSTEXPR ENUM operator^(ENUM a, ENUM b) { \
|
||||
MDBX_NOSANITIZE_ENUM MDBX_CXX01_CONSTEXPR ENUM operator^(ENUM a, ENUM b) { \
|
||||
return ENUM(unsigned(a) ^ unsigned(b)); \
|
||||
} \
|
||||
MDBX_CXX14_CONSTEXPR ENUM &operator^=(ENUM &a, ENUM b) { return a = a ^ b; } \
|
||||
MDBX_NOSANITIZE_ENUM MDBX_CXX14_CONSTEXPR ENUM &operator^=(ENUM &a, \
|
||||
ENUM b) { \
|
||||
return a = a ^ b; \
|
||||
} \
|
||||
}
|
||||
#else /* __cplusplus */
|
||||
/* nope for C since it always allows these operators for enums */
|
||||
@@ -781,6 +809,10 @@ enum MDBX_log_level_t {
|
||||
and all other log-messages */
|
||||
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
|
||||
};
|
||||
@@ -794,6 +826,8 @@ typedef enum MDBX_log_level_t MDBX_log_level_t;
|
||||
* effect, but `MDBX_DBG_ASSERT`, `MDBX_DBG_AUDIT` and `MDBX_DBG_JITTER` only if
|
||||
* libmdbx builded with \ref MDBX_DEBUG. */
|
||||
enum MDBX_debug_flags_t {
|
||||
MDBX_DBG_NONE = 0,
|
||||
|
||||
/** Enable assertion checks.
|
||||
* Requires build with \ref MDBX_DEBUG > 0 */
|
||||
MDBX_DBG_ASSERT = 1,
|
||||
@@ -816,6 +850,11 @@ enum MDBX_debug_flags_t {
|
||||
/** Allow read and write transactions overlapping for the same thread */
|
||||
MDBX_DBG_LEGACY_OVERLAP = 32,
|
||||
|
||||
#ifdef ENABLE_UBSAN
|
||||
MDBX_DBG_MAX = ((unsigned)MDBX_LOG_MAX) << 16 |
|
||||
63 /* 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
|
||||
};
|
||||
@@ -3461,7 +3500,19 @@ LIBMDBX_API int mdbx_canary_get(const MDBX_txn *txn, MDBX_canary *canary);
|
||||
/** \brief A callback function used to compare two keys in a database
|
||||
* \ingroup c_crud
|
||||
* \see mdbx_cmp() \see mdbx_get_keycmp()
|
||||
* \see mdbx_get_datacmp \see mdbx_dcmp() */
|
||||
* \see mdbx_get_datacmp \see mdbx_dcmp()
|
||||
*
|
||||
* \anchor avoid_custom_comparators
|
||||
* It is recommend not using custom comparison functions, but instead
|
||||
* converting the keys to one of the forms that are suitable for built-in
|
||||
* comparators (for instance take look to the \ref value2key).
|
||||
* The reasons to not using custom comparators are:
|
||||
* - The order of records could not be validated without your code.
|
||||
* So `mdbx_chk` utility will reports "wrong order" errors
|
||||
* and the `-i` option is required to ignore ones.
|
||||
* - A records could not be ordered or sorted without your code.
|
||||
* So mdbx_load utility should be used with `-a` option to preserve
|
||||
* input data order. */
|
||||
typedef int(MDBX_cmp_func)(const MDBX_val *a,
|
||||
const MDBX_val *b) MDBX_CXX17_NOEXCEPT;
|
||||
|
||||
@@ -3538,16 +3589,7 @@ typedef int(MDBX_cmp_func)(const MDBX_val *a,
|
||||
*
|
||||
* For \ref mdbx_dbi_open_ex() additional arguments allow you to set custom
|
||||
* comparison functions for keys and values (for multimaps).
|
||||
* However, I recommend not using custom comparison functions, but instead
|
||||
* converting the keys to one of the forms that are suitable for built-in
|
||||
* comparators (for instance take look to the \ref value2key).
|
||||
* The reasons to not using custom comparators are:
|
||||
* - The order of records could not be validated without your code.
|
||||
* So `mdbx_chk` utility will reports "wrong order" errors
|
||||
* and the `-i` option is required to ignore ones.
|
||||
* - A records could not be ordered or sorted without your code.
|
||||
* So mdbx_load utility should be used with `-a` option to preserve
|
||||
* input data order.
|
||||
* \see avoid_custom_comparators
|
||||
*
|
||||
* \returns A non-zero error value on failure and 0 on success,
|
||||
* some possible errors are:
|
||||
@@ -3564,8 +3606,10 @@ typedef int(MDBX_cmp_func)(const MDBX_val *a,
|
||||
LIBMDBX_API int mdbx_dbi_open(MDBX_txn *txn, const char *name,
|
||||
MDBX_db_flags_t flags, MDBX_dbi *dbi);
|
||||
|
||||
/** \deprecated Please avoid using custom comparators
|
||||
* and use mdbx_dbi_open() instead.
|
||||
/** \deprecated Please
|
||||
* \ref avoid_custom_comparators "avoid using custom comparators" and use
|
||||
* \ref mdbx_dbi_open() instead.
|
||||
*
|
||||
* \ingroup c_dbi
|
||||
*
|
||||
* \param [in] txn transaction handle returned by \ref mdbx_txn_begin().
|
||||
@@ -3581,7 +3625,9 @@ MDBX_DEPRECATED LIBMDBX_API int
|
||||
mdbx_dbi_open_ex(MDBX_txn *txn, const char *name, MDBX_db_flags_t flags,
|
||||
MDBX_dbi *dbi, MDBX_cmp_func *keycmp, MDBX_cmp_func *datacmp);
|
||||
|
||||
/** \defgroup value2key Value-to-Key functions to avoid custom comparators
|
||||
/** \defgroup value2key Value-to-Key functions
|
||||
* \brief Value-to-Key functions to
|
||||
* \ref avoid_custom_comparators "avoid using custom comparators"
|
||||
* \see key2value
|
||||
* @{
|
||||
*
|
||||
@@ -3616,7 +3662,9 @@ MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_INLINE_API(uint32_t, mdbx_key_from_int32,
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** \defgroup key2value Key-to-Value functions to avoid custom comparators
|
||||
/** \defgroup key2value Key-to-Value functions
|
||||
* \brief Key-to-Value functions to
|
||||
* \ref avoid_custom_comparators "avoid using custom comparators"
|
||||
* \see value2key
|
||||
* @{ */
|
||||
MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int64_t
|
||||
@@ -4552,6 +4600,7 @@ LIBMDBX_API int mdbx_dbi_sequence(MDBX_txn *txn, MDBX_dbi dbi, uint64_t *result,
|
||||
|
||||
/** \brief Compare two keys according to a particular database.
|
||||
* \ingroup c_crud
|
||||
* \see MDBX_cmp_func
|
||||
*
|
||||
* This returns a comparison as if the two data items were keys in the
|
||||
* specified database.
|
||||
@@ -4576,6 +4625,7 @@ mdbx_get_keycmp(MDBX_db_flags_t flags);
|
||||
|
||||
/** \brief Compare two data items according to a particular database.
|
||||
* \ingroup c_crud
|
||||
* \see MDBX_cmp_func
|
||||
*
|
||||
* This returns a comparison as if the two items were data items of the
|
||||
* specified database.
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
From 7834ae8fc834f5f7b98d45703079cd94e5402ed6 Mon Sep 17 00:00:00 2001
|
||||
From 0c0df833879b3f815959c8bd9b6cc27cb9d71b9e Mon Sep 17 00:00:00 2001
|
||||
From: Leonid Yuriev <leo@yuriev.ru>
|
||||
Date: Fri, 27 Nov 2020 16:31:12 +0300
|
||||
Cc: Heiko Thiery <heiko.thiery@gmail.com>, Thomas Petazzoni <thomas.petazzoni@bootlin.com>, Leonid Yuriev <leo@yuriev.ru
|
||||
Subject: [PATCH v5 1/1] package/libmdbx: new package (library/database).
|
||||
Date: Tue, 3 Aug 2021 00:55:27 +0300
|
||||
Subject: [PATCH] package/libmdbx: new package (library/database).
|
||||
|
||||
This patch adds libmdbx v0.9.2:
|
||||
This patch adds libmdbx v0.10.2:
|
||||
- libmdbx is one of the fastest compact embeddable key-value ACID database.
|
||||
- libmdbx has a specific set of properties and capabilities,
|
||||
focused on creating unique lightweight solutions.
|
||||
@@ -13,44 +12,14 @@ This patch adds libmdbx v0.9.2:
|
||||
- https://github.com/erthink/libmdbx
|
||||
|
||||
Signed-off-by: Leonid Yuriev <leo@yuriev.ru>
|
||||
|
||||
---
|
||||
Changes v1 -> v2:
|
||||
- libmdbx version v0.8.2 -> v0.9.1 (released 2020-09-30)
|
||||
|
||||
Changes v2 -> v3:
|
||||
- removed outcommented stuff (suggested by Heiko Thiery).
|
||||
- cleaned up and simplified the makefile (suggested by Heiko Thiery).
|
||||
- added patch for C++ header installation.
|
||||
- added patch with fix minor copy&paste typo.
|
||||
- added patch with pthread workaround for buggy toolchain/cmake/buildroot.
|
||||
- added patch for `pthread_yield()`.
|
||||
- passed `utils/check-package package/libmdbx/*` (suggested by Heiko Thiery)
|
||||
- passed `utils/test-pkg -a -p libmdbx`,
|
||||
except w/o threads & w/o MMU (suggested by Heiko Thiery).
|
||||
|
||||
Changes v3 -> v4:
|
||||
- fix passing BR2_PACKAGE_LIBMDBX_TOOLS option to cmake.
|
||||
- fix using `depend on` instead of `select`,
|
||||
and add `comment` for C++ toolchain (suggested by Heiko Thiery).
|
||||
- fix minor help typo.
|
||||
|
||||
Changes v4 -> v5:
|
||||
- libmdbx version v0.9.1 -> v0.9.2 (released 2020-11-27)
|
||||
- dropped all patch since not needed.
|
||||
- cosmetic changes (suggested by Thomas Petazzoni <thomas.petazzoni@bootlin.com>).
|
||||
- added dependence of BR2_TOOLCHAIN_HAS_SYNC_4 (suggested by Thomas Petazzoni <thomas.petazzoni@bootlin.com>).
|
||||
- added dependence of !BR2_TOOLCHAIN_HAS_GCC_BUG_64735 (suggested by Thomas Petazzoni <thomas.petazzoni@bootlin.com>).
|
||||
- take in account the BR2_SHARED_STATIC_LIBS (suggested by Thomas Petazzoni <thomas.petazzoni@bootlin.com>).
|
||||
|
||||
Signed-off-by: Leonid Yuriev <leo@yuriev.ru>
|
||||
Signed-off-by: Yann E. MORIN <yann.morin.1998@free.fr>
|
||||
---
|
||||
DEVELOPERS | 3 +++
|
||||
package/Config.in | 1 +
|
||||
package/libmdbx/Config.in | 45 ++++++++++++++++++++++++++++++++++++
|
||||
package/libmdbx/libmdbx.hash | 5 ++++
|
||||
package/libmdbx/libmdbx.mk | 33 ++++++++++++++++++++++++++
|
||||
5 files changed, 87 insertions(+)
|
||||
package/libmdbx/libmdbx.mk | 42 +++++++++++++++++++++++++++++++++
|
||||
5 files changed, 96 insertions(+)
|
||||
create mode 100644 package/libmdbx/Config.in
|
||||
create mode 100644 package/libmdbx/libmdbx.hash
|
||||
create mode 100644 package/libmdbx/libmdbx.mk
|
||||
@@ -134,28 +103,28 @@ index 0000000000..d13f73938f
|
||||
+ !BR2_TOOLCHAIN_GCC_AT_LEAST_4_4
|
||||
diff --git a/package/libmdbx/libmdbx.hash b/package/libmdbx/libmdbx.hash
|
||||
new file mode 100644
|
||||
index 0000000000..0d3501f1d9
|
||||
index 0000000000..c8a28ada34
|
||||
--- /dev/null
|
||||
+++ b/package/libmdbx/libmdbx.hash
|
||||
@@ -0,0 +1,5 @@
|
||||
+# Hashes from: https://github.com/erthink/libmdbx/releases/
|
||||
+sha256 c35cc53d66d74ebfc86e39441ba26276541ac7892bf91dba1e70c83665a02767 libmdbx-amalgamated-0.9.2.tar.gz
|
||||
+sha256 745555704df76626a6612ad0c6bc6b1a66bfab98b9245b07dfb82640aa46d6fa libmdbx-amalgamated-0.10.2.tar.gz
|
||||
+
|
||||
+# Locally calculated
|
||||
+sha256 310fe25c858a9515fc8c8d7d1f24a67c9496f84a91e0a0e41ea9975b1371e569 LICENSE
|
||||
diff --git a/package/libmdbx/libmdbx.mk b/package/libmdbx/libmdbx.mk
|
||||
new file mode 100644
|
||||
index 0000000000..f3720130ec
|
||||
index 0000000000..60c5148625
|
||||
--- /dev/null
|
||||
+++ b/package/libmdbx/libmdbx.mk
|
||||
@@ -0,0 +1,33 @@
|
||||
@@ -0,0 +1,42 @@
|
||||
+################################################################################
|
||||
+#
|
||||
+# libmdbx
|
||||
+#
|
||||
+################################################################################
|
||||
+
|
||||
+LIBMDBX_VERSION = 0.9.2
|
||||
+LIBMDBX_VERSION = 0.10.2
|
||||
+LIBMDBX_SOURCE = libmdbx-amalgamated-$(LIBMDBX_VERSION).tar.gz
|
||||
+LIBMDBX_SITE = https://github.com/erthink/libmdbx/releases/download/v$(LIBMDBX_VERSION)
|
||||
+LIBMDBX_SUPPORTS_IN_SOURCE_BUILD = NO
|
||||
@@ -165,7 +134,12 @@ index 0000000000..f3720130ec
|
||||
+LIBMDBX_STRIP_COMPONENTS = 0
|
||||
+LIBMDBX_INSTALL_STAGING = YES
|
||||
+
|
||||
+LIBMDBX_CONF_OPTS = -DMDBX_INSTALL_MANPAGES=OFF -DBUILD_FOR_NATIVE_CPU=OFF \
|
||||
+# Set CMAKE_BUILD_TYPE to Release to remove -Werror and avoid a build failure
|
||||
+# with glibc < 2.12
|
||||
+LIBMDBX_CONF_OPTS = \
|
||||
+ -DCMAKE_BUILD_TYPE=Release \
|
||||
+ -DMDBX_INSTALL_MANPAGES=OFF \
|
||||
+ -DBUILD_FOR_NATIVE_CPU=OFF \
|
||||
+ -DMDBX_BUILD_CXX=$(if $(BR2_PACKAGE_LIBMDBX_CXX),ON,OFF) \
|
||||
+ -DMDBX_BUILD_TOOLS=$(if $(BR2_PACKAGE_LIBMDBX_TOOLS),ON,OFF)
|
||||
+
|
||||
@@ -176,12 +150,16 @@ index 0000000000..f3720130ec
|
||||
+endif
|
||||
+
|
||||
+ifeq ($(BR2_SHARED_LIBS)$(BR2_SHARED_STATIC_LIBS),y)
|
||||
+LIBMDBX_CONF_OPTS += -DMDBX_BUILD_SHARED_LIBRARY=ON -DMDBX_LINK_TOOLS_NONSTATIC=ON
|
||||
+LIBMDBX_CONF_OPTS += \
|
||||
+ -DMDBX_BUILD_SHARED_LIBRARY=ON \
|
||||
+ -DMDBX_LINK_TOOLS_NONSTATIC=ON
|
||||
+else
|
||||
+LIBMDBX_CONF_OPTS += -DMDBX_BUILD_SHARED_LIBRARY=OFF -DMDBX_LINK_TOOLS_NONSTATIC=OFF
|
||||
+LIBMDBX_CONF_OPTS += \
|
||||
+ -DMDBX_BUILD_SHARED_LIBRARY=OFF \
|
||||
+ -DMDBX_LINK_TOOLS_NONSTATIC=OFF
|
||||
+endif
|
||||
+
|
||||
+$(eval $(cmake-package))
|
||||
--
|
||||
2.29.2
|
||||
2.32.0
|
||||
|
||||
@@ -1,219 +0,0 @@
|
||||
From 0bf9d06e8b090e2d9783d03074f3752ed708f6cf Mon Sep 17 00:00:00 2001
|
||||
From: Leonid Yuriev <leo@yuriev.ru>
|
||||
Date: Fri, 27 Nov 2020 16:31:12 +0300
|
||||
Cc: Heiko Thiery <heiko.thiery@gmail.com>
|
||||
Cc: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
|
||||
Subject: [PATCH v5 0/1] cover letter for package/libmdbx: new package (library/database)
|
||||
|
||||
This patch adds libmdbx v0.9.2 and below is a brief overview of libmdbx.
|
||||
Please merge.
|
||||
|
||||
Regards,
|
||||
Leonid.
|
||||
|
||||
--
|
||||
|
||||
libmdbx is an extremely fast, compact, powerful, embedded, transactional
|
||||
key-value database, with permissive license. libmdbx has a specific set
|
||||
of properties and capabilities, focused on creating unique lightweight
|
||||
solutions.
|
||||
|
||||
Historically, libmdbx (MDBX) is a deeply revised and extended descendant
|
||||
of the legendary LMDB (Lightning Memory-Mapped Database). libmdbx
|
||||
inherits all benefits from LMDB, but resolves some issues and adds a set
|
||||
of improvements.
|
||||
|
||||
According to developers, for now libmdbx surpasses the LMDB in terms of
|
||||
reliability, features and performance.
|
||||
|
||||
|
||||
The most important differences MDBX from LMDB:
|
||||
==============================================
|
||||
|
||||
1. More attention is paid to the quality of the code, to an
|
||||
"unbreakability" of the API, to testing and automatic checks (i.e.
|
||||
sanitizers, etc). So there:
|
||||
- more control during operation;
|
||||
- more checking parameters, internal audit of database structures;
|
||||
- no warnings from compiler;
|
||||
- no issues from ASAN, UBSAN, Valgrind, Coverity;
|
||||
- etc.
|
||||
|
||||
2. Keys could be more than 2 times longer than LMDB.
|
||||
|
||||
3. Up to 20% faster than LMDB in CRUD benchmarks.
|
||||
|
||||
4. Automatic on-the-fly database size adjustment,
|
||||
both increment and reduction.
|
||||
|
||||
5. Automatic continuous zero-overhead database compactification.
|
||||
|
||||
6. The same database format for 32- and 64-bit builds.
|
||||
|
||||
7. LIFO policy for Garbage Collection recycling (this can significantly
|
||||
increase write performance due write-back disk cache up to several times
|
||||
in a best case scenario).
|
||||
|
||||
8. Range query estimation.
|
||||
|
||||
9. Utility for checking the integrity of the database structure with
|
||||
some recovery capabilities.
|
||||
|
||||
For more info please refer:
|
||||
- https://github.com/erthink/libmdbx for source code and README.
|
||||
- https://erthink.github.io/libmdbx for API description.
|
||||
|
||||
--
|
||||
|
||||
MDBX is a Btree-based database management library modeled loosely on the
|
||||
BerkeleyDB API, but much simplified. The entire database (aka
|
||||
"environment") is exposed in a memory map, and all data fetches return
|
||||
data directly from the mapped memory, so no malloc's or memcpy's occur
|
||||
during data fetches. As such, the library is extremely simple because it
|
||||
requires no page caching layer of its own, and it is extremely high
|
||||
performance and memory-efficient. It is also fully transactional with
|
||||
full ACID semantics, and when the memory map is read-only, the database
|
||||
integrity cannot be corrupted by stray pointer writes from application
|
||||
code.
|
||||
|
||||
The library is fully thread-aware and supports concurrent read/write
|
||||
access from multiple processes and threads. Data pages use a
|
||||
copy-on-write strategy so no active data pages are ever overwritten,
|
||||
which also provides resistance to corruption and eliminates the need of
|
||||
any special recovery procedures after a system crash. Writes are fully
|
||||
serialized; only one write transaction may be active at a time, which
|
||||
guarantees that writers can never deadlock. The database structure is
|
||||
multi-versioned so readers run with no locks; writers cannot block
|
||||
readers, and readers don't block writers.
|
||||
|
||||
Unlike other well-known database mechanisms which use either write-ahead
|
||||
transaction logs or append-only data writes, MDBX requires no
|
||||
maintenance during operation. Both write-ahead loggers and append-only
|
||||
databases require periodic checkpointing and/or compaction of their log
|
||||
or database files otherwise they grow without bound. MDBX tracks
|
||||
retired/freed pages within the database and re-uses them for new write
|
||||
operations, so the database size does not grow without bound in normal
|
||||
use.
|
||||
|
||||
The memory map can be used as a read-only or read-write map. It is
|
||||
read-only by default as this provides total immunity to corruption.
|
||||
Using read-write mode offers much higher write performance, but adds the
|
||||
possibility for stray application writes thru pointers to silently
|
||||
corrupt the database.
|
||||
|
||||
|
||||
Features
|
||||
========
|
||||
|
||||
- Key-value data model, keys are always sorted.
|
||||
|
||||
- Fully ACID-compliant, through to MVCC and CoW.
|
||||
|
||||
- Multiple key-value sub-databases within a single datafile.
|
||||
|
||||
- Range lookups, including range query estimation.
|
||||
|
||||
- Efficient support for short fixed length keys, including native
|
||||
32/64-bit integers.
|
||||
|
||||
- Ultra-efficient support for multimaps. Multi-values sorted, searchable
|
||||
and iterable. Keys stored without duplication.
|
||||
|
||||
- Data is memory-mapped and accessible directly/zero-copy. Traversal of
|
||||
database records is extremely-fast.
|
||||
|
||||
- Transactions for readers and writers, ones do not block others.
|
||||
|
||||
- Writes are strongly serialized. No transaction conflicts nor
|
||||
deadlocks.
|
||||
|
||||
- Readers are non-blocking, notwithstanding snapshot isolation.
|
||||
|
||||
- Nested write transactions.
|
||||
|
||||
- Reads scale linearly across CPUs.
|
||||
|
||||
- Continuous zero-overhead database compactification.
|
||||
|
||||
- Automatic on-the-fly database size adjustment.
|
||||
|
||||
- Customizable database page size.
|
||||
|
||||
- Olog(N) cost of lookup, insert, update, and delete operations by
|
||||
virtue of B+ tree characteristics.
|
||||
|
||||
- Online hot backup.
|
||||
|
||||
- Append operation for efficient bulk insertion of pre-sorted data.
|
||||
|
||||
- No WAL nor any transaction journal. No crash recovery needed. No
|
||||
maintenance is required.
|
||||
|
||||
- No internal cache and/or memory management, all done by basic OS
|
||||
services.
|
||||
|
||||
|
||||
Limitations
|
||||
===========
|
||||
|
||||
- Page size: a power of 2, maximum 65536 bytes, default 4096 bytes.
|
||||
|
||||
- Key size: minimum 0, maximum ≈¼ pagesize (1300 bytes for default 4K
|
||||
pagesize, 21780 bytes for 64K pagesize).
|
||||
|
||||
- Value size: minimum 0, maximum 2146435072 (0x7FF00000) bytes for maps,
|
||||
≈¼ pagesize for multimaps (1348 bytes default 4K pagesize, 21828 bytes
|
||||
for 64K pagesize).
|
||||
|
||||
- Write transaction size: up to 4194301 (0x3FFFFD) pages (16 GiB for
|
||||
default 4K pagesize, 256 GiB for 64K pagesize).
|
||||
|
||||
- Database size: up to 2147483648 pages (8 TiB for default 4K pagesize,
|
||||
128 TiB for 64K pagesize).
|
||||
|
||||
- Maximum sub-databases: 32765.
|
||||
|
||||
|
||||
Gotchas
|
||||
=======
|
||||
|
||||
- There cannot be more than one writer at a time, i.e. no more than one
|
||||
write transaction at a time.
|
||||
|
||||
- libmdbx is based on B+ tree, so access to database pages is mostly
|
||||
random. Thus SSDs provide a significant performance boost over
|
||||
spinning disks for large databases.
|
||||
|
||||
- libmdbx uses shadow paging instead of WAL. Thus syncing data to disk
|
||||
might be a bottleneck for write intensive workload.
|
||||
|
||||
- libmdbx uses copy-on-write for snapshot isolation during updates, but
|
||||
read transactions prevents recycling an old retired/freed pages, since
|
||||
it read ones. Thus altering of data during a parallel long-lived read
|
||||
operation will increase the process work set, may exhaust entire free
|
||||
database space, the database can grow quickly, and result in
|
||||
performance degradation. Try to avoid long running read transactions.
|
||||
|
||||
- libmdbx is extraordinarily fast and provides minimal overhead for data
|
||||
access, so you should reconsider using brute force techniques and
|
||||
double check your code. On the one hand, in the case of libmdbx, a
|
||||
simple linear search may be more profitable than complex indexes. On
|
||||
the other hand, if you make something suboptimally, you can notice
|
||||
detrimentally only on sufficiently large data.
|
||||
|
||||
--
|
||||
Leonid Yuriev (1):
|
||||
package/libmdbx: new package (library/database).
|
||||
|
||||
DEVELOPERS | 3 +++
|
||||
package/Config.in | 1 +
|
||||
package/libmdbx/Config.in | 45 ++++++++++++++++++++++++++++++++++++
|
||||
package/libmdbx/libmdbx.hash | 5 ++++
|
||||
package/libmdbx/libmdbx.mk | 33 ++++++++++++++++++++++++++
|
||||
5 files changed, 87 insertions(+)
|
||||
create mode 100644 package/libmdbx/Config.in
|
||||
create mode 100644 package/libmdbx/libmdbx.hash
|
||||
create mode 100644 package/libmdbx/libmdbx.mk
|
||||
|
||||
--
|
||||
2.29.2
|
||||
68
src/bits.md
68
src/bits.md
@@ -1,34 +1,34 @@
|
||||
N | MASK | ENV | TXN | DB | PUT | DBI | NODE | PAGE |
|
||||
--|---------|-----------|--------------|----------|-----------|------------|---------|----------|
|
||||
0 |0000 0001|ALLOC_CACHE|TXN_FINISHED | | |DBI_DIRTY |F_BIGDATA|P_BRANCH
|
||||
1 |0000 0002|ALLOC_GC |TXN_ERROR |REVERSEKEY|F_SUBDATA |DBI_STALE |F_SUBDATA|P_LEAF
|
||||
2 |0000 0004|ALLOC_NEW |TXN_DIRTY |DUPSORT | |DBI_FRESH |F_DUPDATA|P_OVERFLOW
|
||||
3 |0000 0008|ALLOC_SLOT |TXN_SPILLS |INTEGERKEY| |DBI_CREAT | |P_META
|
||||
4 |0000 0010| |TXN_HAS_CHILD |DUPFIXED |NOOVERWRITE|DBI_VALID | |P_BAD
|
||||
5 |0000 0020| | |INTEGERDUP|NODUPDATA |DBI_USRVALID| |P_LEAF2
|
||||
6 |0000 0040| | |REVERSEDUP|CURRENT |DBI_DUPDATA | |P_SUBP
|
||||
7 |0000 0080| | | |ALLDUPS |DBI_AUDITED | |
|
||||
8 |0000 0100| | | | | | |
|
||||
9 |0000 0200| | | | | | |
|
||||
10|0000 0400| | | | | | |
|
||||
11|0000 0800| | | | | | |
|
||||
12|0000 1000| | | | | | |
|
||||
13|0000 2000| | | | | | |P_SPILLED
|
||||
14|0000 4000|NOSUBDIR | | | | | |P_LOOSE
|
||||
15|0000 8000| | |DB_VALID |NOSPILL | | |P_FROZEN
|
||||
16|0001 0000|SAFE_NOSYNC|TXN_NOSYNC | |RESERVE | |RESERVE |
|
||||
17|0002 0000|RDONLY |TXN_RDONLY | |APPEND | |APPEND |
|
||||
18|0004 0000|NOMETASYNC |TXN_NOMETASYNC|CREATE |APPENDDUP
|
||||
19|0008 0000|WRITEMAP |<= | |MULTIPLE
|
||||
20|0010 0000|UTTERLY | |
|
||||
21|0020 0000|NOTLS |<= |
|
||||
22|0040 0000|EXCLUSIVE | |
|
||||
23|0080 0000|NORDAHEAD | |
|
||||
24|0100 0000|NOMEMINIT |TXN_PREPARE |
|
||||
25|0200 0000|COALESCE | |
|
||||
26|0400 0000|LIFORECLAIM| |
|
||||
27|0800 0000|PAGEPERTURB| |
|
||||
28|1000 0000|ENV_TXKEY |TXN_TRY |
|
||||
29|2000 0000|ENV_ACTIVE | |
|
||||
30|4000 0000|ACCEDE |SHRINK_ALLOWED|DB_ACCEDE
|
||||
31|8000 0000|FATAL_ERROR| |
|
||||
N | MASK | ENV | TXN | DB | PUT | DBI | NODE | PAGE | MRESIZE |
|
||||
--|---------|-----------|--------------|----------|-----------|------------|---------|----------|---------|
|
||||
0 |0000 0001|ALLOC_CACHE|TXN_FINISHED | | |DBI_DIRTY |F_BIGDATA|P_BRANCH | |
|
||||
1 |0000 0002|ALLOC_GC |TXN_ERROR |REVERSEKEY|F_SUBDATA |DBI_STALE |F_SUBDATA|P_LEAF | |
|
||||
2 |0000 0004|ALLOC_NEW |TXN_DIRTY |DUPSORT | |DBI_FRESH |F_DUPDATA|P_OVERFLOW| |
|
||||
3 |0000 0008|ALLOC_SLOT |TXN_SPILLS |INTEGERKEY| |DBI_CREAT | |P_META | |
|
||||
4 |0000 0010| |TXN_HAS_CHILD |DUPFIXED |NOOVERWRITE|DBI_VALID | |P_BAD | |
|
||||
5 |0000 0020| | |INTEGERDUP|NODUPDATA |DBI_USRVALID| |P_LEAF2 | |
|
||||
6 |0000 0040| | |REVERSEDUP|CURRENT |DBI_DUPDATA | |P_SUBP | |
|
||||
7 |0000 0080| | | |ALLDUPS |DBI_AUDITED | | | |
|
||||
8 |0000 0100| _MAY_MOVE | | | | | | | <= |
|
||||
9 |0000 0200| _MAY_UNMAP| | | | | | | <= |
|
||||
10|0000 0400| | | | | | | | |
|
||||
11|0000 0800| | | | | | | | |
|
||||
12|0000 1000| | | | | | | | |
|
||||
13|0000 2000| | | | | | |P_SPILLED | |
|
||||
14|0000 4000|NOSUBDIR | | | | | |P_LOOSE | |
|
||||
15|0000 8000| | |DB_VALID |NOSPILL | | |P_FROZEN | |
|
||||
16|0001 0000|SAFE_NOSYNC|TXN_NOSYNC | |RESERVE | |RESERVE | | |
|
||||
17|0002 0000|RDONLY |TXN_RDONLY | |APPEND | |APPEND | | <= |
|
||||
18|0004 0000|NOMETASYNC |TXN_NOMETASYNC|CREATE |APPENDDUP | | | | |
|
||||
19|0008 0000|WRITEMAP |<= | |MULTIPLE | | | | <= |
|
||||
20|0010 0000|UTTERLY | | | | | | | <= |
|
||||
21|0020 0000|NOTLS |<= | | | | | | |
|
||||
22|0040 0000|EXCLUSIVE | | | | | | | |
|
||||
23|0080 0000|NORDAHEAD | | | | | | | |
|
||||
24|0100 0000|NOMEMINIT |TXN_PREPARE | | | | | | |
|
||||
25|0200 0000|COALESCE | | | | | | | |
|
||||
26|0400 0000|LIFORECLAIM| | | | | | | |
|
||||
27|0800 0000|PAGEPERTURB| | | | | | | |
|
||||
28|1000 0000|ENV_TXKEY |TXN_TRY | | | | | | |
|
||||
29|2000 0000|ENV_ACTIVE | | | | | | | |
|
||||
30|4000 0000|ACCEDE |SHRINK_ALLOWED|DB_ACCEDE | | | | | |
|
||||
31|8000 0000|FATAL_ERROR| | | | | | | |
|
||||
|
||||
1050
src/core.c
1050
src/core.c
File diff suppressed because it is too large
Load Diff
@@ -190,7 +190,7 @@
|
||||
# elif defined(__GNUC__) || __has_attribute(__hot__)
|
||||
# define __hot __attribute__((__hot__)) __optimize("O3")
|
||||
# else
|
||||
# define __hot __optimize("O3")
|
||||
# define __hot __optimize("O3")
|
||||
# endif
|
||||
# else
|
||||
# define __hot
|
||||
@@ -304,11 +304,6 @@
|
||||
# define ARRAY_END(array) (&array[ARRAY_LENGTH(array)])
|
||||
#endif /* ARRAY_END */
|
||||
|
||||
#ifndef STRINGIFY
|
||||
# define STRINGIFY_HELPER(x) #x
|
||||
# define STRINGIFY(x) STRINGIFY_HELPER(x)
|
||||
#endif /* STRINGIFY */
|
||||
|
||||
#define CONCAT(a,b) a##b
|
||||
#define XCONCAT(a,b) CONCAT(a,b)
|
||||
|
||||
@@ -326,7 +321,7 @@
|
||||
|
||||
#define MDBX_STRING_TETRAD(str) MDBX_TETRAD(str[0], str[1], str[2], str[3])
|
||||
|
||||
#define FIXME "FIXME: " __FILE__ ", " STRINGIFY(__LINE__)
|
||||
#define FIXME "FIXME: " __FILE__ ", " MDBX_STRINGIFY(__LINE__)
|
||||
|
||||
#ifndef STATIC_ASSERT_MSG
|
||||
# if defined(static_assert)
|
||||
|
||||
@@ -818,15 +818,13 @@ typedef struct MDBX_lockinfo {
|
||||
|
||||
#if MDBX_WORDBITS >= 64
|
||||
#define MAX_MAPSIZE MAX_MAPSIZE64
|
||||
#define MDBX_READERS_LIMIT \
|
||||
((MAX_PAGESIZE - sizeof(MDBX_lockinfo)) / sizeof(MDBX_reader))
|
||||
#define MDBX_PGL_LIMIT ((size_t)MAX_PAGENO)
|
||||
#else
|
||||
#define MDBX_READERS_LIMIT 1024
|
||||
#define MAX_MAPSIZE MAX_MAPSIZE32
|
||||
#define MDBX_PGL_LIMIT (MAX_MAPSIZE32 / MIN_PAGESIZE)
|
||||
#endif /* MDBX_WORDBITS */
|
||||
|
||||
#define MDBX_READERS_LIMIT 32767
|
||||
#define MDBX_RADIXSORT_THRESHOLD 333
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
@@ -1148,7 +1146,7 @@ struct MDBX_env {
|
||||
mdbx_thread_key_t me_txkey; /* thread-key for readers */
|
||||
char *me_pathname; /* path to the DB files */
|
||||
void *me_pbuf; /* scratch area for DUPSORT put() */
|
||||
MDBX_txn *me_txn0; /* prealloc'd write transaction */
|
||||
MDBX_txn *me_txn0; /* preallocated write transaction */
|
||||
|
||||
MDBX_dbx *me_dbxs; /* array of static DB info */
|
||||
uint16_t *me_dbflags; /* array of flags from MDBX_db.md_flags */
|
||||
@@ -1648,3 +1646,17 @@ MDBX_MAYBE_UNUSED static void static_checks(void) {
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#define MDBX_ASAN_POISON_MEMORY_REGION(addr, size) \
|
||||
do { \
|
||||
mdbx_trace("POISON_MEMORY_REGION(%p, %zu) at %u", (void *)(addr), \
|
||||
(size_t)(size), __LINE__); \
|
||||
ASAN_POISON_MEMORY_REGION(addr, size); \
|
||||
} while (0)
|
||||
|
||||
#define MDBX_ASAN_UNPOISON_MEMORY_REGION(addr, size) \
|
||||
do { \
|
||||
mdbx_trace("UNPOISON_MEMORY_REGION(%p, %zu) at %u", (void *)(addr), \
|
||||
(size_t)(size), __LINE__); \
|
||||
ASAN_UNPOISON_MEMORY_REGION(addr, size); \
|
||||
} while (0)
|
||||
|
||||
@@ -29,7 +29,7 @@ uint32_t mdbx_linux_kernel_version;
|
||||
bool mdbx_RunningOnWSL1;
|
||||
#endif /* xMDBX_ALLOY */
|
||||
|
||||
static __cold uint8_t probe_for_WSL(const char *tag) {
|
||||
__cold static uint8_t probe_for_WSL(const char *tag) {
|
||||
const char *const WSL = strstr(tag, "WSL");
|
||||
if (WSL && WSL[3] >= '2' && WSL[3] <= '9')
|
||||
return WSL[3] - '0';
|
||||
@@ -45,7 +45,7 @@ static __cold uint8_t probe_for_WSL(const char *tag) {
|
||||
|
||||
#endif /* Linux */
|
||||
|
||||
static __cold __attribute__((__constructor__)) void
|
||||
__cold static __attribute__((__constructor__)) void
|
||||
mdbx_global_constructor(void) {
|
||||
#if defined(__linux__) || defined(__gnu_linux__)
|
||||
struct utsname buffer;
|
||||
@@ -81,7 +81,7 @@ mdbx_global_constructor(void) {
|
||||
mdbx_rthc_global_init();
|
||||
}
|
||||
|
||||
static __cold __attribute__((__destructor__)) void
|
||||
__cold static __attribute__((__destructor__)) void
|
||||
mdbx_global_destructor(void) {
|
||||
mdbx_rthc_global_dtor();
|
||||
}
|
||||
@@ -145,7 +145,7 @@ mdbx_global_destructor(void) {
|
||||
|
||||
#if MDBX_USE_OFDLOCKS
|
||||
static int op_setlk, op_setlkw, op_getlk;
|
||||
static void __cold choice_fcntl() {
|
||||
__cold static void choice_fcntl() {
|
||||
assert(!op_setlk && !op_setlkw && !op_getlk);
|
||||
if ((mdbx_runtime_flags & MDBX_DBG_LEGACY_MULTIOPEN) == 0
|
||||
#if defined(__linux__) || defined(__gnu_linux__)
|
||||
@@ -334,7 +334,7 @@ static int check_fstat(MDBX_env *env) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int __cold mdbx_lck_seize(MDBX_env *env) {
|
||||
__cold MDBX_INTERNAL_FUNC int mdbx_lck_seize(MDBX_env *env) {
|
||||
assert(env->me_lazy_fd != INVALID_HANDLE_VALUE);
|
||||
if (unlikely(mdbx_getpid() != env->me_pid))
|
||||
return MDBX_PANIC;
|
||||
@@ -487,7 +487,7 @@ MDBX_INTERNAL_FUNC int mdbx_lck_downgrade(MDBX_env *env) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int __cold mdbx_lck_destroy(MDBX_env *env,
|
||||
__cold MDBX_INTERNAL_FUNC int mdbx_lck_destroy(MDBX_env *env,
|
||||
MDBX_env *inprocess_neighbor) {
|
||||
if (unlikely(mdbx_getpid() != env->me_pid))
|
||||
return MDBX_PANIC;
|
||||
@@ -574,7 +574,7 @@ MDBX_INTERNAL_FUNC int __cold mdbx_lck_destroy(MDBX_env *env,
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
MDBX_INTERNAL_FUNC int __cold mdbx_lck_init(MDBX_env *env,
|
||||
__cold MDBX_INTERNAL_FUNC int mdbx_lck_init(MDBX_env *env,
|
||||
MDBX_env *inprocess_neighbor,
|
||||
int global_uniqueness_flag) {
|
||||
#if MDBX_LOCKING == MDBX_LOCKING_SYSV
|
||||
@@ -721,7 +721,7 @@ bailout:
|
||||
#endif /* MDBX_LOCKING > 0 */
|
||||
}
|
||||
|
||||
static int __cold mdbx_ipclock_failed(MDBX_env *env, mdbx_ipclock_t *ipc,
|
||||
__cold static int mdbx_ipclock_failed(MDBX_env *env, mdbx_ipclock_t *ipc,
|
||||
const int err) {
|
||||
int rc = err;
|
||||
#if MDBX_LOCKING == MDBX_LOCKING_POSIX2008 || MDBX_LOCKING == MDBX_LOCKING_SYSV
|
||||
|
||||
@@ -260,6 +260,7 @@ static int suspend_and_append(mdbx_handle_array_t **array,
|
||||
|
||||
MDBX_INTERNAL_FUNC int
|
||||
mdbx_suspend_threads_before_remap(MDBX_env *env, mdbx_handle_array_t **array) {
|
||||
mdbx_assert(env, (env->me_flags & MDBX_NOTLS) == 0);
|
||||
const uintptr_t CurrentTid = GetCurrentThreadId();
|
||||
int rc;
|
||||
if (env->me_lck_mmap.lck) {
|
||||
@@ -277,12 +278,6 @@ mdbx_suspend_threads_before_remap(MDBX_env *env, mdbx_handle_array_t **array) {
|
||||
if (reader->mr_tid.weak == CurrentTid ||
|
||||
reader->mr_tid.weak == WriteTxnOwner)
|
||||
goto skip_lck;
|
||||
if (env->me_flags & MDBX_NOTLS) {
|
||||
/* Skip duplicates in no-tls mode */
|
||||
for (const MDBX_reader *scan = reader; --scan >= begin;)
|
||||
if (scan->mr_tid.weak == reader->mr_tid.weak)
|
||||
goto skip_lck;
|
||||
}
|
||||
|
||||
rc = suspend_and_append(array, (mdbx_tid_t)reader->mr_tid.weak);
|
||||
if (rc != MDBX_SUCCESS) {
|
||||
|
||||
260
src/mdbx.c++
260
src/mdbx.c++
@@ -124,7 +124,7 @@ public:
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
__cold std::string format_va(const char *fmt, va_list ap) {
|
||||
__cold std::string format_va(const char *fmt, va_list ap) {
|
||||
va_list ones;
|
||||
va_copy(ones, ap);
|
||||
#ifdef _MSC_VER
|
||||
@@ -145,7 +145,7 @@ __cold std::string format_va(const char *fmt, va_list ap) {
|
||||
return result;
|
||||
}
|
||||
|
||||
__cold std::string format(const char *fmt, ...) {
|
||||
__cold std::string format(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
std::string result = format_va(fmt, ap);
|
||||
@@ -166,15 +166,15 @@ public:
|
||||
virtual ~bug() noexcept;
|
||||
};
|
||||
|
||||
__cold bug::bug(const trouble_location &location) noexcept
|
||||
__cold bug::bug(const trouble_location &location) noexcept
|
||||
: std::runtime_error(format("mdbx.bug: %s.%s at %s:%u", location.function(),
|
||||
location.condition(), location.filename(),
|
||||
location.line())),
|
||||
location_(location) {}
|
||||
|
||||
__cold bug::~bug() noexcept {}
|
||||
__cold bug::~bug() noexcept {}
|
||||
|
||||
[[noreturn]] __cold void raise_bug(const trouble_location &what_and_where) {
|
||||
[[noreturn]] __cold void raise_bug(const trouble_location &what_and_where) {
|
||||
throw bug(what_and_where);
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ __cold bug::~bug() noexcept {}
|
||||
#define ENSURE(condition) \
|
||||
do \
|
||||
if (MDBX_UNLIKELY(!(condition))) \
|
||||
RAISE_BUG(__LINE__, #condition, __func__, __FILE__); \
|
||||
MDBX_CXX20_UNLIKELY RAISE_BUG(__LINE__, #condition, __func__, __FILE__); \
|
||||
while (0)
|
||||
|
||||
#define NOT_IMPLEMENTED() \
|
||||
@@ -268,16 +268,22 @@ namespace mdbx {
|
||||
|
||||
[[noreturn]] __cold void throw_max_length_exceeded() {
|
||||
throw std::length_error(
|
||||
"mdbx:: exceeded the maximal length of data/slice/buffer");
|
||||
"mdbx:: Exceeded the maximal length of data/slice/buffer.");
|
||||
}
|
||||
|
||||
[[noreturn]] __cold void throw_too_small_target_buffer() {
|
||||
throw std::length_error("mdbx:: the target buffer is too small");
|
||||
throw std::length_error("mdbx:: The target buffer is too small.");
|
||||
}
|
||||
|
||||
[[noreturn]] __cold void throw_out_range() {
|
||||
throw std::out_of_range("mdbx:: slice or buffer method was called with "
|
||||
"an argument that exceeds the length");
|
||||
throw std::out_of_range("mdbx:: Slice or buffer method was called with "
|
||||
"an argument that exceeds the length.");
|
||||
}
|
||||
|
||||
[[noreturn]] __cold void throw_allocators_mismatch() {
|
||||
throw std::logic_error(
|
||||
"mdbx:: An allocators mismatch, so an object could not be transferred "
|
||||
"into an incompatible memory allocation scheme.");
|
||||
}
|
||||
|
||||
__cold exception::exception(const ::mdbx::error &error) noexcept
|
||||
@@ -338,7 +344,7 @@ __cold const char *error::what() const noexcept {
|
||||
switch (code()) {
|
||||
#define ERROR_CASE(CODE) \
|
||||
case CODE: \
|
||||
return STRINGIFY(CODE)
|
||||
return MDBX_STRINGIFY(CODE)
|
||||
ERROR_CASE(MDBX_ENODATA);
|
||||
ERROR_CASE(MDBX_EINVAL);
|
||||
ERROR_CASE(MDBX_EACCESS);
|
||||
@@ -474,15 +480,15 @@ bool slice::is_printable(bool disable_utf8) const noexcept {
|
||||
F0, F1, F1, F1, F4, P_, P_, P_, P_, P_, P_, P_, P_, P_, P_, P_ // f0
|
||||
};
|
||||
|
||||
if (length() < 1)
|
||||
return false;
|
||||
if (MDBX_UNLIKELY(length() < 1))
|
||||
MDBX_CXX20_UNLIKELY return false;
|
||||
|
||||
auto src = byte_ptr();
|
||||
const auto end = src + length();
|
||||
if (MDBX_UNLIKELY(disable_utf8)) {
|
||||
do
|
||||
if (MDBX_UNLIKELY((P_ & map[*src]) == 0))
|
||||
return false;
|
||||
MDBX_CXX20_UNLIKELY return false;
|
||||
while (++src < end);
|
||||
return true;
|
||||
}
|
||||
@@ -493,35 +499,35 @@ bool slice::is_printable(bool disable_utf8) const noexcept {
|
||||
const auto second_to = range_to[bits & second_range_mask];
|
||||
switch (bits >> LS) {
|
||||
default:
|
||||
return false;
|
||||
MDBX_CXX20_UNLIKELY return false;
|
||||
case 1:
|
||||
src += 1;
|
||||
continue;
|
||||
case 2:
|
||||
if (unlikely(src + 1 >= end))
|
||||
return false;
|
||||
if (unlikely(src[1] < second_from || src[1] > second_to))
|
||||
return false;
|
||||
if (MDBX_UNLIKELY(src + 1 >= end))
|
||||
MDBX_CXX20_UNLIKELY return false;
|
||||
if (MDBX_UNLIKELY(src[1] < second_from || src[1] > second_to))
|
||||
MDBX_CXX20_UNLIKELY return false;
|
||||
src += 2;
|
||||
continue;
|
||||
case 3:
|
||||
if (unlikely(src + 3 >= end))
|
||||
return false;
|
||||
if (unlikely(src[1] < second_from || src[1] > second_to))
|
||||
return false;
|
||||
if (unlikely(src[2] < 0x80 || src[2] > 0xBF))
|
||||
return false;
|
||||
if (MDBX_UNLIKELY(src + 3 >= end))
|
||||
MDBX_CXX20_UNLIKELY return false;
|
||||
if (MDBX_UNLIKELY(src[1] < second_from || src[1] > second_to))
|
||||
MDBX_CXX20_UNLIKELY return false;
|
||||
if (MDBX_UNLIKELY(src[2] < 0x80 || src[2] > 0xBF))
|
||||
MDBX_CXX20_UNLIKELY return false;
|
||||
src += 3;
|
||||
continue;
|
||||
case 4:
|
||||
if (unlikely(src + 4 >= end))
|
||||
return false;
|
||||
if (unlikely(src[1] < second_from || src[1] > second_to))
|
||||
return false;
|
||||
if (unlikely(src[2] < 0x80 || src[2] > 0xBF))
|
||||
return false;
|
||||
if (unlikely(src[3] < 0x80 || src[3] > 0xBF))
|
||||
return false;
|
||||
if (MDBX_UNLIKELY(src + 4 >= end))
|
||||
MDBX_CXX20_UNLIKELY return false;
|
||||
if (MDBX_UNLIKELY(src[1] < second_from || src[1] > second_to))
|
||||
MDBX_CXX20_UNLIKELY return false;
|
||||
if (MDBX_UNLIKELY(src[2] < 0x80 || src[2] > 0xBF))
|
||||
MDBX_CXX20_UNLIKELY return false;
|
||||
if (MDBX_UNLIKELY(src[3] < 0x80 || src[3] > 0xBF))
|
||||
MDBX_CXX20_UNLIKELY return false;
|
||||
src += 4;
|
||||
continue;
|
||||
}
|
||||
@@ -532,15 +538,14 @@ bool slice::is_printable(bool disable_utf8) const noexcept {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
char *slice::to_hex(char *__restrict dest, size_t dest_size, bool uppercase,
|
||||
unsigned wrap_width) const {
|
||||
if (MDBX_UNLIKELY(to_hex_bytes(wrap_width) > dest_size))
|
||||
throw_too_small_target_buffer();
|
||||
char *to_hex::write_bytes(char *__restrict dest, size_t dest_size) const {
|
||||
if (MDBX_UNLIKELY(envisage_result_length() > dest_size))
|
||||
MDBX_CXX20_UNLIKELY throw_too_small_target_buffer();
|
||||
|
||||
auto src = byte_ptr();
|
||||
auto src = source.byte_ptr();
|
||||
const char alphabase = (uppercase ? 'A' : 'a') - 10;
|
||||
auto line = dest;
|
||||
for (const auto end = src + length(); src != end; ++src) {
|
||||
for (const auto end = source.end_byte_ptr(); src != end; ++src) {
|
||||
const int8_t hi = *src >> 4;
|
||||
const int8_t lo = *src & 15;
|
||||
dest[0] = char(alphabase + hi + (((hi - 10) >> 7) & -7));
|
||||
@@ -554,16 +559,15 @@ char *slice::to_hex(char *__restrict dest, size_t dest_size, bool uppercase,
|
||||
return dest;
|
||||
}
|
||||
|
||||
byte *slice::from_hex(byte *__restrict dest, size_t dest_size,
|
||||
bool ignore_spaces) const {
|
||||
if (MDBX_UNLIKELY(length() % 2 && !ignore_spaces))
|
||||
throw std::domain_error(
|
||||
char *from_hex::write_bytes(char *__restrict dest, size_t dest_size) const {
|
||||
if (MDBX_UNLIKELY(source.length() % 2 && !ignore_spaces))
|
||||
MDBX_CXX20_UNLIKELY throw std::domain_error(
|
||||
"mdbx::from_hex:: odd length of hexadecimal string");
|
||||
if (MDBX_UNLIKELY(from_hex_bytes() > dest_size))
|
||||
throw_too_small_target_buffer();
|
||||
if (MDBX_UNLIKELY(envisage_result_length() > dest_size))
|
||||
MDBX_CXX20_UNLIKELY throw_too_small_target_buffer();
|
||||
|
||||
auto src = byte_ptr();
|
||||
for (auto left = length(); left > 0;) {
|
||||
auto src = source.byte_ptr();
|
||||
for (auto left = source.length(); left > 0;) {
|
||||
if (MDBX_UNLIKELY(*src <= ' ') &&
|
||||
MDBX_LIKELY(ignore_spaces && isspace(*src))) {
|
||||
++src;
|
||||
@@ -572,7 +576,8 @@ byte *slice::from_hex(byte *__restrict dest, size_t dest_size,
|
||||
}
|
||||
|
||||
if (MDBX_UNLIKELY(left < 1 || !isxdigit(src[0]) || !isxdigit(src[1])))
|
||||
throw std::domain_error("mdbx::from_hex:: invalid hexadecimal string");
|
||||
MDBX_CXX20_UNLIKELY throw std::domain_error(
|
||||
"mdbx::from_hex:: invalid hexadecimal string");
|
||||
|
||||
int8_t hi = src[0];
|
||||
hi = (hi | 0x20) - 'a';
|
||||
@@ -589,13 +594,13 @@ byte *slice::from_hex(byte *__restrict dest, size_t dest_size,
|
||||
return dest;
|
||||
}
|
||||
|
||||
bool slice::is_hex(bool ignore_spaces) const noexcept {
|
||||
if (MDBX_UNLIKELY(length() % 2 && !ignore_spaces))
|
||||
return false;
|
||||
bool from_hex::is_erroneous() const noexcept {
|
||||
if (MDBX_UNLIKELY(source.length() % 2 && !ignore_spaces))
|
||||
MDBX_CXX20_UNLIKELY return true;
|
||||
|
||||
bool got = false;
|
||||
auto src = byte_ptr();
|
||||
for (auto left = length(); left > 0;) {
|
||||
auto src = source.byte_ptr();
|
||||
for (auto left = source.length(); left > 0;) {
|
||||
if (MDBX_UNLIKELY(*src <= ' ') &&
|
||||
MDBX_LIKELY(ignore_spaces && isspace(*src))) {
|
||||
++src;
|
||||
@@ -604,13 +609,13 @@ bool slice::is_hex(bool ignore_spaces) const noexcept {
|
||||
}
|
||||
|
||||
if (MDBX_UNLIKELY(left < 1 || !isxdigit(src[0]) || !isxdigit(src[1])))
|
||||
return false;
|
||||
MDBX_CXX20_UNLIKELY return true;
|
||||
|
||||
got = true;
|
||||
src += 2;
|
||||
left -= 2;
|
||||
}
|
||||
return got;
|
||||
return !got;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -658,13 +663,12 @@ static inline char b58_8to11(uint64_t &v) noexcept {
|
||||
return b58_alphabet[i];
|
||||
}
|
||||
|
||||
char *slice::to_base58(char *__restrict dest, size_t dest_size,
|
||||
unsigned wrap_width) const {
|
||||
if (MDBX_UNLIKELY(to_base58_bytes(wrap_width) > dest_size))
|
||||
throw_too_small_target_buffer();
|
||||
char *to_base58::write_bytes(char *__restrict dest, size_t dest_size) const {
|
||||
if (MDBX_UNLIKELY(envisage_result_length() > dest_size))
|
||||
MDBX_CXX20_UNLIKELY throw_too_small_target_buffer();
|
||||
|
||||
auto src = byte_ptr();
|
||||
size_t left = length();
|
||||
auto src = source.byte_ptr();
|
||||
size_t left = source.length();
|
||||
auto line = dest;
|
||||
while (MDBX_LIKELY(left > 7)) {
|
||||
left -= 8;
|
||||
@@ -741,13 +745,12 @@ static inline signed char b58_11to8(uint64_t &v, const byte c) noexcept {
|
||||
return m;
|
||||
}
|
||||
|
||||
byte *slice::from_base58(byte *__restrict dest, size_t dest_size,
|
||||
bool ignore_spaces) const {
|
||||
if (MDBX_UNLIKELY(from_base58_bytes() > dest_size))
|
||||
throw_too_small_target_buffer();
|
||||
char *from_base58::write_bytes(char *__restrict dest, size_t dest_size) const {
|
||||
if (MDBX_UNLIKELY(envisage_result_length() > dest_size))
|
||||
MDBX_CXX20_UNLIKELY throw_too_small_target_buffer();
|
||||
|
||||
auto src = byte_ptr();
|
||||
for (auto left = length(); left > 0;) {
|
||||
auto src = source.byte_ptr();
|
||||
for (auto left = source.length(); left > 0;) {
|
||||
if (MDBX_UNLIKELY(isspace(*src)) && ignore_spaces) {
|
||||
++src;
|
||||
--left;
|
||||
@@ -762,7 +765,7 @@ byte *slice::from_base58(byte *__restrict dest, size_t dest_size,
|
||||
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))
|
||||
goto bailout;
|
||||
MDBX_CXX20_UNLIKELY goto bailout;
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
v = bswap64(v);
|
||||
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
@@ -777,14 +780,14 @@ byte *slice::from_base58(byte *__restrict dest, size_t dest_size,
|
||||
}
|
||||
|
||||
constexpr unsigned invalid_length_mask = 1 << 1 | 1 << 4 | 1 << 8;
|
||||
if (invalid_length_mask & (1 << left))
|
||||
goto bailout;
|
||||
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))
|
||||
goto bailout;
|
||||
MDBX_CXX20_UNLIKELY goto bailout;
|
||||
parrots += 32;
|
||||
} while (--left);
|
||||
|
||||
@@ -801,10 +804,10 @@ bailout:
|
||||
throw std::domain_error("mdbx::from_base58:: invalid base58 string");
|
||||
}
|
||||
|
||||
bool slice::is_base58(bool ignore_spaces) const noexcept {
|
||||
bool from_base58::is_erroneous() const noexcept {
|
||||
bool got = false;
|
||||
auto src = byte_ptr();
|
||||
for (auto left = length(); left > 0;) {
|
||||
auto src = source.byte_ptr();
|
||||
for (auto left = source.length(); left > 0;) {
|
||||
if (MDBX_UNLIKELY(*src <= ' ') &&
|
||||
MDBX_LIKELY(ignore_spaces && isspace(*src))) {
|
||||
++src;
|
||||
@@ -817,7 +820,7 @@ bool slice::is_base58(bool ignore_spaces) const noexcept {
|
||||
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))
|
||||
return false;
|
||||
MDBX_CXX20_UNLIKELY return true;
|
||||
src += 11;
|
||||
left -= 11;
|
||||
got = true;
|
||||
@@ -830,12 +833,12 @@ bool slice::is_base58(bool ignore_spaces) const noexcept {
|
||||
|
||||
do
|
||||
if (MDBX_UNLIKELY(b58_map[*src++] < 0))
|
||||
return false;
|
||||
MDBX_CXX20_UNLIKELY return true;
|
||||
while (--left);
|
||||
got = true;
|
||||
break;
|
||||
}
|
||||
return got;
|
||||
return !got;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -854,13 +857,12 @@ static inline void b64_3to4(const byte x, const byte y, const byte z,
|
||||
dest[3] = alphabet[z & 0x3f];
|
||||
}
|
||||
|
||||
char *slice::to_base64(char *__restrict dest, size_t dest_size,
|
||||
unsigned wrap_width) const {
|
||||
if (MDBX_UNLIKELY(to_base64_bytes(wrap_width) > dest_size))
|
||||
throw_too_small_target_buffer();
|
||||
char *to_base64::write_bytes(char *__restrict dest, size_t dest_size) const {
|
||||
if (MDBX_UNLIKELY(envisage_result_length() > dest_size))
|
||||
MDBX_CXX20_UNLIKELY throw_too_small_target_buffer();
|
||||
|
||||
auto src = byte_ptr();
|
||||
size_t left = length();
|
||||
auto src = source.byte_ptr();
|
||||
size_t left = source.length();
|
||||
auto line = dest;
|
||||
while (true) {
|
||||
switch (left) {
|
||||
@@ -910,22 +912,22 @@ static const signed char b64_map[256] = {
|
||||
|
||||
static inline signed char b64_4to3(signed char a, signed char b, signed char c,
|
||||
signed char d,
|
||||
byte *__restrict dest) noexcept {
|
||||
char *__restrict dest) noexcept {
|
||||
dest[0] = byte((a << 2) + ((b & 0x30) >> 4));
|
||||
dest[1] = byte(((b & 0xf) << 4) + ((c & 0x3c) >> 2));
|
||||
dest[2] = byte(((c & 0x3) << 6) + d);
|
||||
return a | b | c | d;
|
||||
}
|
||||
|
||||
byte *slice::from_base64(byte *__restrict dest, size_t dest_size,
|
||||
bool ignore_spaces) const {
|
||||
if (MDBX_UNLIKELY(length() % 4 && !ignore_spaces))
|
||||
throw std::domain_error("mdbx::from_base64:: odd length of base64 string");
|
||||
if (MDBX_UNLIKELY(from_base64_bytes() > dest_size))
|
||||
throw_too_small_target_buffer();
|
||||
char *from_base64::write_bytes(char *__restrict dest, size_t dest_size) const {
|
||||
if (MDBX_UNLIKELY(source.length() % 4 && !ignore_spaces))
|
||||
MDBX_CXX20_UNLIKELY throw std::domain_error(
|
||||
"mdbx::from_base64:: odd length of base64 string");
|
||||
if (MDBX_UNLIKELY(envisage_result_length() > dest_size))
|
||||
MDBX_CXX20_UNLIKELY throw_too_small_target_buffer();
|
||||
|
||||
auto src = byte_ptr();
|
||||
for (auto left = length(); left > 0;) {
|
||||
auto src = source.byte_ptr();
|
||||
for (auto left = source.length(); left > 0;) {
|
||||
if (MDBX_UNLIKELY(*src <= ' ') &&
|
||||
MDBX_LIKELY(ignore_spaces && isspace(*src))) {
|
||||
++src;
|
||||
@@ -933,10 +935,11 @@ byte *slice::from_base64(byte *__restrict dest, size_t dest_size,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (MDBX_UNLIKELY(left < 3)) {
|
||||
bailout:
|
||||
throw std::domain_error("mdbx::from_base64:: invalid base64 string");
|
||||
}
|
||||
if (MDBX_UNLIKELY(left < 3))
|
||||
MDBX_CXX20_UNLIKELY {
|
||||
bailout:
|
||||
throw std::domain_error("mdbx::from_base64:: invalid base64 string");
|
||||
}
|
||||
const signed char a = b64_map[src[0]], b = b64_map[src[1]],
|
||||
c = b64_map[src[2]], d = b64_map[src[3]];
|
||||
if (MDBX_UNLIKELY(b64_4to3(a, b, c, d, dest) < 0)) {
|
||||
@@ -946,7 +949,7 @@ byte *slice::from_base64(byte *__restrict dest, size_t dest_size,
|
||||
if (c == d)
|
||||
return dest + 1;
|
||||
}
|
||||
goto bailout;
|
||||
MDBX_CXX20_UNLIKELY goto bailout;
|
||||
}
|
||||
src += 4;
|
||||
left -= 4;
|
||||
@@ -954,13 +957,13 @@ byte *slice::from_base64(byte *__restrict dest, size_t dest_size,
|
||||
return dest;
|
||||
}
|
||||
|
||||
bool slice::is_base64(bool ignore_spaces) const noexcept {
|
||||
if (MDBX_UNLIKELY(length() % 4 && !ignore_spaces))
|
||||
return false;
|
||||
bool from_base64::is_erroneous() const noexcept {
|
||||
if (MDBX_UNLIKELY(source.length() % 4 && !ignore_spaces))
|
||||
MDBX_CXX20_UNLIKELY return true;
|
||||
|
||||
bool got = false;
|
||||
auto src = byte_ptr();
|
||||
for (auto left = length(); left > 0;) {
|
||||
auto src = source.byte_ptr();
|
||||
for (auto left = source.length(); left > 0;) {
|
||||
if (MDBX_UNLIKELY(*src <= ' ') &&
|
||||
MDBX_LIKELY(ignore_spaces && isspace(*src))) {
|
||||
++src;
|
||||
@@ -969,19 +972,20 @@ bool slice::is_base64(bool ignore_spaces) const noexcept {
|
||||
}
|
||||
|
||||
if (MDBX_UNLIKELY(left < 3))
|
||||
return false;
|
||||
MDBX_CXX20_UNLIKELY return false;
|
||||
const signed char a = b64_map[src[0]], b = b64_map[src[1]],
|
||||
c = b64_map[src[2]], d = b64_map[src[3]];
|
||||
if (MDBX_UNLIKELY((a | b | c | d) < 0)) {
|
||||
if (left == 4 && (a | b) >= 0 && d == EQ && (c >= 0 || c == d))
|
||||
if (MDBX_UNLIKELY((a | b | c | d) < 0))
|
||||
MDBX_CXX20_UNLIKELY {
|
||||
if (left == 4 && (a | b) >= 0 && d == EQ && (c >= 0 || c == d))
|
||||
return false;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
got = true;
|
||||
src += 4;
|
||||
left -= 4;
|
||||
}
|
||||
return got;
|
||||
return !got;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -1185,9 +1189,9 @@ static inline MDBX_env *create_env() {
|
||||
}
|
||||
|
||||
env_managed::~env_managed() noexcept {
|
||||
if (handle_)
|
||||
error::success_or_panic(::mdbx_env_close(handle_), "mdbx::~env()",
|
||||
"mdbx_env_close");
|
||||
if (MDBX_UNLIKELY(handle_))
|
||||
MDBX_CXX20_UNLIKELY error::success_or_panic(
|
||||
::mdbx_env_close(handle_), "mdbx::~env()", "mdbx_env_close");
|
||||
}
|
||||
|
||||
void env_managed::close(bool dont_sync) {
|
||||
@@ -1195,12 +1199,12 @@ void env_managed::close(bool dont_sync) {
|
||||
static_cast<MDBX_error_t>(::mdbx_env_close_ex(handle_, dont_sync));
|
||||
switch (rc.code()) {
|
||||
case MDBX_EBADSIGN:
|
||||
handle_ = nullptr;
|
||||
MDBX_CXX20_UNLIKELY handle_ = nullptr;
|
||||
__fallthrough /* fall through */;
|
||||
default:
|
||||
rc.throw_exception();
|
||||
MDBX_CXX20_UNLIKELY rc.throw_exception();
|
||||
case MDBX_SUCCESS:
|
||||
handle_ = nullptr;
|
||||
MDBX_CXX20_LIKELY handle_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1222,7 +1226,7 @@ __cold env_managed::env_managed(const ::std::filesystem::path &pathname,
|
||||
|
||||
if (op.options.nested_write_transactions &&
|
||||
!get_options().nested_write_transactions)
|
||||
error::throw_exception(MDBX_INCOMPATIBLE);
|
||||
MDBX_CXX20_UNLIKELY error::throw_exception(MDBX_INCOMPATIBLE);
|
||||
}
|
||||
|
||||
__cold env_managed::env_managed(const ::std::filesystem::path &pathname,
|
||||
@@ -1238,7 +1242,7 @@ __cold env_managed::env_managed(const ::std::filesystem::path &pathname,
|
||||
|
||||
if (op.options.nested_write_transactions &&
|
||||
!get_options().nested_write_transactions)
|
||||
error::throw_exception(MDBX_INCOMPATIBLE);
|
||||
MDBX_CXX20_UNLIKELY error::throw_exception(MDBX_INCOMPATIBLE);
|
||||
}
|
||||
#endif /* MDBX_STD_FILESYSTEM_PATH */
|
||||
|
||||
@@ -1253,7 +1257,7 @@ __cold env_managed::env_managed(const ::std::wstring &pathname,
|
||||
|
||||
if (op.options.nested_write_transactions &&
|
||||
!get_options().nested_write_transactions)
|
||||
error::throw_exception(MDBX_INCOMPATIBLE);
|
||||
MDBX_CXX20_UNLIKELY error::throw_exception(MDBX_INCOMPATIBLE);
|
||||
}
|
||||
|
||||
__cold env_managed::env_managed(const ::std::wstring &pathname,
|
||||
@@ -1269,7 +1273,7 @@ __cold env_managed::env_managed(const ::std::wstring &pathname,
|
||||
|
||||
if (op.options.nested_write_transactions &&
|
||||
!get_options().nested_write_transactions)
|
||||
error::throw_exception(MDBX_INCOMPATIBLE);
|
||||
MDBX_CXX20_UNLIKELY error::throw_exception(MDBX_INCOMPATIBLE);
|
||||
}
|
||||
#endif /* Windows */
|
||||
|
||||
@@ -1283,7 +1287,7 @@ __cold env_managed::env_managed(const ::std::string &pathname,
|
||||
|
||||
if (op.options.nested_write_transactions &&
|
||||
!get_options().nested_write_transactions)
|
||||
error::throw_exception(MDBX_INCOMPATIBLE);
|
||||
MDBX_CXX20_UNLIKELY error::throw_exception(MDBX_INCOMPATIBLE);
|
||||
}
|
||||
|
||||
__cold env_managed::env_managed(const ::std::string &pathname,
|
||||
@@ -1299,7 +1303,7 @@ __cold env_managed::env_managed(const ::std::string &pathname,
|
||||
|
||||
if (op.options.nested_write_transactions &&
|
||||
!get_options().nested_write_transactions)
|
||||
error::throw_exception(MDBX_INCOMPATIBLE);
|
||||
MDBX_CXX20_UNLIKELY error::throw_exception(MDBX_INCOMPATIBLE);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -1314,25 +1318,25 @@ txn_managed txn::start_nested() {
|
||||
}
|
||||
|
||||
txn_managed::~txn_managed() noexcept {
|
||||
if (handle_)
|
||||
error::success_or_panic(::mdbx_txn_abort(handle_), "mdbx::~txn",
|
||||
"mdbx_txn_abort");
|
||||
if (MDBX_UNLIKELY(handle_))
|
||||
MDBX_CXX20_UNLIKELY error::success_or_panic(::mdbx_txn_abort(handle_),
|
||||
"mdbx::~txn", "mdbx_txn_abort");
|
||||
}
|
||||
|
||||
void txn_managed::abort() {
|
||||
const error err = static_cast<MDBX_error_t>(::mdbx_txn_abort(handle_));
|
||||
if (MDBX_LIKELY(err.code() != MDBX_THREAD_MISMATCH))
|
||||
handle_ = nullptr;
|
||||
MDBX_CXX20_LIKELY handle_ = nullptr;
|
||||
if (MDBX_UNLIKELY(err.code() != MDBX_SUCCESS))
|
||||
err.throw_exception();
|
||||
MDBX_CXX20_UNLIKELY err.throw_exception();
|
||||
}
|
||||
|
||||
void txn_managed::commit() {
|
||||
const error err = static_cast<MDBX_error_t>(::mdbx_txn_commit(handle_));
|
||||
if (MDBX_LIKELY(err.code() != MDBX_THREAD_MISMATCH))
|
||||
handle_ = nullptr;
|
||||
MDBX_CXX20_LIKELY handle_ = nullptr;
|
||||
if (MDBX_UNLIKELY(err.code() != MDBX_SUCCESS))
|
||||
err.throw_exception();
|
||||
MDBX_CXX20_UNLIKELY err.throw_exception();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -1396,7 +1400,7 @@ __cold ::std::ostream &operator<<(::std::ostream &out, const slice &it) {
|
||||
if (root.is_printable())
|
||||
(out << "\"").write(root.char_ptr(), root.length()) << "\"";
|
||||
else
|
||||
out << root.base58_encode();
|
||||
out << root.encode_base58();
|
||||
if (root.length() < it.length())
|
||||
out << "...";
|
||||
}
|
||||
|
||||
201
src/mdbx_chk.c
201
src/mdbx_chk.c
@@ -112,7 +112,7 @@ struct problem {
|
||||
};
|
||||
|
||||
struct problem *problems_list;
|
||||
uint64_t total_problems;
|
||||
unsigned total_problems, data_tree_problems, gc_tree_problems;
|
||||
|
||||
static void MDBX_PRINTF_ARGS(1, 2) print(const char *msg, ...) {
|
||||
if (!quiet) {
|
||||
@@ -299,14 +299,20 @@ static int pgvisitor(const uint64_t pgno, const unsigned pgnumber,
|
||||
const size_t nentries, const size_t payload_bytes,
|
||||
const size_t header_bytes, const size_t unused_bytes) {
|
||||
(void)ctx;
|
||||
const bool is_gc_tree = dbi_name_or_tag == MDBX_PGWALK_GC;
|
||||
if (deep > 42) {
|
||||
problem_add("deep", deep, "too large", nullptr);
|
||||
data_tree_problems += !is_gc_tree;
|
||||
gc_tree_problems += is_gc_tree;
|
||||
return MDBX_CORRUPTED /* avoid infinite loop/recursion */;
|
||||
}
|
||||
|
||||
walk_dbi_t *dbi = pagemap_lookup_dbi(dbi_name_or_tag, false);
|
||||
if (!dbi)
|
||||
if (!dbi) {
|
||||
data_tree_problems += !is_gc_tree;
|
||||
gc_tree_problems += is_gc_tree;
|
||||
return MDBX_ENOMEM;
|
||||
}
|
||||
|
||||
const size_t page_bytes = payload_bytes + header_bytes + unused_bytes;
|
||||
walk.pgcount += pgnumber;
|
||||
@@ -319,13 +325,19 @@ static int pgvisitor(const uint64_t pgno, const unsigned pgnumber,
|
||||
(unsigned)pagetype, deep);
|
||||
pagetype_caption = "unknown";
|
||||
dbi->pages.other += pgnumber;
|
||||
data_tree_problems += !is_gc_tree;
|
||||
gc_tree_problems += is_gc_tree;
|
||||
break;
|
||||
case MDBX_page_broken:
|
||||
pagetype_caption = "broken";
|
||||
dbi->pages.other += pgnumber;
|
||||
data_tree_problems += !is_gc_tree;
|
||||
gc_tree_problems += is_gc_tree;
|
||||
break;
|
||||
case MDBX_subpage_broken:
|
||||
pagetype_caption = "broken-subpage";
|
||||
data_tree_problems += !is_gc_tree;
|
||||
gc_tree_problems += is_gc_tree;
|
||||
break;
|
||||
case MDBX_page_meta:
|
||||
pagetype_caption = "meta";
|
||||
@@ -375,17 +387,21 @@ static int pgvisitor(const uint64_t pgno, const unsigned pgnumber,
|
||||
bool already_used = false;
|
||||
for (unsigned n = 0; n < pgnumber; ++n) {
|
||||
uint64_t spanpgno = pgno + n;
|
||||
if (spanpgno >= alloc_pages)
|
||||
if (spanpgno >= alloc_pages) {
|
||||
problem_add("page", spanpgno, "wrong page-no",
|
||||
"%s-page: %" PRIu64 " > %" PRIu64 ", deep %i",
|
||||
pagetype_caption, spanpgno, alloc_pages, deep);
|
||||
else if (walk.pagemap[spanpgno]) {
|
||||
data_tree_problems += !is_gc_tree;
|
||||
gc_tree_problems += is_gc_tree;
|
||||
} else if (walk.pagemap[spanpgno]) {
|
||||
walk_dbi_t *coll_dbi = &walk.dbi[walk.pagemap[spanpgno] - 1];
|
||||
problem_add("page", spanpgno,
|
||||
(branch && coll_dbi == dbi) ? "loop" : "already used",
|
||||
"%s-page: by %s, deep %i", pagetype_caption, coll_dbi->name,
|
||||
deep);
|
||||
already_used = true;
|
||||
data_tree_problems += !is_gc_tree;
|
||||
gc_tree_problems += is_gc_tree;
|
||||
} else {
|
||||
walk.pagemap[spanpgno] = (short)(dbi - walk.dbi + 1);
|
||||
dbi->pages.total += 1;
|
||||
@@ -399,18 +415,26 @@ static int pgvisitor(const uint64_t pgno, const unsigned pgnumber,
|
||||
|
||||
if (MDBX_IS_ERROR(err)) {
|
||||
problem_add("page", pgno, "invalid/corrupted", "%s-page", pagetype_caption);
|
||||
data_tree_problems += !is_gc_tree;
|
||||
gc_tree_problems += is_gc_tree;
|
||||
} else {
|
||||
if (unused_bytes > page_size)
|
||||
if (unused_bytes > page_size) {
|
||||
problem_add("page", pgno, "illegal unused-bytes",
|
||||
"%s-page: %u < %" PRIuPTR " < %u", pagetype_caption, 0,
|
||||
unused_bytes, envinfo.mi_dxb_pagesize);
|
||||
data_tree_problems += !is_gc_tree;
|
||||
gc_tree_problems += is_gc_tree;
|
||||
}
|
||||
|
||||
if (header_bytes < (int)sizeof(long) ||
|
||||
(size_t)header_bytes >= envinfo.mi_dxb_pagesize - sizeof(long))
|
||||
(size_t)header_bytes >= envinfo.mi_dxb_pagesize - sizeof(long)) {
|
||||
problem_add("page", pgno, "illegal header-length",
|
||||
"%s-page: %" PRIuPTR " < %" PRIuPTR " < %" PRIuPTR,
|
||||
pagetype_caption, sizeof(long), header_bytes,
|
||||
envinfo.mi_dxb_pagesize - sizeof(long));
|
||||
data_tree_problems += !is_gc_tree;
|
||||
gc_tree_problems += is_gc_tree;
|
||||
}
|
||||
if (payload_bytes < 1) {
|
||||
if (nentries > 1) {
|
||||
problem_add("page", pgno, "zero size-of-entry",
|
||||
@@ -420,12 +444,16 @@ static int pgvisitor(const uint64_t pgno, const unsigned pgnumber,
|
||||
// LY: hush a misuse error
|
||||
page_bytes = page_size;
|
||||
} */
|
||||
data_tree_problems += !is_gc_tree;
|
||||
gc_tree_problems += is_gc_tree;
|
||||
} else {
|
||||
problem_add("page", pgno, "empty",
|
||||
"%s-page: payload %" PRIuPTR " bytes, %" PRIuPTR
|
||||
" entries, deep %i",
|
||||
pagetype_caption, payload_bytes, nentries, deep);
|
||||
dbi->pages.empty += 1;
|
||||
data_tree_problems += !is_gc_tree;
|
||||
gc_tree_problems += is_gc_tree;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,6 +466,8 @@ static int pgvisitor(const uint64_t pgno, const unsigned pgnumber,
|
||||
payload_bytes, unused_bytes, deep);
|
||||
if (page_size > page_bytes)
|
||||
dbi->lost_bytes += page_size - page_bytes;
|
||||
data_tree_problems += !is_gc_tree;
|
||||
gc_tree_problems += is_gc_tree;
|
||||
} else {
|
||||
dbi->payload_bytes += payload_bytes + header_bytes;
|
||||
walk.total_payload_bytes += payload_bytes + header_bytes;
|
||||
@@ -807,8 +837,9 @@ static int process_db(MDBX_dbi dbi_handle, char *dbi_name, visitor *handler,
|
||||
bad_data = true;
|
||||
}
|
||||
|
||||
if (prev_key.iov_base && !bad_data) {
|
||||
if ((flags & MDBX_DUPFIXED) && prev_data.iov_len != data.iov_len) {
|
||||
if (prev_key.iov_base) {
|
||||
if (prev_data.iov_base && !bad_data && (flags & MDBX_DUPFIXED) &&
|
||||
prev_data.iov_len != data.iov_len) {
|
||||
problem_add("entry", record_count, "different data length",
|
||||
"%" PRIuPTR " != %" PRIuPTR, prev_data.iov_len,
|
||||
data.iov_len);
|
||||
@@ -821,11 +852,11 @@ static int process_db(MDBX_dbi dbi_handle, char *dbi_name, visitor *handler,
|
||||
++dups;
|
||||
if ((flags & MDBX_DUPSORT) == 0) {
|
||||
problem_add("entry", record_count, "duplicated entries", nullptr);
|
||||
if (data.iov_len == prev_data.iov_len &&
|
||||
if (prev_data.iov_base && data.iov_len == prev_data.iov_len &&
|
||||
memcmp(data.iov_base, prev_data.iov_base, data.iov_len) == 0) {
|
||||
problem_add("entry", record_count, "complete duplicate", nullptr);
|
||||
}
|
||||
} else if (!bad_data) {
|
||||
} else if (!bad_data && prev_data.iov_base) {
|
||||
cmp = mdbx_dcmp(txn, dbi_handle, &data, &prev_data);
|
||||
if (cmp == 0) {
|
||||
problem_add("entry", record_count, "complete duplicate", nullptr);
|
||||
@@ -838,11 +869,6 @@ static int process_db(MDBX_dbi dbi_handle, char *dbi_name, visitor *handler,
|
||||
problem_add("entry", record_count, "wrong order of entries", nullptr);
|
||||
}
|
||||
}
|
||||
} else if (verbose) {
|
||||
if (flags & MDBX_INTEGERKEY)
|
||||
print(" - fixed key-size %" PRIuPTR "\n", key.iov_len);
|
||||
if (flags & (MDBX_INTEGERDUP | MDBX_DUPFIXED))
|
||||
print(" - fixed data-size %" PRIuPTR "\n", data.iov_len);
|
||||
}
|
||||
|
||||
if (handler) {
|
||||
@@ -855,10 +881,17 @@ static int process_db(MDBX_dbi dbi_handle, char *dbi_name, visitor *handler,
|
||||
key_bytes += key.iov_len;
|
||||
data_bytes += data.iov_len;
|
||||
|
||||
if (!bad_key)
|
||||
if (!bad_key) {
|
||||
if (verbose && (flags & MDBX_INTEGERKEY) && !prev_key.iov_base)
|
||||
print(" - fixed key-size %" PRIuPTR "\n", key.iov_len);
|
||||
prev_key = key;
|
||||
if (!bad_data)
|
||||
}
|
||||
if (!bad_data) {
|
||||
if (verbose && (flags & (MDBX_INTEGERDUP | MDBX_DUPFIXED)) &&
|
||||
!prev_data.iov_base)
|
||||
print(" - fixed data-size %" PRIuPTR "\n", data.iov_len);
|
||||
prev_data = data;
|
||||
}
|
||||
rc = mdbx_cursor_get(mc, &key, &data, MDBX_NEXT);
|
||||
}
|
||||
if (rc != MDBX_NOTFOUND)
|
||||
@@ -915,7 +948,7 @@ static __inline bool meta_ot(txnid_t txn_a, uint64_t sign_a, txnid_t txn_b,
|
||||
|
||||
static __inline bool meta_eq(txnid_t txn_a, uint64_t sign_a, txnid_t txn_b,
|
||||
uint64_t sign_b) {
|
||||
if (txn_a != txn_b)
|
||||
if (!txn_a || txn_a != txn_b)
|
||||
return false;
|
||||
|
||||
if (SIGN_IS_STEADY(sign_a) != SIGN_IS_STEADY(sign_b))
|
||||
@@ -1033,7 +1066,7 @@ int main(int argc, char *argv[]) {
|
||||
int rc;
|
||||
char *prog = argv[0];
|
||||
char *envname;
|
||||
int problems_maindb = 0, problems_freedb = 0, problems_meta = 0;
|
||||
unsigned problems_maindb = 0, problems_freedb = 0, problems_meta = 0;
|
||||
bool write_locked = false;
|
||||
bool turn_meta = false;
|
||||
bool force_turn_meta = false;
|
||||
@@ -1376,11 +1409,6 @@ int main(int argc, char *argv[]) {
|
||||
"of may by large than the database itself,\n "
|
||||
"until it will be closed or reopened in read-write mode.\n");
|
||||
#endif
|
||||
print(" - transactions: recent %" PRIu64 ", latter reader %" PRIu64
|
||||
", lag %" PRIi64 "\n",
|
||||
envinfo.mi_recent_txnid, envinfo.mi_latter_reader_txnid,
|
||||
envinfo.mi_recent_txnid - envinfo.mi_latter_reader_txnid);
|
||||
|
||||
verbose_meta(0, envinfo.mi_meta0_txnid, envinfo.mi_meta0_sign,
|
||||
envinfo.mi_bootid.meta0.x, envinfo.mi_bootid.meta0.y);
|
||||
verbose_meta(1, envinfo.mi_meta1_txnid, envinfo.mi_meta1_sign,
|
||||
@@ -1389,52 +1417,70 @@ int main(int argc, char *argv[]) {
|
||||
envinfo.mi_bootid.meta2.x, envinfo.mi_bootid.meta2.y);
|
||||
}
|
||||
|
||||
if (verbose > 1)
|
||||
print(" - performs check for meta-pages clashes\n");
|
||||
if (meta_eq(envinfo.mi_meta0_txnid, envinfo.mi_meta0_sign,
|
||||
envinfo.mi_meta1_txnid, envinfo.mi_meta1_sign)) {
|
||||
print(" ! meta-%d and meta-%d are clashed\n", 0, 1);
|
||||
++problems_meta;
|
||||
}
|
||||
if (meta_eq(envinfo.mi_meta1_txnid, envinfo.mi_meta1_sign,
|
||||
envinfo.mi_meta2_txnid, envinfo.mi_meta2_sign)) {
|
||||
print(" ! meta-%d and meta-%d are clashed\n", 1, 2);
|
||||
++problems_meta;
|
||||
}
|
||||
if (meta_eq(envinfo.mi_meta2_txnid, envinfo.mi_meta2_sign,
|
||||
envinfo.mi_meta0_txnid, envinfo.mi_meta0_sign)) {
|
||||
print(" ! meta-%d and meta-%d are clashed\n", 2, 0);
|
||||
++problems_meta;
|
||||
}
|
||||
if (stuck_meta >= 0) {
|
||||
if (verbose) {
|
||||
print(" - skip checking meta-pages since the %u"
|
||||
" is selected for verification\n",
|
||||
stuck_meta);
|
||||
print(" - transactions: recent %" PRIu64
|
||||
", selected for verification %" PRIu64 ", lag %" PRIi64 "\n",
|
||||
envinfo.mi_recent_txnid, get_meta_txnid(stuck_meta),
|
||||
envinfo.mi_recent_txnid - get_meta_txnid(stuck_meta));
|
||||
}
|
||||
} else {
|
||||
if (verbose > 1)
|
||||
print(" - performs check for meta-pages clashes\n");
|
||||
if (meta_eq(envinfo.mi_meta0_txnid, envinfo.mi_meta0_sign,
|
||||
envinfo.mi_meta1_txnid, envinfo.mi_meta1_sign)) {
|
||||
print(" ! meta-%d and meta-%d are clashed\n", 0, 1);
|
||||
++problems_meta;
|
||||
}
|
||||
if (meta_eq(envinfo.mi_meta1_txnid, envinfo.mi_meta1_sign,
|
||||
envinfo.mi_meta2_txnid, envinfo.mi_meta2_sign)) {
|
||||
print(" ! meta-%d and meta-%d are clashed\n", 1, 2);
|
||||
++problems_meta;
|
||||
}
|
||||
if (meta_eq(envinfo.mi_meta2_txnid, envinfo.mi_meta2_sign,
|
||||
envinfo.mi_meta0_txnid, envinfo.mi_meta0_sign)) {
|
||||
print(" ! meta-%d and meta-%d are clashed\n", 2, 0);
|
||||
++problems_meta;
|
||||
}
|
||||
|
||||
const unsigned steady_meta_id = meta_recent(true);
|
||||
const uint64_t steady_meta_txnid = get_meta_txnid(steady_meta_id);
|
||||
const unsigned weak_meta_id = meta_recent(false);
|
||||
const uint64_t weak_meta_txnid = get_meta_txnid(weak_meta_id);
|
||||
if (envflags & MDBX_EXCLUSIVE) {
|
||||
if (verbose > 1)
|
||||
print(" - performs full check recent-txn-id with meta-pages\n");
|
||||
if (steady_meta_txnid != envinfo.mi_recent_txnid) {
|
||||
print(" ! steady meta-%d txn-id mismatch recent-txn-id (%" PRIi64
|
||||
" != %" PRIi64 ")\n",
|
||||
steady_meta_id, steady_meta_txnid, envinfo.mi_recent_txnid);
|
||||
++problems_meta;
|
||||
const unsigned steady_meta_id = meta_recent(true);
|
||||
const uint64_t steady_meta_txnid = get_meta_txnid(steady_meta_id);
|
||||
const unsigned weak_meta_id = meta_recent(false);
|
||||
const uint64_t weak_meta_txnid = get_meta_txnid(weak_meta_id);
|
||||
if (envflags & MDBX_EXCLUSIVE) {
|
||||
if (verbose > 1)
|
||||
print(" - performs full check recent-txn-id with meta-pages\n");
|
||||
if (steady_meta_txnid != envinfo.mi_recent_txnid) {
|
||||
print(" ! steady meta-%d txn-id mismatch recent-txn-id (%" PRIi64
|
||||
" != %" PRIi64 ")\n",
|
||||
steady_meta_id, steady_meta_txnid, envinfo.mi_recent_txnid);
|
||||
++problems_meta;
|
||||
}
|
||||
} else if (write_locked) {
|
||||
if (verbose > 1)
|
||||
print(" - performs lite check recent-txn-id with meta-pages (not a "
|
||||
"monopolistic mode)\n");
|
||||
if (weak_meta_txnid != envinfo.mi_recent_txnid) {
|
||||
print(" ! weak meta-%d txn-id mismatch recent-txn-id (%" PRIi64
|
||||
" != %" PRIi64 ")\n",
|
||||
weak_meta_id, weak_meta_txnid, envinfo.mi_recent_txnid);
|
||||
++problems_meta;
|
||||
}
|
||||
} else if (verbose) {
|
||||
print(" - skip check recent-txn-id with meta-pages (monopolistic or "
|
||||
"read-write mode only)\n");
|
||||
}
|
||||
} else if (write_locked) {
|
||||
if (verbose > 1)
|
||||
print(" - performs lite check recent-txn-id with meta-pages (not a "
|
||||
"monopolistic mode)\n");
|
||||
if (weak_meta_txnid != envinfo.mi_recent_txnid) {
|
||||
print(" ! weak meta-%d txn-id mismatch recent-txn-id (%" PRIi64
|
||||
" != %" PRIi64 ")\n",
|
||||
weak_meta_id, weak_meta_txnid, envinfo.mi_recent_txnid);
|
||||
++problems_meta;
|
||||
}
|
||||
} else if (verbose) {
|
||||
print(" - skip check recent-txn-id with meta-pages (monopolistic or "
|
||||
"read-write mode only)\n");
|
||||
total_problems += problems_meta;
|
||||
|
||||
if (verbose)
|
||||
print(" - transactions: recent %" PRIu64 ", latter reader %" PRIu64
|
||||
", lag %" PRIi64 "\n",
|
||||
envinfo.mi_recent_txnid, envinfo.mi_latter_reader_txnid,
|
||||
envinfo.mi_recent_txnid - envinfo.mi_latter_reader_txnid);
|
||||
}
|
||||
total_problems += problems_meta;
|
||||
|
||||
if (!dont_traversal) {
|
||||
struct problem *saved_list;
|
||||
@@ -1545,8 +1591,19 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
if (!verbose)
|
||||
print("Iterating DBIs...\n");
|
||||
problems_maindb = process_db(~0u, /* MAIN_DBI */ nullptr, nullptr, false);
|
||||
problems_freedb = process_db(FREE_DBI, "@GC", handle_freedb, false);
|
||||
if (data_tree_problems) {
|
||||
print("Skip processing %s since tree is corrupted (%u problems)\n", "@MAIN",
|
||||
data_tree_problems);
|
||||
problems_maindb = data_tree_problems;
|
||||
} else
|
||||
problems_maindb = process_db(~0u, /* MAIN_DBI */ nullptr, nullptr, false);
|
||||
|
||||
if (gc_tree_problems) {
|
||||
print("Skip processing %s since tree is corrupted (%u problems)\n", "@GC",
|
||||
gc_tree_problems);
|
||||
problems_freedb = gc_tree_problems;
|
||||
} else
|
||||
problems_freedb = process_db(FREE_DBI, "@GC", handle_freedb, false);
|
||||
|
||||
if (verbose) {
|
||||
uint64_t value = envinfo.mi_mapsize / envinfo.mi_dxb_pagesize;
|
||||
@@ -1603,7 +1660,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
if (rc == 0 && total_problems == 1 && problems_meta == 1 && !dont_traversal &&
|
||||
(envflags & MDBX_RDONLY) == 0 && !only_subdb && stuck_meta < 0 &&
|
||||
steady_meta_txnid < envinfo.mi_recent_txnid) {
|
||||
get_meta_txnid(meta_recent(true)) < envinfo.mi_recent_txnid) {
|
||||
print("Perform sync-to-disk for make steady checkpoint at txn-id #%" PRIi64
|
||||
"\n",
|
||||
envinfo.mi_recent_txnid);
|
||||
@@ -1673,8 +1730,8 @@ bailout:
|
||||
#endif /* !WINDOWS */
|
||||
|
||||
if (total_problems) {
|
||||
print("Total %" PRIu64 " error%s detected, elapsed %.3f seconds.\n",
|
||||
total_problems, (total_problems > 1) ? "s are" : " is", elapsed);
|
||||
print("Total %u error%s detected, elapsed %.3f seconds.\n", total_problems,
|
||||
(total_problems > 1) ? "s are" : " is", elapsed);
|
||||
if (problems_meta || problems_maindb || problems_freedb)
|
||||
return EXIT_FAILURE_CHECK_MAJOR;
|
||||
return EXIT_FAILURE_CHECK_MINOR;
|
||||
|
||||
@@ -39,18 +39,18 @@
|
||||
#else
|
||||
#define MDBX_ENV_CHECKPID 1
|
||||
#endif
|
||||
#define MDBX_ENV_CHECKPID_CONFIG "AUTO=" STRINGIFY(MDBX_ENV_CHECKPID)
|
||||
#define MDBX_ENV_CHECKPID_CONFIG "AUTO=" MDBX_STRINGIFY(MDBX_ENV_CHECKPID)
|
||||
#else
|
||||
#define MDBX_ENV_CHECKPID_CONFIG STRINGIFY(MDBX_ENV_CHECKPID)
|
||||
#define MDBX_ENV_CHECKPID_CONFIG MDBX_STRINGIFY(MDBX_ENV_CHECKPID)
|
||||
#endif /* MDBX_ENV_CHECKPID */
|
||||
|
||||
/** Controls checking transaction owner thread against misuse transactions from
|
||||
* other threads. */
|
||||
#ifndef MDBX_TXN_CHECKOWNER
|
||||
#define MDBX_TXN_CHECKOWNER 1
|
||||
#define MDBX_TXN_CHECKOWNER_CONFIG "AUTO=" STRINGIFY(MDBX_TXN_CHECKOWNER)
|
||||
#define MDBX_TXN_CHECKOWNER_CONFIG "AUTO=" MDBX_STRINGIFY(MDBX_TXN_CHECKOWNER)
|
||||
#else
|
||||
#define MDBX_TXN_CHECKOWNER_CONFIG STRINGIFY(MDBX_TXN_CHECKOWNER)
|
||||
#define MDBX_TXN_CHECKOWNER_CONFIG MDBX_STRINGIFY(MDBX_TXN_CHECKOWNER)
|
||||
#endif /* MDBX_TXN_CHECKOWNER */
|
||||
|
||||
/** Does a system have battery-backed Real-Time Clock or just a fake. */
|
||||
@@ -61,9 +61,9 @@
|
||||
#else
|
||||
#define MDBX_TRUST_RTC 1
|
||||
#endif
|
||||
#define MDBX_TRUST_RTC_CONFIG "AUTO=" STRINGIFY(MDBX_TRUST_RTC)
|
||||
#define MDBX_TRUST_RTC_CONFIG "AUTO=" MDBX_STRINGIFY(MDBX_TRUST_RTC)
|
||||
#else
|
||||
#define MDBX_TRUST_RTC_CONFIG STRINGIFY(MDBX_TRUST_RTC)
|
||||
#define MDBX_TRUST_RTC_CONFIG MDBX_STRINGIFY(MDBX_TRUST_RTC)
|
||||
#endif /* MDBX_TRUST_RTC */
|
||||
|
||||
/** Controls online database auto-compactification during write-transactions. */
|
||||
@@ -238,9 +238,9 @@
|
||||
#else
|
||||
#define MDBX_LOCKING MDBX_LOCKING_SYSV
|
||||
#endif
|
||||
#define MDBX_LOCKING_CONFIG "AUTO=" STRINGIFY(MDBX_LOCKING)
|
||||
#define MDBX_LOCKING_CONFIG "AUTO=" MDBX_STRINGIFY(MDBX_LOCKING)
|
||||
#else
|
||||
#define MDBX_LOCKING_CONFIG STRINGIFY(MDBX_LOCKING)
|
||||
#define MDBX_LOCKING_CONFIG MDBX_STRINGIFY(MDBX_LOCKING)
|
||||
#endif /* MDBX_LOCKING */
|
||||
#endif /* !Windows */
|
||||
|
||||
@@ -253,9 +253,9 @@
|
||||
#else
|
||||
#define MDBX_USE_OFDLOCKS 0
|
||||
#endif
|
||||
#define MDBX_USE_OFDLOCKS_CONFIG "AUTO=" STRINGIFY(MDBX_USE_OFDLOCKS)
|
||||
#define MDBX_USE_OFDLOCKS_CONFIG "AUTO=" MDBX_STRINGIFY(MDBX_USE_OFDLOCKS)
|
||||
#else
|
||||
#define MDBX_USE_OFDLOCKS_CONFIG STRINGIFY(MDBX_USE_OFDLOCKS)
|
||||
#define MDBX_USE_OFDLOCKS_CONFIG MDBX_STRINGIFY(MDBX_USE_OFDLOCKS)
|
||||
#endif /* MDBX_USE_OFDLOCKS */
|
||||
|
||||
/** Advanced: Using sendfile() syscall (autodetection by default). */
|
||||
@@ -326,9 +326,9 @@
|
||||
#else
|
||||
#define MDBX_64BIT_ATOMIC 0
|
||||
#endif
|
||||
#define MDBX_64BIT_ATOMIC_CONFIG "AUTO=" STRINGIFY(MDBX_64BIT_ATOMIC)
|
||||
#define MDBX_64BIT_ATOMIC_CONFIG "AUTO=" MDBX_STRINGIFY(MDBX_64BIT_ATOMIC)
|
||||
#else
|
||||
#define MDBX_64BIT_ATOMIC_CONFIG STRINGIFY(MDBX_64BIT_ATOMIC)
|
||||
#define MDBX_64BIT_ATOMIC_CONFIG MDBX_STRINGIFY(MDBX_64BIT_ATOMIC)
|
||||
#endif /* MDBX_64BIT_ATOMIC */
|
||||
|
||||
#ifndef MDBX_64BIT_CAS
|
||||
@@ -355,9 +355,9 @@
|
||||
#else
|
||||
#define MDBX_64BIT_CAS MDBX_64BIT_ATOMIC
|
||||
#endif
|
||||
#define MDBX_64BIT_CAS_CONFIG "AUTO=" STRINGIFY(MDBX_64BIT_CAS)
|
||||
#define MDBX_64BIT_CAS_CONFIG "AUTO=" MDBX_STRINGIFY(MDBX_64BIT_CAS)
|
||||
#else
|
||||
#define MDBX_64BIT_CAS_CONFIG STRINGIFY(MDBX_64BIT_CAS)
|
||||
#define MDBX_64BIT_CAS_CONFIG MDBX_STRINGIFY(MDBX_64BIT_CAS)
|
||||
#endif /* MDBX_64BIT_CAS */
|
||||
|
||||
#ifndef MDBX_UNALIGNED_OK
|
||||
|
||||
196
src/osal.c
196
src/osal.c
@@ -217,7 +217,7 @@ __extern_C void __assert(const char *function, const char *file, int line,
|
||||
|
||||
#if !defined(__ANDROID_API__) || MDBX_DEBUG
|
||||
|
||||
void __cold mdbx_assert_fail(const MDBX_env *env, const char *msg,
|
||||
__cold void mdbx_assert_fail(const MDBX_env *env, const char *msg,
|
||||
const char *func, int line) {
|
||||
#if MDBX_DEBUG
|
||||
if (env && env->me_assert_func) {
|
||||
@@ -1377,6 +1377,36 @@ static int mdbx_check_fs_local(mdbx_filehandle_t handle, int flags) {
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
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) {
|
||||
mdbx_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;
|
||||
}
|
||||
}
|
||||
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int mdbx_mmap(const int flags, mdbx_mmap_t *map,
|
||||
const size_t size, const size_t limit,
|
||||
const unsigned options) {
|
||||
@@ -1384,34 +1414,34 @@ MDBX_INTERNAL_FUNC int mdbx_mmap(const int flags, mdbx_mmap_t *map,
|
||||
map->limit = 0;
|
||||
map->current = 0;
|
||||
map->address = nullptr;
|
||||
map->filesize = 0;
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
map->section = NULL;
|
||||
map->filesize = 0;
|
||||
#endif /* Windows */
|
||||
|
||||
int err = mdbx_check_fs_local(map->fd, flags);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
return err;
|
||||
|
||||
err = check_mmap_limit(limit);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
return err;
|
||||
|
||||
if ((flags & MDBX_RDONLY) == 0 && (options & MMAP_OPTION_TRUNCATE) != 0) {
|
||||
err = mdbx_ftruncate(map->fd, size);
|
||||
if (err != MDBX_SUCCESS)
|
||||
return err;
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
map->filesize = size;
|
||||
#else
|
||||
#if !(defined(_WIN32) || defined(_WIN64))
|
||||
map->current = size;
|
||||
#endif /* ! Windows */
|
||||
#endif /* !Windows */
|
||||
} else {
|
||||
uint64_t filesize = 0;
|
||||
err = mdbx_filesize(map->fd, &filesize);
|
||||
err = mdbx_filesize(map->fd, &map->filesize);
|
||||
if (err != MDBX_SUCCESS)
|
||||
return err;
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
map->filesize = filesize;
|
||||
#else
|
||||
map->current = (filesize > limit) ? limit : (size_t)filesize;
|
||||
#endif /* ! Windows */
|
||||
#if !(defined(_WIN32) || defined(_WIN64))
|
||||
map->current = (map->filesize > limit) ? limit : (size_t)map->filesize;
|
||||
#endif /* !Windows */
|
||||
}
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
@@ -1508,7 +1538,7 @@ MDBX_INTERNAL_FUNC int mdbx_mmap(const int flags, mdbx_mmap_t *map,
|
||||
#endif /* ! Windows */
|
||||
|
||||
VALGRIND_MAKE_MEM_DEFINED(map->address, map->current);
|
||||
ASAN_UNPOISON_MEMORY_REGION(map->address, map->current);
|
||||
MDBX_ASAN_UNPOISON_MEMORY_REGION(map->address, map->current);
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1517,7 +1547,10 @@ MDBX_INTERNAL_FUNC int mdbx_munmap(mdbx_mmap_t *map) {
|
||||
/* Unpoisoning is required for ASAN to avoid false-positive diagnostic
|
||||
* when this memory will re-used by malloc or another mmapping.
|
||||
* See https://github.com/erthink/libmdbx/pull/93#issuecomment-613687203 */
|
||||
ASAN_UNPOISON_MEMORY_REGION(map->address, map->limit);
|
||||
MDBX_ASAN_UNPOISON_MEMORY_REGION(map->address,
|
||||
(map->filesize && map->filesize < map->limit)
|
||||
? map->filesize
|
||||
: map->limit);
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
if (map->section)
|
||||
NtClose(map->section);
|
||||
@@ -1535,8 +1568,8 @@ MDBX_INTERNAL_FUNC int mdbx_munmap(mdbx_mmap_t *map) {
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int mdbx_mresize(int flags, mdbx_mmap_t *map, size_t size,
|
||||
size_t limit, const bool may_move) {
|
||||
MDBX_INTERNAL_FUNC int mdbx_mresize(const int flags, mdbx_mmap_t *map,
|
||||
size_t size, size_t limit) {
|
||||
assert(size <= limit);
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
assert(size != map->current || limit != map->limit || size < map->filesize);
|
||||
@@ -1559,6 +1592,10 @@ MDBX_INTERNAL_FUNC int mdbx_mresize(int flags, mdbx_mmap_t *map, size_t size,
|
||||
}
|
||||
|
||||
if (limit > map->limit) {
|
||||
err = check_mmap_limit(limit);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
return err;
|
||||
|
||||
/* check ability of address space for growth before unmap */
|
||||
PVOID BaseAddress = (PBYTE)map->address + map->limit;
|
||||
SIZE_T RegionSize = limit - map->limit;
|
||||
@@ -1580,6 +1617,13 @@ MDBX_INTERNAL_FUNC int mdbx_mresize(int flags, mdbx_mmap_t *map, size_t size,
|
||||
* - change size of mapped view;
|
||||
* - extend read-only mapping;
|
||||
* Therefore we should unmap/map entire section. */
|
||||
if ((flags & MDBX_MRESIZE_MAY_UNMAP) == 0)
|
||||
return MDBX_RESULT_TRUE;
|
||||
|
||||
/* Unpoisoning is required for ASAN to avoid false-positive diagnostic
|
||||
* when this memory will re-used by malloc or another mmapping.
|
||||
* See https://github.com/erthink/libmdbx/pull/93#issuecomment-613687203 */
|
||||
MDBX_ASAN_UNPOISON_MEMORY_REGION(map->address, map->limit);
|
||||
status = NtUnmapViewOfSection(GetCurrentProcess(), map->address);
|
||||
if (!NT_SUCCESS(status))
|
||||
return ntstatus2errcode(status);
|
||||
@@ -1615,7 +1659,7 @@ retry_file_and_section:
|
||||
if (status != (NTSTATUS) /* STATUS_CONFLICTING_ADDRESSES */ 0xC0000018)
|
||||
goto bailout_ntstatus /* no way to recovery */;
|
||||
|
||||
if (may_move)
|
||||
if (flags & MDBX_MRESIZE_MAY_MOVE)
|
||||
/* the base address could be changed */
|
||||
map->address = NULL;
|
||||
}
|
||||
@@ -1673,7 +1717,7 @@ retry_mapview:;
|
||||
|
||||
if (!NT_SUCCESS(status)) {
|
||||
if (status == (NTSTATUS) /* STATUS_CONFLICTING_ADDRESSES */ 0xC0000018 &&
|
||||
map->address && may_move) {
|
||||
map->address && (flags & MDBX_MRESIZE_MAY_MOVE) != 0) {
|
||||
/* try remap at another base address */
|
||||
map->address = NULL;
|
||||
goto retry_mapview;
|
||||
@@ -1684,7 +1728,7 @@ retry_mapview:;
|
||||
if (map->address && (size != map->current || limit != map->limit)) {
|
||||
/* try remap with previously size and limit,
|
||||
* but will return MDBX_UNABLE_EXTEND_MAPSIZE on success */
|
||||
rc = MDBX_UNABLE_EXTEND_MAPSIZE;
|
||||
rc = (limit > map->limit) ? MDBX_UNABLE_EXTEND_MAPSIZE : MDBX_RESULT_TRUE;
|
||||
size = map->current;
|
||||
ReservedSize = limit = map->limit;
|
||||
goto retry_file_and_section;
|
||||
@@ -1700,40 +1744,62 @@ retry_mapview:;
|
||||
|
||||
#else /* Windows */
|
||||
|
||||
uint64_t filesize = 0;
|
||||
int rc = mdbx_filesize(map->fd, &filesize);
|
||||
map->filesize = 0;
|
||||
int rc = mdbx_filesize(map->fd, &map->filesize);
|
||||
if (rc != MDBX_SUCCESS)
|
||||
return rc;
|
||||
|
||||
if (flags & MDBX_RDONLY) {
|
||||
map->current = (filesize > limit) ? limit : (size_t)filesize;
|
||||
map->current = (map->filesize > limit) ? limit : (size_t)map->filesize;
|
||||
if (map->current != size)
|
||||
rc = MDBX_UNABLE_EXTEND_MAPSIZE;
|
||||
} else if (filesize != size) {
|
||||
rc = mdbx_ftruncate(map->fd, size);
|
||||
if (rc != MDBX_SUCCESS)
|
||||
return rc;
|
||||
rc =
|
||||
(size > map->current) ? MDBX_UNABLE_EXTEND_MAPSIZE : MDBX_RESULT_TRUE;
|
||||
} else {
|
||||
if (map->filesize != size) {
|
||||
rc = mdbx_ftruncate(map->fd, size);
|
||||
if (rc != MDBX_SUCCESS)
|
||||
return rc;
|
||||
map->filesize = size;
|
||||
}
|
||||
|
||||
if (map->current > size) {
|
||||
/* Clearing asan's bitmask for the region which released in shrinking,
|
||||
* since:
|
||||
* - after the shrinking we will get an exception when accessing
|
||||
* this region and (therefore) do not need the help of ASAN.
|
||||
* - this allows us to clear the mask only within the file size
|
||||
* when closing the mapping. */
|
||||
MDBX_ASAN_UNPOISON_MEMORY_REGION(
|
||||
(char *)map->address + size,
|
||||
((map->current < map->limit) ? map->current : map->limit) - size);
|
||||
}
|
||||
map->current = size;
|
||||
}
|
||||
|
||||
if (limit == map->limit)
|
||||
return MDBX_SUCCESS;
|
||||
return rc;
|
||||
|
||||
if (limit < map->limit) {
|
||||
/* unmap an excess at end of mapping. */
|
||||
// coverity[offset_free : FALSE]
|
||||
if (unlikely(munmap(map->dxb + limit, map->limit - limit)))
|
||||
return errno;
|
||||
map->limit = limit;
|
||||
return MDBX_SUCCESS;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int err = check_mmap_limit(limit);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
return err;
|
||||
|
||||
assert(limit > map->limit);
|
||||
uint8_t *ptr = MAP_FAILED;
|
||||
|
||||
#if defined(MREMAP_MAYMOVE)
|
||||
ptr = mremap(map->address, map->limit, limit, may_move ? MREMAP_MAYMOVE : 0);
|
||||
ptr = mremap(map->address, map->limit, limit,
|
||||
(flags & MDBX_MRESIZE_MAY_MOVE) ? MREMAP_MAYMOVE : 0);
|
||||
if (ptr == MAP_FAILED) {
|
||||
const int err = errno;
|
||||
err = errno;
|
||||
switch (err) {
|
||||
default:
|
||||
return err;
|
||||
@@ -1764,7 +1830,7 @@ retry_mapview:;
|
||||
return errno;
|
||||
ptr = MAP_FAILED;
|
||||
} else {
|
||||
const int err = errno;
|
||||
err = errno;
|
||||
switch (err) {
|
||||
default:
|
||||
return err;
|
||||
@@ -1780,7 +1846,7 @@ retry_mapview:;
|
||||
|
||||
if (ptr == MAP_FAILED) {
|
||||
/* unmap and map again whole region */
|
||||
if (!may_move) {
|
||||
if ((flags & MDBX_MRESIZE_MAY_UNMAP) == 0) {
|
||||
/* TODO: Perhaps here it is worth to implement suspend/resume threads
|
||||
* and perform unmap/map as like for Windows. */
|
||||
return MDBX_UNABLE_EXTEND_MAPSIZE;
|
||||
@@ -1789,16 +1855,44 @@ retry_mapview:;
|
||||
if (unlikely(munmap(map->address, map->limit)))
|
||||
return errno;
|
||||
|
||||
ptr = mmap(map->address, limit, mmap_prot, mmap_flags, map->fd, 0);
|
||||
// coverity[pass_freed_arg : FALSE]
|
||||
ptr = mmap(map->address, limit, mmap_prot,
|
||||
(flags & MDBX_MRESIZE_MAY_MOVE)
|
||||
? mmap_flags
|
||||
: mmap_flags | (MAP_FIXED_NOREPLACE ? MAP_FIXED_NOREPLACE
|
||||
: MAP_FIXED),
|
||||
map->fd, 0);
|
||||
if (MAP_FIXED_NOREPLACE != 0 && MAP_FIXED_NOREPLACE != MAP_FIXED &&
|
||||
unlikely(ptr == MAP_FAILED) && !(flags & MDBX_MRESIZE_MAY_MOVE) &&
|
||||
errno == /* kernel don't support MAP_FIXED_NOREPLACE */ EINVAL)
|
||||
// coverity[pass_freed_arg : FALSE]
|
||||
ptr = mmap(map->address, limit, mmap_prot, mmap_flags | MAP_FIXED,
|
||||
map->fd, 0);
|
||||
|
||||
if (unlikely(ptr == MAP_FAILED)) {
|
||||
ptr = mmap(map->address, map->limit, mmap_prot, mmap_flags, map->fd, 0);
|
||||
/* try to restore prev mapping */
|
||||
// coverity[pass_freed_arg : FALSE]
|
||||
ptr = mmap(map->address, map->limit, mmap_prot,
|
||||
(flags & MDBX_MRESIZE_MAY_MOVE)
|
||||
? mmap_flags
|
||||
: mmap_flags | (MAP_FIXED_NOREPLACE ? MAP_FIXED_NOREPLACE
|
||||
: MAP_FIXED),
|
||||
map->fd, 0);
|
||||
if (MAP_FIXED_NOREPLACE != 0 && MAP_FIXED_NOREPLACE != MAP_FIXED &&
|
||||
unlikely(ptr == MAP_FAILED) && !(flags & MDBX_MRESIZE_MAY_MOVE) &&
|
||||
errno == /* kernel don't support MAP_FIXED_NOREPLACE */ EINVAL)
|
||||
// coverity[pass_freed_arg : FALSE]
|
||||
ptr = mmap(map->address, map->limit, mmap_prot, mmap_flags | MAP_FIXED,
|
||||
map->fd, 0);
|
||||
if (unlikely(ptr == MAP_FAILED)) {
|
||||
VALGRIND_MAKE_MEM_NOACCESS(map->address, 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://github.com/erthink/libmdbx/pull/93#issuecomment-613687203
|
||||
*/
|
||||
ASAN_UNPOISON_MEMORY_REGION(map->address, map->limit);
|
||||
MDBX_ASAN_UNPOISON_MEMORY_REGION(
|
||||
map->address,
|
||||
(map->current < map->limit) ? map->current : map->limit);
|
||||
map->limit = 0;
|
||||
map->current = 0;
|
||||
map->address = nullptr;
|
||||
@@ -1814,12 +1908,12 @@ retry_mapview:;
|
||||
VALGRIND_MAKE_MEM_NOACCESS(map->address, 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://github.com/erthink/libmdbx/pull/93#issuecomment-613687203
|
||||
*/
|
||||
ASAN_UNPOISON_MEMORY_REGION(map->address, map->limit);
|
||||
* See https://github.com/erthink/libmdbx/pull/93#issuecomment-613687203 */
|
||||
MDBX_ASAN_UNPOISON_MEMORY_REGION(
|
||||
map->address, (map->current < map->limit) ? map->current : map->limit);
|
||||
|
||||
VALGRIND_MAKE_MEM_DEFINED(ptr, map->current);
|
||||
ASAN_UNPOISON_MEMORY_REGION(ptr, map->current);
|
||||
MDBX_ASAN_UNPOISON_MEMORY_REGION(ptr, map->current);
|
||||
map->address = ptr;
|
||||
}
|
||||
map->limit = limit;
|
||||
@@ -1841,7 +1935,7 @@ retry_mapview:;
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
MDBX_INTERNAL_FUNC __cold void mdbx_osal_jitter(bool tiny) {
|
||||
__cold MDBX_INTERNAL_FUNC void mdbx_osal_jitter(bool tiny) {
|
||||
for (;;) {
|
||||
#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || \
|
||||
defined(__x86_64__)
|
||||
@@ -1869,7 +1963,7 @@ MDBX_INTERNAL_FUNC __cold void mdbx_osal_jitter(bool tiny) {
|
||||
#elif defined(__APPLE__) || defined(__MACH__)
|
||||
#include <mach/mach_time.h>
|
||||
#elif defined(__linux__) || defined(__gnu_linux__)
|
||||
static __cold clockid_t choice_monoclock(void) {
|
||||
__cold static clockid_t choice_monoclock(void) {
|
||||
struct timespec probe;
|
||||
#if defined(CLOCK_BOOTTIME)
|
||||
if (clock_gettime(CLOCK_BOOTTIME, &probe) == 0)
|
||||
@@ -2061,7 +2155,7 @@ static LSTATUS mdbx_RegGetValue(HKEY hKey, LPCSTR lpSubKey, LPCSTR lpValue,
|
||||
}
|
||||
#endif
|
||||
|
||||
MDBX_MAYBE_UNUSED static __cold bool
|
||||
__cold MDBX_MAYBE_UNUSED static bool
|
||||
bootid_parse_uuid(bin128_t *s, const void *p, const size_t n) {
|
||||
if (n > 31) {
|
||||
unsigned bits = 0;
|
||||
@@ -2177,7 +2271,7 @@ __cold MDBX_INTERNAL_FUNC bin128_t mdbx_osal_bootid(void) {
|
||||
if (mdbx_RegGetValue(HKEY_LOCAL_MACHINE, HKLM_MicrosoftCryptography,
|
||||
"MachineGuid", &buf.MachineGuid,
|
||||
&len) == ERROR_SUCCESS &&
|
||||
len > 42 && len < sizeof(buf))
|
||||
len < sizeof(buf))
|
||||
got_machineid = bootid_parse_uuid(&bin, &buf.MachineGuid, len);
|
||||
|
||||
if (!got_machineid) {
|
||||
@@ -2241,12 +2335,16 @@ __cold MDBX_INTERNAL_FUNC bin128_t mdbx_osal_bootid(void) {
|
||||
0x03 /* SystemTmeOfDayInformation */, &buf.SysTimeOfDayInfo,
|
||||
sizeof(buf.SysTimeOfDayInfo), &len);
|
||||
if (NT_SUCCESS(status) &&
|
||||
len >= offsetof(union buf, SysTimeOfDayInfoHacked.BootTime) +
|
||||
sizeof(buf.SysTimeOfDayInfoHacked.BootTime) &&
|
||||
len >= offsetof(union buf, SysTimeOfDayInfoHacked.BootTimeBias) +
|
||||
sizeof(buf.SysTimeOfDayInfoHacked.BootTimeBias) &&
|
||||
buf.SysTimeOfDayInfoHacked.BootTime.QuadPart) {
|
||||
bootid_collect(&bin, &buf.SysTimeOfDayInfoHacked.BootTime,
|
||||
sizeof(buf.SysTimeOfDayInfoHacked.BootTime));
|
||||
got_boottime = true;
|
||||
const uint64_t UnbiasedBootTime =
|
||||
buf.SysTimeOfDayInfoHacked.BootTime.QuadPart -
|
||||
buf.SysTimeOfDayInfoHacked.BootTimeBias;
|
||||
if (UnbiasedBootTime) {
|
||||
bootid_collect(&bin, &UnbiasedBootTime, sizeof(UnbiasedBootTime));
|
||||
got_boottime = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!got_boottime) {
|
||||
|
||||
@@ -460,8 +460,8 @@ typedef struct mdbx_mmap_param {
|
||||
mdbx_filehandle_t fd;
|
||||
size_t limit; /* mapping length, but NOT a size of file nor DB */
|
||||
size_t current; /* mapped region size, i.e. the size of file and DB */
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
uint64_t filesize /* in-process cache of a file size */;
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
HANDLE section; /* memory-mapped section handle */
|
||||
#endif
|
||||
} mdbx_mmap_t;
|
||||
@@ -695,8 +695,10 @@ MDBX_INTERNAL_FUNC int mdbx_mmap(const int flags, mdbx_mmap_t *map,
|
||||
const size_t must, const size_t limit,
|
||||
const unsigned options);
|
||||
MDBX_INTERNAL_FUNC int mdbx_munmap(mdbx_mmap_t *map);
|
||||
MDBX_INTERNAL_FUNC int mdbx_mresize(int flags, mdbx_mmap_t *map, size_t current,
|
||||
size_t wanna, const bool may_move);
|
||||
#define MDBX_MRESIZE_MAY_MOVE 0x00000100
|
||||
#define MDBX_MRESIZE_MAY_UNMAP 0x00000200
|
||||
MDBX_INTERNAL_FUNC int mdbx_mresize(const int flags, mdbx_mmap_t *map,
|
||||
size_t size, size_t limit);
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
typedef struct {
|
||||
unsigned limit, count;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#error "API version mismatch! Had `git fetch --tags` done?"
|
||||
#endif
|
||||
|
||||
static const char sourcery[] = STRINGIFY(MDBX_BUILD_SOURCERY);
|
||||
static const char sourcery[] = MDBX_STRINGIFY(MDBX_BUILD_SOURCERY);
|
||||
|
||||
__dll_export
|
||||
#ifdef __attribute_used__
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
enable_language(CXX)
|
||||
include(../cmake/compiler.cmake)
|
||||
|
||||
add_executable(mdbx_test
|
||||
set(LIBMDBX_TEST_SOURCES
|
||||
base.h
|
||||
cases.cc
|
||||
chrono.cc
|
||||
@@ -30,6 +30,12 @@ add_executable(mdbx_test
|
||||
nested.cc
|
||||
)
|
||||
|
||||
if(NOT MDBX_BUILD_CXX)
|
||||
list(APPEND LIBMDBX_TEST_SOURCES "${MDBX_SOURCE_DIR}/mdbx.c++" ../mdbx.h++)
|
||||
endif()
|
||||
|
||||
add_executable(mdbx_test ${LIBMDBX_TEST_SOURCES})
|
||||
|
||||
if(MDBX_CXX_STANDARD)
|
||||
set_target_properties(mdbx_test PROPERTIES
|
||||
CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
#define MDBX_INTERNAL_FUNC
|
||||
#define MDBX_INTERNAL_VAR extern
|
||||
#define xMDBX_TOOLS /* Avoid using internal mdbx_assert() */
|
||||
#include "../mdbx.h"
|
||||
#include "../mdbx.h++"
|
||||
#include "../src/defs.h"
|
||||
#include "../src/osal.h"
|
||||
|
||||
|
||||
@@ -310,6 +310,7 @@ const struct option_verb mode_bits[] = {
|
||||
{"lifo", unsigned(MDBX_LIFORECLAIM)},
|
||||
{"perturb", unsigned(MDBX_PAGEPERTURB)},
|
||||
{"accede", unsigned(MDBX_ACCEDE)},
|
||||
{"exclusive", unsigned(MDBX_EXCLUSIVE)},
|
||||
{nullptr, 0}};
|
||||
|
||||
const struct option_verb table_bits[] = {
|
||||
|
||||
@@ -243,23 +243,25 @@ void maker::seek2end(serial_t &serial) const {
|
||||
serial = actor_params::serial_mask(mapping.width) - 1;
|
||||
}
|
||||
|
||||
bool maker::increment(serial_t &serial, int delta) const {
|
||||
bool maker::increment(serial_t &serial, int64_t delta) const {
|
||||
if (serial > actor_params::serial_mask(mapping.width)) {
|
||||
log_extra("keygen-increment: %" PRIu64 " > %" PRIu64 ", overflow", serial,
|
||||
actor_params::serial_mask(mapping.width));
|
||||
return false;
|
||||
}
|
||||
|
||||
serial_t target = serial + (int64_t)delta;
|
||||
serial_t target = serial + delta;
|
||||
if (target > actor_params::serial_mask(mapping.width) ||
|
||||
((delta > 0) ? target < serial : target > serial)) {
|
||||
log_extra("keygen-increment: %" PRIu64 "%-d => %" PRIu64 ", overflow",
|
||||
log_extra("keygen-increment: %" PRIu64 "%-" PRId64 " => %" PRIu64
|
||||
", overflow",
|
||||
serial, delta, target);
|
||||
return false;
|
||||
}
|
||||
|
||||
log_extra("keygen-increment: %" PRIu64 "%-d => %" PRIu64 ", continue", serial,
|
||||
delta, target);
|
||||
log_extra("keygen-increment: %" PRIu64 "%-" PRId64 " => %" PRIu64
|
||||
", continue",
|
||||
serial, delta, target);
|
||||
serial = target;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -130,8 +130,8 @@ public:
|
||||
bool is_unordered() const;
|
||||
void seek2end(serial_t &serial) const;
|
||||
|
||||
bool increment(serial_t &serial, int delta) const;
|
||||
bool increment_key_part(serial_t &serial, int delta,
|
||||
bool increment(serial_t &serial, int64_t delta) const;
|
||||
bool increment_key_part(serial_t &serial, int64_t delta,
|
||||
bool reset_value_part = true) const {
|
||||
if (reset_value_part) {
|
||||
serial_t value_part_bits = ((serial_t(1) << mapping.split) - 1);
|
||||
@@ -139,7 +139,7 @@ public:
|
||||
if (delta >= 0)
|
||||
serial &= ~value_part_bits;
|
||||
}
|
||||
return increment(serial, delta << mapping.split);
|
||||
return increment(serial, int64_t(uint64_t(delta) << mapping.split));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,36 +1,121 @@
|
||||
#!/usr/bin/env bash
|
||||
if ! which make cc c++ tee lz4 >/dev/null; then
|
||||
echo "Please install the following prerequisites: make cc c++ tee lz4 banner" >&2
|
||||
if ! which make cc c++ tee >/dev/null; then
|
||||
echo "Please install the following prerequisites: make cc c++ tee banner" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LIST=basic
|
||||
FROM=1
|
||||
UPTO=9999999
|
||||
MONITOR=
|
||||
LOOPS=
|
||||
SKIP_MAKE=no
|
||||
BANNER="$(which banner 2>/dev/null | echo echo)"
|
||||
UNAME="$(uname -s 2>/dev/null || echo Unknown)"
|
||||
set -euo pipefail
|
||||
DB_UPTO_MB=17408
|
||||
|
||||
## NOTE: Valgrind could produce some false-positive warnings
|
||||
## in multi-process environment with shared memory.
|
||||
## For instance, when the process "A" explicitly marks a memory
|
||||
## region as "undefined", the process "B" fill it,
|
||||
## and after this process "A" read such region, etc.
|
||||
#VALGRIND="valgrind --trace-children=yes --log-file=valgrind-%p.log --leak-check=full --track-origins=yes --error-exitcode=42 --suppressions=test/valgrind_suppress.txt"
|
||||
while [ -n "$1" ]
|
||||
do
|
||||
case "$1" in
|
||||
--help)
|
||||
echo "--multi Engage multi-process test scenario (default)"
|
||||
echo "--single Execute series of single-process tests (for QEMU, etc)"
|
||||
echo "--with-valgrind Run tests under Valgrind's memcheck tool"
|
||||
echo "--skip-make Don't (re)build libmdbx and test's executable"
|
||||
echo "--from NN Start iterating from the NN ops per test case"
|
||||
echo "--upto NN Don't run tests with more than NN ops per test case"
|
||||
echo "--loops NN Stop after the NN loops"
|
||||
echo "--dir PATH Specifies directory for test DB and other files (it will be cleared)"
|
||||
echo "--db-upto-mb NN Limits upper size of test DB to the NN megabytes"
|
||||
echo "--help Print this usage help and exit"
|
||||
exit -2
|
||||
;;
|
||||
--multi)
|
||||
LIST=basic
|
||||
;;
|
||||
--single)
|
||||
LIST="--nested --hill --append --ttl --copy"
|
||||
;;
|
||||
--with-valgrind)
|
||||
echo " NOTE: Valgrind could produce some false-positive warnings"
|
||||
echo " in multi-process environment with shared memory."
|
||||
echo " For instance, when the process 'A' explicitly marks a memory"
|
||||
echo " region as 'undefined', the process 'B' fill it,"
|
||||
echo " and after this process 'A' read such region, etc."
|
||||
MONITOR="valgrind --trace-children=yes --log-file=valgrind-%p.log --leak-check=full --track-origins=yes --error-exitcode=42 --suppressions=test/valgrind_suppress.txt"
|
||||
rm -f valgrind-*.log
|
||||
;;
|
||||
--skip-make)
|
||||
SKIP_MAKE=yes
|
||||
;;
|
||||
--from)
|
||||
FROM=$(($2))
|
||||
if [ -z "$FROM" -o "$FROM" -lt 1 ]; then
|
||||
echo "Invalid value '$FROM' for --from option"
|
||||
exit -2
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--upto)
|
||||
UPTO=$(($2))
|
||||
if [ -z "$UPTO" -o "$UPTO" -lt 1 ]; then
|
||||
echo "Invalid value '$UPTO' for --upto option"
|
||||
exit -2
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--loops)
|
||||
LOOPS=$(($2))
|
||||
if [ -z "$LOOPS" -o "$LOOPS" -lt 1 -o "$LOOPS" -gt 99 ]; then
|
||||
echo "Invalid value '$LOOPS' for --loops option"
|
||||
exit -2
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--dir)
|
||||
TESTDB_DIR="$2"
|
||||
if [ -z "$TESTDB_DIR" ]; then
|
||||
echo "Invalid value '$TESTDB_DIR' for --dir option"
|
||||
exit -2
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--db-upto-mb)
|
||||
DB_UPTO_MB=$(($2))
|
||||
if [ -z "$DB_UPTO_MB" -o "$DB_UPTO_MB" -lt 1 -o "$DB_UPTO_MB" -gt 4194304 ]; then
|
||||
echo "Invalid value '$DB_UPTO_MB' for --db-upto-mb option"
|
||||
exit -2
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option '$1'"
|
||||
exit -2
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
set -euo pipefail
|
||||
if [ -z "$MONITOR" ]; then
|
||||
if which time >/dev/null 2>/dev/null; then
|
||||
MONITOR=$(which time)
|
||||
if $MONITOR -o /dev/stdout true >/dev/null 2>/dev/null; then
|
||||
MONITOR="$MONITOR -o /dev/stdout"
|
||||
fi
|
||||
fi
|
||||
export MALLOC_CHECK_=7 MALLOC_PERTURB_=42
|
||||
fi
|
||||
|
||||
###############################################################################
|
||||
# 1. clean data from prev runs and examine available RAM
|
||||
|
||||
if [[ -v VALGRIND && ! -z "$VALGRIND" ]]; then
|
||||
rm -f valgrind-*.log
|
||||
else
|
||||
VALGRIND=time
|
||||
export MALLOC_CHECK_=7 MALLOC_PERTURB_=42
|
||||
fi
|
||||
|
||||
WANNA_MOUNT=0
|
||||
case ${UNAME} in
|
||||
Linux)
|
||||
MAKE=make
|
||||
if [[ ! -v TESTDB_DIR || -z "$TESTDB_DIR" ]]; then
|
||||
for old_test_dir in $(ls -d /dev/shm/mdbx-test.[0-9]*); do
|
||||
if [ -z "${TESTDB_DIR:-}" ]; then
|
||||
for old_test_dir in $(ls -d /dev/shm/mdbx-test.[0-9]* 2>/dev/null); do
|
||||
rm -rf $old_test_dir
|
||||
done
|
||||
TESTDB_DIR="/dev/shm/mdbx-test.$$"
|
||||
@@ -46,8 +131,8 @@ case ${UNAME} in
|
||||
|
||||
FreeBSD)
|
||||
MAKE=gmake
|
||||
if [[ ! -v TESTDB_DIR || -z "$TESTDB_DIR" ]]; then
|
||||
for old_test_dir in $(ls -d /tmp/mdbx-test.[0-9]*); do
|
||||
if [ -z "${TESTDB_DIR:-}" ]; then
|
||||
for old_test_dir in $(ls -d /tmp/mdbx-test.[0-9]* 2>/dev/null); do
|
||||
umount $old_test_dir && rm -r $old_test_dir
|
||||
done
|
||||
TESTDB_DIR="/tmp/mdbx-test.$$"
|
||||
@@ -56,14 +141,13 @@ case ${UNAME} in
|
||||
else
|
||||
mkdir -p $TESTDB_DIR && rm -f $TESTDB_DIR/*
|
||||
fi
|
||||
|
||||
ram_avail_mb=$(($(LC_ALL=C vmstat -s | grep -ie '[0-9] pages free$' | cut -d p -f 1) * ($(LC_ALL=C vmstat -s | grep -ie '[0-9] bytes per page$' | cut -d b -f 1) / 1024) / 1024))
|
||||
;;
|
||||
|
||||
Darwin)
|
||||
MAKE=make
|
||||
if [[ ! -v TESTDB_DIR || -z "$TESTDB_DIR" ]]; then
|
||||
for vol in $(ls -d /Volumes/mdx[0-9]*[0-9]tst); do
|
||||
if [ -z "${TESTDB_DIR:-}" ]; then
|
||||
for vol in $(ls -d /Volumes/mdx[0-9]*[0-9]tst 2>/dev/null); do
|
||||
disk=$(mount | grep $vol | cut -d ' ' -f 1)
|
||||
echo "umount: volume $vol disk $disk"
|
||||
hdiutil unmount $vol -force
|
||||
@@ -74,12 +158,10 @@ case ${UNAME} in
|
||||
else
|
||||
mkdir -p $TESTDB_DIR && rm -f $TESTDB_DIR/*
|
||||
fi
|
||||
|
||||
pagesize=$(($(LC_ALL=C vm_stat | grep -o 'page size of [0-9]\+ bytes' | cut -d' ' -f 4) / 1024))
|
||||
freepages=$(LC_ALL=C vm_stat | grep '^Pages free:' | grep -o '[0-9]\+\.$' | cut -d'.' -f 1)
|
||||
ram_avail_mb=$((pagesize * freepages / 1024))
|
||||
echo "pagesize ${pagesize}K, freepages ${freepages}, ram_avail_mb ${ram_avail_mb}"
|
||||
|
||||
;;
|
||||
|
||||
*)
|
||||
@@ -88,6 +170,8 @@ case ${UNAME} in
|
||||
;;
|
||||
esac
|
||||
|
||||
rm -f ${TESTDB_DIR}/*
|
||||
|
||||
###############################################################################
|
||||
# 2. estimate reasonable RAM space for test-db
|
||||
|
||||
@@ -117,8 +201,8 @@ fi
|
||||
# system immediately, as well some space is required for logs.
|
||||
#
|
||||
db_size_mb=$(((ram_avail_mb - ram_reserve4logs_mb) / 4))
|
||||
if [ $db_size_mb -gt 17408 ]; then
|
||||
db_size_mb=17408
|
||||
if [ $db_size_mb -gt $DB_UPTO_MB ]; then
|
||||
db_size_mb=$DB_UPTO_MB
|
||||
fi
|
||||
echo "=== use ${db_size_mb}M for DB"
|
||||
|
||||
@@ -150,48 +234,80 @@ case ${UNAME} in
|
||||
esac
|
||||
|
||||
###############################################################################
|
||||
# 4. Run basic test, i.e. `make check`
|
||||
# 4. build the test executables
|
||||
|
||||
${MAKE} -j2 all mdbx_test
|
||||
#${MAKE} TEST_DB=${TESTDB_DIR}/smoke.db TEST_LOG=${TESTDB_DIR}/smoke.log check
|
||||
rm -f ${TESTDB_DIR}/*
|
||||
if [ "$SKIP_MAKE" != "yes" ]; then
|
||||
${MAKE} -j$(which nproc >/dev/null 2>/dev/null && nproc || echo 2) build-test
|
||||
fi
|
||||
|
||||
###############################################################################
|
||||
# 5. run stochastic iterations
|
||||
|
||||
function join { local IFS="$1"; shift; echo "$*"; }
|
||||
function bit2option { local -n arr=$1; (( ($2&(1<<$3)) != 0 )) && echo -n '+' || echo -n '-'; echo "${arr[$3]}"; }
|
||||
if which lz4 >/dev/null; then
|
||||
function logger {
|
||||
lz4 > ${TESTDB_DIR}/long.log.lz4
|
||||
}
|
||||
elif which gzip >/dev/null; then
|
||||
function logger {
|
||||
gzip > ${TESTDB_DIR}/long.log.gz
|
||||
}
|
||||
else
|
||||
function logger {
|
||||
cat > ${TESTDB_DIR}/long.log
|
||||
}
|
||||
fi
|
||||
|
||||
syncmodes=("" ,+nosync-safe ,+nosync-utterly)
|
||||
|
||||
options=(writemap coalesce lifo notls perturb)
|
||||
|
||||
function bits2list {
|
||||
local -n arr=$1
|
||||
function join { local IFS="$1"; shift; echo "$*"; }
|
||||
|
||||
function bits2options {
|
||||
local bits=$1
|
||||
local i
|
||||
local list=()
|
||||
for ((i=0; i<${#arr[@]}; ++i)) do
|
||||
list[$i]=$(bit2option $1 $2 $i)
|
||||
for ((i = 0; i < ${#options[@]}; ++i)); do
|
||||
list[$i]=$( (( (bits & (1 << i)) != 0 )) && echo -n '+' || echo -n '-'; echo ${options[$i]})
|
||||
done
|
||||
join , "${list[@]}"
|
||||
join , ${list[@]}
|
||||
}
|
||||
|
||||
function failed {
|
||||
echo "FAILED" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
function check_deep {
|
||||
if [ "$case" = "basic" -o "$case" = "--hill" ]; then
|
||||
tee >(logger) | grep -e reach -e achieve
|
||||
else
|
||||
logger
|
||||
fi
|
||||
}
|
||||
|
||||
function probe {
|
||||
echo "----------------------------------------------- $(date)"
|
||||
echo "${caption}: $*"
|
||||
rm -f ${TESTDB_DIR}/* \
|
||||
&& ${VALGRIND} ./mdbx_test ${speculum} --random-writemap=no --ignore-dbfull --repeat=11 --pathname=${TESTDB_DIR}/long.db --cleanup-after=no "$@" \
|
||||
| tee >(lz4 > ${TESTDB_DIR}/long.log.lz4) | grep -e reach -e achieve \
|
||||
&& ${VALGRIND} ./mdbx_chk ${TESTDB_DIR}/long.db | tee ${TESTDB_DIR}/long-chk.log \
|
||||
&& ([ ! -e ${TESTDB_DIR}/long.db-copy ] || ${VALGRIND} ./mdbx_chk ${TESTDB_DIR}/long.db-copy | tee ${TESTDB_DIR}/long-chk-copy.log) \
|
||||
|| (echo "FAILED"; exit 1)
|
||||
echo "${caption}"
|
||||
rm -f ${TESTDB_DIR}/* || failed
|
||||
for case in $LIST
|
||||
do
|
||||
echo "Run ./mdbx_test ${speculum} --random-writemap=no --ignore-dbfull --repeat=11 --pathname=${TESTDB_DIR}/long.db --cleanup-after=no $@ $case"
|
||||
${MONITOR} ./mdbx_test ${speculum} --random-writemap=no --ignore-dbfull --repeat=11 --pathname=${TESTDB_DIR}/long.db --cleanup-after=no "$@" $case | check_deep \
|
||||
&& ${MONITOR} ./mdbx_chk ${TESTDB_DIR}/long.db | tee ${TESTDB_DIR}/long-chk.log \
|
||||
&& ([ ! -e ${TESTDB_DIR}/long.db-copy ] || ${MONITOR} ./mdbx_chk ${TESTDB_DIR}/long.db-copy | tee ${TESTDB_DIR}/long-chk-copy.log) \
|
||||
|| failed
|
||||
done
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
count=0
|
||||
loop=0
|
||||
cases='?'
|
||||
for nops in 10 33 100 333 1000 3333 10000 33333 100000 333333 1000000 3333333 10000000 33333333 100000000 333333333 1000000000; do
|
||||
if [ $nops -lt $FROM ]; then continue; fi
|
||||
if [ $nops -gt $UPTO ]; then echo "The '--upto $UPTO' limit reached"; break; fi
|
||||
if [ -n "$LOOPS" ] && [ $loop -ge "$LOOPS" ]; then echo "The '--loops $LOOPS' limit reached"; break; fi
|
||||
echo "======================================================================="
|
||||
wbatch=$((nops / 7 + 1))
|
||||
speculum=$([ $nops -le 1000 ] && echo '--speculum' || true)
|
||||
@@ -204,68 +320,70 @@ for nops in 10 33 100 333 1000 3333 10000 33333 100000 333333 1000000 3333333 10
|
||||
|
||||
split=30
|
||||
caption="Probe #$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed} basic
|
||||
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed}
|
||||
caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed} basic
|
||||
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed}
|
||||
caption="Probe #$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=min --size-upper=${db_size_mb}M --table=+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed} basic
|
||||
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed}
|
||||
|
||||
split=24
|
||||
caption="Probe #$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed} basic
|
||||
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed}
|
||||
caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed} basic
|
||||
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed}
|
||||
caption="Probe #$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=min --size-upper=${db_size_mb}M --table=+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed} basic
|
||||
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed}
|
||||
|
||||
split=16
|
||||
caption="Probe #$((++count)) int-key,w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed} basic
|
||||
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+key.integer,-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed}
|
||||
caption="Probe #$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed} basic
|
||||
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed}
|
||||
caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed} basic
|
||||
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed}
|
||||
caption="Probe #$((++count)) w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=min --size-upper=${db_size_mb}M --table=-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed} basic
|
||||
--pagesize=min --size-upper-upto=${db_size_mb}M --table=-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed}
|
||||
caption="Probe #$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=min --size-upper=${db_size_mb}M --table=+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed} basic
|
||||
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed}
|
||||
|
||||
split=4
|
||||
caption="Probe #$((++count)) int-key,w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed} basic
|
||||
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+key.integer,-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed}
|
||||
caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed} basic
|
||||
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed}
|
||||
caption="Probe #$((++count)) w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=min --size-upper=${db_size_mb}M --table=-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed} basic
|
||||
--pagesize=min --size-upper-upto=${db_size_mb}M --table=-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
|
||||
--keygen.seed=${seed}
|
||||
done # options
|
||||
loop=$((loop + 1))
|
||||
if [ -n "$LOOPS" ] && [ $loop -ge "$LOOPS" ]; then break; fi
|
||||
cases="${subcase}"
|
||||
wbatch=$(((wbatch > 7) ? wbatch / 7 : 1))
|
||||
if [ $wbatch -eq 1 -o $((nops / wbatch)) -gt 1000 ]; then break; fi
|
||||
|
||||
67
test/main.cc
67
test/main.cc
@@ -92,6 +92,8 @@ MDBX_NORETURN void usage(void) {
|
||||
" --mode={[+-]FLAG}[,[+-]FLAG]...\n"
|
||||
" nosubdir == MDBX_NOSUBDIR\n"
|
||||
" rdonly == MDBX_RDONLY\n"
|
||||
" exclusive == MDBX_EXCLUSIVE\n"
|
||||
" accede == MDBX_ACCEDE\n"
|
||||
" nometasync == MDBX_NOMETASYNC\n"
|
||||
" lifo == MDBX_LIFORECLAIM\n"
|
||||
" coalesce == MDBX_COALESCE\n"
|
||||
@@ -222,6 +224,43 @@ void cleanup() {
|
||||
log_trace("<< cleanup");
|
||||
}
|
||||
|
||||
static void fixup4qemu(actor_params ¶ms) {
|
||||
#ifdef MDBX_SAFE4QEMU
|
||||
#if MDBX_WORDBITS == 32
|
||||
intptr_t safe4qemu_limit = size_t(512) << 20 /* 512 megabytes */;
|
||||
#if defined(__SANITIZE_ADDRESS__)
|
||||
safe4qemu_limit >>= 1;
|
||||
#else
|
||||
if (RUNNING_ON_VALGRIND)
|
||||
safe4qemu_limit >>= 1;
|
||||
#endif /* __SANITIZE_ADDRESS__ */
|
||||
|
||||
if (params.size_lower > safe4qemu_limit ||
|
||||
params.size_now > safe4qemu_limit ||
|
||||
params.size_upper > safe4qemu_limit) {
|
||||
params.size_upper = std::min(params.size_upper, safe4qemu_limit);
|
||||
params.size_now = std::min(params.size_now, params.size_upper);
|
||||
params.size_lower = std::min(params.size_lower, params.size_now);
|
||||
log_notice("workaround: for conformance 32-bit build with "
|
||||
"QEMU/ASAN/Valgrind database size reduced to %zu megabytes",
|
||||
safe4qemu_limit >> 20);
|
||||
}
|
||||
#endif /* MDBX_WORDBITS == 32 */
|
||||
|
||||
#if defined(__alpha__) || defined(__alpha) || defined(__sparc__) || \
|
||||
defined(__sparc) || defined(__sparc64__) || defined(__sparc64)
|
||||
if (params.size_lower != params.size_upper) {
|
||||
log_notice(
|
||||
"workaround: for conformance Alpha/Sparc build with QEMU/ASAN/Valgrind "
|
||||
"enforce fixed database size %zu megabytes",
|
||||
params.size_upper >> 20);
|
||||
params.size_lower = params.size_now = params.size_upper;
|
||||
}
|
||||
#endif /* Alpha || Sparc */
|
||||
#endif /* MDBX_SAFE4QEMU */
|
||||
(void)params;
|
||||
}
|
||||
|
||||
int main(int argc, char *const argv[]) {
|
||||
|
||||
#ifdef _DEBUG
|
||||
@@ -254,6 +293,7 @@ int main(int argc, char *const argv[]) {
|
||||
const char *value = nullptr;
|
||||
|
||||
if (config::parse_option(argc, argv, narg, "case", &value)) {
|
||||
fixup4qemu(params);
|
||||
testcase_setup(value, params, last_space_id);
|
||||
continue;
|
||||
}
|
||||
@@ -327,6 +367,15 @@ int main(int argc, char *const argv[]) {
|
||||
mdbx_limits_dbsize_min(params.pagesize),
|
||||
mdbx_limits_dbsize_max(params.pagesize)))
|
||||
continue;
|
||||
int64_t i64 = params.size_upper;
|
||||
if (config::parse_option(argc, argv, narg, "size-upper-upto", i64,
|
||||
int64_t(mdbx_limits_dbsize_min(params.pagesize)),
|
||||
INT64_MAX, -1)) {
|
||||
if (i64 > mdbx_limits_dbsize_max(params.pagesize))
|
||||
i64 = mdbx_limits_dbsize_max(params.pagesize);
|
||||
params.size_upper = intptr_t(i64);
|
||||
continue;
|
||||
}
|
||||
if (config::parse_option_intptr(argc, argv, narg, "size-upper",
|
||||
params.size_upper,
|
||||
mdbx_limits_dbsize_min(params.pagesize),
|
||||
@@ -477,38 +526,47 @@ int main(int argc, char *const argv[]) {
|
||||
continue;
|
||||
}
|
||||
if (config::parse_option(argc, argv, narg, "hill", &value, "auto")) {
|
||||
fixup4qemu(params);
|
||||
configure_actor(last_space_id, ac_hill, value, params);
|
||||
continue;
|
||||
}
|
||||
if (config::parse_option(argc, argv, narg, "jitter", nullptr)) {
|
||||
fixup4qemu(params);
|
||||
configure_actor(last_space_id, ac_jitter, value, params);
|
||||
continue;
|
||||
}
|
||||
if (config::parse_option(argc, argv, narg, "dead.reader", nullptr)) {
|
||||
fixup4qemu(params);
|
||||
configure_actor(last_space_id, ac_deadread, value, params);
|
||||
continue;
|
||||
}
|
||||
if (config::parse_option(argc, argv, narg, "dead.writer", nullptr)) {
|
||||
fixup4qemu(params);
|
||||
configure_actor(last_space_id, ac_deadwrite, value, params);
|
||||
continue;
|
||||
}
|
||||
if (config::parse_option(argc, argv, narg, "try", nullptr)) {
|
||||
fixup4qemu(params);
|
||||
configure_actor(last_space_id, ac_try, value, params);
|
||||
continue;
|
||||
}
|
||||
if (config::parse_option(argc, argv, narg, "copy", nullptr)) {
|
||||
fixup4qemu(params);
|
||||
configure_actor(last_space_id, ac_copy, value, params);
|
||||
continue;
|
||||
}
|
||||
if (config::parse_option(argc, argv, narg, "append", nullptr)) {
|
||||
fixup4qemu(params);
|
||||
configure_actor(last_space_id, ac_append, value, params);
|
||||
continue;
|
||||
}
|
||||
if (config::parse_option(argc, argv, narg, "ttl", nullptr)) {
|
||||
fixup4qemu(params);
|
||||
configure_actor(last_space_id, ac_ttl, value, params);
|
||||
continue;
|
||||
}
|
||||
if (config::parse_option(argc, argv, narg, "nested", nullptr)) {
|
||||
fixup4qemu(params);
|
||||
configure_actor(last_space_id, ac_nested, value, params);
|
||||
continue;
|
||||
}
|
||||
@@ -522,9 +580,10 @@ int main(int argc, char *const argv[]) {
|
||||
global::config::console_mode))
|
||||
continue;
|
||||
|
||||
if (*argv[narg] != '-')
|
||||
if (*argv[narg] != '-') {
|
||||
fixup4qemu(params);
|
||||
testcase_setup(argv[narg], params, last_space_id);
|
||||
else
|
||||
} else
|
||||
failure("Unknown option '%s'. Try --help\n", argv[narg]);
|
||||
}
|
||||
|
||||
@@ -550,10 +609,6 @@ int main(int argc, char *const argv[]) {
|
||||
if (global::config::cleanup_before)
|
||||
cleanup();
|
||||
|
||||
log_trace(">> probe entropy_ticks()");
|
||||
entropy_ticks();
|
||||
log_trace("<< probe entropy_ticks()");
|
||||
|
||||
if (global::actors.size() == 1) {
|
||||
logging::setup("main");
|
||||
global::singlemode = true;
|
||||
|
||||
@@ -110,16 +110,24 @@ bool testcase_nested::teardown() {
|
||||
}
|
||||
|
||||
void testcase_nested::push_txn() {
|
||||
MDBX_txn *txn;
|
||||
MDBX_txn *nested_txn;
|
||||
MDBX_txn_flags_t flags = MDBX_txn_flags_t(
|
||||
prng32() & uint32_t(MDBX_TXN_NOSYNC | MDBX_TXN_NOMETASYNC));
|
||||
int err = mdbx_txn_begin(db_guard.get(), txn_guard.get(), flags, &txn);
|
||||
int err = mdbx_txn_begin(db_guard.get(), txn_guard.get(), flags, &nested_txn);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
failure_perror("mdbx_txn_begin(nested)", err);
|
||||
stack.emplace(scoped_txn_guard(txn), serial, fifo, speculum);
|
||||
std::swap(txn_guard, std::get<0>(stack.top()));
|
||||
/* CLANG/LLVM C++ library could stupidly copy std::set<> item-by-item,
|
||||
* i.e. with insertion(s) & comparison(s), which will cause null dereference
|
||||
* during call mdbx_cmp() with zero txn. So it is the workaround for this:
|
||||
* - explicitly set txn_guard with the new nested txn;
|
||||
* - explicitly copy the `speculum` (an instance of std::set<>). */
|
||||
scoped_txn_guard nested_txn_guard(nested_txn);
|
||||
txn_guard.swap(nested_txn_guard);
|
||||
SET speculum_snapshot(speculum);
|
||||
stack.emplace(std::move(nested_txn_guard), serial, fifo,
|
||||
std::move(speculum_snapshot));
|
||||
log_verbose("begin level#%zu txn #%" PRIu64 ", flags 0x%x, serial %" PRIu64,
|
||||
stack.size(), mdbx_txn_id(txn), flags, serial);
|
||||
stack.size(), mdbx_txn_id(nested_txn), flags, serial);
|
||||
}
|
||||
|
||||
bool testcase_nested::pop_txn(bool abort) {
|
||||
@@ -192,7 +200,7 @@ bool testcase_nested::trim_tail(unsigned window_width) {
|
||||
while (fifo.size() > window_width) {
|
||||
uint64_t tail_serial = fifo.back().first;
|
||||
const unsigned tail_count = fifo.back().second;
|
||||
log_verbose("nested: pop-tail (serial %" PRIu64 ", count %u)",
|
||||
log_verbose("nested: trim-tail (serial %" PRIu64 ", count %u)",
|
||||
tail_serial, tail_count);
|
||||
fifo.pop_back();
|
||||
for (unsigned n = 0; n < tail_count; ++n) {
|
||||
|
||||
44
test/test.cc
44
test/test.cc
@@ -197,6 +197,18 @@ int testcase::breakable_commit() {
|
||||
log_trace(">> txn_commit");
|
||||
assert(txn_guard);
|
||||
|
||||
/* CLANG/LLVM C++ library could stupidly copy std::set<> item-by-item,
|
||||
* i.e. with insertion(s) & comparison(s), which will cause null dereference
|
||||
* during call mdbx_cmp() with zero txn. So it is the workaround for this:
|
||||
* - explicitly make copies of the `speculums`;
|
||||
* - explicitly move relevant copy after transaction commit. */
|
||||
SET speculum_committed_copy(ItemCompare(this)),
|
||||
speculum_copy(ItemCompare(this));
|
||||
if (need_speculum_assign) {
|
||||
speculum_committed_copy = speculum_committed;
|
||||
speculum_copy = speculum;
|
||||
}
|
||||
|
||||
MDBX_txn *txn = txn_guard.release();
|
||||
txn_inject_writefault(txn);
|
||||
int rc = mdbx_txn_commit(txn);
|
||||
@@ -207,9 +219,9 @@ int testcase::breakable_commit() {
|
||||
if (need_speculum_assign) {
|
||||
need_speculum_assign = false;
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
speculum = speculum_committed;
|
||||
speculum = std::move(speculum_committed_copy);
|
||||
else
|
||||
speculum_committed = speculum;
|
||||
speculum_committed = std::move(speculum_copy);
|
||||
}
|
||||
|
||||
log_trace("<< txn_commit: %s", rc ? "failed" : "Ok");
|
||||
@@ -714,8 +726,8 @@ void testcase::speculum_check_cursor(const char *where, const char *stage,
|
||||
const testcase::SET::const_iterator &it,
|
||||
MDBX_cursor *cursor,
|
||||
const MDBX_cursor_op op) const {
|
||||
MDBX_val cursor_key = {};
|
||||
MDBX_val cursor_data = {};
|
||||
MDBX_val cursor_key = {0, 0};
|
||||
MDBX_val cursor_data = {0, 0};
|
||||
int err;
|
||||
if (it != speculum.end() && std::next(it) == speculum.end() &&
|
||||
op == MDBX_PREV && (config.params.table_flags & MDBX_DUPSORT)) {
|
||||
@@ -1018,7 +1030,7 @@ bool testcase::speculum_verify() {
|
||||
char dump_mkey[128], dump_mvalue[128];
|
||||
|
||||
MDBX_cursor *cursor;
|
||||
int err = mdbx_cursor_open(txn_guard.get(), dbi, &cursor);
|
||||
int eof, err = mdbx_cursor_open(txn_guard.get(), dbi, &cursor);
|
||||
if (err != MDBX_SUCCESS)
|
||||
failure_perror("mdbx_cursor_open()", err);
|
||||
|
||||
@@ -1034,14 +1046,20 @@ bool testcase::speculum_verify() {
|
||||
if (err != MDBX_SUCCESS) {
|
||||
akey.iov_len = avalue.iov_len = 0;
|
||||
akey.iov_base = avalue.iov_base = nullptr;
|
||||
} else {
|
||||
eof = mdbx_cursor_eof(cursor);
|
||||
if (eof != MDBX_RESULT_FALSE) {
|
||||
log_error("false-positive cursor-eof %u/%u: db{%s, %s}, rc %i", n,
|
||||
extra, mdbx_dump_val(&akey, dump_key, sizeof(dump_key)),
|
||||
mdbx_dump_val(&avalue, dump_value, sizeof(dump_value)), eof);
|
||||
rc = false;
|
||||
}
|
||||
}
|
||||
const auto S_key = iov2dataview(akey);
|
||||
const auto S_data = iov2dataview(avalue);
|
||||
if (it != speculum.cend()) {
|
||||
mkey.iov_base = (void *)it->first.c_str();
|
||||
mkey.iov_len = it->first.size();
|
||||
mvalue.iov_base = (void *)it->second.c_str();
|
||||
mvalue.iov_len = it->second.size();
|
||||
mkey = it->first;
|
||||
mvalue = it->second;
|
||||
}
|
||||
if (err == MDBX_SUCCESS && it != speculum.cend() && S_key == it->first &&
|
||||
S_data == it->second) {
|
||||
@@ -1091,6 +1109,14 @@ bool testcase::speculum_verify() {
|
||||
n += 1;
|
||||
}
|
||||
|
||||
if (err == MDBX_NOTFOUND) {
|
||||
eof = mdbx_cursor_eof(cursor);
|
||||
if (eof != MDBX_RESULT_TRUE) {
|
||||
eof = mdbx_cursor_eof(cursor);
|
||||
log_error("false-negative cursor-eof: %u, rc %i", n, eof);
|
||||
rc = false;
|
||||
}
|
||||
}
|
||||
mdbx_cursor_close(cursor);
|
||||
return rc;
|
||||
}
|
||||
|
||||
18
test/test.h
18
test/test.h
@@ -130,16 +130,12 @@ public:
|
||||
};
|
||||
|
||||
#define REGISTER_TESTCASE(NAME) \
|
||||
static registry::factory<testcase_##NAME> gRegister_##NAME(ac_##NAME, \
|
||||
STRINGIFY(NAME))
|
||||
static registry::factory<testcase_##NAME> gRegister_##NAME( \
|
||||
ac_##NAME, MDBX_STRINGIFY(NAME))
|
||||
|
||||
class testcase {
|
||||
protected:
|
||||
#if HAVE_cxx17_std_string_view
|
||||
using data_view = std::string_view;
|
||||
#else
|
||||
using data_view = std::string;
|
||||
#endif
|
||||
using data_view = mdbx::slice;
|
||||
static inline data_view iov2dataview(const MDBX_val &v) {
|
||||
return (v.iov_base && v.iov_len)
|
||||
? data_view(static_cast<const char *>(v.iov_base), v.iov_len)
|
||||
@@ -149,7 +145,8 @@ protected:
|
||||
return iov2dataview(b->value);
|
||||
}
|
||||
|
||||
using Item = std::pair<std::string, std::string>;
|
||||
using Item = std::pair<::mdbx::buffer<>, ::mdbx::buffer<>>;
|
||||
|
||||
static MDBX_val dataview2iov(const data_view &v) {
|
||||
MDBX_val r;
|
||||
r.iov_base = (void *)v.data();
|
||||
@@ -158,10 +155,13 @@ protected:
|
||||
}
|
||||
struct ItemCompare {
|
||||
const testcase *context;
|
||||
ItemCompare(const testcase *owner) : context(owner) {}
|
||||
ItemCompare(const testcase *owner) : context(owner) {
|
||||
/* The context->txn_guard may be empty/null here */
|
||||
}
|
||||
|
||||
bool operator()(const Item &a, const Item &b) const {
|
||||
MDBX_val va = dataview2iov(a.first), vb = dataview2iov(b.first);
|
||||
assert(context->txn_guard.get() != nullptr);
|
||||
int cmp = mdbx_cmp(context->txn_guard.get(), context->dbi, &va, &vb);
|
||||
if (cmp == 0 &&
|
||||
(context->config.params.table_flags & MDBX_DUPSORT) != 0) {
|
||||
|
||||
178
test/utils.cc
178
test/utils.cc
@@ -101,164 +101,6 @@ bool is_samedata(const MDBX_val *a, const MDBX_val *b) {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/* TODO: replace my 'libmera' from t1ha. */
|
||||
uint64_t entropy_ticks(void) {
|
||||
#if defined(EMSCRIPTEN)
|
||||
return (uint64_t)emscripten_get_now();
|
||||
#endif /* EMSCRIPTEN */
|
||||
|
||||
#if defined(__APPLE__) || defined(__MACH__)
|
||||
return mach_absolute_time();
|
||||
#endif /* defined(__APPLE__) || defined(__MACH__) */
|
||||
|
||||
#if defined(__sun__) || defined(__sun)
|
||||
return gethrtime();
|
||||
#endif /* __sun__ */
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
|
||||
#if defined(__ia64__)
|
||||
uint64_t ticks;
|
||||
__asm __volatile("mov %0=ar.itc" : "=r"(ticks));
|
||||
return ticks;
|
||||
#elif defined(__hppa__)
|
||||
uint64_t ticks;
|
||||
__asm __volatile("mfctl 16, %0" : "=r"(ticks));
|
||||
return ticks;
|
||||
#elif defined(__s390__)
|
||||
uint64_t ticks;
|
||||
__asm __volatile("stck 0(%0)" : : "a"(&(ticks)) : "memory", "cc");
|
||||
return ticks;
|
||||
#elif defined(__alpha__)
|
||||
uint64_t ticks;
|
||||
__asm __volatile("rpcc %0" : "=r"(ticks));
|
||||
return ticks;
|
||||
#elif defined(__sparc__) || defined(__sparc) || defined(__sparc64__) || \
|
||||
defined(__sparc64) || defined(__sparc_v8plus__) || \
|
||||
defined(__sparc_v8plus) || defined(__sparc_v8plusa__) || \
|
||||
defined(__sparc_v8plusa) || defined(__sparc_v9__) || defined(__sparc_v9)
|
||||
|
||||
union {
|
||||
uint64_t u64;
|
||||
struct {
|
||||
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
uint32_t h, l;
|
||||
#else
|
||||
uint32_t l, h;
|
||||
#endif
|
||||
} u32;
|
||||
} cycles;
|
||||
|
||||
#if defined(__sparc_v8plus__) || defined(__sparc_v8plusa__) || \
|
||||
defined(__sparc_v9__) || defined(__sparc_v8plus) || \
|
||||
defined(__sparc_v8plusa) || defined(__sparc_v9)
|
||||
|
||||
#if UINTPTR_MAX > 0xffffFFFFul || ULONG_MAX > 0xffffFFFFul || \
|
||||
defined(__sparc64__) || defined(__sparc64)
|
||||
__asm __volatile("rd %%tick, %0" : "=r"(cycles.u64));
|
||||
#else
|
||||
__asm __volatile("rd %%tick, %1; srlx %1, 32, %0"
|
||||
: "=r"(cycles.u32.h), "=r"(cycles.u32.l));
|
||||
#endif /* __sparc64__ */
|
||||
|
||||
#else
|
||||
__asm __volatile(".byte 0x83, 0x41, 0x00, 0x00; mov %%g1, %0"
|
||||
: "=r"(cycles.u64)
|
||||
:
|
||||
: "%g1");
|
||||
#endif /* __sparc8plus__ || __sparc_v9__ */
|
||||
return cycles.u64;
|
||||
|
||||
#elif (defined(__powerpc64__) || defined(__ppc64__) || defined(__ppc64) || \
|
||||
defined(__powerpc64))
|
||||
uint64_t ticks;
|
||||
__asm __volatile("mfspr %0, 268" : "=r"(ticks));
|
||||
return ticks;
|
||||
#elif (defined(__powerpc__) || defined(__ppc__) || defined(__powerpc) || \
|
||||
defined(__ppc))
|
||||
#if UINTPTR_MAX > 0xffffFFFFul || ULONG_MAX > 0xffffFFFFul
|
||||
uint64_t ticks;
|
||||
__asm __volatile("mftb %0" : "=r"(ticks));
|
||||
*now = ticks;
|
||||
#else
|
||||
uint64_t ticks;
|
||||
uint32_t low, high_before, high_after;
|
||||
__asm __volatile("mftbu %0; mftb %1; mftbu %2"
|
||||
: "=r"(high_before), "=r"(low), "=r"(high_after));
|
||||
ticks = (uint64_t)high_after << 32;
|
||||
ticks |= low & /* zeroes if high part has changed */
|
||||
~(high_before - high_after);
|
||||
#endif
|
||||
#elif (defined(__aarch64__) || (defined(__ARM_ARCH) && __ARM_ARCH > 7)) && \
|
||||
!defined(MDBX_SAFE4QEMU)
|
||||
uint64_t virtual_timer;
|
||||
__asm __volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer));
|
||||
return virtual_timer;
|
||||
#elif (defined(__ARM_ARCH) && __ARM_ARCH > 5 && __ARM_ARCH < 8) || \
|
||||
defined(_M_ARM)
|
||||
static uint32_t pmcntenset = 0x00425B00;
|
||||
if (unlikely(pmcntenset == 0x00425B00)) {
|
||||
uint32_t pmuseren;
|
||||
#ifdef _M_ARM
|
||||
pmuseren = _MoveFromCoprocessor(15, 0, 9, 14, 0);
|
||||
#else
|
||||
__asm("mrc p15, 0, %0, c9, c14, 0" : "=r"(pmuseren));
|
||||
#endif
|
||||
if (1 & pmuseren /* Is it allowed for user mode code? */) {
|
||||
#ifdef _M_ARM
|
||||
pmcntenset = _MoveFromCoprocessor(15, 0, 9, 12, 1);
|
||||
#else
|
||||
__asm("mrc p15, 0, %0, c9, c12, 1" : "=r"(pmcntenset));
|
||||
#endif
|
||||
} else
|
||||
pmcntenset = 0;
|
||||
}
|
||||
if (pmcntenset & 0x80000000ul /* Is it counting? */) {
|
||||
#ifdef _M_ARM
|
||||
return __rdpmccntr64();
|
||||
#else
|
||||
uint32_t pmccntr;
|
||||
__asm __volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr));
|
||||
return pmccntr;
|
||||
#endif
|
||||
}
|
||||
#elif ((defined(_MIPS_ISA) && defined(_MIPS_ISA_MIPS2) && \
|
||||
_MIPS_ISA >= _MIPS_ISA_MIPS2) || \
|
||||
(defined(__mips) && __mips >= 2) || defined(_R4000)) && \
|
||||
!defined(MDBX_SAFE4QEMU) /* QEMU may not emulate the CC register \
|
||||
(High-resolution cycle counter) */
|
||||
unsigned count;
|
||||
__asm __volatile("rdhwr %0, $2" : "=r"(count));
|
||||
return count;
|
||||
#endif /* arch selector */
|
||||
#endif /* __GNUC__ || __clang__ */
|
||||
|
||||
#if defined(__e2k__) || defined(__ia32__)
|
||||
return __rdtsc();
|
||||
#elif defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS)
|
||||
LARGE_INTEGER PerformanceCount;
|
||||
if (QueryPerformanceCounter(&PerformanceCount))
|
||||
return PerformanceCount.QuadPart;
|
||||
return GetTickCount64();
|
||||
#else
|
||||
struct timespec ts;
|
||||
#if defined(CLOCK_MONOTONIC_COARSE)
|
||||
clockid_t clk_id = CLOCK_MONOTONIC_COARSE;
|
||||
#elif defined(CLOCK_MONOTONIC_RAW)
|
||||
clockid_t clk_id = CLOCK_MONOTONIC_RAW;
|
||||
#else
|
||||
clockid_t clk_id = CLOCK_MONOTONIC;
|
||||
#endif
|
||||
int rc = clock_gettime(clk_id, &ts);
|
||||
if (unlikely(rc))
|
||||
failure_perror("clock_gettime()", rc);
|
||||
|
||||
return (((uint64_t)ts.tv_sec) << 32) + ts.tv_nsec;
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
uint64_t prng64_white(uint64_t &state) {
|
||||
state = prng64_map2_careless(state);
|
||||
return bleach64(state);
|
||||
@@ -303,8 +145,6 @@ uint64_t prng64(void) { return prng64_white(prng_state); }
|
||||
|
||||
void prng_fill(void *ptr, size_t bytes) { prng_fill(prng_state, ptr, bytes); }
|
||||
|
||||
uint64_t entropy_white() { return bleach64(entropy_ticks()); }
|
||||
|
||||
double double_from_lower(uint64_t salt) {
|
||||
#ifdef IEEE754_DOUBLE_BIAS
|
||||
ieee754_double r;
|
||||
@@ -336,25 +176,25 @@ double double_from_upper(uint64_t salt) {
|
||||
#endif
|
||||
}
|
||||
|
||||
bool flipcoin() { return bleach32((uint32_t)entropy_ticks()) & 1; }
|
||||
bool flipcoin_x2() { return (bleach32((uint32_t)entropy_ticks()) & 3) == 0; }
|
||||
bool flipcoin_x3() { return (bleach32((uint32_t)entropy_ticks()) & 7) == 0; }
|
||||
bool flipcoin_x4() { return (bleach32((uint32_t)entropy_ticks()) & 15) == 0; }
|
||||
bool flipcoin() { return prng32() & 1; }
|
||||
bool flipcoin_x2() { return (prng32() & 3) == 0; }
|
||||
bool flipcoin_x3() { return (prng32() & 7) == 0; }
|
||||
bool flipcoin_x4() { return (prng32() & 15) == 0; }
|
||||
bool flipcoin_n(unsigned n) {
|
||||
return (bleach64(entropy_ticks()) & ((UINT64_C(1) << n) - 1)) == 0;
|
||||
return (prng64() & ((UINT64_C(1) << n) - 1)) == 0;
|
||||
}
|
||||
|
||||
bool jitter(unsigned probability_percent) {
|
||||
const uint32_t top = UINT32_MAX - UINT32_MAX % 100;
|
||||
uint32_t dice, edge = (top) / 100 * probability_percent;
|
||||
do
|
||||
dice = bleach32((uint32_t)entropy_ticks());
|
||||
dice = prng32();
|
||||
while (dice >= top);
|
||||
return dice < edge;
|
||||
}
|
||||
|
||||
void jitter_delay(bool extra) {
|
||||
unsigned dice = entropy_white() & 3;
|
||||
unsigned dice = prng32() & 3;
|
||||
if (dice == 0) {
|
||||
log_trace("== jitter.no-delay");
|
||||
} else {
|
||||
@@ -367,8 +207,8 @@ void jitter_delay(bool extra) {
|
||||
osal_yield();
|
||||
cpu_relax();
|
||||
if (dice > 2) {
|
||||
unsigned us = entropy_white() &
|
||||
(extra ? 0xffff /* 656 ms */ : 0x3ff /* 1 ms */);
|
||||
unsigned us =
|
||||
prng32() & (extra ? 0xffff /* 656 ms */ : 0x3ff /* 1 ms */);
|
||||
log_trace("== jitter.delay: %0.6f", us / 1000000.0);
|
||||
osal_udelay(us);
|
||||
}
|
||||
|
||||
@@ -292,8 +292,6 @@ inline bool is_samedata(const MDBX_val &a, const MDBX_val &b) {
|
||||
}
|
||||
std::string format(const char *fmt, ...);
|
||||
|
||||
uint64_t entropy_ticks(void);
|
||||
uint64_t entropy_white(void);
|
||||
static inline uint64_t bleach64(uint64_t v) {
|
||||
// Tommy Ettinger, https://www.blogger.com/profile/04953541827437796598
|
||||
// http://mostlymangling.blogspot.com/2019/01/better-stronger-mixer-and-test-procedure.html
|
||||
|
||||
@@ -22,6 +22,14 @@
|
||||
...
|
||||
fun:mdbx_mapresize
|
||||
}
|
||||
{
|
||||
msync-wipe-steady
|
||||
Memcheck:Param
|
||||
msync(start)
|
||||
fun:msync
|
||||
...
|
||||
fun:mdbx_wipe_steady
|
||||
}
|
||||
|
||||
# single-page flush by pwrite()
|
||||
{
|
||||
@@ -42,16 +50,15 @@
|
||||
}
|
||||
|
||||
# modern Valgrind don't support the `vector[...]` pattern
|
||||
#{
|
||||
# pwritev-page-flush
|
||||
# Memcheck:Param
|
||||
# pwritev(vector[...])
|
||||
# fun:pwritev
|
||||
# ...
|
||||
# fun:mdbx_iov_write*
|
||||
#}
|
||||
# for((i=0;i<64;++i)); do echo -e "{\n pwritev-page-flush-$i\n Memcheck:Param\n pwritev(vector[$i])\n fun:pwritev\n ...\n fun:mdbx_iov_write*\n}"; done >> valgrind_suppress.txt
|
||||
|
||||
{
|
||||
pwritev-page-flush
|
||||
Memcheck:Param
|
||||
pwritev(vector[...])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:mdbx_iov_write*
|
||||
}
|
||||
{
|
||||
pwritev-page-flush-0
|
||||
Memcheck:Param
|
||||
|
||||
Reference in New Issue
Block a user