summaryrefslogtreecommitdiff
path: root/libs/libmdbx/src
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2019-06-23 12:11:35 +0300
committerGeorge Hazan <ghazan@miranda.im>2019-06-23 12:11:35 +0300
commitede0e5088aa37d1ef710a7bf74ccead5140c9117 (patch)
tree984ec1470c0c83653e8871953b4b39806fee2b99 /libs/libmdbx/src
parent3632205b947823fa049e91b86a5611e9bbb59673 (diff)
libmdbx: merge
Diffstat (limited to 'libs/libmdbx/src')
-rw-r--r--libs/libmdbx/src/.travis.yml5
-rw-r--r--libs/libmdbx/src/Makefile4
-rw-r--r--libs/libmdbx/src/libmdbx.files1
-rw-r--r--libs/libmdbx/src/mdbx.h7
-rw-r--r--libs/libmdbx/src/src/bits.h4
-rw-r--r--libs/libmdbx/src/src/defs.h15
-rw-r--r--libs/libmdbx/src/src/mdbx.c281
-rw-r--r--libs/libmdbx/src/src/tools/mdbx_chk.c174
-rw-r--r--libs/libmdbx/src/src/version.c8
-rw-r--r--libs/libmdbx/src/test/CMakeLists.txt1
-rw-r--r--libs/libmdbx/src/test/append.cc29
-rw-r--r--libs/libmdbx/src/test/cases.cc1
-rw-r--r--libs/libmdbx/src/test/config.h3
-rw-r--r--libs/libmdbx/src/test/gc.sh6
-rw-r--r--libs/libmdbx/src/test/keygen.cc4
-rw-r--r--libs/libmdbx/src/test/keygen.h4
-rw-r--r--libs/libmdbx/src/test/main.cc11
-rw-r--r--libs/libmdbx/src/test/test.cc53
-rw-r--r--libs/libmdbx/src/test/test.h8
-rw-r--r--libs/libmdbx/src/test/test.vcxproj1
-rw-r--r--libs/libmdbx/src/test/ttl.cc137
-rw-r--r--libs/libmdbx/src/test/utils.cc15
-rw-r--r--libs/libmdbx/src/test/utils.h57
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 &params,
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);