From 0a75417d5f10fbef6405422600340e5ccb63b83e Mon Sep 17 00:00:00 2001 From: Leonid Yuriev Date: Fri, 21 Aug 2020 18:01:16 +0300 Subject: [PATCH] mdbx: add pure- & const-function attributes to C API. Change-Id: Ie4d1742f3d4717a0cd1fd5677b9b1ae641193d45 --- mdbx.h | 221 ++++++++++++++++++++++++++++++++++++++---------- src/core.c | 102 +++++++++++----------- src/defs.h | 54 ------------ src/internals.h | 12 +-- src/osal.h | 3 +- 5 files changed, 238 insertions(+), 154 deletions(-) diff --git a/mdbx.h b/mdbx.h index c3c80831..5dc5db90 100644 --- a/mdbx.h +++ b/mdbx.h @@ -137,6 +137,100 @@ typedef pthread_t mdbx_tid_t; #define __has_attribute(x) (0) #endif /* __has_attribute */ +#ifndef __has_cpp_attribute +#define __has_cpp_attribute(x) 0 +#endif /* __has_cpp_attribute */ + +#ifndef __has_feature +#define __has_feature(x) (0) +#endif /* __has_feature */ + +#ifndef __has_extension +#define __has_extension(x) (0) +#endif /* __has_extension */ + +#ifndef __has_builtin +#define __has_builtin(x) (0) +#endif /* __has_builtin */ + +#ifndef __pure_function +/** Many functions have no effects except the return value and their + * return value depends only on the parameters and/or global variables. + * Such a function can be subject to common subexpression elimination + * and loop optimization just as an arithmetic operator would be. + * These functions should be declared with the attribute pure. */ +#if (defined(__GNUC__) || __has_attribute(__pure__)) && \ + (!defined(__clang__) /* https://bugs.llvm.org/show_bug.cgi?id=43275 */ \ + || !defined(__cplusplus) || !__has_feature(cxx_exceptions)) +#define __pure_function __attribute__((__pure__)) +#elif defined(__cplusplus) && __has_cpp_attribute(gnu::pure) && \ + (!defined(__clang__) || !__has_feature(cxx_exceptions)) +#define __pure_function [[gnu::pure]] +#else +#define __pure_function +#endif +#endif /* __pure_function */ + +#ifndef __nothrow_pure_function +/** Like \ref __pure_function with addition `noexcept` restriction + * that is compatible to CLANG and proposed [[pure]]. */ +#if defined(__GNUC__) || \ + (__has_attribute(__pure__) && __has_attribute(__nothrow__)) +#define __nothrow_pure_function __attribute__((__pure__, __nothrow__)) +#elif defined(__cplusplus) && __has_cpp_attribute(gnu::pure) +#if __has_cpp_attribute(gnu::nothrow) +#define __nothrow_pure_function [[gnu::pure, gnu::nothrow]] +#else +#define __nothrow_pure_function [[gnu::pure]] +#endif +#elif defined(__cplusplus) && __has_cpp_attribute(pure) +#define __nothrow_pure_function [[pure]] +#else +#define __nothrow_pure_function +#endif +#endif /* __nothrow_pure_function */ + +#ifndef __const_function +/** Many functions do not examine any values except their arguments, + * and have no effects except the return value. Basically this is just + * slightly more strict class than the PURE attribute, since function + * is not allowed to read global memory. + * + * Note that a function that has pointer arguments and examines the + * data pointed to must not be declared const. Likewise, a function + * that calls a non-const function usually must not be const. + * It does not make sense for a const function to return void. */ +#if (defined(__GNUC__) || __has_attribute(__pure__)) && \ + (!defined(__clang__) /* https://bugs.llvm.org/show_bug.cgi?id=43275 */ \ + || !defined(__cplusplus) || !__has_feature(cxx_exceptions)) +#define __const_function __attribute__((__const__)) +#elif defined(__cplusplus) && __has_cpp_attribute(gnu::const) && \ + (!defined(__clang__) || !__has_feature(cxx_exceptions)) +#define __const_function [[gnu::const]] +#else +#define __const_function __pure_function +#endif +#endif /* __const_function */ + +#ifndef __nothrow_const_function +/** Like \ref __const_function with addition `noexcept` restriction + * that is compatible to CLANG and future [[const]]. */ +#if defined(__GNUC__) || \ + (__has_attribute(__const__) && __has_attribute(__nothrow__)) +#define __nothrow_const_function __attribute__((__const__, __nothrow__)) +#elif defined(__cplusplus) && __has_cpp_attribute(gnu::const) +#if __has_cpp_attribute(gnu::nothrow) +#define __nothrow_pure_function [[gnu::const, gnu::nothrow]] +#else +#define __nothrow_pure_function [[gnu::const]] +#endif +#elif defined(__cplusplus) && __has_cpp_attribute(const) +#define __nothrow_const_function [[const]] +#else +#define __nothrow_const_function __nothrow_pure_function +#endif +#endif /* __nothrow_pure_function */ + #ifndef MDBX_DEPRECATED #ifdef __deprecated #define MDBX_DEPRECATED __deprecated @@ -2138,40 +2232,47 @@ LIBMDBX_API int mdbx_is_readahead_reasonable(size_t volume, /** Returns the minimal database page size in bytes. * \ingroup c_statinfo */ -__inline intptr_t mdbx_limits_pgsize_min(void) { return MDBX_MIN_PAGESIZE; } +__nothrow_const_function __inline intptr_t mdbx_limits_pgsize_min(void) { + return MDBX_MIN_PAGESIZE; +} /** Returns the maximal database page size in bytes. * \ingroup c_statinfo */ -__inline intptr_t mdbx_limits_pgsize_max(void) { return MDBX_MAX_PAGESIZE; } +__nothrow_const_function __inline intptr_t mdbx_limits_pgsize_max(void) { + return MDBX_MAX_PAGESIZE; +} /** Returns minimal database size in bytes for given page size, * or -1 if pagesize is invalid. * \ingroup c_statinfo */ -LIBMDBX_API intptr_t mdbx_limits_dbsize_min(intptr_t pagesize); +__nothrow_const_function LIBMDBX_API intptr_t +mdbx_limits_dbsize_min(intptr_t pagesize); /** Returns maximal database size in bytes for given page size, * or -1 if pagesize is invalid. * \ingroup c_statinfo */ -LIBMDBX_API intptr_t mdbx_limits_dbsize_max(intptr_t pagesize); +__nothrow_const_function LIBMDBX_API intptr_t +mdbx_limits_dbsize_max(intptr_t pagesize); /** Returns maximal key size in bytes for given page size * and database flags, or -1 if pagesize is invalid. * \ingroup c_statinfo * \see db_flags */ -LIBMDBX_API intptr_t mdbx_limits_keysize_max(intptr_t pagesize, - MDBX_db_flags_t flags); +__nothrow_const_function LIBMDBX_API intptr_t +mdbx_limits_keysize_max(intptr_t pagesize, MDBX_db_flags_t flags); /** Returns maximal data size in bytes for given page size * and database flags, or -1 if pagesize is invalid. * \ingroup c_statinfo * \see db_flags */ -LIBMDBX_API intptr_t mdbx_limits_valsize_max(intptr_t pagesize, - MDBX_db_flags_t flags); +__nothrow_const_function LIBMDBX_API intptr_t +mdbx_limits_valsize_max(intptr_t pagesize, MDBX_db_flags_t flags); /** Returns maximal write transaction size (i.e. limit for summary volume of * dirty pages) in bytes for given page size, or -1 if pagesize is invalid. * \ingroup c_statinfo */ -LIBMDBX_API intptr_t mdbx_limits_txnsize_max(intptr_t pagesize); +__nothrow_const_function LIBMDBX_API intptr_t +mdbx_limits_txnsize_max(intptr_t pagesize); /** Set the maximum number of threads/reader slots for the environment. * \ingroup c_settings @@ -2253,8 +2354,8 @@ LIBMDBX_API int mdbx_env_get_maxdbs(MDBX_env *env, MDBX_dbi *dbs); * * \returns The maximum size of a key can write, * or -1 if something is wrong. */ -LIBMDBX_API int mdbx_env_get_maxkeysize_ex(const MDBX_env *env, - MDBX_db_flags_t flags); +__nothrow_pure_function LIBMDBX_API int +mdbx_env_get_maxkeysize_ex(const MDBX_env *env, MDBX_db_flags_t flags); /** Get the maximum size of data we can write. * \ingroup c_statinfo @@ -2265,13 +2366,14 @@ LIBMDBX_API int mdbx_env_get_maxkeysize_ex(const MDBX_env *env, * * \returns The maximum size of a data can write, * or -1 if something is wrong. */ -LIBMDBX_API int mdbx_env_get_maxvalsize_ex(const MDBX_env *env, - MDBX_db_flags_t flags); +__nothrow_pure_function LIBMDBX_API int +mdbx_env_get_maxvalsize_ex(const MDBX_env *env, MDBX_db_flags_t flags); /** \deprecated Please use \ref mdbx_env_get_maxkeysize_ex() * and/or \ref mdbx_env_get_maxvalsize_ex() * \ingroup c_statinfo */ -MDBX_DEPRECATED LIBMDBX_API int mdbx_env_get_maxkeysize(const MDBX_env *env); +__nothrow_pure_function MDBX_DEPRECATED LIBMDBX_API int +mdbx_env_get_maxkeysize(const MDBX_env *env); /** Set application information associated with the \ref MDBX_env. * \ingroup c_settings @@ -2289,7 +2391,8 @@ LIBMDBX_API int mdbx_env_set_userctx(MDBX_env *env, void *ctx); * * \param [in] env An environment handle returned by \ref mdbx_env_create() * \returns The pointer set by \ref mdbx_env_set_userctx(). */ -LIBMDBX_API void *mdbx_env_get_userctx(const MDBX_env *env); +__nothrow_pure_function LIBMDBX_API void * +mdbx_env_get_userctx(const MDBX_env *env); /** Create a transaction for use with the environment. * \ingroup c_transactions @@ -2414,7 +2517,7 @@ LIBMDBX_API int mdbx_txn_info(const MDBX_txn *txn, MDBX_txn_info *info, * \ingroup c_transactions * * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin() */ -LIBMDBX_API MDBX_env *mdbx_txn_env(const MDBX_txn *txn); +__nothrow_pure_function LIBMDBX_API MDBX_env *mdbx_txn_env(const MDBX_txn *txn); /** Return the transaction's flags. * \ingroup c_transactions @@ -2425,7 +2528,7 @@ LIBMDBX_API MDBX_env *mdbx_txn_env(const MDBX_txn *txn); * * \returns A transaction flags, valid if input is an valid transaction, * otherwise -1. */ -LIBMDBX_API int mdbx_txn_flags(const MDBX_txn *txn); +__nothrow_pure_function LIBMDBX_API int mdbx_txn_flags(const MDBX_txn *txn); /** Return the transaction's ID. * \ingroup c_statinfo @@ -2438,7 +2541,7 @@ LIBMDBX_API int mdbx_txn_flags(const MDBX_txn *txn); * * \returns A transaction ID, valid if input is an active transaction, * otherwise 0. */ -LIBMDBX_API uint64_t mdbx_txn_id(const MDBX_txn *txn); +__nothrow_pure_function LIBMDBX_API uint64_t mdbx_txn_id(const MDBX_txn *txn); /** Commit all the operations of a transaction into the database. * \ingroup c_transactions @@ -2748,15 +2851,28 @@ mdbx_dbi_open_ex(MDBX_txn *txn, const char *name, MDBX_db_flags_t flags, * and IEEE754 double values in one index for JSON-numbers with restriction for * integer numbers range corresponding to RFC-7159, i.e. \f$[-2^{53}+1, * 2^{53}-1]\f$. See bottom of page 6 at https://tools.ietf.org/html/rfc7159 */ -LIBMDBX_API uint64_t mdbx_key_from_jsonInteger(const int64_t json_integer); -LIBMDBX_API uint64_t mdbx_key_from_double(const double ieee754_64bit); -LIBMDBX_API uint64_t mdbx_key_from_ptrdouble(const double *const ieee754_64bit); -LIBMDBX_API uint32_t mdbx_key_from_float(const float ieee754_32bit); -LIBMDBX_API uint32_t mdbx_key_from_ptrfloat(const float *const ieee754_32bit); -__inline uint64_t mdbx_key_from_int64(const int64_t i64) { +__nothrow_const_function LIBMDBX_API uint64_t +mdbx_key_from_jsonInteger(const int64_t json_integer); + +__nothrow_const_function LIBMDBX_API uint64_t +mdbx_key_from_double(const double ieee754_64bit); + +__nothrow_pure_function LIBMDBX_API uint64_t +mdbx_key_from_ptrdouble(const double *const ieee754_64bit); + +__nothrow_const_function LIBMDBX_API uint32_t +mdbx_key_from_float(const float ieee754_32bit); + +__nothrow_const_function LIBMDBX_API uint32_t +mdbx_key_from_ptrfloat(const float *const ieee754_32bit); + +__nothrow_const_function __inline uint64_t +mdbx_key_from_int64(const int64_t i64) { return UINT64_C(0x8000000000000000) + i64; } -__inline uint32_t mdbx_key_from_int32(const int32_t i32) { + +__nothrow_const_function __inline uint32_t +mdbx_key_from_int32(const int32_t i32) { return UINT32_C(0x80000000) + i32; } /** @} */ @@ -2764,11 +2880,16 @@ __inline uint32_t mdbx_key_from_int32(const int32_t i32) { /** \defgroup key2value Key-to-Value functions to avoid custom comparators * \see value2key * @{ */ -LIBMDBX_API int64_t mdbx_jsonInteger_from_key(const MDBX_val); -LIBMDBX_API double mdbx_double_from_key(const MDBX_val); -LIBMDBX_API float mdbx_float_from_key(const MDBX_val); -LIBMDBX_API int32_t mdbx_int32_from_key(const MDBX_val); -LIBMDBX_API int64_t mdbx_int64_from_key(const MDBX_val); +__nothrow_pure_function LIBMDBX_API int64_t +mdbx_jsonInteger_from_key(const MDBX_val); + +__nothrow_pure_function LIBMDBX_API double mdbx_double_from_key(const MDBX_val); + +__nothrow_pure_function LIBMDBX_API float mdbx_float_from_key(const MDBX_val); + +__nothrow_pure_function LIBMDBX_API int32_t mdbx_int32_from_key(const MDBX_val); + +__nothrow_pure_function LIBMDBX_API int64_t mdbx_int64_from_key(const MDBX_val); /** @} */ /** Retrieve statistics for a database. @@ -3181,7 +3302,8 @@ LIBMDBX_API int mdbx_cursor_renew(MDBX_txn *txn, MDBX_cursor *cursor); * \ingroup c_cursors * * \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open(). */ -LIBMDBX_API MDBX_txn *mdbx_cursor_txn(const MDBX_cursor *cursor); +__nothrow_pure_function LIBMDBX_API MDBX_txn * +mdbx_cursor_txn(const MDBX_cursor *cursor); /** Return the cursor's database handle. * \ingroup c_cursors @@ -3347,7 +3469,8 @@ LIBMDBX_API int mdbx_cursor_count(const MDBX_cursor *cursor, size_t *pcount); * positioned * \retval MDBX_RESULT_FALSE A data is available * \retval Otherwise the error code */ -LIBMDBX_API int mdbx_cursor_eof(const MDBX_cursor *cursor); +__nothrow_pure_function LIBMDBX_API int +mdbx_cursor_eof(const MDBX_cursor *cursor); /** Determines whether the cursor is pointed to the first key-value pair or * not. @@ -3358,9 +3481,10 @@ LIBMDBX_API int mdbx_cursor_eof(const MDBX_cursor *cursor); * \returns A MDBX_RESULT_TRUE or MDBX_RESULT_FALSE value, * otherwise the error code: * \retval MDBX_RESULT_TRUE Cursor positioned to the first key-value pair - * \retval MDBX_RESULT_FALSE Cursor NOT positioned to the first key-value pair - * \retval Otherwise the error code */ -LIBMDBX_API int mdbx_cursor_on_first(const MDBX_cursor *cursor); + * \retval MDBX_RESULT_FALSE Cursor NOT positioned to the first key-value + * pair \retval Otherwise the error code */ +__nothrow_pure_function LIBMDBX_API int +mdbx_cursor_on_first(const MDBX_cursor *cursor); /** Determines whether the cursor is pointed to the last key-value pair or not. * \ingroup c_cursors @@ -3372,7 +3496,8 @@ LIBMDBX_API int mdbx_cursor_on_first(const MDBX_cursor *cursor); * \retval MDBX_RESULT_TRUE Cursor positioned to the last key-value pair * \retval MDBX_RESULT_FALSE Cursor NOT positioned to the last key-value pair * \retval Otherwise the error code */ -LIBMDBX_API int mdbx_cursor_on_last(const MDBX_cursor *cursor); +__nothrow_pure_function LIBMDBX_API int +mdbx_cursor_on_last(const MDBX_cursor *cursor); /** \addtogroup c_rqest * \details \note The estimation result varies greatly depending on the filling @@ -3512,7 +3637,8 @@ LIBMDBX_API int mdbx_estimate_range(MDBX_txn *txn, MDBX_dbi dbi, * \retval MDBX_RESULT_TRUE Given address is on the dirty page. * \retval MDBX_RESULT_FALSE Given address is NOT on the dirty page. * \retval Otherwise the error code. */ -LIBMDBX_API int mdbx_is_dirty(const MDBX_txn *txn, const void *ptr); +__nothrow_pure_function LIBMDBX_API int mdbx_is_dirty(const MDBX_txn *txn, + const void *ptr); /** Sequence generation for a database. * \ingroup c_crud @@ -3552,12 +3678,15 @@ LIBMDBX_API int mdbx_dbi_sequence(MDBX_txn *txn, MDBX_dbi dbi, uint64_t *result, * \param [in] b The second item to compare. * * \returns < 0 if a < b, 0 if a == b, > 0 if a > b */ -LIBMDBX_API int mdbx_cmp(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *a, - const MDBX_val *b); +__nothrow_pure_function LIBMDBX_API int mdbx_cmp(const MDBX_txn *txn, + MDBX_dbi dbi, + const MDBX_val *a, + const MDBX_val *b); /** Returns default internal key's comparator for given database flags. * \ingroup c_extra */ -LIBMDBX_API MDBX_cmp_func *mdbx_get_keycmp(MDBX_db_flags_t flags); +__nothrow_const_function LIBMDBX_API MDBX_cmp_func * +mdbx_get_keycmp(MDBX_db_flags_t flags); /** Compare two data items according to a particular database. * \ingroup c_crud @@ -3573,12 +3702,15 @@ LIBMDBX_API MDBX_cmp_func *mdbx_get_keycmp(MDBX_db_flags_t flags); * \param [in] b The second item to compare. * * \returns < 0 if a < b, 0 if a == b, > 0 if a > b */ -LIBMDBX_API int mdbx_dcmp(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *a, - const MDBX_val *b); +__nothrow_pure_function LIBMDBX_API int mdbx_dcmp(const MDBX_txn *txn, + MDBX_dbi dbi, + const MDBX_val *a, + const MDBX_val *b); /** Returns default internal data's comparator for given database flags * \ingroup c_extra */ -LIBMDBX_API MDBX_cmp_func *mdbx_get_datacmp(MDBX_db_flags_t flags); +__nothrow_const_function LIBMDBX_API MDBX_cmp_func * +mdbx_get_datacmp(MDBX_db_flags_t flags); /** A callback function used to enumerate the reader lock table. * \ingroup c_statinfo @@ -3764,7 +3896,8 @@ LIBMDBX_API int mdbx_env_set_oomfunc(MDBX_env *env, MDBX_oom_func *oom_func); * \param [in] env An environment handle returned by \ref mdbx_env_create(). * * \returns A MDBX_oom_func function or NULL if disabled. */ -LIBMDBX_API MDBX_oom_func *mdbx_env_get_oomfunc(const MDBX_env *env); +__nothrow_pure_function LIBMDBX_API MDBX_oom_func * +mdbx_env_get_oomfunc(const MDBX_env *env); /** \defgroup btree_traversal B-tree Traversal * This is internal API for mdbx_chk tool. You should avoid to use it, except diff --git a/src/core.c b/src/core.c index 5d1d5ca6..1646cf19 100644 --- a/src/core.c +++ b/src/core.c @@ -40,7 +40,7 @@ /*------------------------------------------------------------------------------ * Internal inlines */ -static __pure_function unsigned log2n(size_t value) { +__nothrow_const_function static unsigned log2n(size_t value) { assert(value > 0 && value < INT32_MAX && is_powerof2(value)); assert((value & -(int32_t)value) == value); #if __GNUC_PREREQ(4, 1) || __has_builtin(__builtin_ctzl) @@ -60,14 +60,14 @@ static __pure_function unsigned log2n(size_t value) { /*------------------------------------------------------------------------------ * Unaligned access */ -static __pure_function __maybe_unused __always_inline unsigned +__nothrow_const_function static __maybe_unused __always_inline unsigned field_alignment(unsigned alignment_baseline, size_t field_offset) { unsigned merge = alignment_baseline | (unsigned)field_offset; return merge & -(int)merge; } /* read-thunk for UB-sanitizer */ -static __pure_function __always_inline uint8_t +__nothrow_pure_function static __always_inline uint8_t peek_u8(const uint8_t *const __restrict ptr) { return *ptr; } @@ -78,7 +78,7 @@ static __always_inline void poke_u8(uint8_t *const __restrict ptr, *ptr = v; } -static __pure_function __always_inline uint16_t +__nothrow_pure_function static __always_inline uint16_t unaligned_peek_u16(const unsigned expected_alignment, const void *const ptr) { assert((uintptr_t)ptr % expected_alignment == 0); if (MDBX_UNALIGNED_OK || (expected_alignment % sizeof(uint16_t)) == 0) @@ -100,7 +100,7 @@ unaligned_poke_u16(const unsigned expected_alignment, memcpy(ptr, &v, sizeof(v)); } -static __pure_function __always_inline uint32_t unaligned_peek_u32( +__nothrow_pure_function static __always_inline uint32_t unaligned_peek_u32( const unsigned expected_alignment, const void *const __restrict ptr) { assert((uintptr_t)ptr % expected_alignment == 0); if (MDBX_UNALIGNED_OK || (expected_alignment % sizeof(uint32_t)) == 0) @@ -132,7 +132,7 @@ unaligned_poke_u32(const unsigned expected_alignment, memcpy(ptr, &v, sizeof(v)); } -static __pure_function __always_inline uint64_t unaligned_peek_u64( +__nothrow_pure_function static __always_inline uint64_t unaligned_peek_u64( const unsigned expected_alignment, const void *const __restrict ptr) { assert((uintptr_t)ptr % expected_alignment == 0); if (MDBX_UNALIGNED_OK || (expected_alignment % sizeof(uint64_t)) == 0) @@ -185,7 +185,7 @@ unaligned_poke_u64(const unsigned expected_alignment, unaligned_poke_u64(1, (char *)(ptr) + offsetof(struct, field), value) /* Get the page number pointed to by a branch node */ -static __pure_function __always_inline pgno_t +__nothrow_pure_function static __always_inline pgno_t node_pgno(const MDBX_node *const __restrict node) { pgno_t pgno = UNALIGNED_PEEK_32(node, MDBX_node, mn_pgno32); if (sizeof(pgno) > 4) @@ -205,7 +205,7 @@ static __always_inline void node_set_pgno(MDBX_node *const __restrict node, } /* Get the size of the data in a leaf node */ -static __pure_function __always_inline size_t +__nothrow_pure_function static __always_inline size_t node_ds(const MDBX_node *const __restrict node) { return UNALIGNED_PEEK_32(node, MDBX_node, mn_dsize); } @@ -218,7 +218,7 @@ static __always_inline void node_set_ds(MDBX_node *const __restrict node, } /* The size of a key in a node */ -static __pure_function __always_inline size_t +__nothrow_pure_function static __always_inline size_t node_ks(const MDBX_node *const __restrict node) { return UNALIGNED_PEEK_16(node, MDBX_node, mn_ksize); } @@ -230,7 +230,7 @@ static __always_inline void node_set_ks(MDBX_node *const __restrict node, UNALIGNED_POKE_16(node, MDBX_node, mn_ksize, (uint16_t)size); } -static __pure_function __always_inline uint8_t +__nothrow_pure_function static __always_inline uint8_t node_flags(const MDBX_node *const __restrict node) { return UNALIGNED_PEEK_8(node, MDBX_node, mn_flags); } @@ -244,29 +244,29 @@ static __always_inline void node_set_flags(MDBX_node *const __restrict node, #define NODESIZE offsetof(MDBX_node, mn_data) /* Address of the key for the node */ -static __pure_function __always_inline void * +__nothrow_pure_function static __always_inline void * node_key(const MDBX_node *const __restrict node) { return (char *)node + NODESIZE; } /* Address of the data for a node */ -static __pure_function __always_inline void * +__nothrow_pure_function static __always_inline void * node_data(const MDBX_node *const __restrict node) { return (char *)node_key(node) + node_ks(node); } /* Size of a node in a leaf page with a given key and data. * This is node header plus key plus data size. */ -static __pure_function __always_inline size_t +__nothrow_const_function static __always_inline size_t node_size_len(const size_t key_len, const size_t value_len) { return NODESIZE + EVEN(key_len + value_len); } -static __pure_function __always_inline size_t node_size(const MDBX_val *key, - const MDBX_val *value) { +__nothrow_pure_function static __always_inline size_t +node_size(const MDBX_val *key, const MDBX_val *value) { return node_size_len(key ? key->iov_len : 0, value ? value->iov_len : 0); } -static __pure_function __always_inline pgno_t +__nothrow_pure_function static __always_inline pgno_t peek_pgno(const void *const __restrict ptr) { if (sizeof(pgno_t) == sizeof(uint32_t)) return (pgno_t)unaligned_peek_u32(1, ptr); @@ -289,7 +289,7 @@ static __always_inline void poke_pgno(void *const __restrict ptr, memcpy(ptr, &pgno, sizeof(pgno)); } -static __pure_function __always_inline pgno_t +__nothrow_pure_function static __always_inline pgno_t node_largedata_pgno(const MDBX_node *const __restrict node) { assert(node_flags(node) & F_BIGDATA); return peek_pgno(node_data(node)); @@ -411,9 +411,8 @@ __cold intptr_t mdbx_limits_valsize_max(intptr_t pagesize, * size will only include the key and not the data. Sizes are always * rounded up to an even number of bytes, to guarantee 2-byte alignment * of the MDBX_node headers. */ -static __pure_function __always_inline size_t leaf_size(const MDBX_env *env, - const MDBX_val *key, - const MDBX_val *data) { +__nothrow_pure_function static __always_inline size_t +leaf_size(const MDBX_env *env, const MDBX_val *key, const MDBX_val *data) { size_t node_bytes = node_size(key, data); /* NOTE: The actual limit is LEAF_NODEMAX(env->me_psize), but it reasonable to * use env->me_branch_nodemax (which is 3 times less) as the treshold because: @@ -455,8 +454,8 @@ static __pure_function __always_inline size_t leaf_size(const MDBX_env *env, * [in] key The key for the node. * * Returns The number of bytes needed to store the node. */ -static __pure_function __always_inline size_t branch_size(const MDBX_env *env, - const MDBX_val *key) { +__nothrow_pure_function static __always_inline size_t +branch_size(const MDBX_env *env, const MDBX_val *key) { /* Size of a node in a branch page with a given key. * This is just the node header plus the key, there is no data. */ size_t node_bytes = node_size(key, nullptr); @@ -471,7 +470,7 @@ static __pure_function __always_inline size_t branch_size(const MDBX_env *env, return node_bytes + sizeof(indx_t); } -static __pure_function __always_inline uint16_t +__nothrow_const_function static __always_inline uint16_t flags_db2sub(uint16_t db_flags) { uint16_t sub_flags = db_flags & MDBX_DUPFIXED; @@ -492,81 +491,84 @@ flags_db2sub(uint16_t db_flags) { /*----------------------------------------------------------------------------*/ -static __pure_function __always_inline size_t pgno2bytes(const MDBX_env *env, - pgno_t pgno) { +__nothrow_pure_function static __always_inline size_t +pgno2bytes(const MDBX_env *env, pgno_t pgno) { mdbx_assert(env, (1u << env->me_psize2log) == env->me_psize); return ((size_t)pgno) << env->me_psize2log; } -static __pure_function __always_inline MDBX_page *pgno2page(const MDBX_env *env, - pgno_t pgno) { +__nothrow_pure_function static __always_inline MDBX_page * +pgno2page(const MDBX_env *env, pgno_t pgno) { return (MDBX_page *)(env->me_map + pgno2bytes(env, pgno)); } -static __pure_function __always_inline pgno_t bytes2pgno(const MDBX_env *env, - size_t bytes) { +__nothrow_pure_function static __always_inline pgno_t +bytes2pgno(const MDBX_env *env, size_t bytes) { mdbx_assert(env, (env->me_psize >> env->me_psize2log) == 1); return (pgno_t)(bytes >> env->me_psize2log); } -static __pure_function size_t pgno_align2os_bytes(const MDBX_env *env, - pgno_t pgno) { +__nothrow_pure_function static size_t pgno_align2os_bytes(const MDBX_env *env, + pgno_t pgno) { return ceil_powerof2(pgno2bytes(env, pgno), env->me_os_psize); } -static __pure_function pgno_t pgno_align2os_pgno(const MDBX_env *env, - pgno_t pgno) { +__nothrow_pure_function static pgno_t pgno_align2os_pgno(const MDBX_env *env, + pgno_t pgno) { return bytes2pgno(env, pgno_align2os_bytes(env, pgno)); } -static __pure_function size_t bytes_align2os_bytes(const MDBX_env *env, - size_t bytes) { +__nothrow_pure_function static size_t bytes_align2os_bytes(const MDBX_env *env, + size_t bytes) { return ceil_powerof2(ceil_powerof2(bytes, env->me_psize), env->me_os_psize); } /* Address of first usable data byte in a page, after the header */ -static __pure_function __always_inline void *page_data(const MDBX_page *mp) { +__nothrow_pure_function static __always_inline void * +page_data(const MDBX_page *mp) { return (char *)mp + PAGEHDRSZ; } -static __pure_function __always_inline const MDBX_page * +__nothrow_pure_function static __always_inline const MDBX_page * data_page(const void *data) { return container_of(data, MDBX_page, mp_ptrs); } -static __pure_function __always_inline MDBX_meta *page_meta(MDBX_page *mp) { +__nothrow_pure_function static __always_inline MDBX_meta * +page_meta(MDBX_page *mp) { return (MDBX_meta *)page_data(mp); } /* Number of nodes on a page */ -static __pure_function __always_inline unsigned +__nothrow_pure_function static __always_inline unsigned page_numkeys(const MDBX_page *mp) { return mp->mp_lower >> 1; } /* The amount of space remaining in the page */ -static __pure_function __always_inline unsigned page_room(const MDBX_page *mp) { +__nothrow_pure_function static __always_inline unsigned +page_room(const MDBX_page *mp) { return mp->mp_upper - mp->mp_lower; } -static __pure_function __always_inline unsigned +__nothrow_pure_function static __always_inline unsigned page_space(const MDBX_env *env) { STATIC_ASSERT(PAGEHDRSZ % 2 == 0); return env->me_psize - PAGEHDRSZ; } -static __pure_function __always_inline unsigned page_used(const MDBX_env *env, - const MDBX_page *mp) { +__nothrow_pure_function static __always_inline unsigned +page_used(const MDBX_env *env, const MDBX_page *mp) { return page_space(env) - page_room(mp); } /* The percentage of space used in the page, in a percents. */ -static __pure_function __maybe_unused __inline double +__nothrow_pure_function static __maybe_unused __inline double page_fill(const MDBX_env *env, const MDBX_page *mp) { return page_used(env, mp) * 100.0 / page_space(env); } -static __pure_function __inline bool +__nothrow_pure_function static __inline bool page_fill_enough(const MDBX_page *mp, unsigned spaceleft_threshold, unsigned minkeys_threshold) { return page_room(mp) < spaceleft_threshold && @@ -574,14 +576,14 @@ page_fill_enough(const MDBX_page *mp, unsigned spaceleft_threshold, } /* The number of overflow pages needed to store the given size. */ -static __pure_function __always_inline pgno_t +__nothrow_pure_function static __always_inline pgno_t number_of_ovpages(const MDBX_env *env, size_t bytes) { return bytes2pgno(env, PAGEHDRSZ - 1 + bytes) + 1; } /* Address of node i in page p */ -static __pure_function __always_inline MDBX_node *page_node(const MDBX_page *mp, - unsigned i) { +__nothrow_pure_function static __always_inline MDBX_node * +page_node(const MDBX_page *mp, unsigned i) { assert((mp->mp_flags & (P_LEAF2 | P_OVERFLOW | P_META)) == 0); assert(page_numkeys(mp) > (unsigned)(i)); assert(mp->mp_ptrs[i] % 2 == 0); @@ -591,7 +593,7 @@ static __pure_function __always_inline MDBX_node *page_node(const MDBX_page *mp, /* The address of a key in a LEAF2 page. * LEAF2 pages are used for MDBX_DUPFIXED sorted-duplicate sub-DBs. * There are no node headers, keys are stored contiguously. */ -static __pure_function __always_inline void * +__nothrow_pure_function static __always_inline void * page_leaf2key(const MDBX_page *mp, unsigned i, size_t keysize) { assert((mp->mp_flags & (P_BRANCH | P_LEAF | P_LEAF2 | P_OVERFLOW | P_META)) == (P_LEAF | P_LEAF2)); @@ -1362,7 +1364,7 @@ static __inline void lcklist_unlock(void) { #endif } -static uint64_t rrxmrrxmsx_0(uint64_t v) { +__nothrow_const_function static uint64_t rrxmrrxmsx_0(uint64_t v) { /* Pelle Evensen's mixer, https://bit.ly/2HOfynt */ v ^= (v << 39 | v >> 25) ^ (v << 14 | v >> 50); v *= UINT64_C(0xA24BAED4963EE407); diff --git a/src/defs.h b/src/defs.h index d062b7c1..90428add 100644 --- a/src/defs.h +++ b/src/defs.h @@ -43,22 +43,6 @@ # endif #endif /* __GLIBC_PREREQ */ -#ifndef __has_attribute -# define __has_attribute(x) (0) -#endif - -#ifndef __has_feature -# define __has_feature(x) (0) -#endif - -#ifndef __has_extension -# define __has_extension(x) (0) -#endif - -#ifndef __has_builtin -# define __has_builtin(x) (0) -#endif - #ifndef __has_warning # define __has_warning(x) (0) #endif @@ -67,10 +51,6 @@ # define __has_include(x) (0) #endif -#ifndef __has_cpp_attribute -# define __has_cpp_attribute(x) (0) -#endif - #if __has_feature(thread_sanitizer) # define __SANITIZE_THREAD__ 1 #endif @@ -196,40 +176,6 @@ # endif #endif /* __nothrow */ -#ifndef __pure_function - /* Many functions have no effects except the return value and their - * return value depends only on the parameters and/or global variables. - * Such a function can be subject to common subexpression elimination - * and loop optimization just as an arithmetic operator would be. - * These functions should be declared with the attribute pure. */ -# if (defined(__GNUC__) || __has_attribute(__pure__)) && \ - (!defined(__clang__) /* https://bugs.llvm.org/show_bug.cgi?id=43275 */ \ - || !defined(__cplusplus) || !__has_feature(cxx_exceptions)) -# define __pure_function __attribute__((__pure__)) -# else -# define __pure_function -# endif -#endif /* __pure_function */ - -#ifndef __const_function - /* Many functions do not examine any values except their arguments, - * and have no effects except the return value. Basically this is just - * slightly more strict class than the PURE attribute, since function - * is not allowed to read global memory. - * - * Note that a function that has pointer arguments and examines the - * data pointed to must not be declared const. Likewise, a function - * that calls a non-const function usually must not be const. - * It does not make sense for a const function to return void. */ -# if (defined(__GNUC__) || __has_attribute(__pure__)) && \ - (!defined(__clang__) /* https://bugs.llvm.org/show_bug.cgi?id=43275 */ \ - || !defined(__cplusplus) || !__has_feature(cxx_exceptions)) -# define __const_function __attribute__((__const__)) -# else -# define __const_function -# endif -#endif /* __const_function */ - #ifndef __hidden # if defined(__GNUC__) || __has_attribute(__visibility__) # define __hidden __attribute__((__visibility__("hidden"))) diff --git a/src/internals.h b/src/internals.h index 07a84880..094f6571 100644 --- a/src/internals.h +++ b/src/internals.h @@ -1354,12 +1354,14 @@ typedef struct MDBX_node { /* Do not spill pages to disk if txn is getting full, may fail instead */ #define MDBX_NOSPILL 0x8000 -static __maybe_unused __inline pgno_t pgno_add(pgno_t base, pgno_t augend) { +__nothrow_const_function static __maybe_unused __inline pgno_t +pgno_add(pgno_t base, pgno_t augend) { assert(base <= MAX_PAGENO); return (augend < MAX_PAGENO - base) ? base + augend : MAX_PAGENO; } -static __maybe_unused __inline pgno_t pgno_sub(pgno_t base, pgno_t subtrahend) { +__nothrow_const_function static __maybe_unused __inline pgno_t +pgno_sub(pgno_t base, pgno_t subtrahend) { assert(base >= MIN_PAGENO); return (subtrahend < base - MIN_PAGENO) ? base - subtrahend : MIN_PAGENO; } @@ -1373,18 +1375,18 @@ static __maybe_unused __inline void mdbx_jitter4testing(bool tiny) { #endif } -static __pure_function __always_inline __maybe_unused bool +__nothrow_const_function static __always_inline __maybe_unused bool is_powerof2(size_t x) { return (x & (x - 1)) == 0; } -static __pure_function __always_inline __maybe_unused size_t +__nothrow_const_function static __always_inline __maybe_unused size_t floor_powerof2(size_t value, size_t granularity) { assert(is_powerof2(granularity)); return value & ~(granularity - 1); } -static __pure_function __always_inline __maybe_unused size_t +__nothrow_const_function static __always_inline __maybe_unused size_t ceil_powerof2(size_t value, size_t granularity) { return floor_powerof2(value + granularity - 1, granularity); } diff --git a/src/osal.h b/src/osal.h index 8fef7cf7..7e6fa999 100644 --- a/src/osal.h +++ b/src/osal.h @@ -511,7 +511,8 @@ MDBX_INTERNAL_VAR bool /* Get the size of a memory page for the system. * This is the basic size that the platform's memory manager uses, and is * fundamental to the use of memory-mapped files. */ -static __maybe_unused __inline size_t mdbx_syspagesize(void) { +__nothrow_const_function static __maybe_unused __inline size_t +mdbx_syspagesize(void) { #if defined(_WIN32) || defined(_WIN64) SYSTEM_INFO si; GetSystemInfo(&si);