├── .clang-format ├── .gitignore ├── .le.ini ├── AUTHORS ├── CMakeLists.txt ├── CMakeSettings.json ├── COPYRIGHT ├── ChangeLog.md ├── GNUmakefile ├── LICENSE ├── Makefile ├── README.md ├── TODO.md ├── cmake ├── compiler.cmake ├── profile.cmake └── utils.cmake ├── docs ├── Doxyfile.in ├── _preface.md ├── _restrictions.md ├── _starting.md └── _toc.md ├── example ├── CMakeLists.txt ├── README.md ├── example-mdbx.c └── sample-bdb.txt ├── mdbx.h ├── mdbx.h++ ├── packages ├── buildroot │ └── 0001-package-libmdbx-new-package-library-database.patch └── rpm.obsolete │ ├── CMakeLists.txt │ ├── build.sh │ └── package.sh ├── src ├── alloy.c ├── base.h ├── bits.md ├── config.h.in ├── core.c ├── debug_begin.h ├── debug_end.h ├── internals.h ├── lck-posix.c ├── lck-windows.c ├── man1 │ ├── mdbx_chk.1 │ ├── mdbx_copy.1 │ ├── mdbx_drop.1 │ ├── mdbx_dump.1 │ ├── mdbx_load.1 │ └── mdbx_stat.1 ├── mdbx.c++ ├── mdbx_chk.c ├── mdbx_copy.c ├── mdbx_drop.c ├── mdbx_dump.c ├── mdbx_load.c ├── mdbx_stat.c ├── ntdll.def ├── options.h ├── osal.c ├── osal.h ├── version.c.in ├── wingetopt.c └── wingetopt.h └── test ├── CMakeLists.txt ├── append.c++ ├── base.h++ ├── cases.c++ ├── chrono.c++ ├── chrono.h++ ├── config.c++ ├── config.h++ ├── copy.c++ ├── dead.c++ ├── dump-load.sh ├── hill.c++ ├── jitter.c++ ├── keygen.c++ ├── keygen.h++ ├── log.c++ ├── log.h++ ├── long_stochastic.sh ├── main.c++ ├── nested.c++ ├── osal-unix.c++ ├── osal-windows.c++ ├── osal.h++ ├── pcrf ├── README.md └── pcrf_test.c ├── stochastic_small.sh ├── stub ├── LICENSE ├── README.md ├── pthread_barrier.c └── pthread_barrier.h ├── test.c++ ├── test.h++ ├── try.c++ ├── ttl.c++ ├── utils.c++ ├── utils.h++ └── valgrind_suppress.txt /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | Standard: Cpp11 3 | ReflowComments: true 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.[ao] 2 | *.bak 3 | *.exe 4 | *.err 5 | *.gcda 6 | *.gcno 7 | *.gcov 8 | *.lo 9 | *.orig 10 | *.rej 11 | *.so 12 | *.dll 13 | *.dylib 14 | *.dSYM 15 | *[~#] 16 | .idea 17 | .vs/ 18 | .vscode/ 19 | cmake-build-* 20 | @* 21 | core 22 | mdbx_example 23 | libmdbx.creator.user 24 | CMakeLists.txt.user 25 | mdbx_chk 26 | mdbx_copy 27 | mdbx_drop 28 | mdbx_dump 29 | mdbx_load 30 | mdbx_stat 31 | mdbx_test 32 | test.log 33 | test/tmp.db 34 | test/tmp.db-lck 35 | tmp.db 36 | tmp.db-lck 37 | valgrind.* 38 | src/version.c 39 | src/config.h 40 | dist/ 41 | dist-check/ 42 | dist-checked.tag 43 | *.tar* 44 | *.zip 45 | docs/Doxyfile 46 | docs/html/ 47 | buildflags.tag 48 | -------------------------------------------------------------------------------- /.le.ini: -------------------------------------------------------------------------------- 1 | tabsize=8 2 | indentsize=8 3 | autoindent=1 4 | bsunindents=1 5 | insert=1 6 | inputmode=0 7 | editmode=1 8 | makebak=0 9 | bakpath= 10 | make=exec make 11 | shell=exec $SHELL 12 | run=exec make run 13 | compile=exec make "$FNAME.o" 14 | scroll=1 15 | hscroll=32 16 | rblock=0 17 | savepos=1 18 | savehst=1 19 | noreg=1 20 | match_case=1 21 | linelen=72 22 | leftmrg=0 23 | flnmarg=0 24 | leftadj=1 25 | rightadj=0 26 | helpcmd=exec /usr/share/le/help 27 | usecolor=1 28 | usetabs=1 29 | scrollbar=0 30 | statusline=0 31 | backupext=.~%d~ 32 | backupnum=9 33 | preferpagetop=1 34 | wordwrap=0 35 | syntaxhl=1 36 | undo_enable=1 37 | undo_min_levels=4 38 | undo_max_memory=128 39 | undo_glue=1 40 | usemouse=0 41 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Contributors 2 | ============ 3 | 4 | - Alexey Naumov 5 | - Andrew Ashikhmin 6 | - Chris Mikkelson 7 | - Claude Brisson 8 | - David Barbour 9 | - David Wilson 10 | - dreamsxin 11 | - Hallvard Furuseth , 12 | - Heiko Becker 13 | - Howard Chu , 14 | - Ignacio Casal Quinteiro 15 | - James Rouzier 16 | - Jean-Christophe DUBOIS 17 | - John Hewson 18 | - Klaus Malorny 19 | - Kurt Zeilenga 20 | - Leonid Yuriev , 21 | - Lorenz Bauer 22 | - Luke Yeager 23 | - Martin Hedenfalk 24 | - Ondrej Kuznik 25 | - Orivej Desh 26 | - Oskari Timperi 27 | - Pavel Medvedev 28 | - Philipp Storz 29 | - Quanah Gibson-Mount 30 | - Salvador Ortiz 31 | - Sebastien Launay 32 | - Vladimir Romanov 33 | - Zano Foundation 34 | - 장세연 35 | -------------------------------------------------------------------------------- /CMakeSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "x64-Debug", 5 | "generator": "Ninja", 6 | "configurationType": "Debug", 7 | "inheritEnvironments": [ "msvc_x64_x64" ], 8 | "buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\libmdbx\\build\\${name}", 9 | "installRoot": "${env.USERPROFILE}\\CMakeBuilds\\libmdbx\\install\\${name}", 10 | "cmakeCommandArgs": "", 11 | "buildCommandArgs": "", 12 | "ctestCommandArgs": "" 13 | }, 14 | { 15 | "name": "x64-Release", 16 | "generator": "Ninja", 17 | "configurationType": "Release", 18 | "inheritEnvironments": [ "msvc_x64_x64" ], 19 | "buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\libmdbx\\build\\${name}", 20 | "installRoot": "${env.USERPROFILE}\\CMakeBuilds\\libmdbx\\install\\${name}", 21 | "cmakeCommandArgs": "", 22 | "buildCommandArgs": "", 23 | "ctestCommandArgs": "" 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyright 2015-2023 Leonid Yuriev . 2 | Copyright 2011-2015 Howard Chu, Symas Corp. 3 | Copyright 2015,2016 Peter-Service R&D LLC. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted only as authorized by the OpenLDAP 8 | Public License. 9 | 10 | A copy of this license is available in the file LICENSE in the 11 | top-level directory of the distribution or, alternatively, at 12 | . 13 | 14 | OpenLDAP is a registered trademark of the OpenLDAP Foundation. 15 | 16 | Individual files and/or contributed packages may be copyright by 17 | other parties and/or subject to additional restrictions. 18 | 19 | This work also contains materials derived from public sources. 20 | 21 | Additional information about OpenLDAP can be obtained at 22 | . 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The OpenLDAP Public License 2 | Version 2.8, 17 August 2003 3 | 4 | Redistribution and use of this software and associated documentation 5 | ("Software"), with or without modification, are permitted provided 6 | that the following conditions are met: 7 | 8 | 1. Redistributions in source form must retain copyright statements 9 | and notices, 10 | 11 | 2. Redistributions in binary form must reproduce applicable copyright 12 | statements and notices, this list of conditions, and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution, and 15 | 16 | 3. Redistributions must contain a verbatim copy of this document. 17 | 18 | The OpenLDAP Foundation may revise this license from time to time. 19 | Each revision is distinguished by a version number. You may use 20 | this Software under terms of this license revision or under the 21 | terms of any subsequent revision of the license. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS 24 | CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, 25 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 26 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 27 | SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S) 28 | OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, 29 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 34 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 | POSSIBILITY OF SUCH DAMAGE. 36 | 37 | The names of the authors and copyright holders must not be used in 38 | advertising or otherwise to promote the sale, use or other dealing 39 | in this Software without specific, written prior permission. Title 40 | to copyright in this Software shall at all times remain with copyright 41 | holders. 42 | 43 | OpenLDAP is a registered trademark of the OpenLDAP Foundation. 44 | 45 | Copyright 1999-2003 The OpenLDAP Foundation, Redwood City, 46 | California, USA. All Rights Reserved. Permission to copy and 47 | distribute verbatim copies of this document is granted. 48 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # This is thunk-Makefile for calling GNU Make 3.80 or above 2 | 3 | all help options \ 4 | clean install install-no-strip install-strip strip tools uninstall \ 5 | bench bench-clean bench-couple bench-quartet bench-triplet re-bench \ 6 | lib libs lib-static lib-shared tools-static \ 7 | libmdbx mdbx mdbx_chk mdbx_copy mdbx_drop mdbx_dump mdbx_load mdbx_stat \ 8 | check dist memcheck cross-gcc cross-qemu doxygen gcc-analyzer reformat \ 9 | release-assets tags test build-test mdbx_test smoke smoke-fault smoke-singleprocess \ 10 | smoke-assertion test-assertion long-test-assertion \ 11 | test-asan test-leak test-singleprocess test-ubsan test-valgrind: 12 | @CC=$(CC) \ 13 | CXX=`if test -n "$(CXX)" && which "$(CXX)" > /dev/null; then echo "$(CXX)"; elif test -n "$(CCC)" && which "$(CCC)" > /dev/null; then echo "$(CCC)"; else echo "c++"; fi` \ 14 | `which gmake || which gnumake || echo 'echo "GNU Make 3.80 or above is required"; exit 2;'` \ 15 | $(MAKEFLAGS) -f GNUmakefile $@ 16 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | TODO 2 | ---- 3 | 4 | Unfortunately, on 2022-04-15 the Github administration, without any 5 | warning nor explanation, deleted _libmdbx_ along with a lot of other 6 | projects, simultaneously blocking access for many developers. Therefore 7 | on 2022-04-21 we have migrated to a reliable trusted infrastructure. 8 | The origin for now is at[GitFlic](https://gitflic.ru/project/erthink/libmdbx) 9 | with backup at [ABF by ROSA Лаб](https://abf.rosalinux.ru/erthink/libmdbx). 10 | For the same reason ~~Github~~ is blacklisted forever. 11 | 12 | So currently most of the links are broken due to noted malicious ~~Github~~ sabotage. 13 | 14 | - [Move most of `mdbx_chk` functional to the library API](https://libmdbx.dqdkfa.ru/dead-github/issues/204). 15 | - [Replace SRW-lock on Windows to allow shrink DB with `MDBX_NOTLS` option](https://libmdbx.dqdkfa.ru/dead-github/issues/210). 16 | - [More flexible support of asynchronous runtime/framework(s)](https://libmdbx.dqdkfa.ru/dead-github/issues/200). 17 | - [Migration guide from LMDB to MDBX](https://libmdbx.dqdkfa.ru/dead-github/issues/199). 18 | - [Support for RAW devices](https://libmdbx.dqdkfa.ru/dead-github/issues/124). 19 | - [Support MessagePack for Keys & Values](https://libmdbx.dqdkfa.ru/dead-github/issues/115). 20 | - [Engage new terminology](https://libmdbx.dqdkfa.ru/dead-github/issues/137). 21 | - Packages for [Astra Linux](https://astralinux.ru/), [ALT Linux](https://www.altlinux.org/), [ROSA Linux](https://www.rosalinux.ru/), etc. 22 | 23 | Done 24 | ---- 25 | 26 | - [Simple careful mode for working with corrupted DB](https://libmdbx.dqdkfa.ru/dead-github/issues/223). 27 | - [Engage an "overlapped I/O" on Windows](https://libmdbx.dqdkfa.ru/dead-github/issues/224). 28 | - [Large/Overflow pages accounting for dirty-room](https://libmdbx.dqdkfa.ru/dead-github/issues/192). 29 | - [Get rid of dirty-pages list in MDBX_WRITEMAP mode](https://libmdbx.dqdkfa.ru/dead-github/issues/193). 30 | -------------------------------------------------------------------------------- /cmake/profile.cmake: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2012-2023 Leonid Yuriev . 2 | ## 3 | ## Licensed under the Apache License, Version 2.0 (the "License"); 4 | ## you may not use this file except in compliance with the License. 5 | ## You may obtain a copy of the License at 6 | ## 7 | ## http://www.apache.org/licenses/LICENSE-2.0 8 | ## 9 | ## Unless required by applicable law or agreed to in writing, software 10 | ## distributed under the License is distributed on an "AS IS" BASIS, 11 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ## See the License for the specific language governing permissions and 13 | ## limitations under the License. 14 | ## 15 | 16 | if(CMAKE_VERSION VERSION_LESS 3.8.2) 17 | cmake_minimum_required(VERSION 3.0.2) 18 | elseif(CMAKE_VERSION VERSION_LESS 3.12) 19 | cmake_minimum_required(VERSION 3.8.2) 20 | else() 21 | cmake_minimum_required(VERSION 3.12) 22 | endif() 23 | 24 | cmake_policy(PUSH) 25 | cmake_policy(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION}) 26 | 27 | include(CheckLibraryExists) 28 | check_library_exists(gcov __gcov_flush "" HAVE_GCOV) 29 | 30 | option(ENABLE_GCOV 31 | "Enable integration with gcov, a code coverage program" OFF) 32 | 33 | option(ENABLE_GPROF 34 | "Enable integration with gprof, a performance analyzing tool" OFF) 35 | 36 | if(CMAKE_CXX_COMPILER_LOADED) 37 | include(CheckIncludeFileCXX) 38 | check_include_file_cxx(valgrind/memcheck.h HAVE_VALGRIND_MEMCHECK_H) 39 | else() 40 | include(CheckIncludeFile) 41 | check_include_file(valgrind/memcheck.h HAVE_VALGRIND_MEMCHECK_H) 42 | endif() 43 | 44 | option(MDBX_USE_VALGRIND "Enable integration with valgrind, a memory analyzing tool" OFF) 45 | if(MDBX_USE_VALGRIND AND NOT HAVE_VALGRIND_MEMCHECK_H) 46 | message(FATAL_ERROR "MDBX_USE_VALGRIND option is set but valgrind/memcheck.h is not found") 47 | endif() 48 | 49 | option(ENABLE_ASAN 50 | "Enable AddressSanitizer, a fast memory error detector based on compiler instrumentation" OFF) 51 | 52 | option(ENABLE_UBSAN 53 | "Enable UndefinedBehaviorSanitizer, a fast undefined behavior detector based on compiler instrumentation" OFF) 54 | 55 | cmake_policy(POP) 56 | -------------------------------------------------------------------------------- /docs/_preface.md: -------------------------------------------------------------------------------- 1 | \page intro Introduction 2 | \section characteristics Characteristics 3 | 4 | Preface {#preface} 5 | ------------------ 6 | 7 | > For the most part, this section is a copy of the corresponding text 8 | > from LMDB description, but with some edits reflecting the improvements 9 | > and enhancements were made in MDBX. 10 | 11 | MDBX is a Btree-based database management library modeled loosely on the 12 | BerkeleyDB API, but much simplified. The entire database (aka "environment") 13 | is exposed in a memory map, and all data fetches return data directly from 14 | the mapped memory, so no malloc's or memcpy's occur during data fetches. 15 | As such, the library is extremely simple because it requires no page caching 16 | layer of its own, and it is extremely high performance and memory-efficient. 17 | It is also fully transactional with full ACID semantics, and when the memory 18 | map is read-only, the database integrity cannot be corrupted by stray pointer 19 | writes from application code. 20 | 21 | The library is fully thread-aware and supports concurrent read/write access 22 | from multiple processes and threads. Data pages use a copy-on-write strategy 23 | so no active data pages are ever overwritten, which also provides resistance 24 | to corruption and eliminates the need of any special recovery procedures 25 | after a system crash. Writes are fully serialized; only one write transaction 26 | may be active at a time, which guarantees that writers can never deadlock. 27 | The database structure is multi-versioned so readers run with no locks; 28 | writers cannot block readers, and readers don't block writers. 29 | 30 | Unlike other well-known database mechanisms which use either write-ahead 31 | transaction logs or append-only data writes, MDBX requires no maintenance 32 | during operation. Both write-ahead loggers and append-only databases require 33 | periodic checkpointing and/or compaction of their log or database files 34 | otherwise they grow without bound. MDBX tracks retired/freed pages within the 35 | database and re-uses them for new write operations, so the database size does 36 | not grow without bound in normal use. It is worth noting that the "next" 37 | version libmdbx (\ref MithrilDB) will solve this problem. 38 | 39 | The memory map can be used as a read-only or read-write map. It is read-only 40 | by default as this provides total immunity to corruption. Using read-write 41 | mode offers much higher write performance, but adds the possibility for stray 42 | application writes thru pointers to silently corrupt the database. 43 | Of course if your application code is known to be bug-free (...) then this is 44 | not an issue. 45 | 46 | If this is your first time using a transactional embedded key-value store, 47 | you may find the \ref starting section below to be helpful. 48 | -------------------------------------------------------------------------------- /docs/_restrictions.md: -------------------------------------------------------------------------------- 1 | Restrictions & Caveats {#restrictions} 2 | ====================== 3 | In addition to those listed for some functions. 4 | 5 | 6 | ## Long-lived read transactions {#long-lived-read} 7 | Avoid long-lived read transactions, especially in the scenarios with a 8 | high rate of write transactions. Long-lived read transactions prevents 9 | recycling pages retired/freed by newer write transactions, thus the 10 | database can grow quickly. 11 | 12 | Understanding the problem of long-lived read transactions requires some 13 | explanation, but can be difficult for quick perception. So is is 14 | reasonable to simplify this as follows: 15 | 1. Garbage collection problem exists in all databases one way or 16 | another, e.g. VACUUM in PostgreSQL. But in MDBX it's even more 17 | discernible because of high transaction rate and intentional 18 | internals simplification in favor of performance. 19 | 20 | 2. MDBX employs [Multiversion concurrency control](https://en.wikipedia.org/wiki/Multiversion_concurrency_control) 21 | on the [Copy-on-Write](https://en.wikipedia.org/wiki/Copy-on-write) 22 | basis, that allows multiple readers runs in parallel with a write 23 | transaction without blocking. An each write transaction needs free 24 | pages to put the changed data, that pages will be placed in the new 25 | b-tree snapshot at commit. MDBX efficiently recycling pages from 26 | previous created unused snapshots, BUT this is impossible if anyone 27 | a read transaction use such snapshot. 28 | 29 | 3. Thus massive altering of data during a parallel long read operation 30 | will increase the process's work set and may exhaust entire free 31 | database space. 32 | 33 | A good example of long readers is a hot backup to the slow destination 34 | or debugging of a client application while retaining an active read 35 | transaction. LMDB this results in `MDB_MAP_FULL` error and subsequent write 36 | performance degradation. 37 | 38 | MDBX mostly solve "long-lived" readers issue by using the Handle-Slow-Readers 39 | \ref MDBX_hsr_func callback which allows to abort long-lived read transactions, 40 | and using the \ref MDBX_LIFORECLAIM mode which addresses subsequent performance degradation. 41 | The "next" version of libmdbx (\ref MithrilDB) will completely solve this. 42 | 43 | - Avoid suspending a process with active transactions. These would then be 44 | "long-lived" as above. 45 | 46 | - Avoid aborting a process with an active read-only transaction in scenarios 47 | with high rate of write transactions. The transaction becomes "long-lived" 48 | as above until a check for stale readers is performed or the LCK-file is 49 | reset, since the process may not remove it from the lockfile. This does 50 | not apply to write transactions if the system clears stale writers, see 51 | above. 52 | 53 | 54 | ## Large data items and huge transactions 55 | 56 | MDBX allows you to store values up to 1 gigabyte in size, but this is 57 | not the main functionality for a key-value storage, but an additional 58 | feature that should not be abused. Such long values are stored in 59 | consecutive/adjacent DB pages, which has both pros and cons. This allows 60 | you to read long values directly without copying and without any 61 | overhead from a linear section of memory. 62 | 63 | On the other hand, when putting such values in the database, it is 64 | required to find a sufficient number of free consecutive/adjacent 65 | database pages, which can be very difficult and expensive, moreover 66 | sometimes impossible since b-tree tends to fragmentation. So, when 67 | placing very long values, the engine may need to process the entire GC, 68 | and in the absence of a sufficient sequence of free pages, increase the 69 | DB file. Thus, for long values, MDBX provides maximum read performance 70 | at the expense of write performance. 71 | 72 | A similar situation can be with huge transactions, in which a lot of 73 | database pages are retired. The retired pages should be put into GC as a 74 | list of page numbers for future reuse. But in huge transactions, such a 75 | list of retired page numbers can also be huge, i.e. it is a very long 76 | value and requires a long sequence of free pages to be saved. Thus, if 77 | you delete large amounts of information from the database in a single 78 | transaction, MDBX may need to increase the database file to save the 79 | list of pages to be retired. 80 | 81 | Both of these issues will be addressed in MithrilDB. 82 | 83 | 84 | ## Space reservation 85 | An MDBX database configuration will often reserve considerable unused 86 | memory address space and maybe file size for future growth. This does 87 | not use actual memory or disk space, but users may need to understand 88 | the difference so they won't be scared off. 89 | 90 | 91 | ## Remote filesystems 92 | Do not use MDBX databases on remote filesystems, even between processes 93 | on the same host. This breaks file locks on some platforms, possibly 94 | memory map sync, and certainly sync between programs on different hosts. 95 | 96 | On the other hand, MDBX support the exclusive database operation over 97 | a network, and cooperative read-only access to the database placed on 98 | a read-only network shares. 99 | 100 | 101 | ## Child processes 102 | Do not use opened \ref MDBX_env instance(s) in a child processes after `fork()`. 103 | It would be insane to call fork() and any MDBX-functions simultaneously 104 | from multiple threads. The best way is to prevent the presence of open 105 | MDBX-instances during `fork()`. 106 | 107 | The \ref MDBX_ENV_CHECKPID build-time option, which is ON by default on 108 | non-Windows platforms (i.e. where `fork()` is available), enables PID 109 | checking at a few critical points. But this does not give any guarantees, 110 | but only allows you to detect such errors a little sooner. Depending on 111 | the platform, you should expect an application crash and/or database 112 | corruption in such cases. 113 | 114 | On the other hand, MDBX allow calling \ref mdbx_env_close() in such cases to 115 | release resources, but no more and in general this is a wrong way. 116 | 117 | 118 | ## Read-only mode 119 | There is no pure read-only mode in a normal explicitly way, since 120 | readers need write access to LCK-file to be ones visible for writer. 121 | 122 | So MDBX always tries to open/create LCK-file for read-write, but switches 123 | to without-LCK mode on appropriate errors (`EROFS`, `EACCESS`, `EPERM`) 124 | if the read-only mode was requested by the \ref MDBX_RDONLY flag which is 125 | described below. 126 | 127 | The "next" version of libmdbx (\ref MithrilDB) will solve this issue for the "many 128 | readers without writer" case. 129 | 130 | 131 | ## Troubleshooting the LCK-file 132 | 1. A broken LCK-file can cause sync issues, including appearance of 133 | wrong/inconsistent data for readers. When database opened in the 134 | cooperative read-write mode the LCK-file requires to be mapped to 135 | memory in read-write access. In this case it is always possible for 136 | stray/malfunctioned application could writes thru pointers to 137 | silently corrupt the LCK-file. 138 | 139 | Unfortunately, there is no any portable way to prevent such 140 | corruption, since the LCK-file is updated concurrently by 141 | multiple processes in a lock-free manner and any locking is 142 | unwise due to a large overhead. 143 | 144 | The "next" version of libmdbx (\ref MithrilDB) will solve this issue. 145 | 146 | \note Workaround: Just make all programs using the database close it; 147 | the LCK-file is always reset on first open. 148 | 149 | 2. Stale reader transactions left behind by an aborted program cause 150 | further writes to grow the database quickly, and stale locks can 151 | block further operation. 152 | MDBX checks for stale readers while opening environment and before 153 | growth the database. But in some cases, this may not be enough. 154 | 155 | \note Workaround: Check for stale readers periodically, using the 156 | \ref mdbx_reader_check() function or the mdbx_stat tool. 157 | 158 | 3. Stale writers will be cleared automatically by MDBX on supported 159 | platforms. But this is platform-specific, especially of 160 | implementation of shared POSIX-mutexes and support for robust 161 | mutexes. For instance there are no known issues on Linux, OSX, 162 | Windows and FreeBSD. 163 | 164 | \note Workaround: Otherwise just make all programs using the database 165 | close it; the LCK-file is always reset on first open of the environment. 166 | 167 | 168 | ## One thread - One transaction 169 | A thread can only use one transaction at a time, plus any nested 170 | read-write transactions in the non-writemap mode. Each transaction 171 | belongs to one thread. The \ref MDBX_NOTLS flag changes this for read-only 172 | transactions. See below. 173 | 174 | Do not start more than one transaction for a one thread. If you think 175 | about this, it's really strange to do something with two data snapshots 176 | at once, which may be different. MDBX checks and preventing this by 177 | returning corresponding error code (\ref MDBX_TXN_OVERLAPPING, \ref MDBX_BAD_RSLOT, 178 | \ref MDBX_BUSY) unless you using \ref MDBX_NOTLS option on the environment. 179 | Nonetheless, with the `MDBX_NOTLS` option, you must know exactly what you 180 | are doing, otherwise you will get deadlocks or reading an alien data. 181 | 182 | 183 | ## Do not open twice 184 | Do not have open an MDBX database twice in the same process at the same 185 | time. By default MDBX prevent this in most cases by tracking databases 186 | opening and return \ref MDBX_BUSY if anyone LCK-file is already open. 187 | 188 | The reason for this is that when the "Open file description" locks (aka 189 | OFD-locks) are not available, MDBX uses POSIX locks on files, and these 190 | locks have issues if one process opens a file multiple times. If a single 191 | process opens the same environment multiple times, closing it once will 192 | remove all the locks held on it, and the other instances will be 193 | vulnerable to corruption from other processes. 194 | 195 | For compatibility with LMDB which allows multi-opening, MDBX can be 196 | configured at runtime by `mdbx_setup_debug(MDBX_DBG_LEGACY_MULTIOPEN, ...)` 197 | prior to calling other MDBX functions. In this way MDBX will track 198 | databases opening, detect multi-opening cases and then recover POSIX file 199 | locks as necessary. However, lock recovery can cause unexpected pauses, 200 | such as when another process opened the database in exclusive mode before 201 | the lock was restored - we have to wait until such a process releases the 202 | database, and so on. 203 | -------------------------------------------------------------------------------- /docs/_toc.md: -------------------------------------------------------------------------------- 1 | ### We have migrated to a reliable trusted infrastructure 2 | 3 | The origin for now is at 4 | [GitFlic](https://gitflic.ru/project/erthink/libmdbx) since on 5 | 2022-04-15 the Github administration, without any warning nor 6 | explanation, deleted _libmdbx_ along with a lot of other projects, 7 | simultaneously blocking access for many developers. For the same reason 8 | ~~Github~~ is blacklisted forever. 9 | 10 | > Questions, feedback and suggestions are welcome to the [Telegram' group](https://t.me/libmdbx). 11 | 12 | _The Future will (be) [Positive](https://www.ptsecurity.com). Всё будет хорошо._ 13 | 14 | \section toc Table of Contents 15 | 16 | This manual is divided into parts, 17 | each of which is divided into several sections. 18 | 19 | 1. The \ref intro 20 | - \ref characteristics 21 | - \ref improvements 22 | - \ref restrictions 23 | - \ref performance 24 | 2. \ref usage 25 | - \ref getting 26 | - \ref starting 27 | - \ref bindings 28 | 29 | 3. The `C/C++` API manual: 30 | - The \ref c_api reference 31 | - \ref c_crud_hints "Quick reference for Insert/Update/Delete operations" 32 | - The \ref mdbx.h header file reference 33 | - The \ref cxx_api reference 34 | - The \ref mdbx.h++ header file reference 35 | 36 | Please do not hesitate to point out errors in the documentation, 37 | including creating [merge-request](https://gitflic.ru/project/erthink/libmdbx/merge-request) with corrections and improvements. 38 | 39 | --- 40 | 41 | \section MithrilDB MithrilDB and Future 42 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(TARGET mdbx_example) 2 | project(${TARGET}) 3 | 4 | add_executable(${TARGET} example-mdbx.c) 5 | 6 | target_link_libraries(${TARGET} mdbx) 7 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | See [example-mdbx.c](example-mdbx.c) as an example of using _libmdbx_, and do a line-by-line comparison of it with the [sample-bdb.txt](sample-bdb.txt) file. 2 | -------------------------------------------------------------------------------- /example/example-mdbx.c: -------------------------------------------------------------------------------- 1 | /* MDBX usage example 2 | * 3 | * Do a line-by-line comparison of this and sample-bdb.txt 4 | */ 5 | 6 | /* 7 | * Copyright 2015-2023 Leonid Yuriev . 8 | * Copyright 2017 Ilya Shipitsin . 9 | * Copyright 2012-2015 Howard Chu, Symas Corp. 10 | * All rights reserved. 11 | * 12 | * Redistribution and use in source and binary forms, with or without 13 | * modification, are permitted only as authorized by the OpenLDAP 14 | * Public License. 15 | * 16 | * A copy of this license is available in the file LICENSE in the 17 | * top-level directory of the distribution or, alternatively, at 18 | * . 19 | */ 20 | 21 | #if (defined(__MINGW__) || defined(__MINGW32__) || defined(__MINGW64__)) && \ 22 | !defined(__USE_MINGW_ANSI_STDIO) 23 | #define __USE_MINGW_ANSI_STDIO 1 24 | #endif /* MinGW */ 25 | 26 | #include "mdbx.h" 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | int main(int argc, char *argv[]) { 34 | (void)argc; 35 | (void)argv; 36 | 37 | int rc; 38 | MDBX_env *env = NULL; 39 | MDBX_dbi dbi = 0; 40 | MDBX_val key, data; 41 | MDBX_txn *txn = NULL; 42 | MDBX_cursor *cursor = NULL; 43 | char sval[32]; 44 | 45 | printf("MDBX limits:\n"); 46 | #if UINTPTR_MAX > 0xffffFFFFul || ULONG_MAX > 0xffffFFFFul 47 | const double scale_factor = 1099511627776.0; 48 | const char *const scale_unit = "TiB"; 49 | #else 50 | const double scale_factor = 1073741824.0; 51 | const char *const scale_unit = "GiB"; 52 | #endif 53 | const size_t pagesize_min = mdbx_limits_pgsize_min(); 54 | const size_t pagesize_max = mdbx_limits_pgsize_max(); 55 | const size_t pagesize_default = mdbx_default_pagesize(); 56 | 57 | printf("\tPage size: a power of 2, minimum %zu, maximum %zu bytes," 58 | " default %zu bytes.\n", 59 | pagesize_min, pagesize_max, pagesize_default); 60 | printf("\tKey size: minimum %zu, maximum ≈¼ pagesize (%zu bytes for default" 61 | " %zuK pagesize, %zu bytes for %zuK pagesize).\n", 62 | (size_t)0, mdbx_limits_keysize_max(-1, MDBX_DB_DEFAULTS), 63 | pagesize_default / 1024, 64 | mdbx_limits_keysize_max(pagesize_max, MDBX_DB_DEFAULTS), 65 | pagesize_max / 1024); 66 | printf("\tValue size: minimum %zu, maximum %zu (0x%08zX) bytes for maps," 67 | " ≈¼ pagesize for multimaps (%zu bytes for default %zuK pagesize," 68 | " %zu bytes for %zuK pagesize).\n", 69 | (size_t)0, mdbx_limits_valsize_max(pagesize_min, MDBX_DB_DEFAULTS), 70 | mdbx_limits_valsize_max(pagesize_min, MDBX_DB_DEFAULTS), 71 | mdbx_limits_valsize_max(-1, MDBX_DUPSORT), pagesize_default / 1024, 72 | mdbx_limits_valsize_max(pagesize_max, MDBX_DUPSORT), 73 | pagesize_max / 1024); 74 | printf("\tWrite transaction size: up to %zu (0x%zX) pages (%f %s for default " 75 | "%zuK pagesize, %f %s for %zuK pagesize).\n", 76 | mdbx_limits_txnsize_max(pagesize_min) / pagesize_min, 77 | mdbx_limits_txnsize_max(pagesize_min) / pagesize_min, 78 | mdbx_limits_txnsize_max(-1) / scale_factor, scale_unit, 79 | pagesize_default / 1024, 80 | mdbx_limits_txnsize_max(pagesize_max) / scale_factor, scale_unit, 81 | pagesize_max / 1024); 82 | printf("\tDatabase size: up to %zu pages (%f %s for default %zuK " 83 | "pagesize, %f %s for %zuK pagesize).\n", 84 | mdbx_limits_dbsize_max(pagesize_min) / pagesize_min, 85 | mdbx_limits_dbsize_max(-1) / scale_factor, scale_unit, 86 | pagesize_default / 1024, 87 | mdbx_limits_dbsize_max(pagesize_max) / scale_factor, scale_unit, 88 | pagesize_max / 1024); 89 | printf("\tMaximum sub-databases: %u.\n", MDBX_MAX_DBI); 90 | printf("-----\n"); 91 | 92 | rc = mdbx_env_create(&env); 93 | if (rc != MDBX_SUCCESS) { 94 | fprintf(stderr, "mdbx_env_create: (%d) %s\n", rc, mdbx_strerror(rc)); 95 | goto bailout; 96 | } 97 | rc = mdbx_env_open(env, "./example-db", 98 | MDBX_NOSUBDIR | MDBX_COALESCE | MDBX_LIFORECLAIM, 0664); 99 | if (rc != MDBX_SUCCESS) { 100 | fprintf(stderr, "mdbx_env_open: (%d) %s\n", rc, mdbx_strerror(rc)); 101 | goto bailout; 102 | } 103 | 104 | rc = mdbx_txn_begin(env, NULL, 0, &txn); 105 | if (rc != MDBX_SUCCESS) { 106 | fprintf(stderr, "mdbx_txn_begin: (%d) %s\n", rc, mdbx_strerror(rc)); 107 | goto bailout; 108 | } 109 | rc = mdbx_dbi_open(txn, NULL, 0, &dbi); 110 | if (rc != MDBX_SUCCESS) { 111 | fprintf(stderr, "mdbx_dbi_open: (%d) %s\n", rc, mdbx_strerror(rc)); 112 | goto bailout; 113 | } 114 | 115 | key.iov_len = sizeof(int); 116 | key.iov_base = sval; 117 | data.iov_len = sizeof(sval); 118 | data.iov_base = sval; 119 | 120 | sprintf(sval, "%03x %d foo bar", 32, 3141592); 121 | rc = mdbx_put(txn, dbi, &key, &data, 0); 122 | if (rc != MDBX_SUCCESS) { 123 | fprintf(stderr, "mdbx_put: (%d) %s\n", rc, mdbx_strerror(rc)); 124 | goto bailout; 125 | } 126 | rc = mdbx_txn_commit(txn); 127 | if (rc) { 128 | fprintf(stderr, "mdbx_txn_commit: (%d) %s\n", rc, mdbx_strerror(rc)); 129 | goto bailout; 130 | } 131 | txn = NULL; 132 | 133 | rc = mdbx_txn_begin(env, NULL, MDBX_TXN_RDONLY, &txn); 134 | if (rc != MDBX_SUCCESS) { 135 | fprintf(stderr, "mdbx_txn_begin: (%d) %s\n", rc, mdbx_strerror(rc)); 136 | goto bailout; 137 | } 138 | rc = mdbx_cursor_open(txn, dbi, &cursor); 139 | if (rc != MDBX_SUCCESS) { 140 | fprintf(stderr, "mdbx_cursor_open: (%d) %s\n", rc, mdbx_strerror(rc)); 141 | goto bailout; 142 | } 143 | 144 | int found = 0; 145 | while ((rc = mdbx_cursor_get(cursor, &key, &data, MDBX_NEXT)) == 0) { 146 | printf("key: %p %.*s, data: %p %.*s\n", key.iov_base, (int)key.iov_len, 147 | (char *)key.iov_base, data.iov_base, (int)data.iov_len, 148 | (char *)data.iov_base); 149 | found += 1; 150 | } 151 | if (rc != MDBX_NOTFOUND || found == 0) { 152 | fprintf(stderr, "mdbx_cursor_get: (%d) %s\n", rc, mdbx_strerror(rc)); 153 | goto bailout; 154 | } else { 155 | rc = MDBX_SUCCESS; 156 | } 157 | bailout: 158 | if (cursor) 159 | mdbx_cursor_close(cursor); 160 | if (txn) 161 | mdbx_txn_abort(txn); 162 | if (dbi) 163 | mdbx_dbi_close(env, dbi); 164 | if (env) 165 | mdbx_env_close(env); 166 | return (rc != MDBX_SUCCESS) ? EXIT_FAILURE : EXIT_SUCCESS; 167 | } 168 | -------------------------------------------------------------------------------- /example/sample-bdb.txt: -------------------------------------------------------------------------------- 1 | /* BerkeleyDB toy/sample 2 | * 3 | * Do a line-by-line comparison of this and example-mdbx.c 4 | */ 5 | 6 | /* 7 | * Copyright 2015-2023 Leonid Yuriev . 8 | * Copyright 2012-2015 Howard Chu, Symas Corp. 9 | * Copyright 2015,2016 Peter-Service R&D LLC. 10 | * All rights reserved. 11 | * 12 | * Redistribution and use in source and binary forms, with or without 13 | * modification, are permitted only as authorized by the OpenLDAP 14 | * Public License. 15 | * 16 | * A copy of this license is available in the file LICENSE in the 17 | * top-level directory of the distribution or, alternatively, at 18 | * . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | int main(int argc,char * argv[]) 26 | { 27 | int rc; 28 | DB_ENV *env; 29 | DB *dbi; 30 | DBT key, data; 31 | DB_TXN *txn; 32 | DBC *cursor; 33 | char sval[32], kval[32]; 34 | 35 | /* Note: Most error checking omitted for simplicity */ 36 | 37 | #define FLAGS (DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_INIT_MPOOL|DB_CREATE|DB_THREAD) 38 | rc = db_env_create(&env, 0); 39 | rc = env->open(env, "./testdb", FLAGS, 0664); 40 | rc = db_create(&dbi, env, 0); 41 | rc = env->txn_begin(env, NULL, &txn, 0); 42 | rc = dbi->open(dbi, txn, "test.bdb", NULL, DB_BTREE, DB_CREATE, 0664); 43 | 44 | memset(&key, 0, sizeof(DBT)); 45 | memset(&data, 0, sizeof(DBT)); 46 | key.size = sizeof(int); 47 | key.data = sval; 48 | data.size = sizeof(sval); 49 | data.data = sval; 50 | 51 | sprintf(sval, "%03x %d foo bar", 32, 3141592); 52 | rc = dbi->put(dbi, txn, &key, &data, 0); 53 | rc = txn->commit(txn, 0); 54 | if (rc) { 55 | fprintf(stderr, "txn->commit: (%d) %s\n", rc, db_strerror(rc)); 56 | goto leave; 57 | } 58 | rc = env->txn_begin(env, NULL, &txn, 0); 59 | rc = dbi->cursor(dbi, txn, &cursor, 0); 60 | key.flags = DB_DBT_USERMEM; 61 | key.data = kval; 62 | key.ulen = sizeof(kval); 63 | data.flags = DB_DBT_USERMEM; 64 | data.data = sval; 65 | data.ulen = sizeof(sval); 66 | while ((rc = cursor->c_get(cursor, &key, &data, DB_NEXT)) == 0) { 67 | printf("key: %p %.*s, data: %p %.*s\n", 68 | key.data, (int) key.size, (char *) key.data, 69 | data.data, (int) data.size, (char *) data.data); 70 | } 71 | rc = cursor->c_close(cursor); 72 | rc = txn->abort(txn); 73 | leave: 74 | rc = dbi->close(dbi, 0); 75 | rc = env->close(env, 0); 76 | return rc; 77 | } 78 | -------------------------------------------------------------------------------- /packages/buildroot/0001-package-libmdbx-new-package-library-database.patch: -------------------------------------------------------------------------------- 1 | From dd398c9a92b87f7c65798545d776735d27f2a4f9 Mon Sep 17 00:00:00 2001 2 | From: =?UTF-8?q?=D0=9B=D0=B5=D0=BE=D0=BD=D0=B8=D0=B4=20=D0=AE=D1=80=D1=8C?= 3 | =?UTF-8?q?=D0=B5=D0=B2=20=28Leonid=20Yuriev=29?= 4 | Date: Sat, 25 Jun 2022 16:22:22 +0300 5 | Subject: [PATCH] package/libmdbx: new package (library/database). 6 | 7 | This patch adds libmdbx v0.11.8: 8 | - libmdbx is one of the fastest compact embeddable key-value ACID database. 9 | - libmdbx has a specific set of properties and capabilities, 10 | focused on creating unique lightweight solutions. 11 | - libmdbx surpasses the legendary LMDB (Lightning Memory-Mapped Database) 12 | in terms of reliability, features and performance. 13 | - https://github.com/erthink/libmdbx 14 | 15 | Signed-off-by: Leonid Yuriev 16 | Signed-off-by: Yann E. MORIN 17 | --- 18 | DEVELOPERS | 3 +++ 19 | package/Config.in | 1 + 20 | package/libmdbx/Config.in | 45 ++++++++++++++++++++++++++++++++++++ 21 | package/libmdbx/libmdbx.hash | 6 +++++ 22 | package/libmdbx/libmdbx.mk | 42 +++++++++++++++++++++++++++++++++ 23 | 5 files changed, 97 insertions(+) 24 | create mode 100644 package/libmdbx/Config.in 25 | create mode 100644 package/libmdbx/libmdbx.hash 26 | create mode 100644 package/libmdbx/libmdbx.mk 27 | 28 | diff --git a/DEVELOPERS b/DEVELOPERS 29 | index 9ab1e125f4..758ff6a2d5 100644 30 | --- a/DEVELOPERS 31 | +++ b/DEVELOPERS 32 | @@ -1482,6 +1482,9 @@ N: Leon Anavi 33 | F: board/olimex/a10_olinuxino 34 | F: configs/olimex_a10_olinuxino_lime_defconfig 35 | 36 | +N: Leonid Yuriev 37 | +F: package/libmdbx/ 38 | + 39 | N: Lionel Flandrin 40 | F: package/python-babel/ 41 | F: package/python-daemonize/ 42 | diff --git a/package/Config.in b/package/Config.in 43 | index 016a99ed1a..a6f95bfaa9 100644 44 | --- a/package/Config.in 45 | +++ b/package/Config.in 46 | @@ -1372,6 +1372,7 @@ menu "Database" 47 | source "package/kompexsqlite/Config.in" 48 | source "package/leveldb/Config.in" 49 | source "package/libgit2/Config.in" 50 | + source "package/libmdbx/Config.in" 51 | source "package/libodb/Config.in" 52 | source "package/libodb-boost/Config.in" 53 | source "package/libodb-mysql/Config.in" 54 | diff --git a/package/libmdbx/Config.in b/package/libmdbx/Config.in 55 | new file mode 100644 56 | index 0000000000..a9a4ac45c5 57 | --- /dev/null 58 | +++ b/package/libmdbx/Config.in 59 | @@ -0,0 +1,45 @@ 60 | +config BR2_PACKAGE_LIBMDBX 61 | + bool "libmdbx" 62 | + depends on BR2_USE_MMU 63 | + depends on BR2_TOOLCHAIN_HAS_SYNC_4 64 | + depends on BR2_TOOLCHAIN_HAS_THREADS 65 | + depends on BR2_TOOLCHAIN_GCC_AT_LEAST_4_4 66 | + help 67 | + One of the fastest compact key-value ACID database 68 | + without WAL. libmdbx has a specific set of properties 69 | + and capabilities, focused on creating unique lightweight 70 | + solutions. 71 | + 72 | + libmdbx surpasses the legendary LMDB in terms of 73 | + reliability, features and performance. 74 | + 75 | + https://libmdbx.dqdkfa.ru 76 | + 77 | +if BR2_PACKAGE_LIBMDBX 78 | + 79 | +config BR2_PACKAGE_LIBMDBX_TOOLS 80 | + bool "install tools" 81 | + help 82 | + Install libmdbx tools for checking, dump, restore 83 | + and show statistics of databases. 84 | + 85 | +config BR2_PACKAGE_LIBMDBX_CXX 86 | + bool "C++ API" 87 | + depends on BR2_INSTALL_LIBSTDCPP 88 | + depends on BR2_TOOLCHAIN_GCC_AT_LEAST_4_8 89 | + depends on !BR2_TOOLCHAIN_HAS_GCC_BUG_64735 90 | + help 91 | + Enable modern C++11/14/17/20 API for libmdbx. 92 | + 93 | +comment "libmdbx C++ support needs a toolchain w/ C++11, gcc >= 4.8 w/o bug#64735" 94 | + depends on !BR2_INSTALL_LIBSTDCPP || \ 95 | + !BR2_TOOLCHAIN_GCC_AT_LEAST_4_8 || \ 96 | + BR2_TOOLCHAIN_HAS_GCC_BUG_64735 97 | + 98 | +endif 99 | + 100 | +comment "libmdbx needs MMU, a toolchain w/ threads, gcc >= 4.4 w/ 4-byte atomics" 101 | + depends on BR2_USE_MMU 102 | + depends on !BR2_TOOLCHAIN_HAS_THREADS || \ 103 | + !BR2_TOOLCHAIN_HAS_SYNC_4 || \ 104 | + !BR2_TOOLCHAIN_GCC_AT_LEAST_4_4 105 | diff --git a/package/libmdbx/libmdbx.hash b/package/libmdbx/libmdbx.hash 106 | new file mode 100644 107 | index 0000000000..3f2be134c3 108 | --- /dev/null 109 | +++ b/package/libmdbx/libmdbx.hash 110 | @@ -0,0 +1,6 @@ 111 | +# Hashes from: https://libmdbx.dqdkfa.ru/release/SHA256SUMS 112 | +sha256 06011f361481ee7adc2111e48b7b121384294e0b6b8f10c75e7886629297b279 libmdbx-amalgamated-0.11.8.tar.xz 113 | +sha256 3a9fb6a4cd941e646597235518714373fda1ca6d4c5e23669afe70ea87c20940 libmdbx-amalgamated-0.11.7.tar.xz 114 | + 115 | +# Locally calculated 116 | +sha256 310fe25c858a9515fc8c8d7d1f24a67c9496f84a91e0a0e41ea9975b1371e569 LICENSE 117 | diff --git a/package/libmdbx/libmdbx.mk b/package/libmdbx/libmdbx.mk 118 | new file mode 100644 119 | index 0000000000..130fe96793 120 | --- /dev/null 121 | +++ b/package/libmdbx/libmdbx.mk 122 | @@ -0,0 +1,42 @@ 123 | +################################################################################ 124 | +# 125 | +# libmdbx 126 | +# 127 | +################################################################################ 128 | + 129 | +LIBMDBX_VERSION = 0.11.8 130 | +LIBMDBX_SOURCE = libmdbx-amalgamated-$(LIBMDBX_VERSION).tar.xz 131 | +LIBMDBX_SITE = https://libmdbx.dqdkfa.ru/release 132 | +LIBMDBX_SUPPORTS_IN_SOURCE_BUILD = NO 133 | +LIBMDBX_LICENSE = OLDAP-2.8 134 | +LIBMDBX_LICENSE_FILES = LICENSE 135 | +LIBMDBX_REDISTRIBUTE = YES 136 | +LIBMDBX_STRIP_COMPONENTS = 0 137 | +LIBMDBX_INSTALL_STAGING = YES 138 | + 139 | +# Set CMAKE_BUILD_TYPE to Release to remove -Werror and avoid a build failure 140 | +# with glibc < 2.12 141 | +LIBMDBX_CONF_OPTS = \ 142 | + -DCMAKE_BUILD_TYPE=Release \ 143 | + -DMDBX_INSTALL_MANPAGES=OFF \ 144 | + -DBUILD_FOR_NATIVE_CPU=OFF \ 145 | + -DMDBX_BUILD_CXX=$(if $(BR2_PACKAGE_LIBMDBX_CXX),ON,OFF) \ 146 | + -DMDBX_BUILD_TOOLS=$(if $(BR2_PACKAGE_LIBMDBX_TOOLS),ON,OFF) 147 | + 148 | +ifeq ($(BR2_STATIC_LIBS)$(BR2_SHARED_STATIC_LIBS),y) 149 | +LIBMDBX_CONF_OPTS += -DMDBX_INSTALL_STATIC=ON 150 | +else 151 | +LIBMDBX_CONF_OPTS += -DMDBX_INSTALL_STATIC=OFF 152 | +endif 153 | + 154 | +ifeq ($(BR2_SHARED_LIBS)$(BR2_SHARED_STATIC_LIBS),y) 155 | +LIBMDBX_CONF_OPTS += \ 156 | + -DMDBX_BUILD_SHARED_LIBRARY=ON \ 157 | + -DMDBX_LINK_TOOLS_NONSTATIC=ON 158 | +else 159 | +LIBMDBX_CONF_OPTS += \ 160 | + -DMDBX_BUILD_SHARED_LIBRARY=OFF \ 161 | + -DMDBX_LINK_TOOLS_NONSTATIC=OFF 162 | +endif 163 | + 164 | +$(eval $(cmake-package)) 165 | -- 166 | 2.36.1 167 | 168 | -------------------------------------------------------------------------------- /packages/rpm.obsolete/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.7) 2 | set(TARGET mdbx) 3 | project(${TARGET}) 4 | 5 | set(MDBX_VERSION_MAJOR 0) 6 | set(MDBX_VERSION_MINOR 3) 7 | set(MDBX_VERSION_RELEASE 1) 8 | set(MDBX_VERSION_REVISION 0) 9 | 10 | set(MDBX_VERSION_STRING ${MDBX_VERSION_MAJOR}.${MDBX_VERSION_MINOR}.${MDBX_VERSION_RELEASE}) 11 | 12 | enable_language(C) 13 | enable_language(CXX) 14 | 15 | set(CMAKE_CXX_STANDARD 11) 16 | set(CMAKE_CXX_STANDARD_REQUIRED on) 17 | 18 | add_definitions(-DNDEBUG=1 -DMDBX_DEBUG=0 -DLIBMDBX_EXPORTS=1 -D_GNU_SOURCE=1) 19 | 20 | find_package(Threads REQUIRED) 21 | 22 | get_directory_property(hasParent PARENT_DIRECTORY) 23 | if(hasParent) 24 | set(STANDALONE_BUILD 0) 25 | else() 26 | set(STANDALONE_BUILD 1) 27 | enable_testing() 28 | 29 | if (CMAKE_C_COMPILER_ID MATCHES GNU) 30 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2") 31 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3") 32 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") 33 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") 34 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra") 35 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections") 36 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") 37 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") 38 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11") 39 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") 40 | endif() 41 | 42 | if (CMAKE_CXX_COMPILER_ID MATCHES GNU) 43 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W") 44 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") 45 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") 46 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wpointer-arith") 47 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-sign-compare") 48 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wformat-security") 49 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Woverloaded-virtual") 50 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wwrite-strings") 51 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fmax-errors=20") 52 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") 53 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter -Wunused-function -Wunused-variable -Wunused-value -Wmissing-declarations") 54 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-field-initializers") 55 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wcast-qual") 56 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb") 57 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer") 58 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-strict-aliasing") 59 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -finline-functions-called-once") 60 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-packed-bitfield-compat") 61 | 62 | set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3") 63 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g3") 64 | endif() 65 | 66 | if (COVERAGE) 67 | if (NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Debug") 68 | message(FATAL_ERROR "Coverage requires -DCMAKE_BUILD_TYPE=Debug Current value=${CMAKE_BUILD_TYPE}") 69 | endif() 70 | 71 | message(STATUS "Setting coverage compiler flags") 72 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -ggdb3 -O0 --coverage -fprofile-arcs -ftest-coverage") 73 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -ggdb3 -O0 --coverage -fprofile-arcs -ftest-coverage") 74 | add_definitions(-DCOVERAGE_TEST) 75 | endif() 76 | 77 | if (NOT TRAVIS) 78 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address -fsanitize=leak -fstack-protector-strong -static-libasan") 79 | endif() 80 | endif() 81 | 82 | set(${TARGET}_SRC 83 | mdbx.h 84 | src/bits.h 85 | src/defs.h 86 | src/lck-linux.c 87 | src/mdbx.c 88 | src/osal.c 89 | src/osal.h 90 | src/version.c 91 | ) 92 | 93 | add_library(${TARGET}_STATIC STATIC 94 | ${${TARGET}_SRC} 95 | ) 96 | 97 | add_library(${TARGET} ALIAS ${TARGET}_STATIC) 98 | 99 | add_library(${TARGET}_SHARED SHARED 100 | ${${TARGET}_SRC} 101 | ) 102 | 103 | set_target_properties(${TARGET}_SHARED PROPERTIES 104 | VERSION ${MDBX_VERSION_STRING} 105 | SOVERSION ${MDBX_VERSION_MAJOR}.${MDBX_VERSION_MINOR} 106 | OUTPUT_NAME ${TARGET} 107 | CLEAN_DIRECT_OUTPUT 1 108 | ) 109 | 110 | set_target_properties(${TARGET}_STATIC PROPERTIES 111 | VERSION ${MDBX_VERSION_STRING} 112 | SOVERSION ${MDBX_VERSION_MAJOR}.${MDBX_VERSION_MINOR} 113 | OUTPUT_NAME ${TARGET} 114 | CLEAN_DIRECT_OUTPUT 1 115 | ) 116 | 117 | target_include_directories(${TARGET}_STATIC PUBLIC 118 | ${CMAKE_CURRENT_SOURCE_DIR}) 119 | target_include_directories(${TARGET}_SHARED PUBLIC 120 | ${CMAKE_CURRENT_SOURCE_DIR}) 121 | 122 | target_link_libraries(${TARGET}_STATIC ${CMAKE_THREAD_LIBS_INIT}) 123 | target_link_libraries(${TARGET}_SHARED ${CMAKE_THREAD_LIBS_INIT}) 124 | if(UNIX AND NOT APPLE) 125 | target_link_libraries(${TARGET}_STATIC rt) 126 | target_link_libraries(${TARGET}_SHARED rt) 127 | endif() 128 | 129 | install(TARGETS ${TARGET}_STATIC DESTINATION ${CMAKE_INSTALL_PREFIX}/lib64 COMPONENT mdbx) 130 | install(TARGETS ${TARGET}_SHARED DESTINATION ${CMAKE_INSTALL_PREFIX}/lib64 COMPONENT mdbx) 131 | install(FILES mdbx.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include COMPONENT mdbx-devel) 132 | 133 | add_subdirectory(src/tools) 134 | add_subdirectory(test) 135 | add_subdirectory(test/pcrf) 136 | add_subdirectory(tutorial) 137 | 138 | ############################################################################## 139 | 140 | set(CPACK_GENERATOR "RPM") 141 | set(CPACK_RPM_COMPONENT_INSTALL ON) 142 | 143 | # Version 144 | if (NOT "$ENV{BUILD_NUMBER}" STREQUAL "") 145 | set(CPACK_PACKAGE_RELEASE $ENV{BUILD_NUMBER}) 146 | else() 147 | if (NOT "$ENV{CI_PIPELINE_ID}" STREQUAL "") 148 | set(CPACK_PACKAGE_RELEASE $ENV{CI_PIPELINE_ID}) 149 | else() 150 | set(CPACK_PACKAGE_RELEASE 1) 151 | endif() 152 | endif() 153 | set(CPACK_RPM_PACKAGE_RELEASE ${CPACK_PACKAGE_RELEASE}) 154 | 155 | set(CPACK_PACKAGE_VERSION ${MDBX_VERSION_STRING}) 156 | set(CPACK_PACKAGE_VERSION_FULL ${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE}) 157 | 158 | set(CPACK_RPM_mdbx-devel_PACKAGE_REQUIRES "mdbx = ${CPACK_PACKAGE_VERSION}") 159 | 160 | set(CPACK_RPM_SPEC_INSTALL_POST "/bin/true") 161 | set(CPACK_RPM_mdbx_PACKAGE_NAME mdbx) 162 | set(CPACK_RPM_mdbx-devel_PACKAGE_NAME mdbx-devel) 163 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "The revised and extended descendant of Symas LMDB") 164 | 165 | set(CPACK_PACKAGE_VENDOR "???") 166 | set(CPACK_PACKAGE_CONTACT "Vladimir Romanov") 167 | set(CPACK_PACKAGE_RELOCATABLE false) 168 | set(CPACK_RPM_PACKAGE_ARCHITECTURE "x86_64") 169 | set(CPACK_RPM_PACKAGE_REQUIRES "") 170 | set(CPACK_RPM_PACKAGE_GROUP "Applications/Database") 171 | 172 | set(CPACK_RPM_mdbx_FILE_NAME "${CPACK_RPM_mdbx_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION_FULL}.${CPACK_RPM_PACKAGE_ARCHITECTURE}.rpm") 173 | set(CPACK_RPM_mdbx-devel_FILE_NAME "${CPACK_RPM_mdbx-devel_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION_FULL}.${CPACK_RPM_PACKAGE_ARCHITECTURE}.rpm") 174 | 175 | set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION 176 | /usr/local 177 | /usr/local/bin 178 | /usr/local/lib64 179 | /usr/local/include 180 | /usr/local/man 181 | /usr/local/man/man1 182 | ) 183 | 184 | include(CPack) 185 | -------------------------------------------------------------------------------- /packages/rpm.obsolete/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | CONFIG=$1 4 | 5 | if [[ -z "${CONFIG}" ]]; then 6 | CONFIG=Debug 7 | fi 8 | if [[ -r /opt/rh/devtoolset-6/enable ]]; then 9 | source /opt/rh/devtoolset-6/enable 10 | fi 11 | #rm -f -r build || true 12 | mkdir -p cmake-build-${CONFIG} 13 | pushd cmake-build-${CONFIG} &> /dev/null 14 | if [[ ! -r Makefile ]]; then 15 | cmake .. -DCMAKE_BUILD_TYPE=${CONFIG} 16 | fi 17 | make -j8 || exit 1 18 | popd &> /dev/null 19 | -------------------------------------------------------------------------------- /packages/rpm.obsolete/package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | CONFIG=$1 5 | 6 | if [[ -z "${CONFIG}" ]]; then 7 | CONFIG=Debug 8 | fi 9 | 10 | DIRNAME=`dirname ${BASH_SOURCE[0]}` 11 | DIRNAME=`readlink --canonicalize ${DIRNAME}` 12 | 13 | if [[ -r /opt/rh/devtoolset-6/enable ]]; then 14 | source /opt/rh/devtoolset-6/enable 15 | fi 16 | 17 | mkdir -p cmake-build-${CONFIG} 18 | pushd cmake-build-${CONFIG} &> /dev/null 19 | if [[ ! -r Makefile ]]; then 20 | cmake .. -DCMAKE_BUILD_TYPE=${CONFIG} 21 | fi 22 | rm -f *.rpm 23 | make -j8 package || exit 1 24 | rm -f *-Unspecified.rpm 25 | popd &> /dev/null 26 | -------------------------------------------------------------------------------- /src/alloy.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2023 Leonid Yuriev 3 | * and other libmdbx authors: please see AUTHORS file. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . */ 13 | 14 | #define xMDBX_ALLOY 1 /* alloyed build */ 15 | #include "internals.h" /* must be included first */ 16 | 17 | #include "core.c" 18 | #include "osal.c" 19 | #include "version.c" 20 | 21 | #if defined(_WIN32) || defined(_WIN64) 22 | #include "lck-windows.c" 23 | #else 24 | #include "lck-posix.c" 25 | #endif 26 | -------------------------------------------------------------------------------- /src/bits.md: -------------------------------------------------------------------------------- 1 | N | MASK | ENV | TXN | DB | PUT | DBI | NODE | PAGE | MRESIZE | 2 | --|---------|-----------|--------------|----------|-----------|------------|---------|----------|---------| 3 | 0 |0000 0001|ALLOC_RSRV |TXN_FINISHED | | |DBI_DIRTY |F_BIGDATA|P_BRANCH | | 4 | 1 |0000 0002|ALLOC_UNIMP|TXN_ERROR |REVERSEKEY|F_SUBDATA |DBI_STALE |F_SUBDATA|P_LEAF | | 5 | 2 |0000 0004|ALLOC_COLSC|TXN_DIRTY |DUPSORT | |DBI_FRESH |F_DUPDATA|P_OVERFLOW| | 6 | 3 |0000 0008|ALLOC_SSCAN|TXN_SPILLS |INTEGERKEY| |DBI_CREAT | |P_META | | 7 | 4 |0000 0010|ALLOC_FIFO |TXN_HAS_CHILD |DUPFIXED |NOOVERWRITE|DBI_VALID | |P_BAD | | 8 | 5 |0000 0020| |TXN_DRAINED_GC|INTEGERDUP|NODUPDATA |DBI_USRVALID| |P_LEAF2 | | 9 | 6 |0000 0040| | |REVERSEDUP|CURRENT |DBI_DUPDATA | |P_SUBP | | 10 | 7 |0000 0080| | | |ALLDUPS |DBI_AUDITED | | | | 11 | 8 |0000 0100| _MAY_MOVE | | | | | | | <= | 12 | 9 |0000 0200| _MAY_UNMAP| | | | | | | <= | 13 | 10|0000 0400| | | | | | | | | 14 | 11|0000 0800| | | | | | | | | 15 | 12|0000 1000| | | | | | | | | 16 | 13|0000 2000|VALIDATION | | | | | |P_SPILLED | | 17 | 14|0000 4000|NOSUBDIR | | | | | |P_LOOSE | | 18 | 15|0000 8000| | |DB_VALID | | | |P_FROZEN | | 19 | 16|0001 0000|SAFE_NOSYNC|TXN_NOSYNC | |RESERVE | |RESERVE | | | 20 | 17|0002 0000|RDONLY |TXN_RDONLY | |APPEND | |APPEND | | <= | 21 | 18|0004 0000|NOMETASYNC |TXN_NOMETASYNC|CREATE |APPENDDUP | | | | | 22 | 19|0008 0000|WRITEMAP |<= | |MULTIPLE | | | | <= | 23 | 20|0010 0000|UTTERLY | | | | | | | <= | 24 | 21|0020 0000|NOTLS |<= | | | | | | | 25 | 22|0040 0000|EXCLUSIVE | | | | | | | | 26 | 23|0080 0000|NORDAHEAD | | | | | | | | 27 | 24|0100 0000|NOMEMINIT |TXN_PREPARE | | | | | | | 28 | 25|0200 0000|COALESCE | | | | | | | | 29 | 26|0400 0000|LIFORECLAIM| | | | | | | | 30 | 27|0800 0000|PAGEPERTURB| | | | | | | | 31 | 28|1000 0000|ENV_TXKEY |TXN_TRY | | | | | | | 32 | 29|2000 0000|ENV_ACTIVE | | | | | | | | 33 | 30|4000 0000|ACCEDE |SHRINK_ALLOWED|DB_ACCEDE | | | | | | 34 | 31|8000 0000|FATAL_ERROR| | | | | | | | 35 | -------------------------------------------------------------------------------- /src/config.h.in: -------------------------------------------------------------------------------- 1 | /* This is CMake-template for libmdbx's config.h 2 | ******************************************************************************/ 3 | 4 | /* *INDENT-OFF* */ 5 | /* clang-format off */ 6 | 7 | #cmakedefine LTO_ENABLED 8 | #cmakedefine MDBX_USE_VALGRIND 9 | #cmakedefine ENABLE_GPROF 10 | #cmakedefine ENABLE_GCOV 11 | #cmakedefine ENABLE_ASAN 12 | #cmakedefine ENABLE_UBSAN 13 | #cmakedefine01 MDBX_FORCE_ASSERTIONS 14 | 15 | /* Common */ 16 | #cmakedefine01 MDBX_TXN_CHECKOWNER 17 | #cmakedefine MDBX_ENV_CHECKPID_AUTO 18 | #ifndef MDBX_ENV_CHECKPID_AUTO 19 | #cmakedefine01 MDBX_ENV_CHECKPID 20 | #endif 21 | #cmakedefine MDBX_LOCKING_AUTO 22 | #ifndef MDBX_LOCKING_AUTO 23 | #cmakedefine MDBX_LOCKING @MDBX_LOCKING@ 24 | #endif 25 | #cmakedefine MDBX_TRUST_RTC_AUTO 26 | #ifndef MDBX_TRUST_RTC_AUTO 27 | #cmakedefine01 MDBX_TRUST_RTC 28 | #endif 29 | #cmakedefine01 MDBX_DISABLE_VALIDATION 30 | #cmakedefine01 MDBX_AVOID_MSYNC 31 | #cmakedefine01 MDBX_ENABLE_REFUND 32 | #cmakedefine01 MDBX_ENABLE_MADVISE 33 | #cmakedefine01 MDBX_ENABLE_BIGFOOT 34 | #cmakedefine01 MDBX_ENABLE_PGOP_STAT 35 | #cmakedefine01 MDBX_ENABLE_PROFGC 36 | 37 | /* Windows */ 38 | #cmakedefine01 MDBX_WITHOUT_MSVC_CRT 39 | 40 | /* MacOS & iOS */ 41 | #cmakedefine01 MDBX_OSX_SPEED_INSTEADOF_DURABILITY 42 | 43 | /* POSIX */ 44 | #cmakedefine01 MDBX_DISABLE_GNU_SOURCE 45 | #cmakedefine MDBX_USE_OFDLOCKS_AUTO 46 | #ifndef MDBX_USE_OFDLOCKS_AUTO 47 | #cmakedefine01 MDBX_USE_OFDLOCKS 48 | #endif 49 | 50 | /* Build Info */ 51 | #ifndef MDBX_BUILD_TIMESTAMP 52 | #cmakedefine MDBX_BUILD_TIMESTAMP "@MDBX_BUILD_TIMESTAMP@" 53 | #endif 54 | #ifndef MDBX_BUILD_TARGET 55 | #cmakedefine MDBX_BUILD_TARGET "@MDBX_BUILD_TARGET@" 56 | #endif 57 | #ifndef MDBX_BUILD_TYPE 58 | #cmakedefine MDBX_BUILD_TYPE "@MDBX_BUILD_TYPE@" 59 | #endif 60 | #ifndef MDBX_BUILD_COMPILER 61 | #cmakedefine MDBX_BUILD_COMPILER "@MDBX_BUILD_COMPILER@" 62 | #endif 63 | #ifndef MDBX_BUILD_FLAGS 64 | #cmakedefine MDBX_BUILD_FLAGS "@MDBX_BUILD_FLAGS@" 65 | #endif 66 | #cmakedefine MDBX_BUILD_SOURCERY @MDBX_BUILD_SOURCERY@ 67 | 68 | /* *INDENT-ON* */ 69 | /* clang-format on */ 70 | -------------------------------------------------------------------------------- /src/debug_begin.h: -------------------------------------------------------------------------------- 1 | #if defined(__GNUC__) && !defined(__LCC__) 2 | 3 | #pragma push_macro("TRACE") 4 | #pragma push_macro("DEBUG") 5 | #pragma push_macro("VERBOSE") 6 | #pragma push_macro("NOTICE") 7 | #pragma push_macro("WARNING") 8 | #pragma push_macro("ERROR") 9 | #pragma push_macro("eASSERT") 10 | 11 | #undef TRACE 12 | #define TRACE(fmt, ...) \ 13 | debug_log(MDBX_LOG_TRACE, __func__, __LINE__, fmt "\n", __VA_ARGS__) 14 | 15 | #undef DEBUG 16 | #define DEBUG(fmt, ...) \ 17 | debug_log(MDBX_LOG_DEBUG, __func__, __LINE__, fmt "\n", __VA_ARGS__) 18 | 19 | #undef VERBOSE 20 | #define VERBOSE(fmt, ...) \ 21 | debug_log(MDBX_LOG_VERBOSE, __func__, __LINE__, fmt "\n", __VA_ARGS__) 22 | 23 | #undef NOTICE 24 | #define NOTICE(fmt, ...) \ 25 | debug_log(MDBX_LOG_NOTICE, __func__, __LINE__, fmt "\n", __VA_ARGS__) 26 | 27 | #undef WARNING 28 | #define WARNING(fmt, ...) \ 29 | debug_log(MDBX_LOG_WARN, __func__, __LINE__, fmt "\n", __VA_ARGS__) 30 | 31 | #undef ERROR 32 | #define ERROR(fmt, ...) \ 33 | debug_log(MDBX_LOG_ERROR, __func__, __LINE__, fmt "\n", __VA_ARGS__) 34 | 35 | #undef eASSERT 36 | #define eASSERT(env, expr) ENSURE(env, expr) 37 | 38 | #if !defined(__clang__) 39 | #pragma GCC optimize("-Og") 40 | #endif 41 | 42 | #endif /* GCC only */ 43 | -------------------------------------------------------------------------------- /src/debug_end.h: -------------------------------------------------------------------------------- 1 | #if defined(__GNUC__) && !defined(__LCC__) 2 | 3 | #pragma pop_macro("TRACE") 4 | #pragma pop_macro("DEBUG") 5 | #pragma pop_macro("VERBOSE") 6 | #pragma pop_macro("NOTICE") 7 | #pragma pop_macro("WARNING") 8 | #pragma pop_macro("ERROR") 9 | #pragma pop_macro("eASSERT") 10 | 11 | #if !defined(__clang__) 12 | #pragma GCC reset_options 13 | #endif 14 | 15 | #endif /* GCC only */ 16 | -------------------------------------------------------------------------------- /src/man1/mdbx_chk.1: -------------------------------------------------------------------------------- 1 | .\" Copyright 2015-2023 Leonid Yuriev . 2 | .\" Copying restrictions apply. See COPYRIGHT/LICENSE. 3 | .TH MDBX_CHK 1 "2023-01-07" "MDBX 0.12.3" 4 | .SH NAME 5 | mdbx_chk \- MDBX checking tool 6 | .SH SYNOPSIS 7 | .B mdbx_chk 8 | [\c 9 | .BR \-V ] 10 | [\c 11 | .BR \-v [ v [ v ]]] 12 | [\c 13 | .BR \-n ] 14 | [\c 15 | .BR \-q ] 16 | [\c 17 | .BR \-c ] 18 | [\c 19 | .BR \-w ] 20 | [\c 21 | .BR \-d ] 22 | [\c 23 | .BR \-i ] 24 | [\c 25 | .BI \-s \ subdb\fR] 26 | .BR \ dbpath 27 | .SH DESCRIPTION 28 | The 29 | .B mdbx_chk 30 | utility intended to check an MDBX database file. 31 | .SH OPTIONS 32 | .TP 33 | .BR \-V 34 | Write the library version number to the standard output, and exit. 35 | .TP 36 | .BR \-v 37 | Produce verbose output, including summarize space and page usage statistics. 38 | If \fB\-vv\fP is given, be more verbose, show summarized B-tree info 39 | and space allocation. 40 | If \fB\-vvv\fP is given, be more verbose, include summarized statistics 41 | of leaf B-tree pages. 42 | If \fB\-vvvv\fP is given, be even more verbose, show info of each page 43 | during B-tree traversal and basic info of each GC record. 44 | If \fB\-vvvvv\fP is given, turn maximal verbosity, display the full list 45 | of page IDs in the GC records and size of each key-value pair of database(s). 46 | .TP 47 | .BR \-q 48 | Be quiet; do not output anything even if an error was detected. 49 | .TP 50 | .BR \-c 51 | Force using cooperative mode while opening environment, i.e. don't try to open 52 | in exclusive/monopolistic mode. Only exclusive/monopolistic mode allow complete 53 | check, including full check of all meta-pages and actual size of database file. 54 | .TP 55 | .BR \-w 56 | Open environment in read-write mode and lock for writing while checking. 57 | This could be impossible if environment already used by another process(s) 58 | in an incompatible read-write mode. This allow rollback to last steady commit 59 | (in case environment was not closed properly) and then check transaction IDs 60 | of meta-pages. Otherwise, without \fB\-w\fP option environment will be 61 | opened in read-only mode. 62 | .TP 63 | .BR \-d 64 | Disable page-by-page traversal of B-tree. In this case, without B-tree 65 | traversal, it is unable to check for lost-unused pages nor for double-used 66 | pages. 67 | .TP 68 | .BR \-i 69 | Ignore wrong order errors, which will likely false-positive if custom 70 | comparator(s) was used. 71 | .TP 72 | .BR \-s \ subdb 73 | Verify and show info only for a specific subdatabase. 74 | .TP 75 | .BR \-0 | \-1 | \-2 76 | Using specific meta-page 0, or 2 for checking. 77 | .TP 78 | .BR \-t 79 | Turn to a specified meta-page on successful check. 80 | .TP 81 | .BR \-T 82 | Turn to a specified meta-page EVEN ON UNSUCCESSFUL CHECK! 83 | .TP 84 | .BR \-u 85 | Warms up the DB before checking via notifying OS kernel of subsequent access to the database pages. 86 | .TP 87 | .BR \-U 88 | Warms up the DB before checking, notifying the OS kernel of subsequent access to the database pages, 89 | then forcibly loads ones by sequential access and tries to lock database pages in memory. 90 | .TP 91 | .BR \-n 92 | Open MDBX environment(s) which do not use subdirectories. 93 | This is legacy option. For now MDBX handles this automatically. 94 | 95 | .SH DIAGNOSTICS 96 | Exit status is zero if no errors occur. Errors result in a non-zero exit status 97 | and a diagnostic message being written to standard error 98 | if no quiet mode was requested. 99 | .SH "SEE ALSO" 100 | .BR mdbx_stat (1), 101 | .BR mdbx_copy (1), 102 | .BR mdbx_dump (1), 103 | .BR mdbx_load (1) 104 | .BR mdbx_drop (1) 105 | .SH AUTHOR 106 | Leonid Yuriev 107 | -------------------------------------------------------------------------------- /src/man1/mdbx_copy.1: -------------------------------------------------------------------------------- 1 | .\" Copyright 2015-2023 Leonid Yuriev . 2 | .\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved. 3 | .\" Copyright 2015,2016 Peter-Service R&D LLC . 4 | .\" Copying restrictions apply. See COPYRIGHT/LICENSE. 5 | .TH MDBX_COPY 1 "2023-01-07" "MDBX 0.12.3" 6 | .SH NAME 7 | mdbx_copy \- MDBX environment copy tool 8 | .SH SYNOPSIS 9 | .B mdbx_copy 10 | [\c 11 | .BR \-V ] 12 | [\c 13 | .BR \-q ] 14 | [\c 15 | .BR \-c ] 16 | [\c 17 | .BR \-n ] 18 | .B src_path 19 | [\c 20 | .BR dest_path ] 21 | .SH DESCRIPTION 22 | The 23 | .B mdbx_copy 24 | utility copies an MDBX environment. The environment can 25 | be copied regardless of whether it is currently in use. 26 | No lockfile is created, since it gets recreated at need. 27 | 28 | If 29 | .I dest_path 30 | is specified it must be the path of an empty directory 31 | for storing the backup. Otherwise, the backup will be 32 | written to stdout. 33 | 34 | .SH OPTIONS 35 | .TP 36 | .BR \-V 37 | Write the library version number to the standard output, and exit. 38 | .TP 39 | .BR \-q 40 | Be quiet. 41 | .TP 42 | .BR \-c 43 | Compact while copying. Only current data pages will be copied; freed 44 | or unused pages will be omitted from the copy. This option will 45 | slow down the backup process as it is more CPU-intensive. 46 | Currently it fails if the environment has suffered a page leak. 47 | .TP 48 | .BR \-u 49 | Warms up the DB before copying via notifying OS kernel of subsequent access to the database pages. 50 | .TP 51 | .BR \-U 52 | Warms up the DB before copying, notifying the OS kernel of subsequent access to the database pages, 53 | then forcibly loads ones by sequential access and tries to lock database pages in memory. 54 | .TP 55 | .BR \-n 56 | Open MDBX environment(s) which do not use subdirectories. 57 | This is legacy option. For now MDBX handles this automatically. 58 | 59 | .SH DIAGNOSTICS 60 | Exit status is zero if no errors occur. 61 | Errors result in a non-zero exit status and 62 | a diagnostic message being written to standard error. 63 | .SH CAVEATS 64 | This utility can trigger significant file size growth if run 65 | in parallel with write transactions, because pages which they 66 | free during copying cannot be reused until the copy is done. 67 | .SH "SEE ALSO" 68 | .BR mdbx_dump (1), 69 | .BR mdbx_chk (1), 70 | .BR mdbx_stat (1), 71 | .BR mdbx_load (1) 72 | .BR mdbx_drop (1) 73 | .SH AUTHOR 74 | Howard Chu of Symas Corporation , 75 | Leonid Yuriev 76 | -------------------------------------------------------------------------------- /src/man1/mdbx_drop.1: -------------------------------------------------------------------------------- 1 | .\" Copyright 2021-2023 Leonid Yuriev . 2 | .\" Copyright 2014-2021 Howard Chu, Symas Corp. All Rights Reserved. 3 | .\" Copying restrictions apply. See COPYRIGHT/LICENSE. 4 | .TH MDBX_DROP 1 "2023-01-07" "MDBX 0.12.3" 5 | .SH NAME 6 | mdbx_drop \- MDBX database delete tool 7 | .SH SYNOPSIS 8 | .B mdbx_drop 9 | [\c 10 | .BR \-V ] 11 | [\c 12 | .BR \-d ] 13 | [\c 14 | .BI \-s \ subdb\fR] 15 | [\c 16 | .BR \-n ] 17 | .BR \ dbpath 18 | .SH DESCRIPTION 19 | The 20 | .B mdbx_drop 21 | utility empties or deletes a database in the specified 22 | environment. 23 | .SH OPTIONS 24 | .TP 25 | .BR \-V 26 | Write the library version number to the standard output, and exit. 27 | .TP 28 | .BR \-d 29 | Delete the specified database, don't just empty it. 30 | .TP 31 | .BR \-s \ subdb 32 | Operate on a specific subdatabase. If no database is specified, only the main database is dropped. 33 | .TP 34 | .BR \-n 35 | Dump an MDBX database which does not use subdirectories. 36 | This is legacy option. For now MDBX handles this automatically. 37 | 38 | .SH DIAGNOSTICS 39 | Exit status is zero if no errors occur. 40 | Errors result in a non-zero exit status and 41 | a diagnostic message being written to standard error. 42 | .SH "SEE ALSO" 43 | .BR mdbx_load (1), 44 | .BR mdbx_copy (1), 45 | .BR mdbx_chk (1), 46 | .BR mdbx_stat (1) 47 | .SH AUTHOR 48 | Howard Chu of Symas Corporation 49 | -------------------------------------------------------------------------------- /src/man1/mdbx_dump.1: -------------------------------------------------------------------------------- 1 | .\" Copyright 2015-2023 Leonid Yuriev . 2 | .\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved. 3 | .\" Copyright 2015,2016 Peter-Service R&D LLC . 4 | .\" Copying restrictions apply. See COPYRIGHT/LICENSE. 5 | .TH MDBX_DUMP 1 "2023-01-07" "MDBX 0.12.3" 6 | .SH NAME 7 | mdbx_dump \- MDBX environment export tool 8 | .SH SYNOPSIS 9 | .B mdbx_dump 10 | [\c 11 | .BR \-V ] 12 | [\c 13 | .BR \-q ] 14 | [\c 15 | .BI \-f \ file\fR] 16 | [\c 17 | .BR \-l ] 18 | [\c 19 | .BR \-p ] 20 | [\c 21 | .BR \-a \ | 22 | .BI \-s \ subdb\fR] 23 | [\c 24 | .BR \-r ] 25 | [\c 26 | .BR \-n ] 27 | .BR \ dbpath 28 | .SH DESCRIPTION 29 | The 30 | .B mdbx_dump 31 | utility reads a database and writes its contents to the 32 | standard output using a portable flat-text format 33 | understood by the 34 | .BR mdbx_load (1) 35 | utility. 36 | .SH OPTIONS 37 | .TP 38 | .BR \-V 39 | Write the library version number to the standard output, and exit. 40 | .TP 41 | .BR \-q 42 | Be quiet. 43 | .TP 44 | .BR \-f \ file 45 | Write to the specified file instead of to the standard output. 46 | .TP 47 | .BR \-l 48 | List the databases stored in the environment. Just the 49 | names will be listed, no data will be output. 50 | .TP 51 | .BR \-p 52 | If characters in either the key or data items are printing characters (as 53 | defined by isprint(3)), output them directly. This option permits users to 54 | use standard text editors and tools to modify the contents of databases. 55 | 56 | Note: different systems may have different notions about what characters 57 | are considered printing characters, and databases dumped in this manner may 58 | be less portable to external systems. 59 | .TP 60 | .BR \-a 61 | Dump all of the subdatabases in the environment. 62 | .TP 63 | .BR \-s \ subdb 64 | Dump a specific subdatabase. If no database is specified, only the main database is dumped. 65 | .TP 66 | .BR \-r 67 | Rescure mode. Ignore some errors to dump corrupted DB. 68 | .TP 69 | .BR \-u 70 | Warms up the DB before dumping via notifying OS kernel of subsequent access to the database pages. 71 | .TP 72 | .BR \-U 73 | Warms up the DB before dumping, notifying the OS kernel of subsequent access to the database pages, 74 | then forcibly loads ones by sequential access and tries to lock database pages in memory. 75 | .TP 76 | .BR \-n 77 | Dump an MDBX database which does not use subdirectories. 78 | This is legacy option. For now MDBX handles this automatically. 79 | 80 | .SH DIAGNOSTICS 81 | Exit status is zero if no errors occur. 82 | Errors result in a non-zero exit status and 83 | a diagnostic message being written to standard error. 84 | 85 | Dumping and reloading databases that use user-defined comparison functions 86 | will result in new databases that use the default comparison functions. 87 | \fBIn this case it is quite likely that the reloaded database will be 88 | damaged beyond repair permitting neither record storage nor retrieval.\fP 89 | 90 | The only available workaround is to modify the source for the 91 | .BR mdbx_load (1) 92 | utility to load the database using the correct comparison functions. 93 | .SH "SEE ALSO" 94 | .BR mdbx_load (1), 95 | .BR mdbx_copy (1), 96 | .BR mdbx_chk (1), 97 | .BR mdbx_stat (1) 98 | .BR mdbx_drop (1) 99 | .SH AUTHOR 100 | Howard Chu of Symas Corporation , 101 | Leonid Yuriev 102 | -------------------------------------------------------------------------------- /src/man1/mdbx_load.1: -------------------------------------------------------------------------------- 1 | .\" Copyright 2015-2023 Leonid Yuriev . 2 | .\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved. 3 | .\" Copyright 2015,2016 Peter-Service R&D LLC . 4 | .\" Copying restrictions apply. See COPYRIGHT/LICENSE. 5 | .TH MDBX_LOAD 1 "2023-01-07" "MDBX 0.12.3" 6 | .SH NAME 7 | mdbx_load \- MDBX environment import tool 8 | .SH SYNOPSIS 9 | .B mdbx_load 10 | [\c 11 | .BR \-V ] 12 | [\c 13 | .BR \-q ] 14 | [\c 15 | .BR \-a ] 16 | [\c 17 | .BI \-f \ file\fR] 18 | [\c 19 | .BI \-s \ subdb\fR] 20 | [\c 21 | .BR \-N ] 22 | [\c 23 | .BR \-T ] 24 | [\c 25 | .BR \-r ] 26 | [\c 27 | .BR \-n ] 28 | .BR \ dbpath 29 | .SH DESCRIPTION 30 | The 31 | .B mdbx_load 32 | utility reads from the standard input and loads it into the 33 | MDBX environment 34 | .BR dbpath . 35 | 36 | The input to 37 | .B mdbx_load 38 | must be in the output format specified by the 39 | .BR mdbx_dump (1) 40 | utility or as specified by the 41 | .B -T 42 | option below. 43 | 44 | A simple escape mechanism, where newline and backslash (\\) characters are special, is 45 | applied to the text input. Newline characters are interpreted as record separators. 46 | Backslash characters in the text will be interpreted in one of two ways: If the backslash 47 | character precedes another backslash character, the pair will be interpreted as a literal 48 | backslash. If the backslash character precedes any other character, the two characters 49 | following the backslash will be interpreted as a hexadecimal specification of a single 50 | character; for example, \\0a is a newline character in the ASCII character set. 51 | 52 | For this reason, any backslash or newline characters that naturally occur in the text 53 | input must be escaped to avoid misinterpretation by 54 | .BR mdbx_load . 55 | 56 | .SH OPTIONS 57 | .TP 58 | .BR \-V 59 | Write the library version number to the standard output, and exit. 60 | .TP 61 | .BR \-q 62 | Be quiet. 63 | .TP 64 | .BR \-a 65 | Append all records in the order they appear in the input. The input is assumed to already be 66 | in correctly sorted order and no sorting or checking for redundant values will be performed. 67 | This option must be used to reload data that was produced by running 68 | .B mdbx_dump 69 | on a database that uses custom compare functions. 70 | .TP 71 | .BR \-f \ file 72 | Read from the specified file instead of from the standard input. 73 | .TP 74 | .BR \-s \ subdb 75 | Load a specific subdatabase. If no database is specified, data is loaded into the main database. 76 | .TP 77 | .BR \-N 78 | Don't overwrite existing records when loading into an already existing database; just skip them. 79 | .TP 80 | .BR \-T 81 | Load data from simple text files. The input must be paired lines of text, where the first 82 | line of the pair is the key item, and the second line of the pair is its corresponding 83 | data item. 84 | .TP 85 | .BR \-r 86 | Rescure mode. Ignore errors to load corrupted DB dump. 87 | .TP 88 | .BR \-n 89 | Load an MDBX database which does not use subdirectories. 90 | This is legacy option. For now MDBX handles this automatically. 91 | 92 | .SH DIAGNOSTICS 93 | Exit status is zero if no errors occur. 94 | Errors result in a non-zero exit status and 95 | a diagnostic message being written to standard error. 96 | 97 | .SH "SEE ALSO" 98 | .BR mdbx_dump (1), 99 | .BR mdbx_chk (1), 100 | .BR mdbx_stat (1), 101 | .BR mdbx_copy (1) 102 | .BR mdbx_drop (1) 103 | .SH AUTHOR 104 | Howard Chu of Symas Corporation , 105 | Leonid Yuriev 106 | -------------------------------------------------------------------------------- /src/man1/mdbx_stat.1: -------------------------------------------------------------------------------- 1 | .\" Copyright 2015-2023 Leonid Yuriev . 2 | .\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved. 3 | .\" Copyright 2015,2016 Peter-Service R&D LLC . 4 | .\" Copying restrictions apply. See COPYRIGHT/LICENSE. 5 | .TH MDBX_STAT 1 "2023-01-07" "MDBX 0.12.3" 6 | .SH NAME 7 | mdbx_stat \- MDBX environment status tool 8 | .SH SYNOPSIS 9 | .B mdbx_stat 10 | [\c 11 | .BR \-V ] 12 | [\c 13 | .BR \-q ] 14 | [\c 15 | .BR \-p ] 16 | [\c 17 | .BR \-e ] 18 | [\c 19 | .BR \-f [ f [ f ]]] 20 | [\c 21 | .BR \-r [ r ]] 22 | [\c 23 | .BR \-a \ | 24 | .BI \-s \ subdb\fR] 25 | .BR \ dbpath 26 | [\c 27 | .BR \-n ] 28 | .SH DESCRIPTION 29 | The 30 | .B mdbx_stat 31 | utility displays the status of an MDBX environment. 32 | .SH OPTIONS 33 | .TP 34 | .BR \-V 35 | Write the library version number to the standard output, and exit. 36 | .TP 37 | .BR \-q 38 | Be quiet. 39 | .TP 40 | .BR \-p 41 | Display overall statistics of page operations of all (running, completed 42 | and aborted) transactions in the current multi-process session (since the 43 | first process opened the database after everyone had previously closed it). 44 | .TP 45 | .BR \-e 46 | Display information about the database environment. 47 | .TP 48 | .BR \-f 49 | Display information about the environment GC. 50 | If \fB\-ff\fP is given, summarize each GC/freelist entry. 51 | If \fB\-fff\fP is given, display the full list of page IDs in the GC/freelist. 52 | .TP 53 | .BR \-r 54 | Display information about the environment reader table. 55 | Shows the process ID, thread ID, and transaction ID for each active 56 | reader slot. The process ID and transaction ID are in decimal, the 57 | thread ID is in hexadecimal. The transaction ID is displayed as "-" 58 | if the reader does not currently have a read transaction open. 59 | If \fB\-rr\fP is given, check for stale entries in the reader 60 | table and clear them. The reader table will be printed again 61 | after the check is performed. 62 | .TP 63 | .BR \-a 64 | Display the status of all of the subdatabases in the environment. 65 | .TP 66 | .BR \-s \ subdb 67 | Display the status of a specific subdatabase. 68 | .TP 69 | .BR \-n 70 | Display the status of an MDBX database which does not use subdirectories. 71 | This is legacy option. For now MDBX handles this automatically 72 | for existing databases, but may be required while creating new. 73 | 74 | .SH DIAGNOSTICS 75 | Exit status is zero if no errors occur. 76 | Errors result in a non-zero exit status and 77 | a diagnostic message being written to standard error. 78 | .SH "SEE ALSO" 79 | .BR mdbx_chk (1), 80 | .BR mdbx_copy (1), 81 | .BR mdbx_dump (1), 82 | .BR mdbx_load (1) 83 | .BR mdbx_drop (1) 84 | .SH AUTHOR 85 | Howard Chu of Symas Corporation , 86 | Leonid Yuriev 87 | -------------------------------------------------------------------------------- /src/mdbx_copy.c: -------------------------------------------------------------------------------- 1 | /* mdbx_copy.c - memory-mapped database backup tool */ 2 | 3 | /* 4 | * Copyright 2015-2023 Leonid Yuriev 5 | * and other libmdbx authors: please see AUTHORS file. 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted only as authorized by the OpenLDAP 10 | * Public License. 11 | * 12 | * A copy of this license is available in the file LICENSE in the 13 | * top-level directory of the distribution or, alternatively, at 14 | * . */ 15 | 16 | #ifdef _MSC_VER 17 | #if _MSC_VER > 1800 18 | #pragma warning(disable : 4464) /* relative include path contains '..' */ 19 | #endif 20 | #pragma warning(disable : 4996) /* The POSIX name is deprecated... */ 21 | #endif /* _MSC_VER (warnings) */ 22 | 23 | #define xMDBX_TOOLS /* Avoid using internal eASSERT() */ 24 | #include "internals.h" 25 | 26 | #if defined(_WIN32) || defined(_WIN64) 27 | #include "wingetopt.h" 28 | 29 | static volatile BOOL user_break; 30 | static BOOL WINAPI ConsoleBreakHandlerRoutine(DWORD dwCtrlType) { 31 | (void)dwCtrlType; 32 | user_break = true; 33 | return true; 34 | } 35 | 36 | #else /* WINDOWS */ 37 | 38 | static volatile sig_atomic_t user_break; 39 | static void signal_handler(int sig) { 40 | (void)sig; 41 | user_break = 1; 42 | } 43 | 44 | #endif /* !WINDOWS */ 45 | 46 | static void usage(const char *prog) { 47 | fprintf( 48 | stderr, 49 | "usage: %s [-V] [-q] [-c] [-u|U] src_path [dest_path]\n" 50 | " -V\t\tprint version and exit\n" 51 | " -q\t\tbe quiet\n" 52 | " -c\t\tenable compactification (skip unused pages)\n" 53 | " -u\t\twarmup database before copying\n" 54 | " -U\t\twarmup and try lock database pages in memory before copying\n" 55 | " src_path\tsource database\n" 56 | " dest_path\tdestination (stdout if not specified)\n", 57 | prog); 58 | exit(EXIT_FAILURE); 59 | } 60 | 61 | int main(int argc, char *argv[]) { 62 | int rc; 63 | MDBX_env *env = NULL; 64 | const char *progname = argv[0], *act; 65 | unsigned flags = MDBX_RDONLY; 66 | unsigned cpflags = 0; 67 | bool quiet = false; 68 | bool warmup = false; 69 | MDBX_warmup_flags_t warmup_flags = MDBX_warmup_default; 70 | 71 | for (; argc > 1 && argv[1][0] == '-'; argc--, argv++) { 72 | if (argv[1][1] == 'n' && argv[1][2] == '\0') 73 | flags |= MDBX_NOSUBDIR; 74 | else if (argv[1][1] == 'c' && argv[1][2] == '\0') 75 | cpflags |= MDBX_CP_COMPACT; 76 | else if (argv[1][1] == 'q' && argv[1][2] == '\0') 77 | quiet = true; 78 | else if (argv[1][1] == 'u' && argv[1][2] == '\0') 79 | warmup = true; 80 | else if (argv[1][1] == 'U' && argv[1][2] == '\0') { 81 | warmup = true; 82 | warmup_flags = 83 | MDBX_warmup_force | MDBX_warmup_touchlimit | MDBX_warmup_lock; 84 | } else if ((argv[1][1] == 'h' && argv[1][2] == '\0') || 85 | strcmp(argv[1], "--help") == 0) 86 | usage(progname); 87 | else if (argv[1][1] == 'V' && argv[1][2] == '\0') { 88 | printf("mdbx_copy version %d.%d.%d.%d\n" 89 | " - source: %s %s, commit %s, tree %s\n" 90 | " - anchor: %s\n" 91 | " - build: %s for %s by %s\n" 92 | " - flags: %s\n" 93 | " - options: %s\n", 94 | mdbx_version.major, mdbx_version.minor, mdbx_version.release, 95 | mdbx_version.revision, mdbx_version.git.describe, 96 | mdbx_version.git.datetime, mdbx_version.git.commit, 97 | mdbx_version.git.tree, mdbx_sourcery_anchor, mdbx_build.datetime, 98 | mdbx_build.target, mdbx_build.compiler, mdbx_build.flags, 99 | mdbx_build.options); 100 | return EXIT_SUCCESS; 101 | } else 102 | argc = 0; 103 | } 104 | 105 | if (argc < 2 || argc > 3) 106 | usage(progname); 107 | 108 | #if defined(_WIN32) || defined(_WIN64) 109 | SetConsoleCtrlHandler(ConsoleBreakHandlerRoutine, true); 110 | #else 111 | #ifdef SIGPIPE 112 | signal(SIGPIPE, signal_handler); 113 | #endif 114 | #ifdef SIGHUP 115 | signal(SIGHUP, signal_handler); 116 | #endif 117 | signal(SIGINT, signal_handler); 118 | signal(SIGTERM, signal_handler); 119 | #endif /* !WINDOWS */ 120 | 121 | if (!quiet) { 122 | fprintf((argc == 2) ? stderr : stdout, 123 | "mdbx_copy %s (%s, T-%s)\nRunning for copy %s to %s...\n", 124 | mdbx_version.git.describe, mdbx_version.git.datetime, 125 | mdbx_version.git.tree, argv[1], (argc == 2) ? "stdout" : argv[2]); 126 | fflush(NULL); 127 | } 128 | 129 | act = "opening environment"; 130 | rc = mdbx_env_create(&env); 131 | if (rc == MDBX_SUCCESS) 132 | rc = mdbx_env_open(env, argv[1], flags, 0); 133 | 134 | if (rc == MDBX_SUCCESS && warmup) { 135 | act = "warming up"; 136 | rc = mdbx_env_warmup(env, nullptr, warmup_flags, 3600 * 65536); 137 | } 138 | 139 | if (!MDBX_IS_ERROR(rc)) { 140 | act = "copying"; 141 | if (argc == 2) { 142 | mdbx_filehandle_t fd; 143 | #if defined(_WIN32) || defined(_WIN64) 144 | fd = GetStdHandle(STD_OUTPUT_HANDLE); 145 | #else 146 | fd = fileno(stdout); 147 | #endif 148 | rc = mdbx_env_copy2fd(env, fd, cpflags); 149 | } else 150 | rc = mdbx_env_copy(env, argv[2], cpflags); 151 | } 152 | if (rc) 153 | fprintf(stderr, "%s: %s failed, error %d (%s)\n", progname, act, rc, 154 | mdbx_strerror(rc)); 155 | mdbx_env_close(env); 156 | 157 | return rc ? EXIT_FAILURE : EXIT_SUCCESS; 158 | } 159 | -------------------------------------------------------------------------------- /src/mdbx_drop.c: -------------------------------------------------------------------------------- 1 | /* mdbx_drop.c - memory-mapped database delete tool */ 2 | 3 | /* 4 | * Copyright 2021-2023 Leonid Yuriev 5 | * and other libmdbx authors: please see AUTHORS file. 6 | * 7 | * Copyright 2016-2021 Howard Chu, Symas Corp. 8 | * All rights reserved. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted only as authorized by the OpenLDAP 12 | * Public License. 13 | * 14 | * A copy of this license is available in the file LICENSE in the 15 | * top-level directory of the distribution or, alternatively, at 16 | * . */ 17 | 18 | #ifdef _MSC_VER 19 | #if _MSC_VER > 1800 20 | #pragma warning(disable : 4464) /* relative include path contains '..' */ 21 | #endif 22 | #pragma warning(disable : 4996) /* The POSIX name is deprecated... */ 23 | #endif /* _MSC_VER (warnings) */ 24 | 25 | #define xMDBX_TOOLS /* Avoid using internal eASSERT() */ 26 | #include "internals.h" 27 | 28 | #include 29 | 30 | #if defined(_WIN32) || defined(_WIN64) 31 | #include "wingetopt.h" 32 | 33 | static volatile BOOL user_break; 34 | static BOOL WINAPI ConsoleBreakHandlerRoutine(DWORD dwCtrlType) { 35 | (void)dwCtrlType; 36 | user_break = true; 37 | return true; 38 | } 39 | 40 | #else /* WINDOWS */ 41 | 42 | static volatile sig_atomic_t user_break; 43 | static void signal_handler(int sig) { 44 | (void)sig; 45 | user_break = 1; 46 | } 47 | 48 | #endif /* !WINDOWS */ 49 | 50 | static char *prog; 51 | bool quiet = false; 52 | static void usage(void) { 53 | fprintf(stderr, 54 | "usage: %s [-V] [-q] [-d] [-s name] dbpath\n" 55 | " -V\t\tprint version and exit\n" 56 | " -q\t\tbe quiet\n" 57 | " -d\t\tdelete the specified database, don't just empty it\n" 58 | " -s name\tdrop the specified named subDB\n" 59 | " \t\tby default empty the main DB\n", 60 | prog); 61 | exit(EXIT_FAILURE); 62 | } 63 | 64 | static void error(const char *func, int rc) { 65 | if (!quiet) 66 | fprintf(stderr, "%s: %s() error %d %s\n", prog, func, rc, 67 | mdbx_strerror(rc)); 68 | } 69 | 70 | int main(int argc, char *argv[]) { 71 | int i, rc; 72 | MDBX_env *env; 73 | MDBX_txn *txn; 74 | MDBX_dbi dbi; 75 | char *envname = nullptr; 76 | char *subname = nullptr; 77 | int envflags = MDBX_ACCEDE; 78 | bool delete = false; 79 | 80 | prog = argv[0]; 81 | if (argc < 2) 82 | usage(); 83 | 84 | while ((i = getopt(argc, argv, 85 | "d" 86 | "s:" 87 | "n" 88 | "q" 89 | "V")) != EOF) { 90 | switch (i) { 91 | case 'V': 92 | printf("mdbx_drop version %d.%d.%d.%d\n" 93 | " - source: %s %s, commit %s, tree %s\n" 94 | " - anchor: %s\n" 95 | " - build: %s for %s by %s\n" 96 | " - flags: %s\n" 97 | " - options: %s\n", 98 | mdbx_version.major, mdbx_version.minor, mdbx_version.release, 99 | mdbx_version.revision, mdbx_version.git.describe, 100 | mdbx_version.git.datetime, mdbx_version.git.commit, 101 | mdbx_version.git.tree, mdbx_sourcery_anchor, mdbx_build.datetime, 102 | mdbx_build.target, mdbx_build.compiler, mdbx_build.flags, 103 | mdbx_build.options); 104 | return EXIT_SUCCESS; 105 | case 'q': 106 | quiet = true; 107 | break; 108 | case 'd': 109 | delete = true; 110 | break; 111 | case 'n': 112 | break; 113 | case 's': 114 | subname = optarg; 115 | break; 116 | default: 117 | usage(); 118 | } 119 | } 120 | 121 | if (optind != argc - 1) 122 | usage(); 123 | 124 | #if defined(_WIN32) || defined(_WIN64) 125 | SetConsoleCtrlHandler(ConsoleBreakHandlerRoutine, true); 126 | #else 127 | #ifdef SIGPIPE 128 | signal(SIGPIPE, signal_handler); 129 | #endif 130 | #ifdef SIGHUP 131 | signal(SIGHUP, signal_handler); 132 | #endif 133 | signal(SIGINT, signal_handler); 134 | signal(SIGTERM, signal_handler); 135 | #endif /* !WINDOWS */ 136 | 137 | envname = argv[optind]; 138 | if (!quiet) { 139 | printf("mdbx_drop %s (%s, T-%s)\nRunning for %s/%s...\n", 140 | mdbx_version.git.describe, mdbx_version.git.datetime, 141 | mdbx_version.git.tree, envname, subname ? subname : "@MAIN"); 142 | fflush(nullptr); 143 | } 144 | 145 | rc = mdbx_env_create(&env); 146 | if (unlikely(rc != MDBX_SUCCESS)) { 147 | error("mdbx_env_create", rc); 148 | return EXIT_FAILURE; 149 | } 150 | 151 | if (subname) { 152 | rc = mdbx_env_set_maxdbs(env, 2); 153 | if (unlikely(rc != MDBX_SUCCESS)) { 154 | error("mdbx_env_set_maxdbs", rc); 155 | goto env_close; 156 | } 157 | } 158 | 159 | rc = mdbx_env_open(env, envname, envflags, 0); 160 | if (unlikely(rc != MDBX_SUCCESS)) { 161 | error("mdbx_env_open", rc); 162 | goto env_close; 163 | } 164 | 165 | rc = mdbx_txn_begin(env, NULL, 0, &txn); 166 | if (unlikely(rc != MDBX_SUCCESS)) { 167 | error("mdbx_txn_begin", rc); 168 | goto env_close; 169 | } 170 | 171 | rc = mdbx_dbi_open(txn, subname, MDBX_DB_ACCEDE, &dbi); 172 | if (unlikely(rc != MDBX_SUCCESS)) { 173 | error("mdbx_dbi_open", rc); 174 | goto txn_abort; 175 | } 176 | 177 | rc = mdbx_drop(txn, dbi, delete); 178 | if (unlikely(rc != MDBX_SUCCESS)) { 179 | error("mdbx_drop", rc); 180 | goto txn_abort; 181 | } 182 | 183 | rc = mdbx_txn_commit(txn); 184 | if (unlikely(rc != MDBX_SUCCESS)) { 185 | error("mdbx_txn_commit", rc); 186 | goto txn_abort; 187 | } 188 | txn = nullptr; 189 | 190 | txn_abort: 191 | if (txn) 192 | mdbx_txn_abort(txn); 193 | env_close: 194 | mdbx_env_close(env); 195 | 196 | return rc ? EXIT_FAILURE : EXIT_SUCCESS; 197 | } 198 | -------------------------------------------------------------------------------- /src/version.c.in: -------------------------------------------------------------------------------- 1 | /* This is CMake-template for libmdbx's version.c 2 | ******************************************************************************/ 3 | 4 | #include "internals.h" 5 | 6 | #if MDBX_VERSION_MAJOR != ${MDBX_VERSION_MAJOR} || \ 7 | MDBX_VERSION_MINOR != ${MDBX_VERSION_MINOR} 8 | #error "API version mismatch! Had `git fetch --tags` done?" 9 | #endif 10 | 11 | static const char sourcery[] = MDBX_STRINGIFY(MDBX_BUILD_SOURCERY); 12 | 13 | __dll_export 14 | #ifdef __attribute_used__ 15 | __attribute_used__ 16 | #elif defined(__GNUC__) || __has_attribute(__used__) 17 | __attribute__((__used__)) 18 | #endif 19 | #ifdef __attribute_externally_visible__ 20 | __attribute_externally_visible__ 21 | #elif (defined(__GNUC__) && !defined(__clang__)) || \ 22 | __has_attribute(__externally_visible__) 23 | __attribute__((__externally_visible__)) 24 | #endif 25 | const struct MDBX_version_info mdbx_version = { 26 | ${MDBX_VERSION_MAJOR}, 27 | ${MDBX_VERSION_MINOR}, 28 | ${MDBX_VERSION_RELEASE}, 29 | ${MDBX_VERSION_REVISION}, 30 | {"@MDBX_GIT_TIMESTAMP@", "@MDBX_GIT_TREE@", "@MDBX_GIT_COMMIT@", 31 | "@MDBX_GIT_DESCRIBE@"}, 32 | sourcery}; 33 | 34 | __dll_export 35 | #ifdef __attribute_used__ 36 | __attribute_used__ 37 | #elif defined(__GNUC__) || __has_attribute(__used__) 38 | __attribute__((__used__)) 39 | #endif 40 | #ifdef __attribute_externally_visible__ 41 | __attribute_externally_visible__ 42 | #elif (defined(__GNUC__) && !defined(__clang__)) || \ 43 | __has_attribute(__externally_visible__) 44 | __attribute__((__externally_visible__)) 45 | #endif 46 | const char *const mdbx_sourcery_anchor = sourcery; 47 | -------------------------------------------------------------------------------- /src/wingetopt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * POSIX getopt for Windows 3 | * 4 | * AT&T Public License 5 | * 6 | * Code given out at the 1985 UNIFORUM conference in Dallas. 7 | */ 8 | 9 | /*----------------------------------------------------------------------------*/ 10 | /* Microsoft compiler generates a lot of warning for self includes... */ 11 | 12 | #ifdef _MSC_VER 13 | #pragma warning(push, 1) 14 | #pragma warning(disable : 4548) /* expression before comma has no effect; \ 15 | expected expression with side - effect */ 16 | #pragma warning(disable : 4530) /* C++ exception handler used, but unwind \ 17 | * semantics are not enabled. Specify /EHsc */ 18 | #pragma warning(disable : 4577) /* 'noexcept' used with no exception handling \ 19 | * mode specified; termination on exception is \ 20 | * not guaranteed. Specify /EHsc */ 21 | #if !defined(_CRT_SECURE_NO_WARNINGS) 22 | #define _CRT_SECURE_NO_WARNINGS 23 | #endif 24 | #endif /* _MSC_VER (warnings) */ 25 | 26 | #include "wingetopt.h" 27 | #include 28 | #include 29 | 30 | #ifdef _MSC_VER 31 | #pragma warning(pop) 32 | #endif 33 | /*----------------------------------------------------------------------------*/ 34 | 35 | #ifndef NULL 36 | #define NULL 0 37 | #endif 38 | 39 | #ifndef EOF 40 | #define EOF (-1) 41 | #endif 42 | 43 | int optind = 1; 44 | int optopt; 45 | char *optarg; 46 | 47 | int getopt(int argc, char *const argv[], const char *opts) { 48 | static int sp = 1; 49 | int c; 50 | const char *cp; 51 | 52 | if (sp == 1) { 53 | if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') 54 | return EOF; 55 | else if (strcmp(argv[optind], "--") == 0) { 56 | optind++; 57 | return EOF; 58 | } 59 | } 60 | optopt = c = argv[optind][sp]; 61 | if (c == ':' || (cp = strchr(opts, c)) == NULL) { 62 | fprintf(stderr, "%s: %s -- %c\n", argv[0], "illegal option", c); 63 | if (argv[optind][++sp] == '\0') { 64 | optind++; 65 | sp = 1; 66 | } 67 | return '?'; 68 | } 69 | if (*++cp == ':') { 70 | if (argv[optind][sp + 1] != '\0') 71 | optarg = &argv[optind++][sp + 1]; 72 | else if (++optind >= argc) { 73 | fprintf(stderr, "%s: %s -- %c\n", argv[0], "option requires an argument", 74 | c); 75 | sp = 1; 76 | return '?'; 77 | } else 78 | optarg = argv[optind++]; 79 | sp = 1; 80 | } else { 81 | if (argv[optind][++sp] == '\0') { 82 | sp = 1; 83 | optind++; 84 | } 85 | optarg = NULL; 86 | } 87 | return c; 88 | } 89 | -------------------------------------------------------------------------------- /src/wingetopt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * POSIX getopt for Windows 3 | * 4 | * AT&T Public License 5 | * 6 | * Code given out at the 1985 UNIFORUM conference in Dallas. 7 | */ 8 | 9 | #ifndef _WINGETOPT_H_ 10 | #define _WINGETOPT_H_ 11 | 12 | /* Bit of madness for Windows console */ 13 | #define mdbx_strerror mdbx_strerror_ANSI2OEM 14 | #define mdbx_strerror_r mdbx_strerror_r_ANSI2OEM 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | extern int opterr; 21 | extern int optind; 22 | extern int optopt; 23 | extern char *optarg; 24 | int getopt(int argc, char *const argv[], const char *optstring); 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | 30 | #endif /* _GETOPT_H_ */ 31 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | enable_language(CXX) 2 | include(../cmake/compiler.cmake) 3 | 4 | set(LIBMDBX_TEST_SOURCES 5 | base.h++ 6 | cases.c++ 7 | chrono.c++ 8 | chrono.h++ 9 | config.c++ 10 | config.h++ 11 | copy.c++ 12 | dead.c++ 13 | hill.c++ 14 | jitter.c++ 15 | keygen.c++ 16 | keygen.h++ 17 | log.c++ 18 | log.h++ 19 | main.c++ 20 | osal.h++ 21 | osal-unix.c++ 22 | osal-windows.c++ 23 | test.c++ 24 | test.h++ 25 | try.c++ 26 | utils.c++ 27 | utils.h++ 28 | append.c++ 29 | ttl.c++ 30 | nested.c++ 31 | ) 32 | 33 | if(NOT MDBX_BUILD_CXX) 34 | probe_libcxx_filesystem() 35 | list(APPEND LIBMDBX_TEST_SOURCES "${MDBX_SOURCE_DIR}/mdbx.c++" ../mdbx.h++) 36 | endif() 37 | 38 | add_executable(mdbx_test ${LIBMDBX_TEST_SOURCES}) 39 | 40 | if(MDBX_CXX_STANDARD) 41 | set_target_properties(mdbx_test PROPERTIES 42 | CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON) 43 | endif() 44 | 45 | set_target_properties(mdbx_test PROPERTIES 46 | INTERPROCEDURAL_OPTIMIZATION $) 47 | target_setup_options(mdbx_test) 48 | 49 | if(NOT MDBX_BUILD_CXX AND LIBCXX_FILESYSTEM) 50 | if(CMAKE_COMPILER_IS_ELBRUSCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 1.25.23 51 | AND NOT CMAKE_VERSION VERSION_LESS 3.13) 52 | target_link_options(mdbx_test PRIVATE "-Wl,--allow-multiple-definition") 53 | endif() 54 | target_link_libraries(mdbx_test ${LIBCXX_FILESYSTEM}) 55 | endif() 56 | 57 | if(CMAKE_VERSION VERSION_LESS 3.1) 58 | target_link_libraries(mdbx_test ${TOOL_MDBX_LIB} ${LIB_MATH} ${CMAKE_THREAD_LIBS_INIT}) 59 | else() 60 | target_link_libraries(mdbx_test ${TOOL_MDBX_LIB} ${LIB_MATH} Threads::Threads) 61 | endif() 62 | if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") 63 | target_link_libraries(mdbx_test winmm.lib) 64 | endif() 65 | 66 | if(UNIX AND NOT SUBPROJECT) 67 | add_executable(pcrf_test pcrf/pcrf_test.c) 68 | target_include_directories(pcrf_test PRIVATE "${PROJECT_SOURCE_DIR}") 69 | target_link_libraries(pcrf_test ${TOOL_MDBX_LIB}) 70 | endif() 71 | 72 | ################################################################################ 73 | 74 | if (CMAKE_CROSSCOMPILING AND NOT CMAKE_CROSSCOMPILING_EMULATOR) 75 | message(WARNING "No emulator to run cross-compiled tests") 76 | add_test(NAME fake_since_no_crosscompiling_emulator COMMAND ${CMAKE_COMMAND} -E 77 | echo "No emulator to run cross-compiled tests") 78 | else() 79 | 80 | string(RANDOM LENGTH 9 ALPHABET "1234567890" test_seed) 81 | message(STATUS "The ${test_seed} will be used for seeding tests. Re-run cmake to re-seed it.") 82 | 83 | add_test(NAME smoke COMMAND ${MDBX_OUTPUT_DIR}/mdbx_test 84 | --loglevel=verbose 85 | --keygen.seed=${test_seed} 86 | --progress --console=no --pathname=smoke.db --dont-cleanup-after basic) 87 | set_tests_properties(smoke PROPERTIES 88 | TIMEOUT 600 89 | RUN_SERIAL OFF) 90 | if(MDBX_BUILD_TOOLS) 91 | add_test(NAME smoke_chk COMMAND ${MDBX_OUTPUT_DIR}/mdbx_chk -nvv smoke.db) 92 | set_tests_properties(smoke_chk PROPERTIES 93 | DEPENDS smoke 94 | TIMEOUT 60 95 | FAIL_REGULAR_EXPRESSION "cooperative mode" 96 | REQUIRED_FILES smoke.db) 97 | add_test(NAME smoke_chk_copy COMMAND ${MDBX_OUTPUT_DIR}/mdbx_chk -nvv smoke.db-copy) 98 | set_tests_properties(smoke_chk_copy PROPERTIES 99 | DEPENDS smoke 100 | TIMEOUT 60 101 | FAIL_REGULAR_EXPRESSION "cooperative mode" 102 | REQUIRED_FILES smoke.db-copy) 103 | endif() 104 | 105 | add_test(NAME dupsort_writemap COMMAND ${MDBX_OUTPUT_DIR}/mdbx_test 106 | --loglevel=notice 107 | --keygen.seed=${test_seed} 108 | --table=+data.integer --keygen.split=29 --datalen.min=min --datalen.max=max --progress --console=no 109 | --repeat=2 --pathname=dupsort_writemap.db --dont-cleanup-after basic) 110 | set_tests_properties(dupsort_writemap PROPERTIES 111 | TIMEOUT 600 112 | RUN_SERIAL OFF) 113 | if(MDBX_BUILD_TOOLS) 114 | add_test(NAME dupsort_writemap_chk COMMAND ${MDBX_OUTPUT_DIR}/mdbx_chk -nvvwc dupsort_writemap.db) 115 | set_tests_properties(dupsort_writemap_chk PROPERTIES 116 | DEPENDS dupsort_writemap 117 | TIMEOUT 60 118 | REQUIRED_FILES dupsort_writemap.db) 119 | add_test(NAME dupsort_writemap_chk_copy COMMAND ${MDBX_OUTPUT_DIR}/mdbx_chk -nvvc dupsort_writemap.db-copy) 120 | set_tests_properties(dupsort_writemap_chk_copy PROPERTIES 121 | DEPENDS dupsort_writemap 122 | TIMEOUT 60 123 | FAIL_REGULAR_EXPRESSION "monopolistic mode" 124 | REQUIRED_FILES dupsort_writemap.db-copy) 125 | endif() 126 | 127 | add_test(NAME uniq_nested COMMAND ${MDBX_OUTPUT_DIR}/mdbx_test 128 | --loglevel=notice 129 | --mode=-writemap,-nosync-safe,-lifo --progress --console=no --repeat=2 --pathname=uniq_nested.db --dont-cleanup-after basic) 130 | set_tests_properties(uniq_nested PROPERTIES 131 | TIMEOUT 1800 132 | RUN_SERIAL OFF) 133 | if(MDBX_BUILD_TOOLS) 134 | add_test(NAME uniq_nested_chk COMMAND ${MDBX_OUTPUT_DIR}/mdbx_chk -nvvw uniq_nested.db) 135 | set_tests_properties(uniq_nested_chk PROPERTIES 136 | DEPENDS uniq_nested 137 | TIMEOUT 60 138 | FAIL_REGULAR_EXPRESSION "cooperative mode" 139 | REQUIRED_FILES uniq_nested.db) 140 | add_test(NAME uniq_nested_chk_copy COMMAND ${MDBX_OUTPUT_DIR}/mdbx_chk -nvv uniq_nested.db-copy) 141 | set_tests_properties(uniq_nested_chk_copy PROPERTIES 142 | DEPENDS uniq_nested 143 | TIMEOUT 60 144 | FAIL_REGULAR_EXPRESSION "cooperative mode" 145 | REQUIRED_FILES uniq_nested.db-copy) 146 | endif() 147 | 148 | endif() 149 | -------------------------------------------------------------------------------- /test/append.c++: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2023 Leonid Yuriev 3 | * and other libmdbx authors: please see AUTHORS file. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | 15 | #include "test.h++" 16 | 17 | class testcase_append : public testcase { 18 | public: 19 | testcase_append(const actor_config &config, const mdbx_pid_t pid) 20 | : testcase(config, pid) {} 21 | bool run() override; 22 | 23 | static bool review_params(actor_params ¶ms) { 24 | if (!testcase::review_params(params)) 25 | return false; 26 | const bool ordered = !flipcoin_x3(); 27 | log_notice("the '%s' key-generation mode is selected", 28 | ordered ? "ordered/linear" : "unordered/non-linear"); 29 | if (ordered && !params.make_keygen_linear()) 30 | return false; 31 | return true; 32 | } 33 | }; 34 | REGISTER_TESTCASE(append); 35 | 36 | bool testcase_append::run() { 37 | const bool reverse = flipcoin(); 38 | const char *const caption = reverse ? "ahead" : "append"; 39 | log_notice("the '%s' scenario is selected", caption); 40 | 41 | int err = db_open__begin__table_create_open_clean(dbi); 42 | if (unlikely(err != MDBX_SUCCESS)) { 43 | log_notice("%s: bailout-prepare due '%s'", caption, mdbx_strerror(err)); 44 | return true; 45 | } 46 | 47 | cursor_open(dbi); 48 | keyvalue_maker.setup(config.params, config.actor_id, 0 /* thread_number */); 49 | /* LY: тест наполнения таблиц в append-режиме, 50 | * при котором записи добавляются строго в конец (в порядке сортировки) */ 51 | const MDBX_put_flags_t flags = 52 | reverse 53 | ? ((config.params.table_flags & MDBX_DUPSORT) ? MDBX_UPSERT 54 | : MDBX_NOOVERWRITE) 55 | : ((config.params.table_flags & MDBX_DUPSORT) 56 | ? (flipcoin() ? MDBX_APPEND | MDBX_APPENDDUP : MDBX_APPENDDUP) 57 | : MDBX_APPEND); 58 | 59 | key = keygen::alloc(config.params.keylen_max); 60 | data = keygen::alloc(config.params.datalen_max); 61 | 62 | simple_checksum inserted_checksum; 63 | uint64_t inserted_number = 0; 64 | uint64_t serial_count = 0; 65 | if (reverse) 66 | keyvalue_maker.seek2end(serial_count); 67 | 68 | unsigned txn_nops = 0; 69 | uint64_t committed_inserted_number = inserted_number; 70 | simple_checksum committed_inserted_checksum = inserted_checksum; 71 | while (should_continue()) { 72 | const keygen::serial_t serial = serial_count; 73 | const bool turn_key = (config.params.table_flags & MDBX_DUPSORT) == 0 || 74 | flipcoin_n(config.params.keygen.split); 75 | if (turn_key 76 | ? !keyvalue_maker.increment_key_part(serial_count, reverse ? -1 : 1) 77 | : !keyvalue_maker.increment(serial_count, reverse ? -1 : 1)) { 78 | // дошли до границы пространства ключей 79 | break; 80 | } 81 | 82 | log_trace("%s: insert-a %" PRIu64, caption, serial); 83 | generate_pair(serial); 84 | // keygen::log_pair(logging::verbose, "append.", key, data); 85 | 86 | bool expect_key_mismatch = false; 87 | if (flags & (MDBX_APPEND | MDBX_APPENDDUP)) { 88 | MDBX_val ge_key = key->value; 89 | MDBX_val ge_data = data->value; 90 | err = mdbx_get_equal_or_great(txn_guard.get(), dbi, &ge_key, &ge_data); 91 | 92 | if (err == MDBX_SUCCESS /* exact match */) { 93 | expect_key_mismatch = true; 94 | assert(inserted_number > 0); 95 | assert(mdbx_cmp(txn_guard.get(), dbi, &key->value, &ge_key) == 0); 96 | assert((config.params.table_flags & MDBX_DUPSORT) == 0 || 97 | mdbx_dcmp(txn_guard.get(), dbi, &data->value, &ge_data) == 0); 98 | assert(inserted_number > 0); 99 | } else if (err == MDBX_RESULT_TRUE /* have key-value pair great than */) { 100 | assert(mdbx_cmp(txn_guard.get(), dbi, &key->value, &ge_key) < 0 || 101 | ((config.params.table_flags & MDBX_DUPSORT) && 102 | mdbx_cmp(txn_guard.get(), dbi, &key->value, &ge_key) == 0 && 103 | mdbx_dcmp(txn_guard.get(), dbi, &data->value, &ge_data) < 0)); 104 | switch (int(flags)) { 105 | default: 106 | abort(); 107 | #if CONSTEXPR_ENUM_FLAGS_OPERATIONS 108 | case MDBX_APPEND | MDBX_APPENDDUP: 109 | #else 110 | case int(MDBX_APPEND) | int(MDBX_APPENDDUP): 111 | #endif 112 | assert((config.params.table_flags & MDBX_DUPSORT) != 0); 113 | __fallthrough; 114 | // fall through 115 | case MDBX_APPEND: 116 | expect_key_mismatch = true; 117 | break; 118 | case MDBX_APPENDDUP: 119 | assert((config.params.table_flags & MDBX_DUPSORT) != 0); 120 | expect_key_mismatch = 121 | mdbx_cmp(txn_guard.get(), dbi, &key->value, &ge_key) == 0; 122 | break; 123 | } 124 | } else if (err == MDBX_NOTFOUND /* all pair are less than */) { 125 | switch (int(flags)) { 126 | default: 127 | abort(); 128 | case MDBX_APPENDDUP: 129 | #if CONSTEXPR_ENUM_FLAGS_OPERATIONS 130 | case MDBX_APPEND | MDBX_APPENDDUP: 131 | #else 132 | case int(MDBX_APPEND) | int(MDBX_APPENDDUP): 133 | #endif 134 | assert((config.params.table_flags & MDBX_DUPSORT) != 0); 135 | __fallthrough; 136 | // fall through 137 | case MDBX_APPEND: 138 | expect_key_mismatch = false; 139 | break; 140 | } 141 | } else 142 | failure_perror("mdbx_get_equal_or_great()", err); 143 | } 144 | 145 | err = mdbx_cursor_put(cursor_guard.get(), &key->value, &data->value, flags); 146 | if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) { 147 | log_notice("%s: bailout-insert due '%s'", caption, mdbx_strerror(err)); 148 | txn_end(true); 149 | inserted_number = committed_inserted_number; 150 | inserted_checksum = committed_inserted_checksum; 151 | break; 152 | } 153 | 154 | if (!expect_key_mismatch) { 155 | if (unlikely(err != MDBX_SUCCESS)) 156 | failure_perror("mdbx_cursor_put(append)", err); 157 | ++inserted_number; 158 | inserted_checksum.push((uint32_t)inserted_number, key->value); 159 | inserted_checksum.push(10639, data->value); 160 | 161 | if (config.params.speculum) { 162 | Item item(iov2dataview(key), iov2dataview(data)); 163 | const auto insertion_result = speculum.insert(item); 164 | if (!insertion_result.second) { 165 | char dump_key[32], dump_value[32]; 166 | log_error( 167 | "speculum.append: unexpected %s {%s, %s}", "MDBX_SUCCESS", 168 | mdbx_dump_val(&key->value, dump_key, sizeof(dump_key)), 169 | mdbx_dump_val(&data->value, dump_value, sizeof(dump_value))); 170 | return false; 171 | } 172 | } 173 | } else if (unlikely(err != MDBX_EKEYMISMATCH)) 174 | failure_perror("mdbx_cursor_put(append) != MDBX_EKEYMISMATCH", err); 175 | 176 | if (++txn_nops >= config.params.batch_write) { 177 | err = breakable_restart(); 178 | if (unlikely(err != MDBX_SUCCESS)) { 179 | log_notice("%s: bailout-commit due '%s'", caption, mdbx_strerror(err)); 180 | inserted_number = committed_inserted_number; 181 | inserted_checksum = committed_inserted_checksum; 182 | break; 183 | } 184 | committed_inserted_number = inserted_number; 185 | committed_inserted_checksum = inserted_checksum; 186 | txn_nops = 0; 187 | if (!speculum_verify()) { 188 | log_notice("append: bailout breakable_restart"); 189 | return false; 190 | } 191 | } 192 | 193 | report(1); 194 | } 195 | 196 | if (txn_guard) { 197 | err = breakable_commit(); 198 | if (unlikely(err != MDBX_SUCCESS)) { 199 | log_notice("%s: bailout-commit due '%s'", caption, mdbx_strerror(err)); 200 | inserted_number = committed_inserted_number; 201 | inserted_checksum = committed_inserted_checksum; 202 | } 203 | } 204 | //---------------------------------------------------------------------------- 205 | txn_begin(true); 206 | if (!speculum_verify()) { 207 | log_notice("append: bailout verify"); 208 | return false; 209 | } 210 | cursor_renew(); 211 | 212 | MDBX_val check_key, check_data; 213 | err = mdbx_cursor_get(cursor_guard.get(), &check_key, &check_data, 214 | reverse ? MDBX_LAST : MDBX_FIRST); 215 | if (likely(inserted_number)) { 216 | if (unlikely(err != MDBX_SUCCESS)) 217 | failure_perror("mdbx_cursor_get(MDBX_FIRST)", err); 218 | } 219 | 220 | simple_checksum read_checksum; 221 | uint64_t read_count = 0; 222 | while (err == MDBX_SUCCESS) { 223 | ++read_count; 224 | read_checksum.push((uint32_t)read_count, check_key); 225 | read_checksum.push(10639, check_data); 226 | 227 | err = mdbx_cursor_get(cursor_guard.get(), &check_key, &check_data, 228 | reverse ? MDBX_PREV : MDBX_NEXT); 229 | } 230 | 231 | if (unlikely(err != MDBX_NOTFOUND)) 232 | failure_perror("mdbx_cursor_get(MDBX_NEXT) != EOF", err); 233 | 234 | if (unlikely(read_count != inserted_number)) 235 | failure("read_count(%" PRIu64 ") != inserted_number(%" PRIu64 ")", 236 | read_count, inserted_number); 237 | 238 | if (unlikely(read_checksum.value != inserted_checksum.value) && 239 | !keyvalue_maker.is_unordered()) 240 | failure("read_checksum(0x%016" PRIu64 ") " 241 | "!= inserted_checksum(0x%016" PRIu64 ")", 242 | read_checksum.value, inserted_checksum.value); 243 | 244 | cursor_close(); 245 | txn_end(true); 246 | //---------------------------------------------------------------------------- 247 | 248 | if (dbi) { 249 | if (config.params.drop_table && !mode_readonly()) { 250 | txn_begin(false); 251 | db_table_drop(dbi); 252 | err = breakable_commit(); 253 | if (unlikely(err != MDBX_SUCCESS)) { 254 | log_notice("%s: bailout-clean due '%s'", caption, mdbx_strerror(err)); 255 | return true; 256 | } 257 | } else 258 | db_table_close(dbi); 259 | } 260 | return true; 261 | } 262 | -------------------------------------------------------------------------------- /test/base.h++: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2023 Leonid Yuriev 3 | * and other libmdbx authors: please see AUTHORS file. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | 15 | #pragma once 16 | 17 | #ifndef NOMINMAX 18 | #define NOMINMAX 19 | #endif 20 | 21 | /* Workaround for modern libstdc++ with CLANG < 4.x */ 22 | #if defined(__SIZEOF_INT128__) && !defined(__GLIBCXX_TYPE_INT_N_0) && \ 23 | defined(__clang__) && __clang_major__ < 4 24 | #define __GLIBCXX_BITSIZE_INT_N_0 128 25 | #define __GLIBCXX_TYPE_INT_N_0 __int128 26 | #endif /* Workaround for modern libstdc++ with CLANG < 4.x */ 27 | 28 | #if defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS) 29 | #ifndef _WIN32_WINNT 30 | #define _WIN32_WINNT 0x0601 /* Windows 7 */ 31 | #endif 32 | #ifdef _MSC_VER 33 | #ifndef _CRT_SECURE_NO_WARNINGS 34 | #define _CRT_SECURE_NO_WARNINGS 35 | #endif /* _CRT_SECURE_NO_WARNINGS */ 36 | #pragma warning(push, 1) 37 | #pragma warning(disable : 4548) /* expression before comma has no effect; \ 38 | expected expression with side - effect */ 39 | #pragma warning(disable : 4530) /* C++ exception handler used, but unwind \ 40 | semantics are not enabled. Specify /EHsc */ 41 | #pragma warning(disable : 4577) /* 'noexcept' used with no exception handling \ 42 | mode specified; termination on exception \ 43 | is not guaranteed. Specify /EHsc */ 44 | #endif /* _MSC_VER (warnings) */ 45 | 46 | /* If you wish to build your application for a previous Windows platform, 47 | * include WinSDKVer.h and set the _WIN32_WINNT macro to the platform you 48 | * wish to support before including SDKDDKVer.h. 49 | * 50 | * TODO: #define _WIN32_WINNT WIN32_MUSTDIE */ 51 | #include 52 | #endif /* WINDOWS */ 53 | 54 | #ifdef __APPLE__ 55 | #define _DARWIN_C_SOURCE 56 | #endif 57 | 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | 65 | #if defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS) 66 | #include 67 | #else 68 | #include 69 | #include 70 | #include 71 | #include 72 | #include 73 | #include 74 | #endif 75 | 76 | #ifdef _BSD_SOURCE 77 | #include 78 | #endif 79 | 80 | #include 81 | #include 82 | #include // for PRId64, PRIu64 83 | #include 84 | #include 85 | #include 86 | #include 87 | #include 88 | #include 89 | #include 90 | #include 91 | #include 92 | #include 93 | #include 94 | 95 | #define MDBX_INTERNAL_FUNC 96 | #define MDBX_INTERNAL_VAR extern 97 | #define xMDBX_TOOLS /* Avoid using internal eASSERT() */ 98 | #include "../mdbx.h++" 99 | #include "../src/base.h" 100 | #include "../src/osal.h" 101 | 102 | #if !defined(__thread) && (defined(_MSC_VER) || defined(__DMC__)) 103 | #define __thread __declspec(thread) 104 | #endif /* __thread */ 105 | 106 | #include "../src/options.h" 107 | 108 | #ifdef _MSC_VER 109 | #pragma warning(pop) 110 | #pragma warning(disable : 4201) /* nonstandard extension used : \ 111 | nameless struct / union */ 112 | #pragma warning(disable : 4127) /* conditional expression is constant */ 113 | #if _MSC_VER < 1900 114 | #pragma warning(disable : 4510) /* default constructor could \ 115 | not be generated */ 116 | #pragma warning(disable : 4512) /* assignment operator could \ 117 | not be generated */ 118 | #pragma warning(disable : 4610) /* user-defined constructor required */ 119 | #ifndef snprintf 120 | #define snprintf(buffer, buffer_size, format, ...) \ 121 | _snprintf_s(buffer, buffer_size, _TRUNCATE, format, __VA_ARGS__) 122 | #endif 123 | #ifndef vsnprintf 124 | #define vsnprintf(buffer, buffer_size, format, args) \ 125 | _vsnprintf_s(buffer, buffer_size, _TRUNCATE, format, args) 126 | #endif 127 | #pragma warning(disable : 4996) /* 'vsnprintf': This function or variable \ 128 | may be unsafe */ 129 | #endif 130 | #endif /* _MSC_VER */ 131 | -------------------------------------------------------------------------------- /test/cases.c++: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2023 Leonid Yuriev 3 | * and other libmdbx authors: please see AUTHORS file. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | 15 | #include "test.h++" 16 | 17 | registry *registry::instance() { 18 | static registry *singleton; 19 | if (!singleton) 20 | singleton = new registry(); 21 | return singleton; 22 | } 23 | 24 | bool registry::add(const record *item) { 25 | auto const singleton = instance(); 26 | assert(singleton->name2id.count(std::string(item->name)) == 0); 27 | assert(singleton->id2record.count(item->id) == 0); 28 | if (singleton->name2id.count(std::string(item->name)) + 29 | singleton->id2record.count(item->id) == 30 | 0) { 31 | singleton->name2id[std::string(item->name)] = item; 32 | singleton->id2record[item->id] = item; 33 | return true; 34 | } 35 | return false; 36 | } 37 | 38 | testcase *registry::create_actor(const actor_config &config, 39 | const mdbx_pid_t pid) { 40 | return instance()->id2record.at(config.testcase)->constructor(config, pid); 41 | } 42 | 43 | bool registry::review_actor_params(const actor_testcase id, 44 | actor_params ¶ms) { 45 | return instance()->id2record.at(id)->review_params(params); 46 | } 47 | 48 | //----------------------------------------------------------------------------- 49 | 50 | void configure_actor(unsigned &last_space_id, const actor_testcase testcase, 51 | const char *space_id_cstr, actor_params params) { 52 | unsigned wait4id = 0; 53 | if (params.waitfor_nops) { 54 | for (auto i = global::actors.rbegin(); i != global::actors.rend(); ++i) { 55 | if (i->is_waitable(params.waitfor_nops)) { 56 | if (i->signal_nops && i->signal_nops != params.waitfor_nops) 57 | failure("Previous waitable actor (id=%u) already linked on %u-ops\n", 58 | i->actor_id, i->signal_nops); 59 | wait4id = i->actor_id; 60 | i->signal_nops = params.waitfor_nops; 61 | break; 62 | } 63 | } 64 | if (!wait4id) 65 | failure("No previous waitable actor for %u-ops\n", params.waitfor_nops); 66 | } 67 | 68 | unsigned long space_id = 0; 69 | if (!space_id_cstr || strcmp(space_id_cstr, "auto") == 0) 70 | space_id = last_space_id + 1; 71 | else { 72 | char *end = nullptr; 73 | errno = 0; 74 | space_id = strtoul(space_id_cstr, &end, 0); 75 | if (errno) 76 | failure_perror("Expects an integer value for space-id\n", errno); 77 | if (end && *end) 78 | failure("The '%s' is unexpected for space-id\n", end); 79 | } 80 | 81 | if (!registry::review_actor_params(testcase, params)) 82 | failure("Actor config-review failed for space-id %lu\n", space_id); 83 | 84 | if (space_id > ACTOR_ID_MAX) 85 | failure("Invalid space-id %lu\n", space_id); 86 | last_space_id = unsigned(space_id); 87 | 88 | log_trace("configure_actor: space %lu for %s", space_id, 89 | testcase2str(testcase)); 90 | global::actors.emplace_back( 91 | actor_config(testcase, params, unsigned(space_id), wait4id)); 92 | global::databases.insert(params.pathname_db); 93 | } 94 | 95 | void testcase_setup(const char *casename, const actor_params ¶ms, 96 | unsigned &last_space_id) { 97 | if (strcmp(casename, "basic") == 0) { 98 | log_notice(">>> testcase_setup(%s)", casename); 99 | configure_actor(last_space_id, ac_nested, nullptr, params); 100 | configure_actor(last_space_id, ac_hill, nullptr, params); 101 | configure_actor(last_space_id, ac_ttl, nullptr, params); 102 | configure_actor(last_space_id, ac_copy, nullptr, params); 103 | configure_actor(last_space_id, ac_append, nullptr, params); 104 | configure_actor(last_space_id, ac_jitter, nullptr, params); 105 | configure_actor(last_space_id, ac_try, nullptr, params); 106 | configure_actor(last_space_id, ac_jitter, nullptr, params); 107 | configure_actor(last_space_id, ac_try, nullptr, params); 108 | log_notice("<<< testcase_setup(%s): done", casename); 109 | } else { 110 | failure("unknown testcase `%s`", casename); 111 | } 112 | } 113 | 114 | void keycase_setup(const char *casename, actor_params ¶ms) { 115 | if (strcmp(casename, "random") == 0 || strcmp(casename, "prng") == 0) { 116 | log_notice(">>> keycase_setup(%s)", casename); 117 | params.keygen.keycase = kc_random; 118 | // TODO 119 | log_notice("<<< keycase_setup(%s): done", casename); 120 | } else if (strcmp(casename, "dashes") == 0 || 121 | strcmp(casename, "aside") == 0) { 122 | log_notice(">>> keycase_setup(%s)", casename); 123 | params.keygen.keycase = kc_dashes; 124 | // TODO 125 | log_notice("<<< keycase_setup(%s): done", casename); 126 | } else if (strcmp(casename, "custom") == 0) { 127 | log_notice("=== keycase_setup(%s): skip", casename); 128 | params.keygen.keycase = kc_custom; 129 | } else { 130 | failure("unknown keycase `%s`", casename); 131 | } 132 | } 133 | 134 | /* TODO */ 135 | -------------------------------------------------------------------------------- /test/chrono.c++: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2023 Leonid Yuriev 3 | * and other libmdbx authors: please see AUTHORS file. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | 15 | #include "test.h++" 16 | 17 | namespace chrono { 18 | 19 | #ifndef NSEC_PER_SEC 20 | #define NSEC_PER_SEC 1000000000u 21 | #endif /* NSEC_PER_SEC */ 22 | 23 | uint32_t ns2fractional(uint32_t ns) { 24 | assert(ns < NSEC_PER_SEC); 25 | /* LY: здесь и далее используется "длинное деление", которое 26 | * для ясности кода оставлено как есть (без ручной оптимизации). Так как 27 | * GCC, Clang и даже MSVC сами давно умеют конвертировать деление на 28 | * константу в быструю reciprocal-форму. */ 29 | return uint32_t((uint64_t(ns) << 32) / NSEC_PER_SEC); 30 | } 31 | 32 | uint32_t fractional2ns(uint32_t fractional) { 33 | return uint32_t((fractional * uint64_t(NSEC_PER_SEC)) >> 32); 34 | } 35 | 36 | #ifndef USEC_PER_SEC 37 | #define USEC_PER_SEC 1000000u 38 | #endif /* USEC_PER_SEC */ 39 | uint32_t us2fractional(uint32_t us) { 40 | assert(us < USEC_PER_SEC); 41 | return uint32_t((uint64_t(us) << 32) / USEC_PER_SEC); 42 | } 43 | 44 | uint32_t fractional2us(uint32_t fractional) { 45 | return uint32_t((fractional * uint64_t(USEC_PER_SEC)) >> 32); 46 | } 47 | 48 | #ifndef MSEC_PER_SEC 49 | #define MSEC_PER_SEC 1000u 50 | #endif /* MSEC_PER_SEC */ 51 | uint32_t ms2fractional(uint32_t ms) { 52 | assert(ms < MSEC_PER_SEC); 53 | return uint32_t((uint64_t(ms) << 32) / MSEC_PER_SEC); 54 | } 55 | 56 | uint32_t fractional2ms(uint32_t fractional) { 57 | return uint32_t((fractional * uint64_t(MSEC_PER_SEC)) >> 32); 58 | } 59 | 60 | time from_ns(uint64_t ns) { 61 | time result; 62 | result.fixedpoint = 63 | ((ns / NSEC_PER_SEC) << 32) | ns2fractional(uint32_t(ns % NSEC_PER_SEC)); 64 | return result; 65 | } 66 | 67 | time from_us(uint64_t us) { 68 | time result; 69 | result.fixedpoint = 70 | ((us / USEC_PER_SEC) << 32) | us2fractional(uint32_t(us % USEC_PER_SEC)); 71 | return result; 72 | } 73 | 74 | time from_ms(uint64_t ms) { 75 | time result; 76 | result.fixedpoint = 77 | ((ms / MSEC_PER_SEC) << 32) | ms2fractional(uint32_t(ms % MSEC_PER_SEC)); 78 | return result; 79 | } 80 | 81 | #if __GNUC_PREREQ(8, 0) && \ 82 | (defined(__MINGW__) || defined(__MINGW32__) || defined(__MINGW64__)) 83 | #pragma GCC diagnostic push 84 | #pragma GCC diagnostic ignored "-Wcast-function-type" 85 | #endif /* GCC/MINGW */ 86 | 87 | time now_realtime() { 88 | #if defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS) 89 | static void(WINAPI * query_time)(LPFILETIME); 90 | if (unlikely(!query_time)) { 91 | HMODULE hModule = GetModuleHandle(TEXT("kernel32.dll")); 92 | if (hModule) 93 | query_time = (void(WINAPI *)(LPFILETIME))GetProcAddress( 94 | hModule, "GetSystemTimePreciseAsFileTime"); 95 | if (!query_time) 96 | query_time = GetSystemTimeAsFileTime; 97 | } 98 | 99 | FILETIME filetime; 100 | query_time(&filetime); 101 | uint64_t ns100 = 102 | (uint64_t)filetime.dwHighDateTime << 32 | filetime.dwLowDateTime; 103 | return from_ns((ns100 - UINT64_C(116444736000000000)) * 100u); 104 | #else 105 | struct timespec ts; 106 | if (unlikely(clock_gettime(CLOCK_REALTIME, &ts))) 107 | failure_perror("clock_gettime(CLOCK_REALTIME", errno); 108 | 109 | return from_timespec(ts); 110 | #endif 111 | } 112 | 113 | time now_monotonic() { 114 | #if defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS) 115 | static uint64_t reciprocal; 116 | static LARGE_INTEGER Frequency; 117 | if (reciprocal == 0) { 118 | if (!QueryPerformanceFrequency(&Frequency)) 119 | failure_perror("QueryPerformanceFrequency()", GetLastError()); 120 | reciprocal = (((UINT64_C(1) << 48) + Frequency.QuadPart / 2 + 1) / 121 | Frequency.QuadPart); 122 | assert(reciprocal); 123 | } 124 | 125 | LARGE_INTEGER Counter; 126 | if (!QueryPerformanceCounter(&Counter)) 127 | failure_perror("QueryPerformanceCounter()", GetLastError()); 128 | 129 | time result; 130 | result.fixedpoint = (Counter.QuadPart / Frequency.QuadPart) << 32; 131 | uint64_t mod = Counter.QuadPart % Frequency.QuadPart; 132 | result.fixedpoint += (mod * reciprocal) >> 16; 133 | return result; 134 | #else 135 | struct timespec ts; 136 | if (unlikely(clock_gettime(CLOCK_MONOTONIC, &ts))) 137 | failure_perror("clock_gettime(CLOCK_MONOTONIC)", errno); 138 | 139 | return from_timespec(ts); 140 | #endif 141 | } 142 | 143 | #if __GNUC_PREREQ(8, 0) && \ 144 | (defined(__MINGW__) || defined(__MINGW32__) || defined(__MINGW64__)) 145 | #pragma GCC diagnostic pop 146 | #endif /* GCC/MINGW */ 147 | 148 | } /* namespace chrono */ 149 | -------------------------------------------------------------------------------- /test/chrono.h++: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2023 Leonid Yuriev 3 | * and other libmdbx authors: please see AUTHORS file. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | 15 | #pragma once 16 | 17 | #include "base.h++" 18 | #include "utils.h++" 19 | 20 | namespace chrono { 21 | 22 | #pragma pack(push, 4) 23 | 24 | typedef union time { 25 | uint64_t fixedpoint; 26 | __anonymous_struct_extension__ struct { 27 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 28 | uint32_t fractional; 29 | union { 30 | uint32_t utc; 31 | uint32_t integer; 32 | }; 33 | #else 34 | union { 35 | uint32_t utc; 36 | uint32_t integer; 37 | }; 38 | uint32_t fractional; 39 | #endif 40 | }; 41 | 42 | void reset() { fixedpoint = 0; } 43 | uint32_t seconds() const { return utc; } 44 | } time; 45 | 46 | #pragma pack(pop) 47 | 48 | uint32_t ns2fractional(uint32_t); 49 | uint32_t fractional2ns(uint32_t); 50 | uint32_t us2fractional(uint32_t); 51 | uint32_t fractional2us(uint32_t); 52 | uint32_t ms2fractional(uint32_t); 53 | uint32_t fractional2ms(uint32_t); 54 | 55 | time from_ns(uint64_t us); 56 | time from_us(uint64_t ns); 57 | time from_ms(uint64_t ms); 58 | 59 | inline time from_seconds(uint64_t seconds) { 60 | assert(seconds < UINT32_MAX); 61 | time result; 62 | result.fixedpoint = seconds << 32; 63 | return result; 64 | } 65 | 66 | inline time from_utc(time_t utc) { 67 | assert(utc >= 0); 68 | return from_seconds((uint64_t)utc); 69 | } 70 | 71 | inline time infinite() { 72 | time result; 73 | result.fixedpoint = UINT64_MAX; 74 | return result; 75 | } 76 | 77 | #if defined(HAVE_TIMESPEC_TV_NSEC) || defined(__timespec_defined) || \ 78 | defined(CLOCK_REALTIME) 79 | inline time from_timespec(const struct timespec &ts) { 80 | time result; 81 | result.fixedpoint = 82 | ((uint64_t)ts.tv_sec << 32) | ns2fractional((uint32_t)ts.tv_nsec); 83 | return result; 84 | } 85 | #endif /* HAVE_TIMESPEC_TV_NSEC */ 86 | 87 | #if defined(HAVE_TIMEVAL_TV_USEC) || defined(_STRUCT_TIMEVAL) 88 | inline time from_timeval(const struct timeval &tv) { 89 | time result; 90 | result.fixedpoint = 91 | ((uint64_t)tv.tv_sec << 32) | us2fractional((uint32_t)tv.tv_usec); 92 | return result; 93 | } 94 | #endif /* HAVE_TIMEVAL_TV_USEC */ 95 | 96 | time now_realtime(); 97 | time now_monotonic(); 98 | 99 | } /* namespace chrono */ 100 | -------------------------------------------------------------------------------- /test/copy.c++: -------------------------------------------------------------------------------- 1 | #include "test.h++" 2 | 3 | class testcase_copy : public testcase { 4 | const std::string copy_pathname; 5 | void copy_db(const bool with_compaction); 6 | 7 | public: 8 | testcase_copy(const actor_config &config, const mdbx_pid_t pid) 9 | : testcase(config, pid), 10 | copy_pathname(config.params.pathname_db + "-copy") {} 11 | bool run() override; 12 | }; 13 | REGISTER_TESTCASE(copy); 14 | 15 | void testcase_copy::copy_db(const bool with_compaction) { 16 | int err = mdbx_env_delete(copy_pathname.c_str(), MDBX_ENV_JUST_DELETE); 17 | if (err != MDBX_SUCCESS && err != MDBX_RESULT_TRUE) 18 | failure_perror("osal_removefile()", err); 19 | 20 | err = mdbx_env_copy(db_guard.get(), copy_pathname.c_str(), 21 | with_compaction ? MDBX_CP_COMPACT : MDBX_CP_DEFAULTS); 22 | if (unlikely(err != MDBX_SUCCESS)) 23 | failure_perror(with_compaction ? "mdbx_env_copy(MDBX_CP_COMPACT)" 24 | : "mdbx_env_copy(MDBX_CP_ASIS)", 25 | err); 26 | } 27 | 28 | bool testcase_copy::run() { 29 | jitter_delay(); 30 | db_open(); 31 | assert(!txn_guard); 32 | const bool order = flipcoin(); 33 | jitter_delay(); 34 | copy_db(order); 35 | jitter_delay(); 36 | copy_db(!order); 37 | return true; 38 | } 39 | -------------------------------------------------------------------------------- /test/dead.c++: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2023 Leonid Yuriev 3 | * and other libmdbx authors: please see AUTHORS file. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | 15 | #include "test.h++" 16 | 17 | class testcase_deadread : public testcase { 18 | public: 19 | testcase_deadread(const actor_config &config, const mdbx_pid_t pid) 20 | : testcase(config, pid) {} 21 | bool run() override; 22 | }; 23 | REGISTER_TESTCASE(deadread); 24 | 25 | bool testcase_deadread::run() { 26 | db_open(); 27 | txn_begin(true); 28 | cursor_guard.reset(); 29 | txn_guard.reset(); 30 | db_guard.reset(); 31 | return true; 32 | } 33 | 34 | //----------------------------------------------------------------------------- 35 | 36 | class testcase_deadwrite : public testcase { 37 | public: 38 | testcase_deadwrite(const actor_config &config, const mdbx_pid_t pid) 39 | : testcase(config, pid) {} 40 | bool run() override; 41 | }; 42 | 43 | REGISTER_TESTCASE(deadwrite); 44 | 45 | bool testcase_deadwrite::run() { 46 | db_open(); 47 | txn_begin(false); 48 | cursor_guard.reset(); 49 | txn_guard.reset(); 50 | db_guard.reset(); 51 | return true; 52 | } 53 | -------------------------------------------------------------------------------- /test/dump-load.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "------------------------------------------------------------------------------" 4 | 5 | if [ -z "$1" ]; then 6 | echo "No mdbx-db pathname given"; 7 | exit 2 8 | elif [ ! -e "$1" ]; then 9 | echo "The mdbx-db '$1' don't exists"; 10 | exit 2 11 | else 12 | echo ">>>>>>>>>> $1" 13 | RECO="$1.recovered" 14 | rm -f dump1.txt dump2.txt "$RECO" 15 | if ./mdbx_chk "$1"; then 16 | echo ">>>>>>>>>> SOURCE VALID" 17 | (./mdbx_dump -a "$1" > dump1.txt && \ 18 | ./mdbx_load -nf dump1.txt "$RECO" && \ 19 | ./mdbx_chk "$RECO" && \ 20 | echo ">>>>>>>>>> DUMP/LOAD/CHK OK") || (echo ">>>>>>>>>> DUMP/LOAD/CHK FAILED"; exit 1) 21 | REMOVE_RECO=1 22 | elif ./mdbx_chk -i "$1"; then 23 | echo ">>>>>>>>>> SOURCE HAS WRONG-ORDER, TRY RECOVERY" 24 | (./mdbx_dump -a "$1" > dump1.txt && \ 25 | ./mdbx_load -anf dump1.txt "$RECO" && \ 26 | ./mdbx_chk -i "$RECO" && \ 27 | echo ">>>>>>>>>> DUMP/LOAD/CHK OK") || (echo ">>>>>>>>>> DUMP/LOAD/CHK FAILED"; exit 1) 28 | REMOVE_RECO=0 29 | else 30 | echo ">>>>>>>>>> SOURCE CORRUPTED, TRY RECOVERY" 31 | (./mdbx_dump -ar "$1" > dump1.txt && \ 32 | ./mdbx_load -ranf dump1.txt "$RECO" && \ 33 | ./mdbx_chk -i "$RECO" && \ 34 | echo ">>>>>>>>>> DUMP/LOAD/CHK OK") || (echo ">>>>>>>>>> DUMP/LOAD/CHK FAILED"; exit 1) 35 | REMOVE_RECO=0 36 | fi 37 | ./mdbx_dump -a "$RECO" > dump2.txt && diff -u dump1.txt dump2.txt && \ 38 | rm -f dump1.txt dump2.txt && [ $REMOVE_RECO -ne 0 ] && rm -f "$RECO" 39 | exit 0 40 | fi 41 | -------------------------------------------------------------------------------- /test/jitter.c++: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2023 Leonid Yuriev 3 | * and other libmdbx authors: please see AUTHORS file. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | 15 | #include "test.h++" 16 | 17 | class testcase_jitter : public testcase { 18 | protected: 19 | void check_dbi_error(int expect, const char *stage); 20 | 21 | public: 22 | testcase_jitter(const actor_config &config, const mdbx_pid_t pid) 23 | : testcase(config, pid) {} 24 | bool run() override; 25 | }; 26 | REGISTER_TESTCASE(jitter); 27 | 28 | void testcase_jitter::check_dbi_error(int expect, const char *stage) { 29 | MDBX_stat stat; 30 | int err = mdbx_dbi_stat(txn_guard.get(), dbi, &stat, sizeof(stat)); 31 | if (err != expect) 32 | failure("unexpected result for %s dbi-handle: expect %d, got %d", stage, 33 | expect, err); 34 | } 35 | 36 | bool testcase_jitter::run() { 37 | int err; 38 | size_t upper_limit = config.params.size_upper; 39 | if (upper_limit < 1) 40 | upper_limit = config.params.size_now * 2; 41 | 42 | while (should_continue()) { 43 | jitter_delay(); 44 | db_open(); 45 | 46 | if (!dbi && !mode_readonly()) { 47 | // create table 48 | txn_begin(false); 49 | dbi = db_table_open(true); 50 | check_dbi_error(MDBX_SUCCESS, "created-uncommitted"); 51 | // note: here and below the 4-byte length keys and value are used 52 | // to be compatible with any Db-flags given from command line. 53 | MDBX_val k = {(void *)"k000", 4}, v = {(void *)"v001", 4}; 54 | err = mdbx_put(txn_guard.get(), dbi, &k, &v, MDBX_UPSERT); 55 | if (err != MDBX_SUCCESS) 56 | failure_perror("jitter.put-1", err); 57 | txn_end(false); 58 | 59 | // drop & re-create table, but abort txn 60 | txn_begin(false); 61 | check_dbi_error(MDBX_SUCCESS, "created-committed"); 62 | err = mdbx_drop(txn_guard.get(), dbi, true); 63 | if (unlikely(err != MDBX_SUCCESS)) 64 | failure_perror("mdbx_drop(delete=true)", err); 65 | check_dbi_error(MDBX_BAD_DBI, "dropped-uncommitted"); 66 | dbi = db_table_open(true); 67 | check_dbi_error(MDBX_SUCCESS, "recreated-uncommitted"); 68 | txn_end(true); 69 | 70 | // check after aborted txn 71 | txn_begin(false); 72 | v = {(void *)"v002", 4}; 73 | err = mdbx_put(txn_guard.get(), dbi, &k, &v, MDBX_UPSERT); 74 | if (err != MDBX_BAD_DBI) 75 | failure_perror("jitter.put-2", err); 76 | check_dbi_error(MDBX_BAD_DBI, "dropped-recreated-aborted"); 77 | // restore DBI 78 | dbi = db_table_open(false); 79 | check_dbi_error(MDBX_SUCCESS, "dropped-recreated-aborted+reopened"); 80 | v = {(void *)"v003", 4}; 81 | err = mdbx_put(txn_guard.get(), dbi, &k, &v, MDBX_UPSERT); 82 | if (err != MDBX_SUCCESS) 83 | failure_perror("jitter.put-3", err); 84 | txn_end(false); 85 | } 86 | 87 | if (upper_limit < 1) { 88 | MDBX_envinfo info; 89 | err = mdbx_env_info_ex(db_guard.get(), txn_guard.get(), &info, 90 | sizeof(info)); 91 | if (err) 92 | failure_perror("mdbx_env_info_ex()", err); 93 | upper_limit = (info.mi_geo.upper < INTPTR_MAX) 94 | ? (intptr_t)info.mi_geo.upper 95 | : INTPTR_MAX; 96 | } 97 | 98 | if (flipcoin()) { 99 | jitter_delay(); 100 | txn_begin(true); 101 | fetch_canary(); 102 | jitter_delay(); 103 | txn_end(flipcoin()); 104 | } 105 | 106 | const bool coin4size = flipcoin(); 107 | jitter_delay(); 108 | txn_begin(mode_readonly()); 109 | jitter_delay(); 110 | if (!mode_readonly()) { 111 | fetch_canary(); 112 | update_canary(1); 113 | if (global::config::geometry_jitter) { 114 | err = mdbx_env_set_geometry( 115 | db_guard.get(), -1, -1, 116 | coin4size ? upper_limit * 2 / 3 : upper_limit * 3 / 2, -1, -1, -1); 117 | if (err != MDBX_SUCCESS && err != MDBX_UNABLE_EXTEND_MAPSIZE && 118 | err != MDBX_MAP_FULL && err != MDBX_TOO_LARGE && err != MDBX_EPERM) 119 | failure_perror("mdbx_env_set_geometry-1", err); 120 | } 121 | } 122 | txn_end(flipcoin()); 123 | 124 | if (global::config::geometry_jitter) { 125 | err = mdbx_env_set_geometry( 126 | db_guard.get(), -1, -1, 127 | !coin4size ? upper_limit * 2 / 3 : upper_limit * 3 / 2, -1, -1, -1); 128 | if (err != MDBX_SUCCESS && err != MDBX_UNABLE_EXTEND_MAPSIZE && 129 | err != MDBX_MAP_FULL && err != MDBX_TOO_LARGE && err != MDBX_EPERM) 130 | failure_perror("mdbx_env_set_geometry-2", err); 131 | } 132 | 133 | if (flipcoin()) { 134 | jitter_delay(); 135 | txn_begin(true); 136 | jitter_delay(); 137 | txn_end(flipcoin()); 138 | } 139 | 140 | if (global::config::geometry_jitter) { 141 | jitter_delay(); 142 | err = mdbx_env_set_geometry(db_guard.get(), -1, -1, upper_limit, -1, -1, 143 | -1); 144 | if (err != MDBX_SUCCESS && err != MDBX_UNABLE_EXTEND_MAPSIZE && 145 | err != MDBX_MAP_FULL && err != MDBX_TOO_LARGE && err != MDBX_EPERM) 146 | failure_perror("mdbx_env_set_geometry-3", err); 147 | } 148 | 149 | db_close(); 150 | 151 | /* just 'align' nops with other tests with batching */ 152 | const auto batching = 153 | std::max(config.params.batch_read, config.params.batch_write); 154 | report(std::max(1u, batching / 2)); 155 | } 156 | return true; 157 | } 158 | -------------------------------------------------------------------------------- /test/keygen.h++: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2023 Leonid Yuriev 3 | * and other libmdbx authors: please see AUTHORS file. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | 15 | #pragma once 16 | 17 | #include "base.h++" 18 | #include "config.h++" 19 | #include "log.h++" 20 | #include "utils.h++" 21 | 22 | namespace keygen { 23 | 24 | /* Под "генерацией ключей" здесь понимается генерация обоих значений для 25 | * пар key-value, т.е. не только ключей, но и ассоциированных с ними данных. 26 | * 27 | * Генерацию ключей нельзя отнести к простым задачам, так как требования 28 | * примерно следующие: 29 | * - генерация разного количества уникальных ключей различной длины 30 | * в задаваемом диапазоне; 31 | * - возможность выбора как псевдо-случайного порядка ключей, 32 | * так и по некоторым специфическим законам (ограниченными упорядоченными 33 | * последовательностями, в шахматном порядке по граница диапазона и т.д.); 34 | * - возможность генерации дубликатов с задаваемым законом распределения; 35 | * - возможность генерации непересекающимися кластерами для параллельного 36 | * использования в нескольких потоках; 37 | * - использовать минимум ресурсов, как CPU, так и RAM, в том числе 38 | * включая cache pollution и ram bandwidth. 39 | * 40 | * При этом заведомо известно, что для MDBX не имеет значения: 41 | * - используемый алфавит (значения байтов); 42 | * - частотное распределение по алфавиту; 43 | * - абсолютное значение ключей или разность между отдельными значениями; 44 | * 45 | * Соответственно, в общих чертах, схема генерации следующая: 46 | * - вводится плоская одномерная "координата" serial (uint64_t); 47 | * - генерация специфических паттернов (последовательностей) 48 | * реализуется посредством соответствующих преобразований "координат", при 49 | * этом все подобные преобразования выполняются только над "координатой"; 50 | * - итоговая "координата" преобразуется в 8-байтное суррогатное значение 51 | * ключа; 52 | * - для получения ключей длиной МЕНЕЕ 8 байт суррогат может усекаться 53 | * до ненулевых байт, в том числе до нулевой длины; 54 | * - для получения ключей длиной БОЛЕЕ 8 байт суррогат дополняется 55 | * нулями или псевдослучайной последовательностью; 56 | * 57 | * Механизм генерации паттернов: 58 | * - реализованный механизм является компромиссом между скоростью/простотой 59 | * и гибкостью, необходимой для получения последовательностей, которых 60 | * будет достаточно для проверки сценариев разделения и слияния страниц 61 | * с данными внутри mdbx; 62 | * - псевдо-случайные паттерны реализуются посредством набора инъективных 63 | * отображающих функций; 64 | * - не-псевдо-случайные паттерны реализуются посредством параметризируемого 65 | * трех-этапного преобразования: 66 | * 1) смещение (сложение) по модулю; 67 | * 2) циклический сдвиг; 68 | * 3) добавление абсолютного смещения (базы); 69 | * 70 | * Также см. описание параметров генератора ключей и значений в config.h */ 71 | 72 | typedef uint64_t serial_t; 73 | 74 | enum : serial_t { 75 | serial_minwith = 8, 76 | serial_maxwith = sizeof(serial_t) * 8, 77 | serial_allones = ~(serial_t)0u 78 | }; 79 | 80 | struct result { 81 | MDBX_val value; 82 | size_t limit; 83 | union { 84 | uint8_t bytes[sizeof(uint64_t)]; 85 | uint32_t u32; 86 | uint64_t u64; 87 | }; 88 | 89 | std::string as_string() const { 90 | return std::string((const char *)value.iov_base, value.iov_len); 91 | } 92 | }; 93 | 94 | //----------------------------------------------------------------------------- 95 | 96 | struct buffer_deleter /* : public std::unary_function */ { 97 | void operator()(result *buffer) const { free(buffer); } 98 | }; 99 | 100 | typedef std::unique_ptr buffer; 101 | 102 | buffer alloc(size_t limit); 103 | 104 | class maker { 105 | config::keygen_params_pod mapping; 106 | serial_t base{0}; 107 | serial_t salt{0}; 108 | 109 | struct essentials { 110 | uint16_t minlen{0}; 111 | enum { prng_fill_flag = 1 }; 112 | uint16_t flags{0}; 113 | uint32_t maxlen{0}; 114 | } key_essentials, value_essentials; 115 | 116 | static void mk_begin(const serial_t serial, const essentials ¶ms, 117 | result &out); 118 | static void mk_continue(const serial_t serial, const essentials ¶ms, 119 | result &out); 120 | static void mk(const serial_t serial, const essentials ¶ms, result &out) { 121 | mk_begin(serial, params, out); 122 | mk_continue(serial, params, out); 123 | } 124 | 125 | public: 126 | void pair(serial_t serial, const buffer &key, buffer &value, 127 | serial_t value_age, const bool keylen_changeable); 128 | void setup(const config::actor_params_pod &actor, unsigned actor_id, 129 | unsigned thread_number); 130 | bool is_unordered() const; 131 | void seek2end(serial_t &serial) const; 132 | 133 | bool increment(serial_t &serial, int64_t delta) const; 134 | bool increment_key_part(serial_t &serial, int64_t delta, 135 | bool reset_value_part = true) const { 136 | if (reset_value_part) { 137 | serial_t value_part_bits = ((serial_t(1) << mapping.split) - 1); 138 | serial |= value_part_bits; 139 | if (delta >= 0) 140 | serial &= ~value_part_bits; 141 | } 142 | return increment(serial, int64_t(uint64_t(delta) << mapping.split)); 143 | } 144 | }; 145 | 146 | void log_pair(logging::loglevel level, const char *prefix, const buffer &key, 147 | buffer &value); 148 | 149 | } /* namespace keygen */ 150 | -------------------------------------------------------------------------------- /test/log.c++: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2023 Leonid Yuriev 3 | * and other libmdbx authors: please see AUTHORS file. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | 15 | #include "test.h++" 16 | 17 | static void fflushall() { fflush(nullptr); } 18 | 19 | void failure(const char *fmt, ...) { 20 | va_list ap; 21 | va_start(ap, fmt); 22 | fflushall(); 23 | logging::output_nocheckloglevel_ap(logging::failure, fmt, ap); 24 | va_end(ap); 25 | fflushall(); 26 | exit(EXIT_FAILURE); 27 | } 28 | 29 | const char *test_strerror(int errnum) { 30 | static __thread char buf[1024]; 31 | return mdbx_strerror_r(errnum, buf, sizeof(buf)); 32 | } 33 | 34 | MDBX_NORETURN void failure_perror(const char *what, int errnum) { 35 | failure("%s failed: %s (%d)\n", what, test_strerror(errnum), errnum); 36 | } 37 | 38 | //----------------------------------------------------------------------------- 39 | 40 | static void mdbx_logger(MDBX_log_level_t priority, const char *function, 41 | int line, const char *fmt, 42 | va_list args) MDBX_CXX17_NOEXCEPT { 43 | if (function) { 44 | if (priority == MDBX_LOG_FATAL) 45 | log_error("mdbx: fatal failure: %s, %d", function, line); 46 | logging::output_nocheckloglevel( 47 | logging::loglevel(priority), 48 | strncmp(function, "mdbx_", 5) == 0 ? "%s: " : "mdbx %s: ", function); 49 | logging::feed_ap(fmt, args); 50 | } else 51 | logging::feed_ap(fmt, args); 52 | } 53 | 54 | namespace logging { 55 | 56 | static std::string prefix; 57 | static std::string suffix; 58 | static loglevel level; 59 | static FILE *last; 60 | 61 | void setlevel(loglevel priority) { 62 | level = priority; 63 | int rc = mdbx_setup_debug(MDBX_log_level_t(priority), 64 | MDBX_DBG_ASSERT | MDBX_DBG_AUDIT | MDBX_DBG_JITTER | 65 | MDBX_DBG_DUMP, 66 | mdbx_logger); 67 | log_trace("set mdbx debug-opts: 0x%02x", rc); 68 | } 69 | 70 | void setup(loglevel priority, const std::string &_prefix) { 71 | setlevel(priority); 72 | prefix = _prefix; 73 | } 74 | 75 | void setup(const std::string &_prefix) { prefix = _prefix; } 76 | 77 | const char *level2str(const loglevel alevel) { 78 | switch (alevel) { 79 | default: 80 | return "invalid/unknown"; 81 | case extra: 82 | return "extra"; 83 | case trace: 84 | return "trace"; 85 | case debug: 86 | return "debug"; 87 | case verbose: 88 | return "verbose"; 89 | case notice: 90 | return "notice"; 91 | case warning: 92 | return "warning"; 93 | case error: 94 | return "error"; 95 | case failure: 96 | return "failure"; 97 | } 98 | } 99 | 100 | bool output(const loglevel priority, const char *format, ...) { 101 | if (lower(priority, level)) 102 | return false; 103 | 104 | va_list ap; 105 | va_start(ap, format); 106 | output_nocheckloglevel_ap(priority, format, ap); 107 | va_end(ap); 108 | return true; 109 | } 110 | 111 | void output_nocheckloglevel_ap(const logging::loglevel priority, 112 | const char *format, va_list ap) { 113 | if (last) { 114 | putc('\n', last); 115 | fflush(last); 116 | if (last == stderr) { 117 | putc('\n', stdout); 118 | fflush(stdout); 119 | } 120 | last = nullptr; 121 | } 122 | 123 | chrono::time now = chrono::now_realtime(); 124 | struct tm tm; 125 | #ifdef _MSC_VER 126 | int rc = _localtime32_s(&tm, (const __time32_t *)&now.utc); 127 | #elif defined(_WIN32) || defined(_WIN64) 128 | const time_t time_proxy = now.utc; 129 | int rc = localtime_s(&tm, &time_proxy); 130 | #else 131 | time_t time = now.utc; 132 | int rc = localtime_r(&time, &tm) ? MDBX_SUCCESS : errno; 133 | #endif 134 | if (rc != MDBX_SUCCESS) 135 | failure_perror("localtime_r()", rc); 136 | 137 | last = stdout; 138 | fprintf(last, 139 | "[ %02d%02d%02d-%02d:%02d:%02d.%06d_%05lu %-10s %.4s ] %s" /* TODO */, 140 | tm.tm_year - 100, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, 141 | tm.tm_sec, chrono::fractional2us(now.fractional), (long)osal_getpid(), 142 | prefix.c_str(), level2str(priority), suffix.c_str()); 143 | 144 | va_list ones; 145 | memset(&ones, 0, sizeof(ones)) /* zap MSVC and other goofy compilers */; 146 | if (same_or_higher(priority, error)) 147 | va_copy(ones, ap); 148 | vfprintf(last, format, ap); 149 | 150 | size_t len = strlen(format); 151 | char end = len ? format[len - 1] : '\0'; 152 | 153 | switch (end) { 154 | default: 155 | putc('\n', last); 156 | MDBX_CXX17_FALLTHROUGH; // fall through 157 | case '\n': 158 | fflush(last); 159 | last = nullptr; 160 | MDBX_CXX17_FALLTHROUGH; // fall through 161 | case ' ': 162 | case '_': 163 | case ':': 164 | case '|': 165 | case ',': 166 | case '\t': 167 | case '\b': 168 | case '\r': 169 | case '\0': 170 | break; 171 | } 172 | 173 | if (same_or_higher(priority, error)) { 174 | if (last != stderr) { 175 | fprintf(stderr, "[ %05lu %-10s %.4s ] %s", (long)osal_getpid(), 176 | prefix.c_str(), level2str(priority), suffix.c_str()); 177 | vfprintf(stderr, format, ones); 178 | if (end == '\n') 179 | fflush(stderr); 180 | else 181 | last = stderr; 182 | } 183 | va_end(ones); 184 | } 185 | } 186 | 187 | bool feed_ap(const char *format, va_list ap) { 188 | if (!last) 189 | return false; 190 | 191 | if (last == stderr) { 192 | va_list ones; 193 | va_copy(ones, ap); 194 | vfprintf(stdout, format, ones); 195 | va_end(ones); 196 | } 197 | vfprintf(last, format, ap); 198 | size_t len = strlen(format); 199 | if (len && format[len - 1] == '\n') { 200 | fflush(last); 201 | if (last == stderr) 202 | fflush(stdout); 203 | last = nullptr; 204 | } 205 | return true; 206 | } 207 | 208 | bool feed(const char *format, ...) { 209 | if (!last) 210 | return false; 211 | 212 | va_list ap; 213 | va_start(ap, format); 214 | feed_ap(format, ap); 215 | va_end(ap); 216 | return true; 217 | } 218 | 219 | local_suffix::local_suffix(const char *c_str) 220 | : trim_pos(suffix.size()), indent(0) { 221 | suffix.append(c_str); 222 | } 223 | 224 | local_suffix::local_suffix(const std::string &str) 225 | : trim_pos(suffix.size()), indent(0) { 226 | suffix.append(str); 227 | } 228 | 229 | void local_suffix::push() { 230 | indent += 1; 231 | suffix.push_back('\t'); 232 | } 233 | 234 | void local_suffix::pop() { 235 | assert(indent > 0); 236 | if (indent > 0) { 237 | indent -= 1; 238 | suffix.pop_back(); 239 | } 240 | } 241 | 242 | local_suffix::~local_suffix() { suffix.erase(trim_pos); } 243 | 244 | void progress_canary(bool active) { 245 | static chrono::time progress_timestamp; 246 | chrono::time now = chrono::now_monotonic(); 247 | 248 | if (now.fixedpoint - progress_timestamp.fixedpoint < 249 | chrono::from_ms(42).fixedpoint) 250 | return; 251 | 252 | if (osal_progress_push(active)) { 253 | progress_timestamp = now; 254 | return; 255 | } 256 | 257 | if (progress_timestamp.fixedpoint == 0) { 258 | putc('>', stderr); 259 | progress_timestamp = now; 260 | } else if (global::config::console_mode) { 261 | if (active) { 262 | static int last_point = -1; 263 | int point = (now.fixedpoint >> 29) & 3; 264 | if (point != last_point) { 265 | progress_timestamp = now; 266 | fprintf(stderr, "%c\b", "-\\|/"[last_point = point]); 267 | } 268 | } else if (now.fixedpoint - progress_timestamp.fixedpoint > 269 | chrono::from_seconds(2).fixedpoint) { 270 | progress_timestamp = now; 271 | fprintf(stderr, "%c\b", "@*"[now.utc & 1]); 272 | } 273 | } else { 274 | static int count; 275 | if (active && now.fixedpoint - progress_timestamp.fixedpoint > 276 | chrono::from_seconds(1).fixedpoint) { 277 | putc('.', stderr); 278 | progress_timestamp = now; 279 | ++count; 280 | } else if (now.fixedpoint - progress_timestamp.fixedpoint > 281 | chrono::from_seconds(5).fixedpoint) { 282 | putc("@*"[now.utc & 1], stderr); 283 | progress_timestamp = now; 284 | ++count; 285 | } 286 | if (count == 60) { 287 | count = 0; 288 | putc('\n', stderr); 289 | } 290 | } 291 | fflush(stderr); 292 | } 293 | 294 | } // namespace logging 295 | 296 | void log_extra(const char *msg, ...) { 297 | if (logging::same_or_higher(logging::extra, logging::level)) { 298 | va_list ap; 299 | va_start(ap, msg); 300 | logging::output_nocheckloglevel_ap(logging::extra, msg, ap); 301 | va_end(ap); 302 | } else 303 | logging::last = nullptr; 304 | } 305 | 306 | void log_trace(const char *msg, ...) { 307 | if (logging::same_or_higher(logging::trace, logging::level)) { 308 | va_list ap; 309 | va_start(ap, msg); 310 | logging::output_nocheckloglevel_ap(logging::trace, msg, ap); 311 | va_end(ap); 312 | } else 313 | logging::last = nullptr; 314 | } 315 | 316 | void log_debug(const char *msg, ...) { 317 | if (logging::same_or_higher(logging::debug, logging::level)) { 318 | va_list ap; 319 | va_start(ap, msg); 320 | logging::output_nocheckloglevel_ap(logging::debug, msg, ap); 321 | va_end(ap); 322 | } else 323 | logging::last = nullptr; 324 | } 325 | 326 | void log_verbose(const char *msg, ...) { 327 | if (logging::same_or_higher(logging::verbose, logging::level)) { 328 | va_list ap; 329 | va_start(ap, msg); 330 | logging::output_nocheckloglevel_ap(logging::verbose, msg, ap); 331 | va_end(ap); 332 | } else 333 | logging::last = nullptr; 334 | } 335 | 336 | void log_notice(const char *msg, ...) { 337 | if (logging::same_or_higher(logging::notice, logging::level)) { 338 | va_list ap; 339 | va_start(ap, msg); 340 | logging::output_nocheckloglevel_ap(logging::notice, msg, ap); 341 | va_end(ap); 342 | } else 343 | logging::last = nullptr; 344 | } 345 | 346 | void log_warning(const char *msg, ...) { 347 | if (logging::same_or_higher(logging::warning, logging::level)) { 348 | va_list ap; 349 | va_start(ap, msg); 350 | logging::output_nocheckloglevel_ap(logging::warning, msg, ap); 351 | va_end(ap); 352 | } else 353 | logging::last = nullptr; 354 | } 355 | 356 | void log_error(const char *msg, ...) { 357 | if (logging::same_or_higher(logging::error, logging::level)) { 358 | va_list ap; 359 | va_start(ap, msg); 360 | logging::output_nocheckloglevel_ap(logging::error, msg, ap); 361 | va_end(ap); 362 | } else 363 | logging::last = nullptr; 364 | } 365 | 366 | void log_trouble(const char *where, const char *what, int errnum) { 367 | log_error("%s: %s %s", where, what, test_strerror(errnum)); 368 | } 369 | 370 | bool log_enabled(const logging::loglevel priority) { 371 | return logging::same_or_higher(priority, logging::level); 372 | } 373 | 374 | void log_flush(void) { fflushall(); } 375 | -------------------------------------------------------------------------------- /test/log.h++: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2023 Leonid Yuriev 3 | * and other libmdbx authors: please see AUTHORS file. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | 15 | #pragma once 16 | 17 | #include "base.h++" 18 | #include "chrono.h++" 19 | 20 | MDBX_NORETURN void usage(void); 21 | MDBX_NORETURN void MDBX_PRINTF_ARGS(1, 2) failure(const char *fmt, ...); 22 | MDBX_NORETURN void failure_perror(const char *what, int errnum); 23 | const char *test_strerror(int errnum); 24 | 25 | namespace logging { 26 | 27 | enum loglevel { 28 | extra = MDBX_LOG_EXTRA, 29 | trace = MDBX_LOG_TRACE, 30 | debug = MDBX_LOG_DEBUG, 31 | verbose = MDBX_LOG_VERBOSE, 32 | notice = MDBX_LOG_NOTICE, 33 | warning = MDBX_LOG_WARN, 34 | error = MDBX_LOG_ERROR, 35 | failure = MDBX_LOG_FATAL 36 | }; 37 | 38 | inline bool lower(loglevel left, loglevel right) { 39 | static_assert(MDBX_LOG_EXTRA > MDBX_LOG_FATAL, "WTF?"); 40 | return left > right; 41 | } 42 | 43 | inline bool same_or_higher(loglevel left, loglevel right) { 44 | return left <= right; 45 | } 46 | 47 | const char *level2str(const loglevel level); 48 | void setup(loglevel priority, const std::string &prefix); 49 | void setup(const std::string &prefix); 50 | void setlevel(loglevel priority); 51 | 52 | void output_nocheckloglevel_ap(const loglevel priority, const char *format, 53 | va_list ap); 54 | bool MDBX_PRINTF_ARGS(2, 3) 55 | output(const loglevel priority, const char *format, ...); 56 | bool feed_ap(const char *format, va_list ap); 57 | bool MDBX_PRINTF_ARGS(1, 2) feed(const char *format, ...); 58 | 59 | void inline MDBX_PRINTF_ARGS(2, 3) 60 | output_nocheckloglevel(const loglevel priority, const char *format, ...) { 61 | va_list ap; 62 | va_start(ap, format); 63 | output_nocheckloglevel_ap(priority, format, ap); 64 | va_end(ap); 65 | } 66 | 67 | void progress_canary(bool active); 68 | 69 | class local_suffix { 70 | protected: 71 | size_t trim_pos; 72 | int indent; 73 | 74 | public: 75 | local_suffix(const local_suffix &) = delete; 76 | local_suffix(const local_suffix &&) = delete; 77 | const local_suffix &operator=(const local_suffix &) = delete; 78 | 79 | local_suffix(const char *c_str); 80 | local_suffix(const std::string &str); 81 | void push(); 82 | void pop(); 83 | ~local_suffix(); 84 | }; 85 | 86 | } // namespace logging 87 | 88 | void MDBX_PRINTF_ARGS(1, 2) static inline log_null(const char *msg, ...) { 89 | return (void)msg; 90 | } 91 | void MDBX_PRINTF_ARGS(1, 2) log_extra(const char *msg, ...); 92 | void MDBX_PRINTF_ARGS(1, 2) log_trace(const char *msg, ...); 93 | void MDBX_PRINTF_ARGS(1, 2) log_debug(const char *msg, ...); 94 | void MDBX_PRINTF_ARGS(1, 2) log_verbose(const char *msg, ...); 95 | void MDBX_PRINTF_ARGS(1, 2) log_notice(const char *msg, ...); 96 | void MDBX_PRINTF_ARGS(1, 2) log_warning(const char *msg, ...); 97 | void MDBX_PRINTF_ARGS(1, 2) log_error(const char *msg, ...); 98 | 99 | void log_trouble(const char *where, const char *what, int errnum); 100 | void log_flush(void); 101 | bool log_enabled(const logging::loglevel priority); 102 | 103 | #ifdef _DEBUG 104 | #define TRACE(...) log_trace(__VA_ARGS__) 105 | #else 106 | #define TRACE(...) log_null(__VA_ARGS__) 107 | #endif 108 | -------------------------------------------------------------------------------- /test/osal.h++: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2023 Leonid Yuriev 3 | * and other libmdbx authors: please see AUTHORS file. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | 15 | #pragma once 16 | 17 | #include "base.h++" 18 | 19 | void osal_setup(const std::vector &actors); 20 | void osal_broadcast(unsigned id); 21 | int osal_waitfor(unsigned id); 22 | 23 | int osal_actor_start(const actor_config &config, mdbx_pid_t &pid); 24 | actor_status osal_actor_info(const mdbx_pid_t pid); 25 | void osal_killall_actors(void); 26 | int osal_actor_poll(mdbx_pid_t &pid, unsigned timeout); 27 | void osal_wait4barrier(void); 28 | 29 | bool osal_progress_push(bool active); 30 | 31 | mdbx_pid_t osal_getpid(void); 32 | int osal_delay(unsigned seconds); 33 | void osal_udelay(size_t us); 34 | void osal_yield(void); 35 | bool osal_istty(int fd); 36 | std::string osal_tempdir(void); 37 | 38 | #ifdef _MSC_VER 39 | #ifndef STDIN_FILENO 40 | #define STDIN_FILENO _fileno(stdin) 41 | #endif 42 | #ifndef STDOUT_FILENO 43 | #define STDOUT_FILENO _fileno(stdout) 44 | #endif 45 | #ifndef STDERR_FILENO 46 | #define STDERR_FILENO _fileno(stderr) 47 | #endif 48 | #endif /* _MSC_VER */ 49 | -------------------------------------------------------------------------------- /test/pcrf/README.md: -------------------------------------------------------------------------------- 1 | PCRF Session DB emulation test 2 | 3 | -------------------------------------------------------------------------------- /test/stub/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Aleksey Demakov 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /test/stub/README.md: -------------------------------------------------------------------------------- 1 | # DarwinPthreadBarrier 2 | 3 | A pthread_barrier_t implementation for Mac OS/X 4 | 5 | There is no pthread_barrier_t in Mac OS/X pthreads. This project fixes 6 | this omission by providing a simple-minded barrier implementation based 7 | on a pair of pthread_mutex_t and pthread_cond_t. 8 | 9 | -------------------------------------------------------------------------------- /test/stub/pthread_barrier.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Aleksey Demakov 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include "pthread_barrier.h" 29 | 30 | #include 31 | 32 | int pthread_barrierattr_init(pthread_barrierattr_t *attr) { 33 | memset(attr, 0, sizeof(pthread_barrierattr_t)); 34 | int m = pthread_mutexattr_init(&attr->mattr); 35 | int c = pthread_condattr_init(&attr->cattr); 36 | return m ? m : c; 37 | } 38 | 39 | int pthread_barrierattr_destroy(pthread_barrierattr_t *attr) { 40 | int c = pthread_condattr_destroy(&attr->cattr); 41 | int m = pthread_mutexattr_destroy(&attr->mattr); 42 | return m ? m : c; 43 | } 44 | 45 | int pthread_barrierattr_getpshared(const pthread_barrierattr_t *__restrict attr, 46 | int *__restrict pshared) { 47 | return pthread_condattr_getpshared(&attr->cattr, pshared); 48 | } 49 | 50 | int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared) { 51 | int m = pthread_mutexattr_setpshared(&attr->mattr, pshared); 52 | int c = pthread_condattr_setpshared(&attr->cattr, pshared); 53 | return m ? m : c; 54 | } 55 | 56 | int pthread_barrier_init(pthread_barrier_t *__restrict barrier, 57 | const pthread_barrierattr_t *__restrict attr, 58 | unsigned count) { 59 | if (count == 0) 60 | return errno = EINVAL; 61 | 62 | int rc = pthread_mutex_init(&barrier->mutex, attr ? &attr->mattr : 0); 63 | if (rc) 64 | return rc; 65 | 66 | rc = pthread_cond_init(&barrier->cond, attr ? &attr->cattr : 0); 67 | if (rc) { 68 | int errno_save = errno; 69 | pthread_mutex_destroy(&barrier->mutex); 70 | errno = errno_save; 71 | return rc; 72 | } 73 | 74 | barrier->limit = count; 75 | barrier->count = 0; 76 | barrier->phase = 0; 77 | return 0; 78 | } 79 | 80 | int pthread_barrier_destroy(pthread_barrier_t *barrier) { 81 | pthread_mutex_destroy(&barrier->mutex); 82 | pthread_cond_destroy(&barrier->cond); 83 | return 0; 84 | } 85 | 86 | int pthread_barrier_wait(pthread_barrier_t *barrier) { 87 | int rc = pthread_mutex_lock(&barrier->mutex); 88 | if (rc) 89 | return rc; 90 | 91 | barrier->count++; 92 | if (barrier->count >= barrier->limit) { 93 | barrier->phase++; 94 | barrier->count = 0; 95 | pthread_cond_broadcast(&barrier->cond); 96 | pthread_mutex_unlock(&barrier->mutex); 97 | return PTHREAD_BARRIER_SERIAL_THREAD; 98 | } else { 99 | unsigned phase = barrier->phase; 100 | do 101 | pthread_cond_wait(&barrier->cond, &barrier->mutex); 102 | while (phase == barrier->phase); 103 | pthread_mutex_unlock(&barrier->mutex); 104 | return 0; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /test/stub/pthread_barrier.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Aleksey Demakov 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef PTHREAD_BARRIER_H 29 | #define PTHREAD_BARRIER_H 30 | 31 | #include 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | #if !defined(PTHREAD_BARRIER_SERIAL_THREAD) 38 | #define PTHREAD_BARRIER_SERIAL_THREAD (1) 39 | #endif 40 | 41 | #if !defined(PTHREAD_PROCESS_PRIVATE) 42 | #define PTHREAD_PROCESS_PRIVATE (42) 43 | #endif 44 | #if !defined(PTHREAD_PROCESS_SHARED) 45 | #define PTHREAD_PROCESS_SHARED (43) 46 | #endif 47 | 48 | typedef struct { 49 | pthread_mutexattr_t mattr; 50 | pthread_condattr_t cattr; 51 | } pthread_barrierattr_t; 52 | 53 | typedef struct { 54 | pthread_mutex_t mutex; 55 | pthread_cond_t cond; 56 | unsigned int limit; 57 | unsigned int count; 58 | unsigned int phase; 59 | } pthread_barrier_t; 60 | 61 | int pthread_barrierattr_init(pthread_barrierattr_t *attr); 62 | int pthread_barrierattr_destroy(pthread_barrierattr_t *attr); 63 | 64 | int pthread_barrierattr_getpshared(const pthread_barrierattr_t *__restrict attr, 65 | int *__restrict pshared); 66 | int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared); 67 | 68 | int pthread_barrier_init(pthread_barrier_t *__restrict barrier, 69 | const pthread_barrierattr_t *__restrict attr, 70 | unsigned int count); 71 | int pthread_barrier_destroy(pthread_barrier_t *barrier); 72 | 73 | int pthread_barrier_wait(pthread_barrier_t *barrier); 74 | 75 | #ifdef __cplusplus 76 | } 77 | #endif 78 | 79 | #endif /* PTHREAD_BARRIER_H */ 80 | -------------------------------------------------------------------------------- /test/test.h++: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2023 Leonid Yuriev 3 | * and other libmdbx authors: please see AUTHORS file. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | 15 | #pragma once 16 | 17 | #include "base.h++" 18 | #include "chrono.h++" 19 | #include "config.h++" 20 | #include "keygen.h++" 21 | #include "log.h++" 22 | #include "osal.h++" 23 | #include "utils.h++" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #ifndef HAVE_cxx17_std_string_view 31 | #if __cplusplus >= 201703L && __has_include() 32 | #include 33 | #define HAVE_cxx17_std_string_view 1 34 | #else 35 | #define HAVE_cxx17_std_string_view 0 36 | #endif 37 | #endif /* HAVE_cxx17_std_string_view */ 38 | 39 | #if HAVE_cxx17_std_string_view 40 | #include 41 | #endif 42 | 43 | bool test_execute(const actor_config &config); 44 | std::string thunk_param(const actor_config &config); 45 | void testcase_setup(const char *casename, const actor_params ¶ms, 46 | unsigned &last_space_id); 47 | void configure_actor(unsigned &last_space_id, const actor_testcase testcase, 48 | const char *space_id_cstr, actor_params params); 49 | void keycase_setup(const char *casename, actor_params ¶ms); 50 | 51 | namespace global { 52 | 53 | extern const char thunk_param_prefix[]; 54 | extern std::vector actors; 55 | extern std::unordered_map events; 56 | extern std::unordered_map pid2actor; 57 | extern std::set databases; 58 | extern unsigned nactors; 59 | extern chrono::time start_monotonic; 60 | extern chrono::time deadline_monotonic; 61 | extern bool singlemode; 62 | 63 | namespace config { 64 | extern unsigned timeout_duration_seconds; 65 | extern bool dump_config; 66 | extern bool cleanup_before; 67 | extern bool cleanup_after; 68 | extern bool failfast; 69 | extern bool progress_indicator; 70 | extern bool console_mode; 71 | extern bool geometry_jitter; 72 | } /* namespace config */ 73 | 74 | } /* namespace global */ 75 | 76 | //----------------------------------------------------------------------------- 77 | 78 | struct db_deleter /* : public std::unary_function */ { 79 | void operator()(MDBX_env *env) const { mdbx_env_close(env); } 80 | }; 81 | 82 | struct txn_deleter /* : public std::unary_function */ { 83 | void operator()(MDBX_txn *txn) const { 84 | int rc = mdbx_txn_abort(txn); 85 | if (rc) 86 | log_trouble(__func__, "mdbx_txn_abort()", rc); 87 | } 88 | }; 89 | 90 | struct cursor_deleter /* : public std::unary_function */ { 91 | void operator()(MDBX_cursor *cursor) const { mdbx_cursor_close(cursor); } 92 | }; 93 | 94 | using scoped_db_guard = std::unique_ptr; 95 | using scoped_txn_guard = std::unique_ptr; 96 | using scoped_cursor_guard = std::unique_ptr; 97 | 98 | //----------------------------------------------------------------------------- 99 | 100 | class testcase; 101 | 102 | class registry { 103 | struct record { 104 | actor_testcase id = ac_none; 105 | std::string name; 106 | bool (*review_params)(actor_params &) = nullptr; 107 | testcase *(*constructor)(const actor_config &, const mdbx_pid_t) = nullptr; 108 | }; 109 | std::unordered_map name2id; 110 | std::unordered_map id2record; 111 | static bool add(const record *item); 112 | static registry *instance(); 113 | 114 | public: 115 | template struct factory : public record { 116 | factory(const actor_testcase id, const char *name) { 117 | this->id = id; 118 | this->name = name; 119 | review_params = TESTCASE::review_params; 120 | constructor = [](const actor_config &config, 121 | const mdbx_pid_t pid) -> testcase * { 122 | return new TESTCASE(config, pid); 123 | }; 124 | add(this); 125 | } 126 | }; 127 | static bool review_actor_params(const actor_testcase id, 128 | actor_params ¶ms); 129 | static testcase *create_actor(const actor_config &config, 130 | const mdbx_pid_t pid); 131 | }; 132 | 133 | #define REGISTER_TESTCASE(NAME) \ 134 | static registry::factory gRegister_##NAME( \ 135 | ac_##NAME, MDBX_STRINGIFY(NAME)) 136 | 137 | class testcase { 138 | protected: 139 | using data_view = mdbx::slice; 140 | static inline data_view iov2dataview(const MDBX_val &v) { 141 | return (v.iov_base && v.iov_len) 142 | ? data_view(static_cast(v.iov_base), v.iov_len) 143 | : data_view(); 144 | } 145 | static inline data_view iov2dataview(const keygen::buffer &b) { 146 | return iov2dataview(b->value); 147 | } 148 | 149 | using Item = std::pair<::mdbx::buffer<>, ::mdbx::buffer<>>; 150 | 151 | static MDBX_val dataview2iov(const data_view &v) { 152 | MDBX_val r; 153 | r.iov_base = (void *)v.data(); 154 | r.iov_len = v.size(); 155 | return r; 156 | } 157 | struct ItemCompare { 158 | const testcase *context; 159 | ItemCompare(const testcase *owner) : context(owner) { 160 | /* The context->txn_guard may be empty/null here */ 161 | } 162 | 163 | bool operator()(const Item &a, const Item &b) const { 164 | MDBX_val va = dataview2iov(a.first), vb = dataview2iov(b.first); 165 | assert(context->txn_guard.get() != nullptr); 166 | int cmp = mdbx_cmp(context->txn_guard.get(), context->dbi, &va, &vb); 167 | if (cmp == 0 && 168 | (context->config.params.table_flags & MDBX_DUPSORT) != 0) { 169 | va = dataview2iov(a.second); 170 | vb = dataview2iov(b.second); 171 | cmp = mdbx_dcmp(context->txn_guard.get(), context->dbi, &va, &vb); 172 | } 173 | return cmp < 0; 174 | } 175 | }; 176 | 177 | // for simplify the set> 178 | // is used instead of multimap 179 | using SET = std::set; 180 | 181 | const actor_config &config; 182 | const mdbx_pid_t pid; 183 | 184 | MDBX_dbi dbi{0}; 185 | scoped_db_guard db_guard; 186 | scoped_txn_guard txn_guard; 187 | scoped_cursor_guard cursor_guard; 188 | bool signalled{false}; 189 | bool need_speculum_assign{false}; 190 | 191 | uint64_t nops_completed{0}; 192 | chrono::time start_timestamp; 193 | keygen::buffer key; 194 | keygen::buffer data; 195 | keygen::maker keyvalue_maker; 196 | 197 | struct { 198 | MDBX_canary canary; 199 | } last; 200 | 201 | SET speculum{ItemCompare(this)}, speculum_committed{ItemCompare(this)}; 202 | #ifndef SPECULUM_CURSORS 203 | #define SPECULUM_CURSORS 1 204 | #endif /* SPECULUM_CURSORS */ 205 | #if SPECULUM_CURSORS 206 | scoped_cursor_guard speculum_cursors[5 + 1]; 207 | void speculum_prepare_cursors(const Item &item); 208 | void speculum_check_cursor(const char *where, const char *stage, 209 | const testcase::SET::const_iterator &it, 210 | int cursor_err, const MDBX_val &cursor_key, 211 | const MDBX_val &cursor_data) const; 212 | void speculum_check_cursor(const char *where, const char *stage, 213 | const testcase::SET::const_iterator &it, 214 | MDBX_cursor *cursor, 215 | const MDBX_cursor_op op) const; 216 | #endif /* SPECULUM_CURSORS */ 217 | void speculum_check_iterator(const char *where, const char *stage, 218 | const testcase::SET::const_iterator &it, 219 | const MDBX_val &k, const MDBX_val &v) const; 220 | 221 | void verbose(const char *where, const char *stage, 222 | const testcase::SET::const_iterator &it) const; 223 | void verbose(const char *where, const char *stage, const MDBX_val &k, 224 | const MDBX_val &v, int err = MDBX_SUCCESS) const; 225 | 226 | bool is_same(const Item &a, const Item &b) const; 227 | bool is_same(const SET::const_iterator &it, const MDBX_val &k, 228 | const MDBX_val &v) const; 229 | 230 | bool speculum_verify(); 231 | bool check_batch_get(); 232 | int insert(const keygen::buffer &akey, const keygen::buffer &adata, 233 | MDBX_put_flags_t flags); 234 | int replace(const keygen::buffer &akey, const keygen::buffer &new_value, 235 | const keygen::buffer &old_value, MDBX_put_flags_t flags); 236 | int remove(const keygen::buffer &akey, const keygen::buffer &adata); 237 | 238 | static int hsr_callback(const MDBX_env *env, const MDBX_txn *txn, 239 | mdbx_pid_t pid, mdbx_tid_t tid, uint64_t laggard, 240 | unsigned gap, size_t space, 241 | int retry) MDBX_CXX17_NOEXCEPT; 242 | 243 | MDBX_env_flags_t actual_env_mode{MDBX_ENV_DEFAULTS}; 244 | bool is_nested_txn_available() const { 245 | return (actual_env_mode & MDBX_WRITEMAP) == 0; 246 | } 247 | void kick_progress(bool active) const; 248 | void db_prepare(); 249 | void db_open(); 250 | void db_close(); 251 | void txn_begin(bool readonly, MDBX_txn_flags_t flags = MDBX_TXN_READWRITE); 252 | int breakable_commit(); 253 | void txn_end(bool abort); 254 | int breakable_restart(); 255 | void txn_restart(bool abort, bool readonly, 256 | MDBX_txn_flags_t flags = MDBX_TXN_READWRITE); 257 | void cursor_open(MDBX_dbi handle); 258 | void cursor_close(); 259 | void cursor_renew(); 260 | void txn_inject_writefault(void); 261 | void txn_inject_writefault(MDBX_txn *txn); 262 | void fetch_canary(); 263 | void update_canary(uint64_t increment); 264 | void checkdata(const char *step, MDBX_dbi handle, MDBX_val key2check, 265 | MDBX_val expected_valued); 266 | unsigned txn_underutilization_x256(MDBX_txn *txn) const; 267 | 268 | MDBX_dbi db_table_open(bool create); 269 | void db_table_drop(MDBX_dbi handle); 270 | void db_table_clear(MDBX_dbi handle, MDBX_txn *txn = nullptr); 271 | void db_table_close(MDBX_dbi handle); 272 | int db_open__begin__table_create_open_clean(MDBX_dbi &handle); 273 | bool is_handle_created_in_current_txn(const MDBX_dbi handle, MDBX_txn *txn); 274 | 275 | bool wait4start(); 276 | void report(size_t nops_done); 277 | void signal(); 278 | bool should_continue(bool check_timeout_only = false) const; 279 | 280 | void generate_pair(const keygen::serial_t serial, keygen::buffer &out_key, 281 | keygen::buffer &out_value, keygen::serial_t data_age) { 282 | keyvalue_maker.pair(serial, out_key, out_value, data_age, false); 283 | } 284 | 285 | void generate_pair(const keygen::serial_t serial) { 286 | keyvalue_maker.pair(serial, key, data, 0, true); 287 | } 288 | 289 | bool mode_readonly() const { 290 | return (config.params.mode_flags & MDBX_RDONLY) ? true : false; 291 | } 292 | 293 | public: 294 | testcase(const actor_config &config, const mdbx_pid_t pid) 295 | : config(config), pid(pid) { 296 | start_timestamp.reset(); 297 | memset(&last, 0, sizeof(last)); 298 | } 299 | 300 | static bool review_params(actor_params ¶ms) { 301 | // silently fix key/data length for fixed-length modes 302 | if ((params.table_flags & MDBX_INTEGERKEY) && 303 | params.keylen_min != params.keylen_max) 304 | params.keylen_min = params.keylen_max; 305 | if ((params.table_flags & (MDBX_INTEGERDUP | MDBX_DUPFIXED)) && 306 | params.datalen_min != params.datalen_max) 307 | params.datalen_min = params.datalen_max; 308 | return true; 309 | } 310 | 311 | virtual bool setup(); 312 | virtual bool run() { return true; } 313 | virtual bool teardown(); 314 | virtual ~testcase() {} 315 | }; 316 | 317 | //----------------------------------------------------------------------------- 318 | 319 | class testcase_ttl : public testcase { 320 | using inherited = testcase; 321 | 322 | protected: 323 | struct { 324 | unsigned max_window_size{0}; 325 | unsigned max_step_size{0}; 326 | } sliding; 327 | unsigned edge2window(uint64_t edge); 328 | unsigned edge2count(uint64_t edge); 329 | 330 | public: 331 | testcase_ttl(const actor_config &config, const mdbx_pid_t pid) 332 | : inherited(config, pid) {} 333 | bool setup() override; 334 | bool run() override; 335 | }; 336 | -------------------------------------------------------------------------------- /test/try.c++: -------------------------------------------------------------------------------- 1 | #include "test.h++" 2 | 3 | class testcase_try : public testcase { 4 | public: 5 | testcase_try(const actor_config &config, const mdbx_pid_t pid) 6 | : testcase(config, pid) {} 7 | bool run() override; 8 | }; 9 | REGISTER_TESTCASE(try); 10 | 11 | bool testcase_try::run() { 12 | db_open(); 13 | assert(!txn_guard); 14 | 15 | MDBX_txn *txn = nullptr; 16 | MDBX_txn *txn2 = nullptr; 17 | int rc = mdbx_txn_begin(db_guard.get(), nullptr, MDBX_TXN_READWRITE, &txn); 18 | if (unlikely(rc != MDBX_SUCCESS)) 19 | failure_perror("mdbx_txn_begin(MDBX_TXN_TRY)", rc); 20 | else { 21 | rc = mdbx_txn_begin(db_guard.get(), nullptr, MDBX_TXN_TRY, &txn2); 22 | if (unlikely(rc != MDBX_BUSY)) 23 | failure_perror("mdbx_txn_begin(MDBX_TXN_TRY)", rc); 24 | } 25 | 26 | txn_guard.reset(txn); 27 | return true; 28 | } 29 | -------------------------------------------------------------------------------- /test/ttl.c++: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2023 Leonid Yuriev 3 | * and other libmdbx authors: please see AUTHORS file. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | 15 | #include "test.h++" 16 | #include 17 | #include 18 | 19 | /* LY: тест "эмуляцией time-to-live": 20 | * - организуется "скользящее окно", которое двигается вперед вдоль 21 | * числовой оси каждую транзакцию. 22 | * - по переднему краю "скользящего окна" записи добавляются в таблицу, 23 | * а по заднему удаляются. 24 | * - количество добавляемых/удаляемых записей псевдослучайно зависит 25 | * от номера транзакции, но с экспоненциальным распределением. 26 | * - размер "скользящего окна" также псевдослучайно зависит от номера 27 | * транзакции с "отрицательным" экспоненциальным распределением 28 | * MAX_WIDTH - exp(rnd(N)), при уменьшении окна сдвигается задний 29 | * край и удаляются записи позади него. 30 | * 31 | * Таким образом имитируется поведение таблицы с TTL: записи стохастически 32 | * добавляются и удаляются, но изредка происходит массивное удаление. 33 | */ 34 | REGISTER_TESTCASE(ttl); 35 | 36 | unsigned testcase_ttl::edge2count(uint64_t edge) { 37 | const double rnd = u64_to_double1(prng64_map1_white(edge)); 38 | const unsigned count = 39 | unsigned(std::lrint(std::pow(sliding.max_step_size, rnd))); 40 | // average value: (X - 1) / ln(X), where X = sliding.max_step_size 41 | return count; 42 | } 43 | 44 | unsigned testcase_ttl::edge2window(uint64_t edge) { 45 | const double rnd = u64_to_double1(bleach64(edge)); 46 | const unsigned size = 47 | sliding.max_window_size - 48 | unsigned(std::lrint(std::pow(sliding.max_window_size, rnd))); 49 | // average value: Y - (Y - 1) / ln(Y), where Y = sliding.max_window_size 50 | return size; 51 | } 52 | 53 | static inline double estimate(const double x, const double y) { 54 | /* среднее кол-во операций N = X' * Y', где X' и Y' средние значения 55 | * размера окна и кол-ва добавляемых за один шаг записей: 56 | * X' = (X - 1) / ln(X), где X = sliding.max_step_size 57 | * Y' = Y - (Y - 1) / ln(Y), где Y = sliding.max_window_size */ 58 | return (x - 1) / std::log(x) * (y - (y - 1) / std::log(y)); 59 | } 60 | 61 | bool testcase_ttl::setup() { 62 | const unsigned window_top_lower = 63 | 7 /* нижний предел для верхней границы диапазона, в котором будет 64 | стохастически колебаться размер окна */ 65 | ; 66 | const unsigned count_top_lower = 67 | 7 /* нижний предел для верхней границы диапазона, в котором будет 68 | стохастически колебаться кол-во записей добавляемых на одном шаге */ 69 | ; 70 | 71 | /* для параметризации используем подходящие параметры, 72 | * которые не имеют здесь смысла в первоначальном значении. */ 73 | const double ratio = 74 | double(config.params.batch_read ? config.params.batch_read : 1) / 75 | double(config.params.batch_write ? config.params.batch_write : 1); 76 | 77 | /* проще найти двоичным поиском (вариация метода Ньютона) */ 78 | double hi = config.params.test_nops, lo = 1; 79 | double x = std::sqrt(hi + lo) / ratio; 80 | while (hi > lo) { 81 | const double n = estimate(x, x * ratio); 82 | if (n > config.params.test_nops) 83 | hi = x - 1; 84 | else 85 | lo = x + 1; 86 | x = (hi + lo) / 2; 87 | } 88 | 89 | sliding.max_step_size = unsigned(std::lrint(x)); 90 | if (sliding.max_step_size < count_top_lower) 91 | sliding.max_step_size = count_top_lower; 92 | sliding.max_window_size = unsigned(std::lrint(x * ratio)); 93 | if (sliding.max_window_size < window_top_lower) 94 | sliding.max_window_size = window_top_lower; 95 | 96 | while (estimate(sliding.max_step_size, sliding.max_window_size) > 97 | config.params.test_nops * 2.0) { 98 | if (ratio * sliding.max_step_size > sliding.max_window_size) { 99 | if (sliding.max_step_size < count_top_lower) 100 | break; 101 | sliding.max_step_size = sliding.max_step_size * 7 / 8; 102 | } else { 103 | if (sliding.max_window_size < window_top_lower) 104 | break; 105 | sliding.max_window_size = sliding.max_window_size * 7 / 8; 106 | } 107 | } 108 | 109 | log_verbose("come up window_max %u from `batch_read`", 110 | sliding.max_window_size); 111 | log_verbose("come up step_max %u from `batch_write`", sliding.max_step_size); 112 | return inherited::setup(); 113 | } 114 | 115 | bool testcase_ttl::run() { 116 | int err = db_open__begin__table_create_open_clean(dbi); 117 | if (unlikely(err != MDBX_SUCCESS)) { 118 | log_notice("ttl: bailout-prepare due '%s'", mdbx_strerror(err)); 119 | return false; 120 | } 121 | 122 | uint64_t seed = 123 | prng64_map2_white(config.params.keygen.seed) + config.actor_id; 124 | keyvalue_maker.setup(config.params, config.actor_id, 0 /* thread_number */); 125 | key = keygen::alloc(config.params.keylen_max); 126 | data = keygen::alloc(config.params.datalen_max); 127 | const MDBX_put_flags_t insert_flags = 128 | (config.params.table_flags & MDBX_DUPSORT) 129 | ? MDBX_NODUPDATA 130 | : MDBX_NODUPDATA | MDBX_NOOVERWRITE; 131 | 132 | std::deque> fifo; 133 | uint64_t serial = 0; 134 | bool rc = false; 135 | unsigned clear_wholetable_passed = 0; 136 | unsigned clear_stepbystep_passed = 0; 137 | unsigned dbfull_passed = 0; 138 | unsigned loops = 0; 139 | bool keyspace_overflow = false; 140 | while (true) { 141 | const uint64_t salt = prng64_white(seed) /* mdbx_txn_id(txn_guard.get()) */; 142 | 143 | const unsigned window_width = 144 | (!should_continue() || flipcoin_x4()) ? 0 : edge2window(salt); 145 | unsigned head_count = edge2count(salt); 146 | log_debug("ttl: step #%" PRIu64 " (serial %" PRIu64 147 | ", window %u, count %u) salt %" PRIu64, 148 | nops_completed, serial, window_width, head_count, salt); 149 | 150 | if (window_width || flipcoin()) { 151 | clear_stepbystep_passed += window_width == 0; 152 | while (fifo.size() > window_width) { 153 | uint64_t tail_serial = fifo.back().first; 154 | const unsigned tail_count = fifo.back().second; 155 | log_trace("ttl: pop-tail (serial %" PRIu64 ", count %u)", tail_serial, 156 | tail_count); 157 | fifo.pop_back(); 158 | for (unsigned n = 0; n < tail_count; ++n) { 159 | log_trace("ttl: remove-tail %" PRIu64, tail_serial); 160 | generate_pair(tail_serial); 161 | err = remove(key, data); 162 | if (unlikely(err != MDBX_SUCCESS)) { 163 | if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) { 164 | log_notice("ttl: tail-bailout due '%s'", mdbx_strerror(err)); 165 | goto bailout; 166 | } 167 | failure_perror("mdbx_del(tail)", err); 168 | } 169 | if (unlikely(!keyvalue_maker.increment(tail_serial, 1))) 170 | failure("ttl: unexpected key-space overflow on the tail"); 171 | } 172 | report(tail_count); 173 | } 174 | } else { 175 | log_trace("ttl: purge state"); 176 | db_table_clear(dbi); 177 | fifo.clear(); 178 | clear_wholetable_passed += 1; 179 | report(1); 180 | } 181 | 182 | err = breakable_restart(); 183 | if (unlikely(err != MDBX_SUCCESS)) { 184 | log_notice("ttl: bailout at commit due '%s'", mdbx_strerror(err)); 185 | break; 186 | } 187 | if (!speculum_verify()) { 188 | log_notice("ttl: bailout after tail-trim"); 189 | return false; 190 | } 191 | 192 | if (!keyspace_overflow && (should_continue() || !clear_wholetable_passed || 193 | !clear_stepbystep_passed)) { 194 | unsigned underutilization_x256 = 195 | txn_underutilization_x256(txn_guard.get()); 196 | if (dbfull_passed > underutilization_x256) { 197 | log_notice("ttl: skip head-grow to avoid one more dbfull (was %u, " 198 | "underutilization %.2f%%)", 199 | dbfull_passed, underutilization_x256 / 2.560); 200 | continue; 201 | } 202 | fifo.push_front(std::make_pair(serial, head_count)); 203 | retry: 204 | for (unsigned n = 0; n < head_count; ++n) { 205 | log_trace("ttl: insert-head %" PRIu64, serial); 206 | generate_pair(serial); 207 | err = insert(key, data, insert_flags); 208 | if (unlikely(err != MDBX_SUCCESS)) { 209 | if ((err == MDBX_TXN_FULL || err == MDBX_MAP_FULL) && 210 | config.params.ignore_dbfull) { 211 | log_notice("ttl: head-insert skip due '%s'", mdbx_strerror(err)); 212 | txn_restart(true, false); 213 | serial = fifo.front().first; 214 | fifo.front().second = head_count = n; 215 | dbfull_passed += 1; 216 | goto retry; 217 | } 218 | failure_perror("mdbx_put(head)", err); 219 | } 220 | 221 | if (unlikely(!keyvalue_maker.increment(serial, 1))) { 222 | log_notice("ttl: unexpected key-space overflow"); 223 | keyspace_overflow = true; 224 | txn_restart(true, false); 225 | serial = fifo.front().first; 226 | fifo.front().second = head_count = n; 227 | goto retry; 228 | } 229 | } 230 | err = breakable_restart(); 231 | if (unlikely(err != MDBX_SUCCESS)) { 232 | log_notice("ttl: head-commit skip due '%s'", mdbx_strerror(err)); 233 | serial = fifo.front().first; 234 | fifo.pop_front(); 235 | } 236 | if (!speculum_verify()) { 237 | log_notice("ttl: bailout after head-grow"); 238 | return false; 239 | } 240 | loops += 1; 241 | } else if (fifo.empty()) { 242 | log_notice("ttl: done %u whole loops, %" PRIu64 " ops, %" PRIu64 " items", 243 | loops, nops_completed, serial); 244 | rc = true; 245 | break; 246 | } else { 247 | log_notice("ttl: done, wait for empty, skip head-grow"); 248 | } 249 | } 250 | 251 | bailout: 252 | if (!rc && err == MDBX_MAP_FULL && config.params.ignore_dbfull) 253 | rc = true; 254 | txn_end(true); 255 | if (dbi) { 256 | if (config.params.drop_table && !mode_readonly()) { 257 | txn_begin(false); 258 | db_table_drop(dbi); 259 | err = breakable_commit(); 260 | if (unlikely(err != MDBX_SUCCESS)) { 261 | log_notice("ttl: bailout-clean due '%s'", mdbx_strerror(err)); 262 | if (err != MDBX_MAP_FULL || !config.params.ignore_dbfull) 263 | rc = false; 264 | } 265 | } else 266 | db_table_close(dbi); 267 | } 268 | return rc; 269 | } 270 | -------------------------------------------------------------------------------- /test/utils.c++: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2023 Leonid Yuriev 3 | * and other libmdbx authors: please see AUTHORS file. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | 15 | #include "test.h++" 16 | #include 17 | #if defined(HAVE_IEEE754_H) || __has_include() 18 | #include 19 | #endif 20 | #if defined(__APPLE__) || defined(__MACH__) 21 | #include 22 | #endif /* defined(__APPLE__) || defined(__MACH__) */ 23 | 24 | std::string format(const char *fmt, ...) { 25 | va_list ap, ones; 26 | va_start(ap, fmt); 27 | va_copy(ones, ap); 28 | #ifdef _MSC_VER 29 | int needed = _vscprintf(fmt, ap); 30 | #else 31 | int needed = vsnprintf(nullptr, 0, fmt, ap); 32 | #endif 33 | assert(needed >= 0); 34 | va_end(ap); 35 | std::string result; 36 | result.reserve((size_t)needed + 1); 37 | result.resize((size_t)needed, '\0'); 38 | MDBX_MAYBE_UNUSED int actual = 39 | vsnprintf((char *)result.data(), result.capacity(), fmt, ones); 40 | assert(actual == needed); 41 | (void)actual; 42 | va_end(ones); 43 | return result; 44 | } 45 | 46 | std::string data2hex(const void *ptr, size_t bytes, simple_checksum &checksum) { 47 | std::string result; 48 | if (bytes > 0) { 49 | const uint8_t *data = (const uint8_t *)ptr; 50 | checksum.push(data, bytes); 51 | result.reserve(bytes * 2); 52 | const uint8_t *const end = data + bytes; 53 | do { 54 | char h = *data >> 4; 55 | char l = *data & 15; 56 | result.push_back((l < 10) ? l + '0' : l - 10 + 'a'); 57 | result.push_back((h < 10) ? h + '0' : h - 10 + 'a'); 58 | } while (++data < end); 59 | } 60 | assert(result.size() == bytes * 2); 61 | return result; 62 | } 63 | 64 | bool hex2data(const char *hex_begin, const char *hex_end, void *ptr, 65 | size_t bytes, simple_checksum &checksum) { 66 | if (bytes * 2 != (size_t)(hex_end - hex_begin)) 67 | return false; 68 | 69 | uint8_t *data = (uint8_t *)ptr; 70 | for (const char *hex = hex_begin; hex != hex_end; hex += 2, ++data) { 71 | unsigned l = hex[0], h = hex[1]; 72 | 73 | if (l >= '0' && l <= '9') 74 | l = l - '0'; 75 | else if (l >= 'A' && l <= 'F') 76 | l = l - 'A' + 10; 77 | else if (l >= 'a' && l <= 'f') 78 | l = l - 'a' + 10; 79 | else 80 | return false; 81 | 82 | if (h >= '0' && h <= '9') 83 | h = h - '0'; 84 | else if (h >= 'A' && h <= 'F') 85 | h = h - 'A' + 10; 86 | else if (h >= 'a' && h <= 'f') 87 | h = h - 'a' + 10; 88 | else 89 | return false; 90 | 91 | uint32_t c = l + (h << 4); 92 | checksum.push(c); 93 | *data = (uint8_t)c; 94 | } 95 | return true; 96 | } 97 | 98 | bool is_samedata(const MDBX_val *a, const MDBX_val *b) { 99 | return a->iov_len == b->iov_len && 100 | memcmp(a->iov_base, b->iov_base, a->iov_len) == 0; 101 | } 102 | 103 | //----------------------------------------------------------------------------- 104 | 105 | uint64_t prng64_white(uint64_t &state) { 106 | state = prng64_map2_careless(state); 107 | return bleach64(state); 108 | } 109 | 110 | uint32_t prng32(uint64_t &state) { 111 | return (uint32_t)(prng64_careless(state) >> 32); 112 | } 113 | 114 | void prng_fill(uint64_t &state, void *ptr, size_t bytes) { 115 | uint32_t u32 = prng32(state); 116 | 117 | while (bytes >= 4) { 118 | memcpy(ptr, &u32, 4); 119 | ptr = (uint32_t *)ptr + 1; 120 | bytes -= 4; 121 | u32 = prng32(state); 122 | } 123 | 124 | switch (bytes & 3) { 125 | case 3: 126 | memcpy(ptr, &u32, 3); 127 | break; 128 | case 2: 129 | memcpy(ptr, &u32, 2); 130 | break; 131 | case 1: 132 | memcpy(ptr, &u32, 1); 133 | break; 134 | case 0: 135 | break; 136 | } 137 | } 138 | 139 | static __thread uint64_t prng_state; 140 | 141 | void prng_seed(uint64_t seed) { prng_state = bleach64(seed); } 142 | 143 | uint32_t prng32(void) { return prng32(prng_state); } 144 | 145 | uint64_t prng64(void) { return prng64_white(prng_state); } 146 | 147 | void prng_fill(void *ptr, size_t bytes) { prng_fill(prng_state, ptr, bytes); } 148 | 149 | double double_from_lower(uint64_t salt) { 150 | #ifdef IEEE754_DOUBLE_BIAS 151 | ieee754_double r; 152 | r.ieee.negative = 0; 153 | r.ieee.exponent = IEEE754_DOUBLE_BIAS; 154 | r.ieee.mantissa0 = (unsigned)(salt >> 32); 155 | r.ieee.mantissa1 = (unsigned)salt; 156 | return r.d; 157 | #else 158 | const uint64_t top = (UINT64_C(1) << DBL_MANT_DIG) - 1; 159 | const double scale = 1.0 / (double)top; 160 | return (salt & top) * scale; 161 | #endif 162 | } 163 | 164 | double double_from_upper(uint64_t salt) { 165 | #ifdef IEEE754_DOUBLE_BIAS 166 | ieee754_double r; 167 | r.ieee.negative = 0; 168 | r.ieee.exponent = IEEE754_DOUBLE_BIAS; 169 | salt >>= 64 - DBL_MANT_DIG; 170 | r.ieee.mantissa0 = unsigned(salt >> 32); 171 | r.ieee.mantissa1 = unsigned(salt); 172 | return r.d; 173 | #else 174 | const uint64_t top = (UINT64_C(1) << DBL_MANT_DIG) - 1; 175 | const double scale = 1.0 / (double)top; 176 | return (salt >> (64 - DBL_MANT_DIG)) * scale; 177 | #endif 178 | } 179 | 180 | bool flipcoin() { return prng32() & 1; } 181 | bool flipcoin_x2() { return (prng32() & 3) == 0; } 182 | bool flipcoin_x3() { return (prng32() & 7) == 0; } 183 | bool flipcoin_x4() { return (prng32() & 15) == 0; } 184 | bool flipcoin_n(unsigned n) { 185 | return (prng64() & ((UINT64_C(1) << n) - 1)) == 0; 186 | } 187 | 188 | bool jitter(unsigned probability_percent) { 189 | const uint32_t top = UINT32_MAX - UINT32_MAX % 100; 190 | uint32_t dice, edge = (top) / 100 * probability_percent; 191 | do 192 | dice = prng32(); 193 | while (dice >= top); 194 | return dice < edge; 195 | } 196 | 197 | void jitter_delay(bool extra) { 198 | unsigned dice = prng32() & 3; 199 | if (dice == 0) { 200 | log_trace("== jitter.no-delay"); 201 | } else { 202 | log_trace(">> jitter.delay: dice %u", dice); 203 | do { 204 | cpu_relax(); 205 | memory_barrier(); 206 | cpu_relax(); 207 | if (dice > 1) { 208 | osal_yield(); 209 | cpu_relax(); 210 | if (dice > 2) { 211 | size_t us = 212 | prng32() & (extra ? 0xffff /* 656 ms */ : 0x3ff /* 1 ms */); 213 | log_trace("== jitter.delay: %0.6f", us / 1000000.0); 214 | osal_udelay(us); 215 | } 216 | } 217 | } while (flipcoin()); 218 | log_trace("<< jitter.delay: dice %u", dice); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /test/utils.h++: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2023 Leonid Yuriev 3 | * and other libmdbx authors: please see AUTHORS file. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | 15 | #pragma once 16 | #include "base.h++" 17 | 18 | #if !defined(__BYTE_ORDER__) || !defined(__ORDER_LITTLE_ENDIAN__) || \ 19 | !defined(__ORDER_BIG_ENDIAN__) 20 | #error __BYTE_ORDER__ should be defined. 21 | #endif 22 | 23 | #if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ && \ 24 | __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__ 25 | #error Unsupported byte order. 26 | #endif 27 | 28 | #if __GNUC_PREREQ(4, 4) || defined(__clang__) 29 | #ifndef bswap64 30 | #define bswap64(v) __builtin_bswap64(v) 31 | #endif 32 | #ifndef bswap32 33 | #define bswap32(v) __builtin_bswap32(v) 34 | #endif 35 | #if (__GNUC_PREREQ(4, 8) || __has_builtin(__builtin_bswap16)) && \ 36 | !defined(bswap16) 37 | #define bswap16(v) __builtin_bswap16(v) 38 | #endif 39 | 40 | #elif defined(_MSC_VER) 41 | 42 | #if _MSC_FULL_VER < 190024215 43 | #pragma message( \ 44 | "It is recommended to use Visual Studio 2015 (MSC 19.0) or newer.") 45 | #endif 46 | 47 | #define bswap64(v) _byteswap_uint64(v) 48 | #define bswap32(v) _byteswap_ulong(v) 49 | #define bswap16(v) _byteswap_ushort(v) 50 | #define rot64(v, s) _rotr64(v, s) 51 | #define rot32(v, s) _rotr(v, s) 52 | 53 | #if defined(_M_X64) || defined(_M_IA64) 54 | #pragma intrinsic(_umul128) 55 | #define mul_64x64_128(a, b, ph) _umul128(a, b, ph) 56 | #pragma intrinsic(__umulh) 57 | #define mul_64x64_high(a, b) __umulh(a, b) 58 | #endif 59 | 60 | #if defined(_M_IX86) 61 | #pragma intrinsic(__emulu) 62 | #define mul_32x32_64(a, b) __emulu(a, b) 63 | #elif defined(_M_ARM) 64 | #define mul_32x32_64(a, b) _arm_umull(a, b) 65 | #endif 66 | 67 | #endif /* compiler */ 68 | 69 | #ifndef bswap64 70 | #ifdef __bswap_64 71 | #define bswap64(v) __bswap_64(v) 72 | #else 73 | static __inline uint64_t bswap64(uint64_t v) { 74 | return v << 56 | v >> 56 | ((v << 40) & UINT64_C(0x00ff000000000000)) | 75 | ((v << 24) & UINT64_C(0x0000ff0000000000)) | 76 | ((v << 8) & UINT64_C(0x000000ff00000000)) | 77 | ((v >> 8) & UINT64_C(0x00000000ff0000000)) | 78 | ((v >> 24) & UINT64_C(0x0000000000ff0000)) | 79 | ((v >> 40) & UINT64_C(0x000000000000ff00)); 80 | } 81 | #endif 82 | #endif /* bswap64 */ 83 | 84 | #ifndef bswap32 85 | #ifdef __bswap_32 86 | #define bswap32(v) __bswap_32(v) 87 | #else 88 | static __inline uint32_t bswap32(uint32_t v) { 89 | return v << 24 | v >> 24 | ((v << 8) & UINT32_C(0x00ff0000)) | 90 | ((v >> 8) & UINT32_C(0x0000ff00)); 91 | } 92 | #endif 93 | #endif /* bswap32 */ 94 | 95 | #ifndef bswap16 96 | #ifdef __bswap_16 97 | #define bswap16(v) __bswap_16(v) 98 | #else 99 | static __inline uint16_t bswap16(uint16_t v) { return v << 8 | v >> 8; } 100 | #endif 101 | #endif /* bswap16 */ 102 | 103 | #define is_byteorder_le() (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) 104 | #define is_byteorder_be() (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) 105 | 106 | #ifndef htole16 107 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 108 | #define htobe16(v) bswap16(v) 109 | #define htole16(v) (v) 110 | #define be16toh(v) bswap16(v) 111 | #define le16toh(v) (v) 112 | #else 113 | #define htobe16(v) (v) 114 | #define htole16(v) bswap16(v) 115 | #define be16toh(v) (v) 116 | #define le16toh(v) bswap16(v) 117 | #endif 118 | #endif /* htole16 */ 119 | 120 | #ifndef htole32 121 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 122 | #define htobe32(v) bswap32(v) 123 | #define htole32(v) (v) 124 | #define be32toh(v) bswap32(v) 125 | #define le32toh(v) (v) 126 | #else 127 | #define htobe32(v) (v) 128 | #define htole32(v) bswap32(v) 129 | #define be32toh(v) (v) 130 | #define le32toh(v) bswap32(v) 131 | #endif 132 | #endif /* htole32 */ 133 | 134 | #ifndef htole64 135 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 136 | #define htobe64(v) bswap64(v) 137 | #define htole64(v) (v) 138 | #define be64toh(v) bswap64(v) 139 | #define le64toh(v) (v) 140 | #else 141 | #define htobe64(v) (v) 142 | #define htole64(v) bswap_64(v) 143 | #define be64toh(v) (v) 144 | #define le64toh(v) bswap_64(v) 145 | #endif 146 | #endif /* htole64 */ 147 | 148 | namespace unaligned { 149 | 150 | template static __inline T load(const void *ptr) { 151 | if (MDBX_UNALIGNED_OK >= sizeof(T)) 152 | return *(const T *)ptr; 153 | else { 154 | #if defined(__unaligned) || defined(_M_ARM) || defined(_M_ARM64) || \ 155 | defined(_M_X64) || defined(_M_IA64) 156 | return *(const T __unaligned *)ptr; 157 | #else 158 | T local; 159 | memcpy(&local, (const T *)ptr, sizeof(T)); 160 | return local; 161 | #endif /* _MSC_VER || __unaligned */ 162 | } 163 | } 164 | 165 | template static __inline void store(void *ptr, const T &value) { 166 | if (MDBX_UNALIGNED_OK >= sizeof(T)) 167 | *(T *)ptr = value; 168 | else { 169 | #if defined(__unaligned) || defined(_M_ARM) || defined(_M_ARM64) || \ 170 | defined(_M_X64) || defined(_M_IA64) 171 | *((T __unaligned *)ptr) = value; 172 | #else 173 | memcpy(ptr, &value, sizeof(T)); 174 | #endif /* _MSC_VER || __unaligned */ 175 | } 176 | } 177 | 178 | } /* namespace unaligned */ 179 | 180 | //----------------------------------------------------------------------------- 181 | 182 | #ifndef rot64 183 | static __inline uint64_t rot64(uint64_t v, unsigned s) { 184 | return (v >> s) | (v << (64 - s)); 185 | } 186 | #endif /* rot64 */ 187 | 188 | static __inline bool is_power2(size_t x) { return (x & (x - 1)) == 0; } 189 | 190 | #undef roundup2 191 | static __inline size_t roundup2(size_t value, size_t granularity) { 192 | assert(is_power2(granularity)); 193 | return (value + granularity - 1) & ~(granularity - 1); 194 | } 195 | 196 | //----------------------------------------------------------------------------- 197 | 198 | static __inline void memory_barrier(void) { 199 | #if __has_extension(c_atomic) || __has_extension(cxx_atomic) 200 | __c11_atomic_thread_fence(__ATOMIC_SEQ_CST); 201 | #elif defined(__ATOMIC_SEQ_CST) 202 | __atomic_thread_fence(__ATOMIC_SEQ_CST); 203 | #elif defined(__clang__) || defined(__GNUC__) 204 | __sync_synchronize(); 205 | #elif defined(_MSC_VER) 206 | MemoryBarrier(); 207 | #elif defined(__INTEL_COMPILER) /* LY: Intel Compiler may mimic GCC and MSC */ 208 | #if defined(__ia64__) || defined(__ia64) || defined(_M_IA64) 209 | __mf(); 210 | #elif defined(__ia32__) 211 | _mm_mfence(); 212 | #else 213 | #error "Unknown target for Intel Compiler, please report to us." 214 | #endif 215 | #elif defined(__SUNPRO_C) || defined(__sun) || defined(sun) 216 | __machine_rw_barrier(); 217 | #elif (defined(_HPUX_SOURCE) || defined(__hpux) || defined(__HP_aCC)) && \ 218 | (defined(HP_IA64) || defined(__ia64)) 219 | _Asm_mf(); 220 | #elif defined(_AIX) || defined(__ppc__) || defined(__powerpc__) || \ 221 | defined(__ppc64__) || defined(__powerpc64__) 222 | __lwsync(); 223 | #else 224 | #error "Could not guess the kind of compiler, please report to us." 225 | #endif 226 | } 227 | 228 | static __inline void cpu_relax() { 229 | #if defined(__ia32__) 230 | _mm_pause(); 231 | #elif defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS) || \ 232 | defined(YieldProcessor) 233 | YieldProcessor(); 234 | #else 235 | /* nope */ 236 | #endif 237 | } 238 | 239 | //----------------------------------------------------------------------------- 240 | 241 | struct simple_checksum { 242 | uint64_t value{0}; 243 | 244 | simple_checksum() = default; 245 | 246 | void push(const uint32_t &data) { 247 | value += data * UINT64_C(9386433910765580089) + 1; 248 | value ^= value >> 41; 249 | value *= UINT64_C(0xBD9CACC22C6E9571); 250 | } 251 | 252 | void push(const uint64_t &data) { 253 | push((uint32_t)data); 254 | push((uint32_t)(data >> 32)); 255 | } 256 | 257 | void push(const bool data) { 258 | push(data ? UINT32_C(0x780E) : UINT32_C(0xFA18E)); 259 | } 260 | 261 | void push(const void *ptr, size_t bytes) { 262 | const uint8_t *data = (const uint8_t *)ptr; 263 | for (size_t i = 0; i < bytes; ++i) 264 | push((uint32_t)data[i]); 265 | } 266 | 267 | void push(const double &data) { push(&data, sizeof(double)); } 268 | void push(const char *cstr) { push(cstr, strlen(cstr)); } 269 | void push(const std::string &str) { push(str.data(), str.size()); } 270 | 271 | void push(unsigned salt, const MDBX_val &val) { 272 | push(unsigned(val.iov_len)); 273 | push(salt); 274 | push(val.iov_base, val.iov_len); 275 | } 276 | 277 | #if defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS) 278 | void push(const HANDLE &handle) { push(&handle, sizeof(handle)); } 279 | #endif /* _WINDOWS */ 280 | }; 281 | 282 | std::string data2hex(const void *ptr, size_t bytes, simple_checksum &checksum); 283 | bool hex2data(const char *hex_begin, const char *hex_end, void *ptr, 284 | size_t bytes, simple_checksum &checksum); 285 | bool is_samedata(const MDBX_val *a, const MDBX_val *b); 286 | inline bool is_samedata(const MDBX_val &a, const MDBX_val &b) { 287 | return is_samedata(&a, &b); 288 | } 289 | std::string format(const char *fmt, ...); 290 | 291 | static inline uint64_t bleach64(uint64_t v) { 292 | // Tommy Ettinger, https://www.blogger.com/profile/04953541827437796598 293 | // http://mostlymangling.blogspot.com/2019/01/better-stronger-mixer-and-test-procedure.html 294 | v ^= rot64(v, 25) ^ rot64(v, 50); 295 | v *= UINT64_C(0xA24BAED4963EE407); 296 | v ^= rot64(v, 24) ^ rot64(v, 49); 297 | v *= UINT64_C(0x9FB21C651E98DF25); 298 | return v ^ v >> 28; 299 | } 300 | 301 | static inline uint32_t bleach32(uint32_t x) { 302 | // https://github.com/skeeto/hash-prospector 303 | // exact bias: 0.17353355999581582 304 | x ^= x >> 16; 305 | x *= UINT32_C(0x7feb352d); 306 | x ^= 0x3027C563 ^ (x >> 15); 307 | x *= UINT32_C(0x846ca68b); 308 | x ^= x >> 16; 309 | return x; 310 | } 311 | 312 | static inline uint64_t prng64_map1_careless(uint64_t state) { 313 | return state * UINT64_C(6364136223846793005) + 1; 314 | } 315 | 316 | static inline uint64_t prng64_map2_careless(uint64_t state) { 317 | return (state + UINT64_C(1442695040888963407)) * 318 | UINT64_C(6364136223846793005); 319 | } 320 | 321 | static inline uint64_t prng64_map1_white(uint64_t state) { 322 | return bleach64(prng64_map1_careless(state)); 323 | } 324 | 325 | static inline uint64_t prng64_map2_white(uint64_t state) { 326 | return bleach64(prng64_map2_careless(state)); 327 | } 328 | 329 | static inline uint64_t prng64_careless(uint64_t &state) { 330 | state = prng64_map1_careless(state); 331 | return state; 332 | } 333 | 334 | static inline double u64_to_double1(uint64_t v) { 335 | union { 336 | uint64_t u64; 337 | double d; 338 | } casting; 339 | 340 | casting.u64 = UINT64_C(0x3ff) << 52 | (v >> 12); 341 | assert(casting.d >= 1.0 && casting.d < 2.0); 342 | return casting.d - 1.0; 343 | } 344 | 345 | uint64_t prng64_white(uint64_t &state); 346 | uint32_t prng32(uint64_t &state); 347 | void prng_fill(uint64_t &state, void *ptr, size_t bytes); 348 | 349 | void prng_seed(uint64_t seed); 350 | uint32_t prng32(void); 351 | uint64_t prng64(void); 352 | void prng_fill(void *ptr, size_t bytes); 353 | 354 | bool flipcoin(); 355 | bool flipcoin_x2(); 356 | bool flipcoin_x3(); 357 | bool flipcoin_x4(); 358 | bool flipcoin_n(unsigned n); 359 | bool jitter(unsigned probability_percent); 360 | void jitter_delay(bool extra = false); 361 | -------------------------------------------------------------------------------- /test/valgrind_suppress.txt: -------------------------------------------------------------------------------- 1 | { 2 | msync-whole-mmap-1 3 | Memcheck:Param 4 | msync(start) 5 | fun:msync 6 | ... 7 | fun:sync_locked* 8 | } 9 | { 10 | msync-whole-mmap-2 11 | Memcheck:Param 12 | msync(start) 13 | fun:msync 14 | ... 15 | fun:env_sync* 16 | } 17 | { 18 | msync-whole-mmap-3 19 | Memcheck:Param 20 | msync(start) 21 | fun:msync 22 | ... 23 | fun:map_resize* 24 | } 25 | { 26 | msync-wipe-steady 27 | Memcheck:Param 28 | msync(start) 29 | fun:msync 30 | ... 31 | fun:wipe_steady* 32 | } 33 | { 34 | msync-meta 35 | Memcheck:Param 36 | msync(start) 37 | fun:msync 38 | ... 39 | fun:meta_sync* 40 | } 41 | { 42 | msync-spill 43 | Memcheck:Param 44 | msync(start) 45 | fun:msync 46 | ... 47 | fun:txn_spill* 48 | } 49 | 50 | # memcmp() inside iov_write() as workaround for https://libmdbx.dqdkfa.ru/dead-github/issues/269 51 | { 52 | iov-pagecheck-1 53 | Memcheck:Cond 54 | fun:bcmp 55 | fun:iov_callback4dirtypages 56 | fun:osal_ioring_walk 57 | fun:iov_complete 58 | fun:iov_write 59 | } 60 | { 61 | iov-pagecheck-2 62 | Memcheck:Cond 63 | fun:memcmp* 64 | fun:iov_callback4dirtypages 65 | fun:osal_ioring_walk 66 | fun:iov_complete 67 | fun:iov_write 68 | } 69 | 70 | # single-page flush by pwrite() 71 | { 72 | pwrite-page-flush 73 | Memcheck:Param 74 | pwrite(buf) 75 | fun:pwrite 76 | ... 77 | fun:iov_write* 78 | } 79 | { 80 | pwrite64-page-flush 81 | Memcheck:Param 82 | pwrite64(buf) 83 | fun:pwrite 84 | ... 85 | fun:iov_write* 86 | } 87 | 88 | # modern Valgrind don't support the `vector[...]` pattern 89 | #{ 90 | # pwritev-page-flush 91 | # Memcheck:Param 92 | # pwritev(vector[...]) 93 | # fun:pwritev 94 | # ... 95 | # fun:iov_write* 96 | #} 97 | # for((i=0;i<64;++i)); do echo -e "{\n pwritev-page-flush-$i\n Memcheck:Param\n pwritev(vector[$i])\n fun:pwritev\n ...\n fun:iov_write*\n}"; done >> valgrind_suppress.txt 98 | { 99 | pwritev-page-flush-0 100 | Memcheck:Param 101 | pwritev(vector[0]) 102 | fun:pwritev 103 | ... 104 | fun:iov_write* 105 | } 106 | { 107 | pwritev-page-flush-1 108 | Memcheck:Param 109 | pwritev(vector[1]) 110 | fun:pwritev 111 | ... 112 | fun:iov_write* 113 | } 114 | { 115 | pwritev-page-flush-2 116 | Memcheck:Param 117 | pwritev(vector[2]) 118 | fun:pwritev 119 | ... 120 | fun:iov_write* 121 | } 122 | { 123 | pwritev-page-flush-3 124 | Memcheck:Param 125 | pwritev(vector[3]) 126 | fun:pwritev 127 | ... 128 | fun:iov_write* 129 | } 130 | { 131 | pwritev-page-flush-4 132 | Memcheck:Param 133 | pwritev(vector[4]) 134 | fun:pwritev 135 | ... 136 | fun:iov_write* 137 | } 138 | { 139 | pwritev-page-flush-5 140 | Memcheck:Param 141 | pwritev(vector[5]) 142 | fun:pwritev 143 | ... 144 | fun:iov_write* 145 | } 146 | { 147 | pwritev-page-flush-6 148 | Memcheck:Param 149 | pwritev(vector[6]) 150 | fun:pwritev 151 | ... 152 | fun:iov_write* 153 | } 154 | { 155 | pwritev-page-flush-7 156 | Memcheck:Param 157 | pwritev(vector[7]) 158 | fun:pwritev 159 | ... 160 | fun:iov_write* 161 | } 162 | { 163 | pwritev-page-flush-8 164 | Memcheck:Param 165 | pwritev(vector[8]) 166 | fun:pwritev 167 | ... 168 | fun:iov_write* 169 | } 170 | { 171 | pwritev-page-flush-9 172 | Memcheck:Param 173 | pwritev(vector[9]) 174 | fun:pwritev 175 | ... 176 | fun:iov_write* 177 | } 178 | { 179 | pwritev-page-flush-10 180 | Memcheck:Param 181 | pwritev(vector[10]) 182 | fun:pwritev 183 | ... 184 | fun:iov_write* 185 | } 186 | { 187 | pwritev-page-flush-11 188 | Memcheck:Param 189 | pwritev(vector[11]) 190 | fun:pwritev 191 | ... 192 | fun:iov_write* 193 | } 194 | { 195 | pwritev-page-flush-12 196 | Memcheck:Param 197 | pwritev(vector[12]) 198 | fun:pwritev 199 | ... 200 | fun:iov_write* 201 | } 202 | { 203 | pwritev-page-flush-13 204 | Memcheck:Param 205 | pwritev(vector[13]) 206 | fun:pwritev 207 | ... 208 | fun:iov_write* 209 | } 210 | { 211 | pwritev-page-flush-14 212 | Memcheck:Param 213 | pwritev(vector[14]) 214 | fun:pwritev 215 | ... 216 | fun:iov_write* 217 | } 218 | { 219 | pwritev-page-flush-15 220 | Memcheck:Param 221 | pwritev(vector[15]) 222 | fun:pwritev 223 | ... 224 | fun:iov_write* 225 | } 226 | { 227 | pwritev-page-flush-16 228 | Memcheck:Param 229 | pwritev(vector[16]) 230 | fun:pwritev 231 | ... 232 | fun:iov_write* 233 | } 234 | { 235 | pwritev-page-flush-17 236 | Memcheck:Param 237 | pwritev(vector[17]) 238 | fun:pwritev 239 | ... 240 | fun:iov_write* 241 | } 242 | { 243 | pwritev-page-flush-18 244 | Memcheck:Param 245 | pwritev(vector[18]) 246 | fun:pwritev 247 | ... 248 | fun:iov_write* 249 | } 250 | { 251 | pwritev-page-flush-19 252 | Memcheck:Param 253 | pwritev(vector[19]) 254 | fun:pwritev 255 | ... 256 | fun:iov_write* 257 | } 258 | { 259 | pwritev-page-flush-20 260 | Memcheck:Param 261 | pwritev(vector[20]) 262 | fun:pwritev 263 | ... 264 | fun:iov_write* 265 | } 266 | { 267 | pwritev-page-flush-21 268 | Memcheck:Param 269 | pwritev(vector[21]) 270 | fun:pwritev 271 | ... 272 | fun:iov_write* 273 | } 274 | { 275 | pwritev-page-flush-22 276 | Memcheck:Param 277 | pwritev(vector[22]) 278 | fun:pwritev 279 | ... 280 | fun:iov_write* 281 | } 282 | { 283 | pwritev-page-flush-23 284 | Memcheck:Param 285 | pwritev(vector[23]) 286 | fun:pwritev 287 | ... 288 | fun:iov_write* 289 | } 290 | { 291 | pwritev-page-flush-24 292 | Memcheck:Param 293 | pwritev(vector[24]) 294 | fun:pwritev 295 | ... 296 | fun:iov_write* 297 | } 298 | { 299 | pwritev-page-flush-25 300 | Memcheck:Param 301 | pwritev(vector[25]) 302 | fun:pwritev 303 | ... 304 | fun:iov_write* 305 | } 306 | { 307 | pwritev-page-flush-26 308 | Memcheck:Param 309 | pwritev(vector[26]) 310 | fun:pwritev 311 | ... 312 | fun:iov_write* 313 | } 314 | { 315 | pwritev-page-flush-27 316 | Memcheck:Param 317 | pwritev(vector[27]) 318 | fun:pwritev 319 | ... 320 | fun:iov_write* 321 | } 322 | { 323 | pwritev-page-flush-28 324 | Memcheck:Param 325 | pwritev(vector[28]) 326 | fun:pwritev 327 | ... 328 | fun:iov_write* 329 | } 330 | { 331 | pwritev-page-flush-29 332 | Memcheck:Param 333 | pwritev(vector[29]) 334 | fun:pwritev 335 | ... 336 | fun:iov_write* 337 | } 338 | { 339 | pwritev-page-flush-30 340 | Memcheck:Param 341 | pwritev(vector[30]) 342 | fun:pwritev 343 | ... 344 | fun:iov_write* 345 | } 346 | { 347 | pwritev-page-flush-31 348 | Memcheck:Param 349 | pwritev(vector[31]) 350 | fun:pwritev 351 | ... 352 | fun:iov_write* 353 | } 354 | { 355 | pwritev-page-flush-32 356 | Memcheck:Param 357 | pwritev(vector[32]) 358 | fun:pwritev 359 | ... 360 | fun:iov_write* 361 | } 362 | { 363 | pwritev-page-flush-33 364 | Memcheck:Param 365 | pwritev(vector[33]) 366 | fun:pwritev 367 | ... 368 | fun:iov_write* 369 | } 370 | { 371 | pwritev-page-flush-34 372 | Memcheck:Param 373 | pwritev(vector[34]) 374 | fun:pwritev 375 | ... 376 | fun:iov_write* 377 | } 378 | { 379 | pwritev-page-flush-35 380 | Memcheck:Param 381 | pwritev(vector[35]) 382 | fun:pwritev 383 | ... 384 | fun:iov_write* 385 | } 386 | { 387 | pwritev-page-flush-36 388 | Memcheck:Param 389 | pwritev(vector[36]) 390 | fun:pwritev 391 | ... 392 | fun:iov_write* 393 | } 394 | { 395 | pwritev-page-flush-37 396 | Memcheck:Param 397 | pwritev(vector[37]) 398 | fun:pwritev 399 | ... 400 | fun:iov_write* 401 | } 402 | { 403 | pwritev-page-flush-38 404 | Memcheck:Param 405 | pwritev(vector[38]) 406 | fun:pwritev 407 | ... 408 | fun:iov_write* 409 | } 410 | { 411 | pwritev-page-flush-39 412 | Memcheck:Param 413 | pwritev(vector[39]) 414 | fun:pwritev 415 | ... 416 | fun:iov_write* 417 | } 418 | { 419 | pwritev-page-flush-40 420 | Memcheck:Param 421 | pwritev(vector[40]) 422 | fun:pwritev 423 | ... 424 | fun:iov_write* 425 | } 426 | { 427 | pwritev-page-flush-41 428 | Memcheck:Param 429 | pwritev(vector[41]) 430 | fun:pwritev 431 | ... 432 | fun:iov_write* 433 | } 434 | { 435 | pwritev-page-flush-42 436 | Memcheck:Param 437 | pwritev(vector[42]) 438 | fun:pwritev 439 | ... 440 | fun:iov_write* 441 | } 442 | { 443 | pwritev-page-flush-43 444 | Memcheck:Param 445 | pwritev(vector[43]) 446 | fun:pwritev 447 | ... 448 | fun:iov_write* 449 | } 450 | { 451 | pwritev-page-flush-44 452 | Memcheck:Param 453 | pwritev(vector[44]) 454 | fun:pwritev 455 | ... 456 | fun:iov_write* 457 | } 458 | { 459 | pwritev-page-flush-45 460 | Memcheck:Param 461 | pwritev(vector[45]) 462 | fun:pwritev 463 | ... 464 | fun:iov_write* 465 | } 466 | { 467 | pwritev-page-flush-46 468 | Memcheck:Param 469 | pwritev(vector[46]) 470 | fun:pwritev 471 | ... 472 | fun:iov_write* 473 | } 474 | { 475 | pwritev-page-flush-47 476 | Memcheck:Param 477 | pwritev(vector[47]) 478 | fun:pwritev 479 | ... 480 | fun:iov_write* 481 | } 482 | { 483 | pwritev-page-flush-48 484 | Memcheck:Param 485 | pwritev(vector[48]) 486 | fun:pwritev 487 | ... 488 | fun:iov_write* 489 | } 490 | { 491 | pwritev-page-flush-49 492 | Memcheck:Param 493 | pwritev(vector[49]) 494 | fun:pwritev 495 | ... 496 | fun:iov_write* 497 | } 498 | { 499 | pwritev-page-flush-50 500 | Memcheck:Param 501 | pwritev(vector[50]) 502 | fun:pwritev 503 | ... 504 | fun:iov_write* 505 | } 506 | { 507 | pwritev-page-flush-51 508 | Memcheck:Param 509 | pwritev(vector[51]) 510 | fun:pwritev 511 | ... 512 | fun:iov_write* 513 | } 514 | { 515 | pwritev-page-flush-52 516 | Memcheck:Param 517 | pwritev(vector[52]) 518 | fun:pwritev 519 | ... 520 | fun:iov_write* 521 | } 522 | { 523 | pwritev-page-flush-53 524 | Memcheck:Param 525 | pwritev(vector[53]) 526 | fun:pwritev 527 | ... 528 | fun:iov_write* 529 | } 530 | { 531 | pwritev-page-flush-54 532 | Memcheck:Param 533 | pwritev(vector[54]) 534 | fun:pwritev 535 | ... 536 | fun:iov_write* 537 | } 538 | { 539 | pwritev-page-flush-55 540 | Memcheck:Param 541 | pwritev(vector[55]) 542 | fun:pwritev 543 | ... 544 | fun:iov_write* 545 | } 546 | { 547 | pwritev-page-flush-56 548 | Memcheck:Param 549 | pwritev(vector[56]) 550 | fun:pwritev 551 | ... 552 | fun:iov_write* 553 | } 554 | { 555 | pwritev-page-flush-57 556 | Memcheck:Param 557 | pwritev(vector[57]) 558 | fun:pwritev 559 | ... 560 | fun:iov_write* 561 | } 562 | { 563 | pwritev-page-flush-58 564 | Memcheck:Param 565 | pwritev(vector[58]) 566 | fun:pwritev 567 | ... 568 | fun:iov_write* 569 | } 570 | { 571 | pwritev-page-flush-59 572 | Memcheck:Param 573 | pwritev(vector[59]) 574 | fun:pwritev 575 | ... 576 | fun:iov_write* 577 | } 578 | { 579 | pwritev-page-flush-60 580 | Memcheck:Param 581 | pwritev(vector[60]) 582 | fun:pwritev 583 | ... 584 | fun:iov_write* 585 | } 586 | { 587 | pwritev-page-flush-61 588 | Memcheck:Param 589 | pwritev(vector[61]) 590 | fun:pwritev 591 | ... 592 | fun:iov_write* 593 | } 594 | { 595 | pwritev-page-flush-62 596 | Memcheck:Param 597 | pwritev(vector[62]) 598 | fun:pwritev 599 | ... 600 | fun:iov_write* 601 | } 602 | { 603 | pwritev-page-flush-63 604 | Memcheck:Param 605 | pwritev(vector[63]) 606 | fun:pwritev 607 | ... 608 | fun:iov_write* 609 | } 610 | --------------------------------------------------------------------------------