mdbx++: Initial C++ API (some extra methods are not implemented).

Change-Id: I0478d0c94dcd12b52916e87815e5731817407c3c
This commit is contained in:
Leonid Yuriev 2020-08-22 20:19:46 +03:00
parent e3f0db7708
commit 88a4b8cb9b
22 changed files with 4507 additions and 168 deletions

View File

@ -62,7 +62,8 @@ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git" AND
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.in" AND
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/version.c.in" AND
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/man1" AND
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/mdbx_chk.c")
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/mdbx_chk.c" AND
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/mdbx.c++")
set(MDBX_AMALGAMATED_SOURCE FALSE)
find_program(GIT git)
if(NOT GIT)
@ -71,6 +72,7 @@ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git" AND
set(MDBX_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src")
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" 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
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/man1" AND
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/mdbx_chk.c")
@ -294,14 +296,30 @@ else()
endif(SUBPROJECT)
list(FIND CMAKE_C_COMPILE_FEATURES c_std_11 HAS_C11)
list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_11 HAS_CXX11)
list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_14 HAS_CXX14)
list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_17 HAS_CXX17)
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)
set(MDBX_CXX_STANDARD 20)
elseif(NOT HAS_CXX17 LESS 0)
set(MDBX_CXX_STANDARD 17)
elseif(NOT HAS_CXX14 LESS 0)
set(MDBX_CXX_STANDARD 14)
elseif(NOT HAS_CXX11 LESS 0)
set(MDBX_CXX_STANDARD 11)
else()
set(MDBX_CXX_STANDARD 98)
endif()
endif()
if(NOT HAS_C11 LESS 0)
set(MDBX_C_STANDARD 11)
else()
set(MDBX_C_STANDARD 99)
endif()
if(MDBX_C_STANDARD)
message(STATUS "Use C${MDBX_C_STANDARD} for libmdbx")
endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows" AND EXISTS "${MDBX_SOURCE_DIR}/ntdll.def")
if(MSVC)
@ -392,7 +410,7 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" OR IOS)
endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
if(MDBX_NTDLL_EXTRA_IMPLIB)
add_mdbx_option(MDBX_AVOID_CRT "Avoid dependence from MSVC CRT and use ntdll.dll instead" ${NOT_SUBPROJECT})
add_mdbx_option(MDBX_AVOID_CRT "Avoid dependence from MSVC CRT and use ntdll.dll instead" OFF)
endif()
add_mdbx_option(MDBX_CONFIG_MANUAL_TLS_CALLBACK
"Provide mdbx_dll_handler() for manual initialization" OFF)
@ -409,8 +427,7 @@ option(MDBX_FORCE_ASSERTIONS "Force enable assertion checking" OFF)
if(NOT MDBX_AMALGAMATED_SOURCE)
add_mdbx_option(MDBX_ALLOY_BUILD "Build MDBX library through single/alloyed object file" ON)
option(MDBX_ENABLE_TESTS "Build MDBX tests" ${BUILD_TESTING})
endif(NOT MDBX_AMALGAMATED_SOURCE)
endif()
if((MDBX_BUILD_TOOLS OR MDBX_ENABLE_TESTS) AND MDBX_BUILD_SHARED_LIBRARY)
add_mdbx_option(MDBX_LINK_TOOLS_NONSTATIC "Link MDBX tools with non-static libmdbx" OFF)
@ -418,6 +435,22 @@ else()
unset(MDBX_LINK_TOOLS_NONSTATIC CACHE)
endif()
if(MDBX_CXX_STANDARD GREATER_EQUAL 11 AND MDBX_CXX_STANDARD LESS 83)
if(NOT MDBX_AMALGAMATED_SOURCE)
option(MDBX_ENABLE_TESTS "Build MDBX tests" ${BUILD_TESTING})
endif()
if(NOT MDBX_AVOID_CRT
AND NOT (CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5)
AND NOT (CMAKE_COMPILER_IS_CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4)
AND NOT (MSVC AND MSVC_VERSION LESS 1900))
option(MDBX_BUILD_CXX "Build C++ portion" ON)
else()
set(MDBX_BUILD_CXX FALSE)
endif()
else()
set(MDBX_BUILD_CXX FALSE)
endif()
################################################################################
################################################################################
@ -426,7 +459,7 @@ fetch_version(MDBX "${CMAKE_CURRENT_SOURCE_DIR}" FALSE)
message(STATUS "libmdbx version is ${MDBX_VERSION}")
# sources list
set(LIBMDBX_SOURCES mdbx.h "${CMAKE_CURRENT_BINARY_DIR}/config.h")
set(LIBMDBX_SOURCES mdbx.h "${CMAKE_CURRENT_BINARY_DIR}/config.h" )
if(MDBX_AMALGAMATED_SOURCE)
list(APPEND LIBMDBX_SOURCES mdbx.c)
else()
@ -438,7 +471,7 @@ else()
set(MDBX_BUILD_SOURCERY "${MDBX_SOURCERY_DIGEST}_${MDBX_SOURCERY_SUFFIX}")
if(MDBX_ALLOY_BUILD)
list(APPEND LIBMDBX_SOURCES ${MDBX_SOURCE_DIR}/alloy.c)
list(APPEND LIBMDBX_SOURCES "${MDBX_SOURCE_DIR}/alloy.c")
include_directories("${MDBX_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}")
else()
list(APPEND LIBMDBX_SOURCES
@ -450,12 +483,24 @@ else()
include_directories("${MDBX_SOURCE_DIR}")
endif()
endif(MDBX_AMALGAMATED_SOURCE)
if(MDBX_BUILD_CXX)
message(STATUS "Use C${MDBX_C_STANDARD} and C++${MDBX_CXX_STANDARD} for libmdbx")
list(APPEND LIBMDBX_SOURCES "${MDBX_SOURCE_DIR}/mdbx.c++" mdbx.h++)
else()
message(STATUS "Use C${MDBX_C_STANDARD} for libmdbx but C++ portion is disabled")
endif()
macro(target_setup_options TARGET)
if(DEFINED INTERPROCEDURAL_OPTIMIZATION)
set_target_properties(${TARGET} PROPERTIES
INTERPROCEDURAL_OPTIMIZATION $<BOOL:${INTERPROCEDURAL_OPTIMIZATION}>)
endif()
set_target_properties(${TARGET} PROPERTIES
C_STANDARD ${MDBX_C_STANDARD} C_STANDARD_REQUIRED ON)
if(MDBX_BUILD_CXX)
set_target_properties(${TARGET} PROPERTIES
CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON)
endif()
if(CC_HAS_FASTMATH)
target_compile_options(${TARGET} PRIVATE "-ffast-math")
endif()
@ -479,6 +524,9 @@ 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)
target_link_libraries(${TARGET} ${MODE} ${LIBCXX_FILESYSTEM})
endif()
endmacro()
# build static library
@ -650,10 +698,16 @@ endif(MDBX_INSTALL_STATIC)
# collect options & build info
string(TIMESTAMP MDBX_BUILD_TIMESTAMP UTC)
set(MDBX_BUILD_FLAGS ${CMAKE_C_FLAGS})
if(MDBX_BUILD_CXX)
set(MDBX_BUILD_FLAGS ${CMAKE_CXX_FLAGS})
endif()
# append cmake's build-type flags and defines
if(NOT CMAKE_CONFIGURATION_TYPES)
list(APPEND MDBX_BUILD_FLAGS ${CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPERCASE}})
if(MDBX_BUILD_CXX)
list(APPEND MDBX_BUILD_FLAGS ${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPERCASE}})
endif()
endif()
# get definitions

View File

