mdbxmou 0.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/.github/workflows/ci.yml +32 -0
  2. package/.github/workflows/publish.yml +27 -0
  3. package/.gitmodules +3 -0
  4. package/CMakeLists.txt +53 -0
  5. package/LICENSE +201 -0
  6. package/README.md +639 -0
  7. package/build.js +11 -0
  8. package/deps/libmdbx/.clang-format +3 -0
  9. package/deps/libmdbx/.cmake-format.yaml +3 -0
  10. package/deps/libmdbx/.le.ini +40 -0
  11. package/deps/libmdbx/CMakeLists.txt +1269 -0
  12. package/deps/libmdbx/COPYRIGHT +159 -0
  13. package/deps/libmdbx/ChangeLog.md +2786 -0
  14. package/deps/libmdbx/GNUmakefile +950 -0
  15. package/deps/libmdbx/LICENSE +177 -0
  16. package/deps/libmdbx/Makefile +16 -0
  17. package/deps/libmdbx/NOTICE +39 -0
  18. package/deps/libmdbx/README.md +863 -0
  19. package/deps/libmdbx/TODO.md +43 -0
  20. package/deps/libmdbx/cmake/compiler.cmake +1221 -0
  21. package/deps/libmdbx/cmake/profile.cmake +58 -0
  22. package/deps/libmdbx/cmake/utils.cmake +524 -0
  23. package/deps/libmdbx/conanfile.py +323 -0
  24. package/deps/libmdbx/docs/Doxyfile.in +2734 -0
  25. package/deps/libmdbx/docs/_preface.md +47 -0
  26. package/deps/libmdbx/docs/_restrictions.md +248 -0
  27. package/deps/libmdbx/docs/_starting.md +245 -0
  28. package/deps/libmdbx/docs/_toc.md +34 -0
  29. package/deps/libmdbx/docs/header.html +96 -0
  30. package/deps/libmdbx/example/CMakeLists.txt +6 -0
  31. package/deps/libmdbx/example/README.md +1 -0
  32. package/deps/libmdbx/example/example-mdbx.c +154 -0
  33. package/deps/libmdbx/example/sample-bdb.txt +77 -0
  34. package/deps/libmdbx/mdbx.h +6655 -0
  35. package/deps/libmdbx/mdbx.h++ +6428 -0
  36. package/deps/libmdbx/packages/buildroot/0001-package-libmdbx-new-package-library-database.patch +173 -0
  37. package/deps/libmdbx/src/alloy.c +54 -0
  38. package/deps/libmdbx/src/api-cold.c +543 -0
  39. package/deps/libmdbx/src/api-copy.c +912 -0
  40. package/deps/libmdbx/src/api-cursor.c +754 -0
  41. package/deps/libmdbx/src/api-dbi.c +315 -0
  42. package/deps/libmdbx/src/api-env.c +1434 -0
  43. package/deps/libmdbx/src/api-extra.c +165 -0
  44. package/deps/libmdbx/src/api-key-transform.c +197 -0
  45. package/deps/libmdbx/src/api-misc.c +286 -0
  46. package/deps/libmdbx/src/api-opts.c +575 -0
  47. package/deps/libmdbx/src/api-range-estimate.c +365 -0
  48. package/deps/libmdbx/src/api-txn-data.c +454 -0
  49. package/deps/libmdbx/src/api-txn.c +921 -0
  50. package/deps/libmdbx/src/atomics-ops.h +364 -0
  51. package/deps/libmdbx/src/atomics-types.h +97 -0
  52. package/deps/libmdbx/src/audit.c +109 -0
  53. package/deps/libmdbx/src/bits.md +34 -0
  54. package/deps/libmdbx/src/chk.c +1796 -0
  55. package/deps/libmdbx/src/cogs.c +309 -0
  56. package/deps/libmdbx/src/cogs.h +506 -0
  57. package/deps/libmdbx/src/coherency.c +170 -0
  58. package/deps/libmdbx/src/config.h.in +88 -0
  59. package/deps/libmdbx/src/cursor.c +2396 -0
  60. package/deps/libmdbx/src/cursor.h +391 -0
  61. package/deps/libmdbx/src/dbi.c +717 -0
  62. package/deps/libmdbx/src/dbi.h +142 -0
  63. package/deps/libmdbx/src/debug_begin.h +36 -0
  64. package/deps/libmdbx/src/debug_end.h +15 -0
  65. package/deps/libmdbx/src/dpl.c +486 -0
  66. package/deps/libmdbx/src/dpl.h +134 -0
  67. package/deps/libmdbx/src/dxb.c +1335 -0
  68. package/deps/libmdbx/src/env.c +607 -0
  69. package/deps/libmdbx/src/essentials.h +125 -0
  70. package/deps/libmdbx/src/gc-get.c +1345 -0
  71. package/deps/libmdbx/src/gc-put.c +970 -0
  72. package/deps/libmdbx/src/gc.h +40 -0
  73. package/deps/libmdbx/src/global.c +474 -0
  74. package/deps/libmdbx/src/internals.h +585 -0
  75. package/deps/libmdbx/src/layout-dxb.h +288 -0
  76. package/deps/libmdbx/src/layout-lck.h +289 -0
  77. package/deps/libmdbx/src/lck-posix.c +859 -0
  78. package/deps/libmdbx/src/lck-windows.c +607 -0
  79. package/deps/libmdbx/src/lck.c +174 -0
  80. package/deps/libmdbx/src/lck.h +110 -0
  81. package/deps/libmdbx/src/logging_and_debug.c +250 -0
  82. package/deps/libmdbx/src/logging_and_debug.h +159 -0
  83. package/deps/libmdbx/src/man1/mdbx_chk.1 +106 -0
  84. package/deps/libmdbx/src/man1/mdbx_copy.1 +95 -0
  85. package/deps/libmdbx/src/man1/mdbx_drop.1 +48 -0
  86. package/deps/libmdbx/src/man1/mdbx_dump.1 +101 -0
  87. package/deps/libmdbx/src/man1/mdbx_load.1 +105 -0
  88. package/deps/libmdbx/src/man1/mdbx_stat.1 +86 -0
  89. package/deps/libmdbx/src/mdbx.c++ +1837 -0
  90. package/deps/libmdbx/src/meta.c +656 -0
  91. package/deps/libmdbx/src/meta.h +168 -0
  92. package/deps/libmdbx/src/mvcc-readers.c +414 -0
  93. package/deps/libmdbx/src/node.c +365 -0
  94. package/deps/libmdbx/src/node.h +102 -0
  95. package/deps/libmdbx/src/ntdll.def +1246 -0
  96. package/deps/libmdbx/src/options.h +534 -0
  97. package/deps/libmdbx/src/osal.c +3485 -0
  98. package/deps/libmdbx/src/osal.h +587 -0
  99. package/deps/libmdbx/src/page-get.c +483 -0
  100. package/deps/libmdbx/src/page-iov.c +185 -0
  101. package/deps/libmdbx/src/page-iov.h +34 -0
  102. package/deps/libmdbx/src/page-ops.c +744 -0
  103. package/deps/libmdbx/src/page-ops.h +142 -0
  104. package/deps/libmdbx/src/pnl.c +236 -0
  105. package/deps/libmdbx/src/pnl.h +146 -0
  106. package/deps/libmdbx/src/preface.h +990 -0
  107. package/deps/libmdbx/src/proto.h +105 -0
  108. package/deps/libmdbx/src/refund.c +212 -0
  109. package/deps/libmdbx/src/sort.h +484 -0
  110. package/deps/libmdbx/src/spill.c +431 -0
  111. package/deps/libmdbx/src/spill.h +74 -0
  112. package/deps/libmdbx/src/table.c +107 -0
  113. package/deps/libmdbx/src/tls.c +551 -0
  114. package/deps/libmdbx/src/tls.h +43 -0
  115. package/deps/libmdbx/src/tools/chk.c +673 -0
  116. package/deps/libmdbx/src/tools/copy.c +166 -0
  117. package/deps/libmdbx/src/tools/drop.c +199 -0
  118. package/deps/libmdbx/src/tools/dump.c +515 -0
  119. package/deps/libmdbx/src/tools/load.c +831 -0
  120. package/deps/libmdbx/src/tools/stat.c +516 -0
  121. package/deps/libmdbx/src/tools/wingetopt.c +87 -0
  122. package/deps/libmdbx/src/tools/wingetopt.h +30 -0
  123. package/deps/libmdbx/src/tree-ops.c +1554 -0
  124. package/deps/libmdbx/src/tree-search.c +140 -0
  125. package/deps/libmdbx/src/txl.c +99 -0
  126. package/deps/libmdbx/src/txl.h +26 -0
  127. package/deps/libmdbx/src/txn.c +1083 -0
  128. package/deps/libmdbx/src/unaligned.h +205 -0
  129. package/deps/libmdbx/src/utils.c +32 -0
  130. package/deps/libmdbx/src/utils.h +76 -0
  131. package/deps/libmdbx/src/version.c.in +44 -0
  132. package/deps/libmdbx/src/walk.c +290 -0
  133. package/deps/libmdbx/src/walk.h +20 -0
  134. package/deps/libmdbx/src/windows-import.c +152 -0
  135. package/deps/libmdbx/src/windows-import.h +128 -0
  136. package/deps/libmdbx/test/CMakeLists.txt +317 -0
  137. package/deps/libmdbx/test/append.c++ +237 -0
  138. package/deps/libmdbx/test/base.h++ +92 -0
  139. package/deps/libmdbx/test/battery-tmux.sh +64 -0
  140. package/deps/libmdbx/test/cases.c++ +118 -0
  141. package/deps/libmdbx/test/chrono.c++ +134 -0
  142. package/deps/libmdbx/test/chrono.h++ +85 -0
  143. package/deps/libmdbx/test/config.c++ +643 -0
  144. package/deps/libmdbx/test/config.h++ +334 -0
  145. package/deps/libmdbx/test/copy.c++ +62 -0
  146. package/deps/libmdbx/test/dead.c++ +39 -0
  147. package/deps/libmdbx/test/dump-load.sh +40 -0
  148. package/deps/libmdbx/test/extra/crunched_delete.c++ +409 -0
  149. package/deps/libmdbx/test/extra/cursor_closing.c++ +410 -0
  150. package/deps/libmdbx/test/extra/dbi.c++ +229 -0
  151. package/deps/libmdbx/test/extra/doubtless_positioning.c++ +253 -0
  152. package/deps/libmdbx/test/extra/dupfix_addodd.c +94 -0
  153. package/deps/libmdbx/test/extra/dupfix_multiple.c++ +311 -0
  154. package/deps/libmdbx/test/extra/early_close_dbi.c++ +137 -0
  155. package/deps/libmdbx/test/extra/hex_base64_base58.c++ +118 -0
  156. package/deps/libmdbx/test/extra/maindb_ordinal.c++ +61 -0
  157. package/deps/libmdbx/test/extra/open.c++ +96 -0
  158. package/deps/libmdbx/test/extra/pcrf/README.md +2 -0
  159. package/deps/libmdbx/test/extra/pcrf/pcrf_test.c +380 -0
  160. package/deps/libmdbx/test/extra/probe.c++ +10 -0
  161. package/deps/libmdbx/test/extra/txn.c++ +407 -0
  162. package/deps/libmdbx/test/extra/upsert_alldups.c +193 -0
  163. package/deps/libmdbx/test/fork.c++ +263 -0
  164. package/deps/libmdbx/test/hill.c++ +447 -0
  165. package/deps/libmdbx/test/jitter.c++ +197 -0
  166. package/deps/libmdbx/test/keygen.c++ +393 -0
  167. package/deps/libmdbx/test/keygen.h++ +130 -0
  168. package/deps/libmdbx/test/log.c++ +358 -0
  169. package/deps/libmdbx/test/log.h++ +91 -0
  170. package/deps/libmdbx/test/main.c++ +706 -0
  171. package/deps/libmdbx/test/nested.c++ +318 -0
  172. package/deps/libmdbx/test/osal-unix.c++ +647 -0
  173. package/deps/libmdbx/test/osal-windows.c++ +440 -0
  174. package/deps/libmdbx/test/osal.h++ +41 -0
  175. package/deps/libmdbx/test/stochastic.sh +690 -0
  176. package/deps/libmdbx/test/stub/LICENSE +24 -0
  177. package/deps/libmdbx/test/stub/README.md +8 -0
  178. package/deps/libmdbx/test/stub/pthread_barrier.c +104 -0
  179. package/deps/libmdbx/test/stub/pthread_barrier.h +77 -0
  180. package/deps/libmdbx/test/test.c++ +1551 -0
  181. package/deps/libmdbx/test/test.h++ +298 -0
  182. package/deps/libmdbx/test/tmux.conf +3 -0
  183. package/deps/libmdbx/test/try.c++ +30 -0
  184. package/deps/libmdbx/test/ttl.c++ +240 -0
  185. package/deps/libmdbx/test/utils.c++ +203 -0
  186. package/deps/libmdbx/test/utils.h++ +326 -0
  187. package/deps/libmdbx/test/valgrind_suppress.txt +536 -0
  188. package/lib/mdbx_evn_async.js +211 -0
  189. package/lib/mdbx_worker.js +195 -0
  190. package/lib/nativemou.js +6 -0
  191. package/package.json +38 -0
  192. package/src/async/envmou_close.cpp +34 -0
  193. package/src/async/envmou_close.hpp +32 -0
  194. package/src/async/envmou_copy_to.cpp +29 -0
  195. package/src/async/envmou_copy_to.hpp +38 -0
  196. package/src/async/envmou_keys.cpp +201 -0
  197. package/src/async/envmou_keys.hpp +50 -0
  198. package/src/async/envmou_open.cpp +38 -0
  199. package/src/async/envmou_open.hpp +33 -0
  200. package/src/async/envmou_query.cpp +167 -0
  201. package/src/async/envmou_query.hpp +53 -0
  202. package/src/dbimou.cpp +522 -0
  203. package/src/dbimou.hpp +82 -0
  204. package/src/env_arg0.hpp +24 -0
  205. package/src/envmou.cpp +445 -0
  206. package/src/envmou.hpp +116 -0
  207. package/src/modulemou.cpp +113 -0
  208. package/src/querymou.cpp +177 -0
  209. package/src/querymou.hpp +93 -0
  210. package/src/txnmou.cpp +254 -0
  211. package/src/txnmou.hpp +122 -0
  212. package/src/typemou.hpp +239 -0
  213. package/src/valuemou.hpp +194 -0
  214. package/test/async.js +67 -0
  215. package/test/e3.js +38 -0
  216. package/test/e4.js +89 -0
  217. package/test/e5.js +162 -0
  218. package/test/test-batch-ops.js +243 -0
  219. package/test/test-cursor-mode.js +84 -0
  220. package/test/test-multi-mode.js +87 -0
