mirror of
https://gitflic.ru/project/erthink/libmdbx.git
synced 2025-07-19 21:56:01 +00:00
mdbx: изменение лицензии и реструктуризация исходного кода.
This commit is contained in:
parent
e9f5c0c308
commit
3de3d425a1
34
AUTHORS
34
AUTHORS
@ -1,34 +0,0 @@
|
||||
Contributors
|
||||
============
|
||||
|
||||
- Alexey Naumov <alexey.naumov@gmail.com>
|
||||
- Andrew Ashikhmin <andrey.ashikhmin@gmail.com>
|
||||
- Chris Mikkelson <cmikk@qwest.net>
|
||||
- Claude Brisson <claude.brisson@gmail.com>
|
||||
- David Barbour <dmbarbour@gmail.com>
|
||||
- David Wilson <dw@botanicus.net>
|
||||
- dreamsxin <dreamsxin@126.com>
|
||||
- Hallvard Furuseth <hallvard@openldap.org>, <h.b.furuseth@usit.uio.no>
|
||||
- Heiko Becker <heirecka@exherbo.org>
|
||||
- Howard Chu <hyc@openldap.org>, <hyc@symas.com>
|
||||
- Ignacio Casal Quinteiro <ignacio.casal@nice-software.com>
|
||||
- James Rouzier <rouzier@gmail.com>
|
||||
- Jean-Christophe DUBOIS <jcd@tribudubois.net>
|
||||
- John Hewson <john@jahewson.com>
|
||||
- Klaus Malorny <klaus.malorny@knipp.de>
|
||||
- Kurt Zeilenga <kurt.zeilenga@isode.com>
|
||||
- Leonid Yuriev <leo@yuriev.ru>, <lyuryev@ptsecurity.ru>
|
||||
- Lorenz Bauer <lmb@cloudflare.com>
|
||||
- Luke Yeager <lyeager@nvidia.com>
|
||||
- Martin Hedenfalk <martin@bzero.se>
|
||||
- Ondrej Kuznik <ondrej.kuznik@acision.com>
|
||||
- Orivej Desh <orivej@gmx.fr>
|
||||
- Oskari Timperi <oskari.timperi@iki.fi>
|
||||
- Pavel Medvedev <pmedvedev@gmail.com>
|
||||
- Philipp Storz <philipp.storz@bareos.com>
|
||||
- Quanah Gibson-Mount <quanah@openldap.org>
|
||||
- Salvador Ortiz <sog@msg.com.mx>
|
||||
- Sebastien Launay <sebastien@slaunay.fr>
|
||||
- Vladimir Romanov <vromanov@gmail.com>
|
||||
- Zano Foundation <crypto.sowle@gmail.com>
|
||||
- 장세연 <sasgas@castis.com>
|
248
CMakeLists.txt
248
CMakeLists.txt
@ -1,16 +1,5 @@
|
||||
##
|
||||
## Copyright 2020-2024 Leonid Yuriev <leo@yuriev.ru>
|
||||
## and other libmdbx authors: please see AUTHORS file.
|
||||
## All rights reserved.
|
||||
##
|
||||
## Redistribution and use in source and binary forms, with or without
|
||||
## modification, are permitted only as authorized by the OpenLDAP
|
||||
## Public License.
|
||||
##
|
||||
## A copy of this license is available in the file LICENSE in the
|
||||
## top-level directory of the distribution or, alternatively, at
|
||||
## <http://www.OpenLDAP.org/license.html>.
|
||||
##
|
||||
## Copyright (c) 2020-2024 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru>
|
||||
## SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
##
|
||||
## libmdbx = { Revised and extended descendant of Symas LMDB. }
|
||||
@ -69,14 +58,109 @@ else()
|
||||
endif()
|
||||
|
||||
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/COPYRIGHT" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/NOTICE" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/README.md" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/mdbx.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/mdbx.h++" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/core.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/alloy.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/api-cursor.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/api-env.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/api-extra.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/api-key-transform.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/api-txn.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/atomics-ops.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/atomics-types.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/audit.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/chk.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/cogs.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/cogs.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/coherency.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/cold.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.in" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/copy.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/cursor.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/cursor.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/dbi.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/dbi.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/debug_begin.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/debug_end.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/dpl.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/dpl.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/dxb.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/env-opts.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/env.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/essentials.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/gc-get.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/gc-put.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/gc.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/global.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/internals.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/layout-dxb.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/layout-lck.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/lck-posix.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/lck-windows.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/lck.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/lck.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/logging_and_debug.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/logging_and_debug.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/man1/mdbx_chk.1" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/man1/mdbx_copy.1" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/man1/mdbx_drop.1" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/man1/mdbx_dump.1" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/man1/mdbx_load.1" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/man1/mdbx_stat.1" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/mdbx.c++" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/meta.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/meta.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/misc.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/mvcc-readers.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/node.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/node.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/ntdll.def" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/options.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/osal.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/osal.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/page-get.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/page-iov.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/page-iov.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/page-ops.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/page-ops.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/page-search.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/pnl.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/pnl.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/preface.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/proto.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/range-estimate.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/refund.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/sort.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/spill.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/spill.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/subdb.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/tls.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/tls.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/tools/chk.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/tools/copy.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/tools/drop.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/tools/dump.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/tools/load.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/tools/stat.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/tools/wingetopt.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/tools/wingetopt.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/tree.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/txl.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/txl.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/txn.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/unaligned.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/utils.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/utils.h" 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" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/mdbx.c++")
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/walk.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/walk.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/windows-import.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/windows-import.h")
|
||||
set(MDBX_AMALGAMATED_SOURCE FALSE)
|
||||
find_program(GIT git)
|
||||
if(NOT GIT)
|
||||
@ -84,21 +168,27 @@ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git" AND
|
||||
endif()
|
||||
set(MDBX_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src")
|
||||
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION.txt" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/NOTICE" 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")
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/mdbx.h" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/mdbx.h++" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/mdbx_chk.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/mdbx_copy.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/mdbx_dump.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/mdbx_load.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/mdbx_stat.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/mdbx_drop.c" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/ntdll.def" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/config.h.in")
|
||||
set(MDBX_AMALGAMATED_SOURCE TRUE)
|
||||
set(MDBX_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
else()
|
||||
message(FATAL_ERROR "\n"
|
||||
"Please don't use tarballs nor zips which are automatically provided by Github! "
|
||||
"These archives do not contain version information and thus are unfit to build libmdbx. "
|
||||
"You can vote for ability of disabling auto-creation such unsuitable archives at https://github.community/t/disable-tarball\n"
|
||||
"Instead of above, just clone the git repository, either download a tarball or zip with the properly amalgamated source core. "
|
||||
"For embedding libmdbx use a git-submodule or the amalgamated source code.\n"
|
||||
"Please, avoid using any other techniques.")
|
||||
"The set of libmdbx source code files is incomplete! "
|
||||
"Instead just follow the https://libmdbx.dqdkfa.ru/usage.html "
|
||||
"PLEASE, AVOID USING ANY OTHER TECHNIQUES.")
|
||||
endif()
|
||||
|
||||
if(DEFINED PROJECT_NAME)
|
||||
@ -600,13 +690,88 @@ else()
|
||||
include_directories("${MDBX_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
else()
|
||||
list(APPEND LIBMDBX_SOURCES
|
||||
"${MDBX_SOURCE_DIR}/api-cursor.c"
|
||||
"${MDBX_SOURCE_DIR}/api-env.c"
|
||||
"${MDBX_SOURCE_DIR}/api-extra.c"
|
||||
"${MDBX_SOURCE_DIR}/api-key-transform.c"
|
||||
"${MDBX_SOURCE_DIR}/api-txn.c"
|
||||
"${MDBX_SOURCE_DIR}/atomics-ops.h"
|
||||
"${MDBX_SOURCE_DIR}/atomics-types.h"
|
||||
"${MDBX_SOURCE_DIR}/audit.c"
|
||||
"${MDBX_SOURCE_DIR}/chk.c"
|
||||
"${MDBX_SOURCE_DIR}/cogs.c"
|
||||
"${MDBX_SOURCE_DIR}/cogs.h"
|
||||
"${MDBX_SOURCE_DIR}/coherency.c"
|
||||
"${MDBX_SOURCE_DIR}/cold.c"
|
||||
"${MDBX_SOURCE_DIR}/copy.c"
|
||||
"${MDBX_SOURCE_DIR}/cursor.c"
|
||||
"${MDBX_SOURCE_DIR}/cursor.h"
|
||||
"${MDBX_SOURCE_DIR}/dbi.c"
|
||||
"${MDBX_SOURCE_DIR}/dbi.h"
|
||||
"${MDBX_SOURCE_DIR}/dpl.c"
|
||||
"${MDBX_SOURCE_DIR}/dpl.h"
|
||||
"${MDBX_SOURCE_DIR}/dxb.c"
|
||||
"${MDBX_SOURCE_DIR}/env-opts.c"
|
||||
"${MDBX_SOURCE_DIR}/env.c"
|
||||
"${MDBX_SOURCE_DIR}/essentials.h"
|
||||
"${MDBX_SOURCE_DIR}/gc-get.c"
|
||||
"${MDBX_SOURCE_DIR}/gc-put.c"
|
||||
"${MDBX_SOURCE_DIR}/gc.h"
|
||||
"${MDBX_SOURCE_DIR}/global.c"
|
||||
"${MDBX_SOURCE_DIR}/internals.h"
|
||||
"${MDBX_SOURCE_DIR}/layout-dxb.h"
|
||||
"${MDBX_SOURCE_DIR}/layout-lck.h"
|
||||
"${MDBX_SOURCE_DIR}/lck.c"
|
||||
"${MDBX_SOURCE_DIR}/lck.h"
|
||||
"${MDBX_SOURCE_DIR}/logging_and_debug.c"
|
||||
"${MDBX_SOURCE_DIR}/logging_and_debug.h"
|
||||
"${MDBX_SOURCE_DIR}/meta.c"
|
||||
"${MDBX_SOURCE_DIR}/meta.h"
|
||||
"${MDBX_SOURCE_DIR}/misc.c"
|
||||
"${MDBX_SOURCE_DIR}/mvcc-readers.c"
|
||||
"${MDBX_SOURCE_DIR}/node.c"
|
||||
"${MDBX_SOURCE_DIR}/node.h"
|
||||
"${MDBX_SOURCE_DIR}/options.h"
|
||||
"${MDBX_SOURCE_DIR}/osal.c"
|
||||
"${MDBX_SOURCE_DIR}/osal.h"
|
||||
"${MDBX_SOURCE_DIR}/page-get.c"
|
||||
"${MDBX_SOURCE_DIR}/page-iov.c"
|
||||
"${MDBX_SOURCE_DIR}/page-iov.h"
|
||||
"${MDBX_SOURCE_DIR}/page-ops.c"
|
||||
"${MDBX_SOURCE_DIR}/page-ops.h"
|
||||
"${MDBX_SOURCE_DIR}/page-search.c"
|
||||
"${MDBX_SOURCE_DIR}/pnl.c"
|
||||
"${MDBX_SOURCE_DIR}/pnl.h"
|
||||
"${MDBX_SOURCE_DIR}/preface.h"
|
||||
"${MDBX_SOURCE_DIR}/proto.h"
|
||||
"${MDBX_SOURCE_DIR}/range-estimate.c"
|
||||
"${MDBX_SOURCE_DIR}/refund.c"
|
||||
"${MDBX_SOURCE_DIR}/sort.h"
|
||||
"${MDBX_SOURCE_DIR}/spill.c"
|
||||
"${MDBX_SOURCE_DIR}/spill.h"
|
||||
"${MDBX_SOURCE_DIR}/subdb.c"
|
||||
"${MDBX_SOURCE_DIR}/tls.c"
|
||||
"${MDBX_SOURCE_DIR}/tls.h"
|
||||
"${MDBX_SOURCE_DIR}/tree.c"
|
||||
"${MDBX_SOURCE_DIR}/txl.c"
|
||||
"${MDBX_SOURCE_DIR}/txl.h"
|
||||
"${MDBX_SOURCE_DIR}/txn.c"
|
||||
"${MDBX_SOURCE_DIR}/unaligned.h"
|
||||
"${MDBX_SOURCE_DIR}/utils.c"
|
||||
"${MDBX_SOURCE_DIR}/utils.h"
|
||||
"${MDBX_SOURCE_DIR}/walk.c"
|
||||
"${MDBX_SOURCE_DIR}/walk.h"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/version.c"
|
||||
"${MDBX_SOURCE_DIR}/options.h" "${MDBX_SOURCE_DIR}/base.h"
|
||||
"${MDBX_SOURCE_DIR}/internals.h" "${MDBX_SOURCE_DIR}/osal.h"
|
||||
"${MDBX_SOURCE_DIR}/core.c" "${MDBX_SOURCE_DIR}/osal.c"
|
||||
"${MDBX_SOURCE_DIR}/lck-posix.c")
|
||||
)
|
||||
if(NOT MSVC)
|
||||
list(APPEND LIBMDBX_SOURCES "${MDBX_SOURCE_DIR}/lck-posix.c")
|
||||
endif()
|
||||
if(NOT APPLE)
|
||||
list(APPEND LIBMDBX_SOURCES "${MDBX_SOURCE_DIR}/lck-windows.c")
|
||||
list(APPEND LIBMDBX_SOURCES
|
||||
"${MDBX_SOURCE_DIR}/windows-import.h"
|
||||
"${MDBX_SOURCE_DIR}/windows-import.c"
|
||||
"${MDBX_SOURCE_DIR}/lck-windows.c"
|
||||
)
|
||||
endif()
|
||||
include_directories("${MDBX_SOURCE_DIR}")
|
||||
endif()
|
||||
@ -747,20 +912,23 @@ endif()
|
||||
|
||||
# build mdbx-tools
|
||||
if(MDBX_BUILD_TOOLS)
|
||||
if(NOT MDBX_AMALGAMATED_SOURCE AND ${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
|
||||
set(WINGETOPT_SRC ${MDBX_SOURCE_DIR}/wingetopt.c ${MDBX_SOURCE_DIR}/wingetopt.h)
|
||||
else()
|
||||
set(WINGETOPT_SRC "")
|
||||
set(WINGETOPT_SRC "")
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
|
||||
set(WINGETOPT_SRC ${MDBX_SOURCE_DIR}/tools/wingetopt.c ${MDBX_SOURCE_DIR}/tools/wingetopt.h)
|
||||
endif()
|
||||
|
||||
foreach(TOOL mdbx_chk mdbx_copy mdbx_stat mdbx_dump mdbx_load mdbx_drop)
|
||||
add_executable(${TOOL} mdbx.h ${MDBX_SOURCE_DIR}/${TOOL}.c ${WINGETOPT_SRC})
|
||||
foreach(TOOL chk copy stat dump load drop)
|
||||
if(MDBX_AMALGAMATED_SOURCE)
|
||||
add_executable(mdbx_${TOOL} mdbx.h ${MDBX_SOURCE_DIR}/mdbx_${TOOL}.c)
|
||||
else()
|
||||
add_executable(mdbx_${TOOL} mdbx.h ${MDBX_SOURCE_DIR}/tools/${TOOL}.c ${WINGETOPT_SRC})
|
||||
endif()
|
||||
if(NOT C_FALLBACK_GNU11 AND NOT C_FALLBACK_11)
|
||||
set_target_properties(${TOOL} PROPERTIES
|
||||
set_target_properties(mdbx_${TOOL} PROPERTIES
|
||||
C_STANDARD ${MDBX_C_STANDARD} C_STANDARD_REQUIRED ON)
|
||||
endif()
|
||||
target_setup_options(${TOOL})
|
||||
target_link_libraries(${TOOL} ${TOOL_MDBX_LIB})
|
||||
target_setup_options(mdbx_${TOOL})
|
||||
target_link_libraries(mdbx_${TOOL} ${TOOL_MDBX_LIB})
|
||||
endforeach()
|
||||
if(LIB_MATH)
|
||||
target_link_libraries(mdbx_chk ${LIB_MATH})
|
||||
|
158
COPYRIGHT
158
COPYRIGHT
@ -1,7 +1,138 @@
|
||||
Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>.
|
||||
Copyright 2011-2015 Howard Chu, Symas Corp.
|
||||
Copyright 2015,2016 Peter-Service R&D LLC.
|
||||
All rights reserved.
|
||||
Copyright (c) 2015-2024 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
СМЕНА ЛИЦЕНЗИИ (THE LICENSE CHANGE)
|
||||
|
||||
OpenLDAP Public License → Apache 2.0
|
||||
|
||||
Briefly:
|
||||
Historically, in 2015 an early MDBX source code was derived from the
|
||||
"LMDB engine" created by Howard Chu <hyc@symas.com> in 2011-2015,
|
||||
which based on btree.c written by Martin Hedenfalk <martin@bzero.se>.
|
||||
|
||||
By 2024, MDBX source code has actually been rewritten and has so
|
||||
little in common with the original LMDB that I thought it admissible
|
||||
to change the license. Below are more detailed explanations.
|
||||
|
||||
Кратко:
|
||||
Исторически в 2015 году ранний исходный MDBX был заимствован из «LMDB
|
||||
engine», созданной Howard Chu <hyc@symas.com> в 2011-2015, на основе
|
||||
btree.c созданного Martin Hedenfalk <martin@bzero.se> в 2009-2010.
|
||||
|
||||
К 2024 году исходный код MDBX фактически переписан и имеет настолько
|
||||
мало общего с первоначальным заимствованием из LMDB, что я счел
|
||||
уместным сменить лицензию. Ниже более подробные пояснения.
|
||||
|
||||
---
|
||||
|
||||
Первоисточник текста формулирован на Русском языке, который является
|
||||
родным для автора. Предполагается что все заинтересованные могут легко
|
||||
воспользоваться машинным переводом, который при всех недостатках сможет
|
||||
донести суть, намерения и местами даже передать тональность.
|
||||
|
||||
The original source of this text is in Russian, which is the author's
|
||||
native language. It is assumed that all concerned can easily use machine
|
||||
translation, which, with all the disadvantages, will be able to convey
|
||||
the essence, intentions and, in some places, even convey the tonality of
|
||||
a wording.
|
||||
|
||||
1. Причины
|
||||
|
||||
1.1. Лицензия Apache-2.0 является одной из самых популярных, так как
|
||||
содержит ряд уточнений, проясняющих и упрощающих использование исходного
|
||||
кода в производных работах и больших проектах. Эти особенности лицензии
|
||||
Apache-2.0 я нахожу достаточно ценными и удобными. Соответственно,
|
||||
переход на лицензию Apache-2.0 полезным в целом.
|
||||
|
||||
1.2. Проект OpenLDAP имеет определенную известность, в том числе, к
|
||||
сожалению, среди специалистов славится кране плохим качеством кода и
|
||||
сбоями при отходе от простых/базовых сценариев использования. Поэтому
|
||||
использование лицензии OpenLDAP, в глазах части аудитории, бросает тень
|
||||
на качества кода libmdbx, несмотря на то, что исходный код библиотеки
|
||||
переписан, в том числе, с целью повышения качества, надежности,
|
||||
стабильности и пригодности к тестированию.
|
||||
|
||||
Отмечу, что здесь не место для обсуждения объективности подобных мнений
|
||||
и причин, равно как и не место для оценки компетентности специалистов
|
||||
высказывающих такие суждения. Однако, здесь необходимо озвучить сам факт
|
||||
наличия такой негативной коннотации качества кода при упоминании
|
||||
OpenLDAP, совершенно без намерения как-либо задеть или обидеть
|
||||
контрибьюторов OpenLDAP.
|
||||
|
||||
1.3. С точки зрения исходного кода, к настоящему времени libmdbx стала
|
||||
совсем другим продуктом, о котором сейчас правильнее сказать что
|
||||
разработка вдохновлена LMDB, нежели основывается на заимствовании кода.
|
||||
Смена лицензии на переписанный код подчеркивает, что это действительно
|
||||
новый исходный код.
|
||||
|
||||
2. Легитимность
|
||||
|
||||
2.1. Исходная лицензия OpenLDAP 2.8 и актуальная лицензия Apache 2.0
|
||||
совпадают по базовым условиям. При этом лицензия Apache 2.0 уточняет,
|
||||
определяет и проясняет многие аспекты. Поэтому смену лицензии я склонен
|
||||
трактовать как уточнение, но как принципиальное изменение, которое
|
||||
могло-бы нарушить чьи-либо права.
|
||||
|
||||
2.2. С процедурной точки зрения, у меня есть право сменить лицензию на
|
||||
новый, написанный мной, исходный код. При этом объективно существует как
|
||||
техническая, так и юридическая проблемы отделения «нового кода» от
|
||||
«заимствованного», а также выделение/классификация кода, который
|
||||
является общественным достоянием и/или общеупотребительным воплощением
|
||||
«математических моделей и других публичных знаний».
|
||||
|
||||
Основываясь на собственной субъективной оценке кодовой базы, включая
|
||||
соотношения «нового», «заимствованного» и «общеупотребительного»
|
||||
исходного кода, я считаю что смена лицензии допустима. Одновременно с
|
||||
этим, я понимаю и признаю, что можно найти повод, чтобы трактовать
|
||||
ситуацию как «стакан наполовину полон/пуст». Поэтому декларирую
|
||||
готовность принимать претензии и устранять их путем полного
|
||||
переписывания оставшегося исходного кода, который попадает под критерии
|
||||
«заимствованного» и кто-то из контрибьюторов которого будет против
|
||||
изменения лицензии.
|
||||
|
||||
2.3. Вне зависимости от истории происхождения каждой строки исходного
|
||||
кода и её буквального авторства, прошу не считать производимую смену
|
||||
лицензии, и связанных с этим технических действий, как попытку плагиата,
|
||||
присвоения чужого труда, присвоения авторства или принижения вклада
|
||||
других авторов/контрибьторов. Безусловно проект MDBX/libmdbx не появился
|
||||
бы без LMDB и всех участников проекта LMDB, в особенности Говарда Чу
|
||||
(Howard Chu), Холлварда Фурусет (Hallvard Furuseth) и Мартина Хеденфок
|
||||
(Martin Hedenfalk). Как-бы исходный код не переписывался он всё равно
|
||||
будет основываться на базовых идеях и включать основные концепции LMDB.
|
||||
|
||||
3. Последствия и актуальные требования
|
||||
|
||||
Всё очень просто. Потребуется обеспечить требования новой лицензии в
|
||||
соответствии с 4-м пунктом лицензции Apache 2.0.
|
||||
|
||||
В частности, при использовании/распространении libmdbx потребуется
|
||||
обеспечить наличие файлов с текстом лицензии и файла NOTICE, а также
|
||||
обеспечить пользователям возможность ознакомиться с их содержимым в
|
||||
работах/продуктах использующих libmdbx.
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
Далее в справочных целях приведены уведомления об авторских правах из
|
||||
первоначально заимствованного кода.
|
||||
|
||||
---
|
||||
|
||||
Original source code was derived from LMDB in 2015,
|
||||
and later evolutionarily rewritten in 2015-2024:
|
||||
Copyright (c) 2011-2015 Howard Chu, Symas Corp. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted only as authorized by the OpenLDAP
|
||||
@ -11,12 +142,17 @@ A copy of this license is available in the file LICENSE in the
|
||||
top-level directory of the distribution or, alternatively, at
|
||||
<http://www.OpenLDAP.org/license.html>.
|
||||
|
||||
OpenLDAP is a registered trademark of the OpenLDAP Foundation.
|
||||
LMDB itself devived code from btree.c written by Martin Hedenfalk:
|
||||
Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se>
|
||||
|
||||
Individual files and/or contributed packages may be copyright by
|
||||
other parties and/or subject to additional restrictions.
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
This work also contains materials derived from public sources.
|
||||
|
||||
Additional information about OpenLDAP can be obtained at
|
||||
<http://www.openldap.org/>.
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
196
GNUmakefile
196
GNUmakefile
@ -61,7 +61,7 @@ MDBX_BUILD_CXX ?= YES
|
||||
CFLAGS ?= $(strip $(eval CFLAGS := -std=gnu11 -O2 -g -Wall -Werror -Wextra -Wpedantic -ffunction-sections -fPIC -fvisibility=hidden -pthread -Wno-error=attributes $$(shell for opt in -fno-semantic-interposition -Wno-unused-command-line-argument -Wno-tautological-compare; do [ -z "$$$$($(CC) '-DMDBX_BUILD_FLAGS="probe"' $$$${opt} -c $(SRC_PROBE_C) -o /dev/null >/dev/null 2>&1 || echo failed)" ] && echo "$$$${opt} "; done)$(CFLAGS_EXTRA))$(CFLAGS))
|
||||
|
||||
# choosing C++ standard with variable expansion trick (seems this work two times per session for GNU Make 3.81)
|
||||
CXXSTD ?= $(eval CXXSTD := $$(shell for std in gnu++23 c++23 gnu++2b c++2b gnu++20 c++20 gnu++2a c++2a gnu++17 c++17 gnu++1z c++1z gnu++14 c++14 gnu++1y c++1y gnu+11 c++11 gnu++0x c++0x; do $(CXX) -std=$$$${std} -c $(SRC_PROBE_CXX) -o /dev/null 2>probe4std-$$$${std}.err >/dev/null && echo "-std=$$$${std}" && exit; done))$(CXXSTD)
|
||||
CXXSTD ?= $(eval CXXSTD := $$(shell for std in gnu++23 c++23 gnu++2b c++2b gnu++20 c++20 gnu++2a c++2a gnu++17 c++17 gnu++1z c++1z gnu++14 c++14 gnu++1y c++1y gnu+11 c++11 gnu++0x c++0x; do $(CXX) -std=$$$${std} -DMDBX_BUILD_CXX=1 -c $(SRC_PROBE_CXX) -o /dev/null 2>probe4std-$$$${std}.err >/dev/null && echo "-std=$$$${std}" && exit; done))$(CXXSTD)
|
||||
CXXFLAGS ?= $(strip $(CXXSTD) $(filter-out -std=gnu11,$(CFLAGS)))
|
||||
|
||||
# libraries and options for linking
|
||||
@ -121,7 +121,8 @@ endef
|
||||
SO_SUFFIX := $(shell $(uname2sosuffix))
|
||||
HEADERS := mdbx.h mdbx.h++
|
||||
LIBRARIES := libmdbx.a libmdbx.$(SO_SUFFIX)
|
||||
TOOLS := mdbx_stat mdbx_copy mdbx_dump mdbx_load mdbx_chk mdbx_drop
|
||||
TOOLS := chk copy drop dump load stat
|
||||
MDBX_TOOLS := $(addprefix mdbx_,$(TOOLS))
|
||||
MANPAGES := mdbx_stat.1 mdbx_copy.1 mdbx_dump.1 mdbx_load.1 mdbx_chk.1 mdbx_drop.1
|
||||
TIP := // TIP:
|
||||
|
||||
@ -148,7 +149,7 @@ else
|
||||
$(info $(TIP) Use `make V=1` for verbose.)
|
||||
endif
|
||||
|
||||
all: show-options $(LIBRARIES) $(TOOLS)
|
||||
all: show-options $(LIBRARIES) $(MDBX_TOOLS)
|
||||
|
||||
help:
|
||||
@echo " make all - build libraries and tools"
|
||||
@ -234,26 +235,26 @@ options:
|
||||
ifeq ($(wildcard mdbx.c),mdbx.c)
|
||||
#< dist-cutoff-end
|
||||
@echo "## in README and source code (see mdbx.c) if you do."
|
||||
@grep -h '#ifndef MDBX_' mdbx.c | grep -v BUILD | uniq | sed 's/#ifndef / /'
|
||||
@grep -h '#ifndef MDBX_' mdbx.c | grep -v BUILD | sort -u | sed 's/#ifndef / /'
|
||||
#> dist-cutoff-begin
|
||||
else
|
||||
@echo "## in README and source code (see src/options.h) if you do."
|
||||
@grep -h '#ifndef MDBX_' src/internals.h src/options.h | grep -v BUILD | uniq | sed 's/#ifndef / /'
|
||||
@grep -h '#ifndef MDBX_' src/*.h | grep -v BUILD | sort -u | sed 's/#ifndef / /'
|
||||
endif
|
||||
#< dist-cutoff-end
|
||||
|
||||
lib libs libmdbx mdbx: libmdbx.a libmdbx.$(SO_SUFFIX)
|
||||
|
||||
tools: $(TOOLS)
|
||||
tools-static: $(addsuffix .static,$(TOOLS)) $(addsuffix .static-lto,$(TOOLS))
|
||||
tools: $(MDBX_TOOLS)
|
||||
tools-static: $(addsuffix .static,$(MDBX_TOOLS)) $(addsuffix .static-lto,$(MDBX_TOOLS))
|
||||
|
||||
strip: all
|
||||
@echo ' STRIP libmdbx.$(SO_SUFFIX) $(TOOLS)'
|
||||
$(TRACE )strip libmdbx.$(SO_SUFFIX) $(TOOLS)
|
||||
@echo ' STRIP libmdbx.$(SO_SUFFIX) $(MDBX_TOOLS)'
|
||||
$(TRACE )strip libmdbx.$(SO_SUFFIX) $(MDBX_TOOLS)
|
||||
|
||||
clean:
|
||||
@echo ' REMOVE ...'
|
||||
$(QUIET)rm -rf $(TOOLS) mdbx_test @* *.[ao] *.[ls]o *.$(SO_SUFFIX) *.dSYM *~ tmp.db/* \
|
||||
$(QUIET)rm -rf $(MDBX_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* buildflags.tag \
|
||||
mdbx_*.static mdbx_*.static-lto
|
||||
@ -284,27 +285,28 @@ ifeq ($(wildcard mdbx.c),mdbx.c)
|
||||
# Amalgamated source code, i.e. distributed after `make dist`
|
||||
MAN_SRCDIR := man1/
|
||||
|
||||
config.h: buildflags.tag mdbx.c $(lastword $(MAKEFILE_LIST))
|
||||
config.h: buildflags.tag mdbx.c $(lastword $(MAKEFILE_LIST)) LICENSE NOTICE
|
||||
@echo ' MAKE $@'
|
||||
$(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_CXX $(call select_by,MDBX_BUILD_CXX,1,0)' \
|
||||
) >$@
|
||||
|
||||
mdbx-dylib.o: config.h mdbx.c mdbx.h $(lastword $(MAKEFILE_LIST))
|
||||
mdbx-dylib.o: config.h mdbx.c mdbx.h $(lastword $(MAKEFILE_LIST)) LICENSE NOTICE
|
||||
@echo ' CC $@'
|
||||
$(QUIET)$(CC) $(CFLAGS) $(MDBX_BUILD_OPTIONS) '-DMDBX_CONFIG_H="config.h"' -DLIBMDBX_EXPORTS=1 -c mdbx.c -o $@
|
||||
|
||||
mdbx-static.o: config.h mdbx.c mdbx.h $(lastword $(MAKEFILE_LIST))
|
||||
mdbx-static.o: config.h mdbx.c mdbx.h $(lastword $(MAKEFILE_LIST)) LICENSE NOTICE
|
||||
@echo ' CC $@'
|
||||
$(QUIET)$(CC) $(CFLAGS) $(MDBX_BUILD_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))
|
||||
mdbx++-dylib.o: config.h mdbx.c++ mdbx.h mdbx.h++ $(lastword $(MAKEFILE_LIST)) LICENSE NOTICE
|
||||
@echo ' CC $@'
|
||||
$(QUIET)$(CXX) $(CXXFLAGS) $(MDBX_BUILD_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))
|
||||
mdbx++-static.o: config.h mdbx.c++ mdbx.h mdbx.h++ $(lastword $(MAKEFILE_LIST)) LICENSE NOTICE
|
||||
@echo ' CC $@'
|
||||
$(QUIET)$(CXX) $(CXXFLAGS) $(MDBX_BUILD_OPTIONS) '-DMDBX_CONFIG_H="config.h"' -ULIBMDBX_EXPORTS -c mdbx.c++ -o $@
|
||||
|
||||
@ -351,9 +353,9 @@ define uname2titer
|
||||
esac
|
||||
endef
|
||||
|
||||
DIST_EXTRA := LICENSE README.md CMakeLists.txt GNUmakefile Makefile ChangeLog.md VERSION.txt config.h.in ntdll.def \
|
||||
DIST_EXTRA := LICENSE NOTICE 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))
|
||||
DIST_SRC := mdbx.h mdbx.h++ mdbx.c mdbx.c++ $(addsuffix .c, $(MDBX_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
|
||||
@ -362,20 +364,20 @@ TEST_ITER := $(shell $(uname2titer))
|
||||
TEST_SRC := test/osal-$(TEST_OSAL).c++ $(filter-out $(wildcard test/osal-*.c++),$(wildcard test/*.c++)) $(call select_by,MDBX_BUILD_CXX,,src/mdbx.c++)
|
||||
TEST_INC := $(wildcard test/*.h++)
|
||||
TEST_OBJ := $(patsubst %.c++,%.o,$(TEST_SRC))
|
||||
TAR ?= $(shell which gnu-tar || echo tar)
|
||||
TAR ?= $(shell which gnu-tar 2>&- || echo tar)
|
||||
ZIP ?= $(shell which zip || echo "echo 'Please install zip'")
|
||||
CLANG_FORMAT ?= $(shell (which clang-format-14 || which clang-format-13 || which clang-format) 2>/dev/null)
|
||||
CLANG_FORMAT ?= $(shell (which clang-format-19 || which clang-format) 2>/dev/null)
|
||||
|
||||
reformat:
|
||||
@echo ' RUNNING clang-format...'
|
||||
$(QUIET)if [ -n "$(CLANG_FORMAT)" ]; then \
|
||||
git ls-files | grep -E '\.(c|c++|h|h++)(\.in)?$$' | xargs -r $(CLANG_FORMAT) -i --style=file; \
|
||||
else \
|
||||
echo "clang-format version 13..14 not found for 'reformat'"; \
|
||||
echo "clang-format version 19 not found for 'reformat'"; \
|
||||
fi
|
||||
|
||||
MAN_SRCDIR := src/man1/
|
||||
ALLOY_DEPS := $(shell git ls-files src/)
|
||||
ALLOY_DEPS := $(shell git ls-files src/ | grep -e /tools -e /man -v)
|
||||
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 '--match=v[0-9]*' 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 `git describe --tags --abbrev=0`..HEAD --count 2>&- || echo 'Please fetch tags and/or use non-obsolete git version')
|
||||
@ -392,11 +394,11 @@ MDBX_SMOKE_EXTRA ?=
|
||||
check: DESTDIR = $(shell pwd)/@check-install
|
||||
check: test dist install
|
||||
|
||||
smoke-assertion: MDBX_BUILD_OPTIONS:=$(strip $(MDBX_BUILD_OPTIONS) -DMDBX_FORCE_ASSERTIONS=1)
|
||||
smoke-assertion: MDBX_BUILD_OPTIONS:=$(strip $(MDBX_BUILD_OPTIONS) -DMDBX_FORCE_ASSERTIONS=1 -UNDEBUG -DMDBX_DEBUG=0)
|
||||
smoke-assertion: smoke
|
||||
test-assertion: MDBX_BUILD_OPTIONS:=$(strip $(MDBX_BUILD_OPTIONS) -DMDBX_FORCE_ASSERTIONS=1)
|
||||
test-assertion: MDBX_BUILD_OPTIONS:=$(strip $(MDBX_BUILD_OPTIONS) -DMDBX_FORCE_ASSERTIONS=1 -UNDEBUG -DMDBX_DEBUG=0)
|
||||
test-assertion: smoke
|
||||
long-test-assertion: MDBX_BUILD_OPTIONS:=$(strip $(MDBX_BUILD_OPTIONS) -DMDBX_FORCE_ASSERTIONS=1)
|
||||
long-test-assertion: MDBX_BUILD_OPTIONS:=$(strip $(MDBX_BUILD_OPTIONS) -DMDBX_FORCE_ASSERTIONS=1 -UNDEBUG -DMDBX_DEBUG=0)
|
||||
long-test-assertion: smoke
|
||||
|
||||
smoke: build-test
|
||||
@ -424,7 +426,7 @@ smoke-fault: build-test
|
||||
|
||||
test: build-test
|
||||
@echo ' RUNNING `test/long_stochastic.sh --loops 2`...'
|
||||
$(QUIET)test/long_stochastic.sh --dont-check-ram-size --loops 2 --db-upto-mb 256 --extra --skip-make --taillog >$(TEST_LOG) || (cat $(TEST_LOG) && false)
|
||||
$(QUIET)test/long_stochastic.sh --dont-check-ram-size --loops 2 --db-upto-mb 256 --skip-make --taillog >$(TEST_LOG) || (cat $(TEST_LOG) && false)
|
||||
|
||||
long-test: test-long
|
||||
test-long: build-test
|
||||
@ -439,7 +441,7 @@ test-valgrind: test-memcheck
|
||||
test-memcheck: CFLAGS_EXTRA=-Ofast -DENABLE_MEMCHECK
|
||||
test-memcheck: build-test
|
||||
@echo ' RUNNING `test/long_stochastic.sh --with-valgrind --loops 2`...'
|
||||
$(QUIET)test/long_stochastic.sh --with-valgrind --extra --loops 2 --db-upto-mb 256 --skip-make >$(TEST_LOG) || (cat $(TEST_LOG) && false)
|
||||
$(QUIET)test/long_stochastic.sh --with-valgrind --loops 2 --db-upto-mb 256 --skip-make >$(TEST_LOG) || (cat $(TEST_LOG) && false)
|
||||
|
||||
memcheck: smoke-memcheck
|
||||
smoke-memcheck: VALGRIND=valgrind --trace-children=yes --log-file=valgrind-%p.log --leak-check=full --track-origins=yes --read-var-info=yes --error-exitcode=42 --suppressions=test/valgrind_suppress.txt
|
||||
@ -480,23 +482,27 @@ build-test: all mdbx_example mdbx_test
|
||||
define test-rule
|
||||
$(patsubst %.c++,%.o,$(1)): $(1) $(TEST_INC) $(HEADERS) $(lastword $(MAKEFILE_LIST))
|
||||
@echo ' CC $$@'
|
||||
$(QUIET)$$(CXX) $$(CXXFLAGS) $$(MDBX_BUILD_OPTIONS) -c $(1) -o $$@
|
||||
$(QUIET)$$(CXX) $$(CXXFLAGS) $$(MDBX_BUILD_OPTIONS) -DMDBX_BUILD_CXX=1 -DMDBX_WITHOUT_MSVC_CRT=0 -c $(1) -o $$@
|
||||
|
||||
endef
|
||||
$(foreach file,$(TEST_SRC),$(eval $(call test-rule,$(file))))
|
||||
|
||||
mdbx_%: src/mdbx_%.c libmdbx.a
|
||||
@echo ' CC+LD $@'
|
||||
$(QUIET)$(CC) $(CFLAGS) $(MDBX_BUILD_OPTIONS) '-DMDBX_CONFIG_H="config.h"' $^ $(EXE_LDFLAGS) $(LIBS) -o $@
|
||||
define tool-rule
|
||||
mdbx_$(1): src/tools/$(1).c libmdbx.a
|
||||
@echo ' CC+LD $$@'
|
||||
$(QUIET)$$(CC) $$(CFLAGS) $$(MDBX_BUILD_OPTIONS) -Isrc '-DMDBX_CONFIG_H="config.h"' $$^ $$(EXE_LDFLAGS) $$(LIBS) -o $$@
|
||||
|
||||
mdbx_%.static: src/mdbx_%.c mdbx-static.o
|
||||
@echo ' CC+LD $@'
|
||||
$(QUIET)$(CC) $(CFLAGS) $(MDBX_BUILD_OPTIONS) '-DMDBX_CONFIG_H="config.h"' $^ $(EXE_LDFLAGS) $(LIBS) -static -Wl,--strip-all -o $@
|
||||
mdbx_$(1).static: src/tools/$(1).c mdbx-static.o
|
||||
@echo ' CC+LD $$@'
|
||||
$(QUIET)$$(CC) $$(CFLAGS) $$(MDBX_BUILD_OPTIONS) -Isrc '-DMDBX_CONFIG_H="config.h"' $$^ $$(EXE_LDFLAGS) $$(LIBS) -static -Wl,--strip-all -o $$@
|
||||
|
||||
mdbx_%.static-lto: src/mdbx_%.c src/config.h src/version.c src/alloy.c $(ALLOY_DEPS)
|
||||
@echo ' CC+LD $@'
|
||||
$(QUIET)$(CC) $(CFLAGS) -Os -flto $(MDBX_BUILD_OPTIONS) '-DLIBMDBX_API=' '-DMDBX_CONFIG_H="config.h"' \
|
||||
$< src/alloy.c $(EXE_LDFLAGS) $(LIBS) -static -Wl,--strip-all -o $@
|
||||
mdbx_$(1).static-lto: src/tools/$(1).c src/config.h src/version.c src/alloy.c $(ALLOY_DEPS)
|
||||
@echo ' CC+LD $$@'
|
||||
$(QUIET)$$(CC) $$(CFLAGS) -Os -flto $$(MDBX_BUILD_OPTIONS) -Isrc '-DLIBMDBX_API=' '-DMDBX_CONFIG_H="config.h"' \
|
||||
$$< src/alloy.c $$(EXE_LDFLAGS) $$(LIBS) -static -Wl,--strip-all -o $$@
|
||||
|
||||
endef
|
||||
$(foreach file,$(TOOLS),$(eval $(call tool-rule,$(file))))
|
||||
|
||||
mdbx_test: $(TEST_OBJ) libmdbx.$(SO_SUFFIX)
|
||||
@echo ' LD $@'
|
||||
@ -506,16 +512,13 @@ $(git_DIR)/HEAD $(git_DIR)/index $(git_DIR)/refs/tags:
|
||||
@echo '*** ' >&2
|
||||
@echo '*** Please don''t use tarballs nor zips which are automatically provided by Github !' >&2
|
||||
@echo '*** These archives do not contain version information and thus are unfit to build libmdbx.' >&2
|
||||
@echo '*** You can vote for ability of disabling auto-creation such unsuitable archives at https://github.community/t/disable-tarball' >&2
|
||||
@echo '*** ' >&2
|
||||
@echo '*** Instead of above, just clone the git repository, either download a tarball or zip with the properly amalgamated source core.' >&2
|
||||
@echo '*** For embedding libmdbx use a git-submodule or the amalgamated source code.' >&2
|
||||
@echo '*** ' >&2
|
||||
@echo '*** Please, avoid using any other techniques.' >&2
|
||||
@echo '*** Instead just follow the https://libmdbx.dqdkfa.ru/usage.html' >&2
|
||||
@echo '*** PLEASE, AVOID USING ANY OTHER TECHNIQUES.' >&2
|
||||
@echo '*** ' >&2
|
||||
@false
|
||||
|
||||
src/version.c: src/version.c.in $(lastword $(MAKEFILE_LIST)) $(git_DIR)/HEAD $(git_DIR)/index $(git_DIR)/refs/tags
|
||||
src/version.c: src/version.c.in $(lastword $(MAKEFILE_LIST)) $(git_DIR)/HEAD $(git_DIR)/index $(git_DIR)/refs/tags LICENSE NOTICE
|
||||
@echo ' MAKE $@'
|
||||
$(QUIET)sed \
|
||||
-e "s|@MDBX_GIT_TIMESTAMP@|$(MDBX_GIT_TIMESTAMP)|" \
|
||||
@ -528,20 +531,21 @@ src/version.c: src/version.c.in $(lastword $(MAKEFILE_LIST)) $(git_DIR)/HEAD $(g
|
||||
-e "s|\$${MDBX_VERSION_REVISION}|$(MDBX_GIT_REVISION)|" \
|
||||
src/version.c.in >$@
|
||||
|
||||
src/config.h: buildflags.tag src/version.c $(lastword $(MAKEFILE_LIST))
|
||||
src/config.h: buildflags.tag src/version.c $(lastword $(MAKEFILE_LIST)) LICENSE NOTICE
|
||||
@echo ' MAKE $@'
|
||||
$(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)' \
|
||||
&& echo '#define MDBX_BUILD_CXX $(call select_by,MDBX_BUILD_CXX,1,0)' \
|
||||
) >$@
|
||||
|
||||
mdbx-dylib.o: src/config.h src/version.c src/alloy.c $(ALLOY_DEPS) $(lastword $(MAKEFILE_LIST))
|
||||
mdbx-dylib.o: src/config.h src/version.c src/alloy.c $(ALLOY_DEPS) $(lastword $(MAKEFILE_LIST)) LICENSE NOTICE
|
||||
@echo ' CC $@'
|
||||
$(QUIET)$(CC) $(CFLAGS) $(MDBX_BUILD_OPTIONS) '-DMDBX_CONFIG_H="config.h"' -DLIBMDBX_EXPORTS=1 -c src/alloy.c -o $@
|
||||
|
||||
mdbx-static.o: src/config.h src/version.c src/alloy.c $(ALLOY_DEPS) $(lastword $(MAKEFILE_LIST))
|
||||
mdbx-static.o: src/config.h src/version.c src/alloy.c $(ALLOY_DEPS) $(lastword $(MAKEFILE_LIST)) LICENSE NOTICE
|
||||
@echo ' CC $@'
|
||||
$(QUIET)$(CC) $(CFLAGS) $(MDBX_BUILD_OPTIONS) '-DMDBX_CONFIG_H="config.h"' -ULIBMDBX_EXPORTS -c src/alloy.c -o $@
|
||||
|
||||
@ -570,9 +574,9 @@ docs/contrib.fame: src/version.c $(lastword $(MAKEFILE_LIST))
|
||||
@echo ' MAKE $@'
|
||||
$(QUIET)echo "" > $@ && git fame --show-email --format=md --silent-progress -w -M -C | grep '^|' >> $@
|
||||
|
||||
docs/overall.md: docs/__overview.md docs/_toc.md docs/__mithril.md docs/__history.md AUTHORS docs/contrib.fame LICENSE $(lastword $(MAKEFILE_LIST))
|
||||
docs/overall.md: docs/__overview.md docs/_toc.md docs/__mithril.md docs/__history.md COPYRIGHT LICENSE NOTICE $(lastword $(MAKEFILE_LIST))
|
||||
@echo ' MAKE $@'
|
||||
$(QUIET)echo -e "\\mainpage Overall\n\\section brief Brief" | cat - $(filter %.md, $^) >$@ && echo -e "\n\n\nLicense\n=======\n" | cat AUTHORS docs/contrib.fame - LICENSE >>$@
|
||||
$(QUIET)echo -e "\\mainpage Overall\n\\section brief Brief" | cat - $(filter %.md, $^) >$@ && echo -e "\n\n\nLicense\n=======\n" | cat - LICENSE >>$@
|
||||
|
||||
docs/intro.md: docs/_preface.md docs/__characteristics.md docs/__improvements.md docs/_restrictions.md docs/__performance.md
|
||||
@echo ' MAKE $@'
|
||||
@ -582,11 +586,11 @@ 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}/' >$@
|
||||
|
||||
doxygen: docs/Doxyfile docs/overall.md docs/intro.md docs/usage.md mdbx.h mdbx.h++ src/options.h ChangeLog.md AUTHORS LICENSE $(lastword $(MAKEFILE_LIST))
|
||||
doxygen: docs/Doxyfile docs/overall.md docs/intro.md docs/usage.md mdbx.h mdbx.h++ src/options.h ChangeLog.md COPYRIGHT LICENSE NOTICE $(lastword $(MAKEFILE_LIST))
|
||||
@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 && \
|
||||
cp mdbx.h++ src/options.h ChangeLog.md docs/ && (cd docs && doxygen Doxyfile $(HUSH)) && cp AUTHORS LICENSE docs/html/
|
||||
cp mdbx.h++ src/options.h ChangeLog.md docs/ && (cd docs && doxygen Doxyfile $(HUSH)) && cp COPYRIGHT LICENSE NOTICE docs/html/
|
||||
|
||||
mdbx++-dylib.o: src/config.h src/mdbx.c++ mdbx.h mdbx.h++ $(lastword $(MAKEFILE_LIST))
|
||||
@echo ' CC $@'
|
||||
@ -617,7 +621,7 @@ release-assets: libmdbx-amalgamated-$(MDBX_GIT_VERSION).zpaq \
|
||||
|
||||
dist-checked.tag: $(addprefix dist/, $(DIST_SRC) $(DIST_EXTRA))
|
||||
@echo -n ' VERIFY amalgamated sources...'
|
||||
$(QUIET)rm -rf $@ dist/@tmp-shared_internals.inc \
|
||||
$(QUIET)rm -rf $@ dist/@tmp-essentials.inc dist/@tmp-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) 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"
|
||||
@ -634,7 +638,6 @@ dist-checked.tag: $(addprefix dist/, $(DIST_SRC) $(DIST_EXTRA))
|
||||
@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) | bzip2 -9 -z >$@
|
||||
|
||||
|
||||
%.zip: dist-checked.tag
|
||||
@echo ' CREATE $@'
|
||||
$(QUIET)rm -rf $@ && (cd dist && $(ZIP) -9 ../$@ $(DIST_SRC) $(DIST_EXTRA)) &>zip.log
|
||||
@ -643,52 +646,81 @@ dist-checked.tag: $(addprefix dist/, $(DIST_SRC) $(DIST_EXTRA))
|
||||
@echo ' CREATE $@'
|
||||
$(QUIET)rm -rf $@ && (cd dist && zpaq a ../$@ $(DIST_SRC) $(DIST_EXTRA) -m59) &>zpaq.log
|
||||
|
||||
dist/mdbx.h: mdbx.h src/version.c $(lastword $(MAKEFILE_LIST))
|
||||
@echo ' COPY $@'
|
||||
$(QUIET)mkdir -p dist && cp $< $@
|
||||
|
||||
dist/mdbx.h++: mdbx.h++ src/version.c $(lastword $(MAKEFILE_LIST))
|
||||
@echo ' COPY $@'
|
||||
$(QUIET)mkdir -p dist && cp $< $@
|
||||
|
||||
dist/@tmp-shared_internals.inc: src/version.c $(ALLOY_DEPS) $(lastword $(MAKEFILE_LIST))
|
||||
dist/@tmp-essentials.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 \
|
||||
&& (grep -v '#include ' src/alloy.c && echo '#define MDBX_BUILD_SOURCERY $(MDBX_BUILD_SOURCERY)' \
|
||||
&& sed \
|
||||
-e '/#pragma once/r dist/@tmp-sed.inc' \
|
||||
-e 's|#include "../mdbx.h"|@INCLUDE "mdbx.h"|' \
|
||||
-e '/#include "base.h"/r src/base.h' \
|
||||
-e '/#include "preface.h"/r src/preface.h' \
|
||||
-e '/#include "osal.h"/r src/osal.h' \
|
||||
-e '/#include "options.h"/r src/options.h' \
|
||||
-e '/#include "atomics-types.h"/r src/atomics-types.h' \
|
||||
-e '/#include "layout-dxb.h"/r src/layout-dxb.h' \
|
||||
-e '/#include "layout-lck.h"/r src/layout-lck.h' \
|
||||
-e '/#include "logging_and_debug.h"/r src/logging_and_debug.h' \
|
||||
-e '/#include "utils.h"/r src/utils.h' \
|
||||
-e '/#include "pnl.h"/r src/pnl.h' \
|
||||
src/essentials.h \
|
||||
| sed \
|
||||
-e '/#pragma once/d' -e '/#include "/d' \
|
||||
-e '/ clang-format o/d' -e '/ \*INDENT-O/d' \
|
||||
src/internals.h >$@ \
|
||||
&& rm -rf dist/@tmp-sed.inc
|
||||
| grep -v '^/// ') >$@
|
||||
|
||||
dist/mdbx.c: dist/@tmp-shared_internals.inc $(lastword $(MAKEFILE_LIST))
|
||||
dist/@tmp-internals.inc: dist/@tmp-essentials.inc src/version.c $(ALLOY_DEPS) $(lastword $(MAKEFILE_LIST))
|
||||
$(QUIET)(cat dist/@tmp-essentials.inc \
|
||||
&& sed \
|
||||
-e '/#include "essentials.h"/d' \
|
||||
-e '/#include "atomics-ops.h"/r src/atomics-ops.h' \
|
||||
-e '/#include "proto.h"/r src/proto.h' \
|
||||
-e '/#include "txl.h"/r src/txl.h' \
|
||||
-e '/#include "unaligned.h"/r src/unaligned.h' \
|
||||
-e '/#include "cogs.h"/r src/cogs.h' \
|
||||
-e '/#include "cursor.h"/r src/cursor.h' \
|
||||
-e '/#include "dbi.h"/r src/dbi.h' \
|
||||
-e '/#include "dpl.h"/r src/dpl.h' \
|
||||
-e '/#include "gc.h"/r src/gc.h' \
|
||||
-e '/#include "lck.h"/r src/lck.h' \
|
||||
-e '/#include "meta.h"/r src/meta.h' \
|
||||
-e '/#include "node.h"/r src/node.h' \
|
||||
-e '/#include "page-iov.h"/r src/page-iov.h' \
|
||||
-e '/#include "page-ops.h"/r src/page-ops.h' \
|
||||
-e '/#include "spill.h"/r src/spill.h' \
|
||||
-e '/#include "sort.h"/r src/sort.h' \
|
||||
-e '/#include "tls.h"/r src/tls.h' \
|
||||
-e '/#include "walk.h"/r src/walk.h' \
|
||||
-e '/#include "windows-import.h"/r src/windows-import.h' \
|
||||
src/internals.h \
|
||||
| sed \
|
||||
-e '/#pragma once/d' -e '/#include "/d' \
|
||||
-e '/ clang-format o/d' -e '/ \*INDENT-O/d' \
|
||||
| grep -v '^/// ') >$@
|
||||
|
||||
dist/mdbx.c: dist/@tmp-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 | sed \
|
||||
$(QUIET)(cat dist/@tmp-internals.inc $(shell git ls-files src/*.c | grep -v alloy) src/version.c | sed \
|
||||
-e '/#include "debug_begin.h"/r src/debug_begin.h' \
|
||||
-e '/#include "debug_end.h"/r src/debug_end.h' \
|
||||
) | sed -e '/#include "/d;/#pragma once/d' -e 's|@INCLUDE|#include|' \
|
||||
-e '/ clang-format o/d;/ \*INDENT-O/d' >$@
|
||||
|
||||
dist/mdbx.c++: dist/@tmp-shared_internals.inc src/mdbx.c++ $(lastword $(MAKEFILE_LIST))
|
||||
dist/mdbx.c++: dist/@tmp-essentials.inc src/mdbx.c++ $(lastword $(MAKEFILE_LIST))
|
||||
@echo ' MAKE $@'
|
||||
$(QUIET)mkdir -p dist && (cat dist/@tmp-shared_internals.inc && cat src/mdbx.c++) \
|
||||
| sed -e '/#include "/d;/#pragma once/d' -e 's|@INCLUDE|#include|;s|"mdbx.h"|"mdbx.h++"|' \
|
||||
$(QUIET)cat dist/@tmp-essentials.inc src/mdbx.c++ | sed \
|
||||
-e '/#define xMDBX_ALLOY/d' \
|
||||
-e '/#include "/d;/#pragma once/d' \
|
||||
-e 's|@INCLUDE|#include|;s|"mdbx.h"|"mdbx.h++"|' \
|
||||
-e '/ clang-format o/d;/ \*INDENT-O/d' >$@
|
||||
|
||||
define dist-tool-rule
|
||||
dist/$(1).c: src/$(1).c src/wingetopt.h src/wingetopt.c \
|
||||
dist/@tmp-shared_internals.inc $(lastword $(MAKEFILE_LIST))
|
||||
dist/mdbx_$(1).c: src/tools/$(1).c src/tools/wingetopt.h src/tools/wingetopt.c \
|
||||
dist/@tmp-internals.inc $(lastword $(MAKEFILE_LIST))
|
||||
@echo ' MAKE $$@'
|
||||
$(QUIET)mkdir -p dist && sed \
|
||||
-e '/#include "internals.h"/r dist/@tmp-shared_internals.inc' \
|
||||
-e '/#include "wingetopt.h"/r src/wingetopt.c' \
|
||||
-e '/#include "essentials.h"/r dist/@tmp-essentials.inc' \
|
||||
-e '/#include "wingetopt.h"/r src/tools/wingetopt.c' \
|
||||
-e '/ clang-format o/d' -e '/ \*INDENT-O/d' \
|
||||
src/$(1).c \
|
||||
src/tools/$(1).c \
|
||||
| sed -e '/#include "/d;/#pragma once/d;/#define xMDBX_ALLOY/d' -e 's|@INCLUDE|#include|' \
|
||||
-e '/ clang-format o/d;/ \*INDENT-O/d' >$$@
|
||||
|
||||
@ -696,12 +728,12 @@ endef
|
||||
$(foreach file,$(TOOLS),$(eval $(call dist-tool-rule,$(file))))
|
||||
|
||||
define dist-extra-rule
|
||||
dist/$(1): $(1)
|
||||
dist/$(1): $(1) src/version.c $(lastword $(MAKEFILE_LIST))
|
||||
@echo ' REFINE $$@'
|
||||
$(QUIET)mkdir -p $$(dir $$@) && sed -e '/^#> dist-cutoff-begin/,/^#< dist-cutoff-end/d' $$< >$$@
|
||||
|
||||
endef
|
||||
$(foreach file,$(filter-out man1/% VERSION.txt %.in ntdll.def,$(DIST_EXTRA)),$(eval $(call dist-extra-rule,$(file))))
|
||||
$(foreach file,mdbx.h mdbx.h++ $(filter-out man1/% VERSION.txt %.in ntdll.def,$(DIST_EXTRA)),$(eval $(call dist-extra-rule,$(file))))
|
||||
|
||||
dist/VERSION.txt: src/version.c
|
||||
@echo ' MAKE $@'
|
||||
@ -763,10 +795,10 @@ cross-qemu:
|
||||
|
||||
#< dist-cutoff-end
|
||||
|
||||
install: $(LIBRARIES) $(TOOLS) $(HEADERS)
|
||||
install: $(LIBRARIES) $(MDBX_TOOLS) $(HEADERS)
|
||||
@echo ' INSTALLING...'
|
||||
$(QUIET)mkdir -p $(DESTDIR)$(prefix)/bin$(suffix) && \
|
||||
$(INSTALL) -p $(EXE_INSTALL_FLAGS) $(TOOLS) $(DESTDIR)$(prefix)/bin$(suffix)/ && \
|
||||
$(INSTALL) -p $(EXE_INSTALL_FLAGS) $(MDBX_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)/ && \
|
||||
@ -784,7 +816,7 @@ install-no-strip: install
|
||||
|
||||
uninstall:
|
||||
@echo ' UNINSTALLING/REMOVE...'
|
||||
$(QUIET)rm -f $(addprefix $(DESTDIR)$(prefix)/bin$(suffix)/,$(TOOLS)) \
|
||||
$(QUIET)rm -f $(addprefix $(DESTDIR)$(prefix)/bin$(suffix)/,$(MDBX_TOOLS)) \
|
||||
$(addprefix $(DESTDIR)$(prefix)/lib$(suffix)/,$(LIBRARIES)) \
|
||||
$(addprefix $(DESTDIR)$(prefix)/include/,$(HEADERS)) \
|
||||
$(addprefix $(DESTDIR)$(mandir)/man1/,$(MANPAGES))
|
||||
|
206
LICENSE
206
LICENSE
@ -1,47 +1,177 @@
|
||||
The OpenLDAP Public License
|
||||
Version 2.8, 17 August 2003
|
||||
|
||||
Redistribution and use of this software and associated documentation
|
||||
("Software"), with or without modification, are permitted provided
|
||||
that the following conditions are met:
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
1. Redistributions in source form must retain copyright statements
|
||||
and notices,
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
2. Redistributions in binary form must reproduce applicable copyright
|
||||
statements and notices, this list of conditions, and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution, and
|
||||
1. Definitions.
|
||||
|
||||
3. Redistributions must contain a verbatim copy of this document.
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
The OpenLDAP Foundation may revise this license from time to time.
|
||||
Each revision is distinguished by a version number. You may use
|
||||
this Software under terms of this license revision or under the
|
||||
terms of any subsequent revision of the license.
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS
|
||||
CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S)
|
||||
OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
The names of the authors and copyright holders must not be used in
|
||||
advertising or otherwise to promote the sale, use or other dealing
|
||||
in this Software without specific, written prior permission. Title
|
||||
to copyright in this Software shall at all times remain with copyright
|
||||
holders.
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
OpenLDAP is a registered trademark of the OpenLDAP Foundation.
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
Copyright 1999-2003 The OpenLDAP Foundation, Redwood City,
|
||||
California, USA. All Rights Reserved. Permission to copy and
|
||||
distribute verbatim copies of this document is granted.
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
23
NOTICE
Normal file
23
NOTICE
Normal file
@ -0,0 +1,23 @@
|
||||
libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable,
|
||||
transactional key-value storage engine with open-source code. MDBX has a
|
||||
specific set of properties and capabilities, focused on creating unique
|
||||
lightweight solutions.
|
||||
|
||||
Please visit https://libmdbx.dqdkfa.ru for more information, changelog,
|
||||
documentation, C++ API description and links to the original git repo
|
||||
with the source code. Questions, feedback and suggestions are welcome
|
||||
to the Telegram' group https://t.me/libmdbx.
|
||||
|
||||
Since 2017 _libmdbx_ development is funded by [Positive Technologies](https://www.ptsecurity.com)
|
||||
and used inside company products. Всё будет хорошо!
|
||||
|
||||
Copyright 2015-2024 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru>
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
For notes about the license change, credits and acknowledgments,
|
||||
please refer to the COPYRIGHT file within original libmdbx source code
|
||||
repository https://gitflic.ru/project/erthink/libmdbx
|
||||
|
||||
On 2022-04-15 the Github administration, without any warning nor
|
||||
explanation, deleted _libmdbx_ along with a lot of other projects,
|
||||
simultaneously blocking access for many developers.
|
||||
For the same reason ~~Github~~ is blacklisted forever.
|
75
README.md
75
README.md
@ -1,36 +1,5 @@
|
||||
<!-- Required extensions: pymdownx.betterem, pymdownx.tilde, pymdownx.emoji, pymdownx.tasklist, pymdownx.superfences -->
|
||||
|
||||
### Время учить Русский
|
||||
|
||||
Начиная с 2021 года наблюдается устойчивые тенденции к распространению
|
||||
недостоверной информации о _libmdbx_ в странах ~~НАТО~~,
|
||||
политизированной критика, а также отказу от использования библиотеки в
|
||||
пользу LMDB, несмотря на явные проблемы с одной стороны и преимущества с
|
||||
другой. Поэтому начиная с 17 марта 2024 года прекращается
|
||||
документирование и сопровождение проекта на английском языке. Новый
|
||||
функционал будет документироваться только на русском языке, однако,
|
||||
целенаправленного переписывания/перевода документации пока не
|
||||
планируется.
|
||||
|
||||
### The origin has been migrated to [GitFlic](https://gitflic.ru/project/erthink/libmdbx)
|
||||
Since on 2022-04-15 the Github administration, without any warning
|
||||
nor explanation, deleted _libmdbx_ along with a lot of other projects,
|
||||
simultaneously blocking access for many developers.
|
||||
For the same reason ~~Github~~ is blacklisted forever.
|
||||
|
||||
GitFlic's developers plan to support other languages,
|
||||
including English 和 中文, in the near future.
|
||||
|
||||
### Основной репозиторий перемещен на [GitFlic](https://gitflic.ru/project/erthink/libmdbx)
|
||||
Так как 15 апреля 2022 администрация Github без предупреждения и
|
||||
объяснения причин удалила _libmdbx_ вместе с массой других проектов,
|
||||
одновременно заблокировав доступ многим разработчикам.
|
||||
По этой же причине ~~Github~~ навсегда занесен в черный список.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
*The Future will (be) [Positive](https://www.ptsecurity.com). Всё будет хорошо.*
|
||||
|
||||
> Please refer to the online [documentation](https://libmdbx.dqdkfa.ru)
|
||||
> with [`C` API description](https://libmdbx.dqdkfa.ru/group__c__api.html)
|
||||
> and pay attention to the [`C++` API](https://gitflic.ru/project/erthink/libmdbx/blob?file=mdbx.h%2B%2B#line-num-1).
|
||||
@ -40,6 +9,8 @@ including English 和 中文, in the near future.
|
||||
> For NEWS take a look to the [ChangeLog](https://gitflic.ru/project/erthink/libmdbx/blob?file=ChangeLog.md)
|
||||
> or the [TODO](https://gitflic.ru/project/erthink/libmdbx/blob?file=TODO.md).
|
||||
|
||||
*The Future will (be) [Positive](https://www.ptsecurity.com). Всё будет хорошо.*
|
||||
|
||||
|
||||
libmdbx
|
||||
========
|
||||
@ -48,7 +19,7 @@ libmdbx
|
||||
|
||||
_libmdbx_ is an extremely fast, compact, powerful, embedded, transactional
|
||||
[key-value database](https://en.wikipedia.org/wiki/Key-value_database),
|
||||
with [permissive license](https://gitflic.ru/project/erthink/libmdbx/blob?file=LICENSE).
|
||||
with [Apache 2.0 license](https://gitflic.ru/project/erthink/libmdbx/blob?file=LICENSE).
|
||||
_libmdbx_ has a specific set of properties and capabilities,
|
||||
focused on creating unique lightweight solutions.
|
||||
|
||||
@ -144,15 +115,14 @@ $ objdump -f -h -j .text libmdbx.so
|
||||
libmdbx.so: формат файла elf64-e2k
|
||||
архитектура: elbrus-v6:64, флаги 0x00000150:
|
||||
HAS_SYMS, DYNAMIC, D_PAGED
|
||||
начальный адрес 0x0000000000021680
|
||||
начальный адрес 0x00000000??????00
|
||||
|
||||
Разделы:
|
||||
Idx Name Разм VMA LMA Фа смещ. Выр.
|
||||
10 .text 000ddd28 0000000000021680 0000000000021680 00021680 2**3
|
||||
CONTENTS, ALLOC, LOAD, READONLY, CODE
|
||||
Idx Name Разм VMA LMA Фа смещ. Выр. Флаги
|
||||
10 .text 000e7460 0000000000025c00 0000000000025c00 00025c00 2**10 CONTENTS, ALLOC, LOAD, READONLY, CODE
|
||||
|
||||
$ cc --version
|
||||
lcc:1.26.12:Jun-05-2022:e2k-v6-linux
|
||||
lcc:1.27.14:Jan-31-2024:e2k-v6-linux
|
||||
gcc (GCC) 9.3.0 compatible
|
||||
```
|
||||
|
||||
@ -276,7 +246,7 @@ out-of-the-box, not silently and catastrophically break down. The list
|
||||
below is pruned down to the improvements most notable and obvious from
|
||||
the user's point of view.
|
||||
|
||||
## Added Features
|
||||
## Some Added Features
|
||||
|
||||
1. Keys could be more than 2 times longer than _LMDB_.
|
||||
> For DB with default page size _libmdbx_ support keys up to 2022 bytes
|
||||
@ -319,8 +289,7 @@ be found between a `KEY1` and a `KEY2`. This is a prerequisite for build
|
||||
and/or optimize query execution plans.
|
||||
> _libmdbx_ performs a rough estimate based on common B-tree pages of the paths from root to corresponding keys.
|
||||
|
||||
8. `mdbx_chk` utility for database integrity check.
|
||||
Since version 0.9.1, the utility supports checking the database using any of the three meta pages and the ability to switch to it.
|
||||
8. Database integrity check API both with standalone `mdbx_chk` utility.
|
||||
|
||||
9. Support for opening databases in the exclusive mode, including on a network share.
|
||||
|
||||
@ -410,12 +379,26 @@ The origin for now is at [GitFlic](https://gitflic.ru/project/erthink/libmdbx)
|
||||
with backup at [ABF by ROSA Лаб](https://abf.rosalinux.ru/erthink/libmdbx).
|
||||
For the same reason ~~Github~~ is blacklisted forever.
|
||||
|
||||
Начиная с 2021 года наблюдаются устойчивые тенденции к распространению
|
||||
недостоверной информации о libmdbx в странах НАТО, политизированной
|
||||
критики, а также отказу от использования библиотеки в пользу LMDB,
|
||||
несмотря на явные проблемы с одной стороны и преимущества с другой.
|
||||
Поэтому, начиная с 17 марта 2024 года, прекращается документирование и
|
||||
сопровождение проекта на английском языке. Новая функциональность будет
|
||||
документироваться только на русском языке, однако, целенаправленного
|
||||
переписывания/перевода документации пока не планируется.
|
||||
|
||||
Since May 2024 and version v0.13 _libmdbx_ was re-licensed under Apache-2.0 license.
|
||||
Please refer to the `COPYRIGHT` file for license change explanations.
|
||||
|
||||
|
||||
## Acknowledgments
|
||||
Howard Chu <hyc@openldap.org> is the author of LMDB, from which
|
||||
originated the _libmdbx_ in 2015.
|
||||
Howard Chu <hyc@openldap.org> and Hallvard Furuseth
|
||||
<hallvard@openldap.org> are the authors of _LMDB_, from which _libmdbx_
|
||||
was forked in 2015.
|
||||
|
||||
Martin Hedenfalk <martin@bzero.se> is the author of `btree.c` code, which
|
||||
was used to begin development of LMDB.
|
||||
was used to begin development of _LMDB_.
|
||||
|
||||
<!-- section-end -->
|
||||
|
||||
@ -523,8 +506,10 @@ There are no special traits nor quirks if you use libmdbx ONLY inside the single
|
||||
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 DB.
|
||||
Basically this means must be only a single physical copy of each memory mapped DB' page in the system memory.
|
||||
1. Coherence of memory mapping content and unified page cache inside OS
|
||||
kernel for host and all container(s) operated with a 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](https://en.wikipedia.org/wiki/Process_identifier) values and/or a common space for ones:
|
||||
- for POSIX systems: PID uniqueness for all processes operated with a DB.
|
||||
|
@ -1,17 +1,5 @@
|
||||
## Copyright (c) 2012-2024 Leonid Yuriev <leo@yuriev.ru>.
|
||||
##
|
||||
## Licensed under the Apache License, Version 2.0 (the "License");
|
||||
## you may not use this file except in compliance with the License.
|
||||
## You may obtain a copy of the License at
|
||||
##
|
||||
## http://www.apache.org/licenses/LICENSE-2.0
|
||||
##
|
||||
## Unless required by applicable law or agreed to in writing, software
|
||||
## distributed under the License is distributed on an "AS IS" BASIS,
|
||||
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
## See the License for the specific language governing permissions and
|
||||
## limitations under the License.
|
||||
##
|
||||
## Copyright (c) 2010-2024 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru>
|
||||
## SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS 3.8.2)
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
|
@ -1,17 +1,5 @@
|
||||
## Copyright (c) 2012-2024 Leonid Yuriev <leo@yuriev.ru>.
|
||||
##
|
||||
## Licensed under the Apache License, Version 2.0 (the "License");
|
||||
## you may not use this file except in compliance with the License.
|
||||
## You may obtain a copy of the License at
|
||||
##
|
||||
## http://www.apache.org/licenses/LICENSE-2.0
|
||||
##
|
||||
## Unless required by applicable law or agreed to in writing, software
|
||||
## distributed under the License is distributed on an "AS IS" BASIS,
|
||||
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
## See the License for the specific language governing permissions and
|
||||
## limitations under the License.
|
||||
##
|
||||
## Copyright (c) 2012-2024 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru>
|
||||
## SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS 3.8.2)
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
|
@ -1,17 +1,5 @@
|
||||
## Copyright (c) 2012-2024 Leonid Yuriev <leo@yuriev.ru>.
|
||||
##
|
||||
## Licensed under the Apache License, Version 2.0 (the "License");
|
||||
## you may not use this file except in compliance with the License.
|
||||
## You may obtain a copy of the License at
|
||||
##
|
||||
## http://www.apache.org/licenses/LICENSE-2.0
|
||||
##
|
||||
## Unless required by applicable law or agreed to in writing, software
|
||||
## distributed under the License is distributed on an "AS IS" BASIS,
|
||||
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
## See the License for the specific language governing permissions and
|
||||
## limitations under the License.
|
||||
##
|
||||
## Copyright (c) 2012-2024 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru>
|
||||
## SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS 3.8.2)
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
|
313
mdbx.h
313
mdbx.h
@ -1,11 +1,10 @@
|
||||
/**
|
||||
|
||||
_libmdbx_ is an extremely fast, compact, powerful, embedded,
|
||||
_libmdbx_ (aka MDBX) is an extremely fast, compact, powerful, embeddable,
|
||||
transactional [key-value
|
||||
store](https://en.wikipedia.org/wiki/Key-value_database) database, with
|
||||
[permissive license](./LICENSE). _MDBX_ has a specific set of properties and
|
||||
capabilities, focused on creating unique lightweight solutions with
|
||||
extraordinary performance.
|
||||
store](https://en.wikipedia.org/wiki/Key-value_database), with [Apache 2.0
|
||||
license](./LICENSE). _MDBX_ has a specific set of properties and capabilities,
|
||||
focused on creating unique lightweight solutions with extraordinary performance.
|
||||
|
||||
_libmdbx_ is superior to [LMDB](https://bit.ly/26ts7tL) in terms of features
|
||||
and reliability, not inferior in performance. In comparison to LMDB, _libmdbx_
|
||||
@ -14,60 +13,24 @@ break down. _libmdbx_ supports Linux, Windows, MacOS, OSX, iOS, Android,
|
||||
FreeBSD, DragonFly, Solaris, OpenSolaris, OpenIndiana, NetBSD, OpenBSD and other
|
||||
systems compliant with POSIX.1-2008.
|
||||
|
||||
The origin has been migrated to
|
||||
[GitFlic](https://gitflic.ru/project/erthink/libmdbx) since on 2022-04-15
|
||||
the Github administration, without any warning nor explanation, deleted libmdbx
|
||||
along with a lot of other projects, simultaneously blocking access for many
|
||||
developers. For the same reason ~~Github~~ is blacklisted forever.
|
||||
Please visit https://libmdbx.dqdkfa.ru for more information, documentation,
|
||||
C++ API description and links to the origin git repo with the source code.
|
||||
Questions, feedback and suggestions are welcome to the Telegram' group
|
||||
https://t.me/libmdbx.
|
||||
|
||||
_The Future will (be) [Positive](https://www.ptsecurity.com). Всё будет хорошо._
|
||||
|
||||
\note The origin has been migrated to
|
||||
[GitFlic](https://gitflic.ru/project/erthink/libmdbx) since on 2022-04-15 the
|
||||
Github administration, without any warning nor explanation, deleted libmdbx
|
||||
along with a lot of other projects, simultaneously blocking access for many
|
||||
developers. For the same reason ~~Github~~ is blacklisted forever.
|
||||
|
||||
\section copyright LICENSE & COPYRIGHT
|
||||
|
||||
\authors Copyright (c) 2015-2024, Leonid Yuriev <leo@yuriev.ru>
|
||||
and other _libmdbx_ authors: please see [AUTHORS](./AUTHORS) file.
|
||||
|
||||
\copyright Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted only as authorized by the OpenLDAP Public License.
|
||||
|
||||
A copy of this license is available in the file LICENSE in the
|
||||
top-level directory of the distribution or, alternatively, at
|
||||
<http://www.OpenLDAP.org/license.html>.
|
||||
|
||||
---
|
||||
|
||||
This code is derived from "LMDB engine" written by
|
||||
Howard Chu (Symas Corporation), which itself derived from btree.c
|
||||
written by Martin Hedenfalk.
|
||||
|
||||
---
|
||||
|
||||
Portions Copyright 2011-2015 Howard Chu, Symas Corp. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted only as authorized by the OpenLDAP
|
||||
Public License.
|
||||
|
||||
A copy of this license is available in the file LICENSE in the
|
||||
top-level directory of the distribution or, alternatively, at
|
||||
<http://www.OpenLDAP.org/license.html>.
|
||||
|
||||
---
|
||||
|
||||
Portions Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se>
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
\copyright SPDX-License-Identifier: Apache-2.0
|
||||
\note Please refer to the COPYRIGHT file for explanations license change,
|
||||
credits and acknowledgments.
|
||||
\author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
@ -98,7 +61,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
/* clang-format off */
|
||||
/**
|
||||
\file mdbx.h
|
||||
\brief The libmdbx C API header file
|
||||
\brief The libmdbx C API header file.
|
||||
|
||||
\defgroup c_api C API
|
||||
@{
|
||||
@ -359,6 +322,14 @@ typedef mode_t mdbx_mode_t;
|
||||
#endif
|
||||
#endif /* MDBX_DEPRECATED */
|
||||
|
||||
#ifndef MDBX_DEPRECATED_ENUM
|
||||
#if !defined(DOXYGEN) && (!defined(_MSC_VER) || _MSC_VER >= 1930)
|
||||
#define MDBX_DEPRECATED_ENUM MDBX_DEPRECATED
|
||||
#else
|
||||
#define MDBX_DEPRECATED_ENUM /* avoid madness MSVC */
|
||||
#endif
|
||||
#endif /* MDBX_DEPRECATED_ENUM */
|
||||
|
||||
#ifndef __dll_export
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) || \
|
||||
defined(__MINGW__) || defined(__MINGW32__) || defined(__MINGW64__)
|
||||
@ -393,7 +364,8 @@ typedef mode_t mdbx_mode_t;
|
||||
|
||||
/** \brief Auxiliary macro for robustly define the both inline version of API
|
||||
* function and non-inline fallback dll-exported version for applications linked
|
||||
* with old version of libmdbx, with a strictly ODR-common implementation. */
|
||||
* with old version of libmdbx, with a strictly ODR-common implementation. Thus,
|
||||
* we emulate __extern_inline for all compilers, including non-GNU ones. */
|
||||
#if defined(LIBMDBX_INTERNALS) && !defined(LIBMDBX_NO_EXPORTS_LEGACY_API)
|
||||
#define LIBMDBX_INLINE_API(TYPE, NAME, ARGS) \
|
||||
/* proto of exported which uses common impl */ LIBMDBX_API TYPE NAME ARGS; \
|
||||
@ -888,7 +860,7 @@ enum MDBX_constants {
|
||||
/** Log level
|
||||
* \note Levels detailed than (great than) \ref MDBX_LOG_NOTICE
|
||||
* requires build libmdbx with \ref MDBX_DEBUG option. */
|
||||
enum MDBX_log_level_t {
|
||||
typedef enum MDBX_log_level {
|
||||
/** Critical conditions, i.e. assertion failures.
|
||||
* \note libmdbx always produces such messages regardless
|
||||
* of \ref MDBX_DEBUG build option. */
|
||||
@ -938,17 +910,14 @@ enum MDBX_log_level_t {
|
||||
|
||||
/** for \ref mdbx_setup_debug() only: Don't change current settings */
|
||||
MDBX_LOG_DONTCHANGE = -1
|
||||
};
|
||||
#ifndef __cplusplus
|
||||
typedef enum MDBX_log_level_t MDBX_log_level_t;
|
||||
#endif
|
||||
} MDBX_log_level_t;
|
||||
|
||||
/** \brief Runtime debug flags
|
||||
*
|
||||
* \details `MDBX_DBG_DUMP` and `MDBX_DBG_LEGACY_MULTIOPEN` always have an
|
||||
* effect, but `MDBX_DBG_ASSERT`, `MDBX_DBG_AUDIT` and `MDBX_DBG_JITTER` only if
|
||||
* libmdbx built with \ref MDBX_DEBUG. */
|
||||
enum MDBX_debug_flags_t {
|
||||
typedef enum MDBX_debug_flags {
|
||||
MDBX_DBG_NONE = 0,
|
||||
|
||||
/** Enable assertion checks.
|
||||
@ -986,12 +955,8 @@ enum MDBX_debug_flags_t {
|
||||
|
||||
/** for mdbx_setup_debug() only: Don't change current settings */
|
||||
MDBX_DBG_DONTCHANGE = -1
|
||||
};
|
||||
#ifndef __cplusplus
|
||||
typedef enum MDBX_debug_flags_t MDBX_debug_flags_t;
|
||||
#else
|
||||
DEFINE_ENUM_FLAG_OPERATORS(MDBX_debug_flags_t)
|
||||
#endif
|
||||
} MDBX_debug_flags_t;
|
||||
DEFINE_ENUM_FLAG_OPERATORS(MDBX_debug_flags)
|
||||
|
||||
/** \brief A debug-logger callback function,
|
||||
* called before printing the message and aborting.
|
||||
@ -1086,7 +1051,7 @@ MDBX_NORETURN LIBMDBX_API void mdbx_assert_fail(const MDBX_env *env,
|
||||
* \ingroup c_opening
|
||||
* \anchor env_flags
|
||||
* \see mdbx_env_open() \see mdbx_env_set_flags() */
|
||||
enum MDBX_env_flags_t {
|
||||
typedef enum MDBX_env_flags {
|
||||
MDBX_ENV_DEFAULTS = 0,
|
||||
|
||||
/** Extra validation of DB structure and pages content.
|
||||
@ -1210,7 +1175,7 @@ enum MDBX_env_flags_t {
|
||||
|
||||
/** Отвязывает транзакции от потоков/threads насколько это возможно.
|
||||
*
|
||||
* Эта опция предназначена для приложений, которые мультиплексируют множество
|
||||
* Опция предназначена для приложений, которые мультиплексируют множество
|
||||
* пользовательских легковесных потоков выполнения по отдельным потокам
|
||||
* операционной системы, например как это происходит в средах выполнения
|
||||
* GoLang и Rust. Таким приложениям также рекомендуется сериализовать
|
||||
@ -1278,10 +1243,9 @@ enum MDBX_env_flags_t {
|
||||
* Этот флаг вступает в силу при открытии среды и не может быть изменен после.
|
||||
*/
|
||||
MDBX_NOSTICKYTHREADS = UINT32_C(0x200000),
|
||||
#ifndef _MSC_VER /* avoid madness MSVC */
|
||||
|
||||
/** \deprecated Please use \ref MDBX_NOSTICKYTHREADS instead. */
|
||||
MDBX_NOTLS MDBX_DEPRECATED = MDBX_NOSTICKYTHREADS,
|
||||
#endif /* avoid madness MSVC */
|
||||
MDBX_NOTLS MDBX_DEPRECATED_ENUM = MDBX_NOSTICKYTHREADS,
|
||||
|
||||
/** Don't do readahead.
|
||||
*
|
||||
@ -1327,7 +1291,6 @@ enum MDBX_env_flags_t {
|
||||
* This flag may be changed at any time using `mdbx_env_set_flags()`. */
|
||||
MDBX_NOMEMINIT = UINT32_C(0x1000000),
|
||||
|
||||
#ifndef _MSC_VER /* avoid madness MSVC */
|
||||
/** Aims to coalesce a Garbage Collection items.
|
||||
* \deprecated Always enabled since v0.12 and deprecated since v0.13.
|
||||
*
|
||||
@ -1339,8 +1302,7 @@ enum MDBX_env_flags_t {
|
||||
* Unallocated space and reducing the database file.
|
||||
*
|
||||
* This flag may be changed at any time using mdbx_env_set_flags(). */
|
||||
MDBX_COALESCE MDBX_DEPRECATED = UINT32_C(0x2000000),
|
||||
#endif /* avoid madness MSVC */
|
||||
MDBX_COALESCE MDBX_DEPRECATED_ENUM = UINT32_C(0x2000000),
|
||||
|
||||
/** LIFO policy for recycling a Garbage Collection items.
|
||||
*
|
||||
@ -1543,19 +1505,14 @@ enum MDBX_env_flags_t {
|
||||
MDBX_UTTERLY_NOSYNC = MDBX_SAFE_NOSYNC | UINT32_C(0x100000),
|
||||
|
||||
/** end of sync_modes @} */
|
||||
};
|
||||
#ifndef __cplusplus
|
||||
/** \ingroup c_opening */
|
||||
typedef enum MDBX_env_flags_t MDBX_env_flags_t;
|
||||
#else
|
||||
DEFINE_ENUM_FLAG_OPERATORS(MDBX_env_flags_t)
|
||||
#endif
|
||||
} MDBX_env_flags_t;
|
||||
DEFINE_ENUM_FLAG_OPERATORS(MDBX_env_flags)
|
||||
|
||||
/** Transaction flags
|
||||
* \ingroup c_transactions
|
||||
* \anchor txn_flags
|
||||
* \see mdbx_txn_begin() \see mdbx_txn_flags() */
|
||||
enum MDBX_txn_flags_t {
|
||||
typedef enum MDBX_txn_flags {
|
||||
/** Start read-write transaction.
|
||||
*
|
||||
* Only one write transaction may be active at a time. Writes are fully
|
||||
@ -1627,18 +1584,14 @@ enum MDBX_txn_flags_t {
|
||||
* \note Transaction state flag. Returned from \ref mdbx_txn_flags()
|
||||
* but can't be used with \ref mdbx_txn_begin(). */
|
||||
MDBX_TXN_BLOCKED = MDBX_TXN_FINISHED | MDBX_TXN_ERROR | MDBX_TXN_HAS_CHILD
|
||||
};
|
||||
#ifndef __cplusplus
|
||||
typedef enum MDBX_txn_flags_t MDBX_txn_flags_t;
|
||||
#else
|
||||
DEFINE_ENUM_FLAG_OPERATORS(MDBX_txn_flags_t)
|
||||
#endif
|
||||
} MDBX_txn_flags_t;
|
||||
DEFINE_ENUM_FLAG_OPERATORS(MDBX_txn_flags)
|
||||
|
||||
/** \brief Database flags
|
||||
* \ingroup c_dbi
|
||||
* \anchor db_flags
|
||||
* \see mdbx_dbi_open() */
|
||||
enum MDBX_db_flags_t {
|
||||
typedef enum MDBX_db_flags {
|
||||
/** Variable length unique keys with usual byte-by-byte string comparison. */
|
||||
MDBX_DB_DEFAULTS = 0,
|
||||
|
||||
@ -1681,19 +1634,14 @@ enum MDBX_db_flags_t {
|
||||
* sub-database will be opened with flags which it was created, and then an
|
||||
* application could determine the actual flags by \ref mdbx_dbi_flags(). */
|
||||
MDBX_DB_ACCEDE = MDBX_ACCEDE
|
||||
};
|
||||
#ifndef __cplusplus
|
||||
/** \ingroup c_dbi */
|
||||
typedef enum MDBX_db_flags_t MDBX_db_flags_t;
|
||||
#else
|
||||
} MDBX_db_flags_t;
|
||||
DEFINE_ENUM_FLAG_OPERATORS(MDBX_db_flags_t)
|
||||
#endif
|
||||
|
||||
/** \brief Data changing flags
|
||||
* \ingroup c_crud
|
||||
* \see \ref c_crud_hints "Quick reference for Insert/Update/Delete operations"
|
||||
* \see mdbx_put() \see mdbx_cursor_put() \see mdbx_replace() */
|
||||
enum MDBX_put_flags_t {
|
||||
typedef enum MDBX_put_flags {
|
||||
/** Upsertion by default (without any other flags) */
|
||||
MDBX_UPSERT = 0,
|
||||
|
||||
@ -1731,18 +1679,13 @@ enum MDBX_put_flags_t {
|
||||
/** Only for \ref MDBX_DUPFIXED.
|
||||
* Store multiple data items in one call. */
|
||||
MDBX_MULTIPLE = UINT32_C(0x80000)
|
||||
};
|
||||
#ifndef __cplusplus
|
||||
/** \ingroup c_crud */
|
||||
typedef enum MDBX_put_flags_t MDBX_put_flags_t;
|
||||
#else
|
||||
DEFINE_ENUM_FLAG_OPERATORS(MDBX_put_flags_t)
|
||||
#endif
|
||||
} MDBX_put_flags_t;
|
||||
DEFINE_ENUM_FLAG_OPERATORS(MDBX_put_flags)
|
||||
|
||||
/** \brief Environment copy flags
|
||||
* \ingroup c_extra
|
||||
* \see mdbx_env_copy() \see mdbx_env_copy2fd() */
|
||||
enum MDBX_copy_flags_t {
|
||||
typedef enum MDBX_copy_flags {
|
||||
MDBX_CP_DEFAULTS = 0,
|
||||
|
||||
/** Copy with compactification: Omit free space from copy and renumber all
|
||||
@ -1751,19 +1694,14 @@ enum MDBX_copy_flags_t {
|
||||
|
||||
/** Force to make resizable copy, i.e. dynamic size instead of fixed */
|
||||
MDBX_CP_FORCE_DYNAMIC_SIZE = 2u
|
||||
};
|
||||
#ifndef __cplusplus
|
||||
/** \ingroup c_extra */
|
||||
typedef enum MDBX_copy_flags_t MDBX_copy_flags_t;
|
||||
#else
|
||||
DEFINE_ENUM_FLAG_OPERATORS(MDBX_copy_flags_t)
|
||||
#endif
|
||||
} MDBX_copy_flags_t;
|
||||
DEFINE_ENUM_FLAG_OPERATORS(MDBX_copy_flags)
|
||||
|
||||
/** \brief Cursor operations
|
||||
* \ingroup c_cursors
|
||||
* This is the set of all operations for retrieving data using a cursor.
|
||||
* \see mdbx_cursor_get() */
|
||||
enum MDBX_cursor_op {
|
||||
typedef enum MDBX_cursor_op {
|
||||
/** Position at first key/data item */
|
||||
MDBX_FIRST,
|
||||
|
||||
@ -1875,18 +1813,14 @@ enum MDBX_cursor_op {
|
||||
MDBX_TO_PAIR_EQUAL,
|
||||
MDBX_TO_PAIR_GREATER_OR_EQUAL,
|
||||
MDBX_TO_PAIR_GREATER_THAN
|
||||
};
|
||||
#ifndef __cplusplus
|
||||
/** \ingroup c_cursors */
|
||||
typedef enum MDBX_cursor_op MDBX_cursor_op;
|
||||
#endif
|
||||
} MDBX_cursor_op;
|
||||
|
||||
/** \brief Errors and return codes
|
||||
* \ingroup c_err
|
||||
*
|
||||
* BerkeleyDB uses -30800 to -30999, we'll go under them
|
||||
* \see mdbx_strerror() \see mdbx_strerror_r() \see mdbx_liberr2str() */
|
||||
enum MDBX_error_t {
|
||||
typedef enum MDBX_error {
|
||||
/** Successful result */
|
||||
MDBX_SUCCESS = 0,
|
||||
|
||||
@ -2062,11 +1996,7 @@ enum MDBX_error_t {
|
||||
MDBX_EREMOTE = ENOTBLK,
|
||||
MDBX_EDEADLK = EDEADLK
|
||||
#endif /* !Windows */
|
||||
};
|
||||
#ifndef __cplusplus
|
||||
/** \ingroup c_err */
|
||||
typedef enum MDBX_error_t MDBX_error_t;
|
||||
#endif
|
||||
} MDBX_error_t;
|
||||
|
||||
/** MDBX_MAP_RESIZED
|
||||
* \ingroup c_err
|
||||
@ -2158,7 +2088,7 @@ LIBMDBX_API int mdbx_env_create(MDBX_env **penv);
|
||||
/** \brief MDBX environment extra runtime options.
|
||||
* \ingroup c_settings
|
||||
* \see mdbx_env_set_option() \see mdbx_env_get_option() */
|
||||
enum MDBX_option_t {
|
||||
typedef enum MDBX_option {
|
||||
/** \brief Controls the maximum number of named databases for the environment.
|
||||
*
|
||||
* \details By default only unnamed key-value database could used and
|
||||
@ -2323,10 +2253,11 @@ enum MDBX_option_t {
|
||||
* \details This option controls the in-process threshold of minimum page
|
||||
* fill, as used space of percentage of a page. Neighbour pages emptier than
|
||||
* this value are candidates for merging. The threshold value is specified
|
||||
* in 1/65536 of percent, which is equivalent to the 16-dot-16 fixed point
|
||||
* format. The specified value must be in the range from 12.5% (almost empty)
|
||||
* to 50% (half empty) which corresponds to the range from 8192 and to 32768
|
||||
* in units respectively.
|
||||
* in 1/65536 points of a whole page, which is equivalent to the 16-dot-16
|
||||
* fixed point format.
|
||||
* The specified value must be in the range from 12.5% (almost empty page)
|
||||
* to 50% (half empty page) which corresponds to the range from 8192 and
|
||||
* to 32768 in units respectively.
|
||||
* \see MDBX_opt_prefer_waf_insteadof_balance */
|
||||
MDBX_opt_merge_threshold_16dot16_percent,
|
||||
|
||||
@ -2414,11 +2345,7 @@ enum MDBX_option_t {
|
||||
*
|
||||
* \see MDBX_opt_merge_threshold_16dot16_percent */
|
||||
MDBX_opt_prefer_waf_insteadof_balance
|
||||
};
|
||||
#ifndef __cplusplus
|
||||
/** \ingroup c_settings */
|
||||
typedef enum MDBX_option_t MDBX_option_t;
|
||||
#endif
|
||||
} MDBX_option_t;
|
||||
|
||||
/** \brief Sets the value of a extra runtime options for an environment.
|
||||
* \ingroup c_settings
|
||||
@ -2533,7 +2460,7 @@ LIBMDBX_API int mdbx_env_openW(MDBX_env *env, const wchar_t *pathname,
|
||||
/** \brief Deletion modes for \ref mdbx_env_delete().
|
||||
* \ingroup c_extra
|
||||
* \see mdbx_env_delete() */
|
||||
enum MDBX_env_delete_mode_t {
|
||||
typedef enum MDBX_env_delete_mode {
|
||||
/** \brief Just delete the environment's files and directory if any.
|
||||
* \note On POSIX systems, processes already working with the database will
|
||||
* continue to work without interference until it close the environment.
|
||||
@ -2547,11 +2474,7 @@ enum MDBX_env_delete_mode_t {
|
||||
/** \brief Wait until other processes closes the environment before deletion.
|
||||
*/
|
||||
MDBX_ENV_WAIT_FOR_UNUSED = 2,
|
||||
};
|
||||
#ifndef __cplusplus
|
||||
/** \ingroup c_extra */
|
||||
typedef enum MDBX_env_delete_mode_t MDBX_env_delete_mode_t;
|
||||
#endif
|
||||
} MDBX_env_delete_mode_t;
|
||||
|
||||
/** \brief Delete the environment's files in a proper and multiprocess-safe way.
|
||||
* \ingroup c_extra
|
||||
@ -2662,7 +2585,7 @@ struct MDBX_stat {
|
||||
uint32_t ms_depth; /**< Depth (height) of the B-tree */
|
||||
uint64_t ms_branch_pages; /**< Number of internal (non-leaf) pages */
|
||||
uint64_t ms_leaf_pages; /**< Number of leaf pages */
|
||||
uint64_t ms_overflow_pages; /**< Number of overflow pages */
|
||||
uint64_t ms_overflow_pages; /**< Number of large/overflow pages */
|
||||
uint64_t ms_entries; /**< Number of data items */
|
||||
uint64_t ms_mod_txnid; /**< Transaction ID of committed last modification */
|
||||
};
|
||||
@ -3122,7 +3045,7 @@ LIBMDBX_API int mdbx_env_resurrect_after_fork(MDBX_env *env);
|
||||
* \ingroup c_settings
|
||||
* \anchor warmup_flags
|
||||
* \see mdbx_env_warmup() */
|
||||
enum MDBX_warmup_flags_t {
|
||||
typedef enum MDBX_warmup_flags {
|
||||
/** By default \ref mdbx_env_warmup() just ask OS kernel to asynchronously
|
||||
* prefetch database pages. */
|
||||
MDBX_warmup_default = 0,
|
||||
@ -3165,12 +3088,8 @@ enum MDBX_warmup_flags_t {
|
||||
|
||||
/** Release the lock that was performed before by \ref MDBX_warmup_lock. */
|
||||
MDBX_warmup_release = 16,
|
||||
};
|
||||
#ifndef __cplusplus
|
||||
typedef enum MDBX_warmup_flags_t MDBX_warmup_flags_t;
|
||||
#else
|
||||
DEFINE_ENUM_FLAG_OPERATORS(MDBX_warmup_flags_t)
|
||||
#endif
|
||||
} MDBX_warmup_flags_t;
|
||||
DEFINE_ENUM_FLAG_OPERATORS(MDBX_warmup_flags)
|
||||
|
||||
/** \brief Warms up the database by loading pages into memory, optionally lock
|
||||
* ones. \ingroup c_settings
|
||||
@ -3564,7 +3483,7 @@ MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_API intptr_t
|
||||
mdbx_limits_pairsize4page_max(intptr_t pagesize, MDBX_db_flags_t flags);
|
||||
|
||||
/** \brief Returns maximal data size in bytes to fit in a leaf-page or
|
||||
* single overflow/large-page with the given page size and database flags,
|
||||
* single large/overflow-page with the given page size and database flags,
|
||||
* or -1 if pagesize is invalid.
|
||||
* \ingroup c_statinfo
|
||||
* \see db_flags */
|
||||
@ -3740,7 +3659,7 @@ MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int
|
||||
mdbx_env_get_pairsize4page_max(const MDBX_env *env, MDBX_db_flags_t flags);
|
||||
|
||||
/** \brief Returns maximal data size in bytes to fit in a leaf-page or
|
||||
* single overflow/large-page for specified database flags.
|
||||
* single large/overflow-page for specified database flags.
|
||||
* \ingroup c_statinfo
|
||||
*
|
||||
* \param [in] env An environment handle returned by \ref mdbx_env_create().
|
||||
@ -4578,7 +4497,7 @@ LIBMDBX_API int mdbx_dbi_dupsort_depthmask(const MDBX_txn *txn, MDBX_dbi dbi,
|
||||
/** \brief DBI state bits returted by \ref mdbx_dbi_flags_ex()
|
||||
* \ingroup c_statinfo
|
||||
* \see mdbx_dbi_flags_ex() */
|
||||
enum MDBX_dbi_state_t {
|
||||
typedef enum MDBX_dbi_state {
|
||||
/** DB was written in this txn */
|
||||
MDBX_DBI_DIRTY = 0x01,
|
||||
/** Cached Named-DB record is older than txnID */
|
||||
@ -4587,13 +4506,8 @@ enum MDBX_dbi_state_t {
|
||||
MDBX_DBI_FRESH = 0x04,
|
||||
/** Named-DB handle created in this txn */
|
||||
MDBX_DBI_CREAT = 0x08,
|
||||
};
|
||||
#ifndef __cplusplus
|
||||
/** \ingroup c_statinfo */
|
||||
typedef enum MDBX_dbi_state_t MDBX_dbi_state_t;
|
||||
#else
|
||||
DEFINE_ENUM_FLAG_OPERATORS(MDBX_dbi_state_t)
|
||||
#endif
|
||||
} MDBX_dbi_state_t;
|
||||
DEFINE_ENUM_FLAG_OPERATORS(MDBX_dbi_state)
|
||||
|
||||
/** \brief Retrieve the DB flags and status for a database handle.
|
||||
* \ingroup c_statinfo
|
||||
@ -5005,6 +4919,7 @@ LIBMDBX_API int mdbx_cursor_bind(const MDBX_txn *txn, MDBX_cursor *cursor,
|
||||
* \see mdbx_cursor_renew()
|
||||
* \see mdbx_cursor_bind()
|
||||
* \see mdbx_cursor_close()
|
||||
* \see mdbx_cursor_reset()
|
||||
*
|
||||
* \note In contrast to LMDB, the MDBX required that any opened cursors can be
|
||||
* reused and must be freed explicitly, regardless ones was opened in a
|
||||
@ -5017,6 +4932,20 @@ LIBMDBX_API int mdbx_cursor_bind(const MDBX_txn *txn, MDBX_cursor *cursor,
|
||||
* \returns A non-zero error value on failure and 0 on success. */
|
||||
LIBMDBX_API int mdbx_cursor_unbind(MDBX_cursor *cursor);
|
||||
|
||||
/** \brief Сбрасывает состояние курсора.
|
||||
* \ingroup c_cursors
|
||||
*
|
||||
* В результате сброса курсор становится неустановленным и не позволяет
|
||||
* выполнять операции относительного позиционирования, получения или изменения
|
||||
* данных, до установки на позицию не зависящую от текущей. Что позволяет
|
||||
* приложению пресекать дальнейшие операции без предварительного
|
||||
* позиционирования курсора.
|
||||
*
|
||||
* \param [in] cursor Указатель на курсор.
|
||||
*
|
||||
* \returns Результат операции сканирования, либо код ошибки. */
|
||||
LIBMDBX_API int mdbx_cursor_reset(MDBX_cursor *cursor);
|
||||
|
||||
/** \brief Create a cursor handle for the specified transaction and DBI handle.
|
||||
* \ingroup c_cursors
|
||||
*
|
||||
@ -5197,6 +5126,21 @@ LIBMDBX_API int mdbx_cursor_compare(const MDBX_cursor *left,
|
||||
LIBMDBX_API int mdbx_cursor_get(MDBX_cursor *cursor, MDBX_val *key,
|
||||
MDBX_val *data, MDBX_cursor_op op);
|
||||
|
||||
/** \brief Служебная функция для использования в утилитах.
|
||||
* \ingroup c_extra
|
||||
*
|
||||
* При использовании определяемых пользователем функций сравнения (aka custom
|
||||
* comparison functions) проверка порядка ключей может приводить к неверным
|
||||
* результатам и возврате ошибки \ref MDBX_CORRUPTED.
|
||||
*
|
||||
* Эта функция отключает контроль порядка следования ключей на страницах при
|
||||
* чтении страниц БД для этого курсора, и таким образом, позволяет прочитать
|
||||
* данные при отсутствии/недоступности использованных функций сравнения.
|
||||
* \see avoid_custom_comparators
|
||||
*
|
||||
* \returns Результат операции сканирования, либо код ошибки. */
|
||||
LIBMDBX_API int mdbx_cursor_ignord(MDBX_cursor *cursor);
|
||||
|
||||
/** \brief Тип предикативных функций обратного вызова используемых
|
||||
* \ref mdbx_cursor_scan() и \ref mdbx_cursor_scan_from() для пробирования
|
||||
* пар ключ-значения.
|
||||
@ -5424,18 +5368,16 @@ LIBMDBX_API int mdbx_cursor_scan_from(MDBX_cursor *cursor,
|
||||
* \param [in] limit The size of pairs buffer as the number of items,
|
||||
* but not a pairs.
|
||||
* \param [in] op A cursor operation \ref MDBX_cursor_op (only
|
||||
* \ref MDBX_FIRST, \ref MDBX_NEXT, \ref MDBX_GET_CURRENT
|
||||
* are supported).
|
||||
* \ref MDBX_FIRST and \ref MDBX_NEXT are supported).
|
||||
*
|
||||
* \returns A non-zero error value on failure and 0 on success,
|
||||
* some possible errors are:
|
||||
* \retval MDBX_THREAD_MISMATCH Given transaction is not owned
|
||||
* by current thread.
|
||||
* \retval MDBX_NOTFOUND No more key-value pairs are available.
|
||||
* \retval MDBX_NOTFOUND No any key-value pairs are available.
|
||||
* \retval MDBX_ENODATA The cursor is already at the end of data.
|
||||
* \retval MDBX_RESULT_TRUE The specified limit is less than the available
|
||||
* key-value pairs on the current page/position
|
||||
* that the cursor points to.
|
||||
* \retval MDBX_RESULT_TRUE The returned chunk is the last one,
|
||||
* and there are no pairs left.
|
||||
* \retval MDBX_EINVAL An invalid parameter was specified. */
|
||||
LIBMDBX_API int mdbx_cursor_get_batch(MDBX_cursor *cursor, size_t *count,
|
||||
MDBX_val *pairs, size_t limit,
|
||||
@ -6166,7 +6108,7 @@ LIBMDBX_API int mdbx_preopen_snapinfoW(const wchar_t *pathname,
|
||||
* \note Данный API еще не зафиксирован, в последующих версиях могут быть
|
||||
* незначительные доработки и изменения.
|
||||
* \see mdbx_env_chk() */
|
||||
enum MDBX_chk_flags_t {
|
||||
typedef enum MDBX_chk_flags {
|
||||
/** Режим проверки по-умолчанию, в том числе в режиме только-чтения. */
|
||||
MDBX_CHK_DEFAULTS = 0,
|
||||
|
||||
@ -6184,18 +6126,13 @@ enum MDBX_chk_flags_t {
|
||||
* \note Требуется при проверке унаследованных БД созданных с использованием
|
||||
* нестандартных (пользовательских) функций сравнения ключей или значений. */
|
||||
MDBX_CHK_IGNORE_ORDER = 8
|
||||
};
|
||||
#ifndef __cplusplus
|
||||
/** \ingroup c_opening */
|
||||
typedef enum MDBX_chk_flags_t MDBX_chk_flags_t;
|
||||
#else
|
||||
DEFINE_ENUM_FLAG_OPERATORS(MDBX_chk_flags_t)
|
||||
#endif
|
||||
} MDBX_chk_flags_t;
|
||||
DEFINE_ENUM_FLAG_OPERATORS(MDBX_chk_flags)
|
||||
|
||||
/** \brief Уровни логирование/детализации информации,
|
||||
* поставляемой через обратные вызовы при проверке целостности базы данных.
|
||||
* \see mdbx_env_chk() */
|
||||
enum MDBX_chk_severity {
|
||||
typedef enum MDBX_chk_severity {
|
||||
MDBX_chk_severity_prio_shift = 4,
|
||||
MDBX_chk_severity_kind_mask = 0xF,
|
||||
MDBX_chk_fatal = 0x00u,
|
||||
@ -6209,25 +6146,25 @@ enum MDBX_chk_severity {
|
||||
MDBX_chk_verbose = 0x78u,
|
||||
MDBX_chk_details = 0x89u,
|
||||
MDBX_chk_extra = 0x9Au
|
||||
};
|
||||
} MDBX_chk_severity_t;
|
||||
|
||||
/** \brief Стадии проверки,
|
||||
* сообщаемые через обратные вызовы при проверке целостности базы данных.
|
||||
* \see mdbx_env_chk() */
|
||||
enum MDBX_chk_stage {
|
||||
typedef enum MDBX_chk_stage {
|
||||
MDBX_chk_none,
|
||||
MDBX_chk_init,
|
||||
MDBX_chk_lock,
|
||||
MDBX_chk_meta,
|
||||
MDBX_chk_traversal_tree,
|
||||
MDBX_chk_traversal_freedb,
|
||||
MDBX_chk_tree,
|
||||
MDBX_chk_gc,
|
||||
MDBX_chk_space,
|
||||
MDBX_chk_traversal_maindb,
|
||||
MDBX_chk_traversal_subdbs,
|
||||
MDBX_chk_maindb,
|
||||
MDBX_chk_subdbs,
|
||||
MDBX_chk_conclude,
|
||||
MDBX_chk_unlock,
|
||||
MDBX_chk_finalize
|
||||
};
|
||||
} MDBX_chk_stage_t;
|
||||
|
||||
/** \brief Виртуальная строка отчета, формируемого при проверке целостности базы
|
||||
* данных. \see mdbx_env_chk() */
|
||||
@ -6251,8 +6188,8 @@ typedef struct MDBX_chk_scope {
|
||||
MDBX_chk_issue_t *issues;
|
||||
struct MDBX_chk_internal *internal;
|
||||
const void *object;
|
||||
enum MDBX_chk_stage stage;
|
||||
enum MDBX_chk_severity verbosity;
|
||||
MDBX_chk_stage_t stage;
|
||||
MDBX_chk_severity_t verbosity;
|
||||
size_t subtotal_issues;
|
||||
union {
|
||||
void *ptr;
|
||||
@ -6373,11 +6310,11 @@ typedef struct MDBX_chk_callbacks {
|
||||
size_t entry_number, const MDBX_val *key,
|
||||
const MDBX_val *value);
|
||||
|
||||
int (*stage_begin)(MDBX_chk_context_t *ctx, enum MDBX_chk_stage);
|
||||
int (*stage_end)(MDBX_chk_context_t *ctx, enum MDBX_chk_stage, int err);
|
||||
int (*stage_begin)(MDBX_chk_context_t *ctx, MDBX_chk_stage_t);
|
||||
int (*stage_end)(MDBX_chk_context_t *ctx, MDBX_chk_stage_t, int err);
|
||||
|
||||
MDBX_chk_line_t *(*print_begin)(MDBX_chk_context_t *ctx,
|
||||
enum MDBX_chk_severity severity);
|
||||
MDBX_chk_severity_t severity);
|
||||
void (*print_flush)(MDBX_chk_line_t *);
|
||||
void (*print_done)(MDBX_chk_line_t *);
|
||||
void (*print_chars)(MDBX_chk_line_t *, const char *str, size_t len);
|
||||
@ -6417,8 +6354,8 @@ typedef struct MDBX_chk_callbacks {
|
||||
* \returns Нулевое значение в случае успеха, иначе код ошибки. */
|
||||
LIBMDBX_API int mdbx_env_chk(MDBX_env *env, const MDBX_chk_callbacks_t *cb,
|
||||
MDBX_chk_context_t *ctx,
|
||||
const enum MDBX_chk_flags_t flags,
|
||||
enum MDBX_chk_severity verbosity,
|
||||
const MDBX_chk_flags_t flags,
|
||||
MDBX_chk_severity_t verbosity,
|
||||
unsigned timeout_seconds_16dot16);
|
||||
|
||||
/** \brief Вспомогательная функция для подсчета проблем детектируемых
|
||||
|
41
mdbx.h++
41
mdbx.h++
@ -1,8 +1,8 @@
|
||||
/// \file mdbx.h++
|
||||
/// \brief The libmdbx C++ API header file.
|
||||
/// \copyright SPDX-License-Identifier: Apache-2.0
|
||||
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2020-2024
|
||||
///
|
||||
/// \author Copyright (c) 2020-2024, Leonid Yuriev <leo@yuriev.ru>.
|
||||
/// \copyright SPDX-License-Identifier: Apache-2.0
|
||||
/// \file mdbx.h++
|
||||
/// \brief The libmdbx C++ API header file.
|
||||
///
|
||||
/// Tested with:
|
||||
/// - Elbrus LCC >= 1.23 (http://www.mcst.ru/lcc);
|
||||
@ -2329,14 +2329,16 @@ public:
|
||||
|
||||
buffer(const char *c_str, bool make_reference,
|
||||
const allocator_type &allocator = allocator_type())
|
||||
: buffer(::mdbx::slice(c_str), make_reference, allocator) {}
|
||||
: buffer(::mdbx::slice(c_str), make_reference, allocator){}
|
||||
|
||||
#if defined(DOXYGEN) || \
|
||||
(defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L)
|
||||
template <class CHAR, class T>
|
||||
buffer(const ::std::basic_string_view<CHAR, T> &view, bool make_reference,
|
||||
const allocator_type &allocator = allocator_type())
|
||||
: buffer(::mdbx::slice(view), make_reference, allocator) {}
|
||||
template <class CHAR, class T>
|
||||
buffer(const ::std::basic_string_view<CHAR, T> &view,
|
||||
bool make_reference,
|
||||
const allocator_type &allocator = allocator_type())
|
||||
: buffer(::mdbx::slice(view), make_reference, allocator) {
|
||||
}
|
||||
#endif /* __cpp_lib_string_view >= 201606L */
|
||||
|
||||
MDBX_CXX20_CONSTEXPR
|
||||
@ -2362,15 +2364,16 @@ public:
|
||||
|
||||
MDBX_CXX20_CONSTEXPR
|
||||
buffer(const char *c_str, const allocator_type &allocator = allocator_type())
|
||||
: buffer(::mdbx::slice(c_str), allocator) {}
|
||||
: buffer(::mdbx::slice(c_str), allocator){}
|
||||
|
||||
#if defined(DOXYGEN) || \
|
||||
(defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L)
|
||||
template <class CHAR, class T>
|
||||
MDBX_CXX20_CONSTEXPR
|
||||
buffer(const ::std::basic_string_view<CHAR, T> &view,
|
||||
const allocator_type &allocator = allocator_type())
|
||||
: buffer(::mdbx::slice(view), allocator) {}
|
||||
template <class CHAR, class T>
|
||||
MDBX_CXX20_CONSTEXPR
|
||||
buffer(const ::std::basic_string_view<CHAR, T> &view,
|
||||
const allocator_type &allocator = allocator_type())
|
||||
: buffer(::mdbx::slice(view), allocator) {
|
||||
}
|
||||
#endif /* __cpp_lib_string_view >= 201606L */
|
||||
|
||||
buffer(size_t head_room, size_t tail_room,
|
||||
@ -3819,17 +3822,17 @@ public:
|
||||
static inline size_t pairsize4page_max(const env &, value_mode);
|
||||
|
||||
/// \brief Returns maximal data size in bytes to fit in a leaf-page or
|
||||
/// single overflow/large-page for specified size and database flags.
|
||||
/// single large/overflow-page for specified size and database flags.
|
||||
static inline size_t valsize4page_max(intptr_t pagesize,
|
||||
MDBX_db_flags_t flags);
|
||||
/// \brief Returns maximal data size in bytes to fit in a leaf-page or
|
||||
/// single overflow/large-page for specified page size and values mode.
|
||||
/// single large/overflow-page for specified page size and values mode.
|
||||
static inline size_t valsize4page_max(intptr_t pagesize, value_mode);
|
||||
/// \brief Returns maximal data size in bytes to fit in a leaf-page or
|
||||
/// single overflow/large-page for given environment and database flags.
|
||||
/// single large/overflow-page for given environment and database flags.
|
||||
static inline size_t valsize4page_max(const env &, MDBX_db_flags_t flags);
|
||||
/// \brief Returns maximal data size in bytes to fit in a leaf-page or
|
||||
/// single overflow/large-page for specified page size and values mode.
|
||||
/// single large/overflow-page for specified page size and values mode.
|
||||
static inline size_t valsize4page_max(const env &, value_mode);
|
||||
|
||||
/// \brief Returns the maximal write transaction size (i.e. limit for
|
||||
|
@ -1,184 +0,0 @@
|
||||
cmake_minimum_required(VERSION 2.8.7)
|
||||
set(TARGET mdbx)
|
||||
project(${TARGET})
|
||||
|
||||
set(MDBX_VERSION_MAJOR 0)
|
||||
set(MDBX_VERSION_MINOR 3)
|
||||
set(MDBX_VERSION_RELEASE 1)
|
||||
set(MDBX_VERSION_REVISION 0)
|
||||
|
||||
set(MDBX_VERSION_STRING ${MDBX_VERSION_MAJOR}.${MDBX_VERSION_MINOR}.${MDBX_VERSION_RELEASE})
|
||||
|
||||
enable_language(C)
|
||||
enable_language(CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED on)
|
||||
|
||||
add_definitions(-DNDEBUG=1 -DMDBX_DEBUG=0 -DLIBMDBX_EXPORTS=1 -D_GNU_SOURCE=1)
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
get_directory_property(hasParent PARENT_DIRECTORY)
|
||||
if(hasParent)
|
||||
set(STANDALONE_BUILD 0)
|
||||
else()
|
||||
set(STANDALONE_BUILD 1)
|
||||
enable_testing()
|
||||
|
||||
if (CMAKE_C_COMPILER_ID MATCHES GNU)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wpointer-arith")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-sign-compare")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wformat-security")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Woverloaded-virtual")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wwrite-strings")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fmax-errors=20")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter -Wunused-function -Wunused-variable -Wunused-value -Wmissing-declarations")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-field-initializers")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wcast-qual")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-strict-aliasing")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -finline-functions-called-once")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-packed-bitfield-compat")
|
||||
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g3")
|
||||
endif()
|
||||
|
||||
if (COVERAGE)
|
||||
if (NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
||||
message(FATAL_ERROR "Coverage requires -DCMAKE_BUILD_TYPE=Debug Current value=${CMAKE_BUILD_TYPE}")
|
||||
endif()
|
||||
|
||||
message(STATUS "Setting coverage compiler flags")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -ggdb3 -O0 --coverage -fprofile-arcs -ftest-coverage")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -ggdb3 -O0 --coverage -fprofile-arcs -ftest-coverage")
|
||||
add_definitions(-DCOVERAGE_TEST)
|
||||
endif()
|
||||
|
||||
if (NOT TRAVIS)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address -fsanitize=leak -fstack-protector-strong -static-libasan")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(${TARGET}_SRC
|
||||
mdbx.h
|
||||
src/bits.h
|
||||
src/defs.h
|
||||
src/lck-linux.c
|
||||
src/mdbx.c
|
||||
src/osal.c
|
||||
src/osal.h
|
||||
src/version.c
|
||||
)
|
||||
|
||||
add_library(${TARGET}_STATIC STATIC
|
||||
${${TARGET}_SRC}
|
||||
)
|
||||
|
||||
add_library(${TARGET} ALIAS ${TARGET}_STATIC)
|
||||
|
||||
add_library(${TARGET}_SHARED SHARED
|
||||
${${TARGET}_SRC}
|
||||
)
|
||||
|
||||
set_target_properties(${TARGET}_SHARED PROPERTIES
|
||||
VERSION ${MDBX_VERSION_STRING}
|
||||
SOVERSION ${MDBX_VERSION_MAJOR}.${MDBX_VERSION_MINOR}
|
||||
OUTPUT_NAME ${TARGET}
|
||||
CLEAN_DIRECT_OUTPUT 1
|
||||
)
|
||||
|
||||
set_target_properties(${TARGET}_STATIC PROPERTIES
|
||||
VERSION ${MDBX_VERSION_STRING}
|
||||
SOVERSION ${MDBX_VERSION_MAJOR}.${MDBX_VERSION_MINOR}
|
||||
OUTPUT_NAME ${TARGET}
|
||||
CLEAN_DIRECT_OUTPUT 1
|
||||
)
|
||||
|
||||
target_include_directories(${TARGET}_STATIC PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_include_directories(${TARGET}_SHARED PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
target_link_libraries(${TARGET}_STATIC ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_link_libraries(${TARGET}_SHARED ${CMAKE_THREAD_LIBS_INIT})
|
||||
if(UNIX AND NOT APPLE)
|
||||
target_link_libraries(${TARGET}_STATIC rt)
|
||||
target_link_libraries(${TARGET}_SHARED rt)
|
||||
endif()
|
||||
|
||||
install(TARGETS ${TARGET}_STATIC DESTINATION ${CMAKE_INSTALL_PREFIX}/lib64 COMPONENT mdbx)
|
||||
install(TARGETS ${TARGET}_SHARED DESTINATION ${CMAKE_INSTALL_PREFIX}/lib64 COMPONENT mdbx)
|
||||
install(FILES mdbx.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include COMPONENT mdbx-devel)
|
||||
|
||||
add_subdirectory(src/tools)
|
||||
add_subdirectory(test)
|
||||
add_subdirectory(test/pcrf)
|
||||
add_subdirectory(tutorial)
|
||||
|
||||
##############################################################################
|
||||
|
||||
set(CPACK_GENERATOR "RPM")
|
||||
set(CPACK_RPM_COMPONENT_INSTALL ON)
|
||||
|
||||
# Version
|
||||
if (NOT "$ENV{BUILD_NUMBER}" STREQUAL "")
|
||||
set(CPACK_PACKAGE_RELEASE $ENV{BUILD_NUMBER})
|
||||
else()
|
||||
if (NOT "$ENV{CI_PIPELINE_ID}" STREQUAL "")
|
||||
set(CPACK_PACKAGE_RELEASE $ENV{CI_PIPELINE_ID})
|
||||
else()
|
||||
set(CPACK_PACKAGE_RELEASE 1)
|
||||
endif()
|
||||
endif()
|
||||
set(CPACK_RPM_PACKAGE_RELEASE ${CPACK_PACKAGE_RELEASE})
|
||||
|
||||
set(CPACK_PACKAGE_VERSION ${MDBX_VERSION_STRING})
|
||||
set(CPACK_PACKAGE_VERSION_FULL ${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE})
|
||||
|
||||
set(CPACK_RPM_mdbx-devel_PACKAGE_REQUIRES "mdbx = ${CPACK_PACKAGE_VERSION}")
|
||||
|
||||
set(CPACK_RPM_SPEC_INSTALL_POST "/bin/true")
|
||||
set(CPACK_RPM_mdbx_PACKAGE_NAME mdbx)
|
||||
set(CPACK_RPM_mdbx-devel_PACKAGE_NAME mdbx-devel)
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "The revised and extended descendant of Symas LMDB")
|
||||
|
||||
set(CPACK_PACKAGE_VENDOR "???")
|
||||
set(CPACK_PACKAGE_CONTACT "Vladimir Romanov")
|
||||
set(CPACK_PACKAGE_RELOCATABLE false)
|
||||
set(CPACK_RPM_PACKAGE_ARCHITECTURE "x86_64")
|
||||
set(CPACK_RPM_PACKAGE_REQUIRES "")
|
||||
set(CPACK_RPM_PACKAGE_GROUP "Applications/Database")
|
||||
|
||||
set(CPACK_RPM_mdbx_FILE_NAME "${CPACK_RPM_mdbx_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION_FULL}.${CPACK_RPM_PACKAGE_ARCHITECTURE}.rpm")
|
||||
set(CPACK_RPM_mdbx-devel_FILE_NAME "${CPACK_RPM_mdbx-devel_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION_FULL}.${CPACK_RPM_PACKAGE_ARCHITECTURE}.rpm")
|
||||
|
||||
set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION
|
||||
/usr/local
|
||||
/usr/local/bin
|
||||
/usr/local/lib64
|
||||
/usr/local/include
|
||||
/usr/local/man
|
||||
/usr/local/man/man1
|
||||
)
|
||||
|
||||
include(CPack)
|
@ -1,18 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
CONFIG=$1
|
||||
|
||||
if [[ -z "${CONFIG}" ]]; then
|
||||
CONFIG=Debug
|
||||
fi
|
||||
if [[ -r /opt/rh/devtoolset-6/enable ]]; then
|
||||
source /opt/rh/devtoolset-6/enable
|
||||
fi
|
||||
#rm -f -r build || true
|
||||
mkdir -p cmake-build-${CONFIG}
|
||||
pushd cmake-build-${CONFIG} &> /dev/null
|
||||
if [[ ! -r Makefile ]]; then
|
||||
cmake .. -DCMAKE_BUILD_TYPE=${CONFIG}
|
||||
fi
|
||||
make -j8 || exit 1
|
||||
popd &> /dev/null
|
@ -1,25 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
CONFIG=$1
|
||||
|
||||
if [[ -z "${CONFIG}" ]]; then
|
||||
CONFIG=Debug
|
||||
fi
|
||||
|
||||
DIRNAME=`dirname ${BASH_SOURCE[0]}`
|
||||
DIRNAME=`readlink --canonicalize ${DIRNAME}`
|
||||
|
||||
if [[ -r /opt/rh/devtoolset-6/enable ]]; then
|
||||
source /opt/rh/devtoolset-6/enable
|
||||
fi
|
||||
|
||||
mkdir -p cmake-build-${CONFIG}
|
||||
pushd cmake-build-${CONFIG} &> /dev/null
|
||||
if [[ ! -r Makefile ]]; then
|
||||
cmake .. -DCMAKE_BUILD_TYPE=${CONFIG}
|
||||
fi
|
||||
rm -f *.rpm
|
||||
make -j8 package || exit 1
|
||||
rm -f *-Unspecified.rpm
|
||||
popd &> /dev/null
|
67
src/alloy.c
67
src/alloy.c
@ -1,25 +1,52 @@
|
||||
/*
|
||||
* Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>
|
||||
* and other libmdbx authors: please see AUTHORS file.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>. */
|
||||
/// \copyright SPDX-License-Identifier: Apache-2.0
|
||||
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
|
||||
|
||||
#define xMDBX_ALLOY 1 /* alloyed build */
|
||||
#include "internals.h" /* must be included first */
|
||||
|
||||
#include "core.c"
|
||||
#include "osal.c"
|
||||
#include "version.c"
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#include "lck-windows.c"
|
||||
#else
|
||||
#include "api-cursor.c"
|
||||
#include "api-env.c"
|
||||
#include "api-extra.c"
|
||||
#include "api-key-transform.c"
|
||||
#include "api-txn.c"
|
||||
#include "audit.c"
|
||||
#include "chk.c"
|
||||
#include "cogs.c"
|
||||
#include "coherency.c"
|
||||
#include "cold.c"
|
||||
#include "copy.c"
|
||||
#include "cursor.c"
|
||||
#include "dbi.c"
|
||||
#include "dpl.c"
|
||||
#include "dxb.c"
|
||||
#include "env-opts.c"
|
||||
#include "env.c"
|
||||
#include "gc-get.c"
|
||||
#include "gc-put.c"
|
||||
#include "global.c"
|
||||
#include "lck-posix.c"
|
||||
#endif
|
||||
#include "lck-windows.c"
|
||||
#include "lck.c"
|
||||
#include "logging_and_debug.c"
|
||||
#include "meta.c"
|
||||
#include "misc.c"
|
||||
#include "mvcc-readers.c"
|
||||
#include "node.c"
|
||||
#include "osal.c"
|
||||
#include "page-get.c"
|
||||
#include "page-iov.c"
|
||||
#include "page-ops.c"
|
||||
#include "page-search.c"
|
||||
#include "pnl.c"
|
||||
#include "range-estimate.c"
|
||||
#include "refund.c"
|
||||
#include "spill.c"
|
||||
#include "subdb.c"
|
||||
#include "tls.c"
|
||||
#include "tree.c"
|
||||
#include "txl.c"
|
||||
#include "txn.c"
|
||||
#include "utils.c"
|
||||
#include "version.c"
|
||||
#include "walk.c"
|
||||
#include "windows-import.c"
|
||||
|
797
src/api-cursor.c
Normal file
797
src/api-cursor.c
Normal file
@ -0,0 +1,797 @@
|
||||
/// \copyright SPDX-License-Identifier: Apache-2.0
|
||||
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
|
||||
|
||||
#include "internals.h"
|
||||
|
||||
MDBX_cursor *mdbx_cursor_create(void *context) {
|
||||
cursor_couple_t *couple = osal_calloc(1, sizeof(cursor_couple_t));
|
||||
if (unlikely(!couple))
|
||||
return nullptr;
|
||||
|
||||
VALGRIND_MAKE_MEM_UNDEFINED(couple, sizeof(cursor_couple_t));
|
||||
couple->outer.signature = cur_signature_ready4dispose;
|
||||
couple->outer.next = &couple->outer;
|
||||
couple->userctx = context;
|
||||
couple->outer.top_and_flags = z_poor_mark;
|
||||
couple->inner.cursor.top_and_flags = z_poor_mark | z_inner;
|
||||
VALGRIND_MAKE_MEM_DEFINED(&couple->outer.backup,
|
||||
sizeof(couple->outer.backup));
|
||||
VALGRIND_MAKE_MEM_DEFINED(&couple->outer.tree, sizeof(couple->outer.tree));
|
||||
VALGRIND_MAKE_MEM_DEFINED(&couple->outer.clc, sizeof(couple->outer.clc));
|
||||
VALGRIND_MAKE_MEM_DEFINED(&couple->outer.dbi_state,
|
||||
sizeof(couple->outer.dbi_state));
|
||||
VALGRIND_MAKE_MEM_DEFINED(&couple->outer.subcur,
|
||||
sizeof(couple->outer.subcur));
|
||||
VALGRIND_MAKE_MEM_DEFINED(&couple->outer.txn, sizeof(couple->outer.txn));
|
||||
return &couple->outer;
|
||||
}
|
||||
|
||||
int mdbx_cursor_renew(const MDBX_txn *txn, MDBX_cursor *mc) {
|
||||
return likely(mc)
|
||||
? mdbx_cursor_bind(txn, mc, (kvx_t *)mc->clc - txn->env->kvs)
|
||||
: MDBX_EINVAL;
|
||||
}
|
||||
|
||||
int mdbx_cursor_reset(MDBX_cursor *mc) {
|
||||
if (unlikely(!mc))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(mc->signature != cur_signature_ready4dispose &&
|
||||
mc->signature != cur_signature_live))
|
||||
return MDBX_EBADSIGN;
|
||||
|
||||
cursor_couple_t *couple = (cursor_couple_t *)mc;
|
||||
couple->outer.top_and_flags = z_poor_mark;
|
||||
couple->inner.cursor.top_and_flags = z_poor_mark | z_inner;
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
int mdbx_cursor_bind(const MDBX_txn *txn, MDBX_cursor *mc, MDBX_dbi dbi) {
|
||||
if (unlikely(!mc))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(mc->signature != cur_signature_ready4dispose &&
|
||||
mc->signature != cur_signature_live))
|
||||
return MDBX_EBADSIGN;
|
||||
|
||||
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
rc = dbi_check(txn, dbi);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
if (unlikely(dbi == FREE_DBI && !(txn->flags & MDBX_TXN_RDONLY)))
|
||||
return MDBX_EACCESS;
|
||||
|
||||
if (unlikely(mc->backup)) /* Cursor from parent transaction */ {
|
||||
cASSERT(mc, mc->signature == cur_signature_live);
|
||||
if (unlikely(cursor_dbi(mc) != dbi ||
|
||||
/* paranoia */ mc->signature != cur_signature_live ||
|
||||
mc->txn != txn))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
cASSERT(mc, mc->tree == &txn->dbs[dbi]);
|
||||
cASSERT(mc, mc->clc == &txn->env->kvs[dbi].clc);
|
||||
cASSERT(mc, cursor_dbi(mc) == dbi);
|
||||
return likely(cursor_dbi(mc) == dbi &&
|
||||
/* paranoia */ mc->signature == cur_signature_live &&
|
||||
mc->txn == txn)
|
||||
? MDBX_SUCCESS
|
||||
: MDBX_EINVAL /* Disallow change DBI in nested transactions */;
|
||||
}
|
||||
|
||||
if (mc->signature == cur_signature_live) {
|
||||
rc = mdbx_cursor_unbind(mc);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
}
|
||||
cASSERT(mc, mc->next == mc);
|
||||
|
||||
rc = cursor_init(mc, txn, dbi);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
mc->next = txn->cursors[dbi];
|
||||
txn->cursors[dbi] = mc;
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
int mdbx_cursor_unbind(MDBX_cursor *mc) {
|
||||
if (unlikely(!mc))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(mc->signature != cur_signature_live))
|
||||
return (mc->signature == cur_signature_ready4dispose) ? MDBX_SUCCESS
|
||||
: MDBX_EBADSIGN;
|
||||
|
||||
if (unlikely(mc->backup)) /* Cursor from parent transaction */
|
||||
return MDBX_EINVAL;
|
||||
|
||||
eASSERT(nullptr, mc->txn && mc->txn->signature == txn_signature);
|
||||
cASSERT(mc, mc->signature == cur_signature_live);
|
||||
cASSERT(mc, !mc->backup);
|
||||
if (unlikely(!mc->txn || mc->txn->signature != txn_signature)) {
|
||||
ERROR("Wrong cursor's transaction %p 0x%x",
|
||||
__Wpedantic_format_voidptr(mc->txn),
|
||||
mc->txn ? mc->txn->signature : 0);
|
||||
return MDBX_PROBLEM;
|
||||
}
|
||||
if (mc->next != mc) {
|
||||
const size_t dbi = (kvx_t *)mc->clc - mc->txn->env->kvs;
|
||||
cASSERT(mc, cursor_dbi(mc) == dbi);
|
||||
cASSERT(mc, dbi < mc->txn->n_dbi);
|
||||
if (dbi < mc->txn->n_dbi) {
|
||||
MDBX_cursor **prev = &mc->txn->cursors[dbi];
|
||||
while (*prev && *prev != mc)
|
||||
prev = &(*prev)->next;
|
||||
cASSERT(mc, *prev == mc);
|
||||
*prev = mc->next;
|
||||
}
|
||||
mc->next = mc;
|
||||
}
|
||||
mc->signature = cur_signature_ready4dispose;
|
||||
mc->flags = 0;
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
int mdbx_cursor_open(const MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) {
|
||||
if (unlikely(!ret))
|
||||
return MDBX_EINVAL;
|
||||
*ret = nullptr;
|
||||
|
||||
MDBX_cursor *const mc = mdbx_cursor_create(nullptr);
|
||||
if (unlikely(!mc))
|
||||
return MDBX_ENOMEM;
|
||||
|
||||
int rc = mdbx_cursor_bind(txn, mc, dbi);
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
mdbx_cursor_close(mc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
*ret = mc;
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
void mdbx_cursor_close(MDBX_cursor *mc) {
|
||||
if (likely(mc)) {
|
||||
ENSURE(nullptr, mc->signature == cur_signature_live ||
|
||||
mc->signature == cur_signature_ready4dispose);
|
||||
MDBX_txn *const txn = mc->txn;
|
||||
if (!mc->backup) {
|
||||
mc->txn = nullptr;
|
||||
/* Unlink from txn, if tracked. */
|
||||
if (mc->next != mc) {
|
||||
ENSURE(txn->env, check_txn(txn, 0) == MDBX_SUCCESS);
|
||||
const size_t dbi = (kvx_t *)mc->clc - txn->env->kvs;
|
||||
tASSERT(txn, dbi < txn->n_dbi);
|
||||
if (dbi < txn->n_dbi) {
|
||||
MDBX_cursor **prev = &txn->cursors[dbi];
|
||||
while (*prev && *prev != mc)
|
||||
prev = &(*prev)->next;
|
||||
tASSERT(txn, *prev == mc);
|
||||
*prev = mc->next;
|
||||
}
|
||||
mc->next = mc;
|
||||
}
|
||||
mc->signature = 0;
|
||||
osal_free(mc);
|
||||
} else {
|
||||
/* Cursor closed before nested txn ends */
|
||||
tASSERT(txn, mc->signature == cur_signature_live);
|
||||
ENSURE(txn->env, check_txn_rw(txn, 0) == MDBX_SUCCESS);
|
||||
mc->signature = cur_signature_wait4eot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int mdbx_cursor_copy(const MDBX_cursor *src, MDBX_cursor *dest) {
|
||||
if (unlikely(!src))
|
||||
return MDBX_EINVAL;
|
||||
if (unlikely(src->signature != cur_signature_live))
|
||||
return (src->signature == cur_signature_ready4dispose) ? MDBX_EINVAL
|
||||
: MDBX_EBADSIGN;
|
||||
|
||||
int rc = mdbx_cursor_bind(src->txn, dest, cursor_dbi(src));
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
assert(dest->tree == src->tree);
|
||||
assert(cursor_dbi(dest) == cursor_dbi(src));
|
||||
again:
|
||||
assert(dest->clc == src->clc);
|
||||
assert(dest->txn == src->txn);
|
||||
dest->top_and_flags = src->top_and_flags;
|
||||
for (intptr_t i = 0; i <= src->top; ++i) {
|
||||
dest->ki[i] = src->ki[i];
|
||||
dest->pg[i] = src->pg[i];
|
||||
}
|
||||
|
||||
if (src->subcur) {
|
||||
dest->subcur->nested_tree = src->subcur->nested_tree;
|
||||
src = &src->subcur->cursor;
|
||||
dest = &dest->subcur->cursor;
|
||||
goto again;
|
||||
}
|
||||
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
int mdbx_txn_release_all_cursors(const MDBX_txn *txn, bool unbind) {
|
||||
int rc = check_txn(txn, MDBX_TXN_FINISHED | MDBX_TXN_HAS_CHILD);
|
||||
if (likely(rc == MDBX_SUCCESS)) {
|
||||
TXN_FOREACH_DBI_FROM(txn, i, MAIN_DBI) {
|
||||
while (txn->cursors[i]) {
|
||||
MDBX_cursor *mc = txn->cursors[i];
|
||||
ENSURE(nullptr, mc->signature == cur_signature_live &&
|
||||
(mc->next != mc) && !mc->backup);
|
||||
rc = likely(rc < INT_MAX) ? rc + 1 : rc;
|
||||
txn->cursors[i] = mc->next;
|
||||
mc->next = mc;
|
||||
if (unbind) {
|
||||
mc->signature = cur_signature_ready4dispose;
|
||||
mc->flags = 0;
|
||||
} else {
|
||||
mc->signature = 0;
|
||||
osal_free(mc);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eASSERT(nullptr, rc < 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int mdbx_cursor_compare(const MDBX_cursor *l, const MDBX_cursor *r,
|
||||
bool ignore_multival) {
|
||||
const int incomparable = INT16_MAX + 1;
|
||||
if (unlikely(!l))
|
||||
return r ? -incomparable * 9 : 0;
|
||||
else if (unlikely(!r))
|
||||
return incomparable * 9;
|
||||
|
||||
if (unlikely(l->signature != cur_signature_live))
|
||||
return (r->signature == cur_signature_live) ? -incomparable * 8 : 0;
|
||||
if (unlikely(r->signature != cur_signature_live))
|
||||
return (l->signature == cur_signature_live) ? incomparable * 8 : 0;
|
||||
|
||||
if (unlikely(l->clc != r->clc)) {
|
||||
if (l->txn->env != r->txn->env)
|
||||
return (l->txn->env > r->txn->env) ? incomparable * 7 : -incomparable * 7;
|
||||
if (l->txn->txnid != r->txn->txnid)
|
||||
return (l->txn->txnid > r->txn->txnid) ? incomparable * 6
|
||||
: -incomparable * 6;
|
||||
return (l->clc > r->clc) ? incomparable * 5 : -incomparable * 5;
|
||||
}
|
||||
assert(cursor_dbi(l) == cursor_dbi(r));
|
||||
|
||||
int diff = is_pointed(l) - is_pointed(r);
|
||||
if (unlikely(diff))
|
||||
return (diff > 0) ? incomparable * 4 : -incomparable * 4;
|
||||
if (unlikely(!is_pointed(l)))
|
||||
return 0;
|
||||
|
||||
intptr_t detent = (l->top <= r->top) ? l->top : r->top;
|
||||
for (intptr_t i = 0; i <= detent; ++i) {
|
||||
diff = l->ki[i] - r->ki[i];
|
||||
if (diff)
|
||||
return diff;
|
||||
}
|
||||
if (unlikely(l->top != r->top))
|
||||
return (l->top > r->top) ? incomparable * 3 : -incomparable * 3;
|
||||
|
||||
assert((l->subcur != nullptr) == (r->subcur != nullptr));
|
||||
if (unlikely((l->subcur != nullptr) != (r->subcur != nullptr)))
|
||||
return l->subcur ? incomparable * 2 : -incomparable * 2;
|
||||
if (ignore_multival || !l->subcur)
|
||||
return 0;
|
||||
|
||||
#if MDBX_DEBUG
|
||||
if (is_pointed(&l->subcur->cursor)) {
|
||||
const page_t *mp = l->pg[l->top];
|
||||
const node_t *node = page_node(mp, l->ki[l->top]);
|
||||
assert(node_flags(node) & N_DUPDATA);
|
||||
}
|
||||
if (is_pointed(&r->subcur->cursor)) {
|
||||
const page_t *mp = r->pg[r->top];
|
||||
const node_t *node = page_node(mp, r->ki[r->top]);
|
||||
assert(node_flags(node) & N_DUPDATA);
|
||||
}
|
||||
#endif /* MDBX_DEBUG */
|
||||
|
||||
l = &l->subcur->cursor;
|
||||
r = &r->subcur->cursor;
|
||||
diff = is_pointed(l) - is_pointed(r);
|
||||
if (unlikely(diff))
|
||||
return (diff > 0) ? incomparable * 2 : -incomparable * 2;
|
||||
if (unlikely(!is_pointed(l)))
|
||||
return 0;
|
||||
|
||||
detent = (l->top <= r->top) ? l->top : r->top;
|
||||
for (intptr_t i = 0; i <= detent; ++i) {
|
||||
diff = l->ki[i] - r->ki[i];
|
||||
if (diff)
|
||||
return diff;
|
||||
}
|
||||
if (unlikely(l->top != r->top))
|
||||
return (l->top > r->top) ? incomparable : -incomparable;
|
||||
|
||||
return (l->flags & z_eof_hard) - (r->flags & z_eof_hard);
|
||||
}
|
||||
|
||||
/* Return the count of duplicate data items for the current key */
|
||||
int mdbx_cursor_count(const MDBX_cursor *mc, size_t *countp) {
|
||||
if (unlikely(mc == nullptr))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(mc->signature != cur_signature_live))
|
||||
return (mc->signature == cur_signature_ready4dispose) ? MDBX_EINVAL
|
||||
: MDBX_EBADSIGN;
|
||||
|
||||
int rc = check_txn(mc->txn, MDBX_TXN_BLOCKED);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
if (unlikely(countp == nullptr))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if ((*countp = is_filled(mc)) > 0) {
|
||||
if (!inner_hollow(mc)) {
|
||||
const page_t *mp = mc->pg[mc->top];
|
||||
const node_t *node = page_node(mp, mc->ki[mc->top]);
|
||||
cASSERT(mc, node_flags(node) & N_DUPDATA);
|
||||
*countp = unlikely(mc->subcur->nested_tree.items > PTRDIFF_MAX)
|
||||
? PTRDIFF_MAX
|
||||
: (size_t)mc->subcur->nested_tree.items;
|
||||
}
|
||||
}
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
int mdbx_cursor_on_first(const MDBX_cursor *mc) {
|
||||
if (unlikely(mc == nullptr))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(mc->signature != cur_signature_live))
|
||||
return (mc->signature == cur_signature_ready4dispose) ? MDBX_EINVAL
|
||||
: MDBX_EBADSIGN;
|
||||
|
||||
for (intptr_t i = 0; i <= mc->top; ++i) {
|
||||
if (mc->ki[i])
|
||||
return MDBX_RESULT_FALSE;
|
||||
}
|
||||
|
||||
return MDBX_RESULT_TRUE;
|
||||
}
|
||||
|
||||
int mdbx_cursor_on_first_dup(const MDBX_cursor *mc) {
|
||||
if (unlikely(mc == nullptr))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(mc->signature != cur_signature_live))
|
||||
return (mc->signature == cur_signature_ready4dispose) ? MDBX_EINVAL
|
||||
: MDBX_EBADSIGN;
|
||||
|
||||
if (is_filled(mc) && mc->subcur) {
|
||||
mc = &mc->subcur->cursor;
|
||||
for (intptr_t i = 0; i <= mc->top; ++i) {
|
||||
if (mc->ki[i])
|
||||
return MDBX_RESULT_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return MDBX_RESULT_TRUE;
|
||||
}
|
||||
|
||||
int mdbx_cursor_on_last(const MDBX_cursor *mc) {
|
||||
if (unlikely(mc == nullptr))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(mc->signature != cur_signature_live))
|
||||
return (mc->signature == cur_signature_ready4dispose) ? MDBX_EINVAL
|
||||
: MDBX_EBADSIGN;
|
||||
|
||||
for (intptr_t i = 0; i <= mc->top; ++i) {
|
||||
size_t nkeys = page_numkeys(mc->pg[i]);
|
||||
if (mc->ki[i] < nkeys - 1)
|
||||
return MDBX_RESULT_FALSE;
|
||||
}
|
||||
|
||||
return MDBX_RESULT_TRUE;
|
||||
}
|
||||
|
||||
int mdbx_cursor_on_last_dup(const MDBX_cursor *mc) {
|
||||
if (unlikely(mc == nullptr))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(mc->signature != cur_signature_live))
|
||||
return (mc->signature == cur_signature_ready4dispose) ? MDBX_EINVAL
|
||||
: MDBX_EBADSIGN;
|
||||
|
||||
if (is_filled(mc) && mc->subcur) {
|
||||
mc = &mc->subcur->cursor;
|
||||
for (intptr_t i = 0; i <= mc->top; ++i) {
|
||||
size_t nkeys = page_numkeys(mc->pg[i]);
|
||||
if (mc->ki[i] < nkeys - 1)
|
||||
return MDBX_RESULT_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return MDBX_RESULT_TRUE;
|
||||
}
|
||||
|
||||
int mdbx_cursor_eof(const MDBX_cursor *mc) {
|
||||
if (unlikely(mc == nullptr))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(mc->signature != cur_signature_live))
|
||||
return (mc->signature == cur_signature_ready4dispose) ? MDBX_EINVAL
|
||||
: MDBX_EBADSIGN;
|
||||
|
||||
return is_eof(mc) ? MDBX_RESULT_TRUE : MDBX_RESULT_FALSE;
|
||||
}
|
||||
|
||||
int mdbx_cursor_get(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data,
|
||||
MDBX_cursor_op op) {
|
||||
if (unlikely(mc == nullptr))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(mc->signature != cur_signature_live))
|
||||
return (mc->signature == cur_signature_ready4dispose) ? MDBX_EINVAL
|
||||
: MDBX_EBADSIGN;
|
||||
|
||||
int rc = check_txn(mc->txn, MDBX_TXN_BLOCKED);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
if (unlikely(cursor_dbi_changed(mc)))
|
||||
return MDBX_BAD_DBI;
|
||||
|
||||
return cursor_ops(mc, key, data, op);
|
||||
}
|
||||
|
||||
__hot static int scan_confinue(MDBX_cursor *mc, MDBX_predicate_func *predicate,
|
||||
void *context, void *arg, MDBX_val *key,
|
||||
MDBX_val *value, MDBX_cursor_op turn_op) {
|
||||
int rc;
|
||||
switch (turn_op) {
|
||||
case MDBX_NEXT:
|
||||
case MDBX_NEXT_NODUP:
|
||||
for (;;) {
|
||||
rc = predicate(context, key, value, arg);
|
||||
if (rc != MDBX_RESULT_FALSE)
|
||||
return rc;
|
||||
rc = outer_next(mc, key, value, turn_op);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return (rc == MDBX_NOTFOUND) ? MDBX_RESULT_FALSE : rc;
|
||||
}
|
||||
|
||||
case MDBX_PREV:
|
||||
case MDBX_PREV_NODUP:
|
||||
for (;;) {
|
||||
rc = predicate(context, key, value, arg);
|
||||
if (rc != MDBX_RESULT_FALSE)
|
||||
return rc;
|
||||
rc = outer_prev(mc, key, value, turn_op);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return (rc == MDBX_NOTFOUND) ? MDBX_RESULT_FALSE : rc;
|
||||
}
|
||||
|
||||
case MDBX_NEXT_DUP:
|
||||
if (mc->subcur)
|
||||
for (;;) {
|
||||
rc = predicate(context, key, value, arg);
|
||||
if (rc != MDBX_RESULT_FALSE)
|
||||
return rc;
|
||||
rc = inner_next(&mc->subcur->cursor, value);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return (rc == MDBX_NOTFOUND) ? MDBX_RESULT_FALSE : rc;
|
||||
}
|
||||
return MDBX_NOTFOUND;
|
||||
|
||||
case MDBX_PREV_DUP:
|
||||
if (mc->subcur)
|
||||
for (;;) {
|
||||
rc = predicate(context, key, value, arg);
|
||||
if (rc != MDBX_RESULT_FALSE)
|
||||
return rc;
|
||||
rc = inner_prev(&mc->subcur->cursor, value);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return (rc == MDBX_NOTFOUND) ? MDBX_RESULT_FALSE : rc;
|
||||
}
|
||||
return MDBX_NOTFOUND;
|
||||
|
||||
default:
|
||||
for (;;) {
|
||||
rc = predicate(context, key, value, arg);
|
||||
if (rc != MDBX_RESULT_FALSE)
|
||||
return rc;
|
||||
rc = cursor_ops(mc, key, value, turn_op);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return (rc == MDBX_NOTFOUND) ? MDBX_RESULT_FALSE : rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int mdbx_cursor_scan(MDBX_cursor *mc, MDBX_predicate_func *predicate,
|
||||
void *context, MDBX_cursor_op start_op,
|
||||
MDBX_cursor_op turn_op, void *arg) {
|
||||
if (unlikely(!predicate))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
const unsigned valid_start_mask =
|
||||
1 << MDBX_FIRST | 1 << MDBX_FIRST_DUP | 1 << MDBX_LAST |
|
||||
1 << MDBX_LAST_DUP | 1 << MDBX_GET_CURRENT | 1 << MDBX_GET_MULTIPLE;
|
||||
if (unlikely(start_op > 30 || ((1 << start_op) & valid_start_mask) == 0))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
const unsigned valid_turn_mask =
|
||||
1 << MDBX_NEXT | 1 << MDBX_NEXT_DUP | 1 << MDBX_NEXT_NODUP |
|
||||
1 << MDBX_PREV | 1 << MDBX_PREV_DUP | 1 << MDBX_PREV_NODUP |
|
||||
1 << MDBX_NEXT_MULTIPLE | 1 << MDBX_PREV_MULTIPLE;
|
||||
if (unlikely(turn_op > 30 || ((1 << turn_op) & valid_turn_mask) == 0))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
MDBX_val key = {nullptr, 0}, value = {nullptr, 0};
|
||||
int rc = mdbx_cursor_get(mc, &key, &value, start_op);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
return scan_confinue(mc, predicate, context, arg, &key, &value, turn_op);
|
||||
}
|
||||
|
||||
int mdbx_cursor_scan_from(MDBX_cursor *mc, MDBX_predicate_func *predicate,
|
||||
void *context, MDBX_cursor_op from_op, MDBX_val *key,
|
||||
MDBX_val *value, MDBX_cursor_op turn_op, void *arg) {
|
||||
if (unlikely(!predicate || !key))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
const unsigned valid_start_mask =
|
||||
1 << MDBX_GET_BOTH | 1 << MDBX_GET_BOTH_RANGE | 1 << MDBX_SET_KEY |
|
||||
1 << MDBX_GET_MULTIPLE | 1 << MDBX_SET_LOWERBOUND |
|
||||
1 << MDBX_SET_UPPERBOUND;
|
||||
if (unlikely(from_op < MDBX_TO_KEY_LESSER_THAN &&
|
||||
((1 << from_op) & valid_start_mask) == 0))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
const unsigned valid_turn_mask =
|
||||
1 << MDBX_NEXT | 1 << MDBX_NEXT_DUP | 1 << MDBX_NEXT_NODUP |
|
||||
1 << MDBX_PREV | 1 << MDBX_PREV_DUP | 1 << MDBX_PREV_NODUP |
|
||||
1 << MDBX_NEXT_MULTIPLE | 1 << MDBX_PREV_MULTIPLE;
|
||||
if (unlikely(turn_op > 30 || ((1 << turn_op) & valid_turn_mask) == 0))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
int rc = mdbx_cursor_get(mc, key, value, from_op);
|
||||
if (unlikely(MDBX_IS_ERROR(rc)))
|
||||
return rc;
|
||||
|
||||
cASSERT(mc, key != nullptr);
|
||||
MDBX_val stub;
|
||||
if (!value) {
|
||||
value = &stub;
|
||||
rc = cursor_ops(mc, key, value, MDBX_GET_CURRENT);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
}
|
||||
return scan_confinue(mc, predicate, context, arg, key, value, turn_op);
|
||||
}
|
||||
|
||||
int mdbx_cursor_get_batch(MDBX_cursor *mc, size_t *count, MDBX_val *pairs,
|
||||
size_t limit, MDBX_cursor_op op) {
|
||||
if (unlikely(!count))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
*count = 0;
|
||||
if (unlikely(mc == nullptr || limit < 4 || limit > INTPTR_MAX - 2))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(mc->signature != cur_signature_live))
|
||||
return (mc->signature == cur_signature_ready4dispose) ? MDBX_EINVAL
|
||||
: MDBX_EBADSIGN;
|
||||
|
||||
int rc = check_txn(mc->txn, MDBX_TXN_BLOCKED);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
if (unlikely(cursor_dbi_changed(mc)))
|
||||
return MDBX_BAD_DBI;
|
||||
|
||||
if (unlikely(mc->subcur))
|
||||
return MDBX_INCOMPATIBLE /* must be a non-dupsort subDB */;
|
||||
|
||||
switch (op) {
|
||||
case MDBX_NEXT:
|
||||
if (unlikely(is_eof(mc)))
|
||||
return is_pointed(mc) ? MDBX_NOTFOUND : MDBX_ENODATA;
|
||||
break;
|
||||
|
||||
case MDBX_FIRST:
|
||||
if (!is_filled(mc)) {
|
||||
rc = outer_first(mc, nullptr, nullptr);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG("unhandled/unimplemented cursor operation %u", op);
|
||||
return MDBX_EINVAL;
|
||||
}
|
||||
|
||||
const page_t *mp = mc->pg[mc->top];
|
||||
size_t nkeys = page_numkeys(mp);
|
||||
size_t ki = mc->ki[mc->top];
|
||||
size_t n = 0;
|
||||
while (n + 2 <= limit) {
|
||||
cASSERT(mc, ki < nkeys);
|
||||
if (unlikely(ki >= nkeys))
|
||||
goto sibling;
|
||||
|
||||
const node_t *leaf = page_node(mp, ki);
|
||||
pairs[n] = get_key(leaf);
|
||||
rc = node_read(mc, leaf, &pairs[n + 1], mp);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
goto bailout;
|
||||
|
||||
n += 2;
|
||||
if (++ki == nkeys) {
|
||||
sibling:
|
||||
rc = cursor_sibling_right(mc);
|
||||
if (rc != MDBX_SUCCESS) {
|
||||
if (rc == MDBX_NOTFOUND)
|
||||
rc = MDBX_RESULT_TRUE;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
mp = mc->pg[mc->top];
|
||||
DEBUG("next page is %" PRIaPGNO ", key index %u", mp->pgno,
|
||||
mc->ki[mc->top]);
|
||||
if (!MDBX_DISABLE_VALIDATION && unlikely(!check_leaf_type(mc, mp))) {
|
||||
ERROR("unexpected leaf-page #%" PRIaPGNO " type 0x%x seen by cursor",
|
||||
mp->pgno, mp->flags);
|
||||
rc = MDBX_CORRUPTED;
|
||||
goto bailout;
|
||||
}
|
||||
nkeys = page_numkeys(mp);
|
||||
ki = 0;
|
||||
}
|
||||
}
|
||||
mc->ki[mc->top] = (indx_t)ki;
|
||||
|
||||
bailout:
|
||||
*count = n;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
int mdbx_cursor_set_userctx(MDBX_cursor *mc, void *ctx) {
|
||||
if (unlikely(!mc))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(mc->signature != cur_signature_ready4dispose &&
|
||||
mc->signature != cur_signature_live))
|
||||
return MDBX_EBADSIGN;
|
||||
|
||||
cursor_couple_t *couple = container_of(mc, cursor_couple_t, outer);
|
||||
couple->userctx = ctx;
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
void *mdbx_cursor_get_userctx(const MDBX_cursor *mc) {
|
||||
if (unlikely(!mc))
|
||||
return nullptr;
|
||||
|
||||
if (unlikely(mc->signature != cur_signature_ready4dispose &&
|
||||
mc->signature != cur_signature_live))
|
||||
return nullptr;
|
||||
|
||||
cursor_couple_t *couple = container_of(mc, cursor_couple_t, outer);
|
||||
return couple->userctx;
|
||||
}
|
||||
|
||||
MDBX_txn *mdbx_cursor_txn(const MDBX_cursor *mc) {
|
||||
if (unlikely(!mc || mc->signature != cur_signature_live))
|
||||
return nullptr;
|
||||
MDBX_txn *txn = mc->txn;
|
||||
if (unlikely(!txn || txn->signature != txn_signature))
|
||||
return nullptr;
|
||||
if (unlikely(txn->flags & MDBX_TXN_FINISHED))
|
||||
return nullptr;
|
||||
return txn;
|
||||
}
|
||||
|
||||
MDBX_dbi mdbx_cursor_dbi(const MDBX_cursor *mc) {
|
||||
if (unlikely(!mc || mc->signature != cur_signature_live))
|
||||
return UINT_MAX;
|
||||
return cursor_dbi(mc);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
|
||||
MDBX_put_flags_t flags) {
|
||||
if (unlikely(mc == nullptr || key == nullptr || data == nullptr))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(mc->signature != cur_signature_live))
|
||||
return (mc->signature == cur_signature_ready4dispose) ? MDBX_EINVAL
|
||||
: MDBX_EBADSIGN;
|
||||
|
||||
int rc = check_txn_rw(mc->txn, MDBX_TXN_BLOCKED);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
if (unlikely(cursor_dbi_changed(mc)))
|
||||
return MDBX_BAD_DBI;
|
||||
|
||||
cASSERT(mc, cursor_is_tracked(mc));
|
||||
|
||||
/* Check this first so counter will always be zero on any early failures. */
|
||||
if (unlikely(flags & MDBX_MULTIPLE)) {
|
||||
if (unlikely(flags & MDBX_RESERVE))
|
||||
return MDBX_EINVAL;
|
||||
if (unlikely(!(mc->tree->flags & MDBX_DUPFIXED)))
|
||||
return MDBX_INCOMPATIBLE;
|
||||
const size_t dcount = data[1].iov_len;
|
||||
if (unlikely(dcount < 2 || data->iov_len == 0))
|
||||
return MDBX_BAD_VALSIZE;
|
||||
if (unlikely(mc->tree->dupfix_size != data->iov_len) &&
|
||||
mc->tree->dupfix_size)
|
||||
return MDBX_BAD_VALSIZE;
|
||||
if (unlikely(dcount >
|
||||
MAX_MAPSIZE / 2 /
|
||||
(BRANCH_NODE_MAX(MDBX_MAX_PAGESIZE) - NODESIZE))) {
|
||||
/* checking for multiplication overflow */
|
||||
if (unlikely(dcount > MAX_MAPSIZE / 2 / data->iov_len))
|
||||
return MDBX_TOO_LARGE;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & MDBX_RESERVE) {
|
||||
if (unlikely(mc->tree->flags & (MDBX_DUPSORT | MDBX_REVERSEDUP |
|
||||
MDBX_INTEGERDUP | MDBX_DUPFIXED)))
|
||||
return MDBX_INCOMPATIBLE;
|
||||
data->iov_base = nullptr;
|
||||
}
|
||||
|
||||
if (unlikely(mc->txn->flags & (MDBX_TXN_RDONLY | MDBX_TXN_BLOCKED)))
|
||||
return (mc->txn->flags & MDBX_TXN_RDONLY) ? MDBX_EACCESS : MDBX_BAD_TXN;
|
||||
|
||||
return cursor_put_checklen(mc, key, data, flags);
|
||||
}
|
||||
|
||||
int mdbx_cursor_del(MDBX_cursor *mc, MDBX_put_flags_t flags) {
|
||||
if (unlikely(!mc))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(mc->signature != cur_signature_live))
|
||||
return (mc->signature == cur_signature_ready4dispose) ? MDBX_EINVAL
|
||||
: MDBX_EBADSIGN;
|
||||
|
||||
int rc = check_txn_rw(mc->txn, MDBX_TXN_BLOCKED);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
if (unlikely(cursor_dbi_changed(mc)))
|
||||
return MDBX_BAD_DBI;
|
||||
|
||||
return cursor_del(mc, flags);
|
||||
}
|
||||
|
||||
__cold int mdbx_cursor_ignord(MDBX_cursor *mc) {
|
||||
if (unlikely(!mc))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(mc->signature != cur_signature_live))
|
||||
return (mc->signature == cur_signature_ready4dispose) ? MDBX_EINVAL
|
||||
: MDBX_EBADSIGN;
|
||||
|
||||
mc->checking |= z_ignord;
|
||||
if (mc->subcur)
|
||||
mc->subcur->cursor.checking |= z_ignord;
|
||||
|
||||
return MDBX_SUCCESS;
|
||||
}
|
1399
src/api-env.c
Normal file
1399
src/api-env.c
Normal file
File diff suppressed because it is too large
Load Diff
117
src/api-extra.c
Normal file
117
src/api-extra.c
Normal file
@ -0,0 +1,117 @@
|
||||
/// \copyright SPDX-License-Identifier: Apache-2.0
|
||||
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
|
||||
|
||||
#include "internals.h"
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
* Readers API */
|
||||
|
||||
__cold int mdbx_reader_list(const MDBX_env *env, MDBX_reader_list_func *func,
|
||||
void *ctx) {
|
||||
int rc = check_env(env, true);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
if (unlikely(!func))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
rc = MDBX_RESULT_TRUE;
|
||||
int serial = 0;
|
||||
lck_t *const lck = env->lck_mmap.lck;
|
||||
if (likely(lck)) {
|
||||
const size_t snap_nreaders =
|
||||
atomic_load32(&lck->rdt_length, mo_AcquireRelease);
|
||||
for (size_t i = 0; i < snap_nreaders; i++) {
|
||||
const reader_slot_t *r = lck->rdt + i;
|
||||
retry_reader:;
|
||||
const uint32_t pid = atomic_load32(&r->pid, mo_AcquireRelease);
|
||||
if (!pid)
|
||||
continue;
|
||||
txnid_t txnid = safe64_read(&r->txnid);
|
||||
const uint64_t tid = atomic_load64(&r->tid, mo_Relaxed);
|
||||
const pgno_t pages_used =
|
||||
atomic_load32(&r->snapshot_pages_used, mo_Relaxed);
|
||||
const uint64_t reader_pages_retired =
|
||||
atomic_load64(&r->snapshot_pages_retired, mo_Relaxed);
|
||||
if (unlikely(txnid != safe64_read(&r->txnid) ||
|
||||
pid != atomic_load32(&r->pid, mo_AcquireRelease) ||
|
||||
tid != atomic_load64(&r->tid, mo_Relaxed) ||
|
||||
pages_used !=
|
||||
atomic_load32(&r->snapshot_pages_used, mo_Relaxed) ||
|
||||
reader_pages_retired !=
|
||||
atomic_load64(&r->snapshot_pages_retired, mo_Relaxed)))
|
||||
goto retry_reader;
|
||||
|
||||
eASSERT(env, txnid > 0);
|
||||
if (txnid >= SAFE64_INVALID_THRESHOLD)
|
||||
txnid = 0;
|
||||
|
||||
size_t bytes_used = 0;
|
||||
size_t bytes_retained = 0;
|
||||
uint64_t lag = 0;
|
||||
if (txnid) {
|
||||
troika_t troika = meta_tap(env);
|
||||
retry_header:;
|
||||
const meta_ptr_t head = meta_recent(env, &troika);
|
||||
const uint64_t head_pages_retired =
|
||||
unaligned_peek_u64_volatile(4, head.ptr_v->pages_retired);
|
||||
if (unlikely(meta_should_retry(env, &troika) ||
|
||||
head_pages_retired != unaligned_peek_u64_volatile(
|
||||
4, head.ptr_v->pages_retired)))
|
||||
goto retry_header;
|
||||
|
||||
lag = (head.txnid - txnid) / xMDBX_TXNID_STEP;
|
||||
bytes_used = pgno2bytes(env, pages_used);
|
||||
bytes_retained = (head_pages_retired > reader_pages_retired)
|
||||
? pgno2bytes(env, (pgno_t)(head_pages_retired -
|
||||
reader_pages_retired))
|
||||
: 0;
|
||||
}
|
||||
rc = func(ctx, ++serial, (unsigned)i, pid, (mdbx_tid_t)((intptr_t)tid),
|
||||
txnid, lag, bytes_used, bytes_retained);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
__cold int mdbx_reader_check(MDBX_env *env, int *dead) {
|
||||
if (dead)
|
||||
*dead = 0;
|
||||
return mvcc_cleanup_dead(env, false, dead);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
* Locking API */
|
||||
|
||||
int mdbx_txn_lock(MDBX_env *env, bool dont_wait) {
|
||||
int rc = check_env(env, true);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
if (unlikely(env->flags & MDBX_RDONLY))
|
||||
return MDBX_EACCESS;
|
||||
if (unlikely(env->basal_txn->owner ||
|
||||
(env->basal_txn->flags & MDBX_TXN_FINISHED) == 0))
|
||||
return MDBX_BUSY;
|
||||
|
||||
return lck_txn_lock(env, dont_wait);
|
||||
}
|
||||
|
||||
int mdbx_txn_unlock(MDBX_env *env) {
|
||||
int rc = check_env(env, true);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
if (unlikely(env->flags & MDBX_RDONLY))
|
||||
return MDBX_EACCESS;
|
||||
if (unlikely(env->basal_txn->owner != osal_thread_self()))
|
||||
return MDBX_THREAD_MISMATCH;
|
||||
if (unlikely((env->basal_txn->flags & MDBX_TXN_FINISHED) == 0))
|
||||
return MDBX_BUSY;
|
||||
|
||||
lck_txn_unlock(env);
|
||||
return MDBX_SUCCESS;
|
||||
}
|
225
src/api-key-transform.c
Normal file
225
src/api-key-transform.c
Normal file
@ -0,0 +1,225 @@
|
||||
/// \copyright SPDX-License-Identifier: Apache-2.0
|
||||
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
|
||||
|
||||
#include "internals.h"
|
||||
|
||||
static inline double key2double(const int64_t key) {
|
||||
union {
|
||||
uint64_t u;
|
||||
double f;
|
||||
} casting;
|
||||
|
||||
casting.u = (key < 0) ? key + UINT64_C(0x8000000000000000)
|
||||
: UINT64_C(0xffffFFFFffffFFFF) - key;
|
||||
return casting.f;
|
||||
}
|
||||
|
||||
static inline uint64_t double2key(const double *const ptr) {
|
||||
STATIC_ASSERT(sizeof(double) == sizeof(int64_t));
|
||||
const int64_t i = *(const int64_t *)ptr;
|
||||
const uint64_t u = (i < 0) ? UINT64_C(0xffffFFFFffffFFFF) - i
|
||||
: i + UINT64_C(0x8000000000000000);
|
||||
if (ASSERT_ENABLED()) {
|
||||
const double f = key2double(u);
|
||||
assert(memcmp(&f, ptr, sizeof(double)) == 0);
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
static inline float key2float(const int32_t key) {
|
||||
union {
|
||||
uint32_t u;
|
||||
float f;
|
||||
} casting;
|
||||
|
||||
casting.u =
|
||||
(key < 0) ? key + UINT32_C(0x80000000) : UINT32_C(0xffffFFFF) - key;
|
||||
return casting.f;
|
||||
}
|
||||
|
||||
static inline uint32_t float2key(const float *const ptr) {
|
||||
STATIC_ASSERT(sizeof(float) == sizeof(int32_t));
|
||||
const int32_t i = *(const int32_t *)ptr;
|
||||
const uint32_t u =
|
||||
(i < 0) ? UINT32_C(0xffffFFFF) - i : i + UINT32_C(0x80000000);
|
||||
if (ASSERT_ENABLED()) {
|
||||
const float f = key2float(u);
|
||||
assert(memcmp(&f, ptr, sizeof(float)) == 0);
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
uint64_t mdbx_key_from_double(const double ieee754_64bit) {
|
||||
return double2key(&ieee754_64bit);
|
||||
}
|
||||
|
||||
uint64_t mdbx_key_from_ptrdouble(const double *const ieee754_64bit) {
|
||||
return double2key(ieee754_64bit);
|
||||
}
|
||||
|
||||
uint32_t mdbx_key_from_float(const float ieee754_32bit) {
|
||||
return float2key(&ieee754_32bit);
|
||||
}
|
||||
|
||||
uint32_t mdbx_key_from_ptrfloat(const float *const ieee754_32bit) {
|
||||
return float2key(ieee754_32bit);
|
||||
}
|
||||
|
||||
#define IEEE754_DOUBLE_MANTISSA_SIZE 52
|
||||
#define IEEE754_DOUBLE_EXPONENTA_BIAS 0x3FF
|
||||
#define IEEE754_DOUBLE_EXPONENTA_MAX 0x7FF
|
||||
#define IEEE754_DOUBLE_IMPLICIT_LEAD UINT64_C(0x0010000000000000)
|
||||
#define IEEE754_DOUBLE_MANTISSA_MASK UINT64_C(0x000FFFFFFFFFFFFF)
|
||||
#define IEEE754_DOUBLE_MANTISSA_AMAX UINT64_C(0x001FFFFFFFFFFFFF)
|
||||
|
||||
static inline int clz64(uint64_t value) {
|
||||
#if __GNUC_PREREQ(4, 1) || __has_builtin(__builtin_clzl)
|
||||
if (sizeof(value) == sizeof(int))
|
||||
return __builtin_clz(value);
|
||||
if (sizeof(value) == sizeof(long))
|
||||
return __builtin_clzl(value);
|
||||
#if (defined(__SIZEOF_LONG_LONG__) && __SIZEOF_LONG_LONG__ == 8) || \
|
||||
__has_builtin(__builtin_clzll)
|
||||
return __builtin_clzll(value);
|
||||
#endif /* have(long long) && long long == uint64_t */
|
||||
#endif /* GNU C */
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
unsigned long index;
|
||||
#if defined(_M_AMD64) || defined(_M_ARM64) || defined(_M_X64)
|
||||
_BitScanReverse64(&index, value);
|
||||
return 63 - index;
|
||||
#else
|
||||
if (value > UINT32_MAX) {
|
||||
_BitScanReverse(&index, (uint32_t)(value >> 32));
|
||||
return 31 - index;
|
||||
}
|
||||
_BitScanReverse(&index, (uint32_t)value);
|
||||
return 63 - index;
|
||||
#endif
|
||||
#endif /* MSVC */
|
||||
|
||||
value |= value >> 1;
|
||||
value |= value >> 2;
|
||||
value |= value >> 4;
|
||||
value |= value >> 8;
|
||||
value |= value >> 16;
|
||||
value |= value >> 32;
|
||||
static const uint8_t debruijn_clz64[64] = {
|
||||
63, 16, 62, 7, 15, 36, 61, 3, 6, 14, 22, 26, 35, 47, 60, 2,
|
||||
9, 5, 28, 11, 13, 21, 42, 19, 25, 31, 34, 40, 46, 52, 59, 1,
|
||||
17, 8, 37, 4, 23, 27, 48, 10, 29, 12, 43, 20, 32, 41, 53, 18,
|
||||
38, 24, 49, 30, 44, 33, 54, 39, 50, 45, 55, 51, 56, 57, 58, 0};
|
||||
return debruijn_clz64[value * UINT64_C(0x03F79D71B4CB0A89) >> 58];
|
||||
}
|
||||
|
||||
static inline uint64_t round_mantissa(const uint64_t u64, int shift) {
|
||||
assert(shift < 0 && u64 > 0);
|
||||
shift = -shift;
|
||||
const unsigned half = 1 << (shift - 1);
|
||||
const unsigned lsb = 1 & (unsigned)(u64 >> shift);
|
||||
const unsigned tie2even = 1 ^ lsb;
|
||||
return (u64 + half - tie2even) >> shift;
|
||||
}
|
||||
|
||||
uint64_t mdbx_key_from_jsonInteger(const int64_t json_integer) {
|
||||
const uint64_t bias = UINT64_C(0x8000000000000000);
|
||||
if (json_integer > 0) {
|
||||
const uint64_t u64 = json_integer;
|
||||
int shift = clz64(u64) - (64 - IEEE754_DOUBLE_MANTISSA_SIZE - 1);
|
||||
uint64_t mantissa = u64 << shift;
|
||||
if (unlikely(shift < 0)) {
|
||||
mantissa = round_mantissa(u64, shift);
|
||||
if (mantissa > IEEE754_DOUBLE_MANTISSA_AMAX)
|
||||
mantissa = round_mantissa(u64, --shift);
|
||||
}
|
||||
|
||||
assert(mantissa >= IEEE754_DOUBLE_IMPLICIT_LEAD &&
|
||||
mantissa <= IEEE754_DOUBLE_MANTISSA_AMAX);
|
||||
const uint64_t exponent = (uint64_t)IEEE754_DOUBLE_EXPONENTA_BIAS +
|
||||
IEEE754_DOUBLE_MANTISSA_SIZE - shift;
|
||||
assert(exponent > 0 && exponent <= IEEE754_DOUBLE_EXPONENTA_MAX);
|
||||
const uint64_t key = bias + (exponent << IEEE754_DOUBLE_MANTISSA_SIZE) +
|
||||
(mantissa - IEEE754_DOUBLE_IMPLICIT_LEAD);
|
||||
#if !defined(_MSC_VER) || \
|
||||
defined( \
|
||||
_DEBUG) /* Workaround for MSVC error LNK2019: unresolved external \
|
||||
symbol __except1 referenced in function __ftol3_except */
|
||||
assert(key == mdbx_key_from_double((double)json_integer));
|
||||
#endif /* Workaround for MSVC */
|
||||
return key;
|
||||
}
|
||||
|
||||
if (json_integer < 0) {
|
||||
const uint64_t u64 = -json_integer;
|
||||
int shift = clz64(u64) - (64 - IEEE754_DOUBLE_MANTISSA_SIZE - 1);
|
||||
uint64_t mantissa = u64 << shift;
|
||||
if (unlikely(shift < 0)) {
|
||||
mantissa = round_mantissa(u64, shift);
|
||||
if (mantissa > IEEE754_DOUBLE_MANTISSA_AMAX)
|
||||
mantissa = round_mantissa(u64, --shift);
|
||||
}
|
||||
|
||||
assert(mantissa >= IEEE754_DOUBLE_IMPLICIT_LEAD &&
|
||||
mantissa <= IEEE754_DOUBLE_MANTISSA_AMAX);
|
||||
const uint64_t exponent = (uint64_t)IEEE754_DOUBLE_EXPONENTA_BIAS +
|
||||
IEEE754_DOUBLE_MANTISSA_SIZE - shift;
|
||||
assert(exponent > 0 && exponent <= IEEE754_DOUBLE_EXPONENTA_MAX);
|
||||
const uint64_t key = bias - 1 - (exponent << IEEE754_DOUBLE_MANTISSA_SIZE) -
|
||||
(mantissa - IEEE754_DOUBLE_IMPLICIT_LEAD);
|
||||
#if !defined(_MSC_VER) || \
|
||||
defined( \
|
||||
_DEBUG) /* Workaround for MSVC error LNK2019: unresolved external \
|
||||
symbol __except1 referenced in function __ftol3_except */
|
||||
assert(key == mdbx_key_from_double((double)json_integer));
|
||||
#endif /* Workaround for MSVC */
|
||||
return key;
|
||||
}
|
||||
|
||||
return bias;
|
||||
}
|
||||
|
||||
int64_t mdbx_jsonInteger_from_key(const MDBX_val v) {
|
||||
assert(v.iov_len == 8);
|
||||
const uint64_t key = unaligned_peek_u64(2, v.iov_base);
|
||||
const uint64_t bias = UINT64_C(0x8000000000000000);
|
||||
const uint64_t covalent = (key > bias) ? key - bias : bias - key - 1;
|
||||
const int shift = IEEE754_DOUBLE_EXPONENTA_BIAS + 63 -
|
||||
(IEEE754_DOUBLE_EXPONENTA_MAX &
|
||||
(int)(covalent >> IEEE754_DOUBLE_MANTISSA_SIZE));
|
||||
if (unlikely(shift < 1))
|
||||
return (key < bias) ? INT64_MIN : INT64_MAX;
|
||||
if (unlikely(shift > 63))
|
||||
return 0;
|
||||
|
||||
const uint64_t unscaled = ((covalent & IEEE754_DOUBLE_MANTISSA_MASK)
|
||||
<< (63 - IEEE754_DOUBLE_MANTISSA_SIZE)) +
|
||||
bias;
|
||||
const int64_t absolute = unscaled >> shift;
|
||||
const int64_t value = (key < bias) ? -absolute : absolute;
|
||||
assert(key == mdbx_key_from_jsonInteger(value) ||
|
||||
(mdbx_key_from_jsonInteger(value - 1) < key &&
|
||||
key < mdbx_key_from_jsonInteger(value + 1)));
|
||||
return value;
|
||||
}
|
||||
|
||||
double mdbx_double_from_key(const MDBX_val v) {
|
||||
assert(v.iov_len == 8);
|
||||
return key2double(unaligned_peek_u64(2, v.iov_base));
|
||||
}
|
||||
|
||||
float mdbx_float_from_key(const MDBX_val v) {
|
||||
assert(v.iov_len == 4);
|
||||
return key2float(unaligned_peek_u32(2, v.iov_base));
|
||||
}
|
||||
|
||||
int32_t mdbx_int32_from_key(const MDBX_val v) {
|
||||
assert(v.iov_len == 4);
|
||||
return (int32_t)(unaligned_peek_u32(2, v.iov_base) - UINT32_C(0x80000000));
|
||||
}
|
||||
|
||||
int64_t mdbx_int64_from_key(const MDBX_val v) {
|
||||
assert(v.iov_len == 8);
|
||||
return (int64_t)(unaligned_peek_u64(2, v.iov_base) -
|
||||
UINT64_C(0x8000000000000000));
|
||||
}
|
508
src/api-txn.c
Normal file
508
src/api-txn.c
Normal file
@ -0,0 +1,508 @@
|
||||
/// \copyright SPDX-License-Identifier: Apache-2.0
|
||||
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
|
||||
|
||||
#include "internals.h"
|
||||
|
||||
#ifdef __SANITIZE_THREAD__
|
||||
/* LY: avoid tsan-trap by txn, mm_last_pg and geo.first_unallocated */
|
||||
__attribute__((__no_sanitize_thread__, __noinline__))
|
||||
#endif
|
||||
int mdbx_txn_straggler(const MDBX_txn *txn, int *percent)
|
||||
{
|
||||
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return (rc > 0) ? -rc : rc;
|
||||
|
||||
MDBX_env *env = txn->env;
|
||||
if (unlikely((txn->flags & MDBX_TXN_RDONLY) == 0)) {
|
||||
if (percent)
|
||||
*percent = (int)((txn->geo.first_unallocated * UINT64_C(100) +
|
||||
txn->geo.end_pgno / 2) /
|
||||
txn->geo.end_pgno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
txnid_t lag;
|
||||
troika_t troika = meta_tap(env);
|
||||
do {
|
||||
const meta_ptr_t head = meta_recent(env, &troika);
|
||||
if (percent) {
|
||||
const pgno_t maxpg = head.ptr_v->geometry.now;
|
||||
*percent = (int)((head.ptr_v->geometry.first_unallocated * UINT64_C(100) +
|
||||
maxpg / 2) /
|
||||
maxpg);
|
||||
}
|
||||
lag = (head.txnid - txn->txnid) / xMDBX_TXNID_STEP;
|
||||
} while (unlikely(meta_should_retry(env, &troika)));
|
||||
|
||||
return (lag > INT_MAX) ? INT_MAX : (int)lag;
|
||||
}
|
||||
|
||||
__cold int mdbx_dbi_dupsort_depthmask(const MDBX_txn *txn, MDBX_dbi dbi,
|
||||
uint32_t *mask) {
|
||||
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
if (unlikely(!mask))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
cursor_couple_t cx;
|
||||
rc = cursor_init(&cx.outer, txn, dbi);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
if ((cx.outer.tree->flags & MDBX_DUPSORT) == 0)
|
||||
return MDBX_RESULT_TRUE;
|
||||
|
||||
MDBX_val key, data;
|
||||
rc = outer_first(&cx.outer, &key, &data);
|
||||
*mask = 0;
|
||||
while (rc == MDBX_SUCCESS) {
|
||||
const node_t *node =
|
||||
page_node(cx.outer.pg[cx.outer.top], cx.outer.ki[cx.outer.top]);
|
||||
const tree_t *db = node_data(node);
|
||||
const unsigned flags = node_flags(node);
|
||||
switch (flags) {
|
||||
case N_BIGDATA:
|
||||
case 0:
|
||||
/* single-value entry, deep = 0 */
|
||||
*mask |= 1 << 0;
|
||||
break;
|
||||
case N_DUPDATA:
|
||||
/* single sub-page, deep = 1 */
|
||||
*mask |= 1 << 1;
|
||||
break;
|
||||
case N_DUPDATA | N_SUBDATA:
|
||||
/* sub-tree */
|
||||
*mask |= 1 << UNALIGNED_PEEK_16(db, tree_t, height);
|
||||
break;
|
||||
default:
|
||||
ERROR("%s/%d: %s %u", "MDBX_CORRUPTED", MDBX_CORRUPTED,
|
||||
"invalid node-size", flags);
|
||||
return MDBX_CORRUPTED;
|
||||
}
|
||||
rc = outer_next(&cx.outer, &key, &data, MDBX_NEXT_NODUP);
|
||||
}
|
||||
|
||||
return (rc == MDBX_NOTFOUND) ? MDBX_SUCCESS : rc;
|
||||
}
|
||||
|
||||
int mdbx_canary_get(const MDBX_txn *txn, MDBX_canary *canary) {
|
||||
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
if (unlikely(canary == nullptr))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
*canary = txn->canary;
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
int mdbx_get(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
|
||||
MDBX_val *data) {
|
||||
DKBUF_DEBUG;
|
||||
DEBUG("===> get db %u key [%s]", dbi, DKEY_DEBUG(key));
|
||||
|
||||
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
if (unlikely(!key || !data))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
cursor_couple_t cx;
|
||||
rc = cursor_init(&cx.outer, txn, dbi);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
return cursor_seek(&cx.outer, (MDBX_val *)key, data, MDBX_SET).err;
|
||||
}
|
||||
|
||||
int mdbx_get_equal_or_great(const MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key,
|
||||
MDBX_val *data) {
|
||||
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
if (unlikely(!key || !data))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(txn->flags & MDBX_TXN_BLOCKED))
|
||||
return MDBX_BAD_TXN;
|
||||
|
||||
cursor_couple_t cx;
|
||||
rc = cursor_init(&cx.outer, txn, dbi);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
return cursor_ops(&cx.outer, key, data, MDBX_SET_LOWERBOUND);
|
||||
}
|
||||
|
||||
int mdbx_get_ex(const MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key,
|
||||
MDBX_val *data, size_t *values_count) {
|
||||
DKBUF_DEBUG;
|
||||
DEBUG("===> get db %u key [%s]", dbi, DKEY_DEBUG(key));
|
||||
|
||||
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
if (unlikely(!key || !data))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
cursor_couple_t cx;
|
||||
rc = cursor_init(&cx.outer, txn, dbi);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
rc = cursor_seek(&cx.outer, key, data, MDBX_SET_KEY).err;
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
if (values_count)
|
||||
*values_count = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (values_count) {
|
||||
*values_count = 1;
|
||||
if (inner_pointed(&cx.outer))
|
||||
*values_count =
|
||||
(sizeof(*values_count) >= sizeof(cx.inner.nested_tree.items) ||
|
||||
cx.inner.nested_tree.items <= PTRDIFF_MAX)
|
||||
? (size_t)cx.inner.nested_tree.items
|
||||
: PTRDIFF_MAX;
|
||||
}
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
int mdbx_canary_put(MDBX_txn *txn, const MDBX_canary *canary) {
|
||||
int rc = check_txn_rw(txn, MDBX_TXN_BLOCKED);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
if (likely(canary)) {
|
||||
if (txn->canary.x == canary->x && txn->canary.y == canary->y &&
|
||||
txn->canary.z == canary->z)
|
||||
return MDBX_SUCCESS;
|
||||
txn->canary.x = canary->x;
|
||||
txn->canary.y = canary->y;
|
||||
txn->canary.z = canary->z;
|
||||
}
|
||||
txn->canary.v = txn->txnid;
|
||||
txn->flags |= MDBX_TXN_DIRTY;
|
||||
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
/* Функция сообщает находится ли указанный адрес в "грязной" странице у
|
||||
* заданной пишущей транзакции. В конечном счете это позволяет избавиться от
|
||||
* лишнего копирования данных из НЕ-грязных страниц.
|
||||
*
|
||||
* "Грязные" страницы - это те, которые уже были изменены в ходе пишущей
|
||||
* транзакции. Соответственно, какие-либо дальнейшие изменения могут привести
|
||||
* к перезаписи таких страниц. Поэтому все функции, выполняющие изменения, в
|
||||
* качестве аргументов НЕ должны получать указатели на данные в таких
|
||||
* страницах. В свою очередь "НЕ грязные" страницы перед модификацией будут
|
||||
* скопированы.
|
||||
*
|
||||
* Другими словами, данные из "грязных" страниц должны быть либо скопированы
|
||||
* перед передачей в качестве аргументов для дальнейших модификаций, либо
|
||||
* отвергнуты на стадии проверки корректности аргументов.
|
||||
*
|
||||
* Таким образом, функция позволяет как избавится от лишнего копирования,
|
||||
* так и выполнить более полную проверку аргументов.
|
||||
*
|
||||
* ВАЖНО: Передаваемый указатель должен указывать на начало данных. Только
|
||||
* так гарантируется что актуальный заголовок страницы будет физически
|
||||
* расположен в той-же странице памяти, в том числе для многостраничных
|
||||
* P_LARGE страниц с длинными данными. */
|
||||
int mdbx_is_dirty(const MDBX_txn *txn, const void *ptr) {
|
||||
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
const MDBX_env *env = txn->env;
|
||||
const ptrdiff_t offset = ptr_dist(ptr, env->dxb_mmap.base);
|
||||
if (offset >= 0) {
|
||||
const pgno_t pgno = bytes2pgno(env, offset);
|
||||
if (likely(pgno < txn->geo.first_unallocated)) {
|
||||
const page_t *page = pgno2page(env, pgno);
|
||||
if (unlikely(page->pgno != pgno || (page->flags & P_ILL_BITS) != 0)) {
|
||||
/* The ptr pointed into middle of a large page,
|
||||
* not to the beginning of a data. */
|
||||
return MDBX_EINVAL;
|
||||
}
|
||||
return ((txn->flags & MDBX_TXN_RDONLY) || !is_modifable(txn, page))
|
||||
? MDBX_RESULT_FALSE
|
||||
: MDBX_RESULT_TRUE;
|
||||
}
|
||||
if ((size_t)offset < env->dxb_mmap.limit) {
|
||||
/* Указатель адресует что-то в пределах mmap, но за границей
|
||||
* распределенных страниц. Такое может случится если mdbx_is_dirty()
|
||||
* вызывается после операции, в ходе которой грязная страница была
|
||||
* возвращена в нераспределенное пространство. */
|
||||
return (txn->flags & MDBX_TXN_RDONLY) ? MDBX_EINVAL : MDBX_RESULT_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Страница вне используемого mmap-диапазона, т.е. либо в функцию был
|
||||
* передан некорректный адрес, либо адрес в теневой странице, которая была
|
||||
* выделена посредством malloc().
|
||||
*
|
||||
* Для режима MDBX_WRITE_MAP режима страница однозначно "не грязная",
|
||||
* а для режимов без MDBX_WRITE_MAP однозначно "не чистая". */
|
||||
return (txn->flags & (MDBX_WRITEMAP | MDBX_TXN_RDONLY)) ? MDBX_EINVAL
|
||||
: MDBX_RESULT_TRUE;
|
||||
}
|
||||
|
||||
int mdbx_del(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
|
||||
const MDBX_val *data) {
|
||||
int rc = check_txn_rw(txn, MDBX_TXN_BLOCKED);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
if (unlikely(!key))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(dbi <= FREE_DBI))
|
||||
return MDBX_BAD_DBI;
|
||||
|
||||
if (unlikely(txn->flags & (MDBX_TXN_RDONLY | MDBX_TXN_BLOCKED)))
|
||||
return (txn->flags & MDBX_TXN_RDONLY) ? MDBX_EACCESS : MDBX_BAD_TXN;
|
||||
|
||||
cursor_couple_t cx;
|
||||
rc = cursor_init(&cx.outer, txn, dbi);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
MDBX_val proxy;
|
||||
MDBX_cursor_op op = MDBX_SET;
|
||||
unsigned flags = MDBX_ALLDUPS;
|
||||
if (data) {
|
||||
proxy = *data;
|
||||
data = &proxy;
|
||||
op = MDBX_GET_BOTH;
|
||||
flags = 0;
|
||||
}
|
||||
rc = cursor_seek(&cx.outer, (MDBX_val *)key, (MDBX_val *)data, op).err;
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
cx.outer.next = txn->cursors[dbi];
|
||||
txn->cursors[dbi] = &cx.outer;
|
||||
rc = cursor_del(&cx.outer, flags);
|
||||
txn->cursors[dbi] = cx.outer.next;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int mdbx_put(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data,
|
||||
MDBX_put_flags_t flags) {
|
||||
int rc = check_txn_rw(txn, MDBX_TXN_BLOCKED);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
if (unlikely(!key || !data))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(dbi <= FREE_DBI))
|
||||
return MDBX_BAD_DBI;
|
||||
|
||||
if (unlikely(flags & ~(MDBX_NOOVERWRITE | MDBX_NODUPDATA | MDBX_ALLDUPS |
|
||||
MDBX_ALLDUPS | MDBX_RESERVE | MDBX_APPEND |
|
||||
MDBX_APPENDDUP | MDBX_CURRENT | MDBX_MULTIPLE)))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(txn->flags & (MDBX_TXN_RDONLY | MDBX_TXN_BLOCKED)))
|
||||
return (txn->flags & MDBX_TXN_RDONLY) ? MDBX_EACCESS : MDBX_BAD_TXN;
|
||||
|
||||
cursor_couple_t cx;
|
||||
rc = cursor_init(&cx.outer, txn, dbi);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
cx.outer.next = txn->cursors[dbi];
|
||||
txn->cursors[dbi] = &cx.outer;
|
||||
|
||||
/* LY: support for update (explicit overwrite) */
|
||||
if (flags & MDBX_CURRENT) {
|
||||
rc = cursor_seek(&cx.outer, (MDBX_val *)key, nullptr, MDBX_SET).err;
|
||||
if (likely(rc == MDBX_SUCCESS) && (txn->dbs[dbi].flags & MDBX_DUPSORT) &&
|
||||
(flags & MDBX_ALLDUPS) == 0) {
|
||||
/* LY: allows update (explicit overwrite) only for unique keys */
|
||||
node_t *node =
|
||||
page_node(cx.outer.pg[cx.outer.top], cx.outer.ki[cx.outer.top]);
|
||||
if (node_flags(node) & N_DUPDATA) {
|
||||
tASSERT(txn, inner_pointed(&cx.outer) &&
|
||||
cx.outer.subcur->nested_tree.items > 1);
|
||||
rc = MDBX_EMULTIVAL;
|
||||
if ((flags & MDBX_NOOVERWRITE) == 0) {
|
||||
flags -= MDBX_CURRENT;
|
||||
rc = cursor_del(&cx.outer, MDBX_ALLDUPS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (likely(rc == MDBX_SUCCESS))
|
||||
rc = cursor_put_checklen(&cx.outer, key, data, flags);
|
||||
txn->cursors[dbi] = cx.outer.next;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/* Позволяет обновить или удалить существующую запись с получением
|
||||
* в old_data предыдущего значения данных. При этом если new_data равен
|
||||
* нулю, то выполняется удаление, иначе обновление/вставка.
|
||||
*
|
||||
* Текущее значение может находиться в уже измененной (грязной) странице.
|
||||
* В этом случае страница будет перезаписана при обновлении, а само старое
|
||||
* значение утрачено. Поэтому исходно в old_data должен быть передан
|
||||
* дополнительный буфер для копирования старого значения.
|
||||
* Если переданный буфер слишком мал, то функция вернет -1, установив
|
||||
* old_data->iov_len в соответствующее значение.
|
||||
*
|
||||
* Для не-уникальных ключей также возможен второй сценарий использования,
|
||||
* когда посредством old_data из записей с одинаковым ключом для
|
||||
* удаления/обновления выбирается конкретная. Для выбора этого сценария
|
||||
* во flags следует одновременно указать MDBX_CURRENT и MDBX_NOOVERWRITE.
|
||||
* Именно эта комбинация выбрана, так как она лишена смысла, и этим позволяет
|
||||
* идентифицировать запрос такого сценария.
|
||||
*
|
||||
* Функция может быть замещена соответствующими операциями с курсорами
|
||||
* после двух доработок (TODO):
|
||||
* - внешняя аллокация курсоров, в том числе на стеке (без malloc).
|
||||
* - получения dirty-статуса страницы по адресу (знать о MUTABLE/WRITEABLE).
|
||||
*/
|
||||
|
||||
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))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(old_data->iov_base == nullptr && old_data->iov_len))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(new_data == nullptr &&
|
||||
(flags & (MDBX_CURRENT | MDBX_RESERVE)) != MDBX_CURRENT))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(dbi <= FREE_DBI))
|
||||
return MDBX_BAD_DBI;
|
||||
|
||||
if (unlikely(flags &
|
||||
~(MDBX_NOOVERWRITE | MDBX_NODUPDATA | MDBX_ALLDUPS |
|
||||
MDBX_RESERVE | MDBX_APPEND | MDBX_APPENDDUP | MDBX_CURRENT)))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
cursor_couple_t cx;
|
||||
rc = cursor_init(&cx.outer, txn, dbi);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
cx.outer.next = txn->cursors[dbi];
|
||||
txn->cursors[dbi] = &cx.outer;
|
||||
|
||||
MDBX_val present_key = *key;
|
||||
if (F_ISSET(flags, MDBX_CURRENT | MDBX_NOOVERWRITE)) {
|
||||
/* в old_data значение для выбора конкретного дубликата */
|
||||
if (unlikely(!(txn->dbs[dbi].flags & MDBX_DUPSORT))) {
|
||||
rc = MDBX_EINVAL;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
/* убираем лишний бит, он был признаком запрошенного режима */
|
||||
flags -= MDBX_NOOVERWRITE;
|
||||
|
||||
rc = cursor_seek(&cx.outer, &present_key, old_data, MDBX_GET_BOTH).err;
|
||||
if (rc != MDBX_SUCCESS)
|
||||
goto bailout;
|
||||
} else {
|
||||
/* в old_data буфер для сохранения предыдущего значения */
|
||||
if (unlikely(new_data && old_data->iov_base == new_data->iov_base))
|
||||
return MDBX_EINVAL;
|
||||
MDBX_val present_data;
|
||||
rc = cursor_seek(&cx.outer, &present_key, &present_data, MDBX_SET_KEY).err;
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
old_data->iov_base = nullptr;
|
||||
old_data->iov_len = 0;
|
||||
if (rc != MDBX_NOTFOUND || (flags & MDBX_CURRENT))
|
||||
goto bailout;
|
||||
} else if (flags & MDBX_NOOVERWRITE) {
|
||||
rc = MDBX_KEYEXIST;
|
||||
*old_data = present_data;
|
||||
goto bailout;
|
||||
} else {
|
||||
page_t *page = cx.outer.pg[cx.outer.top];
|
||||
if (txn->dbs[dbi].flags & MDBX_DUPSORT) {
|
||||
if (flags & MDBX_CURRENT) {
|
||||
/* disallow update/delete for multi-values */
|
||||
node_t *node = page_node(page, cx.outer.ki[cx.outer.top]);
|
||||
if (node_flags(node) & N_DUPDATA) {
|
||||
tASSERT(txn, inner_pointed(&cx.outer) &&
|
||||
cx.outer.subcur->nested_tree.items > 1);
|
||||
if (cx.outer.subcur->nested_tree.items > 1) {
|
||||
rc = MDBX_EMULTIVAL;
|
||||
goto bailout;
|
||||
}
|
||||
}
|
||||
/* В LMDB флажок MDBX_CURRENT здесь приведет
|
||||
* к замене данных без учета MDBX_DUPSORT сортировки,
|
||||
* но здесь это в любом случае допустимо, так как мы
|
||||
* проверили что для ключа есть только одно значение. */
|
||||
}
|
||||
}
|
||||
|
||||
if (is_modifable(txn, page)) {
|
||||
if (new_data && cmp_lenfast(&present_data, new_data) == 0) {
|
||||
/* если данные совпадают, то ничего делать не надо */
|
||||
*old_data = *new_data;
|
||||
goto bailout;
|
||||
}
|
||||
rc = preserver ? preserver(preserver_context, old_data,
|
||||
present_data.iov_base, present_data.iov_len)
|
||||
: MDBX_SUCCESS;
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
goto bailout;
|
||||
} else {
|
||||
*old_data = present_data;
|
||||
}
|
||||
flags |= MDBX_CURRENT;
|
||||
}
|
||||
}
|
||||
|
||||
if (likely(new_data))
|
||||
rc = cursor_put_checklen(&cx.outer, key, new_data, flags);
|
||||
else
|
||||
rc = cursor_del(&cx.outer, flags & MDBX_ALLDUPS);
|
||||
|
||||
bailout:
|
||||
txn->cursors[dbi] = cx.outer.next;
|
||||
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);
|
||||
}
|
390
src/atomics-ops.h
Normal file
390
src/atomics-ops.h
Normal file
@ -0,0 +1,390 @@
|
||||
/// \copyright SPDX-License-Identifier: Apache-2.0
|
||||
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "essentials.h"
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#ifdef MDBX_HAVE_C11ATOMICS
|
||||
#define osal_memory_fence(order, write) \
|
||||
atomic_thread_fence((write) ? mo_c11_store(order) : mo_c11_load(order))
|
||||
#else /* MDBX_HAVE_C11ATOMICS */
|
||||
#define osal_memory_fence(order, write) \
|
||||
do { \
|
||||
osal_compiler_barrier(); \
|
||||
if (write && order > (MDBX_CPU_WRITEBACK_INCOHERENT ? mo_Relaxed \
|
||||
: mo_AcquireRelease)) \
|
||||
osal_memory_barrier(); \
|
||||
} while (0)
|
||||
#endif /* MDBX_HAVE_C11ATOMICS */
|
||||
|
||||
#if defined(MDBX_HAVE_C11ATOMICS) && defined(__LCC__)
|
||||
#define atomic_store32(p, value, order) \
|
||||
({ \
|
||||
const uint32_t value_to_store = (value); \
|
||||
atomic_store_explicit(MDBX_c11a_rw(uint32_t, p), value_to_store, \
|
||||
mo_c11_store(order)); \
|
||||
value_to_store; \
|
||||
})
|
||||
#define atomic_load32(p, order) \
|
||||
atomic_load_explicit(MDBX_c11a_ro(uint32_t, p), mo_c11_load(order))
|
||||
#define atomic_store64(p, value, order) \
|
||||
({ \
|
||||
const uint64_t value_to_store = (value); \
|
||||
atomic_store_explicit(MDBX_c11a_rw(uint64_t, p), value_to_store, \
|
||||
mo_c11_store(order)); \
|
||||
value_to_store; \
|
||||
})
|
||||
#define atomic_load64(p, order) \
|
||||
atomic_load_explicit(MDBX_c11a_ro(uint64_t, p), mo_c11_load(order))
|
||||
#endif /* LCC && MDBX_HAVE_C11ATOMICS */
|
||||
|
||||
#ifndef atomic_store32
|
||||
MDBX_MAYBE_UNUSED static __always_inline uint32_t
|
||||
atomic_store32(mdbx_atomic_uint32_t *p, const uint32_t value,
|
||||
enum mdbx_memory_order order) {
|
||||
STATIC_ASSERT(sizeof(mdbx_atomic_uint32_t) == 4);
|
||||
#ifdef MDBX_HAVE_C11ATOMICS
|
||||
assert(atomic_is_lock_free(MDBX_c11a_rw(uint32_t, p)));
|
||||
atomic_store_explicit(MDBX_c11a_rw(uint32_t, p), value, mo_c11_store(order));
|
||||
#else /* MDBX_HAVE_C11ATOMICS */
|
||||
if (order != mo_Relaxed)
|
||||
osal_compiler_barrier();
|
||||
p->weak = value;
|
||||
osal_memory_fence(order, true);
|
||||
#endif /* MDBX_HAVE_C11ATOMICS */
|
||||
return value;
|
||||
}
|
||||
#endif /* atomic_store32 */
|
||||
|
||||
#ifndef atomic_load32
|
||||
MDBX_MAYBE_UNUSED static __always_inline uint32_t atomic_load32(
|
||||
const volatile mdbx_atomic_uint32_t *p, enum mdbx_memory_order order) {
|
||||
STATIC_ASSERT(sizeof(mdbx_atomic_uint32_t) == 4);
|
||||
#ifdef MDBX_HAVE_C11ATOMICS
|
||||
assert(atomic_is_lock_free(MDBX_c11a_ro(uint32_t, p)));
|
||||
return atomic_load_explicit(MDBX_c11a_ro(uint32_t, p), mo_c11_load(order));
|
||||
#else /* MDBX_HAVE_C11ATOMICS */
|
||||
osal_memory_fence(order, false);
|
||||
const uint32_t value = p->weak;
|
||||
if (order != mo_Relaxed)
|
||||
osal_compiler_barrier();
|
||||
return value;
|
||||
#endif /* MDBX_HAVE_C11ATOMICS */
|
||||
}
|
||||
#endif /* atomic_load32 */
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
* safe read/write volatile 64-bit fields on 32-bit architectures. */
|
||||
|
||||
/* LY: for testing non-atomic 64-bit txnid on 32-bit arches.
|
||||
* #define xMDBX_TXNID_STEP (UINT32_MAX / 3) */
|
||||
#ifndef xMDBX_TXNID_STEP
|
||||
#if MDBX_64BIT_CAS
|
||||
#define xMDBX_TXNID_STEP 1u
|
||||
#else
|
||||
#define xMDBX_TXNID_STEP 2u
|
||||
#endif
|
||||
#endif /* xMDBX_TXNID_STEP */
|
||||
|
||||
#ifndef atomic_store64
|
||||
MDBX_MAYBE_UNUSED static __always_inline uint64_t
|
||||
atomic_store64(mdbx_atomic_uint64_t *p, const uint64_t value,
|
||||
enum mdbx_memory_order order) {
|
||||
STATIC_ASSERT(sizeof(mdbx_atomic_uint64_t) == 8);
|
||||
#if MDBX_64BIT_ATOMIC
|
||||
#if __GNUC_PREREQ(11, 0)
|
||||
STATIC_ASSERT(__alignof__(mdbx_atomic_uint64_t) >= sizeof(uint64_t));
|
||||
#endif /* GNU C >= 11 */
|
||||
#ifdef MDBX_HAVE_C11ATOMICS
|
||||
assert(atomic_is_lock_free(MDBX_c11a_rw(uint64_t, p)));
|
||||
atomic_store_explicit(MDBX_c11a_rw(uint64_t, p), value, mo_c11_store(order));
|
||||
#else /* MDBX_HAVE_C11ATOMICS */
|
||||
if (order != mo_Relaxed)
|
||||
osal_compiler_barrier();
|
||||
p->weak = value;
|
||||
osal_memory_fence(order, true);
|
||||
#endif /* MDBX_HAVE_C11ATOMICS */
|
||||
#else /* !MDBX_64BIT_ATOMIC */
|
||||
osal_compiler_barrier();
|
||||
atomic_store32(&p->low, (uint32_t)value, mo_Relaxed);
|
||||
jitter4testing(true);
|
||||
atomic_store32(&p->high, (uint32_t)(value >> 32), order);
|
||||
jitter4testing(true);
|
||||
#endif /* !MDBX_64BIT_ATOMIC */
|
||||
return value;
|
||||
}
|
||||
#endif /* atomic_store64 */
|
||||
|
||||
#ifndef atomic_load64
|
||||
MDBX_MAYBE_UNUSED static
|
||||
#if MDBX_64BIT_ATOMIC
|
||||
__always_inline
|
||||
#endif /* MDBX_64BIT_ATOMIC */
|
||||
uint64_t
|
||||
atomic_load64(const volatile mdbx_atomic_uint64_t *p,
|
||||
enum mdbx_memory_order order) {
|
||||
STATIC_ASSERT(sizeof(mdbx_atomic_uint64_t) == 8);
|
||||
#if MDBX_64BIT_ATOMIC
|
||||
#ifdef MDBX_HAVE_C11ATOMICS
|
||||
assert(atomic_is_lock_free(MDBX_c11a_ro(uint64_t, p)));
|
||||
return atomic_load_explicit(MDBX_c11a_ro(uint64_t, p), mo_c11_load(order));
|
||||
#else /* MDBX_HAVE_C11ATOMICS */
|
||||
osal_memory_fence(order, false);
|
||||
const uint64_t value = p->weak;
|
||||
if (order != mo_Relaxed)
|
||||
osal_compiler_barrier();
|
||||
return value;
|
||||
#endif /* MDBX_HAVE_C11ATOMICS */
|
||||
#else /* !MDBX_64BIT_ATOMIC */
|
||||
osal_compiler_barrier();
|
||||
uint64_t value = (uint64_t)atomic_load32(&p->high, order) << 32;
|
||||
jitter4testing(true);
|
||||
value |= atomic_load32(&p->low, (order == mo_Relaxed) ? mo_Relaxed
|
||||
: mo_AcquireRelease);
|
||||
jitter4testing(true);
|
||||
for (;;) {
|
||||
osal_compiler_barrier();
|
||||
uint64_t again = (uint64_t)atomic_load32(&p->high, order) << 32;
|
||||
jitter4testing(true);
|
||||
again |= atomic_load32(&p->low, (order == mo_Relaxed) ? mo_Relaxed
|
||||
: mo_AcquireRelease);
|
||||
jitter4testing(true);
|
||||
if (likely(value == again))
|
||||
return value;
|
||||
value = again;
|
||||
}
|
||||
#endif /* !MDBX_64BIT_ATOMIC */
|
||||
}
|
||||
#endif /* atomic_load64 */
|
||||
|
||||
MDBX_MAYBE_UNUSED static __always_inline void atomic_yield(void) {
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
YieldProcessor();
|
||||
#elif defined(__ia32__) || defined(__e2k__)
|
||||
__builtin_ia32_pause();
|
||||
#elif defined(__ia64__)
|
||||
#if defined(__HP_cc__) || defined(__HP_aCC__)
|
||||
_Asm_hint(_HINT_PAUSE);
|
||||
#else
|
||||
__asm__ __volatile__("hint @pause");
|
||||
#endif
|
||||
#elif defined(__aarch64__) || (defined(__ARM_ARCH) && __ARM_ARCH > 6) || \
|
||||
defined(__ARM_ARCH_6K__)
|
||||
#ifdef __CC_ARM
|
||||
__yield();
|
||||
#else
|
||||
__asm__ __volatile__("yield");
|
||||
#endif
|
||||
#elif (defined(__mips64) || defined(__mips64__)) && defined(__mips_isa_rev) && \
|
||||
__mips_isa_rev >= 2
|
||||
__asm__ __volatile__("pause");
|
||||
#elif defined(__mips) || defined(__mips__) || defined(__mips64) || \
|
||||
defined(__mips64__) || defined(_M_MRX000) || defined(_MIPS_) || \
|
||||
defined(__MWERKS__) || defined(__sgi)
|
||||
__asm__ __volatile__(".word 0x00000140");
|
||||
#elif defined(__linux__) || defined(__gnu_linux__) || defined(_UNIX03_SOURCE)
|
||||
sched_yield();
|
||||
#elif (defined(_GNU_SOURCE) && __GLIBC_PREREQ(2, 1)) || defined(_OPEN_THREADS)
|
||||
pthread_yield();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if MDBX_64BIT_CAS
|
||||
MDBX_MAYBE_UNUSED static __always_inline bool
|
||||
atomic_cas64(mdbx_atomic_uint64_t *p, uint64_t c, uint64_t v) {
|
||||
#ifdef MDBX_HAVE_C11ATOMICS
|
||||
STATIC_ASSERT(sizeof(long long) >= sizeof(uint64_t));
|
||||
assert(atomic_is_lock_free(MDBX_c11a_rw(uint64_t, p)));
|
||||
return atomic_compare_exchange_strong(MDBX_c11a_rw(uint64_t, p), &c, v);
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
return __sync_bool_compare_and_swap(&p->weak, c, v);
|
||||
#elif defined(_MSC_VER)
|
||||
return c == (uint64_t)_InterlockedCompareExchange64(
|
||||
(volatile __int64 *)&p->weak, v, c);
|
||||
#elif defined(__APPLE__)
|
||||
return OSAtomicCompareAndSwap64Barrier(c, v, &p->weak);
|
||||
#else
|
||||
#error FIXME: Unsupported compiler
|
||||
#endif
|
||||
}
|
||||
#endif /* MDBX_64BIT_CAS */
|
||||
|
||||
MDBX_MAYBE_UNUSED static __always_inline bool
|
||||
atomic_cas32(mdbx_atomic_uint32_t *p, uint32_t c, uint32_t v) {
|
||||
#ifdef MDBX_HAVE_C11ATOMICS
|
||||
STATIC_ASSERT(sizeof(int) >= sizeof(uint32_t));
|
||||
assert(atomic_is_lock_free(MDBX_c11a_rw(uint32_t, p)));
|
||||
return atomic_compare_exchange_strong(MDBX_c11a_rw(uint32_t, p), &c, v);
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
return __sync_bool_compare_and_swap(&p->weak, c, v);
|
||||
#elif defined(_MSC_VER)
|
||||
STATIC_ASSERT(sizeof(volatile long) == sizeof(volatile uint32_t));
|
||||
return c ==
|
||||
(uint32_t)_InterlockedCompareExchange((volatile long *)&p->weak, v, c);
|
||||
#elif defined(__APPLE__)
|
||||
return OSAtomicCompareAndSwap32Barrier(c, v, &p->weak);
|
||||
#else
|
||||
#error FIXME: Unsupported compiler
|
||||
#endif
|
||||
}
|
||||
|
||||
MDBX_MAYBE_UNUSED static __always_inline uint32_t
|
||||
atomic_add32(mdbx_atomic_uint32_t *p, uint32_t v) {
|
||||
#ifdef MDBX_HAVE_C11ATOMICS
|
||||
STATIC_ASSERT(sizeof(int) >= sizeof(uint32_t));
|
||||
assert(atomic_is_lock_free(MDBX_c11a_rw(uint32_t, p)));
|
||||
return atomic_fetch_add(MDBX_c11a_rw(uint32_t, p), v);
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
return __sync_fetch_and_add(&p->weak, v);
|
||||
#elif defined(_MSC_VER)
|
||||
STATIC_ASSERT(sizeof(volatile long) == sizeof(volatile uint32_t));
|
||||
return (uint32_t)_InterlockedExchangeAdd((volatile long *)&p->weak, v);
|
||||
#elif defined(__APPLE__)
|
||||
return OSAtomicAdd32Barrier(v, &p->weak);
|
||||
#else
|
||||
#error FIXME: Unsupported compiler
|
||||
#endif
|
||||
}
|
||||
|
||||
#define atomic_sub32(p, v) atomic_add32(p, 0 - (v))
|
||||
|
||||
MDBX_MAYBE_UNUSED static __always_inline uint64_t
|
||||
safe64_txnid_next(uint64_t txnid) {
|
||||
txnid += xMDBX_TXNID_STEP;
|
||||
#if !MDBX_64BIT_CAS
|
||||
/* avoid overflow of low-part in safe64_reset() */
|
||||
txnid += (UINT32_MAX == (uint32_t)txnid);
|
||||
#endif
|
||||
return txnid;
|
||||
}
|
||||
|
||||
/* Atomically make target value >= SAFE64_INVALID_THRESHOLD */
|
||||
MDBX_MAYBE_UNUSED static __always_inline void
|
||||
safe64_reset(mdbx_atomic_uint64_t *p, bool single_writer) {
|
||||
if (single_writer) {
|
||||
#if MDBX_64BIT_ATOMIC && MDBX_WORDBITS >= 64
|
||||
atomic_store64(p, UINT64_MAX, mo_AcquireRelease);
|
||||
#else
|
||||
atomic_store32(&p->high, UINT32_MAX, mo_AcquireRelease);
|
||||
#endif /* MDBX_64BIT_ATOMIC && MDBX_WORDBITS >= 64 */
|
||||
} else {
|
||||
#if MDBX_64BIT_CAS && MDBX_64BIT_ATOMIC
|
||||
/* atomically make value >= SAFE64_INVALID_THRESHOLD by 64-bit operation */
|
||||
atomic_store64(p, UINT64_MAX, mo_AcquireRelease);
|
||||
#elif MDBX_64BIT_CAS
|
||||
/* atomically make value >= SAFE64_INVALID_THRESHOLD by 32-bit operation */
|
||||
atomic_store32(&p->high, UINT32_MAX, mo_AcquireRelease);
|
||||
#else
|
||||
/* it is safe to increment low-part to avoid ABA, since xMDBX_TXNID_STEP > 1
|
||||
* and overflow was preserved in safe64_txnid_next() */
|
||||
STATIC_ASSERT(xMDBX_TXNID_STEP > 1);
|
||||
atomic_add32(&p->low, 1) /* avoid ABA in safe64_reset_compare() */;
|
||||
atomic_store32(&p->high, UINT32_MAX, mo_AcquireRelease);
|
||||
atomic_add32(&p->low, 1) /* avoid ABA in safe64_reset_compare() */;
|
||||
#endif /* MDBX_64BIT_CAS && MDBX_64BIT_ATOMIC */
|
||||
}
|
||||
assert(p->weak >= SAFE64_INVALID_THRESHOLD);
|
||||
jitter4testing(true);
|
||||
}
|
||||
|
||||
MDBX_MAYBE_UNUSED static __always_inline bool
|
||||
safe64_reset_compare(mdbx_atomic_uint64_t *p, uint64_t compare) {
|
||||
/* LY: This function is used to reset `txnid` from hsr-handler in case
|
||||
* the asynchronously cancellation of read transaction. Therefore,
|
||||
* there may be a collision between the cleanup performed here and
|
||||
* asynchronous termination and restarting of the read transaction
|
||||
* in another process/thread. In general we MUST NOT reset the `txnid`
|
||||
* if a new transaction was started (i.e. if `txnid` was changed). */
|
||||
#if MDBX_64BIT_CAS
|
||||
bool rc = atomic_cas64(p, compare, UINT64_MAX);
|
||||
#else
|
||||
/* LY: There is no gold ratio here since shared mutex is too costly,
|
||||
* in such way we must acquire/release it for every update of txnid,
|
||||
* i.e. twice for each read transaction). */
|
||||
bool rc = false;
|
||||
if (likely(atomic_load32(&p->low, mo_AcquireRelease) == (uint32_t)compare &&
|
||||
atomic_cas32(&p->high, (uint32_t)(compare >> 32), UINT32_MAX))) {
|
||||
if (unlikely(atomic_load32(&p->low, mo_AcquireRelease) !=
|
||||
(uint32_t)compare))
|
||||
atomic_cas32(&p->high, UINT32_MAX, (uint32_t)(compare >> 32));
|
||||
else
|
||||
rc = true;
|
||||
}
|
||||
#endif /* MDBX_64BIT_CAS */
|
||||
jitter4testing(true);
|
||||
return rc;
|
||||
}
|
||||
|
||||
MDBX_MAYBE_UNUSED static __always_inline void
|
||||
safe64_write(mdbx_atomic_uint64_t *p, const uint64_t v) {
|
||||
assert(p->weak >= SAFE64_INVALID_THRESHOLD);
|
||||
#if MDBX_64BIT_ATOMIC && MDBX_64BIT_CAS
|
||||
atomic_store64(p, v, mo_AcquireRelease);
|
||||
#else /* MDBX_64BIT_ATOMIC */
|
||||
osal_compiler_barrier();
|
||||
/* update low-part but still value >= SAFE64_INVALID_THRESHOLD */
|
||||
atomic_store32(&p->low, (uint32_t)v, mo_Relaxed);
|
||||
assert(p->weak >= SAFE64_INVALID_THRESHOLD);
|
||||
jitter4testing(true);
|
||||
/* update high-part from SAFE64_INVALID_THRESHOLD to actual value */
|
||||
atomic_store32(&p->high, (uint32_t)(v >> 32), mo_AcquireRelease);
|
||||
#endif /* MDBX_64BIT_ATOMIC */
|
||||
assert(p->weak == v);
|
||||
jitter4testing(true);
|
||||
}
|
||||
|
||||
MDBX_MAYBE_UNUSED static __always_inline uint64_t
|
||||
safe64_read(const mdbx_atomic_uint64_t *p) {
|
||||
jitter4testing(true);
|
||||
uint64_t v;
|
||||
do
|
||||
v = atomic_load64(p, mo_AcquireRelease);
|
||||
while (!MDBX_64BIT_ATOMIC && unlikely(v != p->weak));
|
||||
return v;
|
||||
}
|
||||
|
||||
#if 0 /* unused for now */
|
||||
MDBX_MAYBE_UNUSED static __always_inline bool safe64_is_valid(uint64_t v) {
|
||||
#if MDBX_WORDBITS >= 64
|
||||
return v < SAFE64_INVALID_THRESHOLD;
|
||||
#else
|
||||
return (v >> 32) != UINT32_MAX;
|
||||
#endif /* MDBX_WORDBITS */
|
||||
}
|
||||
|
||||
MDBX_MAYBE_UNUSED static __always_inline bool
|
||||
safe64_is_valid_ptr(const mdbx_atomic_uint64_t *p) {
|
||||
#if MDBX_64BIT_ATOMIC
|
||||
return atomic_load64(p, mo_AcquireRelease) < SAFE64_INVALID_THRESHOLD;
|
||||
#else
|
||||
return atomic_load32(&p->high, mo_AcquireRelease) != UINT32_MAX;
|
||||
#endif /* MDBX_64BIT_ATOMIC */
|
||||
}
|
||||
#endif /* unused for now */
|
||||
|
||||
/* non-atomic write with safety for reading a half-updated value */
|
||||
MDBX_MAYBE_UNUSED static __always_inline void
|
||||
safe64_update(mdbx_atomic_uint64_t *p, const uint64_t v) {
|
||||
#if MDBX_64BIT_ATOMIC
|
||||
atomic_store64(p, v, mo_Relaxed);
|
||||
#else
|
||||
safe64_reset(p, true);
|
||||
safe64_write(p, v);
|
||||
#endif /* MDBX_64BIT_ATOMIC */
|
||||
}
|
||||
|
||||
/* non-atomic increment with safety for reading a half-updated value */
|
||||
MDBX_MAYBE_UNUSED static
|
||||
#if MDBX_64BIT_ATOMIC
|
||||
__always_inline
|
||||
#endif /* MDBX_64BIT_ATOMIC */
|
||||
void
|
||||
safe64_inc(mdbx_atomic_uint64_t *p, const uint64_t v) {
|
||||
assert(v > 0);
|
||||
safe64_update(p, safe64_read(p) + v);
|
||||
}
|
||||
|
||||
#endif /* !__cplusplus */
|
99
src/atomics-types.h
Normal file
99
src/atomics-types.h
Normal file
@ -0,0 +1,99 @@
|
||||
/// \copyright SPDX-License-Identifier: Apache-2.0
|
||||
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "essentials.h"
|
||||
|
||||
#ifndef MDBX_64BIT_ATOMIC
|
||||
#error "The MDBX_64BIT_ATOMIC must be defined before"
|
||||
#endif /* MDBX_64BIT_ATOMIC */
|
||||
|
||||
#ifndef MDBX_64BIT_CAS
|
||||
#error "The MDBX_64BIT_CAS must be defined before"
|
||||
#endif /* MDBX_64BIT_CAS */
|
||||
|
||||
#if defined(__cplusplus) && !defined(__STDC_NO_ATOMICS__) && __has_include(<cstdatomic>)
|
||||
#include <cstdatomic>
|
||||
#define MDBX_HAVE_C11ATOMICS
|
||||
#elif !defined(__cplusplus) && \
|
||||
(__STDC_VERSION__ >= 201112L || __has_extension(c_atomic)) && \
|
||||
!defined(__STDC_NO_ATOMICS__) && \
|
||||
(__GNUC_PREREQ(4, 9) || __CLANG_PREREQ(3, 8) || \
|
||||
!(defined(__GNUC__) || defined(__clang__)))
|
||||
#include <stdatomic.h>
|
||||
#define MDBX_HAVE_C11ATOMICS
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning(disable : 4163) /* 'xyz': not available as an intrinsic */
|
||||
#pragma warning(disable : 4133) /* 'function': incompatible types - from \
|
||||
'size_t' to 'LONGLONG' */
|
||||
#pragma warning(disable : 4244) /* 'return': conversion from 'LONGLONG' to \
|
||||
'std::size_t', possible loss of data */
|
||||
#pragma warning(disable : 4267) /* 'function': conversion from 'size_t' to \
|
||||
'long', possible loss of data */
|
||||
#pragma intrinsic(_InterlockedExchangeAdd, _InterlockedCompareExchange)
|
||||
#pragma intrinsic(_InterlockedExchangeAdd64, _InterlockedCompareExchange64)
|
||||
#elif defined(__APPLE__)
|
||||
#include <libkern/OSAtomic.h>
|
||||
#else
|
||||
#error FIXME atomic-ops
|
||||
#endif
|
||||
|
||||
typedef enum mdbx_memory_order {
|
||||
mo_Relaxed,
|
||||
mo_AcquireRelease
|
||||
/* , mo_SequentialConsistency */
|
||||
} mdbx_memory_order_t;
|
||||
|
||||
typedef union {
|
||||
volatile uint32_t weak;
|
||||
#ifdef MDBX_HAVE_C11ATOMICS
|
||||
volatile _Atomic uint32_t c11a;
|
||||
#endif /* MDBX_HAVE_C11ATOMICS */
|
||||
} mdbx_atomic_uint32_t;
|
||||
|
||||
typedef union {
|
||||
volatile uint64_t weak;
|
||||
#if defined(MDBX_HAVE_C11ATOMICS) && (MDBX_64BIT_CAS || MDBX_64BIT_ATOMIC)
|
||||
volatile _Atomic uint64_t c11a;
|
||||
#endif
|
||||
#if !defined(MDBX_HAVE_C11ATOMICS) || !MDBX_64BIT_CAS || !MDBX_64BIT_ATOMIC
|
||||
__anonymous_struct_extension__ struct {
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
mdbx_atomic_uint32_t low, high;
|
||||
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
mdbx_atomic_uint32_t high, low;
|
||||
#else
|
||||
#error "FIXME: Unsupported byte order"
|
||||
#endif /* __BYTE_ORDER__ */
|
||||
};
|
||||
#endif
|
||||
} mdbx_atomic_uint64_t;
|
||||
|
||||
#ifdef MDBX_HAVE_C11ATOMICS
|
||||
|
||||
/* Crutches for C11 atomic compiler's bugs */
|
||||
#if defined(__e2k__) && defined(__LCC__) && __LCC__ < /* FIXME */ 127
|
||||
#define MDBX_c11a_ro(type, ptr) (&(ptr)->weak)
|
||||
#define MDBX_c11a_rw(type, ptr) (&(ptr)->weak)
|
||||
#elif defined(__clang__) && __clang__ < 8
|
||||
#define MDBX_c11a_ro(type, ptr) ((volatile _Atomic(type) *)&(ptr)->c11a)
|
||||
#define MDBX_c11a_rw(type, ptr) (&(ptr)->c11a)
|
||||
#else
|
||||
#define MDBX_c11a_ro(type, ptr) (&(ptr)->c11a)
|
||||
#define MDBX_c11a_rw(type, ptr) (&(ptr)->c11a)
|
||||
#endif /* Crutches for C11 atomic compiler's bugs */
|
||||
|
||||
#define mo_c11_store(fence) \
|
||||
(((fence) == mo_Relaxed) ? memory_order_relaxed \
|
||||
: ((fence) == mo_AcquireRelease) ? memory_order_release \
|
||||
: memory_order_seq_cst)
|
||||
#define mo_c11_load(fence) \
|
||||
(((fence) == mo_Relaxed) ? memory_order_relaxed \
|
||||
: ((fence) == mo_AcquireRelease) ? memory_order_acquire \
|
||||
: memory_order_seq_cst)
|
||||
|
||||
#endif /* MDBX_HAVE_C11ATOMICS */
|
||||
|
||||
#define SAFE64_INVALID_THRESHOLD UINT64_C(0xffffFFFF00000000)
|
164
src/audit.c
Normal file
164
src/audit.c
Normal file
@ -0,0 +1,164 @@
|
||||
/// \copyright SPDX-License-Identifier: Apache-2.0
|
||||
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
|
||||
|
||||
#include "internals.h"
|
||||
|
||||
__cold static tree_t *audit_db_dig(const MDBX_txn *txn, const size_t dbi,
|
||||
tree_t *fallback) {
|
||||
const MDBX_txn *dig = txn;
|
||||
do {
|
||||
tASSERT(txn, txn->n_dbi == dig->n_dbi);
|
||||
const uint8_t state = dbi_state(dig, dbi);
|
||||
if (state & DBI_LINDO)
|
||||
switch (state & (DBI_VALID | DBI_STALE | DBI_OLDEN)) {
|
||||
case DBI_VALID:
|
||||
case DBI_OLDEN:
|
||||
return dig->dbs + dbi;
|
||||
case 0:
|
||||
return nullptr;
|
||||
case DBI_VALID | DBI_STALE:
|
||||
case DBI_OLDEN | DBI_STALE:
|
||||
break;
|
||||
default:
|
||||
tASSERT(txn, !!"unexpected dig->dbi_state[dbi]");
|
||||
}
|
||||
dig = dig->parent;
|
||||
} while (dig);
|
||||
return fallback;
|
||||
}
|
||||
|
||||
static size_t audit_db_used(const tree_t *db) {
|
||||
return db ? (size_t)db->branch_pages + (size_t)db->leaf_pages +
|
||||
(size_t)db->large_pages
|
||||
: 0;
|
||||
}
|
||||
|
||||
__cold static int audit_ex_locked(MDBX_txn *txn, size_t retired_stored,
|
||||
bool dont_filter_gc) {
|
||||
const MDBX_env *const env = txn->env;
|
||||
size_t pending = 0;
|
||||
if ((txn->flags & MDBX_TXN_RDONLY) == 0)
|
||||
pending = txn->tw.loose_count + MDBX_PNL_GETSIZE(txn->tw.relist) +
|
||||
(MDBX_PNL_GETSIZE(txn->tw.retired_pages) - retired_stored);
|
||||
|
||||
cursor_couple_t cx;
|
||||
int rc = cursor_init(&cx.outer, txn, FREE_DBI);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
size_t gc = 0;
|
||||
MDBX_val key, data;
|
||||
rc = outer_first(&cx.outer, &key, &data);
|
||||
while (rc == MDBX_SUCCESS) {
|
||||
if (!dont_filter_gc) {
|
||||
if (unlikely(key.iov_len != sizeof(txnid_t))) {
|
||||
ERROR("%s/%d: %s %u", "MDBX_CORRUPTED", MDBX_CORRUPTED,
|
||||
"invalid GC-key size", (unsigned)key.iov_len);
|
||||
return MDBX_CORRUPTED;
|
||||
}
|
||||
txnid_t id = unaligned_peek_u64(4, key.iov_base);
|
||||
if (txn->tw.gc.reclaimed) {
|
||||
for (size_t i = 1; i <= MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed); ++i)
|
||||
if (id == txn->tw.gc.reclaimed[i])
|
||||
goto skip;
|
||||
} else if (id <= txn->tw.gc.last_reclaimed)
|
||||
goto skip;
|
||||
}
|
||||
gc += *(pgno_t *)data.iov_base;
|
||||
skip:
|
||||
rc = outer_next(&cx.outer, &key, &data, MDBX_NEXT);
|
||||
}
|
||||
tASSERT(txn, rc == MDBX_NOTFOUND);
|
||||
|
||||
const size_t done_bitmap_size = (txn->n_dbi + CHAR_BIT - 1) / CHAR_BIT;
|
||||
uint8_t *const done_bitmap = alloca(done_bitmap_size);
|
||||
memset(done_bitmap, 0, done_bitmap_size);
|
||||
if (txn->parent) {
|
||||
tASSERT(txn, txn->n_dbi == txn->parent->n_dbi &&
|
||||
txn->n_dbi == txn->env->txn->n_dbi);
|
||||
#if MDBX_ENABLE_DBI_SPARSE
|
||||
tASSERT(txn, txn->dbi_sparse == txn->parent->dbi_sparse &&
|
||||
txn->dbi_sparse == txn->env->txn->dbi_sparse);
|
||||
#endif /* MDBX_ENABLE_DBI_SPARSE */
|
||||
}
|
||||
|
||||
size_t used = NUM_METAS +
|
||||
audit_db_used(audit_db_dig(txn, FREE_DBI, nullptr)) +
|
||||
audit_db_used(audit_db_dig(txn, MAIN_DBI, nullptr));
|
||||
rc = cursor_init(&cx.outer, txn, MAIN_DBI);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
rc = tree_search(&cx.outer, nullptr, Z_FIRST);
|
||||
while (rc == MDBX_SUCCESS) {
|
||||
page_t *mp = cx.outer.pg[cx.outer.top];
|
||||
for (size_t k = 0; k < page_numkeys(mp); k++) {
|
||||
node_t *node = page_node(mp, k);
|
||||
if (node_flags(node) != N_SUBDATA)
|
||||
continue;
|
||||
if (unlikely(node_ds(node) != sizeof(tree_t))) {
|
||||
ERROR("%s/%d: %s %u", "MDBX_CORRUPTED", MDBX_CORRUPTED,
|
||||
"invalid dupsort sub-tree node size", (unsigned)node_ds(node));
|
||||
return MDBX_CORRUPTED;
|
||||
}
|
||||
|
||||
tree_t reside;
|
||||
const tree_t *db = memcpy(&reside, node_data(node), sizeof(reside));
|
||||
const MDBX_val name = {node_key(node), node_ks(node)};
|
||||
for (size_t dbi = CORE_DBS; dbi < env->n_dbi; ++dbi) {
|
||||
if (dbi >= txn->n_dbi || !(env->dbs_flags[dbi] & DB_VALID))
|
||||
continue;
|
||||
if (env->kvs[MAIN_DBI].clc.k.cmp(&name, &env->kvs[dbi].name))
|
||||
continue;
|
||||
|
||||
done_bitmap[dbi / CHAR_BIT] |= 1 << dbi % CHAR_BIT;
|
||||
db = audit_db_dig(txn, dbi, &reside);
|
||||
break;
|
||||
}
|
||||
used += audit_db_used(db);
|
||||
}
|
||||
rc = cursor_sibling_right(&cx.outer);
|
||||
}
|
||||
tASSERT(txn, rc == MDBX_NOTFOUND);
|
||||
|
||||
for (size_t dbi = CORE_DBS; dbi < txn->n_dbi; ++dbi) {
|
||||
if (done_bitmap[dbi / CHAR_BIT] & (1 << dbi % CHAR_BIT))
|
||||
continue;
|
||||
const tree_t *db = audit_db_dig(txn, dbi, nullptr);
|
||||
if (db)
|
||||
used += audit_db_used(db);
|
||||
else if (dbi_state(txn, dbi))
|
||||
WARNING("audit %s@%" PRIaTXN
|
||||
": unable account dbi %zd / \"%*s\", state 0x%02x",
|
||||
txn->parent ? "nested-" : "", txn->txnid, dbi,
|
||||
(int)env->kvs[dbi].name.iov_len,
|
||||
(const char *)env->kvs[dbi].name.iov_base, dbi_state(txn, dbi));
|
||||
}
|
||||
|
||||
if (pending + gc + used == txn->geo.first_unallocated)
|
||||
return MDBX_SUCCESS;
|
||||
|
||||
if ((txn->flags & MDBX_TXN_RDONLY) == 0)
|
||||
ERROR("audit @%" PRIaTXN ": %zu(pending) = %zu(loose) + "
|
||||
"%zu(reclaimed) + %zu(retired-pending) - %zu(retired-stored)",
|
||||
txn->txnid, pending, txn->tw.loose_count,
|
||||
MDBX_PNL_GETSIZE(txn->tw.relist),
|
||||
txn->tw.retired_pages ? MDBX_PNL_GETSIZE(txn->tw.retired_pages) : 0,
|
||||
retired_stored);
|
||||
ERROR("audit @%" PRIaTXN ": %zu(pending) + %zu"
|
||||
"(gc) + %zu(count) = %zu(total) <> %zu"
|
||||
"(allocated)",
|
||||
txn->txnid, pending, gc, used, pending + gc + used,
|
||||
(size_t)txn->geo.first_unallocated);
|
||||
return MDBX_PROBLEM;
|
||||
}
|
||||
|
||||
__cold int audit_ex(MDBX_txn *txn, size_t retired_stored, bool dont_filter_gc) {
|
||||
MDBX_env *const env = txn->env;
|
||||
int rc = osal_fastmutex_acquire(&env->dbi_lock);
|
||||
if (likely(rc == MDBX_SUCCESS)) {
|
||||
rc = audit_ex_locked(txn, retired_stored, dont_filter_gc);
|
||||
ENSURE(txn->env, osal_fastmutex_release(&env->dbi_lock) == MDBX_SUCCESS);
|
||||
}
|
||||
return rc;
|
||||
}
|
12
src/bits.md
12
src/bits.md
@ -1,13 +1,13 @@
|
||||
N | MASK | ENV | TXN | DB | PUT | DBI | NODE | PAGE | MRESIZE |
|
||||
--|---------|-----------|--------------|----------|-----------|------------|---------|----------|---------|
|
||||
0 |0000 0001|ALLOC_RSRV |TXN_FINISHED | | |DBI_DIRTY |F_BIGDATA|P_BRANCH | |
|
||||
1 |0000 0002|ALLOC_UNIMP|TXN_ERROR |REVERSEKEY|F_SUBDATA |DBI_STALE |F_SUBDATA|P_LEAF | |
|
||||
2 |0000 0004|ALLOC_COLSC|TXN_DIRTY |DUPSORT | |DBI_FRESH |F_DUPDATA|P_OVERFLOW| |
|
||||
0 |0000 0001|ALLOC_RSRV |TXN_FINISHED | | |DBI_DIRTY |N_BIGDATA|P_BRANCH | |
|
||||
1 |0000 0002|ALLOC_UNIMP|TXN_ERROR |REVERSEKEY|F_SUBDATA |DBI_STALE |N_SUBDATA|P_LEAF | |
|
||||
2 |0000 0004|ALLOC_COLSC|TXN_DIRTY |DUPSORT | |DBI_FRESH |N_DUPDATA|P_LARGE | |
|
||||
3 |0000 0008|ALLOC_SSCAN|TXN_SPILLS |INTEGERKEY| |DBI_CREAT | |P_META | |
|
||||
4 |0000 0010|ALLOC_FIFO |TXN_HAS_CHILD |DUPFIXED |NOOVERWRITE|DBI_VALID | |P_BAD | |
|
||||
5 |0000 0020| |TXN_DRAINED_GC|INTEGERDUP|NODUPDATA | | |P_LEAF2 | |
|
||||
5 |0000 0020| |TXN_DRAINED_GC|INTEGERDUP|NODUPDATA | | |P_DUPFIX | |
|
||||
6 |0000 0040| | |REVERSEDUP|CURRENT |DBI_OLDEN | |P_SUBP | |
|
||||
7 |0000 0080| | | |ALLDUPS |DBI_LINDO | | | |
|
||||
7 |0000 0080| | |DB_VALID |ALLDUPS |DBI_LINDO | | | |
|
||||
8 |0000 0100| _MAY_MOVE | | | | | | | <= |
|
||||
9 |0000 0200| _MAY_UNMAP| | | | | | | <= |
|
||||
10|0000 0400| | | | | | | | |
|
||||
@ -15,7 +15,7 @@ N | MASK | ENV | TXN | DB | PUT | DBI | NOD
|
||||
12|0000 1000| | | | | | | | |
|
||||
13|0000 2000|VALIDATION | | | | | |P_SPILLED | |
|
||||
14|0000 4000|NOSUBDIR | | | | | |P_LOOSE | |
|
||||
15|0000 8000| | |DB_VALID | | | |P_FROZEN | |
|
||||
15|0000 8000| | | | | | |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 | | | | |
|
||||
|
353
src/cogs.c
Normal file
353
src/cogs.c
Normal file
@ -0,0 +1,353 @@
|
||||
/// \copyright SPDX-License-Identifier: Apache-2.0
|
||||
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
|
||||
|
||||
#include "internals.h"
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
* Pack/Unpack 16-bit values for Grow step & Shrink threshold */
|
||||
|
||||
MDBX_NOTHROW_CONST_FUNCTION static inline pgno_t me2v(size_t m, size_t e) {
|
||||
assert(m < 2048 && e < 8);
|
||||
return (pgno_t)(32768 + ((m + 1) << (e + 8)));
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_CONST_FUNCTION static inline uint16_t v2me(size_t v, size_t e) {
|
||||
assert(v > (e ? me2v(2047, e - 1) : 32768));
|
||||
assert(v <= me2v(2047, e));
|
||||
size_t m = (v - 32768 + ((size_t)1 << (e + 8)) - 1) >> (e + 8);
|
||||
m -= m > 0;
|
||||
assert(m < 2048 && e < 8);
|
||||
// f e d c b a 9 8 7 6 5 4 3 2 1 0
|
||||
// 1 e e e m m m m m m m m m m m 1
|
||||
const uint16_t pv = (uint16_t)(0x8001 + (e << 12) + (m << 1));
|
||||
assert(pv != 65535);
|
||||
return pv;
|
||||
}
|
||||
|
||||
/* Convert 16-bit packed (exponential quantized) value to number of pages */
|
||||
pgno_t pv2pages(uint16_t pv) {
|
||||
if ((pv & 0x8001) != 0x8001)
|
||||
return pv;
|
||||
if (pv == 65535)
|
||||
return 65536;
|
||||
// f e d c b a 9 8 7 6 5 4 3 2 1 0
|
||||
// 1 e e e m m m m m m m m m m m 1
|
||||
return me2v((pv >> 1) & 2047, (pv >> 12) & 7);
|
||||
}
|
||||
|
||||
/* Convert number of pages to 16-bit packed (exponential quantized) value */
|
||||
uint16_t pages2pv(size_t pages) {
|
||||
if (pages < 32769 || (pages < 65536 && (pages & 1) == 0))
|
||||
return (uint16_t)pages;
|
||||
if (pages <= me2v(2047, 0))
|
||||
return v2me(pages, 0);
|
||||
if (pages <= me2v(2047, 1))
|
||||
return v2me(pages, 1);
|
||||
if (pages <= me2v(2047, 2))
|
||||
return v2me(pages, 2);
|
||||
if (pages <= me2v(2047, 3))
|
||||
return v2me(pages, 3);
|
||||
if (pages <= me2v(2047, 4))
|
||||
return v2me(pages, 4);
|
||||
if (pages <= me2v(2047, 5))
|
||||
return v2me(pages, 5);
|
||||
if (pages <= me2v(2047, 6))
|
||||
return v2me(pages, 6);
|
||||
return (pages < me2v(2046, 7)) ? v2me(pages, 7) : 65533;
|
||||
}
|
||||
|
||||
__cold bool pv2pages_verify(void) {
|
||||
bool ok = true, dump_translation = false;
|
||||
for (size_t i = 0; i < 65536; ++i) {
|
||||
size_t pages = pv2pages(i);
|
||||
size_t x = pages2pv(pages);
|
||||
size_t xp = pv2pages(x);
|
||||
if (pages != xp) {
|
||||
ERROR("%zu => %zu => %zu => %zu\n", i, pages, x, xp);
|
||||
ok = false;
|
||||
} else if (dump_translation && !(x == i || (x % 2 == 0 && x < 65536))) {
|
||||
DEBUG("%zu => %zu => %zu => %zu\n", i, pages, x, xp);
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION size_t bytes_align2os_bytes(const MDBX_env *env,
|
||||
size_t bytes) {
|
||||
return ceil_powerof2(
|
||||
bytes, (env->ps > globals.sys_pagesize) ? env->ps : globals.sys_pagesize);
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION size_t pgno_align2os_bytes(const MDBX_env *env,
|
||||
size_t pgno) {
|
||||
return ceil_powerof2(pgno2bytes(env, pgno), globals.sys_pagesize);
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION pgno_t pgno_align2os_pgno(const MDBX_env *env,
|
||||
size_t pgno) {
|
||||
return bytes2pgno(env, pgno_align2os_bytes(env, pgno));
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION static __always_inline int
|
||||
cmp_int_inline(const size_t expected_alignment, const MDBX_val *a,
|
||||
const MDBX_val *b) {
|
||||
if (likely(a->iov_len == b->iov_len)) {
|
||||
if (sizeof(size_t) > 7 && likely(a->iov_len == 8))
|
||||
return CMP2INT(unaligned_peek_u64(expected_alignment, a->iov_base),
|
||||
unaligned_peek_u64(expected_alignment, b->iov_base));
|
||||
if (likely(a->iov_len == 4))
|
||||
return CMP2INT(unaligned_peek_u32(expected_alignment, a->iov_base),
|
||||
unaligned_peek_u32(expected_alignment, b->iov_base));
|
||||
if (sizeof(size_t) < 8 && likely(a->iov_len == 8))
|
||||
return CMP2INT(unaligned_peek_u64(expected_alignment, a->iov_base),
|
||||
unaligned_peek_u64(expected_alignment, b->iov_base));
|
||||
}
|
||||
ERROR("mismatch and/or invalid size %p.%zu/%p.%zu for INTEGERKEY/INTEGERDUP",
|
||||
a->iov_base, a->iov_len, b->iov_base, b->iov_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION __hot int cmp_int_unaligned(const MDBX_val *a,
|
||||
const MDBX_val *b) {
|
||||
return cmp_int_inline(1, a, b);
|
||||
}
|
||||
|
||||
#ifndef cmp_int_align2
|
||||
/* Compare two items pointing at 2-byte aligned unsigned int's. */
|
||||
MDBX_NOTHROW_PURE_FUNCTION __hot int cmp_int_align2(const MDBX_val *a,
|
||||
const MDBX_val *b) {
|
||||
return cmp_int_inline(2, a, b);
|
||||
}
|
||||
#endif /* cmp_int_align2 */
|
||||
|
||||
#ifndef cmp_int_align4
|
||||
/* Compare two items pointing at 4-byte aligned unsigned int's. */
|
||||
MDBX_NOTHROW_PURE_FUNCTION __hot int cmp_int_align4(const MDBX_val *a,
|
||||
const MDBX_val *b) {
|
||||
return cmp_int_inline(4, a, b);
|
||||
}
|
||||
#endif /* cmp_int_align4 */
|
||||
|
||||
/* Compare two items lexically */
|
||||
MDBX_NOTHROW_PURE_FUNCTION __hot int cmp_lexical(const MDBX_val *a,
|
||||
const MDBX_val *b) {
|
||||
if (a->iov_len == b->iov_len)
|
||||
return a->iov_len ? memcmp(a->iov_base, b->iov_base, a->iov_len) : 0;
|
||||
|
||||
const int diff_len = (a->iov_len < b->iov_len) ? -1 : 1;
|
||||
const size_t shortest = (a->iov_len < b->iov_len) ? a->iov_len : b->iov_len;
|
||||
int diff_data = shortest ? memcmp(a->iov_base, b->iov_base, shortest) : 0;
|
||||
return likely(diff_data) ? diff_data : diff_len;
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION static __always_inline unsigned
|
||||
tail3le(const uint8_t *p, size_t l) {
|
||||
STATIC_ASSERT(sizeof(unsigned) > 2);
|
||||
// 1: 0 0 0
|
||||
// 2: 0 1 1
|
||||
// 3: 0 1 2
|
||||
return p[0] | p[l >> 1] << 8 | p[l - 1] << 16;
|
||||
}
|
||||
|
||||
/* Compare two items in reverse byte order */
|
||||
MDBX_NOTHROW_PURE_FUNCTION __hot int cmp_reverse(const MDBX_val *a,
|
||||
const MDBX_val *b) {
|
||||
size_t left = (a->iov_len < b->iov_len) ? a->iov_len : b->iov_len;
|
||||
if (likely(left)) {
|
||||
const uint8_t *pa = ptr_disp(a->iov_base, a->iov_len);
|
||||
const uint8_t *pb = ptr_disp(b->iov_base, b->iov_len);
|
||||
while (left >= sizeof(size_t)) {
|
||||
pa -= sizeof(size_t);
|
||||
pb -= sizeof(size_t);
|
||||
left -= sizeof(size_t);
|
||||
STATIC_ASSERT(sizeof(size_t) == 4 || sizeof(size_t) == 8);
|
||||
if (sizeof(size_t) == 4) {
|
||||
uint32_t xa = unaligned_peek_u32(1, pa);
|
||||
uint32_t xb = unaligned_peek_u32(1, pb);
|
||||
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
|
||||
xa = osal_bswap32(xa);
|
||||
xb = osal_bswap32(xb);
|
||||
#endif /* __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__ */
|
||||
if (xa != xb)
|
||||
return (xa < xb) ? -1 : 1;
|
||||
} else {
|
||||
uint64_t xa = unaligned_peek_u64(1, pa);
|
||||
uint64_t xb = unaligned_peek_u64(1, pb);
|
||||
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
|
||||
xa = osal_bswap64(xa);
|
||||
xb = osal_bswap64(xb);
|
||||
#endif /* __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__ */
|
||||
if (xa != xb)
|
||||
return (xa < xb) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
if (sizeof(size_t) == 8 && left >= 4) {
|
||||
pa -= 4;
|
||||
pb -= 4;
|
||||
left -= 4;
|
||||
uint32_t xa = unaligned_peek_u32(1, pa);
|
||||
uint32_t xb = unaligned_peek_u32(1, pb);
|
||||
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
|
||||
xa = osal_bswap32(xa);
|
||||
xb = osal_bswap32(xb);
|
||||
#endif /* __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__ */
|
||||
if (xa != xb)
|
||||
return (xa < xb) ? -1 : 1;
|
||||
}
|
||||
if (left) {
|
||||
unsigned xa = tail3le(pa - left, left);
|
||||
unsigned xb = tail3le(pb - left, left);
|
||||
if (xa != xb)
|
||||
return (xa < xb) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
return CMP2INT(a->iov_len, b->iov_len);
|
||||
}
|
||||
|
||||
/* Fast non-lexically comparator */
|
||||
MDBX_NOTHROW_PURE_FUNCTION __hot int cmp_lenfast(const MDBX_val *a,
|
||||
const MDBX_val *b) {
|
||||
int diff = CMP2INT(a->iov_len, b->iov_len);
|
||||
return (likely(diff) || a->iov_len == 0)
|
||||
? diff
|
||||
: memcmp(a->iov_base, b->iov_base, a->iov_len);
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION __hot bool
|
||||
eq_fast_slowpath(const uint8_t *a, const uint8_t *b, size_t l) {
|
||||
if (likely(l > 3)) {
|
||||
if (MDBX_UNALIGNED_OK >= 4 && likely(l < 9))
|
||||
return ((unaligned_peek_u32(1, a) - unaligned_peek_u32(1, b)) |
|
||||
(unaligned_peek_u32(1, a + l - 4) -
|
||||
unaligned_peek_u32(1, b + l - 4))) == 0;
|
||||
if (MDBX_UNALIGNED_OK >= 8 && sizeof(size_t) > 7 && likely(l < 17))
|
||||
return ((unaligned_peek_u64(1, a) - unaligned_peek_u64(1, b)) |
|
||||
(unaligned_peek_u64(1, a + l - 8) -
|
||||
unaligned_peek_u64(1, b + l - 8))) == 0;
|
||||
return memcmp(a, b, l) == 0;
|
||||
}
|
||||
if (likely(l))
|
||||
return tail3le(a, l) == tail3le(b, l);
|
||||
return true;
|
||||
}
|
||||
|
||||
int cmp_equal_or_greater(const MDBX_val *a, const MDBX_val *b) {
|
||||
return eq_fast(a, b) ? 0 : 1;
|
||||
}
|
||||
|
||||
int cmp_equal_or_wrong(const MDBX_val *a, const MDBX_val *b) {
|
||||
return eq_fast(a, b) ? 0 : -1;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
__cold void update_mlcnt(const MDBX_env *env,
|
||||
const pgno_t new_aligned_mlocked_pgno,
|
||||
const bool lock_not_release) {
|
||||
for (;;) {
|
||||
const pgno_t mlock_pgno_before =
|
||||
atomic_load32(&env->mlocked_pgno, mo_AcquireRelease);
|
||||
eASSERT(env,
|
||||
pgno_align2os_pgno(env, mlock_pgno_before) == mlock_pgno_before);
|
||||
eASSERT(env, pgno_align2os_pgno(env, new_aligned_mlocked_pgno) ==
|
||||
new_aligned_mlocked_pgno);
|
||||
if (lock_not_release ? (mlock_pgno_before >= new_aligned_mlocked_pgno)
|
||||
: (mlock_pgno_before <= new_aligned_mlocked_pgno))
|
||||
break;
|
||||
if (likely(atomic_cas32(&((MDBX_env *)env)->mlocked_pgno, mlock_pgno_before,
|
||||
new_aligned_mlocked_pgno)))
|
||||
for (;;) {
|
||||
mdbx_atomic_uint32_t *const mlcnt = env->lck->mlcnt;
|
||||
const int32_t snap_locked = atomic_load32(mlcnt + 0, mo_Relaxed);
|
||||
const int32_t snap_unlocked = atomic_load32(mlcnt + 1, mo_Relaxed);
|
||||
if (mlock_pgno_before == 0 && (snap_locked - snap_unlocked) < INT_MAX) {
|
||||
eASSERT(env, lock_not_release);
|
||||
if (unlikely(!atomic_cas32(mlcnt + 0, snap_locked, snap_locked + 1)))
|
||||
continue;
|
||||
}
|
||||
if (new_aligned_mlocked_pgno == 0 &&
|
||||
(snap_locked - snap_unlocked) > 0) {
|
||||
eASSERT(env, !lock_not_release);
|
||||
if (unlikely(
|
||||
!atomic_cas32(mlcnt + 1, snap_unlocked, snap_unlocked + 1)))
|
||||
continue;
|
||||
}
|
||||
NOTICE("%s-pages %u..%u, mlocked-process(es) %u -> %u",
|
||||
lock_not_release ? "lock" : "unlock",
|
||||
lock_not_release ? mlock_pgno_before : new_aligned_mlocked_pgno,
|
||||
lock_not_release ? new_aligned_mlocked_pgno : mlock_pgno_before,
|
||||
snap_locked - snap_unlocked,
|
||||
atomic_load32(mlcnt + 0, mo_Relaxed) -
|
||||
atomic_load32(mlcnt + 1, mo_Relaxed));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__cold void munlock_after(const MDBX_env *env, const pgno_t aligned_pgno,
|
||||
const size_t end_bytes) {
|
||||
if (atomic_load32(&env->mlocked_pgno, mo_AcquireRelease) > aligned_pgno) {
|
||||
int err = MDBX_ENOSYS;
|
||||
const size_t munlock_begin = pgno2bytes(env, aligned_pgno);
|
||||
const size_t munlock_size = end_bytes - munlock_begin;
|
||||
eASSERT(env, end_bytes % globals.sys_pagesize == 0 &&
|
||||
munlock_begin % globals.sys_pagesize == 0 &&
|
||||
munlock_size % globals.sys_pagesize == 0);
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
err =
|
||||
VirtualUnlock(ptr_disp(env->dxb_mmap.base, munlock_begin), munlock_size)
|
||||
? MDBX_SUCCESS
|
||||
: (int)GetLastError();
|
||||
if (err == ERROR_NOT_LOCKED)
|
||||
err = MDBX_SUCCESS;
|
||||
#elif defined(_POSIX_MEMLOCK_RANGE)
|
||||
err = munlock(ptr_disp(env->dxb_mmap.base, munlock_begin), munlock_size)
|
||||
? errno
|
||||
: MDBX_SUCCESS;
|
||||
#endif
|
||||
if (likely(err == MDBX_SUCCESS))
|
||||
update_mlcnt(env, aligned_pgno, false);
|
||||
else {
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
WARNING("VirtualUnlock(%zu, %zu) error %d", munlock_begin, munlock_size,
|
||||
err);
|
||||
#else
|
||||
WARNING("munlock(%zu, %zu) error %d", munlock_begin, munlock_size, err);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__cold void munlock_all(const MDBX_env *env) {
|
||||
munlock_after(env, 0, bytes_align2os_bytes(env, env->dxb_mmap.current));
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
uint32_t combine_durability_flags(const uint32_t a, const uint32_t b) {
|
||||
uint32_t r = a | b;
|
||||
|
||||
/* avoid false MDBX_UTTERLY_NOSYNC */
|
||||
if (F_ISSET(r, MDBX_UTTERLY_NOSYNC) && !F_ISSET(a, MDBX_UTTERLY_NOSYNC) &&
|
||||
!F_ISSET(b, MDBX_UTTERLY_NOSYNC))
|
||||
r = (r - MDBX_UTTERLY_NOSYNC) | MDBX_SAFE_NOSYNC;
|
||||
|
||||
/* convert DEPRECATED_MAPASYNC to MDBX_SAFE_NOSYNC */
|
||||
if ((r & (MDBX_WRITEMAP | DEPRECATED_MAPASYNC)) ==
|
||||
(MDBX_WRITEMAP | DEPRECATED_MAPASYNC) &&
|
||||
!F_ISSET(r, MDBX_UTTERLY_NOSYNC))
|
||||
r = (r - DEPRECATED_MAPASYNC) | MDBX_SAFE_NOSYNC;
|
||||
|
||||
/* force MDBX_NOMETASYNC if NOSYNC enabled */
|
||||
if (r & (MDBX_SAFE_NOSYNC | MDBX_UTTERLY_NOSYNC))
|
||||
r |= MDBX_NOMETASYNC;
|
||||
|
||||
assert(!(F_ISSET(r, MDBX_UTTERLY_NOSYNC) &&
|
||||
!F_ISSET(a, MDBX_UTTERLY_NOSYNC) &&
|
||||
!F_ISSET(b, MDBX_UTTERLY_NOSYNC)));
|
||||
return r;
|
||||
}
|
558
src/cogs.h
Normal file
558
src/cogs.h
Normal file
@ -0,0 +1,558 @@
|
||||
/// \copyright SPDX-License-Identifier: Apache-2.0
|
||||
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "essentials.h"
|
||||
|
||||
MDBX_NOTHROW_CONST_FUNCTION MDBX_INTERNAL pgno_t pv2pages(uint16_t pv);
|
||||
|
||||
MDBX_NOTHROW_CONST_FUNCTION MDBX_INTERNAL uint16_t pages2pv(size_t pages);
|
||||
|
||||
MDBX_MAYBE_UNUSED MDBX_INTERNAL bool pv2pages_verify(void);
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
* Nodes, Keys & Values length limitation factors:
|
||||
*
|
||||
* BRANCH_NODE_MAX
|
||||
* Branch-page must contain at least two nodes, within each a key and a child
|
||||
* page number. But page can't be split if it contains less that 4 keys,
|
||||
* i.e. a page should not overflow before adding the fourth key. Therefore,
|
||||
* at least 3 branch-node should fit in the single branch-page. Further, the
|
||||
* first node of a branch-page doesn't contain a key, i.e. the first node
|
||||
* is always require space just for itself. Thus:
|
||||
* PAGESPACE = pagesize - page_hdr_len;
|
||||
* BRANCH_NODE_MAX = even_floor(
|
||||
* (PAGESPACE - sizeof(indx_t) - NODESIZE) / (3 - 1) - sizeof(indx_t));
|
||||
* KEYLEN_MAX = BRANCH_NODE_MAX - node_hdr_len;
|
||||
*
|
||||
* LEAF_NODE_MAX
|
||||
* Leaf-node must fit into single leaf-page, where a value could be placed on
|
||||
* a large/overflow page. However, may require to insert a nearly page-sized
|
||||
* node between two large nodes are already fill-up a page. In this case the
|
||||
* page must be split to two if some pair of nodes fits on one page, or
|
||||
* otherwise the page should be split to the THREE with a single node
|
||||
* per each of ones. Such 1-into-3 page splitting is costly and complex since
|
||||
* requires TWO insertion into the parent page, that could lead to split it
|
||||
* and so on up to the root. Therefore double-splitting is avoided here and
|
||||
* the maximum node size is half of a leaf page space:
|
||||
* LEAF_NODE_MAX = even_floor(PAGESPACE / 2 - sizeof(indx_t));
|
||||
* DATALEN_NO_OVERFLOW = LEAF_NODE_MAX - NODESIZE - KEYLEN_MAX;
|
||||
*
|
||||
* - SubDatabase-node must fit into one leaf-page:
|
||||
* SUBDB_NAME_MAX = LEAF_NODE_MAX - node_hdr_len - sizeof(tree_t);
|
||||
*
|
||||
* - Dupsort values itself are a keys in a dupsort-subdb and couldn't be longer
|
||||
* than the KEYLEN_MAX. But dupsort node must not great than LEAF_NODE_MAX,
|
||||
* since dupsort value couldn't be placed on a large/overflow page:
|
||||
* DUPSORT_DATALEN_MAX = min(KEYLEN_MAX,
|
||||
* max(DATALEN_NO_OVERFLOW, sizeof(tree_t));
|
||||
*/
|
||||
|
||||
#define PAGESPACE(pagesize) ((pagesize) - PAGEHDRSZ)
|
||||
|
||||
#define BRANCH_NODE_MAX(pagesize) \
|
||||
(EVEN_FLOOR((PAGESPACE(pagesize) - sizeof(indx_t) - NODESIZE) / (3 - 1) - \
|
||||
sizeof(indx_t)))
|
||||
|
||||
#define LEAF_NODE_MAX(pagesize) \
|
||||
(EVEN_FLOOR(PAGESPACE(pagesize) / 2) - sizeof(indx_t))
|
||||
|
||||
#define MAX_GC1OVPAGE(pagesize) (PAGESPACE(pagesize) / sizeof(pgno_t) - 1)
|
||||
|
||||
MDBX_NOTHROW_CONST_FUNCTION static inline size_t
|
||||
keysize_max(size_t pagesize, MDBX_db_flags_t flags) {
|
||||
assert(pagesize >= MDBX_MIN_PAGESIZE && pagesize <= MDBX_MAX_PAGESIZE &&
|
||||
is_powerof2(pagesize));
|
||||
STATIC_ASSERT(BRANCH_NODE_MAX(MDBX_MIN_PAGESIZE) - NODESIZE >= 8);
|
||||
if (flags & MDBX_INTEGERKEY)
|
||||
return 8 /* sizeof(uint64_t) */;
|
||||
|
||||
const intptr_t max_branch_key = BRANCH_NODE_MAX(pagesize) - NODESIZE;
|
||||
STATIC_ASSERT(LEAF_NODE_MAX(MDBX_MIN_PAGESIZE) - NODESIZE -
|
||||
/* sizeof(uint64) as a key */ 8 >
|
||||
sizeof(tree_t));
|
||||
if (flags &
|
||||
(MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_REVERSEDUP | MDBX_INTEGERDUP)) {
|
||||
const intptr_t max_dupsort_leaf_key =
|
||||
LEAF_NODE_MAX(pagesize) - NODESIZE - sizeof(tree_t);
|
||||
return (max_branch_key < max_dupsort_leaf_key) ? max_branch_key
|
||||
: max_dupsort_leaf_key;
|
||||
}
|
||||
return max_branch_key;
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_CONST_FUNCTION static inline size_t
|
||||
env_keysize_max(const MDBX_env *env, MDBX_db_flags_t flags) {
|
||||
size_t size_max;
|
||||
if (flags & MDBX_INTEGERKEY)
|
||||
size_max = 8 /* sizeof(uint64_t) */;
|
||||
else {
|
||||
const intptr_t max_branch_key = env->branch_nodemax - NODESIZE;
|
||||
STATIC_ASSERT(LEAF_NODE_MAX(MDBX_MIN_PAGESIZE) - NODESIZE -
|
||||
/* sizeof(uint64) as a key */ 8 >
|
||||
sizeof(tree_t));
|
||||
if (flags &
|
||||
(MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_REVERSEDUP | MDBX_INTEGERDUP)) {
|
||||
const intptr_t max_dupsort_leaf_key =
|
||||
env->leaf_nodemax - NODESIZE - sizeof(tree_t);
|
||||
size_max = (max_branch_key < max_dupsort_leaf_key) ? max_branch_key
|
||||
: max_dupsort_leaf_key;
|
||||
} else
|
||||
size_max = max_branch_key;
|
||||
}
|
||||
eASSERT(env, size_max == keysize_max(env->ps, flags));
|
||||
return size_max;
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_CONST_FUNCTION static inline size_t
|
||||
keysize_min(MDBX_db_flags_t flags) {
|
||||
return (flags & MDBX_INTEGERKEY) ? 4 /* sizeof(uint32_t) */ : 0;
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_CONST_FUNCTION static inline size_t
|
||||
valsize_min(MDBX_db_flags_t flags) {
|
||||
if (flags & MDBX_INTEGERDUP)
|
||||
return 4 /* sizeof(uint32_t) */;
|
||||
else if (flags & MDBX_DUPFIXED)
|
||||
return sizeof(indx_t);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_CONST_FUNCTION static inline size_t
|
||||
valsize_max(size_t pagesize, MDBX_db_flags_t flags) {
|
||||
assert(pagesize >= MDBX_MIN_PAGESIZE && pagesize <= MDBX_MAX_PAGESIZE &&
|
||||
is_powerof2(pagesize));
|
||||
|
||||
if (flags & MDBX_INTEGERDUP)
|
||||
return 8 /* sizeof(uint64_t) */;
|
||||
|
||||
if (flags & (MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_REVERSEDUP))
|
||||
return keysize_max(pagesize, 0);
|
||||
|
||||
const unsigned page_ln2 = log2n_powerof2(pagesize);
|
||||
const size_t hard = 0x7FF00000ul;
|
||||
const size_t hard_pages = hard >> page_ln2;
|
||||
STATIC_ASSERT(PAGELIST_LIMIT <= MAX_PAGENO);
|
||||
const size_t pages_limit = PAGELIST_LIMIT / 4;
|
||||
const size_t limit =
|
||||
(hard_pages < pages_limit) ? hard : (pages_limit << page_ln2);
|
||||
return (limit < MAX_MAPSIZE / 2) ? limit : MAX_MAPSIZE / 2;
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_CONST_FUNCTION static inline size_t
|
||||
env_valsize_max(const MDBX_env *env, MDBX_db_flags_t flags) {
|
||||
size_t size_max;
|
||||
if (flags & MDBX_INTEGERDUP)
|
||||
size_max = 8 /* sizeof(uint64_t) */;
|
||||
else if (flags & (MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_REVERSEDUP))
|
||||
size_max = env_keysize_max(env, 0);
|
||||
else {
|
||||
const size_t hard = 0x7FF00000ul;
|
||||
const size_t hard_pages = hard >> env->ps2ln;
|
||||
STATIC_ASSERT(PAGELIST_LIMIT <= MAX_PAGENO);
|
||||
const size_t pages_limit = PAGELIST_LIMIT / 4;
|
||||
const size_t limit =
|
||||
(hard_pages < pages_limit) ? hard : (pages_limit << env->ps2ln);
|
||||
size_max = (limit < MAX_MAPSIZE / 2) ? limit : MAX_MAPSIZE / 2;
|
||||
}
|
||||
eASSERT(env, size_max == valsize_max(env->ps, flags));
|
||||
return size_max;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION static inline size_t
|
||||
leaf_size(const MDBX_env *env, const MDBX_val *key, const MDBX_val *data) {
|
||||
size_t node_bytes = node_size(key, data);
|
||||
if (node_bytes > env->leaf_nodemax)
|
||||
/* put on large/overflow page */
|
||||
node_bytes = node_size_len(key->iov_len, 0) + sizeof(pgno_t);
|
||||
|
||||
return node_bytes + sizeof(indx_t);
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION static inline size_t
|
||||
branch_size(const MDBX_env *env, const MDBX_val *key) {
|
||||
/* Size of a node in a branch page with a given key.
|
||||
* This is just the node header plus the key, there is no data. */
|
||||
size_t node_bytes = node_size(key, nullptr);
|
||||
if (unlikely(node_bytes > env->branch_nodemax)) {
|
||||
/* put on large/overflow page, not implemented */
|
||||
mdbx_panic("node_size(key) %zu > %u branch_nodemax", node_bytes,
|
||||
env->branch_nodemax);
|
||||
node_bytes = node_size(key, nullptr) + sizeof(pgno_t);
|
||||
}
|
||||
|
||||
return node_bytes + sizeof(indx_t);
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_CONST_FUNCTION static inline uint16_t
|
||||
flags_db2sub(uint16_t db_flags) {
|
||||
uint16_t sub_flags = db_flags & MDBX_DUPFIXED;
|
||||
|
||||
/* MDBX_INTEGERDUP => MDBX_INTEGERKEY */
|
||||
#define SHIFT_INTEGERDUP_TO_INTEGERKEY 2
|
||||
STATIC_ASSERT((MDBX_INTEGERDUP >> SHIFT_INTEGERDUP_TO_INTEGERKEY) ==
|
||||
MDBX_INTEGERKEY);
|
||||
sub_flags |= (db_flags & MDBX_INTEGERDUP) >> SHIFT_INTEGERDUP_TO_INTEGERKEY;
|
||||
|
||||
/* MDBX_REVERSEDUP => MDBX_REVERSEKEY */
|
||||
#define SHIFT_REVERSEDUP_TO_REVERSEKEY 5
|
||||
STATIC_ASSERT((MDBX_REVERSEDUP >> SHIFT_REVERSEDUP_TO_REVERSEKEY) ==
|
||||
MDBX_REVERSEKEY);
|
||||
sub_flags |= (db_flags & MDBX_REVERSEDUP) >> SHIFT_REVERSEDUP_TO_REVERSEKEY;
|
||||
|
||||
return sub_flags;
|
||||
}
|
||||
|
||||
static inline bool check_sdb_flags(unsigned flags) {
|
||||
switch (flags & ~(MDBX_REVERSEKEY | MDBX_INTEGERKEY)) {
|
||||
default:
|
||||
NOTICE("invalid db-flags 0x%x", flags);
|
||||
return false;
|
||||
case MDBX_DUPSORT:
|
||||
case MDBX_DUPSORT | MDBX_REVERSEDUP:
|
||||
case MDBX_DUPSORT | MDBX_DUPFIXED:
|
||||
case MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_REVERSEDUP:
|
||||
case MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_INTEGERDUP:
|
||||
case MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_INTEGERDUP | MDBX_REVERSEDUP:
|
||||
case MDBX_DB_DEFAULTS:
|
||||
return (flags & (MDBX_REVERSEKEY | MDBX_INTEGERKEY)) !=
|
||||
(MDBX_REVERSEKEY | MDBX_INTEGERKEY);
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION static inline size_t pgno2bytes(const MDBX_env *env,
|
||||
size_t pgno) {
|
||||
eASSERT(env, (1u << env->ps2ln) == env->ps);
|
||||
return ((size_t)pgno) << env->ps2ln;
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION static inline page_t *pgno2page(const MDBX_env *env,
|
||||
size_t pgno) {
|
||||
return ptr_disp(env->dxb_mmap.base, pgno2bytes(env, pgno));
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION static inline pgno_t bytes2pgno(const MDBX_env *env,
|
||||
size_t bytes) {
|
||||
eASSERT(env, (env->ps >> env->ps2ln) == 1);
|
||||
return (pgno_t)(bytes >> env->ps2ln);
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL size_t
|
||||
bytes_align2os_bytes(const MDBX_env *env, size_t bytes);
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL size_t
|
||||
pgno_align2os_bytes(const MDBX_env *env, size_t pgno);
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL pgno_t
|
||||
pgno_align2os_pgno(const MDBX_env *env, size_t pgno);
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION static inline pgno_t
|
||||
largechunk_npages(const MDBX_env *env, size_t bytes) {
|
||||
return bytes2pgno(env, PAGEHDRSZ - 1 + bytes) + 1;
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION static inline MDBX_val get_key(const node_t *node) {
|
||||
MDBX_val key;
|
||||
key.iov_len = node_ks(node);
|
||||
key.iov_base = node_key(node);
|
||||
return key;
|
||||
}
|
||||
|
||||
static inline void get_key_optional(const node_t *node,
|
||||
MDBX_val *keyptr /* __may_null */) {
|
||||
if (keyptr)
|
||||
*keyptr = get_key(node);
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION static inline void *page_data(const page_t *mp) {
|
||||
return ptr_disp(mp, PAGEHDRSZ);
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION static inline const page_t *
|
||||
data_page(const void *data) {
|
||||
return container_of(data, page_t, entries);
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION static inline meta_t *page_meta(page_t *mp) {
|
||||
return (meta_t *)page_data(mp);
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION static inline size_t page_numkeys(const page_t *mp) {
|
||||
return mp->lower >> 1;
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION static inline size_t page_room(const page_t *mp) {
|
||||
return mp->upper - mp->lower;
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION static inline size_t
|
||||
page_space(const MDBX_env *env) {
|
||||
STATIC_ASSERT(PAGEHDRSZ % 2 == 0);
|
||||
return env->ps - PAGEHDRSZ;
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION static inline size_t page_used(const MDBX_env *env,
|
||||
const page_t *mp) {
|
||||
return page_space(env) - page_room(mp);
|
||||
}
|
||||
|
||||
/* The percentage of space used in the page, in a percents. */
|
||||
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline unsigned
|
||||
page_fill_percentum_x10(const MDBX_env *env, const page_t *mp) {
|
||||
const size_t space = page_space(env);
|
||||
return (unsigned)((page_used(env, mp) * 1000 + space / 2) / space);
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION static inline node_t *page_node(const page_t *mp,
|
||||
size_t i) {
|
||||
assert(page_type_compat(mp) == P_LEAF || page_type(mp) == P_BRANCH);
|
||||
assert(page_numkeys(mp) > i);
|
||||
assert(mp->entries[i] % 2 == 0);
|
||||
return ptr_disp(mp, mp->entries[i] + PAGEHDRSZ);
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION static inline void *
|
||||
page_dupfix_ptr(const page_t *mp, size_t i, size_t keysize) {
|
||||
assert(page_type_compat(mp) == (P_LEAF | P_DUPFIX) && i == (indx_t)i &&
|
||||
mp->dupfix_ksize == keysize);
|
||||
(void)keysize;
|
||||
return ptr_disp(mp, PAGEHDRSZ + mp->dupfix_ksize * (indx_t)i);
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION static inline MDBX_val
|
||||
page_dupfix_key(const page_t *mp, size_t i, size_t keysize) {
|
||||
MDBX_val r;
|
||||
r.iov_base = page_dupfix_ptr(mp, i, keysize);
|
||||
r.iov_len = mp->dupfix_ksize;
|
||||
return r;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL int
|
||||
cmp_int_unaligned(const MDBX_val *a, const MDBX_val *b);
|
||||
|
||||
#if MDBX_UNALIGNED_OK < 2 || \
|
||||
(MDBX_DEBUG || MDBX_FORCE_ASSERTIONS || !defined(NDEBUG))
|
||||
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL int
|
||||
/* Compare two items pointing at 2-byte aligned unsigned int's. */
|
||||
cmp_int_align2(const MDBX_val *a, const MDBX_val *b);
|
||||
#else
|
||||
#define cmp_int_align2 cmp_int_unaligned
|
||||
#endif /* !MDBX_UNALIGNED_OK || debug */
|
||||
|
||||
#if MDBX_UNALIGNED_OK < 4 || \
|
||||
(MDBX_DEBUG || MDBX_FORCE_ASSERTIONS || !defined(NDEBUG))
|
||||
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL int
|
||||
/* Compare two items pointing at 4-byte aligned unsigned int's. */
|
||||
cmp_int_align4(const MDBX_val *a, const MDBX_val *b);
|
||||
#else
|
||||
#define cmp_int_align4 cmp_int_unaligned
|
||||
#endif /* !MDBX_UNALIGNED_OK || debug */
|
||||
|
||||
/* Compare two items lexically */
|
||||
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL int cmp_lexical(const MDBX_val *a,
|
||||
const MDBX_val *b);
|
||||
|
||||
/* Compare two items in reverse byte order */
|
||||
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL int cmp_reverse(const MDBX_val *a,
|
||||
const MDBX_val *b);
|
||||
|
||||
/* Fast non-lexically comparator */
|
||||
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL int cmp_lenfast(const MDBX_val *a,
|
||||
const MDBX_val *b);
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL bool
|
||||
eq_fast_slowpath(const uint8_t *a, const uint8_t *b, size_t l);
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION static inline bool eq_fast(const MDBX_val *a,
|
||||
const MDBX_val *b) {
|
||||
return unlikely(a->iov_len == b->iov_len) &&
|
||||
eq_fast_slowpath(a->iov_base, b->iov_base, a->iov_len);
|
||||
}
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL int
|
||||
cmp_equal_or_greater(const MDBX_val *a, const MDBX_val *b);
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL int
|
||||
cmp_equal_or_wrong(const MDBX_val *a, const MDBX_val *b);
|
||||
|
||||
static inline MDBX_cmp_func *builtin_keycmp(MDBX_db_flags_t flags) {
|
||||
return (flags & MDBX_REVERSEKEY) ? cmp_reverse
|
||||
: (flags & MDBX_INTEGERKEY) ? cmp_int_align2
|
||||
: cmp_lexical;
|
||||
}
|
||||
|
||||
static inline MDBX_cmp_func *builtin_datacmp(MDBX_db_flags_t flags) {
|
||||
return !(flags & MDBX_DUPSORT)
|
||||
? cmp_lenfast
|
||||
: ((flags & MDBX_INTEGERDUP)
|
||||
? cmp_int_unaligned
|
||||
: ((flags & MDBX_REVERSEDUP) ? cmp_reverse : cmp_lexical));
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
MDBX_INTERNAL uint32_t combine_durability_flags(const uint32_t a,
|
||||
const uint32_t b);
|
||||
|
||||
MDBX_CONST_FUNCTION static inline lck_t *lckless_stub(const MDBX_env *env) {
|
||||
uintptr_t stub = (uintptr_t)&env->lckless_placeholder;
|
||||
/* align to avoid false-positive alarm from UndefinedBehaviorSanitizer */
|
||||
stub = (stub + MDBX_CACHELINE_SIZE - 1) & ~(MDBX_CACHELINE_SIZE - 1);
|
||||
return (lck_t *)stub;
|
||||
}
|
||||
|
||||
#if !(defined(_WIN32) || defined(_WIN64))
|
||||
MDBX_MAYBE_UNUSED static inline int ignore_enosys(int err) {
|
||||
#ifdef ENOSYS
|
||||
if (err == ENOSYS)
|
||||
return MDBX_RESULT_TRUE;
|
||||
#endif /* ENOSYS */
|
||||
#ifdef ENOIMPL
|
||||
if (err == ENOIMPL)
|
||||
return MDBX_RESULT_TRUE;
|
||||
#endif /* ENOIMPL */
|
||||
#ifdef ENOTSUP
|
||||
if (err == ENOTSUP)
|
||||
return MDBX_RESULT_TRUE;
|
||||
#endif /* ENOTSUP */
|
||||
#ifdef ENOSUPP
|
||||
if (err == ENOSUPP)
|
||||
return MDBX_RESULT_TRUE;
|
||||
#endif /* ENOSUPP */
|
||||
#ifdef EOPNOTSUPP
|
||||
if (err == EOPNOTSUPP)
|
||||
return MDBX_RESULT_TRUE;
|
||||
#endif /* EOPNOTSUPP */
|
||||
if (err == EAGAIN)
|
||||
return MDBX_RESULT_TRUE;
|
||||
return err;
|
||||
}
|
||||
#endif /* defined(_WIN32) || defined(_WIN64) */
|
||||
|
||||
static inline int check_env(const MDBX_env *env, const bool wanna_active) {
|
||||
if (unlikely(!env))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(env->signature.weak != env_signature))
|
||||
return MDBX_EBADSIGN;
|
||||
|
||||
if (unlikely(env->flags & ENV_FATAL_ERROR))
|
||||
return MDBX_PANIC;
|
||||
|
||||
if (wanna_active) {
|
||||
#if MDBX_ENV_CHECKPID
|
||||
if (unlikely(env->pid != osal_getpid()) && env->pid) {
|
||||
((MDBX_env *)env)->flags |= ENV_FATAL_ERROR;
|
||||
return MDBX_PANIC;
|
||||
}
|
||||
#endif /* MDBX_ENV_CHECKPID */
|
||||
if (unlikely((env->flags & ENV_ACTIVE) == 0))
|
||||
return MDBX_EPERM;
|
||||
eASSERT(env, env->dxb_mmap.base != nullptr);
|
||||
}
|
||||
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
static inline int check_txn(const MDBX_txn *txn, int bad_bits) {
|
||||
if (unlikely(!txn))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(txn->signature != txn_signature))
|
||||
return MDBX_EBADSIGN;
|
||||
|
||||
if (unlikely(txn->flags & bad_bits))
|
||||
return MDBX_BAD_TXN;
|
||||
|
||||
tASSERT(txn, (txn->flags & MDBX_TXN_FINISHED) ||
|
||||
(txn->flags & MDBX_NOSTICKYTHREADS) ==
|
||||
(txn->env->flags & MDBX_NOSTICKYTHREADS));
|
||||
#if MDBX_TXN_CHECKOWNER
|
||||
STATIC_ASSERT((long)MDBX_NOSTICKYTHREADS > (long)MDBX_TXN_FINISHED);
|
||||
if ((txn->flags & (MDBX_NOSTICKYTHREADS | MDBX_TXN_FINISHED)) <
|
||||
MDBX_TXN_FINISHED &&
|
||||
unlikely(txn->owner != osal_thread_self()))
|
||||
return txn->owner ? MDBX_THREAD_MISMATCH : MDBX_BAD_TXN;
|
||||
#endif /* MDBX_TXN_CHECKOWNER */
|
||||
|
||||
if (bad_bits && unlikely(!txn->env->dxb_mmap.base))
|
||||
return MDBX_EPERM;
|
||||
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
static inline int check_txn_rw(const MDBX_txn *txn, int bad_bits) {
|
||||
int err = check_txn(txn, bad_bits);
|
||||
if (unlikely(err))
|
||||
return err;
|
||||
|
||||
if (unlikely(txn->flags & MDBX_TXN_RDONLY))
|
||||
return MDBX_EACCESS;
|
||||
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
MDBX_INTERNAL void mincore_clean_cache(const MDBX_env *const env);
|
||||
|
||||
MDBX_INTERNAL void update_mlcnt(const MDBX_env *env,
|
||||
const pgno_t new_aligned_mlocked_pgno,
|
||||
const bool lock_not_release);
|
||||
|
||||
MDBX_INTERNAL void munlock_after(const MDBX_env *env, const pgno_t aligned_pgno,
|
||||
const size_t end_bytes);
|
||||
|
||||
MDBX_INTERNAL void munlock_all(const MDBX_env *env);
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Cache coherence and mmap invalidation */
|
||||
#ifndef MDBX_CPU_WRITEBACK_INCOHERENT
|
||||
#error "The MDBX_CPU_WRITEBACK_INCOHERENT must be defined before"
|
||||
#elif MDBX_CPU_WRITEBACK_INCOHERENT
|
||||
#define osal_flush_incoherent_cpu_writeback() osal_memory_barrier()
|
||||
#else
|
||||
#define osal_flush_incoherent_cpu_writeback() osal_compiler_barrier()
|
||||
#endif /* MDBX_CPU_WRITEBACK_INCOHERENT */
|
||||
|
||||
MDBX_MAYBE_UNUSED static inline void
|
||||
osal_flush_incoherent_mmap(const void *addr, size_t nbytes,
|
||||
const intptr_t pagesize) {
|
||||
#ifndef MDBX_MMAP_INCOHERENT_FILE_WRITE
|
||||
#error "The MDBX_MMAP_INCOHERENT_FILE_WRITE must be defined before"
|
||||
#elif MDBX_MMAP_INCOHERENT_FILE_WRITE
|
||||
char *const begin = (char *)(-pagesize & (intptr_t)addr);
|
||||
char *const end =
|
||||
(char *)(-pagesize & (intptr_t)((char *)addr + nbytes + pagesize - 1));
|
||||
int err = msync(begin, end - begin, MS_SYNC | MS_INVALIDATE) ? errno : 0;
|
||||
eASSERT(nullptr, err == 0);
|
||||
(void)err;
|
||||
#else
|
||||
(void)pagesize;
|
||||
#endif /* MDBX_MMAP_INCOHERENT_FILE_WRITE */
|
||||
|
||||
#ifndef MDBX_MMAP_INCOHERENT_CPU_CACHE
|
||||
#error "The MDBX_MMAP_INCOHERENT_CPU_CACHE must be defined before"
|
||||
#elif MDBX_MMAP_INCOHERENT_CPU_CACHE
|
||||
#ifdef DCACHE
|
||||
/* MIPS has cache coherency issues.
|
||||
* Note: for any nbytes >= on-chip cache size, entire is flushed. */
|
||||
cacheflush((void *)addr, nbytes, DCACHE);
|
||||
#else
|
||||
#error "Oops, cacheflush() not available"
|
||||
#endif /* DCACHE */
|
||||
#endif /* MDBX_MMAP_INCOHERENT_CPU_CACHE */
|
||||
|
||||
#if !MDBX_MMAP_INCOHERENT_FILE_WRITE && !MDBX_MMAP_INCOHERENT_CPU_CACHE
|
||||
(void)addr;
|
||||
(void)nbytes;
|
||||
#endif
|
||||
}
|
198
src/coherency.c
Normal file
198
src/coherency.c
Normal file
@ -0,0 +1,198 @@
|
||||
/// \copyright SPDX-License-Identifier: Apache-2.0
|
||||
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
|
||||
|
||||
#include "internals.h"
|
||||
|
||||
/* check against https://libmdbx.dqdkfa.ru/dead-github/issues/269 */
|
||||
static bool coherency_check(const MDBX_env *env, const txnid_t txnid,
|
||||
const volatile tree_t *trees,
|
||||
const volatile meta_t *meta, bool report) {
|
||||
const txnid_t freedb_mod_txnid = trees[FREE_DBI].mod_txnid;
|
||||
const txnid_t maindb_mod_txnid = trees[MAIN_DBI].mod_txnid;
|
||||
const pgno_t last_pgno = meta->geometry.now;
|
||||
|
||||
const pgno_t freedb_root_pgno = trees[FREE_DBI].root;
|
||||
const page_t *freedb_root =
|
||||
(env->dxb_mmap.base && freedb_root_pgno < last_pgno)
|
||||
? pgno2page(env, freedb_root_pgno)
|
||||
: nullptr;
|
||||
|
||||
const pgno_t maindb_root_pgno = trees[MAIN_DBI].root;
|
||||
const page_t *maindb_root =
|
||||
(env->dxb_mmap.base && maindb_root_pgno < last_pgno)
|
||||
? pgno2page(env, maindb_root_pgno)
|
||||
: nullptr;
|
||||
const uint64_t magic_and_version =
|
||||
unaligned_peek_u64_volatile(4, &meta->magic_and_version);
|
||||
|
||||
bool ok = true;
|
||||
if (freedb_root_pgno != P_INVALID &&
|
||||
unlikely(freedb_root_pgno >= last_pgno)) {
|
||||
if (report)
|
||||
WARNING(
|
||||
"catch invalid %sdb root %" PRIaPGNO " for meta_txnid %" PRIaTXN
|
||||
" %s",
|
||||
"free", freedb_root_pgno, txnid,
|
||||
(env->stuck_meta < 0)
|
||||
? "(workaround for incoherent flaw of unified page/buffer cache)"
|
||||
: "(wagering meta)");
|
||||
ok = false;
|
||||
}
|
||||
if (maindb_root_pgno != P_INVALID &&
|
||||
unlikely(maindb_root_pgno >= last_pgno)) {
|
||||
if (report)
|
||||
WARNING(
|
||||
"catch invalid %sdb root %" PRIaPGNO " for meta_txnid %" PRIaTXN
|
||||
" %s",
|
||||
"main", maindb_root_pgno, txnid,
|
||||
(env->stuck_meta < 0)
|
||||
? "(workaround for incoherent flaw of unified page/buffer cache)"
|
||||
: "(wagering meta)");
|
||||
ok = false;
|
||||
}
|
||||
if (unlikely(txnid < freedb_mod_txnid ||
|
||||
(!freedb_mod_txnid && freedb_root &&
|
||||
likely(magic_and_version == MDBX_DATA_MAGIC)))) {
|
||||
if (report)
|
||||
WARNING(
|
||||
"catch invalid %sdb.mod_txnid %" PRIaTXN " for meta_txnid %" PRIaTXN
|
||||
" %s",
|
||||
"free", freedb_mod_txnid, txnid,
|
||||
(env->stuck_meta < 0)
|
||||
? "(workaround for incoherent flaw of unified page/buffer cache)"
|
||||
: "(wagering meta)");
|
||||
ok = false;
|
||||
}
|
||||
if (unlikely(txnid < maindb_mod_txnid ||
|
||||
(!maindb_mod_txnid && maindb_root &&
|
||||
likely(magic_and_version == MDBX_DATA_MAGIC)))) {
|
||||
if (report)
|
||||
WARNING(
|
||||
"catch invalid %sdb.mod_txnid %" PRIaTXN " for meta_txnid %" PRIaTXN
|
||||
" %s",
|
||||
"main", maindb_mod_txnid, txnid,
|
||||
(env->stuck_meta < 0)
|
||||
? "(workaround for incoherent flaw of unified page/buffer cache)"
|
||||
: "(wagering meta)");
|
||||
ok = false;
|
||||
}
|
||||
if (likely(freedb_root && freedb_mod_txnid)) {
|
||||
VALGRIND_MAKE_MEM_DEFINED(freedb_root, sizeof(freedb_root->txnid));
|
||||
MDBX_ASAN_UNPOISON_MEMORY_REGION(freedb_root, sizeof(freedb_root->txnid));
|
||||
const txnid_t root_txnid = freedb_root->txnid;
|
||||
if (unlikely(root_txnid != freedb_mod_txnid)) {
|
||||
if (report)
|
||||
WARNING("catch invalid root_page %" PRIaPGNO " mod_txnid %" PRIaTXN
|
||||
" for %sdb.mod_txnid %" PRIaTXN " %s",
|
||||
freedb_root_pgno, root_txnid, "free", freedb_mod_txnid,
|
||||
(env->stuck_meta < 0) ? "(workaround for incoherent flaw of "
|
||||
"unified page/buffer cache)"
|
||||
: "(wagering meta)");
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
if (likely(maindb_root && maindb_mod_txnid)) {
|
||||
VALGRIND_MAKE_MEM_DEFINED(maindb_root, sizeof(maindb_root->txnid));
|
||||
MDBX_ASAN_UNPOISON_MEMORY_REGION(maindb_root, sizeof(maindb_root->txnid));
|
||||
const txnid_t root_txnid = maindb_root->txnid;
|
||||
if (unlikely(root_txnid != maindb_mod_txnid)) {
|
||||
if (report)
|
||||
WARNING("catch invalid root_page %" PRIaPGNO " mod_txnid %" PRIaTXN
|
||||
" for %sdb.mod_txnid %" PRIaTXN " %s",
|
||||
maindb_root_pgno, root_txnid, "main", maindb_mod_txnid,
|
||||
(env->stuck_meta < 0) ? "(workaround for incoherent flaw of "
|
||||
"unified page/buffer cache)"
|
||||
: "(wagering meta)");
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
if (unlikely(!ok) && report)
|
||||
env->lck->pgops.incoherence.weak =
|
||||
(env->lck->pgops.incoherence.weak >= INT32_MAX)
|
||||
? INT32_MAX
|
||||
: env->lck->pgops.incoherence.weak + 1;
|
||||
return ok;
|
||||
}
|
||||
|
||||
__cold int coherency_timeout(uint64_t *timestamp, intptr_t pgno,
|
||||
const MDBX_env *env) {
|
||||
if (likely(timestamp && *timestamp == 0))
|
||||
*timestamp = osal_monotime();
|
||||
else if (unlikely(!timestamp || osal_monotime() - *timestamp >
|
||||
osal_16dot16_to_monotime(65536 / 10))) {
|
||||
if (pgno >= 0 && pgno != env->stuck_meta)
|
||||
ERROR("bailout waiting for %" PRIuSIZE " page arrival %s", pgno,
|
||||
"(workaround for incoherent flaw of unified page/buffer cache)");
|
||||
else if (env->stuck_meta < 0)
|
||||
ERROR("bailout waiting for valid snapshot (%s)",
|
||||
"workaround for incoherent flaw of unified page/buffer cache");
|
||||
return MDBX_PROBLEM;
|
||||
}
|
||||
|
||||
osal_memory_fence(mo_AcquireRelease, true);
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
SwitchToThread();
|
||||
#elif defined(__linux__) || defined(__gnu_linux__) || defined(_UNIX03_SOURCE)
|
||||
sched_yield();
|
||||
#elif (defined(_GNU_SOURCE) && __GLIBC_PREREQ(2, 1)) || defined(_OPEN_THREADS)
|
||||
pthread_yield();
|
||||
#else
|
||||
usleep(42);
|
||||
#endif
|
||||
return MDBX_RESULT_TRUE;
|
||||
}
|
||||
|
||||
/* check with timeout as the workaround
|
||||
* for https://libmdbx.dqdkfa.ru/dead-github/issues/269 */
|
||||
__hot int coherency_check_head(MDBX_txn *txn, const meta_ptr_t head,
|
||||
uint64_t *timestamp) {
|
||||
/* Copy the DB info and flags */
|
||||
txn->geo = head.ptr_v->geometry;
|
||||
memcpy(txn->dbs, &head.ptr_c->trees, sizeof(head.ptr_c->trees));
|
||||
STATIC_ASSERT(sizeof(head.ptr_c->trees) == CORE_DBS * sizeof(tree_t));
|
||||
VALGRIND_MAKE_MEM_UNDEFINED(txn->dbs + CORE_DBS,
|
||||
txn->env->max_dbi - CORE_DBS);
|
||||
txn->canary = head.ptr_v->canary;
|
||||
|
||||
if (unlikely(!coherency_check(txn->env, head.txnid, txn->dbs, head.ptr_v,
|
||||
*timestamp == 0)))
|
||||
return coherency_timeout(timestamp, -1, txn->env);
|
||||
|
||||
tASSERT(txn, txn->dbs[FREE_DBI].flags == MDBX_INTEGERKEY);
|
||||
tASSERT(txn, check_sdb_flags(txn->dbs[MAIN_DBI].flags));
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
int coherency_check_written(const MDBX_env *env, const txnid_t txnid,
|
||||
const volatile meta_t *meta, const intptr_t pgno,
|
||||
uint64_t *timestamp) {
|
||||
const bool report = !(timestamp && *timestamp);
|
||||
const txnid_t head_txnid = meta_txnid(meta);
|
||||
if (unlikely(head_txnid < MIN_TXNID || head_txnid < txnid)) {
|
||||
if (report) {
|
||||
env->lck->pgops.incoherence.weak =
|
||||
(env->lck->pgops.incoherence.weak >= INT32_MAX)
|
||||
? INT32_MAX
|
||||
: env->lck->pgops.incoherence.weak + 1;
|
||||
WARNING("catch %s txnid %" PRIaTXN " for meta_%" PRIaPGNO " %s",
|
||||
(head_txnid < MIN_TXNID) ? "invalid" : "unexpected", head_txnid,
|
||||
bytes2pgno(env, ptr_dist(meta, env->dxb_mmap.base)),
|
||||
"(workaround for incoherent flaw of unified page/buffer cache)");
|
||||
}
|
||||
return coherency_timeout(timestamp, pgno, env);
|
||||
}
|
||||
if (unlikely(
|
||||
!coherency_check(env, head_txnid, &meta->trees.gc, meta, report)))
|
||||
return coherency_timeout(timestamp, pgno, env);
|
||||
|
||||
eASSERT(env, meta->trees.gc.flags == MDBX_INTEGERKEY);
|
||||
eASSERT(env, check_sdb_flags(meta->trees.main.flags));
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
bool coherency_check_meta(const MDBX_env *env, const volatile meta_t *meta,
|
||||
bool report) {
|
||||
uint64_t timestamp = 0;
|
||||
return coherency_check_written(env, 0, meta, -1,
|
||||
report ? ×tamp : nullptr) == MDBX_SUCCESS;
|
||||
}
|
768
src/cold.c
Normal file
768
src/cold.c
Normal file
@ -0,0 +1,768 @@
|
||||
/// \copyright SPDX-License-Identifier: Apache-2.0
|
||||
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
|
||||
|
||||
#include "internals.h"
|
||||
|
||||
__cold size_t mdbx_default_pagesize(void) {
|
||||
size_t pagesize = globals.sys_pagesize;
|
||||
ENSURE(nullptr, is_powerof2(pagesize));
|
||||
pagesize = (pagesize >= MDBX_MIN_PAGESIZE) ? pagesize : MDBX_MIN_PAGESIZE;
|
||||
pagesize = (pagesize <= MDBX_MAX_PAGESIZE) ? pagesize : MDBX_MAX_PAGESIZE;
|
||||
return pagesize;
|
||||
}
|
||||
|
||||
__cold intptr_t mdbx_limits_dbsize_min(intptr_t pagesize) {
|
||||
if (pagesize < 1)
|
||||
pagesize = (intptr_t)mdbx_default_pagesize();
|
||||
else if (unlikely(pagesize < (intptr_t)MDBX_MIN_PAGESIZE ||
|
||||
pagesize > (intptr_t)MDBX_MAX_PAGESIZE ||
|
||||
!is_powerof2((size_t)pagesize)))
|
||||
return -1;
|
||||
|
||||
return MIN_PAGENO * pagesize;
|
||||
}
|
||||
|
||||
__cold intptr_t mdbx_limits_dbsize_max(intptr_t pagesize) {
|
||||
if (pagesize < 1)
|
||||
pagesize = (intptr_t)mdbx_default_pagesize();
|
||||
else if (unlikely(pagesize < (intptr_t)MDBX_MIN_PAGESIZE ||
|
||||
pagesize > (intptr_t)MDBX_MAX_PAGESIZE ||
|
||||
!is_powerof2((size_t)pagesize)))
|
||||
return -1;
|
||||
|
||||
STATIC_ASSERT(MAX_MAPSIZE < INTPTR_MAX);
|
||||
const uint64_t limit = (1 + (uint64_t)MAX_PAGENO) * pagesize;
|
||||
return (limit < MAX_MAPSIZE) ? (intptr_t)limit : (intptr_t)MAX_MAPSIZE;
|
||||
}
|
||||
|
||||
__cold intptr_t mdbx_limits_txnsize_max(intptr_t pagesize) {
|
||||
if (pagesize < 1)
|
||||
pagesize = (intptr_t)mdbx_default_pagesize();
|
||||
else if (unlikely(pagesize < (intptr_t)MDBX_MIN_PAGESIZE ||
|
||||
pagesize > (intptr_t)MDBX_MAX_PAGESIZE ||
|
||||
!is_powerof2((size_t)pagesize)))
|
||||
return -1;
|
||||
|
||||
STATIC_ASSERT(MAX_MAPSIZE < INTPTR_MAX);
|
||||
const uint64_t pgl_limit =
|
||||
pagesize * (uint64_t)(PAGELIST_LIMIT / MDBX_GOLD_RATIO_DBL);
|
||||
const uint64_t map_limit = (uint64_t)(MAX_MAPSIZE / MDBX_GOLD_RATIO_DBL);
|
||||
return (pgl_limit < map_limit) ? (intptr_t)pgl_limit : (intptr_t)map_limit;
|
||||
}
|
||||
|
||||
__cold intptr_t mdbx_limits_keysize_max(intptr_t pagesize,
|
||||
MDBX_db_flags_t flags) {
|
||||
if (pagesize < 1)
|
||||
pagesize = (intptr_t)mdbx_default_pagesize();
|
||||
if (unlikely(pagesize < (intptr_t)MDBX_MIN_PAGESIZE ||
|
||||
pagesize > (intptr_t)MDBX_MAX_PAGESIZE ||
|
||||
!is_powerof2((size_t)pagesize)))
|
||||
return -1;
|
||||
|
||||
return keysize_max(pagesize, flags);
|
||||
}
|
||||
|
||||
__cold int mdbx_env_get_maxkeysize_ex(const MDBX_env *env,
|
||||
MDBX_db_flags_t flags) {
|
||||
if (unlikely(!env || env->signature.weak != env_signature))
|
||||
return -1;
|
||||
|
||||
return (int)mdbx_limits_keysize_max((intptr_t)env->ps, flags);
|
||||
}
|
||||
|
||||
__cold int mdbx_env_get_maxkeysize(const MDBX_env *env) {
|
||||
return mdbx_env_get_maxkeysize_ex(env, MDBX_DUPSORT);
|
||||
}
|
||||
|
||||
__cold intptr_t mdbx_limits_keysize_min(MDBX_db_flags_t flags) {
|
||||
return keysize_min(flags);
|
||||
}
|
||||
|
||||
__cold intptr_t mdbx_limits_valsize_max(intptr_t pagesize,
|
||||
MDBX_db_flags_t flags) {
|
||||
if (pagesize < 1)
|
||||
pagesize = (intptr_t)mdbx_default_pagesize();
|
||||
if (unlikely(pagesize < (intptr_t)MDBX_MIN_PAGESIZE ||
|
||||
pagesize > (intptr_t)MDBX_MAX_PAGESIZE ||
|
||||
!is_powerof2((size_t)pagesize)))
|
||||
return -1;
|
||||
|
||||
return valsize_max(pagesize, flags);
|
||||
}
|
||||
|
||||
__cold int mdbx_env_get_maxvalsize_ex(const MDBX_env *env,
|
||||
MDBX_db_flags_t flags) {
|
||||
if (unlikely(!env || env->signature.weak != env_signature))
|
||||
return -1;
|
||||
|
||||
return (int)mdbx_limits_valsize_max((intptr_t)env->ps, flags);
|
||||
}
|
||||
|
||||
__cold intptr_t mdbx_limits_valsize_min(MDBX_db_flags_t flags) {
|
||||
return valsize_min(flags);
|
||||
}
|
||||
|
||||
__cold intptr_t mdbx_limits_pairsize4page_max(intptr_t pagesize,
|
||||
MDBX_db_flags_t flags) {
|
||||
if (pagesize < 1)
|
||||
pagesize = (intptr_t)mdbx_default_pagesize();
|
||||
if (unlikely(pagesize < (intptr_t)MDBX_MIN_PAGESIZE ||
|
||||
pagesize > (intptr_t)MDBX_MAX_PAGESIZE ||
|
||||
!is_powerof2((size_t)pagesize)))
|
||||
return -1;
|
||||
|
||||
if (flags &
|
||||
(MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_INTEGERDUP | MDBX_REVERSEDUP))
|
||||
return BRANCH_NODE_MAX(pagesize) - NODESIZE;
|
||||
|
||||
return LEAF_NODE_MAX(pagesize) - NODESIZE;
|
||||
}
|
||||
|
||||
__cold int mdbx_env_get_pairsize4page_max(const MDBX_env *env,
|
||||
MDBX_db_flags_t flags) {
|
||||
if (unlikely(!env || env->signature.weak != env_signature))
|
||||
return -1;
|
||||
|
||||
return (int)mdbx_limits_pairsize4page_max((intptr_t)env->ps, flags);
|
||||
}
|
||||
|
||||
__cold intptr_t mdbx_limits_valsize4page_max(intptr_t pagesize,
|
||||
MDBX_db_flags_t flags) {
|
||||
if (pagesize < 1)
|
||||
pagesize = (intptr_t)mdbx_default_pagesize();
|
||||
if (unlikely(pagesize < (intptr_t)MDBX_MIN_PAGESIZE ||
|
||||
pagesize > (intptr_t)MDBX_MAX_PAGESIZE ||
|
||||
!is_powerof2((size_t)pagesize)))
|
||||
return -1;
|
||||
|
||||
if (flags &
|
||||
(MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_INTEGERDUP | MDBX_REVERSEDUP))
|
||||
return valsize_max(pagesize, flags);
|
||||
|
||||
return PAGESPACE(pagesize);
|
||||
}
|
||||
|
||||
__cold int mdbx_env_get_valsize4page_max(const MDBX_env *env,
|
||||
MDBX_db_flags_t flags) {
|
||||
if (unlikely(!env || env->signature.weak != env_signature))
|
||||
return -1;
|
||||
|
||||
return (int)mdbx_limits_valsize4page_max((intptr_t)env->ps, flags);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
__cold static void stat_add(const tree_t *db, MDBX_stat *const st,
|
||||
const size_t bytes) {
|
||||
st->ms_depth += db->height;
|
||||
st->ms_branch_pages += db->branch_pages;
|
||||
st->ms_leaf_pages += db->leaf_pages;
|
||||
st->ms_overflow_pages += db->large_pages;
|
||||
st->ms_entries += db->items;
|
||||
if (likely(bytes >=
|
||||
offsetof(MDBX_stat, ms_mod_txnid) + sizeof(st->ms_mod_txnid)))
|
||||
st->ms_mod_txnid =
|
||||
(st->ms_mod_txnid > db->mod_txnid) ? st->ms_mod_txnid : db->mod_txnid;
|
||||
}
|
||||
|
||||
__cold static int stat_acc(const MDBX_txn *txn, MDBX_stat *st, size_t bytes) {
|
||||
int err = check_txn(txn, MDBX_TXN_BLOCKED);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
return err;
|
||||
|
||||
cursor_couple_t cx;
|
||||
err = cursor_init(&cx.outer, (MDBX_txn *)txn, MAIN_DBI);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
return err;
|
||||
|
||||
const MDBX_env *const env = txn->env;
|
||||
st->ms_psize = env->ps;
|
||||
TXN_FOREACH_DBI_FROM(
|
||||
txn, dbi,
|
||||
/* assuming GC is internal and not subject for accounting */ MAIN_DBI) {
|
||||
if ((txn->dbi_state[dbi] & (DBI_VALID | DBI_STALE)) == DBI_VALID)
|
||||
stat_add(txn->dbs + dbi, st, bytes);
|
||||
}
|
||||
|
||||
if (!(txn->dbs[MAIN_DBI].flags & MDBX_DUPSORT) &&
|
||||
txn->dbs[MAIN_DBI].items /* TODO: use `md_subs` field */) {
|
||||
|
||||
/* scan and account not opened named subDBs */
|
||||
err = tree_search(&cx.outer, nullptr, Z_FIRST);
|
||||
while (err == MDBX_SUCCESS) {
|
||||
const page_t *mp = cx.outer.pg[cx.outer.top];
|
||||
for (size_t i = 0; i < page_numkeys(mp); i++) {
|
||||
const node_t *node = page_node(mp, i);
|
||||
if (node_flags(node) != N_SUBDATA)
|
||||
continue;
|
||||
if (unlikely(node_ds(node) != sizeof(tree_t))) {
|
||||
ERROR("%s/%d: %s %zu", "MDBX_CORRUPTED", MDBX_CORRUPTED,
|
||||
"invalid subDb node size", node_ds(node));
|
||||
return MDBX_CORRUPTED;
|
||||
}
|
||||
|
||||
/* skip opened and already accounted */
|
||||
const MDBX_val name = {node_key(node), node_ks(node)};
|
||||
TXN_FOREACH_DBI_USER(txn, dbi) {
|
||||
if ((txn->dbi_state[dbi] & (DBI_VALID | DBI_STALE)) == DBI_VALID &&
|
||||
env->kvs[MAIN_DBI].clc.k.cmp(&name, &env->kvs[dbi].name) == 0) {
|
||||
node = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (node) {
|
||||
tree_t db;
|
||||
memcpy(&db, node_data(node), sizeof(db));
|
||||
stat_add(&db, st, bytes);
|
||||
}
|
||||
}
|
||||
err = cursor_sibling_right(&cx.outer);
|
||||
}
|
||||
if (unlikely(err != MDBX_NOTFOUND))
|
||||
return err;
|
||||
}
|
||||
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
__cold int mdbx_env_stat_ex(const MDBX_env *env, const MDBX_txn *txn,
|
||||
MDBX_stat *dest, size_t bytes) {
|
||||
if (unlikely(!dest))
|
||||
return MDBX_EINVAL;
|
||||
const size_t size_before_modtxnid = offsetof(MDBX_stat, ms_mod_txnid);
|
||||
if (unlikely(bytes != sizeof(MDBX_stat)) && bytes != size_before_modtxnid)
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (likely(txn)) {
|
||||
if (env && unlikely(txn->env != env))
|
||||
return MDBX_EINVAL;
|
||||
return stat_acc(txn, dest, bytes);
|
||||
}
|
||||
|
||||
int err = check_env(env, true);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
return err;
|
||||
|
||||
if (env->txn && env_txn0_owned(env))
|
||||
/* inside write-txn */
|
||||
return stat_acc(env->txn, dest, bytes);
|
||||
|
||||
MDBX_txn *tmp_txn;
|
||||
err = mdbx_txn_begin((MDBX_env *)env, nullptr, MDBX_TXN_RDONLY, &tmp_txn);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
return err;
|
||||
|
||||
const int rc = stat_acc(tmp_txn, dest, bytes);
|
||||
err = mdbx_txn_abort(tmp_txn);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
return err;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
static size_t estimate_rss(size_t database_bytes) {
|
||||
return database_bytes + database_bytes / 64 +
|
||||
(512 + MDBX_WORDBITS * 16) * MEGABYTE;
|
||||
}
|
||||
|
||||
__cold int mdbx_env_warmup(const MDBX_env *env, const MDBX_txn *txn,
|
||||
MDBX_warmup_flags_t flags,
|
||||
unsigned timeout_seconds_16dot16) {
|
||||
if (unlikely(env == nullptr && txn == nullptr))
|
||||
return MDBX_EINVAL;
|
||||
if (unlikely(flags >
|
||||
(MDBX_warmup_force | MDBX_warmup_oomsafe | MDBX_warmup_lock |
|
||||
MDBX_warmup_touchlimit | MDBX_warmup_release)))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (txn) {
|
||||
int err = check_txn(txn, MDBX_TXN_BLOCKED - MDBX_TXN_ERROR);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
return err;
|
||||
}
|
||||
if (env) {
|
||||
int err = check_env(env, false);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
return err;
|
||||
if (txn && unlikely(txn->env != env))
|
||||
return MDBX_EINVAL;
|
||||
} else {
|
||||
env = txn->env;
|
||||
}
|
||||
|
||||
const uint64_t timeout_monotime =
|
||||
(timeout_seconds_16dot16 && (flags & MDBX_warmup_force))
|
||||
? osal_monotime() + osal_16dot16_to_monotime(timeout_seconds_16dot16)
|
||||
: 0;
|
||||
|
||||
if (flags & MDBX_warmup_release)
|
||||
munlock_all(env);
|
||||
|
||||
pgno_t used_pgno;
|
||||
if (txn) {
|
||||
used_pgno = txn->geo.first_unallocated;
|
||||
} else {
|
||||
const troika_t troika = meta_tap(env);
|
||||
used_pgno = meta_recent(env, &troika).ptr_v->geometry.first_unallocated;
|
||||
}
|
||||
const size_t used_range = pgno_align2os_bytes(env, used_pgno);
|
||||
const pgno_t mlock_pgno = bytes2pgno(env, used_range);
|
||||
|
||||
int rc = MDBX_SUCCESS;
|
||||
if (flags & MDBX_warmup_touchlimit) {
|
||||
const size_t estimated_rss = estimate_rss(used_range);
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
SIZE_T current_ws_lower, current_ws_upper;
|
||||
if (GetProcessWorkingSetSize(GetCurrentProcess(), ¤t_ws_lower,
|
||||
¤t_ws_upper) &&
|
||||
current_ws_lower < estimated_rss) {
|
||||
const SIZE_T ws_lower = estimated_rss;
|
||||
const SIZE_T ws_upper =
|
||||
(MDBX_WORDBITS == 32 && ws_lower > MEGABYTE * 2048)
|
||||
? ws_lower
|
||||
: ws_lower + MDBX_WORDBITS * MEGABYTE * 32;
|
||||
if (!SetProcessWorkingSetSize(GetCurrentProcess(), ws_lower, ws_upper)) {
|
||||
rc = (int)GetLastError();
|
||||
WARNING("SetProcessWorkingSetSize(%zu, %zu) error %d", ws_lower,
|
||||
ws_upper, rc);
|
||||
}
|
||||
}
|
||||
#endif /* Windows */
|
||||
#ifdef RLIMIT_RSS
|
||||
struct rlimit rss;
|
||||
if (getrlimit(RLIMIT_RSS, &rss) == 0 && rss.rlim_cur < estimated_rss) {
|
||||
rss.rlim_cur = estimated_rss;
|
||||
if (rss.rlim_max < estimated_rss)
|
||||
rss.rlim_max = estimated_rss;
|
||||
if (setrlimit(RLIMIT_RSS, &rss)) {
|
||||
rc = errno;
|
||||
WARNING("setrlimit(%s, {%zu, %zu}) error %d", "RLIMIT_RSS",
|
||||
(size_t)rss.rlim_cur, (size_t)rss.rlim_max, rc);
|
||||
}
|
||||
}
|
||||
#endif /* RLIMIT_RSS */
|
||||
#ifdef RLIMIT_MEMLOCK
|
||||
if (flags & MDBX_warmup_lock) {
|
||||
struct rlimit memlock;
|
||||
if (getrlimit(RLIMIT_MEMLOCK, &memlock) == 0 &&
|
||||
memlock.rlim_cur < estimated_rss) {
|
||||
memlock.rlim_cur = estimated_rss;
|
||||
if (memlock.rlim_max < estimated_rss)
|
||||
memlock.rlim_max = estimated_rss;
|
||||
if (setrlimit(RLIMIT_MEMLOCK, &memlock)) {
|
||||
rc = errno;
|
||||
WARNING("setrlimit(%s, {%zu, %zu}) error %d", "RLIMIT_MEMLOCK",
|
||||
(size_t)memlock.rlim_cur, (size_t)memlock.rlim_max, rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* RLIMIT_MEMLOCK */
|
||||
(void)estimated_rss;
|
||||
}
|
||||
|
||||
#if defined(MLOCK_ONFAULT) && \
|
||||
((defined(_GNU_SOURCE) && __GLIBC_PREREQ(2, 27)) || \
|
||||
(defined(__ANDROID_API__) && __ANDROID_API__ >= 30)) && \
|
||||
(defined(__linux__) || defined(__gnu_linux__))
|
||||
if ((flags & MDBX_warmup_lock) != 0 &&
|
||||
globals.linux_kernel_version >= 0x04040000 &&
|
||||
atomic_load32(&env->mlocked_pgno, mo_AcquireRelease) < mlock_pgno) {
|
||||
if (mlock2(env->dxb_mmap.base, used_range, MLOCK_ONFAULT)) {
|
||||
rc = errno;
|
||||
WARNING("mlock2(%zu, %s) error %d", used_range, "MLOCK_ONFAULT", rc);
|
||||
} else {
|
||||
update_mlcnt(env, mlock_pgno, true);
|
||||
rc = MDBX_SUCCESS;
|
||||
}
|
||||
if (rc != EINVAL)
|
||||
flags -= MDBX_warmup_lock;
|
||||
}
|
||||
#endif /* MLOCK_ONFAULT */
|
||||
|
||||
int err = MDBX_ENOSYS;
|
||||
#if MDBX_ENABLE_MADVISE
|
||||
err = dxb_set_readahead(env, used_pgno, true, true);
|
||||
#else
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
if (imports.PrefetchVirtualMemory) {
|
||||
WIN32_MEMORY_RANGE_ENTRY hint;
|
||||
hint.VirtualAddress = env->dxb_mmap.base;
|
||||
hint.NumberOfBytes = used_range;
|
||||
if (imports.PrefetchVirtualMemory(GetCurrentProcess(), 1, &hint, 0))
|
||||
err = MDBX_SUCCESS;
|
||||
else {
|
||||
err = (int)GetLastError();
|
||||
ERROR("%s(%zu) error %d", "PrefetchVirtualMemory", used_range, err);
|
||||
}
|
||||
}
|
||||
#endif /* Windows */
|
||||
|
||||
#if defined(POSIX_MADV_WILLNEED)
|
||||
err = posix_madvise(env->dxb_mmap.base, used_range, POSIX_MADV_WILLNEED)
|
||||
? ignore_enosys(errno)
|
||||
: MDBX_SUCCESS;
|
||||
#elif defined(MADV_WILLNEED)
|
||||
err = madvise(env->dxb_mmap.base, used_range, MADV_WILLNEED)
|
||||
? ignore_enosys(errno)
|
||||
: MDBX_SUCCESS;
|
||||
#endif
|
||||
|
||||
#if defined(F_RDADVISE)
|
||||
if (err) {
|
||||
fcntl(env->lazy_fd, F_RDAHEAD, true);
|
||||
struct radvisory hint;
|
||||
hint.ra_offset = 0;
|
||||
hint.ra_count = unlikely(used_range > INT_MAX &&
|
||||
sizeof(used_range) > sizeof(hint.ra_count))
|
||||
? INT_MAX
|
||||
: (int)used_range;
|
||||
err = fcntl(env->lazy_fd, F_RDADVISE, &hint) ? ignore_enosys(errno)
|
||||
: MDBX_SUCCESS;
|
||||
if (err == ENOTTY)
|
||||
err = MDBX_SUCCESS /* Ignore ENOTTY for DB on the ram-disk */;
|
||||
}
|
||||
#endif /* F_RDADVISE */
|
||||
#endif /* MDBX_ENABLE_MADVISE */
|
||||
if (err != MDBX_SUCCESS && rc == MDBX_SUCCESS)
|
||||
rc = err;
|
||||
|
||||
if ((flags & MDBX_warmup_force) != 0 &&
|
||||
(rc == MDBX_SUCCESS || rc == MDBX_ENOSYS)) {
|
||||
const volatile uint8_t *ptr = env->dxb_mmap.base;
|
||||
size_t offset = 0, unused = 42;
|
||||
#if !(defined(_WIN32) || defined(_WIN64))
|
||||
if (flags & MDBX_warmup_oomsafe) {
|
||||
const int null_fd = open("/dev/null", O_WRONLY);
|
||||
if (unlikely(null_fd < 0))
|
||||
rc = errno;
|
||||
else {
|
||||
struct iovec iov[MDBX_AUXILARY_IOV_MAX];
|
||||
for (;;) {
|
||||
unsigned i;
|
||||
for (i = 0; i < MDBX_AUXILARY_IOV_MAX && offset < used_range; ++i) {
|
||||
iov[i].iov_base = (void *)(ptr + offset);
|
||||
iov[i].iov_len = 1;
|
||||
offset += globals.sys_pagesize;
|
||||
}
|
||||
if (unlikely(writev(null_fd, iov, i) < 0)) {
|
||||
rc = errno;
|
||||
if (rc == EFAULT)
|
||||
rc = ENOMEM;
|
||||
break;
|
||||
}
|
||||
if (offset >= used_range) {
|
||||
rc = MDBX_SUCCESS;
|
||||
break;
|
||||
}
|
||||
if (timeout_seconds_16dot16 && osal_monotime() > timeout_monotime) {
|
||||
rc = MDBX_RESULT_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(null_fd);
|
||||
}
|
||||
} else
|
||||
#endif /* Windows */
|
||||
for (;;) {
|
||||
unused += ptr[offset];
|
||||
offset += globals.sys_pagesize;
|
||||
if (offset >= used_range) {
|
||||
rc = MDBX_SUCCESS;
|
||||
break;
|
||||
}
|
||||
if (timeout_seconds_16dot16 && osal_monotime() > timeout_monotime) {
|
||||
rc = MDBX_RESULT_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
(void)unused;
|
||||