@ -1,7 +1,7 @@
ChangeLog
---------
## v0.9.0 (in the development):
## v0.9.0 (in the development)
Added features:
@ -9,10 +9,11 @@ Added features:
- Functions to explicit reader threads (de)registration.
- Separated enums for environment, sub-databases, transactions, copying and data-update flags.
- Support for read transactions preparation (`MDBX_TXN_RDONLY_PREPARE` flag).
- Draft: Native bindings for C++.
TODO:
- Native bindings for C++.
- Finalize: Native bindings for C++.
- Packages for AltLinux, Fedora/RHEL, Debian/Ubuntu.
Deprecated functions and flags:
@ -23,7 +24,7 @@ Deprecated functions and flags:
Just use `MDBX_SAFE_NOSYNC` or `MDBX_UTTERLY_NOSYNC` instead of it.
## v0.8.2 2020-07-06:
## v0.8.2 2020-07-06
- Added support multi-opening the same DB in a process with SysV locking (BSD).
- Fixed warnings & minors for LCC compiler (E2K).
- Enabled to simultaneously open the same database from processes with and without the `MDBX_WRITEMAP` option.
@ -38,7 +39,7 @@ Deprecated functions and flags:
Now remapping with a change of address is performed automatically if there are no dependent readers in the current process.
## v0.8.1 2020-06-12:
## v0.8.1 2020-06-12
- Minor change versioning. The last number in the version now means the number of commits since last release/tag.
- Provide ChangeLog file.
- Fix for using libmdbx as a C-only sub-project with CMake.
@ -48,7 +49,7 @@ Deprecated functions and flags:
- Force enabling exceptions handling for MSVC (`/EHsc` option).
## v0.8.0 2020-06-05:
## v0.8.0 2020-06-05
- Support for Android/Bionic.
- Support for iOS.
- Auto-handling `MDBX_NOSUBDIR` while opening for any existing database.
@ -93,7 +94,7 @@ Deprecated functions and flags:
- Avoid some GCC-analyzer false-positive warnings.
## v0.7.0 2020-03-18:
## v0.7.0 2020-03-18
- Workarounds for Wine (Windows compatibility layer for Linux).
- `MDBX_MAP_RESIZED` renamed to `MDBX_UNABLE_EXTEND_MAPSIZE`.
- Clarify API description, fix typos.
@ -105,7 +106,7 @@ Deprecated functions and flags:
- Avoids extra error messages "bad txn" from mdbx_chk when DB is corrupted.
## v0.6.0 2020-01-21:
## v0.6.0 2020-01-21
- Fix `mdbx_load` utility for custom comparators.
- Fix checks related to `MDBX_APPEND` flag inside `mdbx_cursor_put()`.
- Refine/fix dbi_bind() internals.
@ -117,7 +118,7 @@ Deprecated functions and flags:
- Clarify API description & comments, fix typos.
## v0.5.0 2019-12-31:
## v0.5.0 2019-12-31
- Fix returning MDBX_RESULT_TRUE from page_alloc().
- Fix false-positive ASAN issue.
- Fix assertion for `MDBX_NOTLS` option.
@ -132,7 +133,7 @@ Deprecated functions and flags:
- Added install section for CMake.
## v0.4.0 2019-12-02:
## v0.4.0 2019-12-02
- Support for Mac OSX, FreeBSD, NetBSD, OpenBSD, DragonFly BSD, OpenSolaris, OpenIndiana (AIX and HP-UX pending).
- Use bootid for decisions of rollback.
- Counting retired pages and extended transaction info.

View File

