diff options
author | George Hazan <ghazan@miranda.im> | 2019-06-23 12:11:35 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2019-06-23 12:11:35 +0300 |
commit | ede0e5088aa37d1ef710a7bf74ccead5140c9117 (patch) | |
tree | 984ec1470c0c83653e8871953b4b39806fee2b99 /libs/libmdbx/src | |
parent | 3632205b947823fa049e91b86a5611e9bbb59673 (diff) |
libmdbx: merge
Diffstat (limited to 'libs/libmdbx/src')
-rw-r--r-- | libs/libmdbx/src/.travis.yml | 5 | ||||
-rw-r--r-- | libs/libmdbx/src/Makefile | 4 | ||||
-rw-r--r-- | libs/libmdbx/src/libmdbx.files | 1 | ||||
-rw-r--r-- | libs/libmdbx/src/mdbx.h | 7 | ||||
-rw-r--r-- | libs/libmdbx/src/src/bits.h | 4 | ||||
-rw-r--r-- | libs/libmdbx/src/src/defs.h | 15 | ||||
-rw-r--r-- | libs/libmdbx/src/src/mdbx.c | 281 | ||||
-rw-r--r-- | libs/libmdbx/src/src/tools/mdbx_chk.c | 174 | ||||
-rw-r--r-- | libs/libmdbx/src/src/version.c | 8 | ||||
-rw-r--r-- | libs/libmdbx/src/test/CMakeLists.txt | 1 | ||||
-rw-r--r-- | libs/libmdbx/src/test/append.cc | 29 | ||||
-rw-r--r-- | libs/libmdbx/src/test/cases.cc | 1 | ||||
-rw-r--r-- | libs/libmdbx/src/test/config.h | 3 | ||||
-rw-r--r-- | libs/libmdbx/src/test/gc.sh | 6 | ||||
-rw-r--r-- | libs/libmdbx/src/test/keygen.cc | 4 | ||||
-rw-r--r-- | libs/libmdbx/src/test/keygen.h | 4 | ||||
-rw-r--r-- | libs/libmdbx/src/test/main.cc | 11 | ||||
-rw-r--r-- | libs/libmdbx/src/test/test.cc | 53 | ||||
-rw-r--r-- | libs/libmdbx/src/test/test.h | 8 | ||||
-rw-r--r-- | libs/libmdbx/src/test/test.vcxproj | 1 | ||||
-rw-r--r-- | libs/libmdbx/src/test/ttl.cc | 137 | ||||
-rw-r--r-- | libs/libmdbx/src/test/utils.cc | 15 | ||||
-rw-r--r-- | libs/libmdbx/src/test/utils.h | 57 |
23 files changed, 579 insertions, 250 deletions
diff --git a/libs/libmdbx/src/.travis.yml b/libs/libmdbx/src/.travis.yml index 590944bb91..9359084060 100644 --- a/libs/libmdbx/src/.travis.yml +++ b/libs/libmdbx/src/.travis.yml @@ -1,6 +1,5 @@ language: c -sudo: required -dist: trusty +dist: xenial compiler: - gcc @@ -26,5 +25,5 @@ addons: description: "Build submitted via Travis CI" notification_email: leo@yuriev.ru build_command_prepend: "make clean" - build_command: "make all -j 4" + build_command: "make all -j 2" branch_pattern: coverity_scan diff --git a/libs/libmdbx/src/Makefile b/libs/libmdbx/src/Makefile index 4faaaae525..145b7fadcc 100644 --- a/libs/libmdbx/src/Makefile +++ b/libs/libmdbx/src/Makefile @@ -82,11 +82,11 @@ clean: rm -rf $(TOOLS) mdbx_test @* *.[ao] *.[ls]o *~ tmp.db/* *.gcov *.log *.err src/*.o test/*.o check: all - rm -f $(TESTDB) $(TESTLOG) && (set -o pipefail; ./mdbx_test --pathname=$(TESTDB) --dont-cleanup-after basic | tee -a $(TESTLOG) | tail -n 42) \ + rm -f $(TESTDB) $(TESTLOG) && (set -o pipefail; ./mdbx_test --repeat=42 --pathname=$(TESTDB) --dont-cleanup-after basic | tee -a $(TESTLOG) | tail -n 42) \ && ./mdbx_chk -vvn $(TESTDB) && ./mdbx_chk -vvn $(TESTDB)-copy check-singleprocess: all - rm -f $(TESTDB) $(TESTLOG) && (set -o pipefail; ./mdbx_test --pathname=$(TESTDB) --dont-cleanup-after --hill --copy | tee -a $(TESTLOG) | tail -n 42) \ + rm -f $(TESTDB) $(TESTLOG) && (set -o pipefail; ./mdbx_test --repeat=42 --pathname=$(TESTDB) --dont-cleanup-after --hill --copy | tee -a $(TESTLOG) | tail -n 42) \ && ./mdbx_chk -vvn $(TESTDB) && ./mdbx_chk -vvn $(TESTDB)-copy check-fault: all diff --git a/libs/libmdbx/src/libmdbx.files b/libs/libmdbx/src/libmdbx.files index 76ea29fb5a..f147554a2a 100644 --- a/libs/libmdbx/src/libmdbx.files +++ b/libs/libmdbx/src/libmdbx.files @@ -5,6 +5,7 @@ src/tools/CMakeLists.txt test/CMakeLists.txt test/append.cc test/copy.cc +test/ttl.cc tutorial/CMakeLists.txt tutorial/sample-mdbx.c AUTHORS diff --git a/libs/libmdbx/src/mdbx.h b/libs/libmdbx/src/mdbx.h index 3b76bacc7d..8f3b1575dc 100644 --- a/libs/libmdbx/src/mdbx.h +++ b/libs/libmdbx/src/mdbx.h @@ -173,7 +173,7 @@ typedef pthread_t mdbx_tid_t; /*--------------------------------------------------------------------------*/ #define MDBX_VERSION_MAJOR 0 -#define MDBX_VERSION_MINOR 2 +#define MDBX_VERSION_MINOR 3 #if defined(LIBMDBX_EXPORTS) #define LIBMDBX_API __dll_export @@ -1671,6 +1671,10 @@ typedef enum { MDBX_subpage_dupfixed_leaf } MDBX_page_type_t; +#define MDBX_PGWALK_MAIN ((const char *)((ptrdiff_t)0)) +#define MDBX_PGWALK_GC ((const char *)((ptrdiff_t)-1)) +#define MDBX_PGWALK_META ((const char *)((ptrdiff_t)-2)) + typedef int MDBX_pgvisitor_func(uint64_t pgno, unsigned number, void *ctx, int deep, const char *dbi, size_t page_size, MDBX_page_type_t type, size_t nentries, @@ -1742,6 +1746,7 @@ LIBMDBX_API int mdbx_estimate_move(const MDBX_cursor *cursor, MDBX_val *key, * [out] distance_items A pointer to store range estimation result. * * Returns A non-zero error value on failure and 0 on success. */ +#define MDBX_EPSILON ((MDBX_val *)((ptrdiff_t)-1)) LIBMDBX_API int mdbx_estimate_range(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *begin_key, MDBX_val *begin_data, MDBX_val *end_key, MDBX_val *end_data, diff --git a/libs/libmdbx/src/src/bits.h b/libs/libmdbx/src/src/bits.h index 3325c327ae..ad335b6b65 100644 --- a/libs/libmdbx/src/src/bits.h +++ b/libs/libmdbx/src/src/bits.h @@ -1155,7 +1155,7 @@ typedef struct MDBX_node { #define LEAF2KEY(p, i, ks) ((char *)(p) + PAGEHDRSZ + ((i) * (ks))) /* Set the node's key into keyptr, if requested. */ -#define MDBX_GET_KEY(node, keyptr) \ +#define MDBX_GET_MAYNULL_KEYPTR(node, keyptr) \ do { \ if ((keyptr) != NULL) { \ (keyptr)->iov_len = NODEKSZ(node); \ @@ -1164,7 +1164,7 @@ typedef struct MDBX_node { } while (0) /* Set the node's key into key. */ -#define MDBX_GET_KEY2(node, key) \ +#define MDBX_GET_KEYVALUE(node, key) \ do { \ key.iov_len = NODEKSZ(node); \ key.iov_base = NODEKEY(node); \ diff --git a/libs/libmdbx/src/src/defs.h b/libs/libmdbx/src/src/defs.h index 024322a1f2..a672b8bdf0 100644 --- a/libs/libmdbx/src/src/defs.h +++ b/libs/libmdbx/src/src/defs.h @@ -303,7 +303,7 @@ #endif /* __flatten */ #ifndef likely -# if defined(__GNUC__) || defined(__clang__) +# if (defined(__GNUC__) || defined(__clang__)) && !defined(__COVERITY__) # define likely(cond) __builtin_expect(!!(cond), 1) # else # define likely(x) (x) @@ -311,13 +311,24 @@ #endif /* likely */ #ifndef unlikely -# if defined(__GNUC__) || defined(__clang__) +# if (defined(__GNUC__) || defined(__clang__)) && !defined(__COVERITY__) # define unlikely(cond) __builtin_expect(!!(cond), 0) # else # define unlikely(x) (x) # endif #endif /* unlikely */ +/* Workaround for Coverity Scan */ +#if defined(__COVERITY__) && __GNUC_PREREQ(7, 0) && !defined(__cplusplus) +typedef float _Float32; +typedef double _Float32x; +typedef double _Float64; +typedef long double _Float64x; +typedef float _Float128 __attribute__((__mode__(__TF__))); +typedef __complex__ float __cfloat128 __attribute__ ((__mode__ (__TC__))); +typedef _Complex float __cfloat128 __attribute__ ((__mode__ (__TC__))); +#endif /* Workaround for Coverity Scan */ + /* Wrapper around __func__, which is a C99 feature */ #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L # define mdbx_func_ __func__ diff --git a/libs/libmdbx/src/src/mdbx.c b/libs/libmdbx/src/src/mdbx.c index 84b2c1b125..7a5e73b7f1 100644 --- a/libs/libmdbx/src/src/mdbx.c +++ b/libs/libmdbx/src/src/mdbx.c @@ -3757,6 +3757,7 @@ static int mdbx_prep_backlog(MDBX_txn *txn, MDBX_cursor *mc) { const int extra = mdbx_backlog_extragap(txn->mt_env); if (mdbx_backlog_size(txn) < mc->mc_db->md_depth + extra) { + mc->mc_flags &= ~C_RECLAIMING; int rc = mdbx_cursor_touch(mc); if (unlikely(rc)) return rc; @@ -3770,6 +3771,7 @@ static int mdbx_prep_backlog(MDBX_txn *txn, MDBX_cursor *mc) { break; } } + mc->mc_flags |= C_RECLAIMING; } return MDBX_SUCCESS; @@ -3882,6 +3884,7 @@ static int mdbx_update_gc(MDBX_txn *txn) { if (unlikely(rc != MDBX_SUCCESS)) goto bailout_notracking; + mc.mc_flags |= C_RECLAIMING; mc.mc_next = txn->mt_cursors[FREE_DBI]; txn->mt_cursors[FREE_DBI] = &mc; @@ -3927,9 +3930,7 @@ retry: mdbx_tassert(txn, cleaned_gc_id < *env->me_oldest); mdbx_trace("%s.cleanup-reclaimed-id [%u]%" PRIaTXN, dbg_prefix_mode, cleaned_gc_slot, cleaned_gc_id); - mc.mc_flags |= C_RECLAIMING; rc = mdbx_cursor_del(&mc, 0); - mc.mc_flags ^= C_RECLAIMING; if (unlikely(rc != MDBX_SUCCESS)) goto bailout; } while (cleaned_gc_slot < MDBX_PNL_SIZE(txn->mt_lifo_reclaimed)); @@ -3951,9 +3952,7 @@ retry: mdbx_tassert(txn, cleaned_gc_id < *env->me_oldest); mdbx_trace("%s.cleanup-reclaimed-id %" PRIaTXN, dbg_prefix_mode, cleaned_gc_id); - mc.mc_flags |= C_RECLAIMING; rc = mdbx_cursor_del(&mc, 0); - mc.mc_flags ^= C_RECLAIMING; if (unlikely(rc != MDBX_SUCCESS)) goto bailout; settled = 0; @@ -4106,7 +4105,9 @@ retry: if (befree_stored < MDBX_PNL_SIZE(txn->mt_befree_pages)) { if (unlikely(!befree_stored)) { /* Make sure last page of freeDB is touched and on befree-list */ + mc.mc_flags &= ~C_RECLAIMING; rc = mdbx_page_search(&mc, NULL, MDBX_PS_LAST | MDBX_PS_MODIFY); + mc.mc_flags |= C_RECLAIMING; if (unlikely(rc != MDBX_SUCCESS && rc != MDBX_NOTFOUND)) goto bailout; } @@ -4200,7 +4201,9 @@ retry: reused_gc_slot) * env->me_maxgc_ov1page) { /* LY: need just a txn-id for save page list. */ + mc.mc_flags &= ~C_RECLAIMING; rc = mdbx_page_alloc(&mc, 0, NULL, MDBX_ALLOC_GC | MDBX_ALLOC_KICK); + mc.mc_flags |= C_RECLAIMING; if (likely(rc == MDBX_SUCCESS)) { /* LY: ok, reclaimed from freedb. */ mdbx_trace("%s: took @%" PRIaTXN " from GC, continue", @@ -4414,7 +4417,7 @@ retry: key.iov_len = sizeof(fill_gc_id); mdbx_tassert(txn, data.iov_len >= sizeof(pgno_t) * 2); - mc.mc_flags |= C_RECLAIMING | C_GCFREEZE; + mc.mc_flags |= C_GCFREEZE; unsigned chunk = (unsigned)(data.iov_len / sizeof(pgno_t)) - 1; if (unlikely(chunk > left)) { mdbx_trace("%s: chunk %u > left %u, @%" PRIaTXN, dbg_prefix_mode, chunk, @@ -4422,12 +4425,12 @@ retry: if (loop < 5 || chunk - left > env->me_maxgc_ov1page) { data.iov_len = (left + 1) * sizeof(pgno_t); if (loop < 21) - mc.mc_flags -= C_GCFREEZE; + mc.mc_flags &= ~C_GCFREEZE; } chunk = left; } rc = mdbx_cursor_put(&mc, &key, &data, MDBX_CURRENT | MDBX_RESERVE); - mc.mc_flags &= ~(C_RECLAIMING | C_GCFREEZE); + mc.mc_flags &= ~C_GCFREEZE; if (unlikely(rc != MDBX_SUCCESS)) goto bailout; @@ -5097,7 +5100,7 @@ static int __cold mdbx_read_header(MDBX_env *env, MDBX_meta *meta, /* LY: check mapsize limits */ const uint64_t mapsize_min = page.mp_meta.mm_geo.lower * (uint64_t)page.mp_meta.mm_psize; - STATIC_ASSERT(MAX_MAPSIZE < SSIZE_MAX - MAX_PAGESIZE); + STATIC_ASSERT(MAX_MAPSIZE < PTRDIFF_MAX - MAX_PAGESIZE); STATIC_ASSERT(MIN_MAPSIZE < MAX_MAPSIZE); if (mapsize_min < MIN_MAPSIZE || mapsize_min > MAX_MAPSIZE) { mdbx_notice("meta[%u] has invalid min-mapsize (%" PRIu64 "), skip it", @@ -5511,7 +5514,7 @@ int __cold mdbx_env_get_maxkeysize(MDBX_env *env) { (((pagesize)-PAGEHDRSZ) / sizeof(pgno_t) - 1) static void __cold mdbx_setup_pagesize(MDBX_env *env, const size_t pagesize) { - STATIC_ASSERT(SSIZE_MAX > MAX_MAPSIZE); + STATIC_ASSERT(PTRDIFF_MAX > MAX_MAPSIZE); STATIC_ASSERT(MIN_PAGESIZE > sizeof(MDBX_page)); mdbx_ensure(env, mdbx_is_power2(pagesize)); mdbx_ensure(env, pagesize >= MIN_PAGESIZE); @@ -6345,7 +6348,7 @@ static int __cold mdbx_setup_lck(MDBX_env *env, char *lck_pathname, } else { if (env->me_flags & MDBX_EXCLUSIVE) return MDBX_BUSY; - if (size > SSIZE_MAX || (size & (env->me_os_psize - 1)) || + if (size > PTRDIFF_MAX || (size & (env->me_os_psize - 1)) || size < env->me_os_psize) { mdbx_notice("lck-file has invalid size %" PRIu64 " bytes", size); return MDBX_PROBLEM; @@ -7280,14 +7283,14 @@ static int mdbx_ovpage_free(MDBX_cursor *mc, MDBX_page *mp) { mdbx_cassert(mc, a > MDBX_PNL_SIZE(env->me_reclaimed_pglist) || env->me_reclaimed_pglist[a] != pg); if (a <= MDBX_PNL_SIZE(env->me_reclaimed_pglist) && - env->me_reclaimed_pglist[a] == pg) + unlikely(env->me_reclaimed_pglist[a] == pg)) return MDBX_PROBLEM; if (ovpages > 1) { const unsigned b = mdbx_pnl_search(env->me_reclaimed_pglist, pg + ovpages - 1); mdbx_cassert(mc, a == b); - if (a != b) + if (unlikely(a != b)) return MDBX_PROBLEM; } } @@ -7547,7 +7550,7 @@ static int mdbx_cursor_next(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, mdbx_cursor_next(&mc->mc_xcursor->mx_cursor, data, NULL, MDBX_NEXT); if (op != MDBX_NEXT || rc != MDBX_NOTFOUND) { if (likely(rc == MDBX_SUCCESS)) - MDBX_GET_KEY(leaf, key); + MDBX_GET_MAYNULL_KEYPTR(leaf, key); return rc; } } @@ -7583,8 +7586,10 @@ skip: mp->mp_pgno, NUMKEYS(mp), mc->mc_ki[mc->mc_top]); if (IS_LEAF2(mp)) { - key->iov_len = mc->mc_db->md_xsize; - key->iov_base = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], key->iov_len); + if (likely(key)) { + key->iov_len = mc->mc_db->md_xsize; + key->iov_base = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], key->iov_len); + } return MDBX_SUCCESS; } @@ -7607,7 +7612,7 @@ skip: } } - MDBX_GET_KEY(leaf, key); + MDBX_GET_MAYNULL_KEYPTR(leaf, key); return MDBX_SUCCESS; } @@ -7638,7 +7643,7 @@ static int mdbx_cursor_prev(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, mdbx_cursor_prev(&mc->mc_xcursor->mx_cursor, data, NULL, MDBX_PREV); if (op != MDBX_PREV || rc != MDBX_NOTFOUND) { if (likely(rc == MDBX_SUCCESS)) { - MDBX_GET_KEY(leaf, key); + MDBX_GET_MAYNULL_KEYPTR(leaf, key); mc->mc_flags &= ~C_EOF; } return rc; @@ -7673,8 +7678,10 @@ static int mdbx_cursor_prev(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, mp->mp_pgno, NUMKEYS(mp), mc->mc_ki[mc->mc_top]); if (IS_LEAF2(mp)) { - key->iov_len = mc->mc_db->md_xsize; - key->iov_base = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], key->iov_len); + if (likely(key)) { + key->iov_len = mc->mc_db->md_xsize; + key->iov_base = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], key->iov_len); + } return MDBX_SUCCESS; } @@ -7697,7 +7704,7 @@ static int mdbx_cursor_prev(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, } } - MDBX_GET_KEY(leaf, key); + MDBX_GET_MAYNULL_KEYPTR(leaf, key); return MDBX_SUCCESS; } @@ -7734,7 +7741,7 @@ __hot static int mdbx_cursor_set(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, nodekey.iov_base = LEAF2KEY(mp, 0, nodekey.iov_len); } else { leaf = NODEPTR(mp, 0); - MDBX_GET_KEY2(leaf, nodekey); + MDBX_GET_KEYVALUE(leaf, nodekey); } rc = mc->mc_dbx->md_cmp(key, &nodekey); if (unlikely(rc == 0)) { @@ -7753,7 +7760,7 @@ __hot static int mdbx_cursor_set(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, nodekey.iov_base = LEAF2KEY(mp, nkeys - 1, nodekey.iov_len); } else { leaf = NODEPTR(mp, nkeys - 1); - MDBX_GET_KEY2(leaf, nodekey); + MDBX_GET_KEYVALUE(leaf, nodekey); } rc = mc->mc_dbx->md_cmp(key, &nodekey); if (rc == 0) { @@ -7772,7 +7779,7 @@ __hot static int mdbx_cursor_set(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, LEAF2KEY(mp, mc->mc_ki[mc->mc_top], nodekey.iov_len); } else { leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); - MDBX_GET_KEY2(leaf, nodekey); + MDBX_GET_KEYVALUE(leaf, nodekey); } rc = mc->mc_dbx->md_cmp(key, &nodekey); if (rc == 0) { @@ -7894,7 +7901,7 @@ set1: /* The key already matches in all other cases */ if (op == MDBX_SET_RANGE || op == MDBX_SET_KEY) - MDBX_GET_KEY(leaf, key); + MDBX_GET_MAYNULL_KEYPTR(leaf, key); mdbx_debug("==> cursor placed on key [%s], data [%s]", DKEY(key), DVAL(data)); return rc; @@ -7940,7 +7947,7 @@ static int mdbx_cursor_first(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data) { return rc; } } - MDBX_GET_KEY(leaf, key); + MDBX_GET_MAYNULL_KEYPTR(leaf, key); return MDBX_SUCCESS; } @@ -7986,7 +7993,7 @@ static int mdbx_cursor_last(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data) { } } - MDBX_GET_KEY(leaf, key); + MDBX_GET_MAYNULL_KEYPTR(leaf, key); return MDBX_SUCCESS; } @@ -8024,7 +8031,7 @@ int mdbx_cursor_get(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, key->iov_base = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], key->iov_len); } else { MDBX_node *leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); - MDBX_GET_KEY(leaf, key); + MDBX_GET_MAYNULL_KEYPTR(leaf, key); if (data) { if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { if (unlikely(!(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED))) { @@ -8136,7 +8143,7 @@ int mdbx_cursor_get(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, { MDBX_node *leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) { - MDBX_GET_KEY(leaf, key); + MDBX_GET_MAYNULL_KEYPTR(leaf, key); rc = mdbx_node_read(mc, leaf, data); break; } @@ -8770,7 +8777,7 @@ new_sub: } } } - mdbx_cassert(mc, mc->mc_xcursor->mx_db.md_entries < SIZE_MAX); + mdbx_cassert(mc, mc->mc_xcursor->mx_db.md_entries < PTRDIFF_MAX); ecount = (size_t)mc->mc_xcursor->mx_db.md_entries; if (flags & MDBX_APPENDDUP) xflags |= MDBX_APPEND; @@ -9615,8 +9622,8 @@ int mdbx_cursor_count(MDBX_cursor *mc, size_t *countp) { if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { mdbx_cassert(mc, mc->mc_xcursor && (mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED)); - *countp = unlikely(mc->mc_xcursor->mx_db.md_entries > SIZE_MAX) - ? SIZE_MAX + *countp = unlikely(mc->mc_xcursor->mx_db.md_entries > PTRDIFF_MAX) + ? PTRDIFF_MAX : (size_t)mc->mc_xcursor->mx_db.md_entries; } } @@ -12850,7 +12857,7 @@ typedef struct mdbx_walk_ctx { /* Depth-first tree traversal. */ static int __cold mdbx_env_walk(mdbx_walk_ctx_t *ctx, const char *dbi, pgno_t pgno, int deep) { - if (pgno == P_INVALID) + if (unlikely(pgno == P_INVALID)) return MDBX_SUCCESS; /* empty db */ MDBX_cursor mc; @@ -12860,7 +12867,7 @@ static int __cold mdbx_env_walk(mdbx_walk_ctx_t *ctx, const char *dbi, MDBX_page *mp; int rc = mdbx_page_get(&mc, pgno, &mp, NULL); - if (rc) + if (unlikely(rc != MDBX_SUCCESS)) return rc; const int nkeys = NUMKEYS(mp); @@ -12876,7 +12883,7 @@ static int __cold mdbx_env_walk(mdbx_walk_ctx_t *ctx, const char *dbi, switch (mp->mp_flags) { case P_BRANCH: type = MDBX_page_branch; - if (nkeys < 2) + if (unlikely(nkeys < 2)) return MDBX_CORRUPTED; break; case P_LEAF: @@ -12900,12 +12907,8 @@ static int __cold mdbx_env_walk(mdbx_walk_ctx_t *ctx, const char *dbi, MDBX_node *node = NODEPTR(mp, i); payload_size += NODESIZE + NODEKSZ(node); - if (type == MDBX_page_branch) { - rc = mdbx_env_walk(ctx, dbi, NODEPGNO(node), deep); - if (rc) - return rc; + if (type == MDBX_page_branch) continue; - } assert(type == MDBX_page_leaf); switch (node->mn_flags) { @@ -12920,13 +12923,13 @@ static int __cold mdbx_env_walk(mdbx_walk_ctx_t *ctx, const char *dbi, pgno_t large_pgno; memcpy(&large_pgno, NODEDATA(node), sizeof(pgno_t)); rc = mdbx_page_get(&mc, large_pgno, &op, NULL); - if (rc) + if (unlikely(rc != MDBX_SUCCESS)) return rc; /* LY: Don't use mask here, e.g bitwise * (P_BRANCH|P_LEAF|P_LEAF2|P_META|P_OVERFLOW|P_SUBP). * Pages should not me marked dirty/loose or otherwise. */ - if (P_OVERFLOW != op->mp_flags) + if (unlikely(P_OVERFLOW != op->mp_flags)) return MDBX_CORRUPTED; const size_t over_header = PAGEHDRSZ; @@ -12942,36 +12945,15 @@ static int __cold mdbx_env_walk(mdbx_walk_ctx_t *ctx, const char *dbi, case F_SUBDATA /* sub-db */: { const size_t namelen = NODEKSZ(node); - if (namelen == 0 || NODEDSZ(node) != sizeof(MDBX_db)) + if (unlikely(namelen == 0 || NODEDSZ(node) != sizeof(MDBX_db))) return MDBX_CORRUPTED; payload_size += sizeof(MDBX_db); - - MDBX_db db; - memcpy(&db, NODEDATA(node), sizeof(db)); - - char namebuf_onstask[142]; - char *const name = (namelen < sizeof(namebuf_onstask)) - ? namebuf_onstask - : mdbx_malloc(namelen + 1); - if (name) { - memcpy(name, NODEKEY(node), namelen); - name[namelen] = 0; - rc = mdbx_env_walk(ctx, name, db.md_root, deep + 1); - if (name != namebuf_onstask) - mdbx_free(name); - } else { - rc = MDBX_ENOMEM; - } } break; case F_SUBDATA | F_DUPDATA /* dupsorted sub-tree */: { - if (NODEDSZ(node) != sizeof(MDBX_db)) + if (unlikely(NODEDSZ(node) != sizeof(MDBX_db))) return MDBX_CORRUPTED; payload_size += sizeof(MDBX_db); - - MDBX_db db; - memcpy(&db, NODEDATA(node), sizeof(db)); - rc = mdbx_env_walk(ctx, dbi, db.md_root, deep + 1); } break; case F_DUPDATA /* short sub-page */: { @@ -13008,7 +12990,7 @@ static int __cold mdbx_env_walk(mdbx_walk_ctx_t *ctx, const char *dbi, assert(subtype == MDBX_subpage_leaf); MDBX_node *subnode = NODEPTR(sp, j); subpayload_size += NODESIZE + NODEKSZ(subnode) + NODEDSZ(subnode); - if (subnode->mn_flags != 0) + if (unlikely(subnode->mn_flags != 0)) return MDBX_CORRUPTED; } } @@ -13026,13 +13008,68 @@ static int __cold mdbx_env_walk(mdbx_walk_ctx_t *ctx, const char *dbi, return MDBX_CORRUPTED; } - if (unlikely(rc)) + if (unlikely(rc != MDBX_SUCCESS)) return rc; } - return ctx->mw_visitor(mp->mp_pgno, 1, ctx->mw_user, deep, dbi, - ctx->mw_txn->mt_env->me_psize, type, nkeys, - payload_size, header_size, unused_size + align_bytes); + rc = ctx->mw_visitor(mp->mp_pgno, 1, ctx->mw_user, deep, dbi, + ctx->mw_txn->mt_env->me_psize, type, nkeys, payload_size, + header_size, unused_size + align_bytes); + + if (unlikely(rc != MDBX_SUCCESS)) + return (rc == MDBX_RESULT_TRUE) ? MDBX_SUCCESS : MDBX_SUCCESS; + + for (int i = 0; i < nkeys; i++) { + if (type == MDBX_page_dupfixed_leaf) + continue; + + MDBX_node *node = NODEPTR(mp, i); + if (type == MDBX_page_branch) { + rc = mdbx_env_walk(ctx, dbi, NODEPGNO(node), deep + 1); + continue; + } + + assert(type == MDBX_page_leaf); + MDBX_db db; + switch (node->mn_flags) { + default: + continue; + + case F_SUBDATA /* sub-db */: { + const size_t namelen = NODEKSZ(node); + if (unlikely(namelen == 0 || NODEDSZ(node) != sizeof(MDBX_db))) + return MDBX_CORRUPTED; + + char namebuf_onstask[142]; + char *const name = (namelen < sizeof(namebuf_onstask)) + ? namebuf_onstask + : mdbx_malloc(namelen + 1); + if (name) { + memcpy(name, NODEKEY(node), namelen); + name[namelen] = 0; + memcpy(&db, NODEDATA(node), sizeof(db)); + rc = mdbx_env_walk(ctx, name, db.md_root, deep + 1); + if (name != namebuf_onstask) + mdbx_free(name); + } else { + rc = MDBX_ENOMEM; + } + } break; + + case F_SUBDATA | F_DUPDATA /* dupsorted sub-tree */: + if (unlikely(NODEDSZ(node) != sizeof(MDBX_db))) + return MDBX_CORRUPTED; + + memcpy(&db, NODEDATA(node), sizeof(db)); + rc = mdbx_env_walk(ctx, dbi, db.md_root, deep + 1); + break; + } + + if (unlikely(rc != MDBX_SUCCESS)) + return rc; + } + + return MDBX_SUCCESS; } int __cold mdbx_env_pgwalk(MDBX_txn *txn, MDBX_pgvisitor_func *visitor, @@ -13052,15 +13089,16 @@ int __cold mdbx_env_pgwalk(MDBX_txn *txn, MDBX_pgvisitor_func *visitor, ctx.mw_visitor = visitor; int rc = visitor( - 0, NUM_METAS, user, -2, "@META", pgno2bytes(txn->mt_env, NUM_METAS), - MDBX_page_meta, NUM_METAS, sizeof(MDBX_meta) * NUM_METAS, - PAGEHDRSZ * NUM_METAS, + 0, NUM_METAS, user, 0, MDBX_PGWALK_META, + pgno2bytes(txn->mt_env, NUM_METAS), MDBX_page_meta, NUM_METAS, + sizeof(MDBX_meta) * NUM_METAS, PAGEHDRSZ * NUM_METAS, (txn->mt_env->me_psize - sizeof(MDBX_meta) - PAGEHDRSZ) * NUM_METAS); - if (!rc) - rc = mdbx_env_walk(&ctx, "@GC", txn->mt_dbs[FREE_DBI].md_root, -1); - if (!rc) - rc = mdbx_env_walk(&ctx, "@MAIN", txn->mt_dbs[MAIN_DBI].md_root, 0); - if (!rc) + if (!MDBX_IS_ERROR(rc)) + rc = mdbx_env_walk(&ctx, MDBX_PGWALK_GC, txn->mt_dbs[FREE_DBI].md_root, 0); + if (!MDBX_IS_ERROR(rc)) + rc = + mdbx_env_walk(&ctx, MDBX_PGWALK_MAIN, txn->mt_dbs[MAIN_DBI].md_root, 0); + if (!MDBX_IS_ERROR(rc)) rc = visitor(P_INVALID, 0, user, INT_MIN, NULL, 0, MDBX_page_void, 0, 0, 0, 0); return rc; @@ -13405,10 +13443,13 @@ int mdbx_estimate_range(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *begin_key, if (unlikely(!txn || !size_items)) return MDBX_EINVAL; - if (unlikely(!begin_key && begin_data)) + if (unlikely(begin_data && (begin_key == NULL || begin_key == MDBX_EPSILON))) + return MDBX_EINVAL; + + if (unlikely(end_data && (end_key == NULL || end_key == MDBX_EPSILON))) return MDBX_EINVAL; - if (unlikely(!end_key && end_data)) + if (unlikely(begin_key == MDBX_EPSILON && end_key == MDBX_EPSILON)) return MDBX_EINVAL; if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) @@ -13442,35 +13483,61 @@ int mdbx_estimate_range(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *begin_key, } MDBX_val stub = {0, 0}; rc = mdbx_cursor_first(&begin.outer, &stub, &stub); - } else if (end_key && !begin_data && !end_data && - (begin_key == end_key || mdbx_is_samedata(begin_key, end_key))) { - /* LY: single key case */ - int exact = 0; - rc = mdbx_cursor_set(&begin.outer, begin_key, NULL, MDBX_SET, &exact); - if (unlikely(rc != MDBX_SUCCESS)) { - *size_items = 0; - return (rc == MDBX_NOTFOUND) ? MDBX_SUCCESS : rc; + if (unlikely(end_key == MDBX_EPSILON)) { + /* LY: FIRST..+epsilon case */ + return (rc == MDBX_SUCCESS) + ? mdbx_cursor_count(&begin.outer, (size_t *)size_items) + : rc; } - *size_items = 1; - if (begin.outer.mc_xcursor != NULL) { - MDBX_node *leaf = NODEPTR(begin.outer.mc_pg[begin.outer.mc_top], - begin.outer.mc_ki[begin.outer.mc_top]); - if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { - /* LY: return the number of duplicates for given key */ - mdbx_tassert(txn, begin.outer.mc_xcursor == &begin.inner && - (begin.inner.mx_cursor.mc_flags & C_INITIALIZED)); - *size_items = - (sizeof(*size_items) >= sizeof(begin.inner.mx_db.md_entries) || - begin.inner.mx_db.md_entries <= SIZE_MAX) - ? (size_t)begin.inner.mx_db.md_entries - : SIZE_MAX; + } else { + if (unlikely(begin_key == MDBX_EPSILON)) { + if (end_key == NULL) { + /* LY: -epsilon..LAST case */ + MDBX_val stub = {0, 0}; + rc = mdbx_cursor_last(&begin.outer, &stub, &stub); + return (rc == MDBX_SUCCESS) + ? mdbx_cursor_count(&begin.outer, (size_t *)size_items) + : rc; + } + /* LY: -epsilon..value case */ + assert(end_key != MDBX_EPSILON); + begin_key = end_key; + } else if (unlikely(end_key == MDBX_EPSILON)) { + /* LY: value..+epsilon case */ + assert(begin_key != MDBX_EPSILON); + end_key = begin_key; + } + if (end_key && !begin_data && !end_data && + (begin_key == end_key || mdbx_is_samedata(begin_key, end_key))) { + /* LY: single key case */ + int exact = 0; + rc = mdbx_cursor_set(&begin.outer, begin_key, NULL, MDBX_SET, &exact); + if (unlikely(rc != MDBX_SUCCESS)) { + *size_items = 0; + return (rc == MDBX_NOTFOUND) ? MDBX_SUCCESS : rc; + } + *size_items = 1; + if (begin.outer.mc_xcursor != NULL) { + MDBX_node *leaf = NODEPTR(begin.outer.mc_pg[begin.outer.mc_top], + begin.outer.mc_ki[begin.outer.mc_top]); + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + /* LY: return the number of duplicates for given key */ + mdbx_tassert(txn, + begin.outer.mc_xcursor == &begin.inner && + (begin.inner.mx_cursor.mc_flags & C_INITIALIZED)); + *size_items = + (sizeof(*size_items) >= sizeof(begin.inner.mx_db.md_entries) || + begin.inner.mx_db.md_entries <= PTRDIFF_MAX) + ? (size_t)begin.inner.mx_db.md_entries + : PTRDIFF_MAX; + } } + return MDBX_SUCCESS; + } else { + rc = mdbx_cursor_set(&begin.outer, begin_key, begin_data, + begin_data ? MDBX_GET_BOTH_RANGE : MDBX_SET_RANGE, + NULL); } - return MDBX_SUCCESS; - } else { - rc = mdbx_cursor_set(&begin.outer, begin_key, begin_data, - begin_data ? MDBX_GET_BOTH_RANGE : MDBX_SET_RANGE, - NULL); } if (unlikely(rc != MDBX_SUCCESS)) { @@ -13728,9 +13795,9 @@ int mdbx_get_ex(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, (cx.inner.mx_cursor.mc_flags & C_INITIALIZED)); *values_count = (sizeof(*values_count) >= sizeof(cx.inner.mx_db.md_entries) || - cx.inner.mx_db.md_entries <= SIZE_MAX) + cx.inner.mx_db.md_entries <= PTRDIFF_MAX) ? (size_t)cx.inner.mx_db.md_entries - : SIZE_MAX; + : PTRDIFF_MAX; } } } diff --git a/libs/libmdbx/src/src/tools/mdbx_chk.c b/libs/libmdbx/src/src/tools/mdbx_chk.c index 64349cd392..eb8d2681b1 100644 --- a/libs/libmdbx/src/src/tools/mdbx_chk.c +++ b/libs/libmdbx/src/src/tools/mdbx_chk.c @@ -154,6 +154,13 @@ static void pagemap_cleanup(void) { static walk_dbi_t *pagemap_lookup_dbi(const char *dbi_name, bool silent) { static walk_dbi_t *last; + if (dbi_name == MDBX_PGWALK_MAIN) + return &dbi_main; + if (dbi_name == MDBX_PGWALK_GC) + return &dbi_free; + if (dbi_name == MDBX_PGWALK_META) + return &dbi_meta; + if (last && strcmp(last->name, dbi_name) == 0) return last; @@ -166,7 +173,7 @@ static walk_dbi_t *pagemap_lookup_dbi(const char *dbi_name, bool silent) { } dbi->name = mdbx_strdup(dbi_name); - if (verbose > 1 && !silent) { + if (verbose > 0 && !silent) { print(" - found '%s' area\n", dbi_name); fflush(NULL); } @@ -246,35 +253,32 @@ static size_t problems_pop(struct problem *list) { } static int pgvisitor(uint64_t pgno, unsigned pgnumber, void *ctx, int deep, - const char *dbi_name, size_t page_size, + const char *dbi_name_or_tag, size_t page_size, MDBX_page_type_t pagetype, size_t nentries, size_t payload_bytes, size_t header_bytes, size_t unused_bytes) { (void)ctx; + if (deep > 42) { + problem_add("deep", deep, "too large", nullptr); + return MDBX_CORRUPTED /* avoid infinite loop/recursion */; + } + if (pagetype == MDBX_page_void) return MDBX_SUCCESS; - walk_dbi_t fake, *dbi = &fake; - if (deep > 0) { - dbi = pagemap_lookup_dbi(dbi_name, false); - if (!dbi) - return MDBX_ENOMEM; - } else if (deep == 0 && strcmp(dbi_name, dbi_main.name) == 0) - dbi = &dbi_main; - else if (deep == -1 && strcmp(dbi_name, dbi_free.name) == 0) - dbi = &dbi_free; - else if (deep == -2 && strcmp(dbi_name, dbi_meta.name) == 0) - dbi = &dbi_meta; - else - problem_add("deep", deep, "unknown area", "%s", dbi_name); + walk_dbi_t *dbi = pagemap_lookup_dbi(dbi_name_or_tag, false); + if (!dbi) + return MDBX_ENOMEM; const size_t page_bytes = payload_bytes + header_bytes + unused_bytes; walk.pgcount += pgnumber; const char *pagetype_caption; + bool branch = false; switch (pagetype) { default: - problem_add("page", pgno, "unknown page-type", "%u", (unsigned)pagetype); + problem_add("page", pgno, "unknown page-type", "type %u, deep %i", + (unsigned)pagetype, deep); pagetype_caption = "unknown"; dbi->pages.other += pgnumber; break; @@ -290,6 +294,7 @@ static int pgvisitor(uint64_t pgno, unsigned pgnumber, void *ctx, int deep, case MDBX_page_branch: pagetype_caption = "branch"; dbi->pages.branch += pgnumber; + branch = true; break; case MDBX_page_leaf: pagetype_caption = "leaf"; @@ -310,15 +315,38 @@ static int pgvisitor(uint64_t pgno, unsigned pgnumber, void *ctx, int deep, } if (pgnumber) { - if (verbose > 3 && (!only_subdb || strcmp(only_subdb, dbi_name) == 0)) { + if (verbose > 3 && (!only_subdb || strcmp(only_subdb, dbi->name) == 0)) { if (pgnumber == 1) print(" %s-page %" PRIu64, pagetype_caption, pgno); else print(" %s-span %" PRIu64 "[%u]", pagetype_caption, pgno, pgnumber); print(" of %s: header %" PRIiPTR ", payload %" PRIiPTR - ", unused %" PRIiPTR "\n", - dbi_name, header_bytes, payload_bytes, unused_bytes); + ", unused %" PRIiPTR ", deep %i\n", + dbi->name, header_bytes, payload_bytes, unused_bytes, deep); } + + bool already_used = false; + do { + if (pgno >= lastpgno) + problem_add("page", pgno, "wrong page-no", + "%s-page: %" PRIu64 " > %" PRIu64 ", deep %i", + pagetype_caption, pgno, lastpgno, deep); + else if (walk.pagemap[pgno]) { + walk_dbi_t *coll_dbi = &walk.dbi[walk.pagemap[pgno] - 1]; + problem_add( + "page", pgno, (branch && coll_dbi == dbi) ? "loop" : "already used", + "%s-page: by %s, deep %i", pagetype_caption, coll_dbi->name, deep); + already_used = true; + } else { + walk.pagemap[pgno] = (short)(dbi - walk.dbi + 1); + dbi->pages.total += 1; + } + ++pgno; + } while (--pgnumber); + + if (already_used) + return branch ? MDBX_RESULT_TRUE /* avoid infinite loop/recursion */ + : MDBX_SUCCESS; } if (unused_bytes > page_size) @@ -343,8 +371,9 @@ static int pgvisitor(uint64_t pgno, unsigned pgnumber, void *ctx, int deep, } */ } else { problem_add("page", pgno, "empty", - "%s-page: payload %" PRIuPTR " bytes, %" PRIuPTR " entries", - pagetype_caption, payload_bytes, nentries); + "%s-page: payload %" PRIuPTR " bytes, %" PRIuPTR + " entries, deep %i", + pagetype_caption, payload_bytes, nentries, deep); dbi->pages.empty += 1; } } @@ -353,9 +382,9 @@ static int pgvisitor(uint64_t pgno, unsigned pgnumber, void *ctx, int deep, if (page_bytes != page_size) { problem_add("page", pgno, "misused", "%s-page: %" PRIuPTR " != %" PRIuPTR " (%" PRIuPTR - "h + %" PRIuPTR "p + %" PRIuPTR "u)", + "h + %" PRIuPTR "p + %" PRIuPTR "u), deep %i", pagetype_caption, page_size, page_bytes, header_bytes, - payload_bytes, unused_bytes); + payload_bytes, unused_bytes, deep); if (page_size > page_bytes) dbi->lost_bytes += page_size - page_bytes; } else { @@ -364,23 +393,6 @@ static int pgvisitor(uint64_t pgno, unsigned pgnumber, void *ctx, int deep, } } - if (pgnumber) { - do { - if (pgno >= lastpgno) - problem_add("page", pgno, "wrong page-no", - "%s-page: %" PRIu64 " > %" PRIu64, pagetype_caption, pgno, - lastpgno); - else if (walk.pagemap[pgno]) - problem_add("page", pgno, "already used", "%s-page: by %s", - pagetype_caption, walk.dbi[walk.pagemap[pgno] - 1].name); - else { - walk.pagemap[pgno] = (short)(dbi - walk.dbi + 1); - dbi->pages.total += 1; - } - ++pgno; - } while (--pgnumber); - } - return user_break ? MDBX_EINTR : MDBX_SUCCESS; } @@ -628,48 +640,56 @@ static int process_db(MDBX_dbi dbi_handle, char *dbi_name, visitor *handler, goto bailout; } + bool bad_key = false; if (key.iov_len > maxkeysize) { problem_add("entry", record_count, "key length exceeds max-key-size", "%" PRIuPTR " > %" PRIuPTR, key.iov_len, maxkeysize); + bad_key = true; } else if ((flags & MDBX_INTEGERKEY) && key.iov_len != sizeof(uint64_t) && key.iov_len != sizeof(uint32_t)) { problem_add("entry", record_count, "wrong key length", "%" PRIuPTR " != 4or8", key.iov_len); + bad_key = true; } + bool bad_data = false; if ((flags & MDBX_INTEGERDUP) && data.iov_len != sizeof(uint64_t) && data.iov_len != sizeof(uint32_t)) { problem_add("entry", record_count, "wrong data length", "%" PRIuPTR " != 4or8", data.iov_len); + bad_data = true; } - if (prev_key.iov_base) { + if (prev_key.iov_base && !bad_data) { if ((flags & MDBX_DUPFIXED) && prev_data.iov_len != data.iov_len) { problem_add("entry", record_count, "different data length", "%" PRIuPTR " != %" PRIuPTR, prev_data.iov_len, data.iov_len); + bad_data = true; } - int cmp = mdbx_cmp(txn, dbi_handle, &prev_key, &key); - if (cmp == 0) { - ++dups; - if ((flags & MDBX_DUPSORT) == 0) { - problem_add("entry", record_count, "duplicated entries", NULL); - if (data.iov_len == prev_data.iov_len && - memcmp(data.iov_base, prev_data.iov_base, data.iov_len) == 0) { - problem_add("entry", record_count, "complete duplicate", NULL); - } - } else { - cmp = mdbx_dcmp(txn, dbi_handle, &prev_data, &data); - if (cmp == 0) { - problem_add("entry", record_count, "complete duplicate", NULL); - } else if (cmp > 0 && !ignore_wrong_order) { - problem_add("entry", record_count, "wrong order of multi-values", - NULL); + if (!bad_key) { + int cmp = mdbx_cmp(txn, dbi_handle, &prev_key, &key); + if (cmp == 0) { + ++dups; + if ((flags & MDBX_DUPSORT) == 0) { + problem_add("entry", record_count, "duplicated entries", NULL); + if (data.iov_len == prev_data.iov_len && + memcmp(data.iov_base, prev_data.iov_base, data.iov_len) == 0) { + problem_add("entry", record_count, "complete duplicate", NULL); + } + } else if (!bad_data) { + cmp = mdbx_dcmp(txn, dbi_handle, &prev_data, &data); + if (cmp == 0) { + problem_add("entry", record_count, "complete duplicate", NULL); + } else if (cmp > 0 && !ignore_wrong_order) { + problem_add("entry", record_count, "wrong order of multi-values", + NULL); + } } + } else if (cmp > 0 && !ignore_wrong_order) { + problem_add("entry", record_count, "wrong order of entries", NULL); } - } else if (cmp > 0 && !ignore_wrong_order) { - problem_add("entry", record_count, "wrong order of entries", NULL); } } else if (verbose) { if (flags & MDBX_INTEGERKEY) @@ -688,8 +708,10 @@ static int process_db(MDBX_dbi dbi_handle, char *dbi_name, visitor *handler, key_bytes += key.iov_len; data_bytes += data.iov_len; - prev_key = key; - prev_data = data; + if (!bad_key) + prev_key = key; + if (!bad_data) + prev_data = data; rc = mdbx_cursor_get(mc, &key, &data, MDBX_NEXT); } if (rc != MDBX_NOTFOUND) @@ -1177,20 +1199,22 @@ int main(int argc, char *argv[]) { total_page_bytes); if (verbose > 2) { for (walk_dbi_t *dbi = walk.dbi; dbi < walk.dbi + MAX_DBI && dbi->name; - ++dbi) { - uint64_t dbi_bytes = dbi->pages.total * envstat.ms_psize; - print(" %s: subtotal %" PRIu64 " bytes (%.1f%%)," - " payload %" PRIu64 " (%.1f%%), unused %" PRIu64 " (%.1f%%)", - dbi->name, dbi_bytes, dbi_bytes * 100.0 / total_page_bytes, - dbi->payload_bytes, dbi->payload_bytes * 100.0 / dbi_bytes, - dbi_bytes - dbi->payload_bytes, - (dbi_bytes - dbi->payload_bytes) * 100.0 / dbi_bytes); - if (dbi->pages.empty) - print(", %" PRIu64 " empty pages", dbi->pages.empty); - if (dbi->lost_bytes) - print(", %" PRIu64 " bytes lost", dbi->lost_bytes); - print("\n"); - } + ++dbi) + if (dbi->pages.total) { + uint64_t dbi_bytes = dbi->pages.total * envstat.ms_psize; + print(" %s: subtotal %" PRIu64 " bytes (%.1f%%)," + " payload %" PRIu64 " (%.1f%%), unused %" PRIu64 " (%.1f%%)", + dbi->name, dbi_bytes, dbi_bytes * 100.0 / total_page_bytes, + dbi->payload_bytes, dbi->payload_bytes * 100.0 / dbi_bytes, + dbi_bytes - dbi->payload_bytes, + (dbi_bytes - dbi->payload_bytes) * 100.0 / dbi_bytes); + if (dbi->pages.empty) + print(", %" PRIu64 " empty pages", dbi->pages.empty); + if (dbi->lost_bytes) + print(", %" PRIu64 " bytes lost", dbi->lost_bytes); + print("\n"); + } else + print(" %s: empty\n", dbi->name); } print(" - summary: average fill %.1f%%", walk.total_payload_bytes * 100.0 / total_page_bytes); diff --git a/libs/libmdbx/src/src/version.c b/libs/libmdbx/src/src/version.c index dfb4a5724c..a0f7b4f58d 100644 --- a/libs/libmdbx/src/src/version.c +++ b/libs/libmdbx/src/src/version.c @@ -1,5 +1,5 @@ /* - * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2015-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * @@ -14,12 +14,12 @@ #include "./bits.h" -#if MDBX_VERSION_MAJOR != 0 || MDBX_VERSION_MINOR != 2 +#if MDBX_VERSION_MAJOR != 0 || MDBX_VERSION_MINOR != 3 #error "API version mismatch!" #endif #define MDBX_VERSION_RELEASE 0 -#define MDBX_VERSION_REVISION 2 +#define MDBX_VERSION_REVISION 0 /*LIBMDBX_EXPORTS*/ const mdbx_version_info mdbx_version = { MDBX_VERSION_MAJOR, @@ -30,5 +30,5 @@ "@MDBX_GIT_DESCRIBE@"}}; /*LIBMDBX_EXPORTS*/ const mdbx_build_info mdbx_build = { - "@MDBX_BUILD_TIMESTAMP@", "@MDBX_BUILD_TAGRET@", "@MDBX_BUILD_OPTIONS@", + "@MDBX_BUILD_TIMESTAMP@", "@MDBX_BUILD_TARGET@", "@MDBX_BUILD_OPTIONS@", "@MDBX_BUILD_COMPILER@", "@MDBX_BUILD_FLAGS@"}; diff --git a/libs/libmdbx/src/test/CMakeLists.txt b/libs/libmdbx/src/test/CMakeLists.txt index 88fd09e01b..bb4abd5d6d 100644 --- a/libs/libmdbx/src/test/CMakeLists.txt +++ b/libs/libmdbx/src/test/CMakeLists.txt @@ -28,6 +28,7 @@ add_executable(${TARGET} utils.cc utils.h append.cc + ttl.cc ) target_link_libraries(${TARGET} diff --git a/libs/libmdbx/src/test/append.cc b/libs/libmdbx/src/test/append.cc index 3ce53eb292..c5e7e91d9e 100644 --- a/libs/libmdbx/src/test/append.cc +++ b/libs/libmdbx/src/test/append.cc @@ -19,9 +19,7 @@ bool testcase_append::run() { txn_begin(false); MDBX_dbi dbi = db_table_open(true); - int rc = mdbx_drop(txn_guard.get(), dbi, false); - if (unlikely(rc != MDBX_SUCCESS)) - failure_perror("mdbx_drop(delete=false)", rc); + db_table_clear(dbi); keyvalue_maker.setup(config.params, config.actor_id, 0 /* thread_number */); /* LY: тест наполнения таблиц в append-режиме, @@ -59,10 +57,10 @@ bool testcase_append::run() { if (cmp == 0 && (config.params.table_flags & MDBX_DUPSORT)) cmp = mdbx_dcmp(txn_guard.get(), dbi, &data->value, &last_data->value); - rc = mdbx_put(txn_guard.get(), dbi, &key->value, &data->value, flags); + int err = mdbx_put(txn_guard.get(), dbi, &key->value, &data->value, flags); if (cmp > 0) { - if (unlikely(rc != MDBX_SUCCESS)) - failure_perror("mdbx_put(appenda-a)", rc); + if (unlikely(err != MDBX_SUCCESS)) + failure_perror("mdbx_put(appenda-a)", err); memcpy(last_key->value.iov_base, key->value.iov_base, last_key->value.iov_len = key->value.iov_len); memcpy(last_data->value.iov_base, data->value.iov_base, @@ -71,8 +69,8 @@ bool testcase_append::run() { inserted_checksum.push((uint32_t)inserted_number, key->value); inserted_checksum.push(10639, data->value); } else { - if (unlikely(rc != MDBX_EKEYMISMATCH)) - failure_perror("mdbx_put(appenda-a) != MDBX_EKEYMISMATCH", rc); + if (unlikely(err != MDBX_EKEYMISMATCH)) + failure_perror("mdbx_put(appenda-a) != MDBX_EKEYMISMATCH", err); } if (++txn_nops >= config.params.batch_write) { @@ -88,23 +86,24 @@ bool testcase_append::run() { cursor_open(dbi); MDBX_val check_key, check_data; - rc = mdbx_cursor_get(cursor_guard.get(), &check_key, &check_data, MDBX_FIRST); - if (unlikely(rc != MDBX_SUCCESS)) - failure_perror("mdbx_cursor_get(MDBX_FIRST)", rc); + int err = + mdbx_cursor_get(cursor_guard.get(), &check_key, &check_data, MDBX_FIRST); + if (unlikely(err != MDBX_SUCCESS)) + failure_perror("mdbx_cursor_get(MDBX_FIRST)", err); simple_checksum read_checksum; uint64_t read_count = 0; - while (rc == MDBX_SUCCESS) { + while (err == MDBX_SUCCESS) { ++read_count; read_checksum.push((uint32_t)read_count, check_key); read_checksum.push(10639, check_data); - rc = + err = mdbx_cursor_get(cursor_guard.get(), &check_key, &check_data, MDBX_NEXT); } - if (unlikely(rc != MDBX_NOTFOUND)) - failure_perror("mdbx_cursor_get(MDBX_NEXT) != EOF", rc); + if (unlikely(err != MDBX_NOTFOUND)) + failure_perror("mdbx_cursor_get(MDBX_NEXT) != EOF", err); if (unlikely(read_count != inserted_number)) failure("read_count(%" PRIu64 ") != inserted_number(%" PRIu64 ")", diff --git a/libs/libmdbx/src/test/cases.cc b/libs/libmdbx/src/test/cases.cc index 1d41efc82b..023a80020c 100644 --- a/libs/libmdbx/src/test/cases.cc +++ b/libs/libmdbx/src/test/cases.cc @@ -67,6 +67,7 @@ void testcase_setup(const char *casename, actor_params ¶ms, configure_actor(last_space_id, ac_hill, nullptr, params); configure_actor(last_space_id, ac_jitter, nullptr, params); configure_actor(last_space_id, ac_hill, nullptr, params); + configure_actor(last_space_id, ac_ttl, nullptr, params); configure_actor(last_space_id, ac_try, nullptr, params); configure_actor(last_space_id, ac_copy, nullptr, params); configure_actor(last_space_id, ac_append, nullptr, params); diff --git a/libs/libmdbx/src/test/config.h b/libs/libmdbx/src/test/config.h index d6eaea2e54..b8a4b6827d 100644 --- a/libs/libmdbx/src/test/config.h +++ b/libs/libmdbx/src/test/config.h @@ -28,7 +28,8 @@ enum actor_testcase { ac_jitter, ac_try, ac_copy, - ac_append + ac_append, + ac_ttl }; enum actor_status { diff --git a/libs/libmdbx/src/test/gc.sh b/libs/libmdbx/src/test/gc.sh index 76c7a9a51c..9c1407f53e 100644 --- a/libs/libmdbx/src/test/gc.sh +++ b/libs/libmdbx/src/test/gc.sh @@ -23,9 +23,9 @@ function probe { echo "=============================================== $(date)" echo "${caption}: $*" rm -f ${TESTDB_PREFIX}* \ - && ./mdbx_test --pathname=${TESTDB_PREFIX}db "$@" | lz4 > ${TESTDB_PREFIX}log.lz4 \ + && ./mdbx_test --repeat=12 --pathname=${TESTDB_PREFIX}db "$@" | lz4 > ${TESTDB_PREFIX}log.lz4 \ && ./mdbx_chk -nvvv ${TESTDB_PREFIX}db | tee ${TESTDB_PREFIX}chk \ - && ./mdbx_chk -nvvv ${TESTDB_PREFIX}db-copy | tee ${TESTDB_PREFIX}chk-copy \ + && ([ ! -e ${TESTDB_PREFIX}db-copy ] || ./mdbx_chk -nvvv ${TESTDB_PREFIX}db-copy | tee ${TESTDB_PREFIX}chk-copy) \ || (echo "FAILED"; exit 1) } @@ -34,7 +34,7 @@ function probe { count=0 for nops in {2..7}; do for ((wbatch=nops-1; wbatch > 0; --wbatch)); do - loops=$(((3333 >> nops) / nops + 1)) + loops=$(((333 >> nops) / nops + 3)) for ((rep=0; rep++ < loops; )); do for ((bits=2**${#options[@]}; --bits >= 0; )); do seed=$(date +%N) diff --git a/libs/libmdbx/src/test/keygen.cc b/libs/libmdbx/src/test/keygen.cc index 5876fd8cec..30cdf7a571 100644 --- a/libs/libmdbx/src/test/keygen.cc +++ b/libs/libmdbx/src/test/keygen.cc @@ -1,4 +1,4 @@ -/* +/* * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. @@ -176,7 +176,7 @@ bool maker::is_unordered() const { return (mapping.mesh >= serial_minwith || mapping.rotate) != 0; } -bool maker::increment(serial_t &serial, int delta) { +bool maker::increment(serial_t &serial, int delta) const { if (serial > mask(mapping.width)) { log_extra("keygen-increment: %" PRIu64 " > %" PRIu64 ", overflow", serial, mask(mapping.width)); diff --git a/libs/libmdbx/src/test/keygen.h b/libs/libmdbx/src/test/keygen.h index 890397b8ca..d0299e1e7d 100644 --- a/libs/libmdbx/src/test/keygen.h +++ b/libs/libmdbx/src/test/keygen.h @@ -1,4 +1,4 @@ -/* +/* * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. @@ -124,7 +124,7 @@ public: void make_ordered(); bool is_unordered() const; - bool increment(serial_t &serial, int delta); + bool increment(serial_t &serial, int delta) const; }; } /* namespace keygen */ diff --git a/libs/libmdbx/src/test/main.cc b/libs/libmdbx/src/test/main.cc index f3ee76b62f..9dc3eccbae 100644 --- a/libs/libmdbx/src/test/main.cc +++ b/libs/libmdbx/src/test/main.cc @@ -37,7 +37,8 @@ void actor_params::set_defaults(const std::string &tmpdir) { table_flags = MDBX_DUPSORT; size_lower = -1; - size_now = 1024 * 1024 * ((table_flags & MDBX_DUPSORT) ? 4 : 256); + size_now = + intptr_t(1024) * 1024 * ((table_flags & MDBX_DUPSORT) ? 256 : 1024); size_upper = -1; shrink_threshold = -1; growth_step = -1; @@ -61,8 +62,8 @@ void actor_params::set_defaults(const std::string &tmpdir) { datalen_min = mdbx_datalen_min(); datalen_max = std::min(mdbx_datalen_max(), 256u * 1024 + 42); - batch_read = 4; - batch_write = 4; + batch_read = 42; + batch_write = 42; delaystart = 0; waitfor_nops = 0; @@ -345,6 +346,10 @@ int main(int argc, char *const argv[]) { configure_actor(last_space_id, ac_append, value, params); continue; } + if (config::parse_option(argc, argv, narg, "ttl", nullptr)) { + configure_actor(last_space_id, ac_ttl, value, params); + continue; + } if (config::parse_option(argc, argv, narg, "failfast", global::config::failfast)) continue; diff --git a/libs/libmdbx/src/test/test.cc b/libs/libmdbx/src/test/test.cc index e34bd7f0e8..cf61f1eeee 100644 --- a/libs/libmdbx/src/test/test.cc +++ b/libs/libmdbx/src/test/test.cc @@ -35,6 +35,8 @@ const char *testcase2str(const actor_testcase testcase) { return "copy"; case ac_append: return "append"; + case ac_ttl: + return "ttl"; } } @@ -322,6 +324,7 @@ bool testcase::setup() { return false; start_timestamp = chrono::now_motonic(); + nops_completed = 0; return true; } @@ -429,6 +432,14 @@ void testcase::db_table_drop(MDBX_dbi handle) { } } +void testcase::db_table_clear(MDBX_dbi handle) { + log_trace(">> testcase::db_table_clear, handle %u", handle); + int rc = mdbx_drop(txn_guard.get(), handle, false); + if (unlikely(rc != MDBX_SUCCESS)) + failure_perror("mdbx_drop(delete=false)", rc); + log_trace("<< testcase::db_table_clear"); +} + void testcase::db_table_close(MDBX_dbi handle) { log_trace(">> testcase::db_table_close, handle %u", handle); assert(!txn_guard); @@ -450,8 +461,9 @@ void testcase::checkdata(const char *step, MDBX_dbi handle, MDBX_val key2check, //----------------------------------------------------------------------------- -bool test_execute(const actor_config &config) { +bool test_execute(const actor_config &config_const) { const mdbx_pid_t pid = osal_getpid(); + actor_config config = config_const; if (global::singlemode) { logging::setup(format("single_%s", testcase2str(config.testcase))); @@ -487,23 +499,40 @@ bool test_execute(const actor_config &config) { case ac_append: test.reset(new testcase_append(config, pid)); break; + case ac_ttl: + test.reset(new testcase_ttl(config, pid)); + break; default: test.reset(new testcase(config, pid)); break; } - if (!test->setup()) - log_notice("test setup failed"); - else if (!test->run()) - log_notice("test failed"); - else if (!test->teardown()) - log_notice("test teardown failed"); - else { - log_info("test successed"); - return true; - } + size_t iter = 0; + do { + iter++; + if (!test->setup()) { + log_notice("test setup failed"); + return false; + } else if (!test->run()) { + log_notice("test failed"); + return false; + } else if (!test->teardown()) { + log_notice("test teardown failed"); + return false; + } else { + if (config.params.nrepeat == 1) + log_info("test successed"); + else if (config.params.nrepeat == 1) + log_info("test successed (iteration %zi of %zi)", iter, + size_t(config.params.nrepeat)); + else + log_info("test successed (iteration %zi)", iter); + config.params.keygen.seed += INT32_C(0xA4F4D37B); + } + } while (config.params.nrepeat == 0 || iter < config.params.nrepeat); + return true; } catch (const std::exception &pipets) { failure("***** Exception: %s *****", pipets.what()); + return false; } - return false; } diff --git a/libs/libmdbx/src/test/test.h b/libs/libmdbx/src/test/test.h index e726023279..117a66f93c 100644 --- a/libs/libmdbx/src/test/test.h +++ b/libs/libmdbx/src/test/test.h @@ -119,6 +119,7 @@ protected: MDBX_dbi db_table_open(bool create); void db_table_drop(MDBX_dbi handle); + void db_table_clear(MDBX_dbi handle); void db_table_close(MDBX_dbi handle); bool wait4start(); @@ -153,6 +154,13 @@ public: virtual ~testcase() {} }; +class testcase_ttl : public testcase { +public: + testcase_ttl(const actor_config &config, const mdbx_pid_t pid) + : testcase(config, pid) {} + bool run(); +}; + class testcase_hill : public testcase { public: testcase_hill(const actor_config &config, const mdbx_pid_t pid) diff --git a/libs/libmdbx/src/test/test.vcxproj b/libs/libmdbx/src/test/test.vcxproj index 9eb62cdcf5..93de00e3a0 100644 --- a/libs/libmdbx/src/test/test.vcxproj +++ b/libs/libmdbx/src/test/test.vcxproj @@ -181,6 +181,7 @@ <ClInclude Include="utils.h" /> </ItemGroup> <ItemGroup> + <ClCompile Include="ttl.cc" /> <ClCompile Include="append.cc" /> <ClCompile Include="cases.cc" /> <ClCompile Include="chrono.cc" /> diff --git a/libs/libmdbx/src/test/ttl.cc b/libs/libmdbx/src/test/ttl.cc new file mode 100644 index 0000000000..1ecfd0c7e5 --- /dev/null +++ b/libs/libmdbx/src/test/ttl.cc @@ -0,0 +1,137 @@ +/* + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> + * and other libmdbx authors: please see AUTHORS file. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "test.h" +#include <cmath> +#include <deque> + +static unsigned edge2window(uint64_t edge, unsigned window_max) { + const double rnd = u64_to_double1(bleach64(edge)); + const unsigned window = window_max - std::lrint(std::pow(window_max, rnd)); + return window; +} + +static unsigned edge2count(uint64_t edge, unsigned count_max) { + const double rnd = u64_to_double1(prng64_map1_white(edge)); + const unsigned count = std::lrint(std::pow(count_max, rnd)); + return count; +} + +bool testcase_ttl::run() { + db_open(); + + txn_begin(false); + MDBX_dbi dbi = db_table_open(true); + db_table_clear(dbi); + txn_end(false); + + /* LY: тест "эмуляцией time-to-live": + * - организуется "скользящее окно", которое двигается вперед вдоль + * числовой оси каждую транзакцию. + * - по переднему краю "скользящего окна" записи добавляются в таблицу, + * а по заднему удаляются. + * - количество добавляемых/удаляемых записей псевдослучайно зависит + * от номера транзакции, но с экспоненциальным распределением. + * - размер "скользящего окна" также псевдослучайно зависит от номера + * транзакции с "отрицательным" экспоненциальным распределением + * MAX_WIDTH - exp(rnd(N)), при уменьшении окна сдвигается задний + * край и удаляются записи позади него. + * + * Таким образом имитируется поведение таблицы с TTL: записи стохастически + * добавляются и удаляются, но изредка происходят массивные удаления. + */ + + /* LY: для параметризации используем подходящие параметры, которые не имеют + * здесь смысла в первоначальном значении */ + const unsigned window_max = + (config.params.batch_read > 999) ? config.params.batch_read : 1000; + const unsigned count_max = + (config.params.batch_write > 999) ? config.params.batch_write : 1000; + log_info("ttl: using `batch_read` value %u for window_max", window_max); + log_info("ttl: using `batch_write` value %u for count_max", count_max); + + uint64_t seed = + prng64_map2_white(config.params.keygen.seed) + config.actor_id; + keyvalue_maker.setup(config.params, config.actor_id, 0 /* thread_number */); + key = keygen::alloc(config.params.keylen_max); + data = keygen::alloc(config.params.datalen_max); + const unsigned insert_flags = (config.params.table_flags & MDBX_DUPSORT) + ? MDBX_NODUPDATA + : MDBX_NODUPDATA | MDBX_NOOVERWRITE; + + std::deque<std::pair<uint64_t, unsigned>> fifo; + uint64_t serial = 0; + while (should_continue()) { + if (!txn_guard) + txn_begin(false); + const uint64_t salt = prng64_white(seed) /* mdbx_txn_id(txn_guard.get()) */; + + const unsigned window_width = edge2window(salt, window_max); + const unsigned head_count = edge2count(salt, count_max); + log_info("ttl: step #%zu (serial %" PRIu64 + ", window %u, count %u) salt %" PRIu64, + nops_completed, serial, window_width, head_count, salt); + + if (window_width) { + while (fifo.size() > window_width) { + uint64_t tail_serial = fifo.back().first; + const unsigned tail_count = fifo.back().second; + log_trace("ttl: pop-tail (serial %" PRIu64 ", count %u)", tail_serial, + tail_count); + fifo.pop_back(); + for (unsigned n = 0; n < tail_count; ++n) { + log_trace("ttl: remove-tail %" PRIu64, serial); + generate_pair(tail_serial); + int err = mdbx_del(txn_guard.get(), dbi, &key->value, &data->value); + if (unlikely(err != MDBX_SUCCESS)) + failure_perror("mdbx_del(tail)", err); + if (unlikely(!keyvalue_maker.increment(tail_serial, 1))) + failure("ttl: unexpected key-space overflow on the tail"); + } + } + } else { + log_trace("ttl: purge state"); + db_table_clear(dbi); + fifo.clear(); + } + + txn_restart(false, false); + fifo.push_front(std::make_pair(serial, head_count)); + + for (unsigned n = 0; n < head_count; ++n) { + log_trace("ttl: insert-head %" PRIu64, serial); + generate_pair(serial); + int err = mdbx_put(txn_guard.get(), dbi, &key->value, &data->value, + insert_flags); + if (unlikely(err != MDBX_SUCCESS)) + failure_perror("mdbx_put(head)", err); + + if (unlikely(!keyvalue_maker.increment(serial, 1))) + failure("uphill: unexpected key-space overflow"); + } + + txn_end(false); + report(1); + } + + if (dbi) { + if (config.params.drop_table && !mode_readonly()) { + txn_begin(false); + db_table_drop(dbi); + txn_end(false); + } else + db_table_close(dbi); + } + return true; +} diff --git a/libs/libmdbx/src/test/utils.cc b/libs/libmdbx/src/test/utils.cc index 326455a693..ddf47a4cd9 100644 --- a/libs/libmdbx/src/test/utils.cc +++ b/libs/libmdbx/src/test/utils.cc @@ -252,21 +252,8 @@ uint64_t entropy_ticks(void) { //----------------------------------------------------------------------------- -static __inline uint64_t bleach64(uint64_t dirty) { - return mul_64x64_high(bswap64(dirty), UINT64_C(17048867929148541611)); -} - -static __inline uint32_t bleach32(uint32_t dirty) { - return (uint32_t)((bswap32(dirty) * UINT64_C(2175734609)) >> 32); -} - -uint64_t prng64_careless(uint64_t &state) { - state = state * UINT64_C(6364136223846793005) + 1; - return state; -} - uint64_t prng64_white(uint64_t &state) { - state = state * UINT64_C(6364136223846793005) + UINT64_C(1442695040888963407); + state = prng64_map2_careless(state); return bleach64(state); } diff --git a/libs/libmdbx/src/test/utils.h b/libs/libmdbx/src/test/utils.h index 7bf3abd305..efda8394fc 100644 --- a/libs/libmdbx/src/test/utils.h +++ b/libs/libmdbx/src/test/utils.h @@ -1,4 +1,4 @@ -/* +/* * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. @@ -327,7 +327,60 @@ std::string format(const char *fmt, ...); uint64_t entropy_ticks(void); uint64_t entropy_white(void); -uint64_t prng64_careless(uint64_t &state); +static inline uint64_t bleach64(uint64_t v) { + // Tommy Ettinger, https://www.blogger.com/profile/04953541827437796598 + // http://mostlymangling.blogspot.com/2019/01/better-stronger-mixer-and-test-procedure.html + v ^= rot64(v, 25) ^ rot64(v, 50); + v *= UINT64_C(0xA24BAED4963EE407); + v ^= rot64(v, 24) ^ rot64(v, 49); + v *= UINT64_C(0x9FB21C651E98DF25); + return v ^ v >> 28; +} + +static inline uint32_t bleach32(uint32_t x) { + // https://github.com/skeeto/hash-prospector + // exact bias: 0.17353355999581582 + x ^= x >> 16; + x *= UINT32_C(0x7feb352d); + x ^= 0x3027C563 ^ (x >> 15); + x *= UINT32_C(0x846ca68b); + x ^= x >> 16; + return x; +} + +static inline uint64_t prng64_map1_careless(uint64_t state) { + return state * UINT64_C(6364136223846793005) + 1; +} + +static inline uint64_t prng64_map2_careless(uint64_t state) { + return (state + UINT64_C(1442695040888963407)) * + UINT64_C(6364136223846793005); +} + +static inline uint64_t prng64_map1_white(uint64_t state) { + return bleach64(prng64_map1_careless(state)); +} + +static inline uint64_t prng64_map2_white(uint64_t state) { + return bleach64(prng64_map2_careless(state)); +} + +static inline uint64_t prng64_careless(uint64_t &state) { + state = prng64_map1_careless(state); + return state; +} + +static inline double u64_to_double1(uint64_t v) { + union { + uint64_t u64; + double d; + } casting; + + casting.u64 = UINT64_C(0x3ff) << 52 | (v >> 12); + assert(casting.d >= 1.0 && casting.d < 2.0); + return casting.d - 1.0; +} + uint64_t prng64_white(uint64_t &state); uint32_t prng32(uint64_t &state); void prng_fill(uint64_t &state, void *ptr, size_t bytes); |