diff --git a/mdbx.h b/mdbx.h index 0123a139..c1fdd90e 100644 --- a/mdbx.h +++ b/mdbx.h @@ -4929,6 +4929,14 @@ LIBMDBX_API int mdbx_cursor_compare(const MDBX_cursor *left, * \retval MDBX_EINVAL An invalid parameter was specified. */ LIBMDBX_API int mdbx_cursor_get(MDBX_cursor *cursor, MDBX_val *key, MDBX_val *data, MDBX_cursor_op op); +/** FIXME */ +typedef int(MDBX_predicate_func)(void *context, MDBX_val *key, MDBX_val *value, + void *arg) MDBX_CXX17_NOEXCEPT; +/** FIXME */ +LIBMDBX_API int mdbx_cursor_scan(MDBX_cursor *cursor, + MDBX_predicate_func *predicate, void *context, + MDBX_cursor_op start_op, + MDBX_cursor_op turn_op, void *arg); /** \brief Retrieve multiple non-dupsort key/value pairs by cursor. * \ingroup c_crud diff --git a/src/core.c b/src/core.c index 2e577a93..23eaaa92 100644 --- a/src/core.c +++ b/src/core.c @@ -17433,6 +17433,39 @@ int mdbx_cursor_get(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, return cursor_get(mc, key, data, op); } +int mdbx_cursor_scan(MDBX_cursor *mc, MDBX_predicate_func *predicate, + void *context, MDBX_cursor_op start_op, + MDBX_cursor_op turn_op, void *arg) { + if (unlikely(!predicate)) + return MDBX_EINVAL; + + const unsigned valid_start_mask = + 1 << MDBX_FIRST | 1 << MDBX_FIRST_DUP | 1 << MDBX_LAST | + 1 << MDBX_LAST_DUP | 1 << MDBX_GET_CURRENT | 1 << MDBX_GET_MULTIPLE; + if (unlikely(start_op > 30 || ((1 << start_op) & valid_start_mask) == 0)) + return MDBX_EINVAL; + + const unsigned valid_turn_mask = + 1 << MDBX_NEXT | 1 << MDBX_NEXT_DUP | 1 << MDBX_NEXT_NODUP | + 1 << MDBX_PREV | 1 << MDBX_PREV_DUP | 1 << MDBX_PREV_NODUP | + 1 << MDBX_NEXT_MULTIPLE | 1 << MDBX_PREV_MULTIPLE; + if (unlikely(turn_op > 30 || ((1 << turn_op) & valid_turn_mask) == 0)) + return MDBX_EINVAL; + + MDBX_val key, data; + int rc = mdbx_cursor_get(mc, &key, &data, start_op); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; + for (;;) { + rc = predicate(context, &key, &data, arg); + if (rc != MDBX_RESULT_FALSE) + return rc; + rc = cursor_get(mc, &key, &data, turn_op); + if (rc != MDBX_SUCCESS) + return (rc == MDBX_NOTFOUND) ? MDBX_RESULT_FALSE : rc; + } +} + static int cursor_first_batch(MDBX_cursor *mc) { if (!(mc->mc_flags & C_INITIALIZED) || mc->mc_top) { int err = page_search(mc, NULL, MDBX_PS_FIRST);