lmdb: provide both interfaces - advanced 'mdbx' and original 'lmdb'.

This commit is contained in:
Leo Yuriev 2015-10-13 15:46:59 +03:00
parent 09c140c1f4
commit 68171d5f5d
21 changed files with 664 additions and 384 deletions

10
.gitignore vendored
View File

@ -1,11 +1,11 @@
mtest[0123456] mtest[0123456]
wbench wbench
testdb testdb
mdb_copy mdbx_copy
mdb_stat mdbx_stat
mdb_dump mdbx_dump
mdb_load mdbx_load
mdb_chk mdbx_chk
*.lo *.lo
*.[ao] *.[ao]
*.so *.so

151
Makefile
View File

@ -20,17 +20,26 @@
CC ?= gcc CC ?= gcc
CFLAGS ?= -O2 -g -Wall -Werror -Wno-unused-parameter CFLAGS ?= -O2 -g -Wall -Werror -Wno-unused-parameter
CFLAGS += -pthread CFLAGS += -pthread
LDLIBS = -Wl,--no-as-needed -lrt
prefix ?= /usr/local prefix ?= /usr/local
######################################################################## ########################################################################
IHDRS = lmdb.h IHDRS := lmdb.h mdbx.h
ILIBS = liblmdb.a liblmdb.so ILIBS := libmdbx.a libmdbx.so
IPROGS = mdb_stat mdb_copy mdb_dump mdb_load mdb_chk IPROGS := mdbx_stat mdbx_copy mdbx_dump mdbx_load mdbx_chk
IDOCS = mdb_stat.1 mdb_copy.1 mdb_dump.1 mdb_load.1 IDOCS := mdb_stat.1 mdb_copy.1 mdb_dump.1 mdb_load.1
PROGS = $(IPROGS) mtest0 mtest1 mtest2 mtest3 mtest4 mtest5 mtest6 wbench PROGS := $(IPROGS) mtest0 mtest1 mtest2 mtest3 mtest4 mtest5 mtest6 wbench
all: $(ILIBS) $(PROGS)
SRC_LMDB := mdb.c midl.c lmdb.h midl.h
SRC_MDBX := $(SRC_LMDB) mdbx.h
.PHONY: mdbx lmdb all install clean test coverage
all: $(ILIBS) $(IPROGS)
mdbx: libmdbx.a libmdbx.so
lmdb: liblmdb.a liblmdb.so
install: $(ILIBS) $(IPROGS) $(IHDRS) install: $(ILIBS) $(IPROGS) $(IHDRS)
mkdir -p $(DESTDIR)$(prefix)/bin mkdir -p $(DESTDIR)$(prefix)/bin
@ -43,73 +52,95 @@ install: $(ILIBS) $(IPROGS) $(IHDRS)
for f in $(IDOCS); do cp $$f $(DESTDIR)$(prefix)/man/man1; done for f in $(IDOCS); do cp $$f $(DESTDIR)$(prefix)/man/man1; done
clean: clean:
rm -rf $(PROGS) *.[ao] *.[ls]o *~ testdb/* rm -rf $(PROGS) @* *.[ao] *.[ls]o *~ testdb/* *.gcov
test: all test: mdbx $(PROGS)
[ -d testdb ] || mkdir testdb && rm -f testdb/* \ [ -d testdb ] || mkdir testdb && rm -f testdb/* \
&& echo "*** LMDB-TEST-0" && ./mtest0 && ./mdb_chk -v testdb \ && echo "*** LMDB-TEST-0" && ./mtest0 && ./mdbx_chk -v testdb \
&& echo "*** LMDB-TEST-1" && ./mtest1 && ./mdb_chk -v testdb \ && echo "*** LMDB-TEST-1" && ./mtest1 && ./mdbx_chk -v testdb \
&& echo "*** LMDB-TEST-2" && ./mtest2 && ./mdb_chk -v testdb \ && echo "*** LMDB-TEST-2" && ./mtest2 && ./mdbx_chk -v testdb \
&& echo "*** LMDB-TEST-3" && ./mtest3 && ./mdb_chk -v testdb \ && echo "*** LMDB-TEST-3" && ./mtest3 && ./mdbx_chk -v testdb \
&& echo "*** LMDB-TEST-4" && ./mtest4 && ./mdb_chk -v testdb \ && echo "*** LMDB-TEST-4" && ./mtest4 && ./mdbx_chk -v testdb \
&& echo "*** LMDB-TEST-5" && ./mtest5 && ./mdb_chk -v testdb \ && echo "*** LMDB-TEST-5" && ./mtest5 && ./mdbx_chk -v testdb \
&& echo "*** LMDB-TEST-6" && ./mtest6 && ./mdb_chk -v testdb \ && echo "*** LMDB-TEST-6" && ./mtest6 && ./mdbx_chk -v testdb \
&& echo "*** LMDB-TESTs - all done" && echo "*** LMDB-TESTs - all done"
liblmdb.a: mdb.o midl.o libmdbx.a: mdbx.o
$(AR) rs $@ mdb.o midl.o $(AR) rs $@ $^
liblmdb.so: mdb.lo midl.lo libmdbx.so: mdbx.lo
# $(CC) $(LDFLAGS) -pthread -shared -Wl,-Bsymbolic -o $@ mdb.o midl.o $(SOLIBS) $(CC) $(CFLAGS) $(LDFLAGS) -pthread -shared -o $@ $^
$(CC) $(LDFLAGS) -pthread -shared -o $@ mdb.lo midl.lo $(SOLIBS)
mdb_stat: mdb_stat.o liblmdb.a liblmdb.a: lmdb.o
mdb_copy: mdb_copy.o liblmdb.a $(AR) rs $@ $^
mdb_dump: mdb_dump.o liblmdb.a
mdb_load: mdb_load.o liblmdb.a
mdb_chk: mdb_chk.o liblmdb.a
mtest0: mtest0.o liblmdb.a
mtest1: mtest1.o liblmdb.a
mtest2: mtest2.o liblmdb.a
mtest3: mtest3.o liblmdb.a
mtest4: mtest4.o liblmdb.a
mtest5: mtest5.o liblmdb.a
mtest6: mtest6.o liblmdb.a
wbench: wbench.o liblmdb.a
mdb.o: mdb.c lmdb.h midl.h liblmdb.so: lmdb.lo
$(CC) $(CFLAGS) $(CPPFLAGS) -c mdb.c $(CC) $(CFLAGS) $(LDFLAGS) -pthread -shared -o $@ $^
midl.o: midl.c midl.h mdbx_stat: mdb_stat.o mdbx.o
$(CC) $(CFLAGS) $(CPPFLAGS) -c midl.c $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
mdb.lo: mdb.c lmdb.h midl.h mdbx_copy: mdb_copy.o mdbx.o
$(CC) $(CFLAGS) -fPIC $(CPPFLAGS) -c mdb.c -o $@ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
midl.lo: midl.c midl.h mdbx_dump: mdb_dump.o mdbx.o
$(CC) $(CFLAGS) -fPIC $(CPPFLAGS) -c midl.c -o $@ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
mdbx_load: mdb_load.o mdbx.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
mdbx_chk: mdb_chk.o mdbx.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ -lrt
mtest0: mtest0.o mdbx.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
mtest1: mtest1.o mdbx.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
mtest2: mtest2.o mdbx.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
mtest3: mtest3.o mdbx.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
mtest4: mtest4.o mdbx.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
mtest5: mtest5.o mdbx.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
mtest6: mtest6.o mdbx.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
wbench: wbench.o mdbx.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ -lrt
mdbx.o: $(SRC_MDBX)
$(CC) $(CFLAGS) -include mdbx.h -c mdb.c -o $@
mdbx.lo: $(SRC_MDBX)
$(CC) $(CFLAGS) -include mdbx.h -fPIC -c mdb.c -o $@
lmdb.o: $(SRC_LMDB)
$(CC) $(CFLAGS) -c mdb.c -o $@
lmdb.lo: $(SRC_LMDB)
$(CC) $(CFLAGS) -fPIC -c mdb.c -o $@
%: %.o %: %.o
$(CC) $(CFLAGS) $(LDFLAGS) $^ $(LDLIBS) -o $@ $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@
%.o: %.c lmdb.h %.o: %.c lmdb.h mdbx.h
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< $(CC) $(CFLAGS) -c $<
COV_FLAGS=-fprofile-arcs -ftest-coverage COV_FLAGS=-fprofile-arcs -ftest-coverage
COV_OBJS=xmdb.o xmidl.o
coverage: xmtest @gcov-mdb.o: $(SRC_MDBX)
for i in mtest*.c [0-9]*.c; do j=`basename \$$i .c`; $(MAKE) $$j.o; \ $(CC) $(CFLAGS) $(COV_FLAGS) -O0 -include mdbx.h -c mdb.c -o $@
gcc -o x$$j $$j.o $(COV_OBJS) -pthread $(COV_FLAGS); \
rm -rf testdb; mkdir testdb; ./x$$j; done
gcov xmdb.c
gcov xmidl.c
xmtest: mtest.o xmdb.o xmidl.o coverage: @gcov-mdb.o
gcc -o xmtest mtest.o xmdb.o xmidl.o -pthread $(COV_FLAGS) for t in mtest*.c; do x=`basename \$$t .c`; $(MAKE) $$x.o; \
gcc -o @gcov-$$x $$x.o $^ -pthread $(COV_FLAGS); \
xmdb.o: mdb.c lmdb.h midl.h rm -rf testdb; mkdir testdb; ./@gcov-$$x; done
$(CC) $(CFLAGS) -fPIC $(CPPFLAGS) -O0 $(COV_FLAGS) -c mdb.c -o $@ gcov @gcov-mdb
xmidl.o: midl.c midl.h
$(CC) $(CFLAGS) -fPIC $(CPPFLAGS) -O0 $(COV_FLAGS) -c midl.c -o $@

65
lmdb.h
View File

@ -155,6 +155,10 @@
#ifndef _LMDB_H_ #ifndef _LMDB_H_
#define _LMDB_H_ #define _LMDB_H_
#ifndef MDBX_MODE_ENABLED
# define MDBX_MODE_ENABLED 0
#endif /* MDBX_MODE_ENABLED */
#include <sys/types.h> #include <sys/types.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdint.h> #include <stdint.h>
@ -284,11 +288,15 @@ typedef void (MDB_rel_func)(MDB_val *item, void *oldptr, void *newptr, void *rel
#define MDB_NORDAHEAD 0x800000 #define MDB_NORDAHEAD 0x800000
/** don't initialize malloc'd memory before writing to datafile */ /** don't initialize malloc'd memory before writing to datafile */
#define MDB_NOMEMINIT 0x1000000 #define MDB_NOMEMINIT 0x1000000
#if MDBX_MODE_ENABLED
/** aim to coalesce FreeDB records */ /** aim to coalesce FreeDB records */
#define MDB_COALESCE 0x2000000 #define MDB_COALESCE 0x2000000
/** LIFO policy for reclaiming FreeDB records */ /** LIFO policy for reclaiming FreeDB records */
#define MDB_LIFORECLAIM 0x4000000 #define MDB_LIFORECLAIM 0x4000000
/** make a steady-sync only on close and explicit env-sync */ #endif /* MDBX_MODE_ENABLED */
/** make a steady-sync only on close and explicit env-sync */
#define MDB_UTTERLY_NOSYNC (MDB_NOSYNC|MDB_MAPASYNC) #define MDB_UTTERLY_NOSYNC (MDB_NOSYNC|MDB_MAPASYNC)
/** @} */ /** @} */
@ -446,18 +454,31 @@ typedef struct MDB_stat {
size_t ms_entries; /**< Number of data items */ size_t ms_entries; /**< Number of data items */
} MDB_stat; } MDB_stat;
typedef struct MDBX_stat {
MDB_stat base;
#if MDBX_MODE_ENABLED
/* LY: TODO */
#endif /* MDBX_MODE_ENABLED */
} MDBX_stat;
/** @brief Information about the environment */ /** @brief Information about the environment */
typedef struct MDB_envinfo { typedef struct MDB_envinfo {
void *me_mapaddr; /**< Address of map, if fixed */ void *me_mapaddr; /**< Address of map, if fixed */
size_t me_mapsize; /**< Size of the data memory map */ size_t me_mapsize; /**< Size of the data memory map */
size_t me_last_pgno; /**< ID of the last used page */ size_t me_last_pgno; /**< ID of the last used page */
size_t me_last_txnid; /**< ID of the last committed transaction */ size_t me_last_txnid; /**< ID of the last committed transaction */
size_t me_tail_txnid; /**< ID of the last reader transaction */
unsigned me_maxreaders; /**< max reader slots in the environment */ unsigned me_maxreaders; /**< max reader slots in the environment */
unsigned me_numreaders; /**< max reader slots used in the environment */ unsigned me_numreaders; /**< max reader slots used in the environment */
} MDB_envinfo;
typedef struct MDBX_envinfo {
MDB_envinfo base;
#if MDBX_MODE_ENABLED
size_t me_tail_txnid; /**< ID of the last reader transaction */
size_t me_meta1_txnid, me_meta1_sign; size_t me_meta1_txnid, me_meta1_sign;
size_t me_meta2_txnid, me_meta2_sign; size_t me_meta2_txnid, me_meta2_sign;
} MDB_envinfo; #endif /* MDBX_MODE_ENABLED */
} MDBX_envinfo;
/** @brief Return the LMDB library version information. /** @brief Return the LMDB library version information.
* *
@ -618,7 +639,9 @@ int mdb_env_create(MDB_env **env);
* </ul> * </ul>
*/ */
int mdb_env_open(MDB_env *env, const char *path, unsigned flags, mode_t mode); int mdb_env_open(MDB_env *env, const char *path, unsigned flags, mode_t mode);
int mdb_env_open_ex(MDB_env *env, const char *path, unsigned flags, mode_t mode, int *exclusive); #if MDBX_MODE_ENABLED
int mdbx_env_open_ex(MDB_env *env, const char *path, unsigned flags, mode_t mode, int *exclusive);
#endif /* MDBX_MODE_ENABLED */
/** @brief Copy an LMDB environment to the specified path. /** @brief Copy an LMDB environment to the specified path.
* *
* This function may be used to make a backup of an existing environment. * This function may be used to make a backup of an existing environment.
@ -700,6 +723,9 @@ int mdb_env_copyfd2(MDB_env *env, mdb_filehandle_t fd, unsigned flags);
* where the statistics will be copied * where the statistics will be copied
*/ */
int mdb_env_stat(MDB_env *env, MDB_stat *stat); int mdb_env_stat(MDB_env *env, MDB_stat *stat);
#if MDBX_MODE_ENABLED
int mdbx_env_stat(MDB_env *env, MDBX_stat *stat, size_t bytes);
#endif /* MDBX_MODE_ENABLED */
/** @brief Return information about the LMDB environment. /** @brief Return information about the LMDB environment.
* *
@ -708,6 +734,9 @@ int mdb_env_stat(MDB_env *env, MDB_stat *stat);
* where the information will be copied * where the information will be copied
*/ */
int mdb_env_info(MDB_env *env, MDB_envinfo *info); int mdb_env_info(MDB_env *env, MDB_envinfo *info);
#if MDBX_MODE_ENABLED
int mdbx_env_info(MDB_env *env, MDBX_envinfo *info, size_t bytes);
#endif /* MDBX_MODE_ENABLED */
/** @brief Flush the data buffers to disk. /** @brief Flush the data buffers to disk.
* *
@ -744,7 +773,9 @@ int mdb_env_sync(MDB_env *env, int force);
* checkpoint (meta-page update) will rolledback for consistency guarantee. * checkpoint (meta-page update) will rolledback for consistency guarantee.
*/ */
void mdb_env_close(MDB_env *env); void mdb_env_close(MDB_env *env);
void mdb_env_close_ex(MDB_env *env, int dont_sync); #if MDBX_MODE_ENABLED
void mdbx_env_close_ex(MDB_env *env, int dont_sync);
#endif /* MDBX_MODE_ENABLED */
/** @brief Set environment flags. /** @brief Set environment flags.
* *
@ -927,6 +958,7 @@ typedef void MDB_assert_func(MDB_env *env, const char *msg,
*/ */
int mdb_env_set_assert(MDB_env *env, MDB_assert_func *func); int mdb_env_set_assert(MDB_env *env, MDB_assert_func *func);
#if MDBX_MODE_ENABLED
/** @brief Set threshold to force flush the data buffers to disk, /** @brief Set threshold to force flush the data buffers to disk,
* even of #MDB_NOSYNC, #MDB_NOMETASYNC and #MDB_MAPASYNC flags * even of #MDB_NOSYNC, #MDB_NOMETASYNC and #MDB_MAPASYNC flags
* in the environment. * in the environment.
@ -944,7 +976,8 @@ int mdb_env_set_assert(MDB_env *env, MDB_assert_func *func);
* when a synchronous flush would be made. * when a synchronous flush would be made.
* @return A non-zero error value on failure and 0 on success. * @return A non-zero error value on failure and 0 on success.
*/ */
int mdb_env_set_syncbytes(MDB_env *env, size_t bytes); int mdbx_env_set_syncbytes(MDB_env *env, size_t bytes);
#endif /* MDBX_MODE_ENABLED */
/** @brief Create a transaction for use with the environment. /** @brief Create a transaction for use with the environment.
* *
@ -1149,6 +1182,9 @@ int mdb_dbi_open(MDB_txn *txn, const char *name, unsigned flags, MDB_dbi *dbi);
* </ul> * </ul>
*/ */
int mdb_stat(MDB_txn *txn, MDB_dbi dbi, MDB_stat *stat); int mdb_stat(MDB_txn *txn, MDB_dbi dbi, MDB_stat *stat);
#if MDBX_MODE_ENABLED
int mdbx_stat(MDB_txn *txn, MDB_dbi dbi, MDBX_stat *stat, size_t bytes);
#endif /* MDBX_MODE_ENABLED */
/** @brief Retrieve the DB flags for a database handle. /** @brief Retrieve the DB flags for a database handle.
* *
@ -1597,6 +1633,7 @@ int mdb_reader_list(MDB_env *env, MDB_msg_func *func, void *ctx);
*/ */
int mdb_reader_check(MDB_env *env, int *dead); int mdb_reader_check(MDB_env *env, int *dead);
#if MDBX_MODE_ENABLED
/** @brief Returns a lag of the reading. /** @brief Returns a lag of the reading.
* *
* Returns an information for estimate how much given read-only * Returns an information for estimate how much given read-only
@ -1606,7 +1643,7 @@ int mdb_reader_check(MDB_env *env, int *dead);
* @param[out] percent Percentage of page allocation in the database. * @param[out] percent Percentage of page allocation in the database.
* @return Number of transactions committed after the given was started for read, or -1 on failure. * @return Number of transactions committed after the given was started for read, or -1 on failure.
*/ */
int mdb_txn_straggler(MDB_txn *txnm, int *percent); int mdbx_txn_straggler(MDB_txn *txnm, int *percent);
/** @brief A callback function for killing a laggard readers, /** @brief A callback function for killing a laggard readers,
* called in case of MDB_MAP_FULL error. * called in case of MDB_MAP_FULL error.
@ -1630,7 +1667,7 @@ typedef int (MDB_oom_func)(MDB_env *env, int pid, void* thread_id, size_t txn, u
* @param[in] env An environment handle returned by #mdb_env_create(). * @param[in] env An environment handle returned by #mdb_env_create().
* @param[in] oomfunc A #MDB_oom_func function or NULL to disable. * @param[in] oomfunc A #MDB_oom_func function or NULL to disable.
*/ */
void mdb_env_set_oomfunc(MDB_env *env, MDB_oom_func *oom_func); void mdbx_env_set_oomfunc(MDB_env *env, MDB_oom_func *oom_func);
/** @brief Get the current oom_func callback. /** @brief Get the current oom_func callback.
* *
@ -1640,9 +1677,11 @@ void mdb_env_set_oomfunc(MDB_env *env, MDB_oom_func *oom_func);
* @param[in] env An environment handle returned by #mdb_env_create(). * @param[in] env An environment handle returned by #mdb_env_create().
* @return A #MDB_oom_func function or NULL if disabled. * @return A #MDB_oom_func function or NULL if disabled.
*/ */
MDB_oom_func* mdb_env_get_oomfunc(MDB_env *env); MDB_oom_func* mdbx_env_get_oomfunc(MDB_env *env);
#endif /* MDBX_MODE_ENABLED */
/** @} */ /** @} */
#if MDBX_MODE_ENABLED
#define MDB_DBG_ASSERT 1 #define MDB_DBG_ASSERT 1
#define MDB_DBG_PRINT 2 #define MDB_DBG_PRINT 2
#define MDB_DBG_TRACE 4 #define MDB_DBG_TRACE 4
@ -1656,12 +1695,13 @@ MDB_oom_func* mdb_env_get_oomfunc(MDB_env *env);
typedef void MDB_debug_func(int type, const char *function, int line, typedef void MDB_debug_func(int type, const char *function, int line,
const char *msg, va_list args); const char *msg, va_list args);
int mdb_setup_debug(int flags, MDB_debug_func* logger, long edge_txn); int mdbx_setup_debug(int flags, MDB_debug_func* logger, long edge_txn);
typedef int MDB_pgvisitor_func(size_t pgno, unsigned pgnumber, void* ctx, typedef int MDB_pgvisitor_func(size_t pgno, unsigned pgnumber, void* ctx,
const char* dbi, const char *type, int nentries, const char* dbi, const char *type, int nentries,
int payload_bytes, int header_bytes, int unused_bytes); int payload_bytes, int header_bytes, int unused_bytes);
int mdb_env_pgwalk(MDB_txn *txn, MDB_pgvisitor_func* visitor, void* ctx); int mdbx_env_pgwalk(MDB_txn *txn, MDB_pgvisitor_func* visitor, void* ctx);
#endif /* MDBX_MODE_ENABLED */
char* mdb_dkey(MDB_val *key, char *buf); char* mdb_dkey(MDB_val *key, char *buf);
@ -1669,7 +1709,8 @@ char* mdb_dkey(MDB_val *key, char *buf);
} }
#endif #endif
/** @page tools LMDB Command Line Tools /** @page tools LMDB Command Line Tools
The following describes the command line tools that are available for LMDB. The following describes the command line tools that are available for LMDBX.
\li \ref mdb_chk_1
\li \ref mdb_copy_1 \li \ref mdb_copy_1
\li \ref mdb_dump_1 \li \ref mdb_dump_1
\li \ref mdb_load_1 \li \ref mdb_load_1

379
mdb.c
View File

@ -37,8 +37,8 @@
# define MDB_DEBUG 0 # define MDB_DEBUG 0
#endif #endif
#include "reopen.h" #include "./reopen.h"
#include "barriers.h" #include "./barriers.h"
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
@ -96,8 +96,19 @@
# define MISALIGNED_OK 1 # define MISALIGNED_OK 1
#endif #endif
#include "lmdb.h" #include "./lmdb.h"
#include "midl.h" #include "./midl.h"
#if ! MDBX_MODE_ENABLED
# define MDB_COALESCE 0
# define MDB_LIFORECLAIM 0
# define MDB_DBG_ASSERT 0
# define MDB_DBG_PRINT 0
# define MDB_DBG_TRACE 0
# define MDB_DBG_EXTRA 0
# define MDB_DBG_AUDIT 0
# define MDB_DBG_EDGE 0
#endif
#if (BYTE_ORDER == LITTLE_ENDIAN) == (BYTE_ORDER == BIG_ENDIAN) #if (BYTE_ORDER == LITTLE_ENDIAN) == (BYTE_ORDER == BIG_ENDIAN)
# error "Unknown or unsupported endianness (BYTE_ORDER)" # error "Unknown or unsupported endianness (BYTE_ORDER)"
@ -105,17 +116,6 @@
# error "Two's complement, reasonably sized integer types, please" # error "Two's complement, reasonably sized integer types, please"
#endif #endif
#ifdef __GNUC__
/** Put infrequently used env functions in separate section */
# ifdef __APPLE__
# define ESECT __attribute__ ((section("__TEXT,text_env")))
# else
# define ESECT __attribute__ ((section("text_env")))
# endif
#else
# define ESECT
#endif
/** @defgroup internal LMDB Internals /** @defgroup internal LMDB Internals
* @{ * @{
*/ */
@ -955,7 +955,9 @@ struct MDB_env {
#endif #endif
uint64_t me_sync_pending; /**< Total dirty/commited bytes since the last mdb_env_sync() */ uint64_t me_sync_pending; /**< Total dirty/commited bytes since the last mdb_env_sync() */
uint64_t me_sync_threshold; /**< Treshold of above to force synchronous flush */ uint64_t me_sync_threshold; /**< Treshold of above to force synchronous flush */
#if MDBX_MODE_ENABLED
MDB_oom_func *me_oom_func; /**< Callback for kicking laggard readers */ MDB_oom_func *me_oom_func; /**< Callback for kicking laggard readers */
#endif
#ifdef USE_VALGRIND #ifdef USE_VALGRIND
int me_valgrind_handle; int me_valgrind_handle;
#endif #endif
@ -1067,7 +1069,7 @@ static MDB_cmp_func mdb_cmp_memn, mdb_cmp_memnr, mdb_cmp_int_ai, mdb_cmp_int_a2,
/** @endcond */ /** @endcond */
/** Return the library version info. */ /** Return the library version info. */
char * ESECT char * __cold
mdb_version(int *major, int *minor, int *patch) mdb_version(int *major, int *minor, int *patch)
{ {
if (major) *major = MDB_VERSION_MAJOR; if (major) *major = MDB_VERSION_MAJOR;
@ -1100,7 +1102,7 @@ static char *const mdb_errstr[] = {
"MDB_BAD_DBI: The specified DBI handle was closed/changed unexpectedly", "MDB_BAD_DBI: The specified DBI handle was closed/changed unexpectedly",
}; };
char * ESECT char * __cold
mdb_strerror(int err) mdb_strerror(int err)
{ {
int i; int i;
@ -1115,6 +1117,8 @@ mdb_strerror(int err)
return strerror(err); return strerror(err);
} }
#if MDBX_MODE_ENABLED
int mdb_runtime_flags = MDB_DBG_PRINT int mdb_runtime_flags = MDB_DBG_PRINT
#if MDB_DEBUG #if MDB_DEBUG
| MDB_DBG_ASSERT | MDB_DBG_ASSERT
@ -1132,12 +1136,17 @@ int mdb_runtime_flags = MDB_DBG_PRINT
static MDB_debug_func *mdb_debug_logger; static MDB_debug_func *mdb_debug_logger;
#else /* MDBX_MODE_ENABLED */
# define mdb_runtime_flags 0
# define mdb_debug_logger ((void (*)(int, ...)) NULL)
#endif /* ! MDBX_MODE_ENABLED */
#if MDB_DEBUG #if MDB_DEBUG
static txnid_t mdb_debug_edge; static txnid_t mdb_debug_edge;
static void mdb_debug_log(int type, const char *function, int line, static void mdb_debug_log(int type, const char *function, int line,
const char *fmt, ...); const char *fmt, ...);
static void ESECT static void __cold
mdb_assert_fail(MDB_env *env, const char *msg, mdb_assert_fail(MDB_env *env, const char *msg,
const char *func, int line) const char *func, int line)
{ {
@ -1168,8 +1177,9 @@ static MDB_debug_func *mdb_debug_logger;
__assert_fail(msg, __FILE__, line, func) __assert_fail(msg, __FILE__, line, func)
#endif /* MDB_DEBUG */ #endif /* MDB_DEBUG */
int ESECT #if MDBX_MODE_ENABLED
mdb_setup_debug(int flags, MDB_debug_func* logger, long edge_txn) { int __cold
mdbx_setup_debug(int flags, MDB_debug_func* logger, long edge_txn) {
unsigned ret = mdb_runtime_flags; unsigned ret = mdb_runtime_flags;
if (flags != (int) MDB_DBG_DNT) if (flags != (int) MDB_DBG_DNT)
mdb_runtime_flags = flags; mdb_runtime_flags = flags;
@ -1181,8 +1191,9 @@ mdb_setup_debug(int flags, MDB_debug_func* logger, long edge_txn) {
#endif #endif
return ret; return ret;
} }
#endif /* MDBX_MODE_ENABLED */
static void ESECT static void __cold
mdb_debug_log(int type, const char *function, int line, mdb_debug_log(int type, const char *function, int line,
const char *fmt, ...) const char *fmt, ...)
{ {
@ -1881,17 +1892,14 @@ mdb_find_oldest(MDB_env *env, int *laggard)
return oldest; return oldest;
} }
static int ESECT static int __cold
mdb_oomkick(MDB_env *env, txnid_t oldest) mdb_oomkick(MDB_env *env, txnid_t oldest)
{ {
int retry; int retry;
txnid_t snap; txnid_t snap;
for(retry = 0; ; ++retry) { for(retry = 0; ; ++retry) {
MDB_reader *r; int reader;
pthread_t tid;
pid_t pid;
int rc, reader;
if (mdb_reader_check(env, NULL)) if (mdb_reader_check(env, NULL))
break; break;
@ -1903,28 +1911,39 @@ mdb_oomkick(MDB_env *env, txnid_t oldest)
if (reader < 0) if (reader < 0)
return 0; return 0;
if (!env->me_oom_func) #if MDBX_MODE_ENABLED
break; {
MDB_reader *r;
pthread_t tid;
pid_t pid;
int rc;
r = &env->me_txns->mti_readers[ reader ]; if (!env->me_oom_func)
pid = r->mr_pid; break;
tid = r->mr_tid;
if (r->mr_txnid != oldest || pid <= 0)
continue;
rc = env->me_oom_func(env, pid, (void*) tid, oldest, r = &env->me_txns->mti_readers[ reader ];
mdb_meta_head_w(env)->mm_txnid - oldest, retry); pid = r->mr_pid;
if (rc < 0) tid = r->mr_tid;
break; if (r->mr_txnid != oldest || pid <= 0)
continue;
if (rc) { rc = env->me_oom_func(env, pid, (void*) tid, oldest,
r->mr_txnid = (txnid_t)-1L; mdb_meta_head_w(env)->mm_txnid - oldest, retry);
if (rc > 1) { if (rc < 0)
r->mr_tid = 0; break;
r->mr_pid = 0;
mdb_coherent_barrier(); if (rc) {
r->mr_txnid = (txnid_t)-1L;
if (rc > 1) {
r->mr_tid = 0;
r->mr_pid = 0;
mdb_coherent_barrier();
}
} }
} }
#else
break;
#endif /* MDBX_MODE_ENABLED */
} }
snap = mdb_find_oldest(env, NULL); snap = mdb_find_oldest(env, NULL);
@ -2958,7 +2977,7 @@ mdb_dbis_update(MDB_txn *txn, int keep)
} }
int int
mdb_txn_straggler(MDB_txn *txn, int *percent) mdbx_txn_straggler(MDB_txn *txn, int *percent)
{ {
MDB_env *env; MDB_env *env;
MDB_meta *meta; MDB_meta *meta;
@ -3777,7 +3796,7 @@ fail:
return rc; return rc;
} }
int ESECT int __cold
mdb_env_set_syncbytes(MDB_env *env, size_t bytes) { mdb_env_set_syncbytes(MDB_env *env, size_t bytes) {
env->me_sync_threshold = bytes; env->me_sync_threshold = bytes;
return env->me_map ? mdb_env_sync(env, 0) : 0; return env->me_map ? mdb_env_sync(env, 0) : 0;
@ -3789,7 +3808,7 @@ mdb_env_set_syncbytes(MDB_env *env, size_t bytes) {
* @param[out] meta address of where to store the meta information * @param[out] meta address of where to store the meta information
* @return 0 on success, non-zero on failure. * @return 0 on success, non-zero on failure.
*/ */
static int ESECT static int __cold
mdb_env_read_header(MDB_env *env, MDB_meta *meta) mdb_env_read_header(MDB_env *env, MDB_meta *meta)
{ {
MDB_metabuf pbuf; MDB_metabuf pbuf;
@ -3848,7 +3867,7 @@ mdb_env_read_header(MDB_env *env, MDB_meta *meta)
} }
/** Fill in most of the zeroed #MDB_meta for an empty database environment */ /** Fill in most of the zeroed #MDB_meta for an empty database environment */
static void ESECT static void __cold
mdb_env_init_meta0(MDB_env *env, MDB_meta *meta) mdb_env_init_meta0(MDB_env *env, MDB_meta *meta)
{ {
meta->mm_magic = MDB_MAGIC; meta->mm_magic = MDB_MAGIC;
@ -3868,7 +3887,7 @@ mdb_env_init_meta0(MDB_env *env, MDB_meta *meta)
* @param[in] meta the #MDB_meta to write * @param[in] meta the #MDB_meta to write
* @return 0 on success, non-zero on failure. * @return 0 on success, non-zero on failure.
*/ */
static int ESECT static int __cold
mdb_env_init_meta(MDB_env *env, MDB_meta *meta) mdb_env_init_meta(MDB_env *env, MDB_meta *meta)
{ {
MDB_page *p, *q; MDB_page *p, *q;
@ -4063,7 +4082,7 @@ fail:
return rc; return rc;
} }
int ESECT int __cold
mdb_env_create(MDB_env **env) mdb_env_create(MDB_env **env)
{ {
MDB_env *e; MDB_env *e;
@ -4083,7 +4102,7 @@ mdb_env_create(MDB_env **env)
return MDB_SUCCESS; return MDB_SUCCESS;
} }
static int ESECT static int __cold
mdb_env_map(MDB_env *env, void *addr) mdb_env_map(MDB_env *env, void *addr)
{ {
unsigned flags = env->me_flags; unsigned flags = env->me_flags;
@ -4133,7 +4152,7 @@ mdb_env_map(MDB_env *env, void *addr)
return MDB_SUCCESS; return MDB_SUCCESS;
} }
int ESECT int __cold
mdb_env_set_mapsize(MDB_env *env, size_t size) mdb_env_set_mapsize(MDB_env *env, size_t size)
{ {
/* If env is already open, caller is responsible for making /* If env is already open, caller is responsible for making
@ -4171,7 +4190,7 @@ mdb_env_set_mapsize(MDB_env *env, size_t size)
return MDB_SUCCESS; return MDB_SUCCESS;
} }
int ESECT int __cold
mdb_env_set_maxdbs(MDB_env *env, MDB_dbi dbs) mdb_env_set_maxdbs(MDB_env *env, MDB_dbi dbs)
{ {
if (env->me_map) if (env->me_map)
@ -4180,7 +4199,7 @@ mdb_env_set_maxdbs(MDB_env *env, MDB_dbi dbs)
return MDB_SUCCESS; return MDB_SUCCESS;
} }
int ESECT int __cold
mdb_env_set_maxreaders(MDB_env *env, unsigned readers) mdb_env_set_maxreaders(MDB_env *env, unsigned readers)
{ {
if (env->me_map || readers < 1) if (env->me_map || readers < 1)
@ -4189,7 +4208,7 @@ mdb_env_set_maxreaders(MDB_env *env, unsigned readers)
return MDB_SUCCESS; return MDB_SUCCESS;
} }
int ESECT int __cold
mdb_env_get_maxreaders(MDB_env *env, unsigned *readers) mdb_env_get_maxreaders(MDB_env *env, unsigned *readers)
{ {
if (!env || !readers) if (!env || !readers)
@ -4198,7 +4217,7 @@ mdb_env_get_maxreaders(MDB_env *env, unsigned *readers)
return MDB_SUCCESS; return MDB_SUCCESS;
} }
static int ESECT static int __cold
mdb_fsize(HANDLE fd, size_t *size) mdb_fsize(HANDLE fd, size_t *size)
{ {
struct stat st; struct stat st;
@ -4212,7 +4231,7 @@ mdb_fsize(HANDLE fd, size_t *size)
/** Further setup required for opening an LMDB environment /** Further setup required for opening an LMDB environment
*/ */
static int ESECT static int __cold
mdb_env_open2(MDB_env *env, MDB_meta *meta) mdb_env_open2(MDB_env *env, MDB_meta *meta)
{ {
unsigned flags = env->me_flags; unsigned flags = env->me_flags;
@ -4319,7 +4338,7 @@ mdb_env_reader_dest(void *ptr)
} }
/** Downgrade the exclusive lock on the region back to shared */ /** Downgrade the exclusive lock on the region back to shared */
static int ESECT static int __cold
mdb_env_share_locks(MDB_env *env, int *excl, MDB_meta *meta) mdb_env_share_locks(MDB_env *env, int *excl, MDB_meta *meta)
{ {
struct flock lock_info; struct flock lock_info;
@ -4341,7 +4360,7 @@ mdb_env_share_locks(MDB_env *env, int *excl, MDB_meta *meta)
/** Try to get exclusive lock, otherwise shared. /** Try to get exclusive lock, otherwise shared.
* Maintain *excl = -1: no/unknown lock, 0: shared, 1: exclusive. * Maintain *excl = -1: no/unknown lock, 0: shared, 1: exclusive.
*/ */
static int ESECT static int __cold
mdb_env_excl_lock(MDB_env *env, int *excl) mdb_env_excl_lock(MDB_env *env, int *excl)
{ {
int rc = 0; int rc = 0;
@ -4436,7 +4455,7 @@ mdb_hash_val(MDB_val *val, mdb_hash_t hval)
*/ */
static const char mdb_a85[]= "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~"; static const char mdb_a85[]= "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
static void ESECT static void __cold
mdb_pack85(unsigned long l, char *out) mdb_pack85(unsigned long l, char *out)
{ {
int i; int i;
@ -4447,7 +4466,7 @@ mdb_pack85(unsigned long l, char *out)
} }
} }
static void ESECT static void __cold
mdb_hash_enc(MDB_val *val, char *encbuf) mdb_hash_enc(MDB_val *val, char *encbuf)
{ {
mdb_hash_t h = mdb_hash_val(val, MDB_HASH_INIT); mdb_hash_t h = mdb_hash_val(val, MDB_HASH_INIT);
@ -4465,7 +4484,7 @@ mdb_hash_enc(MDB_val *val, char *encbuf)
* @param[in,out] excl In -1, out lock type: -1 none, 0 shared, 1 exclusive * @param[in,out] excl In -1, out lock type: -1 none, 0 shared, 1 exclusive
* @return 0 on success, non-zero on failure. * @return 0 on success, non-zero on failure.
*/ */
static int ESECT static int __cold
mdb_env_setup_locks(MDB_env *env, char *lpath, int mode, int *excl) mdb_env_setup_locks(MDB_env *env, char *lpath, int mode, int *excl)
{ {
int fdflags; int fdflags;
@ -4576,14 +4595,11 @@ fail:
# error "Persistent DB flags & env flags overlap, but both go in mm_flags" # error "Persistent DB flags & env flags overlap, but both go in mm_flags"
#endif #endif
int ESECT #if !MDBX_MODE_ENABLED
mdb_env_open(MDB_env *env, const char *path, unsigned flags, mode_t mode) static
{ #endif /* !MDBX_MODE_ENABLED*/
return mdb_env_open_ex(env, path, flags, mode, NULL); int __cold
} mdbx_env_open_ex(MDB_env *env, const char *path, unsigned flags, mode_t mode, int *exclusive)
int ESECT
mdb_env_open_ex(MDB_env *env, const char *path, unsigned flags, mode_t mode, int *exclusive)
{ {
int oflags, rc, len, excl = -1; int oflags, rc, len, excl = -1;
char *lpath, *dpath; char *lpath, *dpath;
@ -4720,8 +4736,14 @@ leave:
return rc; return rc;
} }
int __cold
mdb_env_open(MDB_env *env, const char *path, unsigned flags, mode_t mode)
{
return mdbx_env_open_ex(env, path, flags, mode, NULL);
}
/** Destroy resources from mdb_env_open(), clear our readers & DBIs */ /** Destroy resources from mdb_env_open(), clear our readers & DBIs */
static void ESECT static void __cold
mdb_env_close0(MDB_env *env) mdb_env_close0(MDB_env *env)
{ {
int i; int i;
@ -4791,14 +4813,11 @@ mdb_env_close0(MDB_env *env)
env->me_flags &= ~(MDB_ENV_ACTIVE|MDB_ENV_TXKEY); env->me_flags &= ~(MDB_ENV_ACTIVE|MDB_ENV_TXKEY);
} }
void ESECT #if !MDBX_MODE_ENABLED
mdb_env_close(MDB_env *env) static
{ #endif /* !MDBX_MODE_ENABLED*/
mdb_env_close_ex(env, 0); void __cold
} mdbx_env_close_ex(MDB_env *env, int dont_sync)
void ESECT
mdb_env_close_ex(MDB_env *env, int dont_sync)
{ {
MDB_page *dp; MDB_page *dp;
@ -4819,6 +4838,12 @@ mdb_env_close_ex(MDB_env *env, int dont_sync)
free(env); free(env);
} }
void __cold
mdb_env_close(MDB_env *env)
{
mdbx_env_close_ex(env, 0);
}
/** Compare two items pointing at aligned unsigned int's. */ /** Compare two items pointing at aligned unsigned int's. */
static long static long
mdb_cmp_int_ai(const MDB_val *a, const MDB_val *b) mdb_cmp_int_ai(const MDB_val *a, const MDB_val *b)
@ -8527,7 +8552,7 @@ typedef struct mdb_copy {
} mdb_copy; } mdb_copy;
/** Dedicated writer thread for compacting copy. */ /** Dedicated writer thread for compacting copy. */
static void* ESECT static void* __cold
mdb_env_copythr(void *arg) mdb_env_copythr(void *arg)
{ {
mdb_copy *my = arg; mdb_copy *my = arg;
@ -8585,7 +8610,7 @@ again:
} }
/** Tell the writer thread there's a buffer ready to write */ /** Tell the writer thread there's a buffer ready to write */
static int ESECT static int __cold
mdb_env_cthr_toggle(mdb_copy *my, int st) mdb_env_cthr_toggle(mdb_copy *my, int st)
{ {
int toggle = my->mc_toggle ^ 1; int toggle = my->mc_toggle ^ 1;
@ -8604,7 +8629,7 @@ mdb_env_cthr_toggle(mdb_copy *my, int st)
} }
/** Depth-first tree traversal for compacting copy. */ /** Depth-first tree traversal for compacting copy. */
static int ESECT static int __cold
mdb_env_cwalk(mdb_copy *my, pgno_t *pg, int flags) mdb_env_cwalk(mdb_copy *my, pgno_t *pg, int flags)
{ {
MDB_cursor mc; MDB_cursor mc;
@ -8762,7 +8787,7 @@ done:
} }
/** Copy environment with compaction. */ /** Copy environment with compaction. */
static int ESECT static int __cold
mdb_env_copyfd1(MDB_env *env, HANDLE fd) mdb_env_copyfd1(MDB_env *env, HANDLE fd)
{ {
MDB_meta *mm; MDB_meta *mm;
@ -8858,7 +8883,7 @@ mdb_env_copyfd1(MDB_env *env, HANDLE fd)
} }
/** Copy environment as-is. */ /** Copy environment as-is. */
static int ESECT static int __cold
mdb_env_copyfd0(MDB_env *env, HANDLE fd) mdb_env_copyfd0(MDB_env *env, HANDLE fd)
{ {
MDB_txn *txn = NULL; MDB_txn *txn = NULL;
@ -8949,7 +8974,7 @@ leave:
return rc; return rc;
} }
int ESECT int __cold
mdb_env_copyfd2(MDB_env *env, HANDLE fd, unsigned flags) mdb_env_copyfd2(MDB_env *env, HANDLE fd, unsigned flags)
{ {
if (flags & MDB_CP_COMPACT) if (flags & MDB_CP_COMPACT)
@ -8958,13 +8983,13 @@ mdb_env_copyfd2(MDB_env *env, HANDLE fd, unsigned flags)
return mdb_env_copyfd0(env, fd); return mdb_env_copyfd0(env, fd);
} }
int ESECT int __cold
mdb_env_copyfd(MDB_env *env, HANDLE fd) mdb_env_copyfd(MDB_env *env, HANDLE fd)
{ {
return mdb_env_copyfd2(env, fd, 0); return mdb_env_copyfd2(env, fd, 0);
} }
int ESECT int __cold
mdb_env_copy2(MDB_env *env, const char *path, unsigned flags) mdb_env_copy2(MDB_env *env, const char *path, unsigned flags)
{ {
int rc, len; int rc, len;
@ -9019,13 +9044,13 @@ leave:
return rc; return rc;
} }
int ESECT int __cold
mdb_env_copy(MDB_env *env, const char *path) mdb_env_copy(MDB_env *env, const char *path)
{ {
return mdb_env_copy2(env, path, 0); return mdb_env_copy2(env, path, 0);
} }
int ESECT int __cold
mdb_env_set_flags(MDB_env *env, unsigned flag, int onoff) mdb_env_set_flags(MDB_env *env, unsigned flag, int onoff)
{ {
if (unlikely(flag & ~CHANGEABLE)) if (unlikely(flag & ~CHANGEABLE))
@ -9037,7 +9062,7 @@ mdb_env_set_flags(MDB_env *env, unsigned flag, int onoff)
return MDB_SUCCESS; return MDB_SUCCESS;
} }
int ESECT int __cold
mdb_env_get_flags(MDB_env *env, unsigned *arg) mdb_env_get_flags(MDB_env *env, unsigned *arg)
{ {
if (unlikely(!env || !arg)) if (unlikely(!env || !arg))
@ -9047,7 +9072,7 @@ mdb_env_get_flags(MDB_env *env, unsigned *arg)
return MDB_SUCCESS; return MDB_SUCCESS;
} }
int ESECT int __cold
mdb_env_set_userctx(MDB_env *env, void *ctx) mdb_env_set_userctx(MDB_env *env, void *ctx)
{ {
if (unlikely(!env)) if (unlikely(!env))
@ -9056,13 +9081,13 @@ mdb_env_set_userctx(MDB_env *env, void *ctx)
return MDB_SUCCESS; return MDB_SUCCESS;
} }
void * ESECT void * __cold
mdb_env_get_userctx(MDB_env *env) mdb_env_get_userctx(MDB_env *env)
{ {
return env ? env->me_userctx : NULL; return env ? env->me_userctx : NULL;
} }
int ESECT int __cold
mdb_env_set_assert(MDB_env *env, MDB_assert_func *func) mdb_env_set_assert(MDB_env *env, MDB_assert_func *func)
{ {
if (unlikely(!env)) if (unlikely(!env))
@ -9073,7 +9098,7 @@ mdb_env_set_assert(MDB_env *env, MDB_assert_func *func)
return MDB_SUCCESS; return MDB_SUCCESS;
} }
int ESECT int __cold
mdb_env_get_path(MDB_env *env, const char **arg) mdb_env_get_path(MDB_env *env, const char **arg)
{ {
if (unlikely(!env || !arg)) if (unlikely(!env || !arg))
@ -9083,7 +9108,7 @@ mdb_env_get_path(MDB_env *env, const char **arg)
return MDB_SUCCESS; return MDB_SUCCESS;
} }
int ESECT int __cold
mdb_env_get_fd(MDB_env *env, mdb_filehandle_t *arg) mdb_env_get_fd(MDB_env *env, mdb_filehandle_t *arg)
{ {
if (unlikely(!env || !arg)) if (unlikely(!env || !arg))
@ -9099,7 +9124,7 @@ mdb_env_get_fd(MDB_env *env, mdb_filehandle_t *arg)
* @param[out] arg the address of an #MDB_stat structure to receive the stats. * @param[out] arg the address of an #MDB_stat structure to receive the stats.
* @return 0, this function always succeeds. * @return 0, this function always succeeds.
*/ */
static int ESECT static int __cold
mdb_stat0(MDB_env *env, MDB_db *db, MDB_stat *arg) mdb_stat0(MDB_env *env, MDB_db *db, MDB_stat *arg)
{ {
arg->ms_psize = env->me_psize; arg->ms_psize = env->me_psize;
@ -9112,61 +9137,99 @@ mdb_stat0(MDB_env *env, MDB_db *db, MDB_stat *arg)
return MDB_SUCCESS; return MDB_SUCCESS;
} }
int ESECT #if !MDBX_MODE_ENABLED
static
#endif /* !MDBX_MODE_ENABLED*/
int __cold
mdbx_env_stat(MDB_env *env, MDBX_stat *arg, size_t bytes)
{
MDB_meta *meta;
if (unlikely(env == NULL || arg == NULL))
return EINVAL;
if (unlikely(bytes != sizeof(MDBX_stat)))
return EINVAL;
meta = mdb_meta_head_r(env);
return mdb_stat0(env, &meta->mm_dbs[MAIN_DBI], &arg->base);
}
int __cold
mdb_env_stat(MDB_env *env, MDB_stat *arg) mdb_env_stat(MDB_env *env, MDB_stat *arg)
{
return mdbx_env_stat(env, (MDBX_stat *) arg, sizeof(MDB_stat));
}
#if !MDBX_MODE_ENABLED
static
#endif /* !MDBX_MODE_ENABLED*/
int __cold mdbx_env_info(MDB_env *env, MDBX_envinfo *arg, size_t bytes)
{ {
MDB_meta *meta; MDB_meta *meta;
if (unlikely(env == NULL || arg == NULL)) if (unlikely(env == NULL || arg == NULL))
return EINVAL; return EINVAL;
meta = mdb_meta_head_r(env); if (bytes == sizeof(MDB_envinfo)) {
return mdb_stat0(env, &meta->mm_dbs[MAIN_DBI], arg); do {
} meta = mdb_meta_head_r(env);
arg->base.me_last_txnid = meta->mm_txnid;
arg->base.me_last_pgno = meta->mm_last_pg;
arg->base.me_mapaddr = meta->mm_address;
arg->base.me_mapsize = env->me_mapsize;
arg->base.me_maxreaders = env->me_maxreaders;
arg->base.me_numreaders = env->me_txns->mti_numreaders;
} while (unlikely( arg->base.me_last_txnid != env->me_txns->mti_txnid));
#if MDBX_MODE_ENABLED
} else if (bytes == sizeof(MDBX_envinfo)) {
MDB_meta *m1, *m2;
MDB_reader *r;
int i;
int ESECT m1 = METAPAGE_1(env);
mdb_env_info(MDB_env *env, MDB_envinfo *arg) m2 = METAPAGE_2(env);
{
MDB_meta *meta, *m1, *m2;
if (unlikely(env == NULL || arg == NULL)) do {
return EINVAL; meta = mdb_meta_head_r(env);
arg->base.me_last_txnid = meta->mm_txnid;
arg->base.me_last_pgno = meta->mm_last_pg;
arg->me_meta1_txnid = m1->mm_txnid;
arg->me_meta1_sign = m1->mm_datasync_sign;
arg->me_meta2_txnid = m2->mm_txnid;
arg->me_meta2_sign = m2->mm_datasync_sign;
} while (unlikely( arg->base.me_last_txnid != env->me_txns->mti_txnid
|| arg->me_meta1_sign != m1->mm_datasync_sign
|| arg->me_meta2_sign != m2->mm_datasync_sign ));
m1 = METAPAGE_1(env); arg->base.me_mapaddr = meta->mm_address;
m2 = METAPAGE_2(env); arg->base.me_mapsize = env->me_mapsize;
arg->base.me_maxreaders = env->me_maxreaders;
arg->base.me_numreaders = env->me_txns->mti_numreaders;
arg->me_tail_txnid = 0;
arg->me_mapsize = env->me_mapsize; r = env->me_txns->mti_readers;
arg->me_maxreaders = env->me_maxreaders; arg->me_tail_txnid = arg->base.me_last_txnid;
arg->me_numreaders = env->me_txns->mti_numreaders; for (i = 0; i < arg->base.me_numreaders; ++i ) {
if (r[i].mr_pid) {
do { txnid_t mr = r[i].mr_txnid;
meta = mdb_meta_head_r(env); if (arg->me_tail_txnid > mr)
arg->me_meta1_txnid = m1->mm_txnid; arg->me_tail_txnid = mr;
arg->me_meta1_sign = m1->mm_datasync_sign; }
arg->me_meta2_txnid = m2->mm_txnid;
arg->me_meta2_sign = m2->mm_datasync_sign;
arg->me_last_pgno = meta->mm_last_pg;
arg->me_last_txnid = meta->mm_txnid;
} while (unlikely( meta->mm_txnid != env->me_txns->mti_txnid
|| arg->me_meta1_sign != m1->mm_datasync_sign
|| arg->me_meta2_sign != m2->mm_datasync_sign ));
arg->me_mapaddr = meta->mm_address;
arg->me_tail_txnid = 0;
MDB_reader *r = env->me_txns->mti_readers;
int i;
arg->me_tail_txnid = arg->me_last_txnid;
for (i = arg->me_numreaders; --i >= 0; ) {
if (r[i].mr_pid) {
txnid_t mr = r[i].mr_txnid;
if (arg->me_tail_txnid > mr)
arg->me_tail_txnid = mr;
} }
#endif /* MDBX_MODE_ENABLED */
} else {
return EINVAL;
} }
return MDB_SUCCESS; return MDB_SUCCESS;
} }
int __cold
mdb_env_info(MDB_env *env, MDB_envinfo *arg)
{
return mdbx_env_info(env, (MDBX_envinfo*) arg, sizeof(MDB_envinfo));
}
/** Set the default comparison functions for a database. /** Set the default comparison functions for a database.
* Called immediately after a database is opened to set the defaults. * Called immediately after a database is opened to set the defaults.
* The user can then override them with #mdb_set_compare() or * The user can then override them with #mdb_set_compare() or
@ -9293,12 +9356,18 @@ int mdb_dbi_open(MDB_txn *txn, const char *name, unsigned flags, MDB_dbi *dbi)
return rc; return rc;
} }
int ESECT #if ! MDBX_MODE_ENABLED
mdb_stat(MDB_txn *txn, MDB_dbi dbi, MDB_stat *arg) static
#endif
int __cold
mdbx_stat(MDB_txn *txn, MDB_dbi dbi, MDBX_stat *arg, size_t bytes)
{ {
if (!arg || unlikely(!TXN_DBI_EXIST(txn, dbi, DB_VALID))) if (!arg || unlikely(!TXN_DBI_EXIST(txn, dbi, DB_VALID)))
return EINVAL; return EINVAL;
if (unlikely(bytes != sizeof(MDBX_stat)))
return EINVAL;
if (unlikely(txn->mt_flags & MDB_TXN_BLOCKED)) if (unlikely(txn->mt_flags & MDB_TXN_BLOCKED))
return MDB_BAD_TXN; return MDB_BAD_TXN;
@ -9308,7 +9377,13 @@ mdb_stat(MDB_txn *txn, MDB_dbi dbi, MDB_stat *arg)
/* Stale, must read the DB's root. cursor_init does it for us. */ /* Stale, must read the DB's root. cursor_init does it for us. */
mdb_cursor_init(&mc, txn, dbi, &mx); mdb_cursor_init(&mc, txn, dbi, &mx);
} }
return mdb_stat0(txn->mt_env, &txn->mt_dbs[dbi], arg); return mdb_stat0(txn->mt_env, &txn->mt_dbs[dbi], &arg->base);
}
int __cold
mdb_stat(MDB_txn *txn, MDB_dbi dbi, MDB_stat *arg)
{
return mdbx_stat(txn, dbi, (MDBX_stat*) arg, sizeof(MDB_stat));
} }
void mdb_dbi_close(MDB_env *env, MDB_dbi dbi) void mdb_dbi_close(MDB_env *env, MDB_dbi dbi)
@ -9511,13 +9586,13 @@ int mdb_set_relctx(MDB_txn *txn, MDB_dbi dbi, void *ctx)
return MDB_SUCCESS; return MDB_SUCCESS;
} }
int ESECT int __cold
mdb_env_get_maxkeysize(MDB_env *env) mdb_env_get_maxkeysize(MDB_env *env)
{ {
return ENV_MAXKEY(env); return ENV_MAXKEY(env);
} }
int ESECT int __cold
mdb_reader_list(MDB_env *env, MDB_msg_func *func, void *ctx) mdb_reader_list(MDB_env *env, MDB_msg_func *func, void *ctx)
{ {
unsigned i, rdrs; unsigned i, rdrs;
@ -9560,7 +9635,7 @@ mdb_reader_list(MDB_env *env, MDB_msg_func *func, void *ctx)
/** Insert pid into list if not already present. /** Insert pid into list if not already present.
* return -1 if already present. * return -1 if already present.
*/ */
static int ESECT static int __cold
mdb_pid_insert(pid_t *ids, pid_t pid) mdb_pid_insert(pid_t *ids, pid_t pid)
{ {
/* binary search of pid in list */ /* binary search of pid in list */
@ -9597,7 +9672,7 @@ mdb_pid_insert(pid_t *ids, pid_t pid)
return 0; return 0;
} }
int ESECT int __cold
mdb_reader_check(MDB_env *env, int *dead) mdb_reader_check(MDB_env *env, int *dead)
{ {
if (!env) if (!env)
@ -9608,7 +9683,7 @@ mdb_reader_check(MDB_env *env, int *dead)
} }
/** As #mdb_reader_check(). rlocked = <caller locked the reader mutex>. */ /** As #mdb_reader_check(). rlocked = <caller locked the reader mutex>. */
static int ESECT static int __cold
mdb_reader_check0(MDB_env *env, int rlocked, int *dead) mdb_reader_check0(MDB_env *env, int rlocked, int *dead)
{ {
pthread_mutex_t *rmutex = rlocked ? NULL : MDB_MUTEX(env, r); pthread_mutex_t *rmutex = rlocked ? NULL : MDB_MUTEX(env, r);
@ -9662,7 +9737,7 @@ mdb_reader_check0(MDB_env *env, int rlocked, int *dead)
return rc; return rc;
} }
static int ESECT static int __cold
mdb_mutex_failed(MDB_env *env, pthread_mutex_t *mutex, int rc) mdb_mutex_failed(MDB_env *env, pthread_mutex_t *mutex, int rc)
{ {
#ifdef EOWNERDEAD #ifdef EOWNERDEAD
@ -9724,15 +9799,17 @@ static void mdb_mutex_unlock(MDB_env *env, pthread_mutex_t *mutex) {
mdb_assert(env, rc == 0); mdb_assert(env, rc == 0);
} }
void ESECT #if MDBX_MODE_ENABLED
mdb_env_set_oomfunc(MDB_env *env, MDB_oom_func *oomfunc)
void __cold
mdbx_env_set_oomfunc(MDB_env *env, MDB_oom_func *oomfunc)
{ {
if (env) if (env)
env->me_oom_func = oomfunc; env->me_oom_func = oomfunc;
} }
MDB_oom_func* ESECT MDB_oom_func* __cold
mdb_env_get_oomfunc(MDB_env *env) mdbx_env_get_oomfunc(MDB_env *env)
{ {
return env ? env->me_oom_func : NULL; return env ? env->me_oom_func : NULL;
} }
@ -9747,7 +9824,7 @@ typedef struct mdb_walk_ctx mdb_walk_ctx_t;
/** Depth-first tree traversal. */ /** Depth-first tree traversal. */
static int ESECT static int __cold
mdb_env_walk(mdb_walk_ctx_t *ctx, const char* dbi, pgno_t pg, int flags, int deep) mdb_env_walk(mdb_walk_ctx_t *ctx, const char* dbi, pgno_t pg, int flags, int deep)
{ {
MDB_page *mp; MDB_page *mp;
@ -9871,8 +9948,8 @@ mdb_env_walk(mdb_walk_ctx_t *ctx, const char* dbi, pgno_t pg, int flags, int dee
nkeys, payload_size, header_size, unused_size + align_bytes); nkeys, payload_size, header_size, unused_size + align_bytes);
} }
int ESECT int __cold
mdb_env_pgwalk(MDB_txn *txn, MDB_pgvisitor_func* visitor, void* user) mdbx_env_pgwalk(MDB_txn *txn, MDB_pgvisitor_func* visitor, void* user)
{ {
mdb_walk_ctx_t ctx; mdb_walk_ctx_t ctx;
int rc; int rc;
@ -9892,4 +9969,8 @@ mdb_env_pgwalk(MDB_txn *txn, MDB_pgvisitor_func* visitor, void* user)
return rc; return rc;
} }
#endif /* MDBX_MODE_ENABLED */
/** @} */ /** @} */
#include "./midl.c"

174
mdb_chk.c
View File

@ -17,7 +17,7 @@
You should have received a copy of the GNU Affero General Public License You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/* mdb_chk.c - memory-mapped database check tool */ /* mdbx_chk.c - memory-mapped database check tool */
#include <stdio.h> #include <stdio.h>
#include <errno.h> #include <errno.h>
#include <stdlib.h> #include <stdlib.h>
@ -29,8 +29,8 @@
#include <malloc.h> #include <malloc.h>
#include <time.h> #include <time.h>
#include "lmdb.h"
#include "midl.h" #include "midl.h"
#include "mdbx.h"
typedef struct flagbit { typedef struct flagbit {
int bit; int bit;
@ -79,8 +79,8 @@ int exclusive = 2;
MDB_env *env; MDB_env *env;
MDB_txn *txn, *locktxn; MDB_txn *txn, *locktxn;
MDB_envinfo info; MDBX_envinfo info;
MDB_stat stat; MDBX_stat stat;
size_t maxkeysize, reclaimable_pages, freedb_pages, lastpgno; size_t maxkeysize, reclaimable_pages, freedb_pages, lastpgno;
size_t userdb_count, skipped_subdb; size_t userdb_count, skipped_subdb;
unsigned verbose, quiet; unsigned verbose, quiet;
@ -215,7 +215,7 @@ static int pgvisitor(size_t pgno, unsigned pgnumber, void* ctx, const char* dbi,
{ {
if (type) { if (type) {
size_t page_bytes = payload_bytes + header_bytes + unused_bytes; size_t page_bytes = payload_bytes + header_bytes + unused_bytes;
size_t page_size = pgnumber * stat.ms_psize; size_t page_size = pgnumber * stat.base.ms_psize;
int index = pagemap_lookup_dbi(dbi); int index = pagemap_lookup_dbi(dbi);
if (index < 0) if (index < 0)
return ENOMEM; return ENOMEM;
@ -233,11 +233,11 @@ static int pgvisitor(size_t pgno, unsigned pgnumber, void* ctx, const char* dbi,
if (unused_bytes < 0 || (size_t) unused_bytes > page_size) if (unused_bytes < 0 || (size_t) unused_bytes > page_size)
problem_add("page", pgno, "illegal unused-bytes", "%zu < %i < %zu", problem_add("page", pgno, "illegal unused-bytes", "%zu < %i < %zu",
0, unused_bytes, stat.ms_psize); 0, unused_bytes, stat.base.ms_psize);
if (header_bytes < sizeof(long) || header_bytes >= stat.ms_psize - sizeof(long)) if (header_bytes < sizeof(long) || header_bytes >= stat.base.ms_psize - sizeof(long))
problem_add("page", pgno, "illegal header-length", "%zu < %i < %zu", problem_add("page", pgno, "illegal header-length", "%zu < %i < %zu",
sizeof(long), header_bytes, stat.ms_psize - sizeof(long)); sizeof(long), header_bytes, stat.base.ms_psize - sizeof(long));
if (payload_bytes < 1) { if (payload_bytes < 1) {
if (nentries > 0) { if (nentries > 0) {
problem_add("page", pgno, "zero size-of-entry", "payload %i bytes, %i entries", problem_add("page", pgno, "zero size-of-entry", "payload %i bytes, %i entries",
@ -298,7 +298,7 @@ static int handle_freedb(size_t record_number, MDB_val *key, MDB_val* data) {
if (key->mv_size != sizeof(txnid)) if (key->mv_size != sizeof(txnid))
problem_add("entry", record_number, "wrong txn-id size", "key-size %zi", key->mv_size); problem_add("entry", record_number, "wrong txn-id size", "key-size %zi", key->mv_size);
else if (txnid < 1 || txnid > info.me_last_txnid) else if (txnid < 1 || txnid > info.base.me_last_txnid)
problem_add("entry", record_number, "wrong txn-id", "%zu", txnid); problem_add("entry", record_number, "wrong txn-id", "%zu", txnid);
if (data->mv_size < sizeof(size_t) || data->mv_size % sizeof(size_t)) if (data->mv_size < sizeof(size_t) || data->mv_size % sizeof(size_t))
@ -316,9 +316,9 @@ static int handle_freedb(size_t record_number, MDB_val *key, MDB_val* data) {
reclaimable_pages += number; reclaimable_pages += number;
for (i = number, prev = 1; --i >= 0; ) { for (i = number, prev = 1; --i >= 0; ) {
pg = iptr[i]; pg = iptr[i];
if (pg < 2 /* META_PAGE */ || pg > info.me_last_pgno) if (pg < 2 /* META_PAGE */ || pg > info.base.me_last_pgno)
problem_add("entry", record_number, "wrong idl entry", "2 < %zi < %zi", problem_add("entry", record_number, "wrong idl entry", "2 < %zi < %zi",
pg, info.me_last_pgno); pg, info.base.me_last_pgno);
else if (pg <= prev) { else if (pg <= prev) {
bad = " [bad sequence]"; bad = " [bad sequence]";
problem_add("entry", record_number, "bad sequence", "%zi <= %zi", problem_add("entry", record_number, "bad sequence", "%zi <= %zi",
@ -375,7 +375,7 @@ static int handle_maindb(size_t record_number, MDB_val *key, MDB_val* data) {
static int process_db(MDB_dbi dbi, char *name, visitor *handler, int silent) static int process_db(MDB_dbi dbi, char *name, visitor *handler, int silent)
{ {
MDB_cursor *mc; MDB_cursor *mc;
MDB_stat ms; MDBX_stat ms;
MDB_val key, data; MDB_val key, data;
MDB_val prev_key, prev_data; MDB_val prev_key, prev_data;
unsigned flags; unsigned flags;
@ -387,11 +387,11 @@ static int process_db(MDB_dbi dbi, char *name, visitor *handler, int silent)
size_t key_bytes = 0, data_bytes = 0; size_t key_bytes = 0, data_bytes = 0;
if (0 > (int) dbi) { if (0 > (int) dbi) {
rc = mdb_dbi_open(txn, name, 0, &dbi); rc = mdbx_dbi_open(txn, name, 0, &dbi);
if (rc) { if (rc) {
if (!name || rc != MDB_INCOMPATIBLE) /* LY: mainDB's record is not a user's DB. */ { if (!name || rc != MDB_INCOMPATIBLE) /* LY: mainDB's record is not a user's DB. */ {
error(" - mdb_open '%s' failed, error %d %s\n", error(" - mdbx_open '%s' failed, error %d %s\n",
name ? name : "main", rc, mdb_strerror(rc)); name ? name : "main", rc, mdbx_strerror(rc));
} }
return rc; return rc;
} }
@ -403,7 +403,7 @@ static int process_db(MDB_dbi dbi, char *name, visitor *handler, int silent)
fflush(NULL); fflush(NULL);
} }
skipped_subdb++; skipped_subdb++;
mdb_dbi_close(env, dbi); mdbx_dbi_close(env, dbi);
return MDB_SUCCESS; return MDB_SUCCESS;
} }
@ -412,17 +412,17 @@ static int process_db(MDB_dbi dbi, char *name, visitor *handler, int silent)
fflush(NULL); fflush(NULL);
} }
rc = mdb_dbi_flags(txn, dbi, &flags); rc = mdbx_dbi_flags(txn, dbi, &flags);
if (rc) { if (rc) {
error(" - mdb_dbi_flags failed, error %d %s\n", rc, mdb_strerror(rc)); error(" - mdbx_dbi_flags failed, error %d %s\n", rc, mdbx_strerror(rc));
mdb_dbi_close(env, dbi); mdbx_dbi_close(env, dbi);
return rc; return rc;
} }
rc = mdb_stat(txn, dbi, &ms); rc = mdbx_stat(txn, dbi, &ms, sizeof(ms));
if (rc) { if (rc) {
error(" - mdb_stat failed, error %d %s\n", rc, mdb_strerror(rc)); error(" - mdbx_stat failed, error %d %s\n", rc, mdbx_strerror(rc));
mdb_dbi_close(env, dbi); mdbx_dbi_close(env, dbi);
return rc; return rc;
} }
@ -437,23 +437,23 @@ static int process_db(MDB_dbi dbi, char *name, visitor *handler, int silent)
} }
print(" (0x%02X)\n", flags); print(" (0x%02X)\n", flags);
if (verbose > 1) { if (verbose > 1) {
print(" - page size %u, entries %zu\n", ms.ms_psize, ms.ms_entries); print(" - page size %u, entries %zu\n", ms.base.ms_psize, ms.base.ms_entries);
print(" - b-tree depth %u, pages: branch %zu, leaf %zu, overflow %zu\n", print(" - b-tree depth %u, pages: branch %zu, leaf %zu, overflow %zu\n",
ms.ms_depth, ms.ms_branch_pages, ms.ms_leaf_pages, ms.ms_overflow_pages); ms.base.ms_depth, ms.base.ms_branch_pages, ms.base.ms_leaf_pages, ms.base.ms_overflow_pages);
} }
} }
rc = mdb_cursor_open(txn, dbi, &mc); rc = mdbx_cursor_open(txn, dbi, &mc);
if (rc) { if (rc) {
error(" - mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc)); error(" - mdbx_cursor_open failed, error %d %s\n", rc, mdbx_strerror(rc));
mdb_dbi_close(env, dbi); mdbx_dbi_close(env, dbi);
return rc; return rc;
} }
saved_list = problems_push(); saved_list = problems_push();
prev_key.mv_data = NULL; prev_key.mv_data = NULL;
prev_data.mv_size = 0; prev_data.mv_size = 0;
rc = mdb_cursor_get(mc, &key, &data, MDB_FIRST); rc = mdbx_cursor_get(mc, &key, &data, MDB_FIRST);
while (rc == MDB_SUCCESS) { while (rc == MDB_SUCCESS) {
if (gotsignal) { if (gotsignal) {
print(" - interrupted by signal\n"); print(" - interrupted by signal\n");
@ -485,7 +485,7 @@ static int process_db(MDB_dbi dbi, char *name, visitor *handler, int silent)
"%zu != %zu", prev_data.mv_size, data.mv_size); "%zu != %zu", prev_data.mv_size, data.mv_size);
} }
int cmp = mdb_cmp(txn, dbi, &prev_key, &key); int cmp = mdbx_cmp(txn, dbi, &prev_key, &key);
if (cmp > 0) { if (cmp > 0) {
problem_add("entry", record_count, "broken ordering of entries", NULL); problem_add("entry", record_count, "broken ordering of entries", NULL);
} else if (cmp == 0) { } else if (cmp == 0) {
@ -493,7 +493,7 @@ static int process_db(MDB_dbi dbi, char *name, visitor *handler, int silent)
if (! (flags & MDB_DUPSORT)) if (! (flags & MDB_DUPSORT))
problem_add("entry", record_count, "duplicated entries", NULL); problem_add("entry", record_count, "duplicated entries", NULL);
else if (flags & MDB_INTEGERDUP) { else if (flags & MDB_INTEGERDUP) {
cmp = mdb_dcmp(txn, dbi, &prev_data, &data); cmp = mdbx_dcmp(txn, dbi, &prev_data, &data);
if (cmp > 0) if (cmp > 0)
problem_add("entry", record_count, "broken ordering of multi-values", NULL); problem_add("entry", record_count, "broken ordering of multi-values", NULL);
} }
@ -517,16 +517,16 @@ static int process_db(MDB_dbi dbi, char *name, visitor *handler, int silent)
prev_key = key; prev_key = key;
prev_data = data; prev_data = data;
rc = mdb_cursor_get(mc, &key, &data, MDB_NEXT); rc = mdbx_cursor_get(mc, &key, &data, MDB_NEXT);
} }
if (rc != MDB_NOTFOUND) if (rc != MDB_NOTFOUND)
error(" - mdb_cursor_get failed, error %d %s\n", rc, mdb_strerror(rc)); error(" - mdbx_cursor_get failed, error %d %s\n", rc, mdbx_strerror(rc));
else else
rc = 0; rc = 0;
if (record_count != ms.ms_entries) if (record_count != ms.base.ms_entries)
problem_add("entry", record_count, "differentent number of entries", problem_add("entry", record_count, "differentent number of entries",
"%zu != %zu", record_count, ms.ms_entries); "%zu != %zu", record_count, ms.base.ms_entries);
bailout: bailout:
problems_count = problems_pop(saved_list); problems_count = problems_pop(saved_list);
if (! silent && verbose) { if (! silent && verbose) {
@ -535,8 +535,8 @@ bailout:
fflush(NULL); fflush(NULL);
} }
mdb_cursor_close(mc); mdbx_cursor_close(mc);
mdb_dbi_close(env, dbi); mdbx_dbi_close(env, dbi);
return rc || problems_count; return rc || problems_count;
} }
@ -583,7 +583,7 @@ int main(int argc, char *argv[])
if (clock_gettime(CLOCK_MONOTONIC, &timestamp_start)) { if (clock_gettime(CLOCK_MONOTONIC, &timestamp_start)) {
rc = errno; rc = errno;
error("clock_gettime failed, error %d %s\n", rc, mdb_strerror(rc)); error("clock_gettime failed, error %d %s\n", rc, mdbx_strerror(rc));
return EXIT_FAILURE_SYS; return EXIT_FAILURE_SYS;
} }
@ -638,97 +638,97 @@ int main(int argc, char *argv[])
signal(SIGTERM, signal_hanlder); signal(SIGTERM, signal_hanlder);
envname = argv[optind]; envname = argv[optind];
print("Running mdb_chk for '%s' in %s mode...\n", print("Running mdbx_chk for '%s' in %s mode...\n",
envname, (envflags & MDB_RDONLY) ? "read-only" : "write-lock"); envname, (envflags & MDB_RDONLY) ? "read-only" : "write-lock");
fflush(NULL); fflush(NULL);
rc = mdb_env_create(&env); rc = mdbx_env_create(&env);
if (rc) { if (rc) {
error("mdb_env_create failed, error %d %s\n", rc, mdb_strerror(rc)); error("mdbx_env_create failed, error %d %s\n", rc, mdbx_strerror(rc));
return rc < 0 ? EXIT_FAILURE_MDB : EXIT_FAILURE_SYS; return rc < 0 ? EXIT_FAILURE_MDB : EXIT_FAILURE_SYS;
} }
rc = mdb_env_get_maxkeysize(env); rc = mdbx_env_get_maxkeysize(env);
if (rc < 0) { if (rc < 0) {
error("mdb_env_get_maxkeysize failed, error %d %s\n", rc, mdb_strerror(rc)); error("mdbx_env_get_maxkeysize failed, error %d %s\n", rc, mdbx_strerror(rc));
goto bailout; goto bailout;
} }
maxkeysize = rc; maxkeysize = rc;
mdb_env_set_maxdbs(env, 3); mdbx_env_set_maxdbs(env, 3);
rc = mdb_env_open_ex(env, envname, envflags, 0664, &exclusive); rc = mdbx_env_open_ex(env, envname, envflags, 0664, &exclusive);
if (rc) { if (rc) {
error("mdb_env_open failed, error %d %s\n", rc, mdb_strerror(rc)); error("mdbx_env_open failed, error %d %s\n", rc, mdbx_strerror(rc));
goto bailout; goto bailout;
} }
if (verbose) if (verbose)
print(" - %s mode\n", exclusive ? "monopolistic" : "cooperative"); print(" - %s mode\n", exclusive ? "monopolistic" : "cooperative");
if (! (envflags & MDB_RDONLY)) { if (! (envflags & MDB_RDONLY)) {
rc = mdb_txn_begin(env, NULL, 0, &locktxn); rc = mdbx_txn_begin(env, NULL, 0, &locktxn);
if (rc) { if (rc) {
error("mdb_txn_begin(lock-write) failed, error %d %s\n", rc, mdb_strerror(rc)); error("mdbx_txn_begin(lock-write) failed, error %d %s\n", rc, mdbx_strerror(rc));
goto bailout; goto bailout;
} }
} }
rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); rc = mdbx_txn_begin(env, NULL, MDB_RDONLY, &txn);
if (rc) { if (rc) {
error("mdb_txn_begin(read-only) failed, error %d %s\n", rc, mdb_strerror(rc)); error("mdbx_txn_begin(read-only) failed, error %d %s\n", rc, mdbx_strerror(rc));
goto bailout; goto bailout;
} }
rc = mdb_env_info(env, &info); rc = mdbx_env_info(env, &info, sizeof(info));
if (rc) { if (rc) {
error("mdb_env_info failed, error %d %s\n", rc, mdb_strerror(rc)); error("mdbx_env_info failed, error %d %s\n", rc, mdbx_strerror(rc));
goto bailout; goto bailout;
} }
rc = mdb_env_stat(env, &stat); rc = mdbx_env_stat(env, &stat, sizeof(stat));
if (rc) { if (rc) {
error("mdb_env_stat failed, error %d %s\n", rc, mdb_strerror(rc)); error("mdbx_env_stat failed, error %d %s\n", rc, mdbx_strerror(rc));
goto bailout; goto bailout;
} }
lastpgno = info.me_last_pgno + 1; lastpgno = info.base.me_last_pgno + 1;
errno = 0; errno = 0;
if (verbose) { if (verbose) {
double k = 1024.0; double k = 1024.0;
const char sf[] = "KMGTPEZY"; /* LY: Kilo, Mega, Giga, Tera, Peta, Exa, Zetta, Yotta! */ const char sf[] = "KMGTPEZY"; /* LY: Kilo, Mega, Giga, Tera, Peta, Exa, Zetta, Yotta! */
for(i = 0; sf[i+1] && info.me_mapsize / k > 1000.0; ++i) for(i = 0; sf[i+1] && info.base.me_mapsize / k > 1000.0; ++i)
k *= 1024; k *= 1024;
print(" - map size %zu (%.2f %cb)\n", info.me_mapsize, print(" - map size %zu (%.2f %cb)\n", info.base.me_mapsize,
info.me_mapsize / k, sf[i]); info.base.me_mapsize / k, sf[i]);
if (info.me_mapaddr) if (info.base.me_mapaddr)
print(" - mapaddr %p\n", info.me_mapaddr); print(" - mapaddr %p\n", info.base.me_mapaddr);
print(" - pagesize %u, max keysize %zu (%s), max readers %u\n", print(" - pagesize %u, max keysize %zu (%s), max readers %u\n",
stat.ms_psize, maxkeysize, stat.base.ms_psize, maxkeysize,
(maxkeysize == 511) ? "default" : (maxkeysize == 511) ? "default" :
(maxkeysize == 0) ? "devel" : "custom", (maxkeysize == 0) ? "devel" : "custom",
info.me_maxreaders); info.base.me_maxreaders);
print(" - transactions: last %zu, bottom %zu, lag reading %zi\n", info.me_last_txnid, print(" - transactions: last %zu, bottom %zu, lag reading %zi\n", info.base.me_last_txnid,
info.me_tail_txnid, info.me_last_txnid - info.me_tail_txnid); info.me_tail_txnid, info.base.me_last_txnid - info.me_tail_txnid);
print(" - meta-1: %s %zu, %s", print(" - meta-1: %s %zu, %s",
meta_synctype(info.me_meta1_sign), info.me_meta1_txnid, meta_synctype(info.me_meta1_sign), info.me_meta1_txnid,
meta_lt(info.me_meta1_txnid, info.me_meta1_sign, meta_lt(info.me_meta1_txnid, info.me_meta1_sign,
info.me_meta2_txnid, info.me_meta2_sign) ? "tail" : "head"); info.me_meta2_txnid, info.me_meta2_sign) ? "tail" : "head");
if (info.me_meta1_txnid > info.me_last_txnid) if (info.me_meta1_txnid > info.base.me_last_txnid)
print(", rolled-back %zu (%zu >>> %zu)\n", print(", rolled-back %zu (%zu >>> %zu)\n",
info.me_meta1_txnid - info.me_last_txnid, info.me_meta1_txnid - info.base.me_last_txnid,
info.me_meta1_txnid, info.me_last_txnid); info.me_meta1_txnid, info.base.me_last_txnid);
print("\n"); print("\n");
print(" - meta-2: %s %zu, %s", print(" - meta-2: %s %zu, %s",
meta_synctype(info.me_meta2_sign), info.me_meta2_txnid, meta_synctype(info.me_meta2_sign), info.me_meta2_txnid,
meta_lt(info.me_meta2_txnid, info.me_meta2_sign, meta_lt(info.me_meta2_txnid, info.me_meta2_sign,
info.me_meta1_txnid, info.me_meta1_sign) ? "tail" : "head"); info.me_meta1_txnid, info.me_meta1_sign) ? "tail" : "head");
if (info.me_meta2_txnid > info.me_last_txnid) if (info.me_meta2_txnid > info.base.me_last_txnid)
print(", rolled-back %zu (%zu >>> %zu)\n", print(", rolled-back %zu (%zu >>> %zu)\n",
info.me_meta2_txnid - info.me_last_txnid, info.me_meta2_txnid - info.base.me_last_txnid,
info.me_meta2_txnid, info.me_last_txnid); info.me_meta2_txnid, info.base.me_last_txnid);
print("\n"); print("\n");
} }
@ -738,26 +738,26 @@ int main(int argc, char *argv[])
if (! meta_lt(info.me_meta1_txnid, info.me_meta1_sign, if (! meta_lt(info.me_meta1_txnid, info.me_meta1_sign,
info.me_meta2_txnid, info.me_meta2_sign) info.me_meta2_txnid, info.me_meta2_sign)
&& info.me_meta1_txnid != info.me_last_txnid) { && info.me_meta1_txnid != info.base.me_last_txnid) {
print(" - meta-1 txn-id mismatch last-txn-id (%zi != %zi)\n", print(" - meta-1 txn-id mismatch last-txn-id (%zi != %zi)\n",
info.me_meta1_txnid, info.me_last_txnid); info.me_meta1_txnid, info.base.me_last_txnid);
++problems_meta; ++problems_meta;
} }
if (! meta_lt(info.me_meta2_txnid, info.me_meta2_sign, if (! meta_lt(info.me_meta2_txnid, info.me_meta2_sign,
info.me_meta1_txnid, info.me_meta1_sign) info.me_meta1_txnid, info.me_meta1_sign)
&& info.me_meta2_txnid != info.me_last_txnid) { && info.me_meta2_txnid != info.base.me_last_txnid) {
print(" - meta-2 txn-id mismatch last-txn-id (%zi != %zi)\n", print(" - meta-2 txn-id mismatch last-txn-id (%zi != %zi)\n",
info.me_meta2_txnid, info.me_last_txnid); info.me_meta2_txnid, info.base.me_last_txnid);
++problems_meta; ++problems_meta;
} }
} else if (locktxn) { } else if (locktxn) {
if (verbose) if (verbose)
print(" - perform lite check last-txn-id with meta-pages (not a monopolistic mode)\n"); print(" - perform lite check last-txn-id with meta-pages (not a monopolistic mode)\n");
size_t last = (info.me_meta2_txnid > info.me_meta1_txnid) ? info.me_meta2_txnid : info.me_meta1_txnid; size_t last = (info.me_meta2_txnid > info.me_meta1_txnid) ? info.me_meta2_txnid : info.me_meta1_txnid;
if (last != info.me_last_txnid) { if (last != info.base.me_last_txnid) {
print(" - last-meta mismatch last-txn-id (%zi != %zi)\n", print(" - last-meta mismatch last-txn-id (%zi != %zi)\n",
last, info.me_last_txnid); last, info.base.me_last_txnid);
++problems_meta; ++problems_meta;
} }
} else if (verbose) { } else if (verbose) {
@ -774,12 +774,12 @@ int main(int argc, char *argv[])
walk.pagemap = calloc(lastpgno, sizeof(*walk.pagemap)); walk.pagemap = calloc(lastpgno, sizeof(*walk.pagemap));
if (! walk.pagemap) { if (! walk.pagemap) {
rc = errno ? errno : ENOMEM; rc = errno ? errno : ENOMEM;
error("calloc failed, error %d %s\n", rc, mdb_strerror(rc)); error("calloc failed, error %d %s\n", rc, mdbx_strerror(rc));
goto bailout; goto bailout;
} }
saved_list = problems_push(); saved_list = problems_push();
rc = mdb_env_pgwalk(txn, pgvisitor, NULL); rc = mdbx_env_pgwalk(txn, pgvisitor, NULL);
traversal_problems = problems_pop(saved_list); traversal_problems = problems_pop(saved_list);
if (rc) { if (rc) {
@ -787,7 +787,7 @@ int main(int argc, char *argv[])
print(" - interrupted by signal\n"); print(" - interrupted by signal\n");
fflush(NULL); fflush(NULL);
} else { } else {
error("mdb_env_pgwalk failed, error %d %s\n", rc, mdb_strerror(rc)); error("mdbx_env_pgwalk failed, error %d %s\n", rc, mdbx_strerror(rc));
} }
goto bailout; goto bailout;
} }
@ -803,7 +803,7 @@ int main(int argc, char *argv[])
} }
if (verbose) { if (verbose) {
size_t total_page_bytes = walk.pgcount * stat.ms_psize; size_t total_page_bytes = walk.pgcount * stat.base.ms_psize;
print(" - dbi pages: %zu total", walk.pgcount); print(" - dbi pages: %zu total", walk.pgcount);
if (verbose > 1) if (verbose > 1)
for (i = 1; i < MAX_DBI && walk.dbi_names[i]; ++i) for (i = 1; i < MAX_DBI && walk.dbi_names[i]; ++i)
@ -816,7 +816,7 @@ int main(int argc, char *argv[])
total_page_bytes - walk.total_payload_bytes, total_page_bytes - walk.total_payload_bytes,
(total_page_bytes - walk.total_payload_bytes) * 100.0 / total_page_bytes); (total_page_bytes - walk.total_payload_bytes) * 100.0 / total_page_bytes);
for (i = 1; i < MAX_DBI && walk.dbi_names[i]; ++i) { for (i = 1; i < MAX_DBI && walk.dbi_names[i]; ++i) {
size_t dbi_bytes = walk.dbi_pages[i] * stat.ms_psize; size_t dbi_bytes = walk.dbi_pages[i] * stat.base.ms_psize;
print(" %s: subtotal %zu bytes (%.1f%%), payload %zu (%.1f%%), unused %zu (%.1f%%)", print(" %s: subtotal %zu bytes (%.1f%%), payload %zu (%.1f%%), unused %zu (%.1f%%)",
walk.dbi_names[i], walk.dbi_names[i],
dbi_bytes, dbi_bytes * 100.0 / total_page_bytes, dbi_bytes, dbi_bytes * 100.0 / total_page_bytes,
@ -848,13 +848,13 @@ int main(int argc, char *argv[])
problems_freedb = process_db(0 /* FREE_DBI */, "free", handle_freedb, 0); problems_freedb = process_db(0 /* FREE_DBI */, "free", handle_freedb, 0);
if (verbose) { if (verbose) {
size_t value = info.me_mapsize / stat.ms_psize; size_t value = info.base.me_mapsize / stat.base.ms_psize;
double percent = value / 100.0; double percent = value / 100.0;
print(" - pages info: %zu total", value); print(" - pages info: %zu total", value);
print(", allocated %zu (%.1f%%)", lastpgno, lastpgno / percent); print(", allocated %zu (%.1f%%)", lastpgno, lastpgno / percent);
if (verbose > 1) { if (verbose > 1) {
value = info.me_mapsize / stat.ms_psize - lastpgno; value = info.base.me_mapsize / stat.base.ms_psize - lastpgno;
print(", remained %zu (%.1f%%)", value, value / percent); print(", remained %zu (%.1f%%)", value, value / percent);
value = lastpgno - freedb_pages; value = lastpgno - freedb_pages;
@ -868,7 +868,7 @@ int main(int argc, char *argv[])
print(", reclaimable %zu (%.1f%%)", reclaimable_pages, reclaimable_pages / percent); print(", reclaimable %zu (%.1f%%)", reclaimable_pages, reclaimable_pages / percent);
} }
value = info.me_mapsize / stat.ms_psize - lastpgno + reclaimable_pages; value = info.base.me_mapsize / stat.base.ms_psize - lastpgno + reclaimable_pages;
print(", available %zu (%.1f%%)\n", value, value / percent); print(", available %zu (%.1f%%)\n", value, value / percent);
} }
@ -892,11 +892,11 @@ int main(int argc, char *argv[])
bailout: bailout:
if (txn) if (txn)
mdb_txn_abort(txn); mdbx_txn_abort(txn);
if (locktxn) if (locktxn)
mdb_txn_abort(locktxn); mdbx_txn_abort(locktxn);
if (env) if (env)
mdb_env_close(env); mdbx_env_close(env);
free(walk.pagemap); free(walk.pagemap);
fflush(NULL); fflush(NULL);
if (rc) { if (rc) {
@ -907,7 +907,7 @@ bailout:
if (clock_gettime(CLOCK_MONOTONIC, &timestamp_finish)) { if (clock_gettime(CLOCK_MONOTONIC, &timestamp_finish)) {
rc = errno; rc = errno;
error("clock_gettime failed, error %d %s\n", rc, mdb_strerror(rc)); error("clock_gettime failed, error %d %s\n", rc, mdbx_strerror(rc));
return EXIT_FAILURE_SYS; return EXIT_FAILURE_SYS;
} }

View File

@ -20,7 +20,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <signal.h> #include <signal.h>
#include "lmdb.h" #include "mdbx.h"
static void static void
sighandle(int sig) sighandle(int sig)

View File

@ -18,7 +18,7 @@
#include <ctype.h> #include <ctype.h>
#include <unistd.h> #include <unistd.h>
#include <signal.h> #include <signal.h>
#include "lmdb.h" #include "mdbx.h"
#ifdef _WIN32 #ifdef _WIN32
#define Z "I" #define Z "I"

View File

@ -17,7 +17,7 @@
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <unistd.h> #include <unistd.h>
#include "lmdb.h" #include "mdbx.h"
#define PRINT 1 #define PRINT 1
#define NOHDR 2 #define NOHDR 2

View File

@ -15,18 +15,18 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include "lmdb.h" #include "mdbx.h"
static void prstat(MDB_stat *ms) static void prstat(MDBX_stat *ms)
{ {
#if 0 #if 0
printf(" Page size: %u\n", ms->ms_psize); printf(" Page size: %u\n", ms->base.ms_psize);
#endif #endif
printf(" Tree depth: %u\n", ms->ms_depth); printf(" Tree depth: %u\n", ms->base.ms_depth);
printf(" Branch pages: %zu\n", ms->ms_branch_pages); printf(" Branch pages: %zu\n", ms->base.ms_branch_pages);
printf(" Leaf pages: %zu\n", ms->ms_leaf_pages); printf(" Leaf pages: %zu\n", ms->base.ms_leaf_pages);
printf(" Overflow pages: %zu\n", ms->ms_overflow_pages); printf(" Overflow pages: %zu\n", ms->base.ms_overflow_pages);
printf(" Entries: %zu\n", ms->ms_entries); printf(" Entries: %zu\n", ms->base.ms_entries);
} }
static void usage(char *prog) static void usage(char *prog)
@ -41,8 +41,8 @@ int main(int argc, char *argv[])
MDB_env *env; MDB_env *env;
MDB_txn *txn; MDB_txn *txn;
MDB_dbi dbi; MDB_dbi dbi;
MDB_stat mst; MDBX_stat mst;
MDB_envinfo mei; MDBX_envinfo mei;
char *prog = argv[0]; char *prog = argv[0];
char *envname; char *envname;
char *subname = NULL; char *subname = NULL;
@ -115,19 +115,19 @@ int main(int argc, char *argv[])
} }
if (envinfo) { if (envinfo) {
(void)mdb_env_stat(env, &mst); (void)mdbx_env_stat(env, &mst, sizeof(mst));
(void)mdb_env_info(env, &mei); (void)mdbx_env_info(env, &mei, sizeof(mei));
printf("Environment Info\n"); printf("Environment Info\n");
printf(" Map address: %p\n", mei.me_mapaddr); printf(" Map address: %p\n", mei.base.me_mapaddr);
printf(" Map size: %zu\n", mei.me_mapsize); printf(" Map size: %zu\n", mei.base.me_mapsize);
printf(" Page size: %u\n", mst.ms_psize); printf(" Page size: %u\n", mst.base.ms_psize);
printf(" Max pages: %zu\n", mei.me_mapsize / mst.ms_psize); printf(" Max pages: %zu\n", mei.base.me_mapsize / mst.base.ms_psize);
printf(" Number of pages used: %zu\n", mei.me_last_pgno+1); printf(" Number of pages used: %zu\n", mei.base.me_last_pgno+1);
printf(" Last transaction ID: %zu\n", mei.me_last_txnid); printf(" Last transaction ID: %zu\n", mei.base.me_last_txnid);
printf(" Tail transaction ID: %zu (%zi)\n", printf(" Tail transaction ID: %zu (%zi)\n",
mei.me_tail_txnid, mei.me_tail_txnid - mei.me_last_txnid); mei.me_tail_txnid, mei.me_tail_txnid - mei.base.me_last_txnid);
printf(" Max readers: %u\n", mei.me_maxreaders); printf(" Max readers: %u\n", mei.base.me_maxreaders);
printf(" Number of readers used: %u\n", mei.me_numreaders); printf(" Number of readers used: %u\n", mei.base.me_numreaders);
} else { } else {
/* LY: zap warnings from gcc */ /* LY: zap warnings from gcc */
memset(&mst, 0, sizeof(mst)); memset(&mst, 0, sizeof(mst));
@ -166,7 +166,7 @@ int main(int argc, char *argv[])
fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc)); fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc));
goto txn_abort; goto txn_abort;
} }
rc = mdb_stat(txn, dbi, &mst); rc = mdbx_stat(txn, dbi, &mst, sizeof(mst));
if (rc) { if (rc) {
fprintf(stderr, "mdb_stat failed, error %d %s\n", rc, mdb_strerror(rc)); fprintf(stderr, "mdb_stat failed, error %d %s\n", rc, mdb_strerror(rc));
goto txn_abort; goto txn_abort;
@ -206,18 +206,18 @@ int main(int argc, char *argv[])
} }
mdb_cursor_close(cursor); mdb_cursor_close(cursor);
if (envinfo) { if (envinfo) {
size_t value = mei.me_mapsize / mst.ms_psize; size_t value = mei.base.me_mapsize / mst.base.ms_psize;
double percent = value / 100.0; double percent = value / 100.0;
printf("Page Allocation Info\n"); printf("Page Allocation Info\n");
printf(" Max pages: %9zu 100%%\n", value); printf(" Max pages: %9zu 100%%\n", value);
value = mei.me_last_pgno+1; value = mei.base.me_last_pgno+1;
printf(" Number of pages used: %zu %.1f%%\n", value, value / percent); printf(" Number of pages used: %zu %.1f%%\n", value, value / percent);
value = mei.me_mapsize / mst.ms_psize - (mei.me_last_pgno+1); value = mei.base.me_mapsize / mst.base.ms_psize - (mei.base.me_last_pgno+1);
printf(" Remained: %zu %.1f%%\n", value, value / percent); printf(" Remained: %zu %.1f%%\n", value, value / percent);
value = mei.me_last_pgno+1 - pages; value = mei.base.me_last_pgno+1 - pages;
printf(" Used now: %zu %.1f%%\n", value, value / percent); printf(" Used now: %zu %.1f%%\n", value, value / percent);
value = pages; value = pages;
@ -229,7 +229,7 @@ int main(int argc, char *argv[])
value = reclaimable; value = reclaimable;
printf(" Reclaimable: %zu %.1f%%\n", value, value / percent); printf(" Reclaimable: %zu %.1f%%\n", value, value / percent);
value = mei.me_mapsize / mst.ms_psize - (mei.me_last_pgno+1) + reclaimable; value = mei.base.me_mapsize / mst.base.ms_psize - (mei.base.me_last_pgno+1) + reclaimable;
printf(" Available: %zu %.1f%%\n", value, value / percent); printf(" Available: %zu %.1f%%\n", value, value / percent);
} else } else
printf(" Free pages: %zu\n", pages); printf(" Free pages: %zu\n", pages);
@ -241,7 +241,7 @@ int main(int argc, char *argv[])
goto txn_abort; goto txn_abort;
} }
rc = mdb_stat(txn, dbi, &mst); rc = mdbx_stat(txn, dbi, &mst, sizeof(mst));
if (rc) { if (rc) {
fprintf(stderr, "mdb_stat failed, error %d %s\n", rc, mdb_strerror(rc)); fprintf(stderr, "mdb_stat failed, error %d %s\n", rc, mdb_strerror(rc));
goto txn_abort; goto txn_abort;
@ -271,7 +271,7 @@ int main(int argc, char *argv[])
printf("Status of %s\n", str); printf("Status of %s\n", str);
free(str); free(str);
if (rc) continue; if (rc) continue;
rc = mdb_stat(txn, db2, &mst); rc = mdbx_stat(txn, db2, &mst, sizeof(mst));
if (rc) { if (rc) {
fprintf(stderr, "mdb_stat failed, error %d %s\n", rc, mdb_strerror(rc)); fprintf(stderr, "mdb_stat failed, error %d %s\n", rc, mdb_strerror(rc));
goto txn_abort; goto txn_abort;

123
mdbx.h Normal file
View File

@ -0,0 +1,123 @@
/*
Copyright (c) 2015 Leonid Yuriev <leo@yuriev.ru>.
Copyright (c) 2015 Peter-Service R&D LLC.
This file is part of ReOpenLDAP.
ReOpenLDAP is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ReOpenLDAP is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
This is solution to provide flexible compatibility with the original liblmdb.
Yeah, this way is partially ugly and madness...
But, on the other hand, only this way allows provide both API with
minimal changes the source code of an applications, and the source
code of the library itself. Anyway, ideas are welcome!
So,
When needed drop-in replacement for liblmdb you should:
- 'make lmdb' to build liblmdb.so and liblmdb.a;
- #include <lmdb.h> and use mdb_* functions;
- linking with liblmdb.so оr liblmdb.a;
= This provides nearly full compatibility with
original LMDB from Symas Corp.
But you should be noted - such compatibility
is not a goal for MDBX.
When exactly the libmdbx is needed, you should:
- 'make mdbx' to build libmdbx.so and libmdbx.a;
- #include <mdbx.h> and use mdbx_* functions;
- linking with libmdbx.so оr libmdbx.a;
= This allows using (linking) both MDBX and LMDB
simultaneously in the one application, for instance
to benchmarking and/or comparison.
*/
#ifndef _MDBX_H_
#define _MDBX_H_
#define MDBX_MODE_ENABLED 1
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#define mdb_version mdbx_version
#define mdb_strerror mdbx_strerror
#define mdb_env_create mdbx_env_create
#define mdb_env_open mdbx_env_open
#define mdb_env_open_ex mdbx_env_open_ex
#define mdb_env_copy mdbx_env_copy
#define mdb_env_copyfd mdbx_env_copyfd
#define mdb_env_copy2 mdbx_env_copy2
#define mdb_env_copyfd2 mdbx_env_copyfd2
#define mdb_env_sync mdbx_env_sync
#define mdb_env_close mdbx_env_close
#define mdb_env_close_ex mdbx_env_close_ex
#define mdb_env_set_flags mdbx_env_set_flags
#define mdb_env_get_flags mdbx_env_get_flags
#define mdb_env_get_path mdbx_env_get_path
#define mdb_env_get_fd mdbx_env_get_fd
#define mdb_env_set_mapsize mdbx_env_set_mapsize
#define mdb_env_set_maxreaders mdbx_env_set_maxreaders
#define mdb_env_get_maxreaders mdbx_env_get_maxreaders
#define mdb_env_set_maxdbs mdbx_env_set_maxdbs
#define mdb_env_get_maxkeysize mdbx_env_get_maxkeysize
#define mdb_env_set_userctx mdbx_env_set_userctx
#define mdb_env_get_userctx mdbx_env_get_userctx
#define mdb_env_set_assert mdbx_env_set_assert
#define mdb_env_set_syncbytes mdbx_env_set_syncbytes
#define mdb_txn_begin mdbx_txn_begin
#define mdb_txn_env mdbx_txn_env
#define mdb_txn_id mdbx_txn_id
#define mdb_txn_commit mdbx_txn_commit
#define mdb_txn_abort mdbx_txn_abort
#define mdb_txn_reset mdbx_txn_reset
#define mdb_txn_renew mdbx_txn_renew
#define mdb_dbi_open mdbx_dbi_open
#define mdb_dbi_flags mdbx_dbi_flags
#define mdb_dbi_close mdbx_dbi_close
#define mdb_drop mdbx_drop
#define mdb_set_compare mdbx_set_compare
#define mdb_set_dupsort mdbx_set_dupsort
#define mdb_set_relfunc mdbx_set_relfunc
#define mdb_set_relctx mdbx_set_relctx
#define mdb_get mdbx_get
#define mdb_put mdbx_put
#define mdb_del mdbx_del
#define mdb_cursor_open mdbx_cursor_open
#define mdb_cursor_close mdbx_cursor_close
#define mdb_cursor_renew mdbx_cursor_renew
#define mdb_cursor_txn mdbx_cursor_txn
#define mdb_cursor_dbi mdbx_cursor_dbi
#define mdb_cursor_get mdbx_cursor_get
#define mdb_cursor_put mdbx_cursor_put
#define mdb_cursor_del mdbx_cursor_del
#define mdb_cursor_count mdbx_cursor_count
#define mdb_cmp mdbx_cmp
#define mdb_dcmp mdbx_dcmp
#define mdb_reader_list mdbx_reader_list
#define mdb_reader_check mdbx_reader_check
#define mdb_dkey mdbx_dkey
/** Compat with version <= 0.9.4, avoid clash with libmdb from MDB Tools project */
#define mdbx_open(txn,name,flags,dbi) mdbx_dbi_open(txn,name,flags,dbi)
#define mdbx_close(env,dbi) mdbx_dbi_close(env,dbi)
#include "./lmdb.h"
#endif /* _MDBX_H_ */

29
midl.c
View File

@ -30,7 +30,7 @@
*/ */
#define CMP(x,y) ( (x) < (y) ? -1 : (x) > (y) ) #define CMP(x,y) ( (x) < (y) ? -1 : (x) > (y) )
unsigned mdb_midl_search( MDB_IDL ids, MDB_ID id ) static unsigned mdb_midl_search( MDB_IDL ids, MDB_ID id )
{ {
/* /*
* binary search of id in ids * binary search of id in ids
@ -66,7 +66,7 @@ unsigned mdb_midl_search( MDB_IDL ids, MDB_ID id )
} }
#if 0 /* superseded by append/sort */ #if 0 /* superseded by append/sort */
int mdb_midl_insert( MDB_IDL ids, MDB_ID id ) static int mdb_midl_insert( MDB_IDL ids, MDB_ID id )
{ {
unsigned x, i; unsigned x, i;
@ -100,7 +100,7 @@ int mdb_midl_insert( MDB_IDL ids, MDB_ID id )
} }
#endif #endif
MDB_IDL mdb_midl_alloc(int num) static MDB_IDL mdb_midl_alloc(int num)
{ {
MDB_IDL ids = malloc((num+2) * sizeof(MDB_ID)); MDB_IDL ids = malloc((num+2) * sizeof(MDB_ID));
if (ids) { if (ids) {
@ -110,13 +110,13 @@ MDB_IDL mdb_midl_alloc(int num)
return ids; return ids;
} }
void mdb_midl_free(MDB_IDL ids) static void mdb_midl_free(MDB_IDL ids)
{ {
if (ids) if (ids)
free(ids-1); free(ids-1);
} }
void mdb_midl_shrink( MDB_IDL *idp ) static void mdb_midl_shrink( MDB_IDL *idp )
{ {
MDB_IDL ids = *idp; MDB_IDL ids = *idp;
if (*(--ids) > MDB_IDL_UM_MAX && if (*(--ids) > MDB_IDL_UM_MAX &&
@ -139,7 +139,7 @@ static int mdb_midl_grow( MDB_IDL *idp, int num )
return 0; return 0;
} }
int mdb_midl_need( MDB_IDL *idp, unsigned num ) static int mdb_midl_need( MDB_IDL *idp, unsigned num )
{ {
MDB_IDL ids = *idp; MDB_IDL ids = *idp;
num += ids[0]; num += ids[0];
@ -153,7 +153,7 @@ int mdb_midl_need( MDB_IDL *idp, unsigned num )
return 0; return 0;
} }
int mdb_midl_append( MDB_IDL *idp, MDB_ID id ) static int mdb_midl_append( MDB_IDL *idp, MDB_ID id )
{ {
MDB_IDL ids = *idp; MDB_IDL ids = *idp;
/* Too big? */ /* Too big? */
@ -167,7 +167,7 @@ int mdb_midl_append( MDB_IDL *idp, MDB_ID id )
return 0; return 0;
} }
int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app ) static int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app )
{ {
MDB_IDL ids = *idp; MDB_IDL ids = *idp;
/* Too big? */ /* Too big? */
@ -181,7 +181,7 @@ int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app )
return 0; return 0;
} }
int mdb_midl_append_range( MDB_IDL *idp, MDB_ID id, unsigned n ) static int mdb_midl_append_range( MDB_IDL *idp, MDB_ID id, unsigned n )
{ {
MDB_ID *ids = *idp, len = ids[0]; MDB_ID *ids = *idp, len = ids[0];
/* Too big? */ /* Too big? */
@ -197,7 +197,7 @@ int mdb_midl_append_range( MDB_IDL *idp, MDB_ID id, unsigned n )
return 0; return 0;
} }
void mdb_midl_xmerge( MDB_IDL idl, MDB_IDL merge ) static void mdb_midl_xmerge( MDB_IDL idl, MDB_IDL merge )
{ {
MDB_ID old_id, merge_id, i = merge[0], j = idl[0], k = i+j, total = k; MDB_ID old_id, merge_id, i = merge[0], j = idl[0], k = i+j, total = k;
idl[0] = (MDB_ID)-1; /* delimiter for idl scan below */ idl[0] = (MDB_ID)-1; /* delimiter for idl scan below */
@ -216,8 +216,7 @@ void mdb_midl_xmerge( MDB_IDL idl, MDB_IDL merge )
#define SMALL 8 #define SMALL 8
#define MIDL_SWAP(a,b) { itmp=(a); (a)=(b); (b)=itmp; } #define MIDL_SWAP(a,b) { itmp=(a); (a)=(b); (b)=itmp; }
void static void mdb_midl_sort( MDB_IDL ids )
mdb_midl_sort( MDB_IDL ids )
{ {
/* Max possible depth of int-indexed tree * 2 items/level */ /* Max possible depth of int-indexed tree * 2 items/level */
int istack[sizeof(int)*CHAR_BIT * 2]; int istack[sizeof(int)*CHAR_BIT * 2];
@ -277,7 +276,7 @@ mdb_midl_sort( MDB_IDL ids )
} }
} }
unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id ) static unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id )
{ {
/* /*
* binary search of id in ids * binary search of id in ids
@ -312,7 +311,7 @@ unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id )
return cursor; return cursor;
} }
int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id ) static int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id )
{ {
unsigned x, i; unsigned x, i;
@ -343,7 +342,7 @@ int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id )
return 0; return 0;
} }
int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id ) static int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id )
{ {
/* Too big? */ /* Too big? */
if (ids[0].mid >= MDB_IDL_UM_MAX) { if (ids[0].mid >= MDB_IDL_UM_MAX) {

30
midl.h
View File

@ -71,6 +71,8 @@ typedef MDB_ID *MDB_IDL;
/** Current max length of an #mdb_midl_alloc()ed IDL */ /** Current max length of an #mdb_midl_alloc()ed IDL */
#define MDB_IDL_ALLOCLEN( ids ) ( (ids)[-1] ) #define MDB_IDL_ALLOCLEN( ids ) ( (ids)[-1] )
#ifdef MDBX_MODE_ENABLED
/** Append ID to IDL. The IDL must be big enough. */ /** Append ID to IDL. The IDL must be big enough. */
#define mdb_midl_xappend(idl, id) do { \ #define mdb_midl_xappend(idl, id) do { \
MDB_ID *xidl = (idl), xlen = ++(xidl[0]); \ MDB_ID *xidl = (idl), xlen = ++(xidl[0]); \
@ -82,45 +84,45 @@ typedef MDB_ID *MDB_IDL;
* @param[in] id The ID to search for. * @param[in] id The ID to search for.
* @return The index of the first ID greater than or equal to \b id. * @return The index of the first ID greater than or equal to \b id.
*/ */
unsigned mdb_midl_search( MDB_IDL ids, MDB_ID id ); static unsigned mdb_midl_search( MDB_IDL ids, MDB_ID id );
/** Allocate an IDL. /** Allocate an IDL.
* Allocates memory for an IDL of the given size. * Allocates memory for an IDL of the given size.
* @return IDL on success, NULL on failure. * @return IDL on success, NULL on failure.
*/ */
MDB_IDL mdb_midl_alloc(int num); static MDB_IDL mdb_midl_alloc(int num);
/** Free an IDL. /** Free an IDL.
* @param[in] ids The IDL to free. * @param[in] ids The IDL to free.
*/ */
void mdb_midl_free(MDB_IDL ids); static void mdb_midl_free(MDB_IDL ids);
/** Shrink an IDL. /** Shrink an IDL.
* Return the IDL to the default size if it has grown larger. * Return the IDL to the default size if it has grown larger.
* @param[in,out] idp Address of the IDL to shrink. * @param[in,out] idp Address of the IDL to shrink.
*/ */
void mdb_midl_shrink(MDB_IDL *idp); static void mdb_midl_shrink(MDB_IDL *idp);
/** Make room for num additional elements in an IDL. /** Make room for num additional elements in an IDL.
* @param[in,out] idp Address of the IDL. * @param[in,out] idp Address of the IDL.
* @param[in] num Number of elements to make room for. * @param[in] num Number of elements to make room for.
* @return 0 on success, ENOMEM on failure. * @return 0 on success, ENOMEM on failure.
*/ */
int mdb_midl_need(MDB_IDL *idp, unsigned num); static int mdb_midl_need(MDB_IDL *idp, unsigned num);
/** Append an ID onto an IDL. /** Append an ID onto an IDL.
* @param[in,out] idp Address of the IDL to append to. * @param[in,out] idp Address of the IDL to append to.
* @param[in] id The ID to append. * @param[in] id The ID to append.
* @return 0 on success, ENOMEM if the IDL is too large. * @return 0 on success, ENOMEM if the IDL is too large.
*/ */
int mdb_midl_append( MDB_IDL *idp, MDB_ID id ); static int mdb_midl_append( MDB_IDL *idp, MDB_ID id );
/** Append an IDL onto an IDL. /** Append an IDL onto an IDL.
* @param[in,out] idp Address of the IDL to append to. * @param[in,out] idp Address of the IDL to append to.
* @param[in] app The IDL to append. * @param[in] app The IDL to append.
* @return 0 on success, ENOMEM if the IDL is too large. * @return 0 on success, ENOMEM if the IDL is too large.
*/ */
int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app ); static int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app );
/** Append an ID range onto an IDL. /** Append an ID range onto an IDL.
* @param[in,out] idp Address of the IDL to append to. * @param[in,out] idp Address of the IDL to append to.
@ -128,18 +130,18 @@ int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app );
* @param[in] n Number of IDs to append. * @param[in] n Number of IDs to append.
* @return 0 on success, ENOMEM if the IDL is too large. * @return 0 on success, ENOMEM if the IDL is too large.
*/ */
int mdb_midl_append_range( MDB_IDL *idp, MDB_ID id, unsigned n ); static int mdb_midl_append_range( MDB_IDL *idp, MDB_ID id, unsigned n );
/** Merge an IDL onto an IDL. The destination IDL must be big enough. /** Merge an IDL onto an IDL. The destination IDL must be big enough.
* @param[in] idl The IDL to merge into. * @param[in] idl The IDL to merge into.
* @param[in] merge The IDL to merge. * @param[in] merge The IDL to merge.
*/ */
void mdb_midl_xmerge( MDB_IDL idl, MDB_IDL merge ); static void mdb_midl_xmerge( MDB_IDL idl, MDB_IDL merge );
/** Sort an IDL. /** Sort an IDL.
* @param[in,out] ids The IDL to sort. * @param[in,out] ids The IDL to sort.
*/ */
void mdb_midl_sort( MDB_IDL ids ); static void mdb_midl_sort( MDB_IDL ids );
/** An ID2 is an ID/pointer pair. /** An ID2 is an ID/pointer pair.
*/ */
@ -160,7 +162,7 @@ typedef MDB_ID2 *MDB_ID2L;
* @param[in] id The ID to search for. * @param[in] id The ID to search for.
* @return The index of the first ID2 whose \b mid member is greater than or equal to \b id. * @return The index of the first ID2 whose \b mid member is greater than or equal to \b id.
*/ */
unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id ); static unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id );
/** Insert an ID2 into a ID2L. /** Insert an ID2 into a ID2L.
@ -168,14 +170,16 @@ unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id );
* @param[in] id The ID2 to insert. * @param[in] id The ID2 to insert.
* @return 0 on success, -1 if the ID was already present in the ID2L. * @return 0 on success, -1 if the ID was already present in the ID2L.
*/ */
int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id ); static int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id );
/** Append an ID2 into a ID2L. /** Append an ID2 into a ID2L.
* @param[in,out] ids The ID2L to append into. * @param[in,out] ids The ID2L to append into.
* @param[in] id The ID2 to append. * @param[in] id The ID2 to append.
* @return 0 on success, -2 if the ID2L is too big. * @return 0 on success, -2 if the ID2L is too big.
*/ */
int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id ); static int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id );
#endif /* #ifdef MDBX_MODE_ENABLED */
/** @} */ /** @} */
/** @} */ /** @} */

View File

@ -17,7 +17,7 @@
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "lmdb.h" #include "mdbx.h"
#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr) #define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0)) #define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))

View File

@ -26,7 +26,7 @@
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "lmdb.h" #include "mdbx.h"
#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr) #define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0)) #define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))

View File

@ -20,7 +20,7 @@
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "lmdb.h" #include "mdbx.h"
#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr) #define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0)) #define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))

View File

@ -20,7 +20,7 @@
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "lmdb.h" #include "mdbx.h"
#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr) #define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0)) #define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))

View File

@ -20,7 +20,7 @@
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "lmdb.h" #include "mdbx.h"
#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr) #define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0)) #define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))

View File

@ -20,7 +20,7 @@
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "lmdb.h" #include "mdbx.h"
#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr) #define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0)) #define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))

View File

@ -20,7 +20,7 @@
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "lmdb.h" #include "mdbx.h"
#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr) #define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0)) #define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))

View File

@ -31,9 +31,7 @@
# define _GNU_SOURCE # define _GNU_SOURCE
#endif #endif
#ifdef HAVE_ANSIDECL_H #include <ansidecl.h>
# include <ansidecl.h>
#endif
#ifndef __has_attribute #ifndef __has_attribute
# define __has_attribute(x) (0) # define __has_attribute(x) (0)
@ -88,6 +86,9 @@
#ifndef __cold #ifndef __cold
# if defined(__GNUC__) && !defined(__clang__) # if defined(__GNUC__) && !defined(__clang__)
# define __cold __attribute__((cold, optimize("Os"))) # define __cold __attribute__((cold, optimize("Os")))
# elif defined(__GNUC__)
/* cland case, just put infrequently used functions in separate section */
# define __cold __attribute__((section("text.cold")))
# else # else
# define __cold # define __cold
# endif # endif

View File

@ -27,7 +27,7 @@
#include <sys/resource.h> #include <sys/resource.h>
#include <errno.h> #include <errno.h>
#include "lmdb.h" #include "mdbx.h"
#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr) #define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0)) #define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))