@ohos-ports/lmdb 2.8.6-beta.0

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 (125) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +580 -0
  3. package/SECURITY.md +12 -0
  4. package/bin/download-prebuilds.js +15 -0
  5. package/binding.gyp +113 -0
  6. package/build/Release/lmdb.node +0 -0
  7. package/caching.js +212 -0
  8. package/dependencies/lmdb/libraries/liblmdb/COPYRIGHT +20 -0
  9. package/dependencies/lmdb/libraries/liblmdb/Doxyfile +1631 -0
  10. package/dependencies/lmdb/libraries/liblmdb/LICENSE +47 -0
  11. package/dependencies/lmdb/libraries/liblmdb/Makefile +136 -0
  12. package/dependencies/lmdb/libraries/liblmdb/chacha8.c +183 -0
  13. package/dependencies/lmdb/libraries/liblmdb/chacha8.h +14 -0
  14. package/dependencies/lmdb/libraries/liblmdb/crypto.c +121 -0
  15. package/dependencies/lmdb/libraries/liblmdb/intro.doc +192 -0
  16. package/dependencies/lmdb/libraries/liblmdb/lmdb.h +1817 -0
  17. package/dependencies/lmdb/libraries/liblmdb/mdb.c +12555 -0
  18. package/dependencies/lmdb/libraries/liblmdb/mdb_copy.1 +74 -0
  19. package/dependencies/lmdb/libraries/liblmdb/mdb_copy.c +106 -0
  20. package/dependencies/lmdb/libraries/liblmdb/mdb_drop.1 +53 -0
  21. package/dependencies/lmdb/libraries/liblmdb/mdb_drop.c +154 -0
  22. package/dependencies/lmdb/libraries/liblmdb/mdb_dump.1 +94 -0
  23. package/dependencies/lmdb/libraries/liblmdb/mdb_dump.c +333 -0
  24. package/dependencies/lmdb/libraries/liblmdb/mdb_load.1 +97 -0
  25. package/dependencies/lmdb/libraries/liblmdb/mdb_load.c +530 -0
  26. package/dependencies/lmdb/libraries/liblmdb/mdb_stat.1 +83 -0
  27. package/dependencies/lmdb/libraries/liblmdb/mdb_stat.c +276 -0
  28. package/dependencies/lmdb/libraries/liblmdb/midl-sparse.c +452 -0
  29. package/dependencies/lmdb/libraries/liblmdb/midl.c +452 -0
  30. package/dependencies/lmdb/libraries/liblmdb/midl.h +208 -0
  31. package/dependencies/lmdb/libraries/liblmdb/module.c +101 -0
  32. package/dependencies/lmdb/libraries/liblmdb/module.h +16 -0
  33. package/dependencies/lmdb/libraries/liblmdb/mtest.c +178 -0
  34. package/dependencies/lmdb/libraries/liblmdb/mtest2.c +124 -0
  35. package/dependencies/lmdb/libraries/liblmdb/mtest3.c +133 -0
  36. package/dependencies/lmdb/libraries/liblmdb/mtest4.c +168 -0
  37. package/dependencies/lmdb/libraries/liblmdb/mtest5.c +135 -0
  38. package/dependencies/lmdb/libraries/liblmdb/mtest6.c +141 -0
  39. package/dependencies/lmdb/libraries/liblmdb/mtest_enc.c +190 -0
  40. package/dependencies/lmdb/libraries/liblmdb/mtest_enc2.c +189 -0
  41. package/dependencies/lmdb/libraries/liblmdb/mtest_remap.c +177 -0
  42. package/dependencies/lmdb/libraries/liblmdb/sample-bdb.txt +73 -0
  43. package/dependencies/lmdb/libraries/liblmdb/sample-mdb.txt +62 -0
  44. package/dependencies/lmdb/libraries/liblmdb/tooltag +27 -0
  45. package/dependencies/lmdb-data-v1/libraries/liblmdb/CHANGES +266 -0
  46. package/dependencies/lmdb-data-v1/libraries/liblmdb/COPYRIGHT +20 -0
  47. package/dependencies/lmdb-data-v1/libraries/liblmdb/Doxyfile +1631 -0
  48. package/dependencies/lmdb-data-v1/libraries/liblmdb/LICENSE +47 -0
  49. package/dependencies/lmdb-data-v1/libraries/liblmdb/intro.doc +192 -0
  50. package/dependencies/lmdb-data-v1/libraries/liblmdb/lmdb.h +1608 -0
  51. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb.c +10320 -0
  52. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_copy.1 +55 -0
  53. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_copy.c +82 -0
  54. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_dump.1 +75 -0
  55. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_dump.c +319 -0
  56. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_load.1 +84 -0
  57. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_load.c +496 -0
  58. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_stat.1 +64 -0
  59. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_stat.c +263 -0
  60. package/dependencies/lmdb-data-v1/libraries/liblmdb/midl.c +359 -0
  61. package/dependencies/lmdb-data-v1/libraries/liblmdb/midl.h +186 -0
  62. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest.c +177 -0
  63. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest2.c +124 -0
  64. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest3.c +133 -0
  65. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest4.c +168 -0
  66. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest5.c +135 -0
  67. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest6.c +141 -0
  68. package/dependencies/lmdb-data-v1/libraries/liblmdb/sample-bdb.txt +73 -0
  69. package/dependencies/lmdb-data-v1/libraries/liblmdb/sample-mdb.txt +62 -0
  70. package/dependencies/lmdb-data-v1/libraries/liblmdb/tooltag +22 -0
  71. package/dependencies/lz4/LICENSE +11 -0
  72. package/dependencies/lz4/lib/LICENSE +24 -0
  73. package/dependencies/lz4/lib/Makefile +225 -0
  74. package/dependencies/lz4/lib/README.md +169 -0
  75. package/dependencies/lz4/lib/dll/example/Makefile +63 -0
  76. package/dependencies/lz4/lib/dll/example/README.md +69 -0
  77. package/dependencies/lz4/lib/dll/example/fullbench-dll.sln +25 -0
  78. package/dependencies/lz4/lib/dll/example/fullbench-dll.vcxproj +182 -0
  79. package/dependencies/lz4/lib/dll/liblz4.def +62 -0
  80. package/dependencies/lz4/lib/liblz4-dll.rc.in +35 -0
  81. package/dependencies/lz4/lib/liblz4.pc.in +14 -0
  82. package/dependencies/lz4/lib/lz4.c +2722 -0
  83. package/dependencies/lz4/lib/lz4.h +842 -0
  84. package/dependencies/lz4/lib/lz4file.c +311 -0
  85. package/dependencies/lz4/lib/lz4file.h +93 -0
  86. package/dependencies/lz4/lib/lz4frame.c +2078 -0
  87. package/dependencies/lz4/lib/lz4frame.h +692 -0
  88. package/dependencies/lz4/lib/lz4frame_static.h +47 -0
  89. package/dependencies/lz4/lib/lz4hc.c +1631 -0
  90. package/dependencies/lz4/lib/lz4hc.h +413 -0
  91. package/dependencies/lz4/lib/xxhash.c +1030 -0
  92. package/dependencies/lz4/lib/xxhash.h +328 -0
  93. package/dependencies/v8/v8-fast-api-calls-v16.h +791 -0
  94. package/dependencies/v8/v8-fast-api-calls.h +888 -0
  95. package/dict/dict.txt +1 -0
  96. package/dict/dict2.txt +1 -0
  97. package/dist/index.cjs +2950 -0
  98. package/dist/index.cjs.map +1 -0
  99. package/index.d.cts +420 -0
  100. package/index.d.ts +450 -0
  101. package/index.js +38 -0
  102. package/keys.js +116 -0
  103. package/level.js +27 -0
  104. package/native.js +85 -0
  105. package/node-index.js +32 -0
  106. package/open.js +448 -0
  107. package/package.json +124 -0
  108. package/read.js +833 -0
  109. package/rollup.config.js +12 -0
  110. package/src/compression.cpp +225 -0
  111. package/src/cursor.cpp +395 -0
  112. package/src/dbi.cpp +382 -0
  113. package/src/env.cpp +1030 -0
  114. package/src/lmdb-js.cpp +64 -0
  115. package/src/lmdb-js.h +633 -0
  116. package/src/misc.cpp +593 -0
  117. package/src/ordered-binary.cpp +46 -0
  118. package/src/txn.cpp +210 -0
  119. package/src/v8-functions.cpp +170 -0
  120. package/src/windows.c +35 -0
  121. package/src/writer.cpp +537 -0
  122. package/util/RangeIterable.js +236 -0
  123. package/util/set-optional-deps.cjs +14 -0
  124. package/util/when.js +8 -0
  125. package/write.js +912 -0