@ -43,7 +43,7 @@ define uname2sosuffix
endef
SO_SUFFIX := $(shell $(uname2sosuffix))
HEADERS := mdbx.h
HEADERS := mdbx.h mdbx.h++
LIBRARIES := libmdbx.a libmdbx.$(SO_SUFFIX)
TOOLS := mdbx_stat mdbx_copy mdbx_dump mdbx_load mdbx_chk
MANPAGES := mdbx_stat.1 mdbx_copy.1 mdbx_dump.1 mdbx_load.1 mdbx_chk.1
@ -64,11 +64,11 @@ clean:
*.gcov *.log *.err src/*.o test/*.o mdbx_example dist \
config.h src/config.h src/version.c *.tar*
libmdbx.a: mdbx-static.o
libmdbx.a: mdbx-static.o mdbx++-static.o
$(AR) rs $@ $?
libmdbx.$(SO_SUFFIX): mdbx-dylib.o
$(CC) $(CFLAGS) $^ -pthread -shared $(LDFLAGS) $(LIBS) -o $@
libmdbx.$(SO_SUFFIX): mdbx-dylib.o mdbx++-dylib.o
$(CXX) $(CXXFLAGS) $^ -pthread -shared $(LDFLAGS) $(LIBS) -o $@
#> dist-cutoff-begin
ifeq ($(wildcard mdbx.c),mdbx.c)
@ -85,12 +85,18 @@ config.h: mdbx.c $(lastword $(MAKEFILE_LIST))
&& 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 $(lastword $(MAKEFILE_LIST))
mdbx-dylib.o: config.h mdbx.c mdbx.h $(lastword $(MAKEFILE_LIST))
$(CC) $(CFLAGS) $(MDBX_OPTIONS) '-DMDBX_CONFIG_H="config.h"' -DLIBMDBX_EXPORTS=1 -c mdbx.c -o $@
mdbx-static.o: config.h mdbx.c $(lastword $(MAKEFILE_LIST))
mdbx-static.o: config.h mdbx.c mdbx.h $(lastword $(MAKEFILE_LIST))
$(CC) $(CFLAGS) $(MDBX_OPTIONS) '-DMDBX_CONFIG_H="config.h"' -ULIBMDBX_EXPORTS -c mdbx.c -o $@
mdbx++-dylib.o: config.h mdbx.c++ mdbx.h mdbx.h++ $(lastword $(MAKEFILE_LIST))
$(CXX) $(CXXFLAGS) $(MDBX_OPTIONS) '-DMDBX_CONFIG_H="config.h"' -DLIBMDBX_EXPORTS=1 -c mdbx.c++ -o $@
mdbx++-static.o: config.h mdbx.c++ mdbx.h mdbx.h++ $(lastword $(MAKEFILE_LIST))
$(CXX) $(CXXFLAGS) $(MDBX_OPTIONS) '-DMDBX_CONFIG_H="config.h"' -ULIBMDBX_EXPORTS -c mdbx.c++ -o $@
mdbx_%: mdbx_%.c libmdbx.a
$(CC) $(CFLAGS) $(MDBX_OPTIONS) '-DMDBX_CONFIG_H="config.h"' $^ $(EXE_LDFLAGS) $(LIBS) -o $@
@ -117,7 +123,7 @@ endef
DIST_EXTRA := LICENSE README.md CMakeLists.txt GNUmakefile Makefile ChangeLog.md VERSION config.h.in ntdll.def \
$(addprefix man1/, $(MANPAGES)) cmake/compiler.cmake cmake/profile.cmake cmake/utils.cmake
DIST_SRC := mdbx.h mdbx.c $(addsuffix .c, $(TOOLS))
DIST_SRC := mdbx.h mdbx.h++ mdbx.c mdbx.c++ $(addsuffix .c, $(TOOLS))
TEST_DB ?= $(shell [ -d /dev/shm ] && echo /dev/shm || echo /tmp)/mdbx-test.db
TEST_LOG ?= $(shell [ -d /dev/shm ] && echo /dev/shm || echo /tmp)/mdbx-test.log.gz
@ -270,8 +276,14 @@ docs/intro.md: docs/_preface.md docs/__characteristics.md docs/__improvements.md
docs/usage.md: docs/__usage.md docs/_starting.md docs/__bindings.md
echo -e "\\page usage Usage\n\\section getting Getting the libmdbx" | cat - $^ | sed 's/^Bindings$$/Bindings {#bindings}/' > $@
doxygen: docs/Doxyfile docs/overall.md docs/intro.md docs/usage.md mdbx.h src/options.h ChangeLog.md AUTHORS LICENSE
rm -rf docs/html && cp mdbx.h src/options.h ChangeLog.md docs/ && (cd docs && doxygen Doxyfile) && cp AUTHORS LICENSE docs/html/
doxygen: docs/Doxyfile docs/overall.md docs/intro.md docs/usage.md mdbx.h mdbx.h++ src/options.h ChangeLog.md AUTHORS LICENSE
rm -rf docs/html && cp mdbx.h mdbx.h++ src/options.h ChangeLog.md docs/ && (cd docs && doxygen Doxyfile) && cp AUTHORS LICENSE docs/html/
mdbx++-dylib.o: src/config.h src/mdbx.c++ mdbx.h mdbx.h++ $(lastword $(MAKEFILE_LIST))
$(CXX) $(CXXFLAGS) $(MDBX_OPTIONS) '-DMDBX_CONFIG_H="config.h"' -DLIBMDBX_EXPORTS=1 -c src/mdbx.c++ -o $@
mdbx++-static.o: src/config.h src/mdbx.c++ mdbx.h mdbx.h++ $(lastword $(MAKEFILE_LIST))
$(CXX) $(CXXFLAGS) $(MDBX_OPTIONS) '-DMDBX_CONFIG_H="config.h"' -ULIBMDBX_EXPORTS -c src/mdbx.c++ -o $@
.PHONY: dist release-assets
dist: libmdbx-sources-$(MDBX_VERSION_SUFFIX).tar.gz $(lastword $(MAKEFILE_LIST))
@ -288,6 +300,9 @@ libmdbx-sources-$(MDBX_VERSION_SUFFIX).zip: $(addprefix dist/, $(DIST_SRC) $(DIS
dist/mdbx.h: mdbx.h src/version.c $(lastword $(MAKEFILE_LIST))
mkdir -p dist && cp $< $@
dist/mdbx.h++: mdbx.h++ src/version.c $(lastword $(MAKEFILE_LIST))
mkdir -p dist && cp $< $@
dist/@tmp-shared_internals.inc: src/version.c $(ALLOY_DEPS) $(lastword $(MAKEFILE_LIST))
mkdir -p dist && sed \
-e 's|#pragma once|#define MDBX_ALLOY 1\n#define MDBX_BUILD_SOURCERY $(MDBX_BUILD_SOURCERY)|' \
@ -302,6 +317,10 @@ dist/mdbx.c: dist/@tmp-shared_internals.inc $(lastword $(MAKEFILE_LIST))
&& 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|' > $@
dist/mdbx.c++: dist/@tmp-shared_internals.inc src/mdbx.c++ $(lastword $(MAKEFILE_LIST))
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++"|' > $@
define dist-tool-rule
dist/$(1).c: src/$(1).c src/wingetopt.h src/wingetopt.c \
dist/@tmp-shared_internals.inc $(lastword $(MAKEFILE_LIST))

View File

@ -2,6 +2,9 @@ version: 0.9.0.{build}
environment:
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
CMAKE_GENERATOR: Visual Studio 14 2015
TOOLSET: 140
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
CMAKE_GENERATOR: Visual Studio 16 2019
TOOLSET: 142
@ -25,9 +28,6 @@ environment:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
CMAKE_GENERATOR: Visual Studio 15 2017
TOOLSET: 141
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
CMAKE_GENERATOR: Visual Studio 14 2015
TOOLSET: 140
branches:
except:

View File

@ -606,8 +606,9 @@ macro(setup_compile_flags)
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_flags("CXX" "/Zc:__cplusplus")
add_compile_flags("C;CXX" "/W4")
if(NOT MSVC_VERSION LESS 1910)
add_compile_flags("CXX" "/Zc:__cplusplus")
endif()
add_compile_flags("C;CXX" "/utf-8")
else()
if(CC_HAS_WALL)

View File

@ -829,7 +829,7 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = overall.md intro.md usage.md mdbx.h options.h ChangeLog.md
INPUT = overall.md intro.md usage.md mdbx.h mdbx.h++ options.h ChangeLog.md
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses

View File

@ -34,9 +34,11 @@ each of which is divided into several sections.
- Cleaning up
- \ref bindings
3. The `C` API manual:
3. The `C/C++` API manual:
- The \ref c_api reference
- The \ref mdbx.h header file reference
- The \ref cxx_api reference
- The \ref mdbx.h++ header file reference
Please do not hesitate to point out errors in the documentation,
including creating [PR](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/proposing-changes-to-your-work-with-pull-requests) with corrections and improvements.

132
mdbx.h
View File

@ -107,7 +107,9 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <windows.h>
#include <winnt.h>
#ifndef __mode_t_defined
typedef unsigned short mode_t;
typedef unsigned short mdbx_mode_t;
#else
typedef mode_t mdbx_mode_t;
#endif /* __mode_t_defined */
typedef HANDLE mdbx_filehandle_t;
typedef DWORD mdbx_pid_t;
@ -121,6 +123,7 @@ typedef DWORD mdbx_tid_t;
typedef int mdbx_filehandle_t;
typedef pid_t mdbx_pid_t;
typedef pthread_t mdbx_tid_t;
typedef mode_t mdbx_mode_t;
#endif /* !Windows */
#ifdef _MSC_VER
@ -347,6 +350,18 @@ typedef pthread_t mdbx_tid_t;
#endif
#endif /* cxx14_constexpr */
#ifndef __noreturn
#ifdef _Noreturn
#define __noreturn _Noreturn
#elif defined(__GNUC__) || __has_attribute(__noreturn__)
#define __noreturn __attribute__((__noreturn__))
#elif defined(_MSC_VER) && !defined(__clang__)
#define __noreturn __declspec(noreturn)
#else
#define __noreturn
#endif
#endif /* __noreturn */
#ifndef DEFINE_ENUM_FLAG_OPERATORS
#if defined(__cplusplus)
/// Define operator overloads to enable bit operations on enum values that are
@ -392,6 +407,16 @@ typedef pthread_t mdbx_tid_t;
* \addtogroup c_api
* @{ */
#ifdef __cplusplus
#if defined(__clang__) || __has_attribute(type_visibility)
#define LIBMDBX_API_TYPE LIBMDBX_API __attribute__((type_visibility("default")))
#else
#define LIBMDBX_API_TYPE LIBMDBX_API
#endif
#else
#define LIBMDBX_API_TYPE
#endif /* LIBMDBX_API_TYPE */
#ifdef __cplusplus
extern "C" {
#endif
@ -516,6 +541,7 @@ struct MDBX_cursor;
#endif
/** Generic structure used for passing keys and data in and out of the database.
* \anchor MDBX_val \see slice \see buffer
*
* \details Values returned from the database are valid only until a subsequent
* update operation, or the end of the transaction. Do not modify or
@ -663,7 +689,8 @@ DEFINE_ENUM_FLAG_OPERATORS(MDBX_debug_flags_t)
* \param [in] env An environment handle returned by \ref mdbx_env_create().
* \param [in] msg The assertion message, not including newline. */
typedef void MDBX_debug_func(MDBX_log_level_t loglevel, const char *function,
int line, const char *msg, va_list args);
int line, const char *msg,
va_list args) cxx17_noexcept;
/** The "don't change `logger`" value for mdbx_setup_debug() */
#define MDBX_LOGGER_DONTCHANGE ((MDBX_debug_func *)(intptr_t)-1)
@ -682,7 +709,8 @@ LIBMDBX_API int mdbx_setup_debug(MDBX_log_level_t log_level,
* \param [in] env An environment handle returned by mdbx_env_create().
* \param [in] msg The assertion message, not including newline. */
typedef void MDBX_assert_func(const MDBX_env *env, const char *msg,
const char *function, unsigned line);
const char *function,
unsigned line) cxx17_noexcept;
/** Set or reset the assert() callback of the environment.
*
@ -1121,13 +1149,17 @@ enum MDBX_txn_flags_t {
* block each other and a write transactions. */
MDBX_TXN_RDONLY = MDBX_RDONLY,
/** Prepare but not start read-only transaction.
*
* Transaction will not be started immediately, but created transaction handle
* will be ready for use with \ref mdbx_txn_renew(). This flag allows to
* preallocate memory and assign a reader slot, thus avoiding these operations
* at the next start of the transaction. */
MDBX_TXN_RDONLY_PREPARE = MDBX_TXN_RDONLY | MDBX_NOMEMINIT,
/** Prepare but not start read-only transaction.
*
* Transaction will not be started immediately, but created transaction handle
* will be ready for use with \ref mdbx_txn_renew(). This flag allows to
* preallocate memory and assign a reader slot, thus avoiding these operations
* at the next start of the transaction. */
#if defined(__cplusplus) && defined(_MSC_VER) && _MSC_VER < 1910
MDBX_TXN_RDONLY_PREPARE = uint32_t(MDBX_RDONLY) | uint32_t(MDBX_NOMEMINIT),
#else
MDBX_TXN_RDONLY_PREPARE = MDBX_RDONLY | MDBX_NOMEMINIT,
#endif
/** Do not block when starting a write transaction. */
MDBX_TXN_TRY = UINT32_C(0x10000000),
@ -1297,7 +1329,7 @@ enum MDBX_cursor_op {
/** \ref MDBX_DUPFIXED -only: Return up to a page of duplicate data items
* from next cursor position. Move cursor to prepare
* for \ref MDBX_NEXT_MULTIPLE. */
* for `MDBX_NEXT_MULTIPLE`. */
MDBX_NEXT_MULTIPLE,
/** Position at first data item of next key */
@ -1348,6 +1380,9 @@ enum MDBX_error_t {
/** key/data pair already exists */
MDBX_KEYEXIST = -30799,
/** The first LMDB-compatible defined error code */
MDBX_FIRST_LMDB_ERRCODE = MDBX_KEYEXIST,
/** key/data pair not found (EOF) */
MDBX_NOTFOUND = -30798,
@ -1431,6 +1466,9 @@ enum MDBX_error_t {
* opening with \ref MDBX_EXCLUSIVE flag */
MDBX_BUSY = -30778,
/** The first of MDBX-added error codes */
MDBX_FIRST_ADDED_ERRCODE = MDBX_BUSY,
/** The specified key has more than one associated value */
MDBX_EMULTIVAL = -30421,
@ -1457,6 +1495,9 @@ enum MDBX_error_t {
/** Overlapping read and write transactions for the current thread */
MDBX_TXN_OVERLAPPING = -30415,
/* The last of MDBX-added error codes */
MDBX_LAST_ADDED_ERRCODEE = MDBX_TXN_OVERLAPPING,
#if defined(_WIN32) || defined(_WIN64)
MDBX_ENODATA = ERROR_HANDLE_EOF,
MDBX_EINVAL = ERROR_INVALID_PARAMETER,
@ -1536,12 +1577,17 @@ LIBMDBX_API const char *mdbx_strerror(int errnum);
* restriction if the returned string points to the supplied buffer.
* \see mdbx_strerror()
*
* mdbx_liberr2str() returns string describing only MDBX error numbers but NULL
* for non-MDBX error codes. This function is thread-safe since return pointer
* to constant non-localized strings.
*
* \param [in] errnum The error code.
* \param [in,out] buf Buffer to store the error message.
* \param [in] buflen The size of buffer to store the message.
*
* \returns "error message" The description of the error. */
LIBMDBX_API const char *mdbx_strerror_r(int errnum, char *buf, size_t buflen);
__nothrow_pure_function LIBMDBX_API const char *mdbx_liberr2str(int errnum);
#if defined(_WIN32) || defined(_WIN64) || defined(DOXYGEN)
/** Bit of Windows' madness. The similar to \ref mdbx_strerror() but returns
@ -1645,7 +1691,7 @@ LIBMDBX_API int mdbx_env_create(MDBX_env **penv);
* i.e. 32-bit process tries to open >4Gb database.
*/
LIBMDBX_API int mdbx_env_open(MDBX_env *env, const char *pathname,
MDBX_env_flags_t flags, mode_t mode);
MDBX_env_flags_t flags, mdbx_mode_t mode);
/** Copy an MDBX environment to the specified path, with options.
* \ingroup c_extra
@ -2752,7 +2798,7 @@ LIBMDBX_API int mdbx_canary_get(const MDBX_txn *txn, MDBX_canary *canary);
* \ingroup c_crud
* \see mdbx_cmp() \see mdbx_get_keycmp()
* \see mdbx_get_datacmp \see mdbx_dcmp() */
typedef int(MDBX_cmp_func)(const MDBX_val *a, const MDBX_val *b);
typedef int(MDBX_cmp_func)(const MDBX_val *a, const MDBX_val *b) cxx17_noexcept;
/** Open or Create a database in the environment.
* \ingroup c_dbi
@ -3092,12 +3138,12 @@ LIBMDBX_API int mdbx_get(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
LIBMDBX_API int mdbx_get_ex(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key,
MDBX_val *data, size_t *values_count);
/** Get nearest items from a database.
/** Get equal or great item from a database.
* \ingroup c_crud
*
* Briefly this function does the same as \ref mdbx_get() with a few
* differences:
* 1. Return nearest (i.e. equal or great due comparison function) key-value
* 1. Return equal or great (due comparison function) key-value
* pair, but not only exactly matching with the key.
* 2. On success return \ref MDBX_SUCCESS if key found exactly,
* and \ref MDBX_RESULT_TRUE otherwise. Moreover, for databases with
@ -3120,8 +3166,8 @@ LIBMDBX_API int mdbx_get_ex(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key,
* by current thread.
* \retval MDBX_NOTFOUND The key was not in the database.
* \retval MDBX_EINVAL An invalid parameter was specified. */
LIBMDBX_API int mdbx_get_nearest(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key,
MDBX_val *data);
LIBMDBX_API int mdbx_get_equal_or_great(MDBX_txn *txn, MDBX_dbi dbi,
MDBX_val *key, MDBX_val *data);
/** Store items into a database.
* \ingroup c_crud
@ -3215,7 +3261,7 @@ LIBMDBX_API int mdbx_put(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
* by \ref mdbx_txn_begin().
* \param [in] dbi A database handle returned by \ref mdbx_dbi_open().
* \param [in] key The key to store in the database.
* \param [in,out] new_data The data to store, if NULL then deletion will
* \param [in] new_data The data to store, if NULL then deletion will
* be performed.
* \param [in,out] old_data The buffer for retrieve previous value as describe
* above.
@ -3233,6 +3279,14 @@ LIBMDBX_API int mdbx_replace(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
MDBX_val *new_data, MDBX_val *old_data,
MDBX_put_flags_t flags);
typedef int (*MDBX_preserve_func)(void *context, MDBX_val *target,
const void *src, size_t bytes);
LIBMDBX_API int mdbx_replace_ex(MDBX_txn *txn, MDBX_dbi dbi,
const MDBX_val *key, MDBX_val *new_data,
MDBX_val *old_data, MDBX_put_flags_t flags,
MDBX_preserve_func preserver,
void *preserver_context);
/** Delete items from a database.
* \ingroup c_crud
*
@ -3769,7 +3823,7 @@ mdbx_get_datacmp(MDBX_db_flags_t flags);
typedef int(MDBX_reader_list_func)(void *ctx, int num, int slot, mdbx_pid_t pid,
mdbx_tid_t thread, uint64_t txnid,
uint64_t lag, size_t bytes_used,
size_t bytes_retained);
size_t bytes_retained) cxx17_noexcept;
/** Enumarete the entries in the reader lock table.
* \ingroup c_statinfo
@ -3903,7 +3957,8 @@ LIBMDBX_API int mdbx_thread_unregister(MDBX_env *env);
* \see mdbx_env_set_oomfunc() \see mdbx_env_get_oomfunc()
*/
typedef int(MDBX_oom_func)(MDBX_env *env, mdbx_pid_t pid, mdbx_tid_t tid,
uint64_t txn, unsigned gap, size_t space, int retry);
uint64_t txn, unsigned gap, size_t space,
int retry) cxx17_noexcept;
/** Set the OOM callback.
* \ingroup c_err
@ -3960,12 +4015,11 @@ typedef enum MDBX_page_type_t MDBX_page_type_t;
#define MDBX_PGWALK_META ((const char *)((ptrdiff_t)-2))
/** Callback function for traverse the b-tree. \see mdbx_env_pgwalk() */
typedef int
MDBX_pgvisitor_func(const uint64_t pgno, const unsigned number, void *const ctx,
const int deep, const char *const dbi,
const size_t page_size, const MDBX_page_type_t type,
const size_t nentries, const size_t payload_bytes,
const size_t header_bytes, const size_t unused_bytes);
typedef int MDBX_pgvisitor_func(
const uint64_t pgno, const unsigned number, void *const ctx, const int deep,
const char *const dbi, const size_t page_size, const MDBX_page_type_t type,
const size_t nentries, const size_t payload_bytes,
const size_t header_bytes, const size_t unused_bytes) cxx17_noexcept;
/** B-tree traversal function. */
LIBMDBX_API int mdbx_env_pgwalk(MDBX_txn *txn, MDBX_pgvisitor_func *visitor,
@ -3973,7 +4027,7 @@ LIBMDBX_API int mdbx_env_pgwalk(MDBX_txn *txn, MDBX_pgvisitor_func *visitor,
/** @} B-tree Traversal */
/**** Attribute support functions for Nexenta
* *********************************/
* *******************************************/
#if defined(MDBX_NEXENTA_ATTRS) || defined(DOXYGEN)
/** \defgroup nexenta Attribute support functions for Nexenta
* \ingroup c_crud
@ -4154,6 +4208,28 @@ LIBMDBX_API int mdbx_e2k_strncmp_bug_workaround(const char *s1, const char *s2,
LIBMDBX_API size_t mdbx_e2k_strlen_bug_workaround(const char *s);
LIBMDBX_API size_t mdbx_e2k_strnlen_bug_workaround(const char *s,
size_t maxlen);
#ifdef __cplusplus
namespace std {
inline int mdbx_e2k_memcmp_bug_workaround(const void *s1, const void *s2,
size_t n) {
return ::mdbx_e2k_memcmp_bug_workaround(s1, s2, n);
}
inline int mdbx_e2k_strcmp_bug_workaround(const char *s1, const char *s2) {
return ::mdbx_e2k_strcmp_bug_workaround(s1, s2);
}
inline int mdbx_e2k_strncmp_bug_workaround(const char *s1, const char *s2,
size_t n) {
return ::mdbx_e2k_strncmp_bug_workaround(s1, s2, n);
}
inline size_t mdbx_e2k_strlen_bug_workaround(const char *s) {
return ::mdbx_e2k_strlen_bug_workaround(s);
}
inline size_t mdbx_e2k_strnlen_bug_workaround(const char *s, size_t maxlen) {
return ::mdbx_e2k_strnlen_bug_workaround(s, maxlen);
}
} // namespace std
#endif /* __cplusplus */
#include <string.h>
#include <strings.h>
#undef memcmp
@ -4171,7 +4247,7 @@ LIBMDBX_API size_t mdbx_e2k_strnlen_bug_workaround(const char *s,
#endif /* MDBX_E2K_MLHCPB_WORKAROUND */
#ifdef __cplusplus
}
} /* extern "C" */
#endif
#endif /* LIBMDBX_H */

3101
mdbx.h++ Normal file

File diff suppressed because it is too large Load Diff

View File

@ -3045,11 +3045,9 @@ static __always_inline void mdbx_dpl_clear(MDBX_DPL dl) {
/*----------------------------------------------------------------------------*/
#ifndef MDBX_ALLOY
uint8_t mdbx_runtime_flags = MDBX_RUNTIME_FLAGS_INIT;
uint8_t mdbx_loglevel = MDBX_DEBUG;
MDBX_debug_func *mdbx_debug_logger;
#endif /* MDBX_ALLOY */
static bool mdbx_refund(MDBX_txn *txn);
static __must_check_result int mdbx_page_retire(MDBX_cursor *mc, MDBX_page *mp);
@ -3199,7 +3197,7 @@ static MDBX_cmp_func cmp_lexical, cmp_reverse, cmp_int_align4, cmp_int_align2,
static __inline MDBX_cmp_func *get_default_keycmp(unsigned flags);
static __inline MDBX_cmp_func *get_default_datacmp(unsigned flags);
static const char *__mdbx_strerr(int errnum) {
__cold const char *mdbx_liberr2str(int errnum) {
/* Table of descriptions for MDBX errors */
static const char *const tbl[] = {
"MDBX_KEYEXIST: Key/data pair already exists",
@ -3272,14 +3270,14 @@ static const char *__mdbx_strerr(int errnum) {
}
const char *__cold mdbx_strerror_r(int errnum, char *buf, size_t buflen) {
const char *msg = __mdbx_strerr(errnum);
const char *msg = mdbx_liberr2str(errnum);
if (!msg && buflen > 0 && buflen < INT_MAX) {
#if defined(_WIN32) || defined(_WIN64)
const DWORD size = FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
errnum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (DWORD)buflen,
NULL);
return size ? buf : NULL;
return size ? buf : "FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM) failed";
#elif defined(_GNU_SOURCE) && defined(__GLIBC__)
/* GNU-specific */
if (errnum > 0)
@ -3311,7 +3309,7 @@ const char *__cold mdbx_strerror(int errnum) {
static char buf[1024];
return mdbx_strerror_r(errnum, buf, sizeof(buf));
#else
const char *msg = __mdbx_strerr(errnum);
const char *msg = mdbx_liberr2str(errnum);
if (!msg) {
if (errnum > 0)
msg = strerror(errnum);
@ -3327,13 +3325,17 @@ const char *__cold mdbx_strerror(int errnum) {
#if defined(_WIN32) || defined(_WIN64) /* Bit of madness for Windows */
const char *mdbx_strerror_r_ANSI2OEM(int errnum, char *buf, size_t buflen) {
const char *msg = __mdbx_strerr(errnum);
const char *msg = mdbx_liberr2str(errnum);
if (!msg && buflen > 0 && buflen < INT_MAX) {
const DWORD size = FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
errnum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (DWORD)buflen,
NULL);
if (size && CharToOemBuffA(buf, buf, size))
if (!size)
msg = "FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM) failed";
else if (!CharToOemBuffA(buf, buf, size))
msg = "CharToOemBuffA() failed";
else
msg = buf;
}
return msg;
@ -9802,7 +9804,7 @@ static int __cold mdbx_setup_dxb(MDBX_env *env, const int lck_rc) {
/* Open and/or initialize the lock region for the environment. */
static int __cold mdbx_setup_lck(MDBX_env *env, char *lck_pathname,
mode_t mode) {
mdbx_mode_t mode) {
mdbx_assert(env, env->me_lazy_fd != INVALID_HANDLE_VALUE);
mdbx_assert(env, env->me_lfd == INVALID_HANDLE_VALUE);
@ -10137,7 +10139,7 @@ static uint32_t merge_sync_flags(const uint32_t a, const uint32_t b) {
}
int __cold mdbx_env_open(MDBX_env *env, const char *pathname,
MDBX_env_flags_t flags, mode_t mode) {
MDBX_env_flags_t flags, mdbx_mode_t mode) {
int rc = check_env(env);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
@ -10198,7 +10200,7 @@ int __cold mdbx_env_open(MDBX_env *env, const char *pathname,
return rc;
/* auto-create directory if requested */
const mode_t dir_mode =
const mdbx_mode_t dir_mode =
(/* inherit read/write permissions for group and others */ mode &
(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) |
/* always add read/write/search for owner */ S_IRWXU |
@ -11183,7 +11185,7 @@ int mdbx_get(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data) {
return MDBX_EINVAL;
if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID)))
return MDBX_EINVAL;
return MDBX_BAD_DBI;
MDBX_cursor_couple cx;
rc = mdbx_cursor_init(&cx.outer, txn, dbi);
@ -11194,8 +11196,8 @@ int mdbx_get(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data) {
return mdbx_cursor_set(&cx.outer, (MDBX_val *)key, data, MDBX_SET, &exact);
}
int mdbx_get_nearest(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key,
MDBX_val *data) {
int mdbx_get_equal_or_great(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key,
MDBX_val *data) {
DKBUF;
mdbx_debug("===> get db %u key [%s]", dbi, DKEY(key));
@ -11207,7 +11209,7 @@ int mdbx_get_nearest(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key,
return MDBX_EINVAL;
if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID)))
return MDBX_EINVAL;
return MDBX_BAD_DBI;
if (unlikely(txn->mt_flags & MDBX_TXN_BLOCKED))
return MDBX_BAD_TXN;
@ -11247,7 +11249,7 @@ int mdbx_get_ex(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data,
return MDBX_EINVAL;
if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID)))
return MDBX_EINVAL;
return MDBX_BAD_DBI;
MDBX_cursor_couple cx;
rc = mdbx_cursor_init(&cx.outer, txn, dbi);
@ -13415,7 +13417,7 @@ int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) {
return rc;
if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_VALID)))
return MDBX_EINVAL;
return MDBX_BAD_DBI;
if (unlikely(dbi == FREE_DBI && !F_ISSET(txn->mt_flags, MDBX_TXN_RDONLY)))
return MDBX_EACCESS;
@ -13457,7 +13459,7 @@ int mdbx_cursor_renew(MDBX_txn *txn, MDBX_cursor *mc) {
return rc;
if (unlikely(!mdbx_txn_dbi_exists(txn, mc->mc_dbi, DBI_VALID)))
return MDBX_EINVAL;
return MDBX_BAD_DBI;
if (unlikely(mc->mc_backup))
return MDBX_EINVAL;
@ -14933,7 +14935,7 @@ int mdbx_del(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
return MDBX_EINVAL;
if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID)))
return MDBX_EINVAL;
return MDBX_BAD_DBI;
if (unlikely(txn->mt_flags & (MDBX_TXN_RDONLY | MDBX_TXN_BLOCKED)))
return (txn->mt_flags & MDBX_TXN_RDONLY) ? MDBX_EACCESS : MDBX_BAD_TXN;
@ -15475,7 +15477,7 @@ int mdbx_put(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data,
return MDBX_EINVAL;
if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID)))
return MDBX_EINVAL;
return MDBX_BAD_DBI;
if (unlikely(flags & ~(MDBX_NOOVERWRITE | MDBX_NODUPDATA | MDBX_RESERVE |
MDBX_APPEND | MDBX_APPENDDUP | MDBX_CURRENT)))
@ -16125,7 +16127,7 @@ int __cold mdbx_env_copy(MDBX_env *env, const char *dest_path,
mdbx_filehandle_t newfd;
rc = mdbx_openfile(MDBX_OPEN_COPY, env, dest_path, &newfd,
#if defined(_WIN32) || defined(_WIN64)
(mode_t)-1
(mdbx_mode_t)-1
#else
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP
#endif
@ -16307,7 +16309,7 @@ int __cold mdbx_dbi_dupsort_depthmask(MDBX_txn *txn, MDBX_dbi dbi,
return MDBX_EINVAL;
if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_VALID)))
return MDBX_EINVAL;
return MDBX_BAD_DBI;
MDBX_cursor_couple cx;
rc = mdbx_cursor_init(&cx.outer, txn, dbi);
@ -16780,7 +16782,7 @@ int __cold mdbx_dbi_stat(MDBX_txn *txn, MDBX_dbi dbi, MDBX_stat *dest,
return MDBX_EINVAL;
if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_VALID)))
return MDBX_EINVAL;
return MDBX_BAD_DBI;
const size_t size_before_modtxnid = offsetof(MDBX_stat, ms_mod_txnid);
if (unlikely(bytes != sizeof(MDBX_stat)) && bytes != size_before_modtxnid)
@ -16822,7 +16824,7 @@ int mdbx_dbi_close(MDBX_env *env, MDBX_dbi dbi) {
return rc;
if (unlikely(dbi < CORE_DBS || dbi >= env->me_maxdbs))
return MDBX_EINVAL;
return MDBX_BAD_DBI;
rc = mdbx_fastmutex_acquire(&env->me_dbi_lock);
if (likely(rc == MDBX_SUCCESS)) {
@ -16842,7 +16844,7 @@ int mdbx_dbi_flags_ex(MDBX_txn *txn, MDBX_dbi dbi, unsigned *flags,
return MDBX_EINVAL;
if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_VALID)))
return MDBX_EINVAL;
return MDBX_BAD_DBI;
*flags = txn->mt_dbs[dbi].md_flags & DB_PERSISTENT_FLAGS;
*state =
@ -16954,7 +16956,7 @@ int mdbx_drop(MDBX_txn *txn, MDBX_dbi dbi, bool del) {
return rc;
if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID)))
return MDBX_EINVAL;
return MDBX_BAD_DBI;
if (unlikely(TXN_DBI_CHANGED(txn, dbi)))
return MDBX_BAD_DBI;
@ -16965,7 +16967,7 @@ int mdbx_drop(MDBX_txn *txn, MDBX_dbi dbi, bool del) {
return rc;
if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID))) {
rc = MDBX_EINVAL;
rc = MDBX_BAD_DBI;
goto bailout;
}
@ -17023,7 +17025,7 @@ int mdbx_set_compare(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cmp_func *cmp) {
return rc;
if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID)))
return MDBX_EINVAL;
return MDBX_BAD_DBI;
txn->mt_dbxs[dbi].md_cmp = cmp;
return MDBX_SUCCESS;
@ -17035,7 +17037,7 @@ int mdbx_set_dupsort(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cmp_func *cmp) {
return rc;
if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID)))
return MDBX_EINVAL;
return MDBX_BAD_DBI;
txn->mt_dbxs[dbi].md_dcmp = cmp;
return MDBX_SUCCESS;
@ -18095,7 +18097,7 @@ int mdbx_estimate_range(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *begin_key,
return MDBX_EINVAL;
if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID)))
return MDBX_EINVAL;
return MDBX_BAD_DBI;
MDBX_cursor_couple begin;
/* LY: first, initialize cursor to refresh a DB in case it have DB_STALE */
@ -18252,24 +18254,27 @@ int mdbx_estimate_range(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *begin_key,
* - внешняя аллокация курсоров, в том числе на стеке (без malloc).
* - получения статуса страницы по адресу (знать о P_DIRTY).
*/
int mdbx_replace(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
MDBX_val *new_data, MDBX_val *old_data,
MDBX_put_flags_t flags) {
int mdbx_replace_ex(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
MDBX_val *new_data, MDBX_val *old_data,
MDBX_put_flags_t flags, MDBX_preserve_func preserver,
void *preserver_context) {
int rc = check_txn_rw(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
if (unlikely(!key || !old_data || old_data == new_data))
if (unlikely(!key || !old_data || old_data == new_data || !preserver))
return MDBX_EINVAL;
if (unlikely(old_data->iov_base == NULL && old_data->iov_len))
return MDBX_EINVAL;
if (unlikely(new_data == NULL && !(flags & MDBX_CURRENT)))
if (unlikely(new_data == NULL &&
(flags & (MDBX_CURRENT | MDBX_RESERVE)) != MDBX_CURRENT))
return MDBX_EINVAL;
if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID)))
return MDBX_EINVAL;
return MDBX_BAD_DBI;
if (unlikely(flags & ~(MDBX_NOOVERWRITE | MDBX_NODUPDATA | MDBX_RESERVE |
MDBX_APPEND | MDBX_APPENDDUP | MDBX_CURRENT)))
@ -18359,14 +18364,11 @@ int mdbx_replace(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
}
if (IS_DIRTY(page)) {
if (unlikely(old_data->iov_len < present_data.iov_len)) {
old_data->iov_base = NULL;
old_data->iov_len = present_data.iov_len;
rc = MDBX_RESULT_TRUE;
rc = preserver ? preserver(preserver_context, old_data,
present_data.iov_base, present_data.iov_len)
: MDBX_SUCCESS;
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
}
memcpy(old_data->iov_base, present_data.iov_base, present_data.iov_len);
old_data->iov_len = present_data.iov_len;
} else {
*old_data = present_data;
}
@ -18383,6 +18385,25 @@ bailout:
return rc;
}
static int default_value_preserver(void *context, MDBX_val *target,
const void *src, size_t bytes) {
(void)context;
if (unlikely(target->iov_len < bytes)) {
target->iov_base = nullptr;
target->iov_len = bytes;
return MDBX_RESULT_TRUE;
}
memcpy(target->iov_base, src, target->iov_len = bytes);
return MDBX_SUCCESS;
}
int mdbx_replace(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
MDBX_val *new_data, MDBX_val *old_data,
MDBX_put_flags_t flags) {
return mdbx_replace_ex(txn, dbi, key, new_data, old_data, flags,
default_value_preserver, nullptr);
}
/* Функция сообщает находится ли указанный адрес в "грязной" странице у
* заданной пишущей транзакции. В конечном счете это позволяет избавиться от
* лишнего копирования данных из НЕ-грязных страниц.
@ -18410,21 +18431,21 @@ int mdbx_is_dirty(const MDBX_txn *txn, const void *ptr) {
if (unlikely(rc != MDBX_SUCCESS))
return rc;
if (txn->mt_flags & MDBX_TXN_RDONLY)
return MDBX_RESULT_FALSE;
const MDBX_env *env = txn->mt_env;
const ptrdiff_t offset = (uint8_t *)ptr - env->me_map;
if (offset >= 0) {
const pgno_t pgno = bytes2pgno(env, offset);
if (likely(pgno < txn->mt_next_pgno)) {
if (txn->mt_flags & MDBX_TXN_RDONLY)
return MDBX_RESULT_FALSE;
const MDBX_page *page = pgno2page(env, pgno);
if (unlikely(page->mp_pgno != pgno)) {
/* The ptr pointed into middle of a large page,
* not to the beginning of a data. */
return MDBX_EINVAL;
}
if (unlikely(page->mp_flags & (P_DIRTY | P_LOOSE | P_KEEP)))
if (unlikely(page->mp_flags & (P_DIRTY | P_LOOSE | P_KEEP | P_META)))
return MDBX_RESULT_TRUE;
if (likely(txn->tw.spill_pages == nullptr))
return MDBX_RESULT_FALSE;
@ -18434,7 +18455,7 @@ int mdbx_is_dirty(const MDBX_txn *txn, const void *ptr) {
if ((size_t)offset < env->me_dxb_mmap.limit) {
/* Указатель адресует что-то в пределах mmap, но за границей
* распределенных страниц. Такое может случится если mdbx_is_dirty()
* вызывает после операции, в ходе которой гразная страница попала
* вызывается после операции, в ходе которой грязная страница попала
* в loose и затем была возвращена в нераспределенное пространство. */
return MDBX_RESULT_TRUE;
}
@ -18444,7 +18465,7 @@ int mdbx_is_dirty(const MDBX_txn *txn, const void *ptr) {
* передан некорректный адрес, либо адрес в теневой странице, которая была
* выделена посредством malloc().
*
* Для WRITE_MAP режима такая страница однозначно "не грязная",
* Для режима WRITE_MAP режима страница однозначно "не грязная",
* а для режимов без WRITE_MAP следует просматривать списки dirty
* и spilled страниц у каких-либо транзакций (в том числе дочерних).
*
@ -18465,7 +18486,7 @@ int mdbx_dbi_sequence(MDBX_txn *txn, MDBX_dbi dbi, uint64_t *result,
return rc;
if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID)))
return MDBX_EINVAL;
return MDBX_BAD_DBI;
if (unlikely(TXN_DBI_CHANGED(txn, dbi)))
return MDBX_BAD_DBI;
@ -18855,7 +18876,7 @@ int mdbx_set_attr(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data,
return MDBX_EBADSIGN;
if (unlikely(!TXN_DBI_EXIST(txn, dbi, DB_USRVALID)))
return MDBX_EINVAL;
return MDBX_BAD_DBI;
if (unlikely(txn->mt_flags & (MDBX_TXN_RDONLY | MDBX_TXN_BLOCKED)))
return (txn->mt_flags & MDBX_TXN_RDONLY) ? MDBX_EACCESS : MDBX_BAD_TXN;

View File

@ -116,7 +116,8 @@
#endif /* __noop */
#ifndef __fallthrough
# if defined(__cplusplus) && __has_cpp_attribute(fallthrough)
# if defined(__cplusplus) && (__has_cpp_attribute(fallthrough) && \
(!defined(__clang__) || __clang__ > 4)) || __cplusplus >= 201703L
# define __fallthrough [[fallthrough]]
# elif __GNUC_PREREQ(8, 0) && defined(__cplusplus) && __cplusplus >= 201103L
# define __fallthrough [[fallthrough]]
@ -151,7 +152,9 @@
#endif /* __prefetch */
#ifndef __noreturn
# if defined(__GNUC__) || __has_attribute(__noreturn__)
# ifdef _Noreturn
# define __noreturn _Noreturn
# elif defined(__GNUC__) || __has_attribute(__noreturn__)
# define __noreturn __attribute__((__noreturn__))
# elif defined(_MSC_VER)
# define __noreturn __declspec(noreturn)

