/// \copyright SPDX-License-Identifier: Apache-2.0 /// \author Леонид Юрьев aka Leonid Yuriev \date 2015-2024 #include "internals.h" __cold size_t mdbx_default_pagesize(void) { size_t pagesize = globals.sys_pagesize; ENSURE(nullptr, is_powerof2(pagesize)); pagesize = (pagesize >= MDBX_MIN_PAGESIZE) ? pagesize : MDBX_MIN_PAGESIZE; pagesize = (pagesize <= MDBX_MAX_PAGESIZE) ? pagesize : MDBX_MAX_PAGESIZE; return pagesize; } __cold intptr_t mdbx_limits_dbsize_min(intptr_t pagesize) { if (pagesize < 1) pagesize = (intptr_t)mdbx_default_pagesize(); else if (unlikely(pagesize < (intptr_t)MDBX_MIN_PAGESIZE || pagesize > (intptr_t)MDBX_MAX_PAGESIZE || !is_powerof2((size_t)pagesize))) return -1; return MIN_PAGENO * pagesize; } __cold intptr_t mdbx_limits_dbsize_max(intptr_t pagesize) { if (pagesize < 1) pagesize = (intptr_t)mdbx_default_pagesize(); else if (unlikely(pagesize < (intptr_t)MDBX_MIN_PAGESIZE || pagesize > (intptr_t)MDBX_MAX_PAGESIZE || !is_powerof2((size_t)pagesize))) return -1; STATIC_ASSERT(MAX_MAPSIZE < INTPTR_MAX); const uint64_t limit = (1 + (uint64_t)MAX_PAGENO) * pagesize; return (limit < MAX_MAPSIZE) ? (intptr_t)limit : (intptr_t)MAX_MAPSIZE; } __cold intptr_t mdbx_limits_txnsize_max(intptr_t pagesize) { if (pagesize < 1) pagesize = (intptr_t)mdbx_default_pagesize(); else if (unlikely(pagesize < (intptr_t)MDBX_MIN_PAGESIZE || pagesize > (intptr_t)MDBX_MAX_PAGESIZE || !is_powerof2((size_t)pagesize))) return -1; STATIC_ASSERT(MAX_MAPSIZE < INTPTR_MAX); const uint64_t pgl_limit = pagesize * (uint64_t)(PAGELIST_LIMIT / MDBX_GOLD_RATIO_DBL); const uint64_t map_limit = (uint64_t)(MAX_MAPSIZE / MDBX_GOLD_RATIO_DBL); return (pgl_limit < map_limit) ? (intptr_t)pgl_limit : (intptr_t)map_limit; } __cold intptr_t mdbx_limits_keysize_max(intptr_t pagesize, MDBX_db_flags_t flags) { if (pagesize < 1) pagesize = (intptr_t)mdbx_default_pagesize(); if (unlikely(pagesize < (intptr_t)MDBX_MIN_PAGESIZE || pagesize > (intptr_t)MDBX_MAX_PAGESIZE || !is_powerof2((size_t)pagesize))) return -1; return keysize_max(pagesize, flags); } __cold int mdbx_env_get_maxkeysize_ex(const MDBX_env *env, MDBX_db_flags_t flags) { if (unlikely(!env || env->signature.weak != env_signature)) return -1; return (int)mdbx_limits_keysize_max((intptr_t)env->ps, flags); } __cold int mdbx_env_get_maxkeysize(const MDBX_env *env) { return mdbx_env_get_maxkeysize_ex(env, MDBX_DUPSORT); } __cold intptr_t mdbx_limits_keysize_min(MDBX_db_flags_t flags) { return keysize_min(flags); } __cold intptr_t mdbx_limits_valsize_max(intptr_t pagesize, MDBX_db_flags_t flags) { if (pagesize < 1) pagesize = (intptr_t)mdbx_default_pagesize(); if (unlikely(pagesize < (intptr_t)MDBX_MIN_PAGESIZE || pagesize > (intptr_t)MDBX_MAX_PAGESIZE || !is_powerof2((size_t)pagesize))) return -1; return valsize_max(pagesize, flags); } __cold int mdbx_env_get_maxvalsize_ex(const MDBX_env *env, MDBX_db_flags_t flags) { if (unlikely(!env || env->signature.weak != env_signature)) return -1; return (int)mdbx_limits_valsize_max((intptr_t)env->ps, flags); } __cold intptr_t mdbx_limits_valsize_min(MDBX_db_flags_t flags) { return valsize_min(flags); } __cold intptr_t mdbx_limits_pairsize4page_max(intptr_t pagesize, MDBX_db_flags_t flags) { if (pagesize < 1) pagesize = (intptr_t)mdbx_default_pagesize(); if (unlikely(pagesize < (intptr_t)MDBX_MIN_PAGESIZE || pagesize > (intptr_t)MDBX_MAX_PAGESIZE || !is_powerof2((size_t)pagesize))) return -1; if (flags & (MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_INTEGERDUP | MDBX_REVERSEDUP)) return BRANCH_NODE_MAX(pagesize) - NODESIZE; return LEAF_NODE_MAX(pagesize) - NODESIZE; } __cold int mdbx_env_get_pairsize4page_max(const MDBX_env *env, MDBX_db_flags_t flags) { if (unlikely(!env || env->signature.weak != env_signature)) return -1; return (int)mdbx_limits_pairsize4page_max((intptr_t)env->ps, flags); } __cold intptr_t mdbx_limits_valsize4page_max(intptr_t pagesize, MDBX_db_flags_t flags) { if (pagesize < 1) pagesize = (intptr_t)mdbx_default_pagesize(); if (unlikely(pagesize < (intptr_t)MDBX_MIN_PAGESIZE || pagesize > (intptr_t)MDBX_MAX_PAGESIZE || !is_powerof2((size_t)pagesize))) return -1; if (flags & (MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_INTEGERDUP | MDBX_REVERSEDUP)) return valsize_max(pagesize, flags); return PAGESPACE(pagesize); } __cold int mdbx_env_get_valsize4page_max(const MDBX_env *env, MDBX_db_flags_t flags) { if (unlikely(!env || env->signature.weak != env_signature)) return -1; return (int)mdbx_limits_valsize4page_max((intptr_t)env->ps, flags); } /*----------------------------------------------------------------------------*/ __cold static void stat_add(const tree_t *db, MDBX_stat *const st, const size_t bytes) { st->ms_depth += db->height; st->ms_branch_pages += db->branch_pages; st->ms_leaf_pages += db->leaf_pages; st->ms_overflow_pages += db->large_pages; st->ms_entries += db->items; if (likely(bytes >= offsetof(MDBX_stat, ms_mod_txnid) + sizeof(st->ms_mod_txnid))) st->ms_mod_txnid = (st->ms_mod_txnid > db->mod_txnid) ? st->ms_mod_txnid : db->mod_txnid; } __cold static int stat_acc(const MDBX_txn *txn, MDBX_stat *st, size_t bytes) { int err = check_txn(txn, MDBX_TXN_BLOCKED); if (unlikely(err != MDBX_SUCCESS)) return err; cursor_couple_t cx; err = cursor_init(&cx.outer, (MDBX_txn *)txn, MAIN_DBI); if (unlikely(err != MDBX_SUCCESS)) return err; const MDBX_env *const env = txn->env; st->ms_psize = env->ps; TXN_FOREACH_DBI_FROM( txn, dbi, /* assuming GC is internal and not subject for accounting */ MAIN_DBI) { if ((txn->dbi_state[dbi] & (DBI_VALID | DBI_STALE)) == DBI_VALID) stat_add(txn->dbs + dbi, st, bytes); } if (!(txn->dbs[MAIN_DBI].flags & MDBX_DUPSORT) && txn->dbs[MAIN_DBI].items /* TODO: use `md_subs` field */) { /* scan and account not opened named subDBs */ err = tree_search(&cx.outer, nullptr, Z_FIRST); while (err == MDBX_SUCCESS) { const page_t *mp = cx.outer.pg[cx.outer.top]; for (size_t i = 0; i < page_numkeys(mp); i++) { const node_t *node = page_node(mp, i); if (node_flags(node) != N_SUBDATA) continue; if (unlikely(node_ds(node) != sizeof(tree_t))) { ERROR("%s/%d: %s %zu", "MDBX_CORRUPTED", MDBX_CORRUPTED, "invalid subDb node size", node_ds(node)); return MDBX_CORRUPTED; } /* skip opened and already accounted */ const MDBX_val name = {node_key(node), node_ks(node)}; TXN_FOREACH_DBI_USER(txn, dbi) { if ((txn->dbi_state[dbi] & (DBI_VALID | DBI_STALE)) == DBI_VALID && env->kvs[MAIN_DBI].clc.k.cmp(&name, &env->kvs[dbi].name) == 0) { node = nullptr; break; } } if (node) { tree_t db; memcpy(&db, node_data(node), sizeof(db)); stat_add(&db, st, bytes); } } err = cursor_sibling_right(&cx.outer); } if (unlikely(err != MDBX_NOTFOUND)) return err; } return MDBX_SUCCESS; } __cold int mdbx_env_stat_ex(const MDBX_env *env, const MDBX_txn *txn, MDBX_stat *dest, size_t bytes) { if (unlikely(!dest)) return MDBX_EINVAL; const size_t size_before_modtxnid = offsetof(MDBX_stat, ms_mod_txnid); if (unlikely(bytes != sizeof(MDBX_stat)) && bytes != size_before_modtxnid) return MDBX_EINVAL; if (likely(txn)) { if (env && unlikely(txn->env != env)) return MDBX_EINVAL; return stat_acc(txn, dest, bytes); } int err = check_env(env, true); if (unlikely(err != MDBX_SUCCESS)) return err; if (env->txn && env_txn0_owned(env)) /* inside write-txn */ return stat_acc(env->txn, dest, bytes); MDBX_txn *tmp_txn; err = mdbx_txn_begin((MDBX_env *)env, nullptr, MDBX_TXN_RDONLY, &tmp_txn); if (unlikely(err != MDBX_SUCCESS)) return err; const int rc = stat_acc(tmp_txn, dest, bytes); err = mdbx_txn_abort(tmp_txn); if (unlikely(err != MDBX_SUCCESS)) return err; return rc; } /*----------------------------------------------------------------------------*/ static size_t estimate_rss(size_t database_bytes) { return database_bytes + database_bytes / 64 + (512 + MDBX_WORDBITS * 16) * MEGABYTE; } __cold int mdbx_env_warmup(const MDBX_env *env, const MDBX_txn *txn, MDBX_warmup_flags_t flags, unsigned timeout_seconds_16dot16) { if (unlikely(env == nullptr && txn == nullptr)) return MDBX_EINVAL; if (unlikely(flags > (MDBX_warmup_force | MDBX_warmup_oomsafe | MDBX_warmup_lock | MDBX_warmup_touchlimit | MDBX_warmup_release))) return MDBX_EINVAL; if (txn) { int err = check_txn(txn, MDBX_TXN_BLOCKED - MDBX_TXN_ERROR); if (unlikely(err != MDBX_SUCCESS)) return err; } if (env) { int err = check_env(env, false); if (unlikely(err != MDBX_SUCCESS)) return err; if (txn && unlikely(txn->env != env)) return MDBX_EINVAL; } else { env = txn->env; } const uint64_t timeout_monotime = (timeout_seconds_16dot16 && (flags & MDBX_warmup_force)) ? osal_monotime() + osal_16dot16_to_monotime(timeout_seconds_16dot16) : 0; if (flags & MDBX_warmup_release) munlock_all(env); pgno_t used_pgno; if (txn) { used_pgno = txn->geo.first_unallocated; } else { const troika_t troika = meta_tap(env); used_pgno = meta_recent(env, &troika).ptr_v->geometry.first_unallocated; } const size_t used_range = pgno_align2os_bytes(env, used_pgno); const pgno_t mlock_pgno = bytes2pgno(env, used_range); int rc = MDBX_SUCCESS; if (flags & MDBX_warmup_touchlimit) { const size_t estimated_rss = estimate_rss(used_range); #if defined(_WIN32) || defined(_WIN64) SIZE_T current_ws_lower, current_ws_upper; if (GetProcessWorkingSetSize(GetCurrentProcess(), ¤t_ws_lower, ¤t_ws_upper) && current_ws_lower < estimated_rss) { const SIZE_T ws_lower = estimated_rss; const SIZE_T ws_upper = (MDBX_WORDBITS == 32 && ws_lower > MEGABYTE * 2048) ? ws_lower : ws_lower + MDBX_WORDBITS * MEGABYTE * 32; if (!SetProcessWorkingSetSize(GetCurrentProcess(), ws_lower, ws_upper)) { rc = (int)GetLastError(); WARNING("SetProcessWorkingSetSize(%zu, %zu) error %d", ws_lower, ws_upper, rc); } } #endif /* Windows */ #ifdef RLIMIT_RSS struct rlimit rss; if (getrlimit(RLIMIT_RSS, &rss) == 0 && rss.rlim_cur < estimated_rss) { rss.rlim_cur = estimated_rss; if (rss.rlim_max < estimated_rss) rss.rlim_max = estimated_rss; if (setrlimit(RLIMIT_RSS, &rss)) { rc = errno; WARNING("setrlimit(%s, {%zu, %zu}) error %d", "RLIMIT_RSS", (size_t)rss.rlim_cur, (size_t)rss.rlim_max, rc); } } #endif /* RLIMIT_RSS */ #ifdef RLIMIT_MEMLOCK if (flags & MDBX_warmup_lock) { struct rlimit memlock; if (getrlimit(RLIMIT_MEMLOCK, &memlock) == 0 && memlock.rlim_cur < estimated_rss) { memlock.rlim_cur = estimated_rss; if (memlock.rlim_max < estimated_rss) memlock.rlim_max = estimated_rss; if (setrlimit(RLIMIT_MEMLOCK, &memlock)) { rc = errno; WARNING("setrlimit(%s, {%zu, %zu}) error %d", "RLIMIT_MEMLOCK", (size_t)memlock.rlim_cur, (size_t)memlock.rlim_max, rc); } } } #endif /* RLIMIT_MEMLOCK */ (void)estimated_rss; } #if defined(MLOCK_ONFAULT) && \ ((defined(_GNU_SOURCE) && __GLIBC_PREREQ(2, 27)) || \ (defined(__ANDROID_API__) && __ANDROID_API__ >= 30)) && \ (defined(__linux__) || defined(__gnu_linux__)) if ((flags & MDBX_warmup_lock) != 0 && globals.linux_kernel_version >= 0x04040000 && atomic_load32(&env->mlocked_pgno, mo_AcquireRelease) < mlock_pgno) { if (mlock2(env->dxb_mmap.base, used_range, MLOCK_ONFAULT)) { rc = errno; WARNING("mlock2(%zu, %s) error %d", used_range, "MLOCK_ONFAULT", rc); } else { update_mlcnt(env, mlock_pgno, true); rc = MDBX_SUCCESS; } if (rc != EINVAL) flags -= MDBX_warmup_lock; } #endif /* MLOCK_ONFAULT */ int err = MDBX_ENOSYS; #if MDBX_ENABLE_MADVISE err = dxb_set_readahead(env, used_pgno, true, true); #else #if defined(_WIN32) || defined(_WIN64) if (imports.PrefetchVirtualMemory) { WIN32_MEMORY_RANGE_ENTRY hint; hint.VirtualAddress = env->dxb_mmap.base; hint.NumberOfBytes = used_range; if (imports.PrefetchVirtualMemory(GetCurrentProcess(), 1, &hint, 0)) err = MDBX_SUCCESS; else { err = (int)GetLastError(); ERROR("%s(%zu) error %d", "PrefetchVirtualMemory", used_range, err); } } #endif /* Windows */ #if defined(POSIX_MADV_WILLNEED) err = posix_madvise(env->dxb_mmap.base, used_range, POSIX_MADV_WILLNEED) ? ignore_enosys(errno) : MDBX_SUCCESS; #elif defined(MADV_WILLNEED) err = madvise(env->dxb_mmap.base, used_range, MADV_WILLNEED) ? ignore_enosys(errno) : MDBX_SUCCESS; #endif #if defined(F_RDADVISE) if (err) { fcntl(env->lazy_fd, F_RDAHEAD, true); struct radvisory hint; hint.ra_offset = 0; hint.ra_count = unlikely(used_range > INT_MAX && sizeof(used_range) > sizeof(hint.ra_count)) ? INT_MAX : (int)used_range; err = fcntl(env->lazy_fd, F_RDADVISE, &hint) ? ignore_enosys(errno) : MDBX_SUCCESS; if (err == ENOTTY) err = MDBX_SUCCESS /* Ignore ENOTTY for DB on the ram-disk */; } #endif /* F_RDADVISE */ #endif /* MDBX_ENABLE_MADVISE */ if (err != MDBX_SUCCESS && rc == MDBX_SUCCESS) rc = err; if ((flags & MDBX_warmup_force) != 0 && (rc == MDBX_SUCCESS || rc == MDBX_ENOSYS)) { const volatile uint8_t *ptr = env->dxb_mmap.base; size_t offset = 0, unused = 42; #if !(defined(_WIN32) || defined(_WIN64)) if (flags & MDBX_warmup_oomsafe) { const int null_fd = open("/dev/null", O_WRONLY); if (unlikely(null_fd < 0)) rc = errno; else { struct iovec iov[MDBX_AUXILARY_IOV_MAX]; for (;;) { unsigned i; for (i = 0; i < MDBX_AUXILARY_IOV_MAX && offset < used_range; ++i) { iov[i].iov_base = (void *)(ptr + offset); iov[i].iov_len = 1; offset += globals.sys_pagesize; } if (unlikely(writev(null_fd, iov, i) < 0)) { rc = errno; if (rc == EFAULT) rc = ENOMEM; break; } if (offset >= used_range) { rc = MDBX_SUCCESS; break; } if (timeout_seconds_16dot16 && osal_monotime() > timeout_monotime) { rc = MDBX_RESULT_TRUE; break; } } close(null_fd); } } else #endif /* Windows */ for (;;) { unused += ptr[offset]; offset += globals.sys_pagesize; if (offset >= used_range) { rc = MDBX_SUCCESS; break; } if (timeout_seconds_16dot16 && osal_monotime() > timeout_monotime) { rc = MDBX_RESULT_TRUE; break; } } (void)unused; } if ((flags & MDBX_warmup_lock) != 0 && (rc == MDBX_SUCCESS || rc == MDBX_ENOSYS) && atomic_load32(&env->mlocked_pgno, mo_AcquireRelease) < mlock_pgno) { #if defined(_WIN32) || defined(_WIN64) if (VirtualLock(env->dxb_mmap.base, used_range)) { update_mlcnt(env, mlock_pgno, true); rc = MDBX_SUCCESS; } else { rc = (int)GetLastError(); WARNING("%s(%zu) error %d", "VirtualLock", used_range, rc); } #elif defined(_POSIX_MEMLOCK_RANGE) if (mlock(env->dxb_mmap.base, used_range) == 0) { update_mlcnt(env, mlock_pgno, true); rc = MDBX_SUCCESS; } else { rc = errno; WARNING("%s(%zu) error %d", "mlock", used_range, rc); } #else rc = MDBX_ENOSYS; #endif } return rc; } /*----------------------------------------------------------------------------*/ __cold int mdbx_env_get_fd(const MDBX_env *env, mdbx_filehandle_t *arg) { int rc = check_env(env, true); if (unlikely(rc != MDBX_SUCCESS)) return rc; if (unlikely(!arg)) return MDBX_EINVAL; *arg = env->lazy_fd; return MDBX_SUCCESS; } __cold int mdbx_env_set_flags(MDBX_env *env, MDBX_env_flags_t flags, bool onoff) { int rc = check_env(env, false); if (unlikely(rc != MDBX_SUCCESS)) return rc; if (unlikely(flags & ((env->flags & ENV_ACTIVE) ? ~ENV_CHANGEABLE_FLAGS : ~ENV_USABLE_FLAGS))) return MDBX_EPERM; if (unlikely(env->flags & MDBX_RDONLY)) return MDBX_EACCESS; const bool lock_needed = (env->flags & ENV_ACTIVE) && !env_txn0_owned(env); bool should_unlock = false; if (lock_needed) { rc = lck_txn_lock(env, false); if (unlikely(rc)) return rc; should_unlock = true; } if (onoff) env->flags = combine_durability_flags(env->flags, flags); else env->flags &= ~flags; if (should_unlock) lck_txn_unlock(env); return MDBX_SUCCESS; } __cold int mdbx_env_get_flags(const MDBX_env *env, unsigned *arg) { int rc = check_env(env, false); if (unlikely(rc != MDBX_SUCCESS)) return rc; if (unlikely(!arg)) return MDBX_EINVAL; *arg = env->flags & ENV_USABLE_FLAGS; return MDBX_SUCCESS; } __cold int mdbx_env_set_userctx(MDBX_env *env, void *ctx) { int rc = check_env(env, false); if (unlikely(rc != MDBX_SUCCESS)) return rc; env->userctx = ctx; return MDBX_SUCCESS; } __cold void *mdbx_env_get_userctx(const MDBX_env *env) { return env ? env->userctx : nullptr; } __cold int mdbx_env_set_assert(MDBX_env *env, MDBX_assert_func *func) { int rc = check_env(env, false); if (unlikely(rc != MDBX_SUCCESS)) return rc; #if MDBX_DEBUG env->assert_func = func; return MDBX_SUCCESS; #else (void)func; return MDBX_ENOSYS; #endif } __cold int mdbx_env_set_hsr(MDBX_env *env, MDBX_hsr_func *hsr) { int rc = check_env(env, false); if (unlikely(rc != MDBX_SUCCESS)) return rc; env->hsr_callback = hsr; return MDBX_SUCCESS; } __cold MDBX_hsr_func *mdbx_env_get_hsr(const MDBX_env *env) { return likely(env && env->signature.weak == env_signature) ? env->hsr_callback : nullptr; } #if defined(_WIN32) || defined(_WIN64) __cold int mdbx_env_get_pathW(const MDBX_env *env, const wchar_t **arg) { int rc = check_env(env, true); if (unlikely(rc != MDBX_SUCCESS)) return rc; if (unlikely(!arg)) return MDBX_EINVAL; *arg = env->pathname.specified; return MDBX_SUCCESS; } #endif /* Windows */ __cold int mdbx_env_get_path(const MDBX_env *env, const char **arg) { int rc = check_env(env, true); if (unlikely(rc != MDBX_SUCCESS)) return rc; if (unlikely(!arg)) return MDBX_EINVAL; #if defined(_WIN32) || defined(_WIN64) if (!env->pathname_char) { *arg = nullptr; DWORD flags = /* WC_ERR_INVALID_CHARS */ 0x80; size_t mb_len = WideCharToMultiByte(CP_THREAD_ACP, flags, env->pathname.specified, -1, nullptr, 0, nullptr, nullptr); rc = mb_len ? MDBX_SUCCESS : (int)GetLastError(); if (rc == ERROR_INVALID_FLAGS) { mb_len = WideCharToMultiByte(CP_THREAD_ACP, flags = 0, env->pathname.specified, -1, nullptr, 0, nullptr, nullptr); rc = mb_len ? MDBX_SUCCESS : (int)GetLastError(); } if (unlikely(rc != MDBX_SUCCESS)) return rc; char *const mb_pathname = osal_malloc(mb_len); if (!mb_pathname) return MDBX_ENOMEM; if (mb_len != (size_t)WideCharToMultiByte( CP_THREAD_ACP, flags, env->pathname.specified, -1, mb_pathname, (int)mb_len, nullptr, nullptr)) { rc = (int)GetLastError(); osal_free(mb_pathname); return rc; } if (env->pathname_char || InterlockedCompareExchangePointer((PVOID volatile *)&env->pathname_char, mb_pathname, nullptr)) osal_free(mb_pathname); } *arg = env->pathname_char; #else *arg = env->pathname.specified; #endif /* Windows */ return MDBX_SUCCESS; } /*------------------------------------------------------------------------------ * Legacy API */ #ifndef LIBMDBX_NO_EXPORTS_LEGACY_API LIBMDBX_API int mdbx_txn_begin(MDBX_env *env, MDBX_txn *parent, MDBX_txn_flags_t flags, MDBX_txn **ret) { return __inline_mdbx_txn_begin(env, parent, flags, ret); } LIBMDBX_API int mdbx_txn_commit(MDBX_txn *txn) { return __inline_mdbx_txn_commit(txn); } LIBMDBX_API __cold int mdbx_env_stat(const MDBX_env *env, MDBX_stat *stat, size_t bytes) { return __inline_mdbx_env_stat(env, stat, bytes); } LIBMDBX_API __cold int mdbx_env_info(const MDBX_env *env, MDBX_envinfo *info, size_t bytes) { return __inline_mdbx_env_info(env, info, bytes); } LIBMDBX_API int mdbx_dbi_flags(const MDBX_txn *txn, MDBX_dbi dbi, unsigned *flags) { return __inline_mdbx_dbi_flags(txn, dbi, flags); } LIBMDBX_API __cold int mdbx_env_sync(MDBX_env *env) { return __inline_mdbx_env_sync(env); } LIBMDBX_API __cold int mdbx_env_sync_poll(MDBX_env *env) { return __inline_mdbx_env_sync_poll(env); } LIBMDBX_API __cold int mdbx_env_close(MDBX_env *env) { return __inline_mdbx_env_close(env); } LIBMDBX_API __cold int mdbx_env_set_mapsize(MDBX_env *env, size_t size) { return __inline_mdbx_env_set_mapsize(env, size); } LIBMDBX_API __cold int mdbx_env_set_maxdbs(MDBX_env *env, MDBX_dbi dbs) { return __inline_mdbx_env_set_maxdbs(env, dbs); } LIBMDBX_API __cold int mdbx_env_get_maxdbs(const MDBX_env *env, MDBX_dbi *dbs) { return __inline_mdbx_env_get_maxdbs(env, dbs); } LIBMDBX_API __cold int mdbx_env_set_maxreaders(MDBX_env *env, unsigned readers) { return __inline_mdbx_env_set_maxreaders(env, readers); } LIBMDBX_API __cold int mdbx_env_get_maxreaders(const MDBX_env *env, unsigned *readers) { return __inline_mdbx_env_get_maxreaders(env, readers); } LIBMDBX_API __cold int mdbx_env_set_syncbytes(MDBX_env *env, size_t threshold) { return __inline_mdbx_env_set_syncbytes(env, threshold); } LIBMDBX_API __cold int mdbx_env_get_syncbytes(const MDBX_env *env, size_t *threshold) { return __inline_mdbx_env_get_syncbytes(env, threshold); } LIBMDBX_API __cold int mdbx_env_set_syncperiod(MDBX_env *env, unsigned seconds_16dot16) { return __inline_mdbx_env_set_syncperiod(env, seconds_16dot16); } LIBMDBX_API __cold int mdbx_env_get_syncperiod(const MDBX_env *env, unsigned *seconds_16dot16) { return __inline_mdbx_env_get_syncperiod(env, seconds_16dot16); } LIBMDBX_API __cold uint64_t mdbx_key_from_int64(const int64_t i64) { return __inline_mdbx_key_from_int64(i64); } LIBMDBX_API __cold uint32_t mdbx_key_from_int32(const int32_t i32) { return __inline_mdbx_key_from_int32(i32); } LIBMDBX_API __cold intptr_t mdbx_limits_pgsize_min(void) { return __inline_mdbx_limits_pgsize_min(); } LIBMDBX_API __cold intptr_t mdbx_limits_pgsize_max(void) { return __inline_mdbx_limits_pgsize_max(); } #endif /* LIBMDBX_NO_EXPORTS_LEGACY_API */