@@ -0,0 +1,165 @@
1
+ /// \copyright SPDX-License-Identifier: Apache-2.0
2
+ /// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
3
+
4
+ #include "internals.h"
5
+
6
+ /*------------------------------------------------------------------------------
7
+ * Readers API */
8
+
9
+ __cold int mdbx_reader_list(const MDBX_env *env, MDBX_reader_list_func *func, void *ctx) {
10
+ int rc = check_env(env, true);
11
+ if (unlikely(rc != MDBX_SUCCESS))
12
+ return LOG_IFERR(rc);
13
+
14
+ if (unlikely(!func))
15
+ return LOG_IFERR(MDBX_EINVAL);
16
+
17
+ rc = MDBX_RESULT_TRUE;
18
+ int serial = 0;
19
+ lck_t *const lck = env->lck_mmap.lck;
20
+ if (likely(lck)) {
21
+ const size_t snap_nreaders = atomic_load32(&lck->rdt_length, mo_AcquireRelease);
22
+ for (size_t i = 0; i < snap_nreaders; i++) {
23
+ const reader_slot_t *r = lck->rdt + i;
24
+ retry_reader:;
25
+ const uint32_t pid = atomic_load32(&r->pid, mo_AcquireRelease);
26
+ if (!pid)
27
+ continue;
28
+ txnid_t txnid = safe64_read(&r->txnid);
29
+ const uint64_t tid = atomic_load64(&r->tid, mo_Relaxed);
30
+ const pgno_t pages_used = atomic_load32(&r->snapshot_pages_used, mo_Relaxed);
31
+ const uint64_t reader_pages_retired = atomic_load64(&r->snapshot_pages_retired, mo_Relaxed);
32
+ if (unlikely(txnid != safe64_read(&r->txnid) || pid != atomic_load32(&r->pid, mo_AcquireRelease) ||
33
+ tid != atomic_load64(&r->tid, mo_Relaxed) ||
34
+ pages_used != atomic_load32(&r->snapshot_pages_used, mo_Relaxed) ||
35
+ reader_pages_retired != atomic_load64(&r->snapshot_pages_retired, mo_Relaxed)))
36
+ goto retry_reader;
37
+
38
+ eASSERT(env, txnid > 0);
39
+ if (txnid >= SAFE64_INVALID_THRESHOLD)
40
+ txnid = 0;
41
+
42
+ size_t bytes_used = 0;
43
+ size_t bytes_retained = 0;
44
+ uint64_t lag = 0;
45
+ if (txnid) {
46
+ troika_t troika = meta_tap(env);
47
+ retry_header:;
48
+ const meta_ptr_t head = meta_recent(env, &troika);
49
+ const uint64_t head_pages_retired = unaligned_peek_u64_volatile(4, head.ptr_v->pages_retired);
50
+ if (unlikely(meta_should_retry(env, &troika) ||
51
+ head_pages_retired != unaligned_peek_u64_volatile(4, head.ptr_v->pages_retired)))
52
+ goto retry_header;
53
+
54
+ lag = (head.txnid - txnid) / xMDBX_TXNID_STEP;
55
+ bytes_used = pgno2bytes(env, pages_used);
56
+ bytes_retained = (head_pages_retired > reader_pages_retired)
57
+ ? pgno2bytes(env, (pgno_t)(head_pages_retired - reader_pages_retired))
58
+ : 0;
59
+ }
60
+ rc = func(ctx, ++serial, (unsigned)i, pid, (mdbx_tid_t)((intptr_t)tid), txnid, lag, bytes_used, bytes_retained);
61
+ if (unlikely(rc != MDBX_SUCCESS))
62
+ break;
63
+ }
64
+ }
65
+
66
+ return LOG_IFERR(rc);
67
+ }
68
+
69
+ __cold int mdbx_reader_check(MDBX_env *env, int *dead) {
70
+ if (dead)
71
+ *dead = 0;
72
+ return LOG_IFERR(mvcc_cleanup_dead(env, false, dead));
73
+ }
74
+
75
+ __cold int mdbx_thread_register(const MDBX_env *env) {
76
+ int rc = check_env(env, true);
77
+ if (unlikely(rc != MDBX_SUCCESS))
78
+ return LOG_IFERR(rc);
79
+
80
+ if (unlikely(!env->lck_mmap.lck))
81
+ return LOG_IFERR((env->flags & MDBX_EXCLUSIVE) ? MDBX_EINVAL : MDBX_EPERM);
82
+
83
+ if (unlikely((env->flags & ENV_TXKEY) == 0)) {
84
+ eASSERT(env, env->flags & MDBX_NOSTICKYTHREADS);
85
+ return LOG_IFERR(MDBX_EINVAL) /* MDBX_NOSTICKYTHREADS mode */;
86
+ }
87
+
88
+ eASSERT(env, (env->flags & (MDBX_NOSTICKYTHREADS | ENV_TXKEY)) == ENV_TXKEY);
89
+ reader_slot_t *r = thread_rthc_get(env->me_txkey);
90
+ if (unlikely(r != nullptr)) {
91
+ eASSERT(env, r->pid.weak == env->pid);
92
+ eASSERT(env, r->tid.weak == osal_thread_self());
93
+ if (unlikely(r->pid.weak != env->pid))
94
+ return LOG_IFERR(MDBX_BAD_RSLOT);
95
+ return MDBX_RESULT_TRUE /* already registered */;
96
+ }
97
+
98
+ return LOG_IFERR(mvcc_bind_slot((MDBX_env *)env).err);
99
+ }
100
+
101
+ __cold int mdbx_thread_unregister(const MDBX_env *env) {
102
+ int rc = check_env(env, true);
103
+ if (unlikely(rc != MDBX_SUCCESS))
104
+ return LOG_IFERR(rc);
105
+
106
+ if (unlikely(!env->lck_mmap.lck))
107
+ return MDBX_RESULT_TRUE;
108
+
109
+ if (unlikely((env->flags & ENV_TXKEY) == 0)) {
110
+ eASSERT(env, env->flags & MDBX_NOSTICKYTHREADS);
111
+ return MDBX_RESULT_TRUE /* MDBX_NOSTICKYTHREADS mode */;
112
+ }
113
+
114
+ eASSERT(env, (env->flags & (MDBX_NOSTICKYTHREADS | ENV_TXKEY)) == ENV_TXKEY);
115
+ reader_slot_t *r = thread_rthc_get(env->me_txkey);
116
+ if (unlikely(r == nullptr))
117
+ return MDBX_RESULT_TRUE /* not registered */;
118
+
119
+ eASSERT(env, r->pid.weak == env->pid);
120
+ if (unlikely(r->pid.weak != env->pid || r->tid.weak != osal_thread_self()))
121
+ return LOG_IFERR(MDBX_BAD_RSLOT);
122
+
123
+ eASSERT(env, r->txnid.weak >= SAFE64_INVALID_THRESHOLD);
124
+ if (unlikely(r->txnid.weak < SAFE64_INVALID_THRESHOLD))
125
+ return LOG_IFERR(MDBX_BUSY) /* transaction is still active */;
126
+
127
+ atomic_store32(&r->pid, 0, mo_Relaxed);
128
+ atomic_store32(&env->lck->rdt_refresh_flag, true, mo_AcquireRelease);
129
+ thread_rthc_set(env->me_txkey, nullptr);
130
+ return MDBX_SUCCESS;
131
+ }
132
+
133
+ /*------------------------------------------------------------------------------
134
+ * Locking API */
135
+
136
+ int mdbx_txn_lock(MDBX_env *env, bool dont_wait) {
137
+ int rc = check_env(env, true);
138
+ if (unlikely(rc != MDBX_SUCCESS))
139
+ return LOG_IFERR(rc);
140
+
141
+ if (unlikely(env->flags & MDBX_RDONLY))
142
+ return LOG_IFERR(MDBX_EACCESS);
143
+ if (dont_wait && unlikely(env->basal_txn->owner || (env->basal_txn->flags & MDBX_TXN_FINISHED) == 0))
144
+ return LOG_IFERR(MDBX_BUSY);
145
+
146
+ return LOG_IFERR(lck_txn_lock(env, dont_wait));
147
+ }
148
+
149
+ int mdbx_txn_unlock(MDBX_env *env) {
150
+ int rc = check_env(env, true);
151
+ if (unlikely(rc != MDBX_SUCCESS))
152
+ return LOG_IFERR(rc);
153
+
154
+ if (unlikely(env->flags & MDBX_RDONLY))
155
+ return LOG_IFERR(MDBX_EACCESS);
156
+ #if MDBX_TXN_CHECKOWNER
157
+ if (unlikely(env->basal_txn->owner != osal_thread_self()))
158
+ return LOG_IFERR(MDBX_THREAD_MISMATCH);
159
+ #endif /* MDBX_TXN_CHECKOWNER */
160
+ if (unlikely((env->basal_txn->flags & MDBX_TXN_FINISHED) == 0))
161
+ return LOG_IFERR(MDBX_BUSY);
162
+
163
+ lck_txn_unlock(env);
164
+ return MDBX_SUCCESS;
165
+ }
@@ -0,0 +1,197 @@
1
+ /// \copyright SPDX-License-Identifier: Apache-2.0
2
+ /// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
3
+
4
+ #include "internals.h"
5
+
6
+ static inline double key2double(const int64_t key) {
7
+ union {
8
+ uint64_t u;
9
+ double f;
10
+ } casting;
11
+
12
+ casting.u = (key < 0) ? key + UINT64_C(0x8000000000000000) : UINT64_C(0xffffFFFFffffFFFF) - key;
13
+ return casting.f;
14
+ }
15
+
16
+ static inline uint64_t double2key(const double *const ptr) {
17
+ STATIC_ASSERT(sizeof(double) == sizeof(int64_t));
18
+ const int64_t i = *(const int64_t *)ptr;
19
+ const uint64_t u = (i < 0) ? UINT64_C(0xffffFFFFffffFFFF) - i : i + UINT64_C(0x8000000000000000);
20
+ if (ASSERT_ENABLED()) {
21
+ const double f = key2double(u);
22
+ assert(memcmp(&f, ptr, sizeof(double)) == 0);
23
+ }
24
+ return u;
25
+ }
26
+
27
+ static inline float key2float(const int32_t key) {
28
+ union {
29
+ uint32_t u;
30
+ float f;
31
+ } casting;
32
+
33
+ casting.u = (key < 0) ? key + UINT32_C(0x80000000) : UINT32_C(0xffffFFFF) - key;
34
+ return casting.f;
35
+ }
36
+
37
+ static inline uint32_t float2key(const float *const ptr) {
38
+ STATIC_ASSERT(sizeof(float) == sizeof(int32_t));
39
+ const int32_t i = *(const int32_t *)ptr;
40
+ const uint32_t u = (i < 0) ? UINT32_C(0xffffFFFF) - i : i + UINT32_C(0x80000000);
41
+ if (ASSERT_ENABLED()) {
42
+ const float f = key2float(u);
43
+ assert(memcmp(&f, ptr, sizeof(float)) == 0);
44
+ }
45
+ return u;
46
+ }
47
+
48
+ uint64_t mdbx_key_from_double(const double ieee754_64bit) { return double2key(&ieee754_64bit); }
49
+
50
+ uint64_t mdbx_key_from_ptrdouble(const double *const ieee754_64bit) { return double2key(ieee754_64bit); }
51
+
52
+ uint32_t mdbx_key_from_float(const float ieee754_32bit) { return float2key(&ieee754_32bit); }
53
+
54
+ uint32_t mdbx_key_from_ptrfloat(const float *const ieee754_32bit) { return float2key(ieee754_32bit); }
55
+
56
+ #define IEEE754_DOUBLE_MANTISSA_SIZE 52
57
+ #define IEEE754_DOUBLE_EXPONENTA_BIAS 0x3FF
58
+ #define IEEE754_DOUBLE_EXPONENTA_MAX 0x7FF
59
+ #define IEEE754_DOUBLE_IMPLICIT_LEAD UINT64_C(0x0010000000000000)
60
+ #define IEEE754_DOUBLE_MANTISSA_MASK UINT64_C(0x000FFFFFFFFFFFFF)
61
+ #define IEEE754_DOUBLE_MANTISSA_AMAX UINT64_C(0x001FFFFFFFFFFFFF)
62
+
63
+ static inline int clz64(uint64_t value) {
64
+ #if __GNUC_PREREQ(4, 1) || __has_builtin(__builtin_clzl)
65
+ if (sizeof(value) == sizeof(int))
66
+ return __builtin_clz(value);
67
+ if (sizeof(value) == sizeof(long))
68
+ return __builtin_clzl(value);
69
+ #if (defined(__SIZEOF_LONG_LONG__) && __SIZEOF_LONG_LONG__ == 8) || __has_builtin(__builtin_clzll)
70
+ return __builtin_clzll(value);
71
+ #endif /* have(long long) && long long == uint64_t */
72
+ #endif /* GNU C */
73
+
74
+ #if defined(_MSC_VER)
75
+ unsigned long index;
76
+ #if defined(_M_AMD64) || defined(_M_ARM64) || defined(_M_X64)
77
+ _BitScanReverse64(&index, value);
78
+ return 63 - index;
79
+ #else
80
+ if (value > UINT32_MAX) {
81
+ _BitScanReverse(&index, (uint32_t)(value >> 32));
82
+ return 31 - index;
83
+ }
84
+ _BitScanReverse(&index, (uint32_t)value);
85
+ return 63 - index;
86
+ #endif
87
+ #endif /* MSVC */
88
+
89
+ value |= value >> 1;
90
+ value |= value >> 2;
91
+ value |= value >> 4;
92
+ value |= value >> 8;
93
+ value |= value >> 16;
94
+ value |= value >> 32;
95
+ static const uint8_t debruijn_clz64[64] = {63, 16, 62, 7, 15, 36, 61, 3, 6, 14, 22, 26, 35, 47, 60, 2,
96
+ 9, 5, 28, 11, 13, 21, 42, 19, 25, 31, 34, 40, 46, 52, 59, 1,
97
+ 17, 8, 37, 4, 23, 27, 48, 10, 29, 12, 43, 20, 32, 41, 53, 18,
98
+ 38, 24, 49, 30, 44, 33, 54, 39, 50, 45, 55, 51, 56, 57, 58, 0};
99
+ return debruijn_clz64[value * UINT64_C(0x03F79D71B4CB0A89) >> 58];
100
+ }
101
+
102
+ static inline uint64_t round_mantissa(const uint64_t u64, int shift) {
103
+ assert(shift < 0 && u64 > 0);
104
+ shift = -shift;
105
+ const unsigned half = 1 << (shift - 1);
106
+ const unsigned lsb = 1 & (unsigned)(u64 >> shift);
107
+ const unsigned tie2even = 1 ^ lsb;
108
+ return (u64 + half - tie2even) >> shift;
109
+ }
110
+
111
+ uint64_t mdbx_key_from_jsonInteger(const int64_t json_integer) {
112
+ const uint64_t bias = UINT64_C(0x8000000000000000);
113
+ if (json_integer > 0) {
114
+ const uint64_t u64 = json_integer;
115
+ int shift = clz64(u64) - (64 - IEEE754_DOUBLE_MANTISSA_SIZE - 1);
116
+ uint64_t mantissa = u64 << shift;
117
+ if (unlikely(shift < 0)) {
118
+ mantissa = round_mantissa(u64, shift);
119
+ if (mantissa > IEEE754_DOUBLE_MANTISSA_AMAX)
120
+ mantissa = round_mantissa(u64, --shift);
121
+ }
122
+
123
+ assert(mantissa >= IEEE754_DOUBLE_IMPLICIT_LEAD && mantissa <= IEEE754_DOUBLE_MANTISSA_AMAX);
124
+ const uint64_t exponent = (uint64_t)IEEE754_DOUBLE_EXPONENTA_BIAS + IEEE754_DOUBLE_MANTISSA_SIZE - shift;
125
+ assert(exponent > 0 && exponent <= IEEE754_DOUBLE_EXPONENTA_MAX);
126
+ const uint64_t key = bias + (exponent << IEEE754_DOUBLE_MANTISSA_SIZE) + (mantissa - IEEE754_DOUBLE_IMPLICIT_LEAD);
127
+ #if !defined(_MSC_VER) || defined(_DEBUG) /* Workaround for MSVC error LNK2019: unresolved external \
128
+ symbol __except1 referenced in function __ftol3_except */
129
+ assert(key == mdbx_key_from_double((double)json_integer));
130
+ #endif /* Workaround for MSVC */
131
+ return key;
132
+ }
133
+
134
+ if (json_integer < 0) {
135
+ const uint64_t u64 = -json_integer;
136
+ int shift = clz64(u64) - (64 - IEEE754_DOUBLE_MANTISSA_SIZE - 1);
137
+ uint64_t mantissa = u64 << shift;
138
+ if (unlikely(shift < 0)) {
139
+ mantissa = round_mantissa(u64, shift);
140
+ if (mantissa > IEEE754_DOUBLE_MANTISSA_AMAX)
141
+ mantissa = round_mantissa(u64, --shift);
142
+ }
143
+
144
+ assert(mantissa >= IEEE754_DOUBLE_IMPLICIT_LEAD && mantissa <= IEEE754_DOUBLE_MANTISSA_AMAX);
145
+ const uint64_t exponent = (uint64_t)IEEE754_DOUBLE_EXPONENTA_BIAS + IEEE754_DOUBLE_MANTISSA_SIZE - shift;
146
+ assert(exponent > 0 && exponent <= IEEE754_DOUBLE_EXPONENTA_MAX);
147
+ const uint64_t key =
148
+ bias - 1 - (exponent << IEEE754_DOUBLE_MANTISSA_SIZE) - (mantissa - IEEE754_DOUBLE_IMPLICIT_LEAD);
149
+ #if !defined(_MSC_VER) || defined(_DEBUG) /* Workaround for MSVC error LNK2019: unresolved external \
150
+ symbol __except1 referenced in function __ftol3_except */
151
+ assert(key == mdbx_key_from_double((double)json_integer));
152
+ #endif /* Workaround for MSVC */
153
+ return key;
154
+ }
155
+
156
+ return bias;
157
+ }
158
+
159
+ int64_t mdbx_jsonInteger_from_key(const MDBX_val v) {
160
+ assert(v.iov_len == 8);
161
+ const uint64_t key = unaligned_peek_u64(2, v.iov_base);
162
+ const uint64_t bias = UINT64_C(0x8000000000000000);
163
+ const uint64_t covalent = (key > bias) ? key - bias : bias - key - 1;
164
+ const int shift = IEEE754_DOUBLE_EXPONENTA_BIAS + 63 -
165
+ (IEEE754_DOUBLE_EXPONENTA_MAX & (int)(covalent >> IEEE754_DOUBLE_MANTISSA_SIZE));
166
+ if (unlikely(shift < 1))
167
+ return (key < bias) ? INT64_MIN : INT64_MAX;
168
+ if (unlikely(shift > 63))
169
+ return 0;
170
+
171
+ const uint64_t unscaled = ((covalent & IEEE754_DOUBLE_MANTISSA_MASK) << (63 - IEEE754_DOUBLE_MANTISSA_SIZE)) + bias;
172
+ const int64_t absolute = unscaled >> shift;
173
+ const int64_t value = (key < bias) ? -absolute : absolute;
174
+ assert(key == mdbx_key_from_jsonInteger(value) ||
175
+ (mdbx_key_from_jsonInteger(value - 1) < key && key < mdbx_key_from_jsonInteger(value + 1)));
176
+ return value;
177
+ }
178
+
179
+ double mdbx_double_from_key(const MDBX_val v) {
180
+ assert(v.iov_len == 8);
181
+ return key2double(unaligned_peek_u64(2, v.iov_base));
182
+ }
183
+
184
+ float mdbx_float_from_key(const MDBX_val v) {
185
+ assert(v.iov_len == 4);
186
+ return key2float(unaligned_peek_u32(2, v.iov_base));
187
+ }
188
+
189
+ int32_t mdbx_int32_from_key(const MDBX_val v) {
190
+ assert(v.iov_len == 4);
191
+ return (int32_t)(unaligned_peek_u32(2, v.iov_base) - UINT32_C(0x80000000));
192
+ }
193
+
194
+ int64_t mdbx_int64_from_key(const MDBX_val v) {
195
+ assert(v.iov_len == 8);
196
+ return (int64_t)(unaligned_peek_u64(2, v.iov_base) - UINT64_C(0x8000000000000000));
197
+ }
@@ -0,0 +1,286 @@
1
+ /// \copyright SPDX-License-Identifier: Apache-2.0
2
+ /// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
3
+
4
+ #include "internals.h"
5
+
6
+ __cold int mdbx_is_readahead_reasonable(size_t volume, intptr_t redundancy) {
7
+ if (volume <= 1024 * 1024 * 4ul)
8
+ return MDBX_RESULT_TRUE;
9
+
10
+ intptr_t pagesize, total_ram_pages;
11
+ int err = mdbx_get_sysraminfo(&pagesize, &total_ram_pages, nullptr);
12
+ if (unlikely(err != MDBX_SUCCESS))
13
+ return LOG_IFERR(err);
14
+
15
+ const int log2page = log2n_powerof2(pagesize);
16
+ const intptr_t volume_pages = (volume + pagesize - 1) >> log2page;
17
+ const intptr_t redundancy_pages = (redundancy < 0) ? -(intptr_t)((-redundancy + pagesize - 1) >> log2page)
18
+ : (intptr_t)(redundancy + pagesize - 1) >> log2page;
19
+ if (volume_pages >= total_ram_pages || volume_pages + redundancy_pages >= total_ram_pages)
20
+ return MDBX_RESULT_FALSE;
21
+
22
+ intptr_t avail_ram_pages;
23
+ err = mdbx_get_sysraminfo(nullptr, nullptr, &avail_ram_pages);
24
+ if (unlikely(err != MDBX_SUCCESS))
25
+ return LOG_IFERR(err);
26
+
27
+ return (volume_pages + redundancy_pages >= avail_ram_pages) ? MDBX_RESULT_FALSE : MDBX_RESULT_TRUE;
28
+ }
29
+
30
+ int mdbx_dbi_sequence(MDBX_txn *txn, MDBX_dbi dbi, uint64_t *result, uint64_t increment) {
31
+ int rc = check_txn(txn, MDBX_TXN_BLOCKED);
32
+ if (unlikely(rc != MDBX_SUCCESS))
33
+ return LOG_IFERR(rc);
34
+
35
+ rc = dbi_check(txn, dbi);
36
+ if (unlikely(rc != MDBX_SUCCESS))
37
+ return LOG_IFERR(rc);
38
+
39
+ if (unlikely(txn->dbi_state[dbi] & DBI_STALE)) {
40
+ rc = tbl_fetch(txn, dbi);
41
+ if (unlikely(rc != MDBX_SUCCESS))
42
+ return LOG_IFERR(rc);
43
+ }
44
+
45
+ tree_t *dbs = &txn->dbs[dbi];
46
+ if (likely(result))
47
+ *result = dbs->sequence;
48
+
49
+ if (likely(increment > 0)) {
50
+ if (unlikely(dbi == FREE_DBI || (txn->flags & MDBX_TXN_RDONLY) != 0))
51
+ return MDBX_EACCESS;
52
+
53
+ uint64_t new = dbs->sequence + increment;
54
+ if (unlikely(new < increment))
55
+ return MDBX_RESULT_TRUE;
56
+
57
+ tASSERT(txn, new > dbs->sequence);
58
+ if ((txn->dbi_state[dbi] & DBI_DIRTY) == 0) {
59
+ txn->flags |= MDBX_TXN_DIRTY;
60
+ txn->dbi_state[dbi] |= DBI_DIRTY;
61
+ if (unlikely(dbi == MAIN_DBI) && txn->dbs[MAIN_DBI].root != P_INVALID) {
62
+ /* LY: Временная подпорка для coherency_check(), которую в перспективе
63
+ * следует заменить вместе с переделкой установки mod_txnid.
64
+ *
65
+ * Суть проблемы:
66
+ * - coherency_check() в качестве одного из критериев "когерентности"
67
+ * проверяет условие meta.maindb.mod_txnid == maindb.root->txnid;
68
+ * - при обновлении maindb.sequence высталяется DBI_DIRTY, что приведет
69
+ * к обновлению meta.maindb.mod_txnid = current_txnid;
70
+ * - однако, если в само дерево maindb обновление не вносились и оно
71
+ * не пустое, то корневая страницы останеться с прежним txnid и из-за
72
+ * этого ложно сработает coherency_check().
73
+ *
74
+ * Временное (текущее) решение: Принудительно обновляем корневую
75
+ * страницу в описанной выше ситуации. Это устраняет проблему, но и
76
+ * не создает рисков регресса.
77
+ *
78
+ * FIXME: Итоговое решение, которое предстоит реализовать:
79
+ * - изменить семантику установки/обновления mod_txnid, привязав его
80
+ * строго к изменению b-tree, но не атрибутов;
81
+ * - обновлять mod_txnid при фиксации вложенных транзакций;
82
+ * - для dbi-хендлов пользовательских table (видимо) можно оставить
83
+ * DBI_DIRTY в качестве признака необходимости обновления записи
84
+ * table в MainDB, при этом взводить DBI_DIRTY вместе с обновлением
85
+ * mod_txnid, в том числе при обновлении sequence.
86
+ * - для MAIN_DBI при обновлении sequence не следует взводить DBI_DIRTY
87
+ * и/или обновлять mod_txnid, а только взводить MDBX_TXN_DIRTY.
88
+ * - альтернативно, можно перераспределить флажки-признаки dbi_state,
89
+ * чтобы различать состояние dirty-tree и dirty-attributes. */
90
+ cursor_couple_t cx;
91
+ rc = cursor_init(&cx.outer, txn, MAIN_DBI);
92
+ if (unlikely(rc != MDBX_SUCCESS))
93
+ return LOG_IFERR(rc);
94
+ rc = tree_search(&cx.outer, nullptr, Z_MODIFY | Z_ROOTONLY);
95
+ if (unlikely(rc != MDBX_SUCCESS))
96
+ return LOG_IFERR(rc);
97
+ }
98
+ }
99
+ dbs->sequence = new;
100
+ }
101
+
102
+ return MDBX_SUCCESS;
103
+ }
104
+
105
+ int mdbx_cmp(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *a, const MDBX_val *b) {
106
+ eASSERT(nullptr, txn->signature == txn_signature);
107
+ tASSERT(txn, (dbi_state(txn, dbi) & DBI_VALID) && !dbi_changed(txn, dbi));
108
+ tASSERT(txn, dbi < txn->env->n_dbi && (txn->env->dbs_flags[dbi] & DB_VALID) != 0);
109
+ return txn->env->kvs[dbi].clc.k.cmp(a, b);
110
+ }
111
+
112
+ int mdbx_dcmp(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *a, const MDBX_val *b) {
113
+ eASSERT(nullptr, txn->signature == txn_signature);
114
+ tASSERT(txn, (dbi_state(txn, dbi) & DBI_VALID) && !dbi_changed(txn, dbi));
115
+ tASSERT(txn, dbi < txn->env->n_dbi && (txn->env->dbs_flags[dbi] & DB_VALID));
116
+ return txn->env->kvs[dbi].clc.v.cmp(a, b);
117
+ }
118
+
119
+ __cold MDBX_cmp_func *mdbx_get_keycmp(MDBX_db_flags_t flags) { return builtin_keycmp(flags); }
120
+
121
+ __cold MDBX_cmp_func *mdbx_get_datacmp(MDBX_db_flags_t flags) { return builtin_datacmp(flags); }
122
+
123
+ /*----------------------------------------------------------------------------*/
124
+
125
+ __cold const char *mdbx_liberr2str(int errnum) {
126
+ /* Table of descriptions for MDBX errors */
127
+ static const char *const tbl[] = {
128
+ "MDBX_KEYEXIST: Key/data pair already exists",
129
+ "MDBX_NOTFOUND: No matching key/data pair found",
130
+ "MDBX_PAGE_NOTFOUND: Requested page not found",
131
+ "MDBX_CORRUPTED: Database is corrupted",
132
+ "MDBX_PANIC: Environment had fatal error",
133
+ "MDBX_VERSION_MISMATCH: DB version mismatch libmdbx",
134
+ "MDBX_INVALID: File is not an MDBX file",
135
+ "MDBX_MAP_FULL: Environment mapsize limit reached",
136
+ "MDBX_DBS_FULL: Too many DBI-handles (maxdbs reached)",
137
+ "MDBX_READERS_FULL: Too many readers (maxreaders reached)",
138
+ nullptr /* MDBX_TLS_FULL (-30789): unused in MDBX */,
139
+ "MDBX_TXN_FULL: Transaction has too many dirty pages,"
140
+ " i.e transaction is too big",
141
+ "MDBX_CURSOR_FULL: Cursor stack limit reachedn - this usually indicates"
142
+ " corruption, i.e branch-pages loop",
143
+ "MDBX_PAGE_FULL: Internal error - Page has no more space",
144
+ "MDBX_UNABLE_EXTEND_MAPSIZE: Database engine was unable to extend"
145
+ " mapping, e.g. since address space is unavailable or busy,"
146
+ " or Operation system not supported such operations",
147
+ "MDBX_INCOMPATIBLE: Environment or database is not compatible"
148
+ " with the requested operation or the specified flags",
149
+ "MDBX_BAD_RSLOT: Invalid reuse of reader locktable slot,"
150
+ " e.g. read-transaction already run for current thread",
151
+ "MDBX_BAD_TXN: Transaction is not valid for requested operation,"
152
+ " e.g. had errored and be must aborted, has a child, or is invalid",
153
+ "MDBX_BAD_VALSIZE: Invalid size or alignment of key or data"
154
+ " for target database, either invalid table name",
155
+ "MDBX_BAD_DBI: The specified DBI-handle is invalid"
156
+ " or changed by another thread/transaction",
157
+ "MDBX_PROBLEM: Unexpected internal error, transaction should be aborted",
158
+ "MDBX_BUSY: Another write transaction is running,"
159
+ " or environment is already used while opening with MDBX_EXCLUSIVE flag",
160
+ };
161
+
162
+ if (errnum >= MDBX_KEYEXIST && errnum <= MDBX_BUSY) {
163
+ int i = errnum - MDBX_KEYEXIST;
164
+ return tbl[i];
165
+ }
166
+
167
+ switch (errnum) {
168
+ case MDBX_SUCCESS:
169
+ return "MDBX_SUCCESS: Successful";
170
+ case MDBX_EMULTIVAL:
171
+ return "MDBX_EMULTIVAL: The specified key has"
172
+ " more than one associated value";
173
+ case MDBX_EBADSIGN:
174
+ return "MDBX_EBADSIGN: Wrong signature of a runtime object(s),"
175
+ " e.g. memory corruption or double-free";
176
+ case MDBX_WANNA_RECOVERY:
177
+ return "MDBX_WANNA_RECOVERY: Database should be recovered,"
178
+ " but this could NOT be done automatically for now"
179
+ " since it opened in read-only mode";
180
+ case MDBX_EKEYMISMATCH:
181
+ return "MDBX_EKEYMISMATCH: The given key value is mismatched to the"
182
+ " current cursor position";
183
+ case MDBX_TOO_LARGE:
184
+ return "MDBX_TOO_LARGE: Database is too large for current system,"
185
+ " e.g. could NOT be mapped into RAM";
186
+ case MDBX_THREAD_MISMATCH:
187
+ return "MDBX_THREAD_MISMATCH: A thread has attempted to use a not"
188
+ " owned object, e.g. a transaction that started by another thread";
189
+ case MDBX_TXN_OVERLAPPING:
190
+ return "MDBX_TXN_OVERLAPPING: Overlapping read and write transactions for"
191
+ " the current thread";
192
+ case MDBX_DUPLICATED_CLK:
193
+ return "MDBX_DUPLICATED_CLK: Alternative/Duplicate LCK-file is exists,"
194
+ " please keep one and remove unused other";
195
+ case MDBX_DANGLING_DBI:
196
+ return "MDBX_DANGLING_DBI: Some cursors and/or other resources should be"
197
+ " closed before table or corresponding DBI-handle could be (re)used";
198
+ case MDBX_OUSTED:
199
+ return "MDBX_OUSTED: The parked read transaction was outed for the sake"
200
+ " of recycling old MVCC snapshots";
201
+ case MDBX_MVCC_RETARDED:
202
+ return "MDBX_MVCC_RETARDED: MVCC snapshot used by parked transaction was bygone";
203
+ default:
204
+ return nullptr;
205
+ }
206
+ }
207
+
208
+ __cold const char *mdbx_strerror_r(int errnum, char *buf, size_t buflen) {
209
+ const char *msg = mdbx_liberr2str(errnum);
210
+ if (!msg && buflen > 0 && buflen < INT_MAX) {
211
+ #if defined(_WIN32) || defined(_WIN64)
212
+ DWORD size = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, errnum,
213
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (DWORD)buflen, nullptr);
214
+ while (size && buf[size - 1] <= ' ')
215
+ --size;
216
+ buf[size] = 0;
217
+ return size ? buf : "FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM) failed";
218
+ #elif defined(_GNU_SOURCE) && defined(__GLIBC__)
219
+ /* GNU-specific */
220
+ if (errnum > 0)
221
+ msg = strerror_r(errnum, buf, buflen);
222
+ #elif (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)
223
+ /* XSI-compliant */
224
+ if (errnum > 0 && strerror_r(errnum, buf, buflen) == 0)
225
+ msg = buf;
226
+ #else
227
+ if (errnum > 0) {
228
+ msg = strerror(errnum);
229
+ if (msg) {
230
+ strncpy(buf, msg, buflen);
231
+ msg = buf;
232
+ }
233
+ }
234
+ #endif
235
+ if (!msg) {
236
+ (void)snprintf(buf, buflen, "error %d", errnum);
237
+ msg = buf;
238
+ }
239
+ buf[buflen - 1] = '\0';
240
+ }
241
+ return msg;
242
+ }
243
+
244
+ __cold const char *mdbx_strerror(int errnum) {
245
+ #if defined(_WIN32) || defined(_WIN64)
246
+ static char buf[1024];
247
+ return mdbx_strerror_r(errnum, buf, sizeof(buf));
248
+ #else
249
+ const char *msg = mdbx_liberr2str(errnum);
250
+ if (!msg) {
251
+ if (errnum > 0)
252
+ msg = strerror(errnum);
253
+ if (!msg) {
254
+ static char buf[32];
255
+ (void)snprintf(buf, sizeof(buf) - 1, "error %d", errnum);
256
+ msg = buf;
257
+ }
258
+ }
259
+ return msg;
260
+ #endif
261
+ }
262
+
263
+ #if defined(_WIN32) || defined(_WIN64) /* Bit of madness for Windows */
264
+ const char *mdbx_strerror_r_ANSI2OEM(int errnum, char *buf, size_t buflen) {
265
+ const char *msg = mdbx_liberr2str(errnum);
266
+ if (!msg && buflen > 0 && buflen < INT_MAX) {
267
+ DWORD size = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, errnum,
268
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (DWORD)buflen, nullptr);
269
+ while (size && buf[size - 1] <= ' ')
270
+ --size;
271
+ buf[size] = 0;
272
+ if (!size)
273
+ msg = "FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM) failed";
274
+ else if (!CharToOemBuffA(buf, buf, size))
275
+ msg = "CharToOemBuffA() failed";
276
+ else
277
+ msg = buf;
278
+ }
279
+ return msg;
280
+ }
281
+
282
+ const char *mdbx_strerror_ANSI2OEM(int errnum) {
283
+ static char buf[1024];
284
+ return mdbx_strerror_r_ANSI2OEM(errnum, buf, sizeof(buf));
285
+ }
286
+ #endif /* Bit of madness for Windows */