View File

@ -48,7 +48,7 @@
#endif
#if MDBX_DISABLE_GNU_SOURCE
#undef _GNU_SOURCE
#elif defined(__linux__) || defined(__gnu_linux__)
#elif (defined(__linux__) || defined(__gnu_linux__)) && !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
@ -89,6 +89,7 @@
#pragma warning(disable : 4366) /* the result of the unary '&' operator may be unaligned */
#pragma warning(disable : 4200) /* nonstandard extension used: zero-sized array in struct/union */
#pragma warning(disable : 4204) /* nonstandard extension used: non-constant aggregate initializer */
#pragma warning(disable : 4505) /* unreferenced local function has been removed */
#endif /* _MSC_VER (warnings) */
#if defined(MDBX_TOOLS)
@ -127,6 +128,16 @@
# warning "libmdbx don't compatible with ThreadSanitizer, you will get a lot of false-positive issues."
#endif /* __SANITIZE_THREAD__ */
#if __has_warning("-Wnested-anon-types")
# if defined(__clang__)
# pragma clang diagnostic ignored "-Wnested-anon-types"
# elif defined(__GNUC__)
# pragma GCC diagnostic ignored "-Wnested-anon-types"
# else
# pragma warning disable "nested-anon-types"
# endif
#endif /* -Wnested-anon-types */
#if __has_warning("-Wconstant-logical-operand")
# if defined(__clang__)
# pragma clang diagnostic ignored "-Wconstant-logical-operand"
@ -155,6 +166,10 @@
/* *INDENT-ON* */
/* clang-format on */
#ifdef __cplusplus
extern "C" {
#endif
#include "osal.h"
#define mdbx_sourcery_anchor XCONCAT(mdbx_sourcery_, MDBX_BUILD_SOURCERY)
@ -216,6 +231,7 @@ typedef uint64_t txnid_t;
#define PRIaTXN PRIi64
#define MIN_TXNID UINT64_C(1)
#define MAX_TXNID (SAFE64_INVALID_THRESHOLD - 1)
#define INITIAL_TXNID (MIN_TXNID + NUM_METAS - 1)
#define INVALID_TXNID UINT64_MAX
/* LY: for testing non-atomic 64-bit txnid on 32-bit arches.
* #define MDBX_TXNID_STEP (UINT32_MAX / 3) */
@ -1019,14 +1035,9 @@ struct MDBX_env {
#define MDBX_RUNTIME_FLAGS_INIT \
((MDBX_DEBUG) > 0) * MDBX_DBG_ASSERT + ((MDBX_DEBUG) > 1) * MDBX_DBG_AUDIT
#ifdef MDBX_ALLOY
static uint8_t mdbx_runtime_flags = MDBX_RUNTIME_FLAGS_INIT;
static uint8_t mdbx_loglevel = MDBX_DEBUG;
#else
extern uint8_t mdbx_runtime_flags;
extern uint8_t mdbx_loglevel;
#endif /* MDBX_ALLOY */
MDBX_INTERNAL_VAR MDBX_debug_func *mdbx_debug_logger;
extern MDBX_debug_func *mdbx_debug_logger;
MDBX_INTERNAL_FUNC void mdbx_debug_log(int type, const char *function, int line,
const char *fmt, ...)
@ -1067,15 +1078,15 @@ MDBX_INTERNAL_FUNC void mdbx_debug_log(int type, const char *function, int line,
#define mdbx_panic(fmt, ...) \
__android_log_assert("panic", "mdbx", fmt, __VA_ARGS__)
#else
MDBX_INTERNAL_FUNC void mdbx_panic(const char *fmt, ...) __printf_args(1, 2);
void mdbx_panic(const char *fmt, ...) __printf_args(1, 2);
#endif
#if !MDBX_DEBUG && defined(__ANDROID_API__)
#define mdbx_assert_fail(env, msg, func, line) \
__android_log_assert(msg, "mdbx", "%s:%u", func, line)
#else
MDBX_INTERNAL_FUNC void mdbx_assert_fail(const MDBX_env *env, const char *msg,
const char *func, int line);
void mdbx_assert_fail(const MDBX_env *env, const char *msg, const char *func,
int line);
#endif
#define mdbx_debug_extra(fmt, ...) \
@ -1318,7 +1329,7 @@ typedef struct MDBX_node {
MDBX_INTEGERDUP | MDBX_REVERSEDUP)
/* mdbx_dbi_open() flags */
#define DB_USABLE_FLAGS (DB_PERSISTENT_FLAGS | MDBX_CREATE | MDBX_ACCEDE)
#define DB_USABLE_FLAGS (DB_PERSISTENT_FLAGS | MDBX_CREATE | MDBX_DB_ACCEDE)
#define DB_VALID 0x8000 /* DB handle is valid, for me_dbflags */
#define DB_INTERNAL_FLAGS DB_VALID
@ -1402,13 +1413,19 @@ ceil_powerof2(size_t value, size_t granularity) {
MDBX_LIFORECLAIM | MDBX_EXCLUSIVE)
#define ENV_USABLE_FLAGS (ENV_CHANGEABLE_FLAGS | ENV_CHANGELESS_FLAGS)
#if !(defined(__cplusplus) && defined(_MSC_VER) && _MSC_VER == 1900)
static __maybe_unused void static_checks(void) {
STATIC_ASSERT_MSG(INT16_MAX - CORE_DBS == MDBX_MAX_DBI,
"Oops, MDBX_MAX_DBI or CORE_DBS?");
STATIC_ASSERT_MSG((MDBX_ACCEDE | MDBX_CREATE) ==
STATIC_ASSERT_MSG((unsigned)(MDBX_DB_ACCEDE | MDBX_CREATE) ==
((DB_USABLE_FLAGS | DB_INTERNAL_FLAGS) &
(ENV_USABLE_FLAGS | ENV_INTERNAL_FLAGS)),
"Oops, some flags overlapped or wrong");
STATIC_ASSERT_MSG((ENV_INTERNAL_FLAGS & ENV_USABLE_FLAGS) == 0,
"Oops, some flags overlapped or wrong");
}
#endif /* Disabled for MSVC 19.0 (VisualStudio 2015) */
#ifdef __cplusplus
}
#endif

1065
src/mdbx.c++ Normal file

File diff suppressed because it is too large Load Diff

View File

@ -199,9 +199,8 @@ __extern_C void __assert(const char *function, const char *file, int line,
#if !defined(__ANDROID_API__) || MDBX_DEBUG
MDBX_INTERNAL_FUNC void __cold mdbx_assert_fail(const MDBX_env *env,
const char *msg,
const char *func, int line) {
void __cold mdbx_assert_fail(const MDBX_env *env, const char *msg,
const char *func, int line) {
#if MDBX_DEBUG
if (env && env->me_assert_func) {
env->me_assert_func(env, msg, func, line);
@ -241,7 +240,7 @@ MDBX_INTERNAL_FUNC void __cold mdbx_assert_fail(const MDBX_env *env,
#if !defined(__ANDROID_API__)
MDBX_INTERNAL_FUNC __cold void mdbx_panic(const char *fmt, ...) {
__cold void mdbx_panic(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
@ -514,7 +513,7 @@ MDBX_INTERNAL_FUNC int mdbx_removefile(const char *pathname) {
MDBX_INTERNAL_FUNC int mdbx_openfile(const enum mdbx_openfile_purpose purpose,
const MDBX_env *env, const char *pathname,
mdbx_filehandle_t *fd,
mode_t unix_mode_bits) {
mdbx_mode_t unix_mode_bits) {
*fd = INVALID_HANDLE_VALUE;
#if defined(_WIN32) || defined(_WIN64)

View File

@ -596,7 +596,7 @@ enum mdbx_openfile_purpose {
MDBX_INTERNAL_FUNC int mdbx_openfile(const enum mdbx_openfile_purpose purpose,
const MDBX_env *env, const char *pathname,
mdbx_filehandle_t *fd,
mode_t unix_mode_bits);
mdbx_mode_t unix_mode_bits);
MDBX_INTERNAL_FUNC int mdbx_closefile(mdbx_filehandle_t fd);
MDBX_INTERNAL_FUNC int mdbx_removefile(const char *pathname);
MDBX_INTERNAL_FUNC int mdbx_is_pipe(mdbx_filehandle_t fd);

View File

@ -27,30 +27,6 @@ add_executable(mdbx_test
nested.cc
)
list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_20 HAS_CXX20)
list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_17 HAS_CXX17)
list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_14 HAS_CXX14)
list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_11 HAS_CXX11)
if(NOT DEFINED MDBX_CXX_STANDARD)
if(DEFINED CMAKE_CXX_STANDARD)
set(MDBX_CXX_STANDARD ${CMAKE_CXX_STANDARD})
elseif(NOT HAS_CXX20 LESS 0)
set(MDBX_CXX_STANDARD 20)
elseif(NOT HAS_CXX17 LESS 0)
set(MDBX_CXX_STANDARD 17)
elseif(NOT HAS_CXX14 LESS 0)
set(MDBX_CXX_STANDARD 14)
elseif(NOT HAS_CXX11 LESS 0)
set(MDBX_CXX_STANDARD 11)
endif()
endif()
if(MDBX_CXX_STANDARD)
message(STATUS "Use C++${MDBX_CXX_STANDARD} for libmdbx")
if(NOT SUBPROJECT OR NOT DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD ${MDBX_CXX_STANDARD})
endif()
endif()
if(MDBX_CXX_STANDARD)
set_target_properties(mdbx_test PROPERTIES
CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON)

View File

@ -31,14 +31,15 @@ const char *test_strerror(int errnum) {
return mdbx_strerror_r(errnum, buf, sizeof(buf));
}
void __noreturn failure_perror(const char *what, int errnum) {
__noreturn void failure_perror(const char *what, int errnum) {
failure("%s failed: %s (%d)\n", what, test_strerror(errnum), errnum);
}
//-----------------------------------------------------------------------------
static void mdbx_logger(MDBX_log_level_t priority, const char *function,
int line, const char *msg, va_list args) {
int line, const char *msg,
va_list args) cxx17_noexcept {
if (!function)
function = "unknown";

View File

@ -17,9 +17,9 @@
#include "base.h"
#include "chrono.h"
void __noreturn usage(void);
void __noreturn __printf_args(1, 2) failure(const char *fmt, ...);
void __noreturn failure_perror(const char *what, int errnum);
__noreturn void usage(void);
__noreturn void __printf_args(1, 2) failure(const char *fmt, ...);
__noreturn void failure_perror(const char *what, int errnum);
const char *test_strerror(int errnum);
namespace logging {

View File

@ -19,7 +19,7 @@
#include <sys/time.h>
#endif /* !Windows */
void __noreturn usage(void) {
__noreturn void usage(void) {
puts(
"usage:\n"
" --help or -h Show this text\n"

View File

@ -16,6 +16,7 @@
#if !(defined(_WIN32) || defined(_WIN64))
#include <atomic>
#include <pthread.h>
#include <signal.h>
#include <sys/mman.h>
@ -309,7 +310,7 @@ bool actor_config::osal_deserialize(const char *str, const char *end,
static pid_t overlord_pid;
static std::atomic<int> sigusr1_head, sigusr2_head;
static std::atomic_int sigusr1_head, sigusr2_head;
static void handler_SIGUSR(int signum) {
switch (signum) {
case SIGUSR1:
@ -337,7 +338,7 @@ bool osal_progress_push(bool active) {
static std::unordered_map<pid_t, actor_status> childs;
static std::atomic<int> sigalarm_head;
static std::atomic_int sigalarm_head;
static void handler_SIGCHLD(int signum) {
if (signum == SIGALRM)
++sigalarm_head;

View File

@ -80,7 +80,7 @@ const char *keygencase2str(const keygen_case keycase) {
int testcase::oom_callback(MDBX_env *env, mdbx_pid_t pid, mdbx_tid_t tid,
uint64_t txn, unsigned gap, size_t space,
int retry) {
int retry) cxx17_noexcept {
testcase *self = (testcase *)mdbx_env_get_userctx(env);
@ -530,7 +530,8 @@ void testcase::db_table_close(MDBX_dbi handle) {
void testcase::checkdata(const char *step, MDBX_dbi handle, MDBX_val key2check,
MDBX_val expected_valued) {
MDBX_val actual_value = expected_valued;
int rc = mdbx_get_nearest(txn_guard.get(), handle, &key2check, &actual_value);
int rc = mdbx_get_equal_or_great(txn_guard.get(), handle, &key2check,
&actual_value);
if (unlikely(rc != MDBX_SUCCESS))
failure_perror(step, rc);
if (!is_samedata(&actual_value, &expected_valued))

View File

@ -167,7 +167,8 @@ protected:
int remove(const keygen::buffer &akey, const keygen::buffer &adata);
static int oom_callback(MDBX_env *env, mdbx_pid_t pid, mdbx_tid_t tid,
uint64_t txn, unsigned gap, size_t space, int retry);
uint64_t txn, unsigned gap, size_t space,
int retry) cxx17_noexcept;
MDBX_env_flags_t actual_env_mode{MDBX_ENV_DEFAULTS};
bool is_nested_txn_available() const {