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