@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,166 @@
1
+ #include "lmdb-js.h"
2
+ #if ENABLE_V8_API
3
+ #include <string.h>
4
+ #include <stdio.h>
5
+ #include <v8.h>
6
+ #include <node.h>
7
+
8
+ #if NODE_VERSION_AT_LEAST(16,6,0)
9
+ #if NODE_VERSION_AT_LEAST(17,0,0)
10
+ #include "../dependencies/v8/v8-fast-api-calls.h"
11
+ #else
12
+ #include "../dependencies/v8/v8-fast-api-calls-v16.h"
13
+ #endif
14
+ #endif
15
+
16
+ using namespace v8;
17
+ int32_t getByBinaryFast(Local<v8::Object> instance, double dwPointer, uint32_t keySize) {
18
+ DbiWrap* dw = (DbiWrap*) (size_t) dwPointer;
19
+ return dw->doGetByBinary(keySize);
20
+ }
21
+
22
+ //class NanWrap : public Nan::ObjectWrap {};
23
+ void getByBinaryV8(const FunctionCallbackInfo<v8::Value>& info) {
24
+ Isolate* isolate = Isolate::GetCurrent();
25
+ DbiWrap* dw = (DbiWrap*) (size_t) info[0]->NumberValue(isolate->GetCurrentContext()).FromJust();
26
+ info.GetReturnValue().Set(v8::Number::New(isolate, dw->doGetByBinary(info[1]->Uint32Value(isolate->GetCurrentContext()).FromJust())));
27
+ }
28
+ int32_t positionFast(Local<v8::Object> instance, double cwPointer, uint32_t flags, uint32_t offset, uint32_t keySize, uint64_t endKeyAddress) {
29
+ CursorWrap* cw = (CursorWrap*) (size_t) cwPointer;
30
+ DbiWrap* dw = cw->dw;
31
+ dw->getFast = true;
32
+ cw->flags = flags;
33
+ return cw->doPosition(offset, keySize, endKeyAddress);
34
+ }
35
+ void positionV8(const FunctionCallbackInfo<v8::Value>& info) {
36
+ Isolate* isolate = Isolate::GetCurrent();
37
+ CursorWrap* cw = (CursorWrap*) (size_t) info[0]->NumberValue(isolate->GetCurrentContext()).FromJust();
38
+ cw->flags = info[1]->Uint32Value(isolate->GetCurrentContext()).FromJust();
39
+ uint32_t offset = info[2]->Uint32Value(isolate->GetCurrentContext()).FromJust();
40
+ uint32_t keySize = info[3]->Uint32Value(isolate->GetCurrentContext()).FromJust();
41
+ uint64_t endKeyAddress = info[4]->IntegerValue(isolate->GetCurrentContext()).FromJust();
42
+ info.GetReturnValue().Set(v8::Number::New(isolate, cw->doPosition(offset, keySize, endKeyAddress)));
43
+ }
44
+
45
+ int32_t iterateFast(Local<v8::Object> instance, double cwPointer) {
46
+ CursorWrap* cw = (CursorWrap*) (size_t) cwPointer;
47
+ DbiWrap* dw = cw->dw;
48
+ dw->getFast = true;
49
+ MDB_val key, data;
50
+ int rc = mdb_cursor_get(cw->cursor, &key, &data, cw->iteratingOp);
51
+ return cw->returnEntry(rc, key, data);
52
+ }
53
+ void iterateV8(const FunctionCallbackInfo<v8::Value>& info) {
54
+ Isolate* isolate = Isolate::GetCurrent();
55
+ CursorWrap* cw = (CursorWrap*) (size_t) info[0]->NumberValue(isolate->GetCurrentContext()).FromJust();
56
+ DbiWrap* dw = cw->dw;
57
+ dw->getFast = true;
58
+ MDB_val key, data;
59
+ int rc = mdb_cursor_get(cw->cursor, &key, &data, cw->iteratingOp);
60
+ info.GetReturnValue().Set(v8::Number::New(isolate, cw->returnEntry(rc, key, data)));
61
+ }
62
+
63
+ int32_t writeFast(Local<v8::Object> instance, double ewPointer, uint64_t instructionAddress) {
64
+ EnvWrap* ew = (EnvWrap*) (size_t) ewPointer;
65
+ int rc;
66
+ if (instructionAddress)
67
+ rc = WriteWorker::DoWrites(ew->writeTxn->txn, ew, (uint32_t*)instructionAddress, nullptr);
68
+ else {
69
+ pthread_cond_signal(ew->writingCond);
70
+ rc = 0;
71
+ }
72
+ return rc;
73
+ // if (rc && !(rc == MDB_KEYEXIST || rc == MDB_NOTFOUND))
74
+ // options.fallback = true;
75
+ }
76
+ void writeV8(const v8::FunctionCallbackInfo<v8::Value>& info) {
77
+ Isolate* isolate = Isolate::GetCurrent();
78
+ EnvWrap* ew = (EnvWrap*) (size_t) info[0]->NumberValue(isolate->GetCurrentContext()).FromJust();
79
+ uint64_t instructionAddress = info[1]->IntegerValue(isolate->GetCurrentContext()).FromJust();
80
+ int rc;
81
+ if (instructionAddress)
82
+ rc = WriteWorker::DoWrites(ew->writeTxn->txn, ew, (uint32_t*)instructionAddress, nullptr);
83
+ else {
84
+ pthread_cond_signal(ew->writingCond);
85
+ rc = 0;
86
+ }
87
+ info.GetReturnValue().Set(v8::Number::New(isolate, rc));
88
+ }
89
+
90
+ void resetTxnFast(Local<v8::Object> instance, double twPointer) {
91
+ TxnWrap* tw = (TxnWrap*) (size_t) twPointer;
92
+ tw->reset();
93
+ }
94
+
95
+ void resetTxnV8(const FunctionCallbackInfo<v8::Value>& info) {
96
+ Isolate* isolate = Isolate::GetCurrent();
97
+ TxnWrap* tw = (TxnWrap*) (size_t) info[0]->NumberValue(isolate->GetCurrentContext()).FromJust();
98
+ tw->reset();
99
+ }
100
+
101
+ void noopFast(Local<v8::Object> instance) {
102
+ }
103
+
104
+ void noopV8(const FunctionCallbackInfo<v8::Value>& info) {
105
+ }
106
+
107
+
108
+ void clearKeptObjects(const FunctionCallbackInfo<v8::Value>& info) {
109
+ #if NODE_VERSION_AT_LEAST(14,0,0)
110
+ v8::Isolate::GetCurrent()->ClearKeptObjects();
111
+ #endif
112
+ }
113
+ void detachBuffer(const FunctionCallbackInfo<v8::Value>& info) {
114
+ #if NODE_VERSION_AT_LEAST(12,0,0)
115
+ v8::Local<v8::ArrayBuffer> v8Buffer = Local<v8::ArrayBuffer>::Cast(info[0]);
116
+ v8Buffer->Detach();
117
+ #endif
118
+ }
119
+
120
+ #endif
121
+
122
+
123
+ #define EXPORT_FAST(exportName, slowName, fastName) {\
124
+ auto fast = CFunction::Make(fastName);\
125
+ exports->Set(isolate->GetCurrentContext(), v8::String::NewFromUtf8(isolate, exportName, NewStringType::kInternalized).ToLocalChecked(), FunctionTemplate::New(\
126
+ isolate, slowName, Local<v8::Value>(),\
127
+ Local<Signature>(), 0, ConstructorBehavior::kThrow,\
128
+ SideEffectType::kHasNoSideEffect, &fast)->GetFunction(isolate->GetCurrentContext()).ToLocalChecked());\
129
+ }
130
+ #define EXPORT_FUNCTION(exportName, funcName) \
131
+ exports->Set(isolate->GetCurrentContext(), v8::String::NewFromUtf8(isolate, exportName, NewStringType::kInternalized).ToLocalChecked(), FunctionTemplate::New(\
132
+ isolate, funcName, Local<v8::Value>(),\
133
+ Local<Signature>(), 0, ConstructorBehavior::kThrow,\
134
+ SideEffectType::kHasNoSideEffect)->GetFunction(isolate->GetCurrentContext()).ToLocalChecked());
135
+
136
+ Napi::Value enableDirectV8(const Napi::CallbackInfo& info) {
137
+ #if ENABLE_V8_API
138
+ Isolate* isolate = Isolate::GetCurrent();
139
+ napi_value exportsValue = info[0];
140
+ Local<v8::Object> exports;
141
+ memcpy((void*) &exports, (void*) &exportsValue, sizeof(exportsValue));
142
+ #if NODE_VERSION_AT_LEAST(16,6,1)
143
+ bool useFastApi;
144
+ napi_get_value_bool(info.Env(), info[1], &useFastApi);
145
+ if (useFastApi) {
146
+ EXPORT_FAST("getByBinary", getByBinaryV8, getByBinaryFast);
147
+ EXPORT_FAST("position", positionV8, positionFast);
148
+ EXPORT_FAST("iterate", iterateV8, iterateFast);
149
+ EXPORT_FAST("write", writeV8, writeFast);
150
+ EXPORT_FAST("resetTxn", resetTxnV8, resetTxnFast);
151
+ EXPORT_FAST("noop", noopV8, noopFast);
152
+ } else {
153
+ #endif
154
+ EXPORT_FUNCTION("getByBinary", getByBinaryV8);
155
+ EXPORT_FUNCTION("position", positionV8);
156
+ EXPORT_FUNCTION("iterate", iterateV8);
157
+ EXPORT_FUNCTION("write", writeV8);
158
+ EXPORT_FUNCTION("noop", noopV8);
159
+ #if NODE_VERSION_AT_LEAST(16,6,1)
160
+ }
161
+ #endif
162
+ EXPORT_FUNCTION("clearKeptObjects", clearKeptObjects);
163
+ EXPORT_FUNCTION("detachBuffer", detachBuffer);
164
+ #endif
165
+ return info.Env().Undefined();
166
+ }
package/src/windows.c ADDED
@@ -0,0 +1,35 @@
1
+ #ifdef _WIN32
2
+ #include <windows.h>
3
+ #include <synchapi.h>
4
+ static int initializeMemoryPriority = 1;
5
+ static MEMORY_PRIORITY_INFORMATION lowMemPriority;
6
+ static MEMORY_PRIORITY_INFORMATION normalMemPriority;
7
+ int lowerMemoryPriority(int priority) {
8
+ if (initializeMemoryPriority) {
9
+ GetThreadInformation(GetCurrentThread(), ThreadMemoryPriority, &normalMemPriority, sizeof(normalMemPriority));
10
+ // fprintf(stderr, "initialized memory %u setting to %u\n", normalMemPriority.MemoryPriority, priority);
11
+ ZeroMemory(&lowMemPriority, sizeof(lowMemPriority));
12
+ lowMemPriority.MemoryPriority = priority;
13
+ initializeMemoryPriority = 0;
14
+ }
15
+ void* instruction;
16
+ void* pointer;
17
+ WaitOnAddress(instruction, pointer, 8, INFINITE);
18
+
19
+ return SetThreadInformation(GetCurrentThread(), ThreadMemoryPriority, &lowMemPriority, sizeof(lowMemPriority));
20
+ }
21
+ int setProcessMemoryPriority(int priority) {
22
+ if (initializeMemoryPriority) {
23
+ GetThreadInformation(GetCurrentThread(), ThreadMemoryPriority, &normalMemPriority, sizeof(normalMemPriority));
24
+ // fprintf(stderr, "initialized memory %u setting to %u\n", normalMemPriority.MemoryPriority, priority);
25
+ ZeroMemory(&lowMemPriority, sizeof(lowMemPriority));
26
+ lowMemPriority.MemoryPriority = priority;
27
+ initializeMemoryPriority = 0;
28
+ }
29
+ return SetProcessInformation(GetCurrentProcess(), ProcessMemoryPriority, &lowMemPriority, sizeof(lowMemPriority));
30
+ }
31
+
32
+ int restoreMemoryPriority() {
33
+ return SetThreadInformation(GetCurrentThread(), ThreadMemoryPriority, &normalMemPriority, sizeof(normalMemPriority));
34
+ }
35
+ #endif
package/src/writer.cpp ADDED
@@ -0,0 +1,452 @@
1
+ /* write instructions
2
+
3
+ 0-3 flags
4
+ 4-7 dbi
5
+ 8-11 key-size
6
+ 12 ... key followed by at least 2 32-bit zeros
7
+ 4 value-size
8
+ 8 bytes: value pointer (or value itself)
9
+ 8 compressor pointer?
10
+ 8 bytes (optional): conditional version
11
+ 8 bytes (optional): version
12
+ inline value?
13
+ */
14
+ #include "lmdb-js.h"
15
+ #include <atomic>
16
+ #ifndef _WIN32
17
+ #include <unistd.h>
18
+ #endif
19
+
20
+ // flags:
21
+ const uint32_t NO_INSTRUCTION_YET = 0;
22
+ const int PUT = 15;
23
+ const int DEL = 13;
24
+ const int DEL_VALUE = 14;
25
+ const int START_CONDITION_BLOCK = 4;
26
+ //const int START_CONDITION_VALUE_BLOCK = 6;
27
+ const int START_BLOCK = 1;
28
+ const int BLOCK_END = 2;
29
+ const int POINTER_NEXT = 3;
30
+ const int USER_CALLBACK = 8;
31
+ const int USER_CALLBACK_STRICT_ORDER = 0x100000;
32
+ const int DROP_DB = 12;
33
+ const int HAS_KEY = 4;
34
+ const int HAS_VALUE = 2;
35
+ const int CONDITIONAL = 8;
36
+ const int CONDITIONAL_VERSION = 0x100;
37
+ const int SET_VERSION = 0x200;
38
+ //const int HAS_INLINE_VALUE = 0x400;
39
+ const int COMPRESSIBLE = 0x100000;
40
+ const int DELETE_DATABASE = 0x400;
41
+ const int TXN_HAD_ERROR = 0x40000000;
42
+ const int TXN_DELIMITER = 0x8000000;
43
+ const int TXN_COMMITTED = 0x10000000;
44
+ //const int TXN_FLUSHED = 0x20000000;
45
+ const int WAITING_OPERATION = 0x2000000;
46
+ const int IF_NO_EXISTS = MDB_NOOVERWRITE; //0x10;
47
+ // result codes:
48
+ const int FAILED_CONDITION = 0x4000000;
49
+ const int FINISHED_OPERATION = 0x1000000;
50
+ const double ANY_VERSION = 3.542694326329068e-103; // special marker for any version
51
+
52
+
53
+ WriteWorker::~WriteWorker() {
54
+ // TODO: Make sure this runs on the JS main thread, or we need to move it
55
+ if (envForTxn->writeWorker == this)
56
+ envForTxn->writeWorker = nullptr;
57
+ }
58
+
59
+ WriteWorker::WriteWorker(MDB_env* env, EnvWrap* envForTxn, uint32_t* instructions)
60
+ : envForTxn(envForTxn),
61
+ instructions(instructions),
62
+ env(env) {
63
+ //fprintf(stdout, "nextCompressibleArg %p\n", nextCompressibleArg);
64
+ interruptionStatus = 0;
65
+ txn = nullptr;
66
+ }
67
+
68
+ AsyncWriteWorker::AsyncWriteWorker(MDB_env* env, EnvWrap* envForTxn, uint32_t* instructions, const Function& callback)
69
+ : WriteWorker(env, envForTxn, instructions), AsyncProgressWorker(callback, "lmdb:write") {
70
+ //fprintf(stdout, "nextCompressibleArg %p\n", nextCompressibleArg);
71
+ interruptionStatus = 0;
72
+ txn = nullptr;
73
+ }
74
+
75
+ void AsyncWriteWorker::Execute(const AsyncProgressWorker::ExecutionProgress& execution) {
76
+ executionProgress = (AsyncProgressWorker::ExecutionProgress*) &execution;
77
+ Write();
78
+ }
79
+ void WriteWorker::SendUpdate() {
80
+ fprintf(stderr, "This SendUpdate does not work!\n");
81
+ }
82
+ void AsyncWriteWorker::SendUpdate() {
83
+ executionProgress->Send(nullptr, 0);
84
+ }
85
+ MDB_txn* WriteWorker::AcquireTxn(int* flags) {
86
+ bool commitSynchronously = *flags & TXN_SYNCHRONOUS_COMMIT;
87
+
88
+ // TODO: if the conditionDepth is 0, we could allow the current worker's txn to be continued, committed and restarted
89
+ pthread_mutex_lock(envForTxn->writingLock);
90
+ retry:
91
+ if (commitSynchronously && interruptionStatus == WORKER_WAITING) {
92
+ //fprintf(stderr, "acquire interupting lock %p %u\n", this, commitSynchronously);
93
+ interruptionStatus = INTERRUPT_BATCH;
94
+ pthread_cond_signal(envForTxn->writingCond);
95
+ pthread_cond_wait(envForTxn->writingCond, envForTxn->writingLock);
96
+ if (interruptionStatus == RESTART_WORKER_TXN) {
97
+ *flags |= TXN_FROM_WORKER;
98
+ return nullptr;
99
+ } else {
100
+ interruptionStatus = WORKER_WAITING;
101
+ goto retry;
102
+ }
103
+ } else {
104
+ //if (interruptionStatus == RESTART_WORKER_TXN)
105
+ // pthread_cond_wait(envForTxn->writingCond, envForTxn->writingLock);
106
+ interruptionStatus = USER_HAS_LOCK;
107
+ *flags |= TXN_FROM_WORKER;
108
+ //if (txn)
109
+ //fprintf(stderr, "acquire lock from worker %p %u\n", txn, commitSynchronously);
110
+ return txn;
111
+ }
112
+ }
113
+
114
+ void WriteWorker::UnlockTxn() {
115
+ //fprintf(stderr, "release txn %u\n", interruptionStatus);
116
+ interruptionStatus = 0;
117
+ pthread_cond_signal(envForTxn->writingCond);
118
+ pthread_mutex_unlock(envForTxn->writingLock);
119
+ }
120
+ void WriteWorker::ReportError(const char* error) {
121
+ hasError = true;
122
+ fprintf(stderr, "Error %s\n", error);
123
+ }
124
+ void AsyncWriteWorker::ReportError(const char* error) {
125
+ hasError = true;
126
+ SetError(error);
127
+ }
128
+ int WriteWorker::WaitForCallbacks(MDB_txn** txn, bool allowCommit, uint32_t* target) {
129
+ int rc;
130
+ //fprintf(stderr, "wait for callback %p\n", target);
131
+ if (!finishedProgress)
132
+ SendUpdate();
133
+ pthread_cond_signal(envForTxn->writingCond);
134
+ interruptionStatus = WORKER_WAITING;
135
+ if (target) {
136
+ uint64_t delay = 1;
137
+ do {
138
+ cond_timedwait(envForTxn->writingCond, envForTxn->writingLock, delay);
139
+ delay = delay << 1ll;
140
+ //if (delay > 500)
141
+ //fprintf(stderr, "waited, %llu %p\n", delay, *target);
142
+ if ((*target & 0xf) || (allowCommit && finishedProgress)) {
143
+ // we are in position to continue writing or commit, so forward progress can be made without interrupting yet
144
+ interruptionStatus = 0;
145
+ return 0;
146
+ }
147
+ } while(interruptionStatus != INTERRUPT_BATCH);
148
+ } else
149
+ pthread_cond_wait(envForTxn->writingCond, envForTxn->writingLock);
150
+ if (interruptionStatus == INTERRUPT_BATCH) { // interrupted by JS code that wants to run a synchronous transaction
151
+ // fprintf(stderr, "Performing batch interruption %u\n", allowCommit);
152
+ interruptionStatus = RESTART_WORKER_TXN;
153
+ rc = mdb_txn_commit(*txn);
154
+ if (rc == 0) {
155
+ // wait again until the sync transaction is completed
156
+ //fprintf(stderr, "Waiting after interruption\n");
157
+ this->txn = *txn = nullptr;
158
+ pthread_cond_signal(envForTxn->writingCond);
159
+ pthread_cond_wait(envForTxn->writingCond, envForTxn->writingLock);
160
+ // now restart our transaction
161
+ rc = mdb_txn_begin(env, nullptr, 0, txn);
162
+ this->txn = *txn;
163
+ //fprintf(stderr, "Restarted txn after interruption\n");
164
+ interruptionStatus = 0;
165
+ }
166
+ if (rc != 0) {
167
+ fprintf(stdout, "wfc unlock due to error %u\n", rc);
168
+ return rc;
169
+ }
170
+ } else
171
+ interruptionStatus = 0;
172
+ return 0;
173
+ }
174
+ int WriteWorker::DoWrites(MDB_txn* txn, EnvWrap* envForTxn, uint32_t* instruction, WriteWorker* worker) {
175
+ MDB_val key, value;
176
+ int rc = 0;
177
+ int conditionDepth = 0;
178
+ int validatedDepth = 0;
179
+ double conditionalVersion, setVersion = 0;
180
+ bool overlappedWord = !!worker;
181
+ uint32_t* start;
182
+ do {
183
+ next_inst: start = instruction++;
184
+ uint32_t flags = *start;
185
+ MDB_dbi dbi = 0;
186
+ bool validated = conditionDepth == validatedDepth;
187
+ if (flags & 0xf0c0) {
188
+ fprintf(stderr, "Unknown flag bits %u %p\n", flags, start);
189
+ fprintf(stderr, "flags after message %u\n", *start);
190
+ worker->ReportError("Unknown flags\n");
191
+ return 0;
192
+ }
193
+ if (flags & HAS_KEY) {
194
+ // a key based instruction, get the key
195
+ dbi = (MDB_dbi) *instruction++;
196
+ key.mv_size = *instruction++;
197
+ key.mv_data = instruction;
198
+ instruction = (uint32_t*) (((size_t) instruction + key.mv_size + 16) & (~7));
199
+ if (flags & HAS_VALUE) {
200
+ if (flags & COMPRESSIBLE) {
201
+ int64_t status = -1;
202
+ status = std::atomic_exchange((std::atomic<int64_t>*)(instruction + 2), (int64_t)1);
203
+ if (status == 2) {
204
+ //fprintf(stderr, "wait on compression %p\n", instruction);
205
+ worker->interruptionStatus = WORKER_WAITING;
206
+ do {
207
+ pthread_cond_wait(envForTxn->writingCond, envForTxn->writingLock);
208
+ } while (std::atomic_load((std::atomic<int64_t>*)(instruction + 2)));
209
+ worker->interruptionStatus = 0;
210
+ } else if (status > 2) {
211
+ //fprintf(stderr, "doing the compression ourselves\n");
212
+ ((Compression*) (size_t) *((double*)&status))->compressInstruction(nullptr, (double*) (instruction + 2));
213
+ } // else status is 0 and compression is done
214
+ // compressed
215
+ value.mv_data = (void*)(size_t) * ((size_t*)instruction);
216
+ if ((size_t)value.mv_data > 0x1000000000000)
217
+ fprintf(stderr, "compression not completed %p %i\n", value.mv_data, (int) status);
218
+ value.mv_size = *(instruction - 1);
219
+ instruction += 4; // skip compression pointers
220
+ } else {
221
+ value.mv_data = (void*)(size_t) * ((double*)instruction);
222
+ value.mv_size = *(instruction - 1);
223
+ instruction += 2;
224
+ }
225
+ }
226
+ if (flags & CONDITIONAL_VERSION) {
227
+ conditionalVersion = *((double*) instruction);
228
+ instruction += 2;
229
+ MDB_val conditionalValue;
230
+ rc = mdb_get(txn, dbi, &key, &conditionalValue);
231
+ if (rc)
232
+ validated = false;
233
+ else if (conditionalVersion != ANY_VERSION) {
234
+ double version;
235
+ memcpy(&version, conditionalValue.mv_data, 8);
236
+ validated = validated && conditionalVersion == version;
237
+ }
238
+ }
239
+ if (flags & SET_VERSION) {
240
+ setVersion = *((double*) instruction);
241
+ instruction += 2;
242
+ }
243
+ if ((flags & IF_NO_EXISTS) && (flags & START_CONDITION_BLOCK)) {
244
+ rc = mdb_get(txn, dbi, &key, &value);
245
+ validated = validated && rc == MDB_NOTFOUND;
246
+ }
247
+ } else
248
+ instruction++;
249
+ //fprintf(stderr, "instr flags %p %p %u\n", start, flags, conditionDepth);
250
+ if (validated || !(flags & CONDITIONAL)) {
251
+ switch (flags & 0xf) {
252
+ case NO_INSTRUCTION_YET:
253
+ instruction -= 2; // reset back to the previous flag as the current instruction
254
+ rc = 0;
255
+ // in windows InterlockedCompareExchange might be faster
256
+ if (!worker->finishedProgress || conditionDepth) {
257
+ if (std::atomic_compare_exchange_strong((std::atomic<uint32_t>*) start,
258
+ (uint32_t*) &flags,
259
+ (uint32_t)WAITING_OPERATION)) {
260
+ worker->WaitForCallbacks(&txn, conditionDepth == 0, start);
261
+ }
262
+ goto next_inst;
263
+ } else {
264
+ if (std::atomic_compare_exchange_strong((std::atomic<uint32_t>*) start,
265
+ (uint32_t*) &flags,
266
+ (uint32_t)TXN_DELIMITER)) {
267
+ worker->instructions = start;
268
+ return 0;
269
+ } else
270
+ goto next_inst;
271
+ }
272
+ case BLOCK_END:
273
+ conditionDepth--;
274
+ if (validatedDepth > conditionDepth)
275
+ validatedDepth--;
276
+ if (conditionDepth < 0) {
277
+ fprintf(stderr, "Negative condition depth");
278
+ }
279
+ goto next_inst;
280
+ case PUT:
281
+ if (flags & SET_VERSION)
282
+ rc = putWithVersion(txn, dbi, &key, &value, flags & (MDB_NOOVERWRITE | MDB_NODUPDATA | MDB_APPEND | MDB_APPENDDUP), setVersion);
283
+ else
284
+ rc = mdb_put(txn, dbi, &key, &value, flags & (MDB_NOOVERWRITE | MDB_NODUPDATA | MDB_APPEND | MDB_APPENDDUP));
285
+ if (flags & COMPRESSIBLE)
286
+ free(value.mv_data);
287
+ //fprintf(stdout, "put %u \n", key.mv_size);
288
+ break;
289
+ case DEL:
290
+ rc = mdb_del(txn, dbi, &key, nullptr);
291
+ break;
292
+ case DEL_VALUE:
293
+ rc = mdb_del(txn, dbi, &key, &value);
294
+ if (flags & COMPRESSIBLE)
295
+ free(value.mv_data);
296
+ break;
297
+ case START_BLOCK: case START_CONDITION_BLOCK:
298
+ rc = validated ? 0 : MDB_NOTFOUND;
299
+ if (validated)
300
+ validatedDepth++;
301
+ conditionDepth++;
302
+ break;
303
+ case USER_CALLBACK:
304
+ worker->finishedProgress = false;
305
+ worker->progressStatus = 2;
306
+ rc = 0;
307
+ if (flags & USER_CALLBACK_STRICT_ORDER) {
308
+ std::atomic_fetch_or((std::atomic<uint32_t>*) start, (uint32_t) FINISHED_OPERATION); // mark it as finished so it is processed
309
+ while (!worker->finishedProgress) {
310
+ worker->WaitForCallbacks(&txn, conditionDepth == 0, nullptr);
311
+ }
312
+ }
313
+ break;
314
+ case DROP_DB:
315
+ rc = mdb_drop(txn, dbi, (flags & DELETE_DATABASE) ? 1 : 0);
316
+ break;
317
+ case POINTER_NEXT:
318
+ instruction = (uint32_t*)(size_t) * ((double*)instruction);
319
+ goto next_inst;
320
+ default:
321
+ fprintf(stderr, "Unknown flags %u %p\n", flags, start);
322
+ fprintf(stderr, "flags after message %u\n", *start);
323
+ worker->ReportError("Unknown flags\n");
324
+ return 22;
325
+ }
326
+ if (rc) {
327
+ if (!(rc == MDB_KEYEXIST || rc == MDB_NOTFOUND)) {
328
+ if (worker) {
329
+ worker->ReportError(mdb_strerror(rc));
330
+ } else {
331
+ return rc;
332
+ }
333
+ }
334
+ flags = FINISHED_OPERATION | FAILED_CONDITION;
335
+ }
336
+ else
337
+ flags = FINISHED_OPERATION;
338
+ } else
339
+ flags = FINISHED_OPERATION | FAILED_CONDITION;
340
+ //fprintf(stderr, "finished flag %p\n", flags);
341
+ if (overlappedWord) {
342
+ std::atomic_fetch_or((std::atomic<uint32_t>*) start, flags);
343
+ overlappedWord = false;
344
+ } else
345
+ *start |= flags;
346
+ } while(worker); // keep iterating in async/multiple-instruction mode, just one instruction in sync mode
347
+ return rc;
348
+ }
349
+
350
+ void WriteWorker::Write() {
351
+ int rc;
352
+ finishedProgress = true;
353
+ unsigned int envFlags;
354
+ mdb_env_get_flags(env, &envFlags);
355
+ pthread_mutex_lock(envForTxn->writingLock);
356
+ #ifdef _WIN32
357
+ rc = mdb_txn_begin(env, nullptr, (envForTxn->jsFlags & MDB_OVERLAPPINGSYNC) ? MDB_NOSYNC : 0, &txn);
358
+ #else
359
+ int retries = 0;
360
+ retry:
361
+ rc = mdb_txn_begin(env, nullptr, (envForTxn->jsFlags & MDB_OVERLAPPINGSYNC) ? MDB_NOSYNC : 0, &txn);
362
+ if (rc == EINVAL) {
363
+ if (retries++ < 10) {
364
+ sleep(1);
365
+ goto retry;
366
+ }
367
+ return ReportError("Invalid parameter, which is often due to more transactions than available robust locked mutexes or semaphors (see docs for more info)");
368
+ }
369
+ #endif
370
+ if (rc != 0) {
371
+ return ReportError(mdb_strerror(rc));
372
+ }
373
+ hasError = false;
374
+ rc = DoWrites(txn, envForTxn, instructions, this);
375
+
376
+ if (rc || hasError)
377
+ mdb_txn_abort(txn);
378
+ else
379
+ rc = mdb_txn_commit(txn);
380
+ txn = nullptr;
381
+ pthread_mutex_unlock(envForTxn->writingLock);
382
+ if (rc || hasError) {
383
+ std::atomic_fetch_or((std::atomic<uint32_t>*) instructions, (uint32_t) TXN_HAD_ERROR);
384
+ if (rc)
385
+ ReportError(mdb_strerror(rc));
386
+ return;
387
+ }
388
+ std::atomic_fetch_or((std::atomic<uint32_t>*) instructions, (uint32_t) TXN_COMMITTED);
389
+ }
390
+
391
+ void AsyncWriteWorker::OnProgress(const char* data, size_t count) {
392
+ if (progressStatus == 1) {
393
+ Callback().Call({ Number::New(Env(), progressStatus)});
394
+ return;
395
+ }
396
+ if (finishedProgress)
397
+ return;
398
+ pthread_mutex_lock(envForTxn->writingLock);
399
+ while(!txn) // possible to jump in after an interrupted txn here
400
+ pthread_cond_wait(envForTxn->writingCond, envForTxn->writingLock);
401
+ envForTxn->writeTxn = new TxnTracked(txn, 0);
402
+ finishedProgress = true;
403
+ napi_value result, arg; // we use direct napi call here because node-addon-api interface with throw a fatal error if a worker thread is terminating, and bun doesn't support escapable scopes yet
404
+ napi_create_int32(Env(), progressStatus, &arg);
405
+ napi_call_function(Env(), Env().Undefined(), Callback().Value(), 1, &arg, &result);
406
+ delete envForTxn->writeTxn;
407
+ envForTxn->writeTxn = nullptr;
408
+ pthread_cond_signal(envForTxn->writingCond);
409
+ pthread_mutex_unlock(envForTxn->writingLock);
410
+ }
411
+
412
+ void AsyncWriteWorker::OnOK() {
413
+ finishedProgress = true;
414
+ napi_value result, arg; // we use direct napi call here because node-addon-api interface with throw a fatal error if a worker thread is terminating, and bun doesn't support escapable scopes yet
415
+ napi_create_int32(Env(), 0, &arg);
416
+ napi_call_function(Env(), Env().Undefined(), Callback().Value(), 1, &arg, &result);
417
+ }
418
+
419
+ Value EnvWrap::startWriting(const Napi::CallbackInfo& info) {
420
+ if (!this->env) {
421
+ return throwError(info.Env(), "The environment is already closed.");
422
+ }
423
+ size_t instructionAddress = info[0].As<Number>().Int64Value();
424
+ AsyncWriteWorker* worker = new AsyncWriteWorker(this->env, this, (uint32_t*) instructionAddress, info[1].As<Function>());
425
+ this->writeWorker = worker;
426
+ worker->Queue();
427
+ return info.Env().Undefined();
428
+ }
429
+
430
+ NAPI_FUNCTION(EnvWrap::write) {
431
+ ARGS(2)
432
+ GET_INT64_ARG(0);
433
+ EnvWrap* ew = (EnvWrap*) i64;
434
+ if (!ew->env) {
435
+ napi_throw_error(env, nullptr, "The environment is already closed.");
436
+ RETURN_UNDEFINED;
437
+ }
438
+
439
+ napi_get_value_int64(env, args[1], &i64);
440
+ uint32_t* instructionAddress = (uint32_t*) i64;
441
+ int rc = 0;
442
+ if (instructionAddress)
443
+ rc = WriteWorker::DoWrites(ew->writeTxn->txn, ew, instructionAddress, nullptr);
444
+ else if (ew->writeWorker) {
445
+ pthread_cond_signal(ew->writingCond);
446
+ }
447
+ if (rc && !(rc == MDB_KEYEXIST || rc == MDB_NOTFOUND)) {
448
+ throwLmdbError(env, rc);
449
+ RETURN_UNDEFINED;
450
+ }
451
+ RETURN_UNDEFINED;
452
+ }