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,253 @@
1
+ /// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
2
+ /// \copyright SPDX-License-Identifier: Apache-2.0
3
+
4
+ #include "mdbx.h++"
5
+ #include <array>
6
+ #include <functional>
7
+ #include <iostream>
8
+ #include <random>
9
+
10
+ static ::std::ostream &operator<<(::std::ostream &out, const mdbx::cursor::move_operation op) {
11
+ static const char *const str[] = {"FIRST",
12
+ "FIRST_DUP",
13
+ "GET_BOTH",
14
+ "GET_BOTH_RANGE",
15
+ "GET_CURRENT",
16
+ "GET_MULTIPLE",
17
+ "LAST",
18
+ "LAST_DUP",
19
+ "NEXT",
20
+ "NEXT_DUP",
21
+ "NEXT_MULTIPLE",
22
+ "NEXT_NODUP",
23
+ "PREV",
24
+ "PREV_DUP",
25
+ "PREV_NODUP",
26
+ "SET",
27
+ "SET_KEY",
28
+ "SET_RANGE",
29
+ "PREV_MULTIPLE",
30
+ "SET_LOWERBOUND",
31
+ "SET_UPPERBOUND",
32
+ "TO_KEY_LESSER_THAN",
33
+ "TO_KEY_LESSER_OR_EQUAL",
34
+ "TO_KEY_EQUAL",
35
+ "TO_KEY_GREATER_OR_EQUAL",
36
+ "TO_KEY_GREATER_THAN",
37
+ "TO_EXACT_KEY_VALUE_LESSER_THAN",
38
+ "TO_EXACT_KEY_VALUE_LESSER_OR_EQUAL",
39
+ "TO_EXACT_KEY_VALUE_EQUAL",
40
+ "TO_EXACT_KEY_VALUE_GREATER_OR_EQUAL",
41
+ "TO_EXACT_KEY_VALUE_GREATER_THAN",
42
+ "TO_PAIR_LESSER_THAN",
43
+ "TO_PAIR_LESSER_OR_EQUAL",
44
+ "TO_PAIR_EQUAL",
45
+ "TO_PAIR_GREATER_OR_EQUAL",
46
+ "TO_PAIR_GREATER_THAN"};
47
+ return out << str[op];
48
+ }
49
+
50
+ using buffer = mdbx::default_buffer;
51
+ using buffer_pair = mdbx::buffer_pair<buffer>;
52
+
53
+ std::default_random_engine prng(42);
54
+
55
+ static buffer random(const unsigned &value) {
56
+ switch (prng() % 3) {
57
+ default:
58
+ return buffer::hex(value);
59
+ case 1:
60
+ return buffer::base64(value);
61
+ case 2:
62
+ return buffer::base58(value);
63
+ }
64
+ }
65
+
66
+ static buffer random_key() { return random(prng() % 10007); }
67
+
68
+ static buffer random_value() { return random(prng() % 47); }
69
+
70
+ using predicate = std::function<bool(const mdbx::pair &, const mdbx::pair &)>;
71
+
72
+ static bool probe(mdbx::txn txn, mdbx::map_handle dbi, mdbx::cursor::move_operation op, predicate cmp,
73
+ const buffer_pair &pair) {
74
+ auto seeker = txn.open_cursor(dbi);
75
+ auto scanner = seeker.clone();
76
+
77
+ const bool scan_backward = op == mdbx::cursor::key_lesser_than || op == mdbx::cursor::key_lesser_or_equal ||
78
+ op == mdbx::cursor::multi_exactkey_value_lesser_than ||
79
+ op == mdbx::cursor::multi_exactkey_value_lesser_or_equal ||
80
+ op == mdbx::cursor::pair_lesser_than || op == mdbx::cursor::pair_lesser_or_equal;
81
+
82
+ const bool is_multi = mdbx::is_multi(txn.get_handle_info(dbi).value_mode());
83
+
84
+ auto seek_result = seeker.move(op, pair.key, pair.value, false);
85
+ auto scan_result =
86
+ scanner.fullscan([cmp, &pair](const mdbx::pair &scan) -> bool { return cmp(scan, pair); }, scan_backward);
87
+ if (seek_result.done == scan_result &&
88
+ (!scan_result ||
89
+ seeker.is_same_position(scanner, op < mdbx::cursor::multi_exactkey_value_lesser_than && is_multi)))
90
+ return true;
91
+
92
+ std::cerr << std::endl;
93
+ std::cerr << "bug:";
94
+ std::cerr << std::endl;
95
+ std::cerr << std::string(is_multi ? "multi" : "single") << "-map, op " << op << ", key " << pair.key << ", value "
96
+ << pair.value;
97
+ std::cerr << std::endl;
98
+ std::cerr << "\tscanner: ";
99
+ if (scan_result)
100
+ std::cerr << " done, key " << scanner.current(false).key << ", value " << scanner.current(false).value;
101
+ else
102
+ std::cerr << "not-found";
103
+ std::cerr << std::endl;
104
+ std::cerr << "\t seeker: " << (seek_result.done ? " done" : "not-found") << ", key " << seek_result.key
105
+ << ", value " << seek_result.value;
106
+ std::cerr << std::endl;
107
+ return false;
108
+ }
109
+
110
+ static bool probe(mdbx::txn txn, mdbx::map_handle dbi, mdbx::cursor::move_operation op, predicate cmp) {
111
+ const auto pair = buffer_pair(random_key(), random_value());
112
+ const bool ok = probe(txn, dbi, op, cmp, pair);
113
+ #if MDBX_DEBUG
114
+ if (!ok)
115
+ // повтор для отладки и поиска причин
116
+ probe(txn, dbi, op, cmp, pair);
117
+ #endif /* MDBX_DEBUG */
118
+ return ok;
119
+ }
120
+
121
+ static bool test(mdbx::txn txn, mdbx::map_handle dbi) {
122
+ bool ok = true;
123
+
124
+ ok = probe(txn, dbi, mdbx::cursor::key_lesser_than,
125
+ [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
126
+ return mdbx_cmp(txn, dbi, l.key, r.key) < 0;
127
+ }) &&
128
+ ok;
129
+ ok = probe(txn, dbi, mdbx::cursor::key_lesser_or_equal,
130
+ [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
131
+ return mdbx_cmp(txn, dbi, l.key, r.key) <= 0;
132
+ }) &&
133
+ ok;
134
+ ok = probe(txn, dbi, mdbx::cursor::key_equal,
135
+ [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
136
+ return mdbx_cmp(txn, dbi, l.key, r.key) == 0;
137
+ }) &&
138
+ ok;
139
+ ok = probe(txn, dbi, mdbx::cursor::key_greater_or_equal,
140
+ [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
141
+ return mdbx_cmp(txn, dbi, l.key, r.key) >= 0;
142
+ }) &&
143
+ ok;
144
+ ok = probe(txn, dbi, mdbx::cursor::key_greater_than,
145
+ [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
146
+ return mdbx_cmp(txn, dbi, l.key, r.key) > 0;
147
+ }) &&
148
+ ok;
149
+
150
+ ok = probe(txn, dbi, mdbx::cursor::multi_exactkey_value_lesser_than,
151
+ [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
152
+ return mdbx_cmp(txn, dbi, l.key, r.key) == 0 && mdbx_dcmp(txn, dbi, l.value, r.value) < 0;
153
+ }) &&
154
+ ok;
155
+ ok = probe(txn, dbi, mdbx::cursor::multi_exactkey_value_lesser_or_equal,
156
+ [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
157
+ return mdbx_cmp(txn, dbi, l.key, r.key) == 0 && mdbx_dcmp(txn, dbi, l.value, r.value) <= 0;
158
+ }) &&
159
+ ok;
160
+ ok = probe(txn, dbi, mdbx::cursor::multi_exactkey_value_equal,
161
+ [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
162
+ return mdbx_cmp(txn, dbi, l.key, r.key) == 0 && mdbx_dcmp(txn, dbi, l.value, r.value) == 0;
163
+ }) &&
164
+ ok;
165
+ ok = probe(txn, dbi, mdbx::cursor::multi_exactkey_value_greater_or_equal,
166
+ [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
167
+ return mdbx_cmp(txn, dbi, l.key, r.key) == 0 && mdbx_dcmp(txn, dbi, l.value, r.value) >= 0;
168
+ }) &&
169
+ ok;
170
+ ok = probe(txn, dbi, mdbx::cursor::multi_exactkey_value_greater,
171
+ [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
172
+ return mdbx_cmp(txn, dbi, l.key, r.key) == 0 && mdbx_dcmp(txn, dbi, l.value, r.value) > 0;
173
+ }) &&
174
+ ok;
175
+
176
+ ok = probe(txn, dbi, mdbx::cursor::pair_lesser_than,
177
+ [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
178
+ auto cmp = mdbx_cmp(txn, dbi, l.key, r.key);
179
+ if (cmp == 0)
180
+ cmp = mdbx_dcmp(txn, dbi, l.value, r.value);
181
+ return cmp < 0;
182
+ }) &&
183
+ ok;
184
+ ok = probe(txn, dbi, mdbx::cursor::pair_lesser_or_equal,
185
+ [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
186
+ auto cmp = mdbx_cmp(txn, dbi, l.key, r.key);
187
+ if (cmp == 0)
188
+ cmp = mdbx_dcmp(txn, dbi, l.value, r.value);
189
+ return cmp <= 0;
190
+ }) &&
191
+ ok;
192
+ ok = probe(txn, dbi, mdbx::cursor::pair_equal,
193
+ [](const mdbx::pair &l, const mdbx::pair &r) -> bool { return l == r; }) &&
194
+ ok;
195
+ ok = probe(txn, dbi, mdbx::cursor::pair_greater_or_equal,
196
+ [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
197
+ auto cmp = mdbx_cmp(txn, dbi, l.key, r.key);
198
+ if (cmp == 0)
199
+ cmp = mdbx_dcmp(txn, dbi, l.value, r.value);
200
+ return cmp >= 0;
201
+ }) &&
202
+ ok;
203
+ ok = probe(txn, dbi, mdbx::cursor::pair_greater_than,
204
+ [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
205
+ auto cmp = mdbx_cmp(txn, dbi, l.key, r.key);
206
+ if (cmp == 0)
207
+ cmp = mdbx_dcmp(txn, dbi, l.value, r.value);
208
+ return cmp > 0;
209
+ }) &&
210
+ ok;
211
+ return ok;
212
+ }
213
+
214
+ int doit() {
215
+ mdbx::path db_filename = "test-posi";
216
+ mdbx::env_managed::remove(db_filename);
217
+ mdbx::env_managed env(db_filename, mdbx::env_managed::create_parameters(), mdbx::env::operate_parameters(3));
218
+
219
+ auto txn = env.start_write();
220
+ auto single = txn.create_map("single", mdbx::key_mode::usual, mdbx::value_mode::single);
221
+ auto multi = txn.create_map("multi", mdbx::key_mode::usual, mdbx::value_mode::multi);
222
+ for (size_t i = 0; i < 1000; ++i) {
223
+ auto key = random_key();
224
+ txn.upsert(single, key, random_value());
225
+ for (auto n = prng() % 5 + 1; n > 0; --n)
226
+ txn.upsert(multi, key, random_value());
227
+ }
228
+ txn.commit_embark_read();
229
+
230
+ bool ok = true;
231
+ for (size_t i = 0; ok && i < 3333; ++i) {
232
+ ok = test(txn, single) && ok;
233
+ ok = test(txn, multi) && ok;
234
+ }
235
+
236
+ if (!ok) {
237
+ std::cerr << "Fail\n";
238
+ return EXIT_FAILURE;
239
+ }
240
+ std::cout << "OK\n";
241
+ return EXIT_SUCCESS;
242
+ }
243
+
244
+ int main(int argc, char *argv[]) {
245
+ (void)argc;
246
+ (void)argv;
247
+ try {
248
+ return doit();
249
+ } catch (const std::exception &ex) {
250
+ std::cerr << "Exception: " << ex.what() << "\n";
251
+ return EXIT_FAILURE;
252
+ }
253
+ }
@@ -0,0 +1,94 @@
1
+ /*
2
+ * @Dvirsw (https://t.me/Dvirsw)
3
+ * I think there is a bug with DUPFIXED. The following code fails.
4
+ *
5
+ * https://t.me/libmdbx/5368
6
+ */
7
+
8
+ #include <sys/stat.h>
9
+ #include <sys/time.h>
10
+
11
+ #include "mdbx.h"
12
+ #include <assert.h>
13
+ #include <inttypes.h>
14
+ #include <limits.h>
15
+ #include <stddef.h>
16
+ #include <stdint.h>
17
+ #include <stdio.h>
18
+ #include <stdlib.h>
19
+ #include <string.h>
20
+
21
+ int main() {
22
+ int rc;
23
+ MDBX_env *env = NULL;
24
+ MDBX_dbi dbi = 0;
25
+ MDBX_val key, data;
26
+ MDBX_txn *txn = NULL;
27
+
28
+ const char *db_filename = "./test-dupfix-addodd";
29
+ mdbx_env_delete(db_filename, MDBX_ENV_JUST_DELETE);
30
+
31
+ rc = mdbx_env_create(&env);
32
+ if (rc != MDBX_SUCCESS) {
33
+ fprintf(stderr, "mdbx_env_create: (%d) %s\n", rc, mdbx_strerror(rc));
34
+ exit(EXIT_FAILURE);
35
+ }
36
+
37
+ rc = mdbx_env_set_maxdbs(env, 1);
38
+ if (rc != MDBX_SUCCESS) {
39
+ fprintf(stderr, "mdbx_env_create: (%d) %s\n", rc, mdbx_strerror(rc));
40
+ exit(EXIT_FAILURE);
41
+ }
42
+
43
+ rc = mdbx_env_open(env, db_filename, MDBX_NOSUBDIR | MDBX_LIFORECLAIM, 0664);
44
+ if (rc != MDBX_SUCCESS) {
45
+ fprintf(stderr, "mdbx_env_open: (%d) %s\n", rc, mdbx_strerror(rc));
46
+ exit(EXIT_FAILURE);
47
+ }
48
+
49
+ rc = mdbx_txn_begin(env, NULL, 0, &txn);
50
+ if (rc != MDBX_SUCCESS) {
51
+ fprintf(stderr, "mdbx_txn_begin: (%d) %s\n", rc, mdbx_strerror(rc));
52
+ exit(EXIT_FAILURE);
53
+ }
54
+
55
+ rc = mdbx_dbi_open(txn, "test", MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_CREATE, &dbi);
56
+ if (rc != MDBX_SUCCESS) {
57
+ fprintf(stderr, "mdbx_dbi_open: (%d) %s\n", rc, mdbx_strerror(rc));
58
+ exit(EXIT_FAILURE);
59
+ }
60
+
61
+ char key_bytes[32] = {0};
62
+ key.iov_len = 32;
63
+ key.iov_base = key_bytes;
64
+
65
+ // Another put after this will fail.
66
+ unsigned char idx;
67
+ for (idx = 0; idx < 129; idx++) {
68
+ char data_bytes[15] = {idx};
69
+ data.iov_len = 15;
70
+ data.iov_base = data_bytes;
71
+ rc = mdbx_put(txn, dbi, &key, &data, 0);
72
+ if (rc != MDBX_SUCCESS) {
73
+ fprintf(stderr, "mdbx_put: (%d) %s\n", rc, mdbx_strerror(rc));
74
+ exit(EXIT_FAILURE);
75
+ }
76
+ }
77
+
78
+ // This will fail and exit.
79
+ char data_bytes[15] = {idx};
80
+ data.iov_len = 15;
81
+ data.iov_base = data_bytes;
82
+ rc = mdbx_put(txn, dbi, &key, &data, 0);
83
+ if (rc != MDBX_SUCCESS) {
84
+ fprintf(stderr, "mdbx_put: (%d) %s\n", rc, mdbx_strerror(rc));
85
+ fprintf(stderr, "expected failure\n");
86
+ exit(EXIT_FAILURE);
87
+ }
88
+
89
+ rc = mdbx_txn_commit(txn);
90
+ if (rc) {
91
+ fprintf(stderr, "mdbx_txn_commit: (%d) %s\n", rc, mdbx_strerror(rc));
92
+ exit(EXIT_FAILURE);
93
+ }
94
+ }
@@ -0,0 +1,311 @@
1
+ /// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
2
+ /// \copyright SPDX-License-Identifier: Apache-2.0
3
+
4
+ #include "mdbx.h++"
5
+ #include <chrono>
6
+ #include <iostream>
7
+
8
+ #if defined(ENABLE_MEMCHECK) || defined(MDBX_CI)
9
+ #if MDBX_DEBUG || !defined(NDEBUG)
10
+ #define RELIEF_FACTOR 16
11
+ #else
12
+ #define RELIEF_FACTOR 8
13
+ #endif
14
+ #elif MDBX_DEBUG || !defined(NDEBUG) || defined(__APPLE__) || defined(_WIN32)
15
+ #define RELIEF_FACTOR 4
16
+ #elif UINTPTR_MAX > 0xffffFFFFul || ULONG_MAX > 0xffffFFFFul
17
+ #define RELIEF_FACTOR 2
18
+ #else
19
+ #define RELIEF_FACTOR 1
20
+ #endif
21
+
22
+ using buffer = mdbx::default_buffer;
23
+
24
+ bool case1_ordering(mdbx::env env) {
25
+ auto txn = env.start_write();
26
+ auto map = txn.create_map("case1", mdbx::key_mode::ordinal, mdbx::value_mode::multi_ordinal);
27
+
28
+ txn.insert(map, buffer::key_from_u64(21), buffer::key_from_u64(18));
29
+ txn.insert(map, buffer::key_from_u64(7), buffer::key_from_u64(19));
30
+ txn.insert(map, buffer::key_from_u64(22), buffer::key_from_u64(17));
31
+ txn.insert(map, buffer::key_from_u64(26), buffer::key_from_u64(13));
32
+ txn.insert(map, buffer::key_from_u64(24), buffer::key_from_u64(15));
33
+ txn.insert(map, buffer::key_from_u64(23), buffer::key_from_u64(16));
34
+ txn.insert(map, buffer::key_from_u64(25), buffer::key_from_u64(14));
35
+ txn.insert(map, buffer::key_from_u64(27), buffer::key_from_u64(12));
36
+ txn.commit();
37
+
38
+ txn = env.start_read();
39
+ auto cursor = txn.open_cursor(map);
40
+ if (cursor.to_first().value.as_uint64() != 19 || cursor.to_next().value.as_uint64() != 18 ||
41
+ cursor.to_next().value.as_uint64() != 17 || cursor.to_next().value.as_uint64() != 16 ||
42
+ cursor.to_next().value.as_uint64() != 15 || cursor.to_next().value.as_uint64() != 14 ||
43
+ cursor.to_next().value.as_uint64() != 13 || cursor.to_next().value.as_uint64() != 12 ||
44
+ cursor.to_next(false).done || !cursor.eof())
45
+ return false;
46
+
47
+ txn.abort();
48
+
49
+ const uint64_t array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 42, 17, 99, 0, 33, 333};
50
+ txn = env.start_write();
51
+ txn.put_multiple_samelength(map, buffer::key_from_u64(13), array + 3, 4, mdbx::upsert);
52
+ txn.put_multiple_samelength(map, buffer::key_from_u64(10), array + 0, 1, mdbx::upsert);
53
+ txn.put_multiple_samelength(map, buffer::key_from_u64(12), array + 2, 3, mdbx::upsert);
54
+ txn.put_multiple_samelength(map, buffer::key_from_u64(15), array + 5, 6, mdbx::upsert);
55
+ txn.put_multiple_samelength(map, buffer::key_from_u64(14), array + 4, 5, mdbx::upsert);
56
+ txn.put_multiple_samelength(map, buffer::key_from_u64(11), array + 1, 2, mdbx::upsert);
57
+ txn.put_multiple_samelength(map, buffer::key_from_u64(16), array + 6, 7, mdbx::upsert);
58
+ txn.commit();
59
+
60
+ txn = env.start_read();
61
+ cursor = txn.open_cursor(map);
62
+ if (/* key = 7 */ cursor.to_first().value.as_uint64() != 19 ||
63
+
64
+ /* key = 10: 1 элемент */
65
+ cursor.to_next().value.as_uint64() != 1 ||
66
+
67
+ /* key = 11: 2 элемента, пропуск 1 */
68
+ cursor.to_next().value.as_uint64() != 2 || cursor.to_next().value.as_uint64() != 3 ||
69
+
70
+ /* key = 12: 3 элемента, пропуск 2 */
71
+ cursor.to_next().value.as_uint64() != 3 || cursor.to_next().value.as_uint64() != 4 ||
72
+ cursor.to_next().value.as_uint64() != 5 ||
73
+
74
+ /* key = 13: 4 элемента, пропуск 3 */
75
+ cursor.to_next().value.as_uint64() != 4 || cursor.to_next().value.as_uint64() != 5 ||
76
+ cursor.to_next().value.as_uint64() != 6 || cursor.to_next().value.as_uint64() != 7 ||
77
+
78
+ /* key = 14: 5 элементов, пропуск 4 */
79
+ cursor.to_next().value.as_uint64() != 5 || cursor.to_next().value.as_uint64() != 6 ||
80
+ cursor.to_next().value.as_uint64() != 7 || cursor.to_next().value.as_uint64() != 8 ||
81
+ cursor.to_next().value.as_uint64() != 9 ||
82
+
83
+ /* key = 15: 6 элементов, пропуск 5 */
84
+ cursor.to_next().value.as_uint64() != 6 || cursor.to_next().value.as_uint64() != 7 ||
85
+ cursor.to_next().value.as_uint64() != 8 || cursor.to_next().value.as_uint64() != 9 ||
86
+ cursor.to_next().value.as_uint64() != 17 || cursor.to_next().value.as_uint64() != 42 ||
87
+
88
+ /* key = 16: 7 элементов, пропуск 6 */
89
+ cursor.to_next().value.as_uint64() != 0 || cursor.to_next().value.as_uint64() != 7 ||
90
+ cursor.to_next().value.as_uint64() != 8 || cursor.to_next().value.as_uint64() != 9 ||
91
+ cursor.to_next().value.as_uint64() != 17 || cursor.to_next().value.as_uint64() != 42 ||
92
+ cursor.to_next().value.as_uint64() != 99 ||
93
+
94
+ /* key = 21 */ cursor.to_next().value.as_uint64() != 18 ||
95
+ /* key = 22 */ cursor.to_next().value.as_uint64() != 17 ||
96
+ /* key = 23 */ cursor.to_next().value.as_uint64() != 16 ||
97
+ /* key = 24 */ cursor.to_next().value.as_uint64() != 15 ||
98
+ /* key = 25 */ cursor.to_next().value.as_uint64() != 14 ||
99
+ /* key = 26 */ cursor.to_next().value.as_uint64() != 13 ||
100
+ /* key = 27 */ cursor.to_next().value.as_uint64() != 12 || cursor.to_next(false).done || !cursor.eof())
101
+ return false;
102
+
103
+ txn.abort();
104
+
105
+ txn = env.start_write();
106
+ txn.put_multiple_samelength(map, buffer::key_from_u64(7), array + 3, 4, mdbx::update);
107
+ txn.upsert(map, buffer::key_from_u64(10), buffer::key_from_u64(14));
108
+ txn.put_multiple_samelength(map, buffer::key_from_u64(11), array + 4, 5, mdbx::upsert);
109
+ txn.put_multiple_samelength(map, buffer::key_from_u64(12), array + 0, 1, mdbx::update);
110
+ txn.update(map, buffer::key_from_u64(13), buffer::key_from_u64(18));
111
+ txn.put_multiple_samelength(map, buffer::key_from_u64(14), array + 2, 3, mdbx::update);
112
+ txn.update(map, buffer::key_from_u64(15), buffer::key_from_u64(13));
113
+ txn.put_multiple_samelength(map, buffer::key_from_u64(16), array + 6, 9, mdbx::update);
114
+ txn.update(map, buffer::key_from_u64(21), buffer::key_from_u64(17));
115
+ txn.update(map, buffer::key_from_u64(22), buffer::key_from_u64(15));
116
+ txn.put_multiple_samelength(map, buffer::key_from_u64(23), array + 1, 2, mdbx::update);
117
+ txn.update(map, buffer::key_from_u64(24), buffer::key_from_u64(16));
118
+ txn.put_multiple_samelength(map, buffer::key_from_u64(25), array + 5, 6, mdbx::update);
119
+ txn.upsert(map, buffer::key_from_u64(26), buffer::key_from_u64(12));
120
+ txn.put_multiple_samelength(map, buffer::key_from_u64(27), array + 12, 3, mdbx::update);
121
+ txn.commit();
122
+
123
+ txn = env.start_read();
124
+ cursor = txn.open_cursor(map);
125
+ if (/* key = 7 */
126
+ cursor.to_first().value.as_uint64() != 4 || cursor.to_next().value.as_uint64() != 5 ||
127
+ cursor.to_next().value.as_uint64() != 6 || cursor.to_next().value.as_uint64() != 7 ||
128
+
129
+ /* key = 10: 1 элемент */
130
+ cursor.to_next().value.as_uint64() != 1 ||
131
+ /* +1 upsert */
132
+ cursor.to_next().value.as_uint64() != 14 ||
133
+
134
+ /* key = 11: 2 элемента, пропуск 1 */
135
+ cursor.to_next().value.as_uint64() != 2 || cursor.to_next().value.as_uint64() != 3 ||
136
+ /* +5 элементов, пропуск 4 */
137
+ cursor.to_next().value.as_uint64() != 5 || cursor.to_next().value.as_uint64() != 6 ||
138
+ cursor.to_next().value.as_uint64() != 7 || cursor.to_next().value.as_uint64() != 8 ||
139
+ cursor.to_next().value.as_uint64() != 9 ||
140
+
141
+ /* key = 12: 1 элемент */
142
+ cursor.to_next().value.as_uint64() != 1 ||
143
+ /* key = 13 */ cursor.to_next().value.as_uint64() != 18 ||
144
+
145
+ /* key = 14: 3 элемента, пропуск 2 */
146
+ cursor.to_next().value.as_uint64() != 3 || cursor.to_next().value.as_uint64() != 4 ||
147
+ cursor.to_next().value.as_uint64() != 5 ||
148
+
149
+ /* key = 15 */ cursor.to_next().value.as_uint64() != 13 ||
150
+
151
+ /* key = 16: 9 элементов, пропуск 6 */
152
+ cursor.to_next().value.as_uint64() != 0 || cursor.to_next().value.as_uint64() != 7 ||
153
+ cursor.to_next().value.as_uint64() != 8 || cursor.to_next().value.as_uint64() != 9 ||
154
+ cursor.to_next().value.as_uint64() != 17 || cursor.to_next().value.as_uint64() != 33 ||
155
+ cursor.to_next().value.as_uint64() != 42 || cursor.to_next().value.as_uint64() != 99 ||
156
+ cursor.to_next().value.as_uint64() != 333 ||
157
+
158
+ /* key = 21 */ cursor.to_next().value.as_uint64() != 17 ||
159
+ /* key = 22 */ cursor.to_next().value.as_uint64() != 15 ||
160
+ /* key = 23: 2 элемента, пропуск 1 */
161
+ cursor.to_next().value.as_uint64() != 2 || cursor.to_next().value.as_uint64() != 3 ||
162
+ /* key = 24 */ cursor.to_next().value.as_uint64() != 16 ||
163
+ /* key = 25: 6 элемента, пропуск 5 */
164
+ cursor.to_next().value.as_uint64() != 6 || cursor.to_next().value.as_uint64() != 7 ||
165
+ cursor.to_next().value.as_uint64() != 8 || cursor.to_next().value.as_uint64() != 9 ||
166
+ cursor.to_next().value.as_uint64() != 17 || cursor.to_next().value.as_uint64() != 42 ||
167
+
168
+ /* key = 26, 1+1 upsert */
169
+ cursor.to_next().value.as_uint64() != 12 || cursor.to_next().value.as_uint64() != 13 ||
170
+
171
+ /* key = 27: 3 элемента, пропуск 12 */
172
+ cursor.to_next().value.as_uint64() != 0 || cursor.to_next().value.as_uint64() != 33 ||
173
+ cursor.to_next().value.as_uint64() != 333 ||
174
+
175
+ cursor.to_next(false).done || !cursor.eof())
176
+ return false;
177
+
178
+ txn.abort();
179
+
180
+ txn = env.start_write();
181
+ txn.clear_map(map);
182
+ txn.commit();
183
+
184
+ return true;
185
+ }
186
+
187
+ //--------------------------------------------------------------------------------------------
188
+
189
+ bool case2_batch_read(mdbx::env env) {
190
+
191
+ auto txn = env.start_write();
192
+ auto map = txn.create_map("case2", mdbx::key_mode::usual, mdbx::value_mode::multi_samelength);
193
+ txn.upsert(map, mdbx::slice("key1"), mdbx::slice("val1"));
194
+ txn.upsert(map, mdbx::pair("key1", "val2"));
195
+ txn.upsert(map, mdbx::pair("key1", "val3"));
196
+ txn.upsert(map, mdbx::slice("key2"), mdbx::slice("val1"));
197
+ txn.upsert(map, mdbx::pair("key2", "val2"));
198
+ txn.upsert(map, mdbx::pair("key2", "val3"));
199
+
200
+ // cursor.close();
201
+ auto cursor = txn.open_cursor(map);
202
+ const auto t1 = cursor.to_first();
203
+ if (!t1 || t1.key != "key1" || t1.value != "val1") {
204
+ std::cerr << "Fail-t1\n";
205
+ return false;
206
+ }
207
+ const auto t2 = cursor.get_multiple_samelength();
208
+ if (!t2 || t2.key != "key1" || t2.value != "val1val2val3") {
209
+ std::cerr << "Fail-t2\n";
210
+ return false;
211
+ }
212
+ const auto t3 = cursor.next_multiple_samelength();
213
+ if (t3) {
214
+ std::cerr << "Fail-t3\n";
215
+ return false;
216
+ }
217
+ const auto t4 = cursor.seek_multiple_samelength("key2");
218
+ if (!t4 || t4.key != "key2" || t4.value != "val1val2val3") {
219
+ std::cerr << "Fail-t4\n";
220
+ return false;
221
+ }
222
+
223
+ txn.clear_map(map);
224
+ txn.commit();
225
+
226
+ return true;
227
+ }
228
+
229
+ //--------------------------------------------------------------------------------------------
230
+
231
+ size_t salt;
232
+
233
+ static size_t prng() {
234
+ salt = salt * 134775813 + 1;
235
+ return salt ^ ((salt >> 11) * 1822226723);
236
+ }
237
+
238
+ static inline size_t prng(size_t range) { return prng() % range; }
239
+
240
+ static mdbx::default_buffer_pair prng_kv(size_t n, size_t space) {
241
+ space = (space + !space) * 1024 * 32 / RELIEF_FACTOR;
242
+ const size_t w = (n ^ 1455614549) * 1664525 + 1013904223;
243
+ const size_t k = (prng(42 + w % space) ^ 1725278851) * 433750991;
244
+ const size_t v = prng();
245
+ return mdbx::default_buffer_pair(mdbx::slice::wrap(k), mdbx::slice::wrap(v));
246
+ }
247
+
248
+ bool case3_put_a_lot(mdbx::env env) {
249
+ salt = size_t(std::chrono::high_resolution_clock::now().time_since_epoch().count());
250
+ auto txn = env.start_write();
251
+ auto map = txn.create_map("case3", mdbx::key_mode::ordinal, mdbx::value_mode::multi_ordinal);
252
+ for (size_t n = 0; n < 5555555 / RELIEF_FACTOR; ++n)
253
+ txn.upsert(map, prng_kv(n, 1));
254
+ txn.commit();
255
+
256
+ for (size_t t = 0; t < 555 / RELIEF_FACTOR; ++t) {
257
+ txn = env.start_write();
258
+ auto cursor = txn.open_cursor(map);
259
+ for (size_t n = 0; n < 111; ++n) {
260
+ auto v = std::vector<size_t>();
261
+ const auto r = 1 + prng(3);
262
+ if (r & 1) {
263
+ const auto k = prng_kv(n + t, 2).key;
264
+ for (size_t i = prng(42 + prng(111) * prng(111 / RELIEF_FACTOR)); i > 0; --i)
265
+ v.push_back(prng());
266
+ txn.put_multiple_samelength(map, k, v, mdbx::upsert);
267
+ }
268
+ if (r & 2) {
269
+ const auto k = prng_kv(n + t, 2).key;
270
+ if (cursor.seek(k)) {
271
+ v.clear();
272
+ for (size_t i = prng(42 + prng(111) * prng(111 / RELIEF_FACTOR)); i > 0; --i)
273
+ v.push_back(prng());
274
+ cursor.put_multiple_samelength(k, v, mdbx::upsert);
275
+ }
276
+ }
277
+ }
278
+ txn.commit();
279
+ }
280
+
281
+ return true;
282
+ }
283
+
284
+ int doit() {
285
+ mdbx::path db_filename = "test-dupfix-multiple";
286
+ mdbx::env_managed::remove(db_filename);
287
+ mdbx::env_managed env(db_filename, mdbx::env_managed::create_parameters(), mdbx::env::operate_parameters(3));
288
+
289
+ bool ok = case1_ordering(env);
290
+ ok = case2_batch_read(env) && ok;
291
+ ok = case3_put_a_lot(env) && ok;
292
+
293
+ if (ok) {
294
+ std::cout << "OK\n";
295
+ return EXIT_SUCCESS;
296
+ } else {
297
+ std::cerr << "Fail\n";
298
+ return EXIT_FAILURE;
299
+ }
300
+ }
301
+
302
+ int main(int argc, const char *argv[]) {
303
+ (void)argc;
304
+ (void)argv;
305
+ try {
306
+ return doit();
307
+ } catch (const std::exception &ex) {
308
+ std::cerr << "Exception: " << ex.what() << "\n";
309
+ return EXIT_FAILURE;
310
+ }
311
+ }