@@ -0,0 +1,12 @@
1
+ export default [
2
+ {
3
+ input: "node-index.js",
4
+ output: [
5
+ {
6
+ file: "dist/index.cjs",
7
+ format: "cjs",
8
+ sourcemap: true
9
+ }
10
+ ]
11
+ }
12
+ ];
@@ -0,0 +1,225 @@
1
+ #include "lz4.h"
2
+ #include "lmdb-js.h"
3
+ #include <atomic>
4
+
5
+ using namespace Napi;
6
+
7
+ thread_local LZ4_stream_t* Compression::stream = nullptr;
8
+ Compression::Compression(const CallbackInfo& info) : ObjectWrap<Compression>(info) {
9
+ unsigned int compressionThreshold = 1000;
10
+ char* dictionary = nullptr;
11
+ size_t dictSize = 0;
12
+ if (info[0].IsObject()) {
13
+ auto dictionaryOption = info[0].As<Object>().Get("dictionary");
14
+ if (!dictionaryOption.IsUndefined()) {
15
+ if (!dictionaryOption.IsTypedArray()) {
16
+ throwError(info.Env(), "Dictionary must be a buffer");
17
+ return;
18
+ }
19
+ napi_get_buffer_info(info.Env(), dictionaryOption, (void**) &dictionary, &dictSize);
20
+ dictSize = (dictSize >> 3) << 3; // make sure it is word-aligned
21
+ }
22
+ auto thresholdOption = info[0].As<Object>().Get("threshold");
23
+ if (thresholdOption.IsNumber()) {
24
+ compressionThreshold = thresholdOption.As<Number>();
25
+ }
26
+ }
27
+ this->dictionary = this->compressDictionary = dictionary;
28
+ this->dictionarySize = dictSize;
29
+ this->decompressTarget = dictionary + dictSize;
30
+ this->decompressSize = 0;
31
+ this->acceleration = 1;
32
+ this->compressionThreshold = compressionThreshold;
33
+ info.This().As<Object>().Set("address", Number::New(info.Env(), (double) (size_t) this));
34
+ }
35
+
36
+ Napi::Value Compression::setBuffer(const CallbackInfo& info) {
37
+ size_t length;
38
+ napi_get_arraybuffer_info(info.Env(), info[0], (void**) &this->decompressTarget, &length);
39
+ unsigned int offset = info[1].As<Number>();
40
+ this->decompressTarget += offset;
41
+ this->decompressSize = info[2].As<Number>();
42
+ this->dictionarySize = info[4].As<Number>();
43
+ napi_get_buffer_info(info.Env(), info[3], (void**) &this->dictionary, &length);
44
+ return info.Env().Undefined();
45
+ }
46
+ void Compression::decompress(MDB_val& data, bool &isValid, bool canAllocate) {
47
+ uint32_t uncompressedLength;
48
+ int compressionHeaderSize;
49
+ uint32_t compressedLength = data.mv_size;
50
+ unsigned char* charData = (unsigned char*) data.mv_data;
51
+
52
+ if (charData[0] == 254) {
53
+ uncompressedLength = ((uint32_t)charData[1] << 16) | ((uint32_t)charData[2] << 8) | (uint32_t)charData[3];
54
+ compressionHeaderSize = 4;
55
+ }
56
+ else if (charData[0] == 255) {
57
+ uncompressedLength = ((uint32_t)charData[4] << 24) | ((uint32_t)charData[5] << 16) | ((uint32_t)charData[6] << 8) | (uint32_t)charData[7];
58
+ compressionHeaderSize = 8;
59
+ }
60
+ else {
61
+ fprintf(stderr, "Unknown status byte %u\n", charData[0]);
62
+ //if (canAllocate)
63
+ // Nan::ThrowError("Unknown status byte");
64
+ isValid = false;
65
+ return;
66
+ }
67
+ data.mv_data = decompressTarget;
68
+ data.mv_size = uncompressedLength;
69
+ //TODO: For larger blocks with known encoding, it might make sense to allocate space for it and use an ExternalString
70
+ //fprintf(stdout, "compressed size %u uncompressedLength %u, first byte %u\n", data.mv_size, uncompressedLength, charData[compressionHeaderSize]);
71
+ if (uncompressedLength > decompressSize) {
72
+ isValid = false;
73
+ return;
74
+ }
75
+ int written = LZ4_decompress_safe_usingDict(
76
+ (char*)charData + compressionHeaderSize, decompressTarget,
77
+ compressedLength - compressionHeaderSize, decompressSize,
78
+ dictionary, dictionarySize);
79
+ //fprintf(stdout, "first uncompressed byte %X %X %X %X %X %X\n", uncompressedData[0], uncompressedData[1], uncompressedData[2], uncompressedData[3], uncompressedData[4], uncompressedData[5]);
80
+ if (written < 0) {
81
+ fprintf(stderr, "Failed to decompress data %u %u bytes:\n", compressionHeaderSize, uncompressedLength);
82
+ for (uint32_t i = 0; i < compressedLength; i++) {
83
+ fprintf(stderr, "%u ", charData[i]);
84
+ }
85
+ //if (canAllocate)
86
+ // Nan::ThrowError("Failed to decompress data");
87
+ isValid = false;
88
+ return;
89
+ }
90
+ isValid = true;
91
+ }
92
+
93
+ int Compression::compressInstruction(EnvWrap* env, double* compressionAddress) {
94
+ MDB_val value;
95
+ value.mv_data = (void*)((size_t) * (compressionAddress - 1));
96
+ value.mv_size = *(((uint32_t*)compressionAddress) - 3);
97
+ argtokey_callback_t compressedData = compress(&value, nullptr);
98
+ if (compressedData) {
99
+ *(((uint32_t*)compressionAddress) - 3) = value.mv_size;
100
+ *((size_t*)(compressionAddress - 1)) = (size_t)value.mv_data;
101
+ int64_t status = std::atomic_exchange((std::atomic<int64_t>*) compressionAddress, (int64_t) 0);
102
+ if (status == 1 && env) {
103
+ pthread_mutex_lock(env->writingLock);
104
+ pthread_cond_signal(env->writingCond);
105
+ pthread_mutex_unlock(env->writingLock);
106
+ //fprintf(stderr, "sent compression completion signal\n");
107
+ }
108
+ //fprintf(stdout, "compressed to %p %u %u %p\n", value.mv_data, value.mv_size, status, env);
109
+ return 0;
110
+ } else {
111
+ fprintf(stdout, "failed to compress\n");
112
+ return 1;
113
+ }
114
+ }
115
+
116
+ argtokey_callback_t Compression::compress(MDB_val* value, void (*freeValue)(MDB_val&)) {
117
+ size_t dataLength = value->mv_size;
118
+ char* data = (char*)value->mv_data;
119
+ if (value->mv_size < compressionThreshold && !(value->mv_size > 0 && ((uint8_t*)data)[0] >= 250))
120
+ return freeValue; // don't compress if less than threshold (but we must compress if the first byte is the compression indicator)
121
+ bool longSize = dataLength >= 0x1000000;
122
+ int prefixSize = (longSize ? 8 : 4);
123
+ int maxCompressedSize = LZ4_COMPRESSBOUND(dataLength);
124
+ char* compressed = new char[maxCompressedSize + prefixSize];
125
+ //fprintf(stdout, "compressing %u\n", dataLength);
126
+ if (!stream)
127
+ stream = LZ4_createStream();
128
+ LZ4_loadDict(stream, compressDictionary, dictionarySize);
129
+ int compressedSize = LZ4_compress_fast_continue(stream, data, compressed + prefixSize, dataLength, maxCompressedSize, acceleration);
130
+ if (compressedSize > 0) {
131
+ if (freeValue)
132
+ freeValue(*value);
133
+ uint8_t* compressedData = (uint8_t*)compressed;
134
+ if (longSize) {
135
+ compressedData[0] = 255;
136
+ compressedData[2] = (uint8_t)(dataLength >> 40u);
137
+ compressedData[3] = (uint8_t)(dataLength >> 32u);
138
+ compressedData[4] = (uint8_t)(dataLength >> 24u);
139
+ compressedData[5] = (uint8_t)(dataLength >> 16u);
140
+ compressedData[6] = (uint8_t)(dataLength >> 8u);
141
+ compressedData[7] = (uint8_t)dataLength;
142
+ }
143
+ else {
144
+ compressedData[0] = 254;
145
+ compressedData[1] = (uint8_t)(dataLength >> 16u);
146
+ compressedData[2] = (uint8_t)(dataLength >> 8u);
147
+ compressedData[3] = (uint8_t)dataLength;
148
+ }
149
+ value->mv_size = compressedSize + prefixSize;
150
+ value->mv_data = compressed;
151
+ return ([](MDB_val &value) -> void {
152
+ delete[] (char*)value.mv_data;
153
+ });
154
+ }
155
+ else {
156
+ delete[] compressed;
157
+ return nullptr;
158
+ }
159
+ }
160
+
161
+ class CompressionWorker : public AsyncWorker {
162
+ public:
163
+ CompressionWorker(EnvWrap* env, double* compressionAddress, const Function& callback)
164
+ : AsyncWorker(callback), env(env), compressionAddress(compressionAddress) {}
165
+
166
+
167
+ void Execute() {
168
+ uint64_t compressionPointer;
169
+ compressionPointer = std::atomic_exchange((std::atomic<int64_t>*) compressionAddress, (int64_t) 2);
170
+ if (compressionPointer > 1) {
171
+ Compression* compression = (Compression*)(size_t) * ((double*)&compressionPointer);
172
+ compression->compressInstruction(env, compressionAddress);
173
+ }
174
+ }
175
+ void OnOK() {
176
+ // don't actually call the callback, no need
177
+ }
178
+
179
+ private:
180
+ EnvWrap* env;
181
+ double* compressionAddress;
182
+ };
183
+
184
+ NAPI_FUNCTION(EnvWrap::compress) {
185
+ ARGS(3)
186
+ GET_INT64_ARG(0);
187
+ EnvWrap* ew = (EnvWrap*) i64;
188
+ napi_get_value_int64(env, args[1], &i64);
189
+ double* compressionAddress = (double*) i64;
190
+ CompressionWorker* worker = new CompressionWorker(ew, (double*) compressionAddress, Function(env, args[2]));
191
+ worker->Queue();
192
+ RETURN_UNDEFINED;
193
+ }
194
+
195
+ void Compression::setupExports(Napi::Env env, Object exports) {
196
+ Function CompressionClass = DefineClass(env, "Compression", {
197
+ Compression::InstanceMethod("setBuffer", &Compression::setBuffer),
198
+ });
199
+ exports.Set("Compression", CompressionClass);
200
+ // compressionTpl->InstanceTemplate()->SetInternalFieldCount(1);
201
+ }
202
+
203
+
204
+ // This file contains code from the node-lmdb project
205
+ // Copyright (c) 2013-2017 Timur Kristóf
206
+ // Copyright (c) 2021 Kristopher Tate
207
+ // Licensed to you under the terms of the MIT license
208
+ //
209
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
210
+ // of this software and associated documentation files (the "Software"), to deal
211
+ // in the Software without restriction, including without limitation the rights
212
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
213
+ // copies of the Software, and to permit persons to whom the Software is
214
+ // furnished to do so, subject to the following conditions:
215
+
216
+ // The above copyright notice and this permission notice shall be included in
217
+ // all copies or substantial portions of the Software.
218
+
219
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
220
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
221
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
222
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
223
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
224
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
225
+ // THE SOFTWARE.
package/src/cursor.cpp ADDED
@@ -0,0 +1,395 @@
1
+ #include "lmdb-js.h"
2
+ #include <string.h>
3
+
4
+ using namespace Napi;
5
+
6
+ const int INCLUDE_VALUES = 0x100;
7
+ const int REVERSE = 0x400;
8
+ const int VALUES_FOR_KEY = 0x800;
9
+ const int ONLY_COUNT = 0x1000;
10
+ const int RENEW_CURSOR = 0x2000;
11
+ const int EXACT_MATCH = 0x4000;
12
+ const int INCLUSIVE_END = 0x8000;
13
+ const int EXCLUSIVE_START = 0x10000;
14
+
15
+ CursorWrap::CursorWrap(const CallbackInfo& info) : Napi::ObjectWrap<CursorWrap>(info) {
16
+ this->keyType = LmdbKeyType::StringKey;
17
+ this->freeKey = nullptr;
18
+ this->endKey.mv_size = 0; // indicates no end key (yet)
19
+ if (info.Length() < 1) {
20
+ throwError(info.Env(), "Wrong number of arguments");
21
+ return;
22
+ }
23
+
24
+ DbiWrap *dw;
25
+ napi_unwrap(info.Env(), info[0], (void**)&dw);
26
+ int64_t tw_address = 0;
27
+ napi_get_value_int64(info.Env(), info[1], &tw_address);
28
+ // Open the cursor
29
+ MDB_cursor *cursor;
30
+ MDB_txn *txn = dw->ew->getReadTxn(tw_address);
31
+ int rc = mdb_cursor_open(txn, dw->dbi, &cursor);
32
+ if (rc != 0) {
33
+ throwLmdbError(info.Env(), rc);
34
+ return;
35
+ }
36
+ info.This().As<Object>().Set("address", Number::New(info.Env(), (size_t) this));
37
+ this->cursor = cursor;
38
+ this->dw = dw;
39
+ this->txn = txn;
40
+ this->keyType = keyType;
41
+ }
42
+
43
+ CursorWrap::~CursorWrap() {
44
+ if (this->cursor) {
45
+ // Don't close cursor here, it is possible that the environment may already be closed, which causes it to crash
46
+ //mdb_cursor_close(this->cursor);
47
+ }
48
+ if (this->freeKey) {
49
+ this->freeKey(this->key);
50
+ }
51
+ }
52
+
53
+ Value CursorWrap::close(const CallbackInfo& info) {
54
+ if (!this->cursor) {
55
+ return throwError(info.Env(), "cursor.close: Attempt to close a closed cursor!");
56
+ }
57
+ mdb_cursor_close(this->cursor);
58
+ this->cursor = nullptr;
59
+ return info.Env().Undefined();
60
+ }
61
+
62
+ Value CursorWrap::del(const CallbackInfo& info) {
63
+ int flags = 0;
64
+
65
+ if (info.Length() == 1) {
66
+ if (!info[0].IsObject()) {
67
+ return throwError(info.Env(), "cursor.del: Invalid options argument. It should be an object.");
68
+ }
69
+
70
+ auto options = info[0].As<Object>();
71
+ setFlagFromValue(&flags, MDB_NODUPDATA, "noDupData", false, options);
72
+ }
73
+
74
+ int rc = mdb_cursor_del(this->cursor, flags);
75
+ if (rc != 0) {
76
+ return throwLmdbError(info.Env(), rc);
77
+ }
78
+ return info.Env().Undefined();
79
+ }
80
+ int CursorWrap::returnEntry(int lastRC, MDB_val &key, MDB_val &data) {
81
+ if (lastRC) {
82
+ if (lastRC == MDB_NOTFOUND)
83
+ return 0;
84
+ else {
85
+ return lastRC > 0 ? -lastRC : lastRC;
86
+ }
87
+ }
88
+ if (endKey.mv_size > 0) {
89
+ int comparison;
90
+ if (flags & VALUES_FOR_KEY)
91
+ comparison = mdb_dcmp(txn, dw->dbi, &endKey, &data);
92
+ else
93
+ comparison = mdb_cmp(txn, dw->dbi, &endKey, &key);
94
+ if ((flags & REVERSE) ? comparison >= 0 : (comparison <= 0)) {
95
+ if (!((flags & INCLUSIVE_END) && comparison == 0))
96
+ return 0;
97
+ }
98
+ }
99
+ char* keyBuffer = dw->ew->keyBuffer;
100
+ if (flags & INCLUDE_VALUES) {
101
+ int result = getVersionAndUncompress(data, dw);
102
+ bool fits = true;
103
+ if (result) {
104
+ fits = valToBinaryFast(data, dw); // it fit in the global/compression-target buffer
105
+ }
106
+ if (fits || result == 2 || data.mv_size < SHARED_BUFFER_THRESHOLD) {// if it was decompressed
107
+ *((uint32_t*)keyBuffer) = data.mv_size;
108
+ *((uint32_t*)(keyBuffer + 4)) = 0; // buffer id of 0
109
+ } else {
110
+ EnvWrap::toSharedBuffer(dw->ew->env, (uint32_t*) dw->ew->keyBuffer, data);
111
+ }
112
+ }
113
+ if (!(flags & VALUES_FOR_KEY)) {
114
+ memcpy(keyBuffer + 32, key.mv_data, key.mv_size);
115
+ *(keyBuffer + 32 + key.mv_size) = 0; // make sure it is null terminated for the sake of better ordered-binary performance
116
+ }
117
+
118
+ return key.mv_size;
119
+ }
120
+
121
+ const int START_ADDRESS_POSITION = 4064;
122
+ int32_t CursorWrap::doPosition(uint32_t offset, uint32_t keySize, uint64_t endKeyAddress) {
123
+ //char* keyBuffer = dw->ew->keyBuffer;
124
+ MDB_val key, data;
125
+ int rc;
126
+ if (dw->ew->env == nullptr) {
127
+ return MDB_BAD_TXN;
128
+ }
129
+ if (flags & RENEW_CURSOR) { // TODO: check the txn_id to determine if we need to renew
130
+ rc = mdb_cursor_renew(txn = dw->ew->getReadTxn(), cursor);
131
+ if (rc) {
132
+ if (rc > 0)
133
+ rc = -rc;
134
+ return rc;
135
+ }
136
+ }
137
+ if (endKeyAddress) {
138
+ uint32_t* keyBuffer = (uint32_t*) endKeyAddress;
139
+ endKey.mv_size = *keyBuffer;
140
+ endKey.mv_data = (char*)(keyBuffer + 1);
141
+ } else
142
+ endKey.mv_size = 0;
143
+ iteratingOp = (flags & REVERSE) ?
144
+ (flags & INCLUDE_VALUES) ?
145
+ (flags & VALUES_FOR_KEY) ? MDB_PREV_DUP : MDB_PREV :
146
+ MDB_PREV_NODUP :
147
+ (flags & INCLUDE_VALUES) ?
148
+ (flags & VALUES_FOR_KEY) ? MDB_NEXT_DUP : MDB_NEXT :
149
+ MDB_NEXT_NODUP;
150
+ key.mv_size = keySize;
151
+ key.mv_data = dw->ew->keyBuffer;
152
+ if (keySize == 0) {
153
+ rc = mdb_cursor_get(cursor, &key, &data, flags & REVERSE ? MDB_LAST : MDB_FIRST);
154
+ } else {
155
+ if (flags & VALUES_FOR_KEY) { // only values for this key
156
+ // take the next part of the key buffer as a pointer to starting data
157
+ uint32_t* startValueBuffer = (uint32_t*)(size_t)(*(double*)(dw->ew->keyBuffer + START_ADDRESS_POSITION));
158
+ data.mv_size = endKeyAddress ? *((uint32_t*)startValueBuffer) : 0;
159
+ data.mv_data = startValueBuffer + 1;
160
+ MDB_val startValue;
161
+ if (flags & EXCLUSIVE_START)
162
+ startValue = data; // save it for comparison
163
+ if (flags & REVERSE) {// reverse through values
164
+ startValue = data; // save it for comparison
165
+ rc = mdb_cursor_get(cursor, &key, &data, data.mv_size ? MDB_GET_BOTH_RANGE : MDB_SET_KEY);
166
+ if (rc) {
167
+ if (startValue.mv_size) {
168
+ // value specified, but not found, so find key and go to last item
169
+ rc = mdb_cursor_get(cursor, &key, &data, MDB_SET_KEY);
170
+ if (!rc)
171
+ rc = mdb_cursor_get(cursor, &key, &data, MDB_LAST_DUP);
172
+ } // else just couldn't find the key
173
+ } else { // found entry
174
+ if (startValue.mv_size == 0) // no value specified, so go to last value
175
+ rc = mdb_cursor_get(cursor, &key, &data, MDB_LAST_DUP);
176
+ else if (mdb_dcmp(txn, dw->dbi, &startValue, &data)) // the range found the next value *after* the start
177
+ rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV_DUP);
178
+ }
179
+ } else // forward, just do a get by range
180
+ rc = mdb_cursor_get(cursor, &key, &data, data.mv_size ?
181
+ (flags & EXACT_MATCH) ? MDB_GET_BOTH : MDB_GET_BOTH_RANGE : MDB_SET_KEY);
182
+
183
+ if (rc == MDB_NOTFOUND)
184
+ return 0;
185
+ if (flags & ONLY_COUNT && (!endKeyAddress || (flags & EXACT_MATCH))) {
186
+ size_t count;
187
+ rc = mdb_cursor_count(cursor, &count);
188
+ if (rc)
189
+ return rc > 0 ? -rc : rc;
190
+ return count;
191
+ }
192
+ if (flags & EXCLUSIVE_START) {
193
+ while(!rc) {
194
+ if (mdb_dcmp(txn, dw->dbi, &startValue, &data))
195
+ break;
196
+ rc = mdb_cursor_get(cursor, &key, &data, iteratingOp);
197
+ }
198
+ }
199
+ } else {
200
+ MDB_val firstKey;
201
+ if (flags & EXCLUSIVE_START)
202
+ firstKey = key; // save it for comparison
203
+ if (flags & REVERSE) {// reverse
204
+ firstKey = key; // save it for comparison
205
+ rc = mdb_cursor_get(cursor, &key, &data, MDB_SET_RANGE);
206
+ if (rc)
207
+ rc = mdb_cursor_get(cursor, &key, &data, MDB_LAST);
208
+ else if (mdb_cmp(txn, dw->dbi, &firstKey, &key)) // the range found the next entry *after* the start
209
+ rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV);
210
+ else if (dw->flags & MDB_DUPSORT)
211
+ // we need to go to the last value of this key
212
+ rc = mdb_cursor_get(cursor, &key, &data, MDB_LAST_DUP);
213
+ } else // forward, just do a get by range
214
+ rc = mdb_cursor_get(cursor, &key, &data, (flags & EXACT_MATCH) ? MDB_SET_KEY : MDB_SET_RANGE);
215
+ if (flags & EXCLUSIVE_START) {
216
+ while(!rc) {
217
+ if (mdb_cmp(txn, dw->dbi, &firstKey, &key))
218
+ break;
219
+ rc = mdb_cursor_get(cursor, &key, &data, iteratingOp);
220
+ }
221
+ }
222
+ }
223
+ }
224
+
225
+ while (offset-- > 0 && !rc) {
226
+ rc = mdb_cursor_get(cursor, &key, &data, iteratingOp);
227
+ }
228
+ if (flags & ONLY_COUNT) {
229
+ uint32_t count = 0;
230
+ bool useCursorCount = false;
231
+ // if we are in a dupsort database, and we are iterating over all entries, we can just count all the values for each key
232
+ if (dw->flags & MDB_DUPSORT) {
233
+ if (iteratingOp == MDB_PREV) {
234
+ iteratingOp = MDB_PREV_NODUP;
235
+ useCursorCount = true;
236
+ }
237
+ if (iteratingOp == MDB_NEXT) {
238
+ iteratingOp = MDB_NEXT_NODUP;
239
+ useCursorCount = true;
240
+ }
241
+ }
242
+
243
+ while (!rc) {
244
+ if (endKey.mv_size > 0) {
245
+ int comparison;
246
+ if (flags & VALUES_FOR_KEY)
247
+ comparison = mdb_dcmp(txn, dw->dbi, &endKey, &data);
248
+ else
249
+ comparison = mdb_cmp(txn, dw->dbi, &endKey, &key);
250
+ if ((flags & REVERSE) ? comparison >= 0 : (comparison <=0)) {
251
+ if (!((flags & INCLUSIVE_END) && comparison == 0))
252
+ return count;
253
+ }
254
+ }
255
+ if (useCursorCount) {
256
+ size_t countForKey;
257
+ rc = mdb_cursor_count(cursor, &countForKey);
258
+ if (rc) {
259
+ if (rc > 0)
260
+ rc = -rc;
261
+ return rc;
262
+ }
263
+ count += countForKey;
264
+ } else
265
+ count++;
266
+ rc = mdb_cursor_get(cursor, &key, &data, iteratingOp);
267
+ }
268
+ return count;
269
+ }
270
+ // TODO: Handle count?
271
+ return returnEntry(rc, key, data);
272
+ }
273
+ NAPI_FUNCTION(position) {
274
+ ARGS(5)
275
+ GET_INT64_ARG(0);
276
+ CursorWrap* cw = (CursorWrap*) i64;
277
+ GET_UINT32_ARG(cw->flags, 1);
278
+ uint32_t offset;
279
+ GET_UINT32_ARG(offset, 2);
280
+ uint32_t keySize;
281
+ GET_UINT32_ARG(keySize, 3);
282
+ napi_get_value_int64(env, args[4], &i64);
283
+ int64_t endKeyAddress = i64;
284
+ int32_t result = cw->doPosition(offset, keySize, endKeyAddress);
285
+ RETURN_INT32(result);
286
+ }
287
+ int32_t positionFFI(double cwPointer, uint32_t flags, uint32_t offset, uint32_t keySize, uint64_t endKeyAddress) {
288
+ CursorWrap* cw = (CursorWrap*) (size_t) cwPointer;
289
+ DbiWrap* dw = cw->dw;
290
+ dw->getFast = true;
291
+ cw->flags = flags;
292
+ return cw->doPosition(offset, keySize, endKeyAddress);
293
+ }
294
+
295
+ NAPI_FUNCTION(iterate) {
296
+ ARGS(1)
297
+ GET_INT64_ARG(0);
298
+ CursorWrap* cw = (CursorWrap*) i64;
299
+ MDB_val key, data;
300
+ int rc;
301
+ if (cw->dw->ew->env == nullptr) rc = MDB_BAD_TXN;
302
+ else
303
+ rc = mdb_cursor_get(cw->cursor, &key, &data, cw->iteratingOp);
304
+ RETURN_INT32(cw->returnEntry(rc, key, data));
305
+ }
306
+
307
+ int32_t iterateFFI(double cwPointer) {
308
+ CursorWrap* cw = (CursorWrap*) (size_t) cwPointer;
309
+ DbiWrap* dw = cw->dw;
310
+ dw->getFast = true;
311
+ MDB_val key, data;
312
+ if (cw->dw->ew->env == nullptr)
313
+ return MDB_BAD_TXN;
314
+ int rc = mdb_cursor_get(cw->cursor, &key, &data, cw->iteratingOp);
315
+ return cw->returnEntry(rc, key, data);
316
+ }
317
+
318
+
319
+ NAPI_FUNCTION(getCurrentValue) {
320
+ ARGS(1)
321
+ GET_INT64_ARG(0);
322
+ CursorWrap* cw = (CursorWrap*) i64;
323
+ MDB_val key, data;
324
+ int rc = mdb_cursor_get(cw->cursor, &key, &data, MDB_GET_CURRENT);
325
+ RETURN_INT32(cw->returnEntry(rc, key, data));
326
+ }
327
+
328
+ napi_finalize noopCursor = [](napi_env, void *, void *) {
329
+ // Data belongs to LMDB, we shouldn't free it here
330
+ };
331
+ NAPI_FUNCTION(getCurrentShared) {
332
+ ARGS(1)
333
+ GET_INT64_ARG(0);
334
+ CursorWrap* cw = (CursorWrap*) i64;
335
+ MDB_val key, data;
336
+ int rc = mdb_cursor_get(cw->cursor, &key, &data, MDB_GET_CURRENT);
337
+ if (rc)
338
+ RETURN_INT32(cw->returnEntry(rc, key, data));
339
+ getVersionAndUncompress(data, cw->dw);
340
+ napi_create_external_buffer(env, data.mv_size,
341
+ (char*) data.mv_data, noopCursor, nullptr, &returnValue);
342
+ return returnValue;
343
+ }
344
+
345
+ NAPI_FUNCTION(renew) {
346
+ ARGS(1)
347
+ GET_INT64_ARG(0);
348
+ CursorWrap* cw = (CursorWrap*) i64;
349
+ mdb_cursor_renew(cw->txn = cw->dw->ew->getReadTxn(), cw->cursor);
350
+ RETURN_UNDEFINED;
351
+ }
352
+
353
+ void CursorWrap::setupExports(Napi::Env env, Object exports) {
354
+ // CursorWrap: Prepare constructor template
355
+ Function CursorClass = DefineClass(env, "Cursor", {
356
+ // CursorWrap: Add functions to the prototype
357
+ CursorWrap::InstanceMethod("close", &CursorWrap::close),
358
+ CursorWrap::InstanceMethod("del", &CursorWrap::del),
359
+ });
360
+ EXPORT_NAPI_FUNCTION("position", position);
361
+ EXPORT_NAPI_FUNCTION("iterate", iterate);
362
+ EXPORT_NAPI_FUNCTION("getCurrentValue", getCurrentValue);
363
+ EXPORT_NAPI_FUNCTION("getCurrentShared", getCurrentShared);
364
+ EXPORT_NAPI_FUNCTION("renew", renew);
365
+ EXPORT_FUNCTION_ADDRESS("positionPtr", positionFFI);
366
+ EXPORT_FUNCTION_ADDRESS("iteratePtr", iterateFFI);
367
+
368
+ exports.Set("Cursor", CursorClass);
369
+
370
+ // cursorTpl->InstanceTemplate()->SetInternalFieldCount(1);
371
+ }
372
+
373
+ // This file contains code from the node-lmdb project
374
+ // Copyright (c) 2013-2017 Timur Kristóf
375
+ // Copyright (c) 2021 Kristopher Tate
376
+ // Licensed to you under the terms of the MIT license
377
+ //
378
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
379
+ // of this software and associated documentation files (the "Software"), to deal
380
+ // in the Software without restriction, including without limitation the rights
381
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
382
+ // copies of the Software, and to permit persons to whom the Software is
383
+ // furnished to do so, subject to the following conditions:
384
+
385
+ // The above copyright notice and this permission notice shall be included in
386
+ // all copies or substantial portions of the Software.
387
+
388
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
389
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
390
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
391
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
392
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
393
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
394
+ // THE SOFTWARE.
395
+