@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.
- package/LICENSE +21 -0
- package/README.md +580 -0
- package/SECURITY.md +12 -0
- package/bin/download-prebuilds.js +15 -0
- package/binding.gyp +113 -0
- package/build/Release/lmdb.node +0 -0
- package/caching.js +212 -0
- package/dependencies/lmdb/libraries/liblmdb/COPYRIGHT +20 -0
- package/dependencies/lmdb/libraries/liblmdb/Doxyfile +1631 -0
- package/dependencies/lmdb/libraries/liblmdb/LICENSE +47 -0
- package/dependencies/lmdb/libraries/liblmdb/Makefile +136 -0
- package/dependencies/lmdb/libraries/liblmdb/chacha8.c +183 -0
- package/dependencies/lmdb/libraries/liblmdb/chacha8.h +14 -0
- package/dependencies/lmdb/libraries/liblmdb/crypto.c +121 -0
- package/dependencies/lmdb/libraries/liblmdb/intro.doc +192 -0
- package/dependencies/lmdb/libraries/liblmdb/lmdb.h +1817 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb.c +12555 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_copy.1 +74 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_copy.c +106 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_drop.1 +53 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_drop.c +154 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_dump.1 +94 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_dump.c +333 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_load.1 +97 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_load.c +530 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_stat.1 +83 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_stat.c +276 -0
- package/dependencies/lmdb/libraries/liblmdb/midl-sparse.c +452 -0
- package/dependencies/lmdb/libraries/liblmdb/midl.c +452 -0
- package/dependencies/lmdb/libraries/liblmdb/midl.h +208 -0
- package/dependencies/lmdb/libraries/liblmdb/module.c +101 -0
- package/dependencies/lmdb/libraries/liblmdb/module.h +16 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest.c +178 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest2.c +124 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest3.c +133 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest4.c +168 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest5.c +135 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest6.c +141 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest_enc.c +190 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest_enc2.c +189 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest_remap.c +177 -0
- package/dependencies/lmdb/libraries/liblmdb/sample-bdb.txt +73 -0
- package/dependencies/lmdb/libraries/liblmdb/sample-mdb.txt +62 -0
- package/dependencies/lmdb/libraries/liblmdb/tooltag +27 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/CHANGES +266 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/COPYRIGHT +20 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/Doxyfile +1631 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/LICENSE +47 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/intro.doc +192 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/lmdb.h +1608 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb.c +10320 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_copy.1 +55 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_copy.c +82 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_dump.1 +75 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_dump.c +319 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_load.1 +84 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_load.c +496 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_stat.1 +64 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_stat.c +263 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/midl.c +359 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/midl.h +186 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest.c +177 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest2.c +124 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest3.c +133 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest4.c +168 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest5.c +135 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest6.c +141 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/sample-bdb.txt +73 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/sample-mdb.txt +62 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/tooltag +22 -0
- package/dependencies/lz4/LICENSE +11 -0
- package/dependencies/lz4/lib/LICENSE +24 -0
- package/dependencies/lz4/lib/Makefile +225 -0
- package/dependencies/lz4/lib/README.md +169 -0
- package/dependencies/lz4/lib/dll/example/Makefile +63 -0
- package/dependencies/lz4/lib/dll/example/README.md +69 -0
- package/dependencies/lz4/lib/dll/example/fullbench-dll.sln +25 -0
- package/dependencies/lz4/lib/dll/example/fullbench-dll.vcxproj +182 -0
- package/dependencies/lz4/lib/dll/liblz4.def +62 -0
- package/dependencies/lz4/lib/liblz4-dll.rc.in +35 -0
- package/dependencies/lz4/lib/liblz4.pc.in +14 -0
- package/dependencies/lz4/lib/lz4.c +2722 -0
- package/dependencies/lz4/lib/lz4.h +842 -0
- package/dependencies/lz4/lib/lz4file.c +311 -0
- package/dependencies/lz4/lib/lz4file.h +93 -0
- package/dependencies/lz4/lib/lz4frame.c +2078 -0
- package/dependencies/lz4/lib/lz4frame.h +692 -0
- package/dependencies/lz4/lib/lz4frame_static.h +47 -0
- package/dependencies/lz4/lib/lz4hc.c +1631 -0
- package/dependencies/lz4/lib/lz4hc.h +413 -0
- package/dependencies/lz4/lib/xxhash.c +1030 -0
- package/dependencies/lz4/lib/xxhash.h +328 -0
- package/dependencies/v8/v8-fast-api-calls-v16.h +791 -0
- package/dependencies/v8/v8-fast-api-calls.h +888 -0
- package/dict/dict.txt +1 -0
- package/dict/dict2.txt +1 -0
- package/dist/index.cjs +2950 -0
- package/dist/index.cjs.map +1 -0
- package/index.d.cts +420 -0
- package/index.d.ts +450 -0
- package/index.js +38 -0
- package/keys.js +116 -0
- package/level.js +27 -0
- package/native.js +85 -0
- package/node-index.js +32 -0
- package/open.js +448 -0
- package/package.json +124 -0
- package/read.js +833 -0
- package/rollup.config.js +12 -0
- package/src/compression.cpp +225 -0
- package/src/cursor.cpp +395 -0
- package/src/dbi.cpp +382 -0
- package/src/env.cpp +1030 -0
- package/src/lmdb-js.cpp +64 -0
- package/src/lmdb-js.h +633 -0
- package/src/misc.cpp +593 -0
- package/src/ordered-binary.cpp +46 -0
- package/src/txn.cpp +210 -0
- package/src/v8-functions.cpp +170 -0
- package/src/windows.c +35 -0
- package/src/writer.cpp +537 -0
- package/util/RangeIterable.js +236 -0
- package/util/set-optional-deps.cjs +14 -0
- package/util/when.js +8 -0
- package/write.js +912 -0
package/src/env.cpp
ADDED
|
@@ -0,0 +1,1030 @@
|
|
|
1
|
+
#include "lmdb-js.h"
|
|
2
|
+
#include <atomic>
|
|
3
|
+
#ifndef _WIN32
|
|
4
|
+
#include <sys/types.h>
|
|
5
|
+
#include <sys/stat.h>
|
|
6
|
+
#include <unistd.h>
|
|
7
|
+
#endif
|
|
8
|
+
using namespace Napi;
|
|
9
|
+
|
|
10
|
+
#define IGNORE_NOTFOUND (1)
|
|
11
|
+
|
|
12
|
+
env_tracking_t* EnvWrap::envTracking = EnvWrap::initTracking();
|
|
13
|
+
thread_local std::vector<EnvWrap*>* EnvWrap::openEnvWraps = nullptr;
|
|
14
|
+
thread_local js_buffers_t* EnvWrap::sharedBuffers = nullptr;
|
|
15
|
+
//thread_local std::unordered_map<void*, buffer_info_t>* EnvWrap::sharedBuffers = nullptr;
|
|
16
|
+
void* getSharedBuffers() {
|
|
17
|
+
return (void*) EnvWrap::sharedBuffers;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
env_tracking_t* EnvWrap::initTracking() {
|
|
21
|
+
env_tracking_t* tracking = new env_tracking_t;
|
|
22
|
+
tracking->envsLock = new pthread_mutex_t;
|
|
23
|
+
pthread_mutex_init(tracking->envsLock, nullptr);
|
|
24
|
+
tracking->getSharedBuffers = getSharedBuffers;
|
|
25
|
+
return tracking;
|
|
26
|
+
}
|
|
27
|
+
static napi_ref testRef;
|
|
28
|
+
static napi_env testRefEnv;
|
|
29
|
+
void EnvWrap::cleanupEnvWraps(void* data) {
|
|
30
|
+
if (openEnvWraps)
|
|
31
|
+
free(openEnvWraps);
|
|
32
|
+
else
|
|
33
|
+
fprintf(stderr, "How do we end up cleanup env wraps that don't exist?\n");
|
|
34
|
+
openEnvWraps = nullptr;
|
|
35
|
+
}
|
|
36
|
+
EnvWrap::EnvWrap(const CallbackInfo& info) : ObjectWrap<EnvWrap>(info) {
|
|
37
|
+
int rc;
|
|
38
|
+
rc = mdb_env_create(&(this->env));
|
|
39
|
+
|
|
40
|
+
if (rc != 0) {
|
|
41
|
+
mdb_env_close(this->env);
|
|
42
|
+
throwLmdbError(info.Env(), rc);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
this->currentWriteTxn = nullptr;
|
|
47
|
+
this->currentReadTxn = nullptr;
|
|
48
|
+
this->writeTxn = nullptr;
|
|
49
|
+
this->writeWorker = nullptr;
|
|
50
|
+
this->readTxnRenewed = false;
|
|
51
|
+
this->hasWrites = false;
|
|
52
|
+
this->writingLock = new pthread_mutex_t;
|
|
53
|
+
this->writingCond = new pthread_cond_t;
|
|
54
|
+
info.This().As<Object>().Set("address", Number::New(info.Env(), (size_t) this));
|
|
55
|
+
pthread_mutex_init(this->writingLock, nullptr);
|
|
56
|
+
cond_init(this->writingCond);
|
|
57
|
+
}
|
|
58
|
+
MDB_env* foundEnv;
|
|
59
|
+
const int EXISTING_ENV_FOUND = 10;
|
|
60
|
+
int checkExistingEnvs(mdb_filehandle_t fd, MDB_env* env) {
|
|
61
|
+
uint64_t inode, dev;
|
|
62
|
+
#ifdef _WIN32
|
|
63
|
+
BY_HANDLE_FILE_INFORMATION fileInformation;
|
|
64
|
+
if (GetFileInformationByHandle(fd, &fileInformation)) {
|
|
65
|
+
dev = fileInformation.dwVolumeSerialNumber;
|
|
66
|
+
inode = ((uint64_t) fileInformation.nFileIndexHigh << 32) | fileInformation.nFileIndexLow;
|
|
67
|
+
} else
|
|
68
|
+
return MDB_NOTFOUND;
|
|
69
|
+
#else
|
|
70
|
+
struct stat sb;
|
|
71
|
+
if (fstat(fd, &sb) == 0) {
|
|
72
|
+
dev = sb.st_dev;
|
|
73
|
+
inode = sb.st_ino;
|
|
74
|
+
} else
|
|
75
|
+
return MDB_NOTFOUND;
|
|
76
|
+
#endif
|
|
77
|
+
for (auto envRef = EnvWrap::envTracking->envs.begin(); envRef != EnvWrap::envTracking->envs.end();) {
|
|
78
|
+
if (envRef->dev == dev && envRef->inode == inode) {
|
|
79
|
+
envRef->count++;
|
|
80
|
+
foundEnv = envRef->env;
|
|
81
|
+
return EXISTING_ENV_FOUND;
|
|
82
|
+
}
|
|
83
|
+
++envRef;
|
|
84
|
+
}
|
|
85
|
+
SharedEnv envRef;
|
|
86
|
+
envRef.dev = dev;
|
|
87
|
+
envRef.inode = inode;
|
|
88
|
+
envRef.env = env;
|
|
89
|
+
envRef.count = 1;
|
|
90
|
+
envRef.hasWrites = false;
|
|
91
|
+
EnvWrap::envTracking->envs.push_back(envRef);
|
|
92
|
+
return 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
EnvWrap::~EnvWrap() {
|
|
96
|
+
// Close if not closed already
|
|
97
|
+
closeEnv();
|
|
98
|
+
pthread_mutex_destroy(this->writingLock);
|
|
99
|
+
pthread_cond_destroy(this->writingCond);
|
|
100
|
+
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
void EnvWrap::cleanupStrayTxns() {
|
|
104
|
+
if (this->currentWriteTxn) {
|
|
105
|
+
mdb_txn_abort(this->currentWriteTxn->txn);
|
|
106
|
+
this->currentWriteTxn->removeFromEnvWrap();
|
|
107
|
+
}
|
|
108
|
+
/* while (this->workers.size()) { // enable this if we do need to do worker cleanup
|
|
109
|
+
AsyncWorker *worker = *this->workers.begin();
|
|
110
|
+
fprintf(stderr, "Deleting running worker\n");
|
|
111
|
+
delete worker;
|
|
112
|
+
}*/
|
|
113
|
+
while (this->readTxns.size()) {
|
|
114
|
+
TxnWrap *tw = *this->readTxns.begin();
|
|
115
|
+
mdb_txn_abort(tw->txn);
|
|
116
|
+
tw->removeFromEnvWrap();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
void EnvWrap::consolidateTxns() {
|
|
120
|
+
// sort read txns by txn id, and then abort newer ones that we can just reference older ones with.
|
|
121
|
+
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
class SyncWorker : public AsyncWorker {
|
|
125
|
+
public:
|
|
126
|
+
SyncWorker(EnvWrap* env, const Function& callback)
|
|
127
|
+
: AsyncWorker(callback), env(env) {
|
|
128
|
+
//env->workers.push_back(this);
|
|
129
|
+
}
|
|
130
|
+
/*~SyncWorker() {
|
|
131
|
+
for (auto workerRef = env->workers.begin(); workerRef != env->workers.end(); ) {
|
|
132
|
+
if (this == *workerRef) {
|
|
133
|
+
env->workers.erase(workerRef);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}*/
|
|
137
|
+
void OnOK() {
|
|
138
|
+
napi_value result; // we use direct napi call here because node-addon-api interface with throw a fatal error if a worker thread is terminating
|
|
139
|
+
napi_call_function(Env(), Env().Undefined(), Callback().Value(), 0, {}, &result);
|
|
140
|
+
}
|
|
141
|
+
void OnError(const Error& e) {
|
|
142
|
+
napi_value result; // we use direct napi call here because node-addon-api interface with throw a fatal error if a worker thread is terminating
|
|
143
|
+
napi_value arg = e.Value();
|
|
144
|
+
napi_call_function(Env(), Env().Undefined(), Callback().Value(), 1, &arg, &result);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
void Execute() {
|
|
148
|
+
#ifdef _WIN32
|
|
149
|
+
int rc = mdb_env_sync(env->env, 1);
|
|
150
|
+
#else
|
|
151
|
+
int retries = 0;
|
|
152
|
+
retry:
|
|
153
|
+
int rc = mdb_env_sync(env->env, 1);
|
|
154
|
+
#ifdef MDB_LOCK_FAILURE
|
|
155
|
+
if (rc == MDB_LOCK_FAILURE) {
|
|
156
|
+
if (retries++ < 4) {
|
|
157
|
+
sleep(1);
|
|
158
|
+
goto retry;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
#endif
|
|
162
|
+
#endif
|
|
163
|
+
if (rc != 0) {
|
|
164
|
+
SetError(mdb_strerror(rc));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private:
|
|
169
|
+
EnvWrap* env;
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
class CopyWorker : public AsyncWorker {
|
|
173
|
+
public:
|
|
174
|
+
CopyWorker(MDB_env* env, std::string inPath, int flags, const Function& callback)
|
|
175
|
+
: AsyncWorker(callback), env(env), path(inPath), flags(flags) {
|
|
176
|
+
}
|
|
177
|
+
~CopyWorker() {
|
|
178
|
+
//free(path);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
void Execute() {
|
|
182
|
+
int rc = mdb_env_copy2(env, path.c_str(), flags);
|
|
183
|
+
if (rc != 0) {
|
|
184
|
+
SetError(mdb_strerror(rc));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private:
|
|
189
|
+
MDB_env* env;
|
|
190
|
+
std::string path;
|
|
191
|
+
int flags;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
MDB_txn* EnvWrap::getReadTxn(int64_t tw_address) {
|
|
195
|
+
MDB_txn* txn;
|
|
196
|
+
if (tw_address) // explicit txn
|
|
197
|
+
txn = ((TxnWrap*)tw_address)->txn;
|
|
198
|
+
else if (writeTxn && (txn = writeTxn->txn)) {
|
|
199
|
+
return txn; // no need to renew write txn
|
|
200
|
+
} else // default to current read txn
|
|
201
|
+
txn = currentReadTxn;
|
|
202
|
+
int rc = mdb_txn_renew(txn); // always try to renew
|
|
203
|
+
if (rc) {
|
|
204
|
+
if (!txn)
|
|
205
|
+
fprintf(stderr, "No current read transaction available");
|
|
206
|
+
if (rc != EINVAL)
|
|
207
|
+
return nullptr; // if there was a real error, signal with nullptr and let error propagate with last_error
|
|
208
|
+
}
|
|
209
|
+
return txn;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
#ifdef MDB_RPAGE_CACHE
|
|
213
|
+
static int encfunc(const MDB_val* src, MDB_val* dst, const MDB_val* key, int encdec)
|
|
214
|
+
{
|
|
215
|
+
chacha8(src->mv_data, src->mv_size, (uint8_t*) key[0].mv_data, (uint8_t*) key[1].mv_data, (char*)dst->mv_data);
|
|
216
|
+
return 0;
|
|
217
|
+
}
|
|
218
|
+
#endif
|
|
219
|
+
|
|
220
|
+
void cleanup(void* data) {
|
|
221
|
+
((EnvWrap*) data)->closeEnv();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
Napi::Value EnvWrap::open(const CallbackInfo& info) {
|
|
225
|
+
int rc;
|
|
226
|
+
// Get the wrapper
|
|
227
|
+
if (!this->env) {
|
|
228
|
+
return throwError(info.Env(), "The environment is already closed.");
|
|
229
|
+
}
|
|
230
|
+
Object options = info[0].As<Object>();
|
|
231
|
+
int flags = info[1].As<Number>();
|
|
232
|
+
int jsFlags = info[2].As<Number>();
|
|
233
|
+
|
|
234
|
+
Compression* compression = nullptr;
|
|
235
|
+
Napi::Value compressionOption = options.Get("compression");
|
|
236
|
+
if (compressionOption.IsObject()) {
|
|
237
|
+
napi_unwrap(info.Env(), compressionOption, (void**)&compression);
|
|
238
|
+
this->compression = compression;
|
|
239
|
+
}
|
|
240
|
+
void* keyBuffer;
|
|
241
|
+
Napi::Value keyBytesValue = options.Get("keyBytes");
|
|
242
|
+
if (!keyBytesValue.IsTypedArray())
|
|
243
|
+
fprintf(stderr, "Invalid key buffer\n");
|
|
244
|
+
size_t keyBufferLength;
|
|
245
|
+
napi_get_buffer_info(info.Env(), keyBytesValue, &keyBuffer, &keyBufferLength);
|
|
246
|
+
String path = options.Get("path").As<String>();
|
|
247
|
+
std::string pathString = path.Utf8Value();
|
|
248
|
+
// Parse the maxDbs option
|
|
249
|
+
int maxDbs = 12;
|
|
250
|
+
Napi::Value option = options.Get("maxDbs");
|
|
251
|
+
if (option.IsNumber())
|
|
252
|
+
maxDbs = option.As<Number>();
|
|
253
|
+
|
|
254
|
+
mdb_size_t mapSize = 0;
|
|
255
|
+
// Parse the mapSize option
|
|
256
|
+
option = options.Get("mapSize");
|
|
257
|
+
if (option.IsNumber())
|
|
258
|
+
mapSize = option.As<Number>().Int64Value();
|
|
259
|
+
int pageSize = 0;
|
|
260
|
+
// Parse the mapSize option
|
|
261
|
+
option = options.Get("pageSize");
|
|
262
|
+
if (option.IsNumber())
|
|
263
|
+
pageSize = option.As<Number>();
|
|
264
|
+
int maxReaders = 126;
|
|
265
|
+
// Parse the mapSize option
|
|
266
|
+
option = options.Get("maxReaders");
|
|
267
|
+
if (option.IsNumber())
|
|
268
|
+
maxReaders = option.As<Number>();
|
|
269
|
+
|
|
270
|
+
Napi::Value encryptionKey = options.Get("encryptionKey");
|
|
271
|
+
std::string encryptKey;
|
|
272
|
+
if (!encryptionKey.IsUndefined()) {
|
|
273
|
+
encryptKey = encryptionKey.As<String>().Utf8Value();
|
|
274
|
+
if (encryptKey.length() != 32) {
|
|
275
|
+
return throwError(info.Env(), "Encryption key must be 32 bytes long");
|
|
276
|
+
}
|
|
277
|
+
#ifndef MDB_RPAGE_CACHE
|
|
278
|
+
return throwError(info.Env(), "Encryption not supported with data format version 1");
|
|
279
|
+
#endif
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
napiEnv = info.Env();
|
|
283
|
+
rc = openEnv(flags, jsFlags, (const char*)pathString.c_str(), (char*) keyBuffer, compression, maxDbs, maxReaders, mapSize, pageSize, encryptKey.empty() ? nullptr : (char*)encryptKey.c_str());
|
|
284
|
+
//delete[] pathBytes;
|
|
285
|
+
if (rc != 0)
|
|
286
|
+
return throwLmdbError(info.Env(), rc);
|
|
287
|
+
napi_add_env_cleanup_hook(napiEnv, cleanup, this);
|
|
288
|
+
return info.Env().Undefined();
|
|
289
|
+
}
|
|
290
|
+
int EnvWrap::openEnv(int flags, int jsFlags, const char* path, char* keyBuffer, Compression* compression, int maxDbs,
|
|
291
|
+
int maxReaders, mdb_size_t mapSize, int pageSize, char* encryptionKey) {
|
|
292
|
+
this->keyBuffer = keyBuffer;
|
|
293
|
+
this->compression = compression;
|
|
294
|
+
this->jsFlags = jsFlags;
|
|
295
|
+
#ifdef MDB_OVERLAPPINGSYNC
|
|
296
|
+
MDB_metrics* metrics;
|
|
297
|
+
#endif
|
|
298
|
+
int rc;
|
|
299
|
+
rc = mdb_env_set_maxdbs(env, maxDbs);
|
|
300
|
+
if (rc) goto fail;
|
|
301
|
+
rc = mdb_env_set_maxreaders(env, maxReaders);
|
|
302
|
+
if (rc) goto fail;
|
|
303
|
+
rc = mdb_env_set_mapsize(env, mapSize);
|
|
304
|
+
if (rc) goto fail;
|
|
305
|
+
#ifdef MDB_RPAGE_CACHE
|
|
306
|
+
if (pageSize)
|
|
307
|
+
rc = mdb_env_set_pagesize(env, pageSize);
|
|
308
|
+
if (rc) goto fail;
|
|
309
|
+
#endif
|
|
310
|
+
if ((size_t) encryptionKey > 100) {
|
|
311
|
+
MDB_val enckey;
|
|
312
|
+
enckey.mv_data = encryptionKey;
|
|
313
|
+
enckey.mv_size = 32;
|
|
314
|
+
#ifdef MDB_RPAGE_CACHE
|
|
315
|
+
rc = mdb_env_set_encrypt(env, encfunc, &enckey, 0);
|
|
316
|
+
#else
|
|
317
|
+
rc = -1;
|
|
318
|
+
#endif
|
|
319
|
+
if (rc != 0) goto fail;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (flags & MDB_NOLOCK) {
|
|
323
|
+
fprintf(stderr, "You chose to use MDB_NOLOCK which is not officially supported by node-lmdb. You have been warned!\n");
|
|
324
|
+
}
|
|
325
|
+
#ifdef MDB_OVERLAPPINGSYNC
|
|
326
|
+
if (flags & MDB_OVERLAPPINGSYNC) {
|
|
327
|
+
flags |= MDB_PREVSNAPSHOT;
|
|
328
|
+
}
|
|
329
|
+
mdb_env_set_callback(env, checkExistingEnvs);
|
|
330
|
+
trackMetrics = !!(flags & MDB_TRACK_METRICS);
|
|
331
|
+
if (trackMetrics) {
|
|
332
|
+
metrics = new MDB_metrics;
|
|
333
|
+
memset(metrics, 0, sizeof(MDB_metrics));
|
|
334
|
+
rc = mdb_env_set_userctx(env, (void*) metrics);
|
|
335
|
+
if (rc) goto fail;
|
|
336
|
+
}
|
|
337
|
+
#endif
|
|
338
|
+
|
|
339
|
+
timeTxnWaiting = 0;
|
|
340
|
+
// Set MDB_NOTLS to enable multiple read-only transactions on the same thread (in this case, the nodejs main thread)
|
|
341
|
+
flags |= MDB_NOTLS;
|
|
342
|
+
// TODO: make file attributes configurable
|
|
343
|
+
// *String::Utf8Value(Isolate::GetCurrent(), path)
|
|
344
|
+
pthread_mutex_lock(envTracking->envsLock);
|
|
345
|
+
rc = mdb_env_open(env, path, flags, 0664);
|
|
346
|
+
|
|
347
|
+
if (rc != 0) {
|
|
348
|
+
#ifdef MDB_OVERLAPPINGSYNC
|
|
349
|
+
if (trackMetrics) {
|
|
350
|
+
delete metrics;
|
|
351
|
+
}
|
|
352
|
+
#endif
|
|
353
|
+
if (rc == EXISTING_ENV_FOUND) {
|
|
354
|
+
mdb_env_close(env);
|
|
355
|
+
env = foundEnv;
|
|
356
|
+
} else {
|
|
357
|
+
this->jsFlags |= OPEN_FAILED;
|
|
358
|
+
closeEnv(true);
|
|
359
|
+
pthread_mutex_unlock(envTracking->envsLock);
|
|
360
|
+
goto fail;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
mdb_env_get_flags(env, (unsigned int*) &flags);
|
|
364
|
+
if ((jsFlags & DELETE_ON_CLOSE)
|
|
365
|
+
#ifdef MDB_OVERLAPPINGSYNC
|
|
366
|
+
|| (flags & MDB_OVERLAPPINGSYNC)
|
|
367
|
+
#endif
|
|
368
|
+
) {
|
|
369
|
+
if (!openEnvWraps) {
|
|
370
|
+
openEnvWraps = new std::vector<EnvWrap*>;
|
|
371
|
+
napi_add_env_cleanup_hook(napiEnv, cleanupEnvWraps, nullptr);
|
|
372
|
+
}
|
|
373
|
+
openEnvWraps->push_back(this);
|
|
374
|
+
}
|
|
375
|
+
pthread_mutex_unlock(envTracking->envsLock);
|
|
376
|
+
return 0;
|
|
377
|
+
|
|
378
|
+
fail:
|
|
379
|
+
env = nullptr;
|
|
380
|
+
return rc;
|
|
381
|
+
}
|
|
382
|
+
Napi::Value EnvWrap::getMaxKeySize(const CallbackInfo& info) {
|
|
383
|
+
return Number::New(info.Env(), mdb_env_get_maxkeysize(this->env));
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
NAPI_FUNCTION(getEnvFlags) {
|
|
387
|
+
ARGS(1)
|
|
388
|
+
GET_INT64_ARG(0);
|
|
389
|
+
EnvWrap* ew = (EnvWrap*) i64;
|
|
390
|
+
unsigned int envFlags;
|
|
391
|
+
mdb_env_get_flags(ew->env, &envFlags);
|
|
392
|
+
RETURN_UINT32(envFlags);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
NAPI_FUNCTION(setJSFlags) {
|
|
396
|
+
ARGS(2)
|
|
397
|
+
GET_INT64_ARG(0);
|
|
398
|
+
EnvWrap* ew = (EnvWrap*) i64;
|
|
399
|
+
int64_t jsFlags;
|
|
400
|
+
napi_get_value_int64(env, args[1], &jsFlags);
|
|
401
|
+
ew->jsFlags = jsFlags;
|
|
402
|
+
RETURN_UNDEFINED;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
#ifdef _WIN32
|
|
406
|
+
// TODO: I think we should switch to DeleteFileW (but have to convert to UTF16)
|
|
407
|
+
#define unlink DeleteFileA
|
|
408
|
+
#else
|
|
409
|
+
#include <unistd.h>
|
|
410
|
+
#endif
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
NAPI_FUNCTION(EnvWrap::onExit) {
|
|
414
|
+
// close all the environments
|
|
415
|
+
if (openEnvWraps) {
|
|
416
|
+
for (auto envWrap : *openEnvWraps)
|
|
417
|
+
envWrap->closeEnv();
|
|
418
|
+
}
|
|
419
|
+
napi_value returnValue;
|
|
420
|
+
RETURN_UNDEFINED;
|
|
421
|
+
}
|
|
422
|
+
NAPI_FUNCTION(getEnvsPointer) {
|
|
423
|
+
napi_value returnValue;
|
|
424
|
+
napi_create_double(env, (double) (size_t) EnvWrap::envTracking, &returnValue);
|
|
425
|
+
if (!EnvWrap::sharedBuffers) {
|
|
426
|
+
EnvWrap::sharedBuffers = new js_buffers_t;
|
|
427
|
+
EnvWrap::sharedBuffers->nextId = 0;
|
|
428
|
+
pthread_mutex_init(&EnvWrap::sharedBuffers->modification_lock, nullptr);
|
|
429
|
+
}
|
|
430
|
+
return returnValue;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
NAPI_FUNCTION(setEnvsPointer) {
|
|
434
|
+
// If another version of lmdb-js is running, switch to using its list of envs
|
|
435
|
+
ARGS(2)
|
|
436
|
+
GET_INT64_ARG(0);
|
|
437
|
+
env_tracking_t* adoptedTracking = (env_tracking_t*) i64;
|
|
438
|
+
// copy any existing ones over to the central one
|
|
439
|
+
adoptedTracking->envs.assign(EnvWrap::envTracking->envs.begin(), EnvWrap::envTracking->envs.end());
|
|
440
|
+
EnvWrap::envTracking = adoptedTracking;
|
|
441
|
+
js_buffers_t* adoptedBuffers = (js_buffers_t*) adoptedTracking->getSharedBuffers();
|
|
442
|
+
if (EnvWrap::sharedBuffers && adoptedBuffers != EnvWrap::sharedBuffers) {
|
|
443
|
+
free(EnvWrap::sharedBuffers);
|
|
444
|
+
}
|
|
445
|
+
EnvWrap::sharedBuffers = adoptedBuffers;
|
|
446
|
+
RETURN_UNDEFINED;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
napi_finalize cleanupSharedExternal = [](napi_env env, void* data, void* buffer_info) {
|
|
450
|
+
// Data belongs to LMDB, we shouldn't free it here
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
napi_finalize cleanupAllocatedExternal = [](napi_env env, void* data, void* buffer_info) {
|
|
454
|
+
int32_t id = ((buffer_info_t*) buffer_info)->id;
|
|
455
|
+
pthread_mutex_lock(&EnvWrap::sharedBuffers->modification_lock);
|
|
456
|
+
for (auto bufferRef = EnvWrap::sharedBuffers->buffers.begin(); bufferRef != EnvWrap::sharedBuffers->buffers.end();) {
|
|
457
|
+
if (bufferRef->second.id == id) {
|
|
458
|
+
// fprintf(stderr, "erasing buffer on cleanpu %p\n", bufferRef->first);
|
|
459
|
+
bufferRef = EnvWrap::sharedBuffers->buffers.erase(bufferRef);
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
bufferRef++;
|
|
463
|
+
}
|
|
464
|
+
pthread_mutex_unlock(&EnvWrap::sharedBuffers->modification_lock);
|
|
465
|
+
// We malloc'ed this data so free it
|
|
466
|
+
free(data);
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
NAPI_FUNCTION(getSharedBuffer) {
|
|
471
|
+
ARGS(2)
|
|
472
|
+
int32_t bufferId;
|
|
473
|
+
GET_UINT32_ARG(bufferId, 0);
|
|
474
|
+
GET_INT64_ARG(1);
|
|
475
|
+
EnvWrap* ew = (EnvWrap*) i64;
|
|
476
|
+
pthread_mutex_lock(&EnvWrap::sharedBuffers->modification_lock);
|
|
477
|
+
for (auto bufferRef = EnvWrap::sharedBuffers->buffers.begin(); bufferRef != EnvWrap::sharedBuffers->buffers.end(); bufferRef++) {
|
|
478
|
+
if (bufferRef->second.id == bufferId) {
|
|
479
|
+
char* start = bufferRef->first;
|
|
480
|
+
buffer_info_t* buffer = &bufferRef->second;
|
|
481
|
+
if (buffer->env == ew->env) {
|
|
482
|
+
//fprintf(stderr, "found exiting buffer for %u\n", bufferId);
|
|
483
|
+
napi_get_reference_value(env, buffer->ref, &returnValue);
|
|
484
|
+
pthread_mutex_unlock(&EnvWrap::sharedBuffers->modification_lock);
|
|
485
|
+
return returnValue;
|
|
486
|
+
}
|
|
487
|
+
if (buffer->env) {
|
|
488
|
+
// if for some reason it is different env that didn't get cleaned up
|
|
489
|
+
napi_value arrayBuffer;
|
|
490
|
+
napi_get_reference_value(env, buffer->ref, &arrayBuffer);
|
|
491
|
+
napi_detach_arraybuffer(env, arrayBuffer);
|
|
492
|
+
napi_delete_reference(env, buffer->ref);
|
|
493
|
+
}
|
|
494
|
+
char* end = buffer->end;
|
|
495
|
+
if (buffer->isSharedMap) // only memory mapped buffers are tied to envs
|
|
496
|
+
buffer->env = ew->env;
|
|
497
|
+
size_t size = end - start;
|
|
498
|
+
if (size > 0x100000000)
|
|
499
|
+
fprintf(stderr, "Getting invalid shared buffer size %llu from start: %llu to %end: %llu", size, start, end);
|
|
500
|
+
napi_create_external_arraybuffer(env, start, size,
|
|
501
|
+
buffer->isSharedMap ? cleanupSharedExternal : cleanupAllocatedExternal, (void*) buffer, &returnValue);
|
|
502
|
+
int64_t result;
|
|
503
|
+
napi_create_reference(env, returnValue, 1, &buffer->ref);
|
|
504
|
+
if (buffer->isSharedMap) {
|
|
505
|
+
napi_adjust_external_memory(env, -(int64_t) size, &result);
|
|
506
|
+
napi_value true_value;
|
|
507
|
+
napi_get_boolean(env, true, &true_value);
|
|
508
|
+
napi_set_named_property(env, returnValue, "isSharedMap", true_value);
|
|
509
|
+
}
|
|
510
|
+
pthread_mutex_unlock(&EnvWrap::sharedBuffers->modification_lock);
|
|
511
|
+
return returnValue;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
pthread_mutex_unlock(&EnvWrap::sharedBuffers->modification_lock);
|
|
515
|
+
RETURN_UNDEFINED;
|
|
516
|
+
}
|
|
517
|
+
NAPI_FUNCTION(setTestRef) {
|
|
518
|
+
ARGS(1)
|
|
519
|
+
napi_create_reference(env, args[0], 1, &testRef);
|
|
520
|
+
testRefEnv = env;
|
|
521
|
+
RETURN_UNDEFINED
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
NAPI_FUNCTION(getTestRef) {
|
|
525
|
+
napi_value returnValue;
|
|
526
|
+
fprintf(stderr,"trying to get refernec\n");
|
|
527
|
+
napi_get_reference_value(env, testRef, &returnValue);
|
|
528
|
+
fprintf(stderr,"got refernec\n");
|
|
529
|
+
return returnValue;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
NAPI_FUNCTION(directWrite) {
|
|
533
|
+
ARGS(4)
|
|
534
|
+
GET_INT64_ARG(0);
|
|
535
|
+
EnvWrap* ew = (EnvWrap*) i64;
|
|
536
|
+
napi_get_value_int64(env, args[1], &i64);
|
|
537
|
+
char* target = (char*) i64;
|
|
538
|
+
napi_get_value_int64(env, args[2], &i64);
|
|
539
|
+
void* source = (void*) i64;
|
|
540
|
+
uint32_t length;
|
|
541
|
+
GET_UINT32_ARG(length, 3);
|
|
542
|
+
mdb_filehandle_t fd;
|
|
543
|
+
mdb_env_get_fd(ew->env, &fd);
|
|
544
|
+
MDB_envinfo stat;
|
|
545
|
+
mdb_env_info(ew->env, &stat);
|
|
546
|
+
int64_t offset = target - (char*) stat.me_mapaddr;
|
|
547
|
+
if (offset > 0 && offset < (int64_t) stat.me_mapsize) {
|
|
548
|
+
#ifdef _WIN32
|
|
549
|
+
OVERLAPPED ov;
|
|
550
|
+
ov.Offset = offset;
|
|
551
|
+
ov.OffsetHigh = 0;
|
|
552
|
+
WriteFile(fd, source, length, nullptr, &ov);
|
|
553
|
+
#else
|
|
554
|
+
pwrite(fd, source, length, offset);
|
|
555
|
+
#endif
|
|
556
|
+
}
|
|
557
|
+
RETURN_UNDEFINED;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
int32_t EnvWrap::toSharedBuffer(MDB_env* env, uint32_t* keyBuffer, MDB_val data) {
|
|
561
|
+
unsigned int flags;
|
|
562
|
+
mdb_env_get_flags(env, (unsigned int*) &flags);
|
|
563
|
+
#ifdef MDB_RPAGE_CACHE
|
|
564
|
+
if (flags & MDB_REMAP_CHUNKS) {
|
|
565
|
+
*((uint32_t*)keyBuffer) = data.mv_size;
|
|
566
|
+
*((uint32_t*) (keyBuffer + 4)) = 0;
|
|
567
|
+
return -30000;
|
|
568
|
+
}
|
|
569
|
+
#endif
|
|
570
|
+
MDB_envinfo stat;
|
|
571
|
+
mdb_env_info(env, &stat);
|
|
572
|
+
size_t mapAddress = (size_t) (char*) stat.me_mapaddr;
|
|
573
|
+
size_t dataAddress = (size_t) (char*) data.mv_data;
|
|
574
|
+
size_t bufferStart;
|
|
575
|
+
uint64_t end;
|
|
576
|
+
if (dataAddress > mapAddress && (dataAddress + data.mv_size) <= (mapAddress + stat.me_mapsize)) {
|
|
577
|
+
// an address within the memory map
|
|
578
|
+
int64_t mapOffset = dataAddress - mapAddress;
|
|
579
|
+
size_t bufferPosition = (mapOffset + (mapOffset >> 4)) >> 32;
|
|
580
|
+
bufferStart = bufferPosition << 32;
|
|
581
|
+
bufferStart += mapAddress - (bufferStart >> 4);
|
|
582
|
+
end = bufferStart + 0xffffffffll;
|
|
583
|
+
if (end > mapAddress + stat.me_mapsize)
|
|
584
|
+
end = mapAddress + stat.me_mapsize;
|
|
585
|
+
} else {
|
|
586
|
+
// outside the memory map, usually because this is from the heap during a write txn or the mmap has been reallocated
|
|
587
|
+
bufferStart = (dataAddress >> 32) << 32;
|
|
588
|
+
if (!bufferStart) // can't use a memory address of 0 because it is considered a nullptr
|
|
589
|
+
bufferStart = 8;
|
|
590
|
+
end = bufferStart + 0xffffffffll;
|
|
591
|
+
}
|
|
592
|
+
if ((dataAddress + data.mv_size) > end) {
|
|
593
|
+
// crosses boundaries, create one-off for this address
|
|
594
|
+
bufferStart = dataAddress;
|
|
595
|
+
end = bufferStart + 0xffffffffll;
|
|
596
|
+
}
|
|
597
|
+
//fprintf(stderr, "mapAddress %p bufferStart %p", mapAddress, bufferStart);
|
|
598
|
+
pthread_mutex_lock(&sharedBuffers->modification_lock);
|
|
599
|
+
auto bufferSearch = sharedBuffers->buffers.find((char*)bufferStart);
|
|
600
|
+
size_t offset = dataAddress - bufferStart;
|
|
601
|
+
buffer_info_t bufferInfo;
|
|
602
|
+
if (bufferSearch == sharedBuffers->buffers.end()) {
|
|
603
|
+
bufferInfo.end = (char*) end;
|
|
604
|
+
bufferInfo.env = nullptr;
|
|
605
|
+
bufferInfo.isSharedMap = true;
|
|
606
|
+
bufferInfo.id = sharedBuffers->nextId++;
|
|
607
|
+
sharedBuffers->buffers.emplace((char*)bufferStart, bufferInfo);
|
|
608
|
+
} else {
|
|
609
|
+
bufferInfo = bufferSearch->second;
|
|
610
|
+
}
|
|
611
|
+
pthread_mutex_unlock(&sharedBuffers->modification_lock);
|
|
612
|
+
*keyBuffer = data.mv_size;
|
|
613
|
+
*(keyBuffer + 1) = bufferInfo.id;
|
|
614
|
+
*(keyBuffer + 2) = offset;
|
|
615
|
+
return -30001;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
void EnvWrap::closeEnv(bool hasLock) {
|
|
619
|
+
if (!env)
|
|
620
|
+
return;
|
|
621
|
+
if (openEnvWraps) {
|
|
622
|
+
for (auto ewRef = openEnvWraps->begin(); ewRef != openEnvWraps->end(); ) {
|
|
623
|
+
if (*ewRef == this) {
|
|
624
|
+
openEnvWraps->erase(ewRef);
|
|
625
|
+
break;
|
|
626
|
+
}
|
|
627
|
+
++ewRef;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
napi_remove_env_cleanup_hook(napiEnv, cleanup, this);
|
|
631
|
+
cleanupStrayTxns();
|
|
632
|
+
if (!hasLock)
|
|
633
|
+
pthread_mutex_lock(envTracking->envsLock);
|
|
634
|
+
for (auto envPath = envTracking->envs.begin(); envPath != envTracking->envs.end(); ) {
|
|
635
|
+
if (envPath->env == env) {
|
|
636
|
+
envPath->count--;
|
|
637
|
+
if (hasWrites)
|
|
638
|
+
envPath->hasWrites = true;
|
|
639
|
+
if (envPath->count <= 0) {
|
|
640
|
+
// last thread using it, we can really close it now
|
|
641
|
+
unsigned int envFlags; // This is primarily useful for detecting termination of threads and sync'ing on their termination
|
|
642
|
+
mdb_env_get_flags(env, &envFlags);
|
|
643
|
+
#ifdef MDB_OVERLAPPINGSYNC
|
|
644
|
+
if ((envFlags & MDB_OVERLAPPINGSYNC) && envPath->hasWrites) {
|
|
645
|
+
mdb_env_sync(env, 1);
|
|
646
|
+
}
|
|
647
|
+
if (envFlags & MDB_TRACK_METRICS) {
|
|
648
|
+
delete (MDB_metrics*) mdb_env_get_userctx(env);
|
|
649
|
+
}
|
|
650
|
+
#endif
|
|
651
|
+
char* path;
|
|
652
|
+
mdb_env_get_path(env, (const char**)&path);
|
|
653
|
+
path = strdup(path);
|
|
654
|
+
mdb_env_close(env);
|
|
655
|
+
pthread_mutex_lock(&sharedBuffers->modification_lock);
|
|
656
|
+
for (auto bufferRef = EnvWrap::sharedBuffers->buffers.begin(); bufferRef != EnvWrap::sharedBuffers->buffers.end();) {
|
|
657
|
+
if (bufferRef->second.env == env) {
|
|
658
|
+
napi_value arrayBuffer;
|
|
659
|
+
napi_get_reference_value(napiEnv, bufferRef->second.ref, &arrayBuffer);
|
|
660
|
+
napi_detach_arraybuffer(napiEnv, arrayBuffer);
|
|
661
|
+
napi_delete_reference(napiEnv, bufferRef->second.ref);
|
|
662
|
+
int64_t result;
|
|
663
|
+
if (bufferRef->second.id >= 0)
|
|
664
|
+
napi_adjust_external_memory(napiEnv, bufferRef->second.end - bufferRef->first, &result);
|
|
665
|
+
bufferRef = EnvWrap::sharedBuffers->buffers.erase(bufferRef);
|
|
666
|
+
} else
|
|
667
|
+
bufferRef++;
|
|
668
|
+
}
|
|
669
|
+
pthread_mutex_unlock(&sharedBuffers->modification_lock);
|
|
670
|
+
if (jsFlags & DELETE_ON_CLOSE) {
|
|
671
|
+
unlink(path);
|
|
672
|
+
//unlink(strcat(envPath->path, "-lock"));
|
|
673
|
+
}
|
|
674
|
+
envTracking->envs.erase(envPath);
|
|
675
|
+
}
|
|
676
|
+
break;
|
|
677
|
+
}
|
|
678
|
+
++envPath;
|
|
679
|
+
}
|
|
680
|
+
if (!hasLock)
|
|
681
|
+
pthread_mutex_unlock(envTracking->envsLock);
|
|
682
|
+
env = nullptr;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
Napi::Value EnvWrap::close(const CallbackInfo& info) {
|
|
686
|
+
if (!this->env) {
|
|
687
|
+
return throwError(info.Env(), "The environment is already closed.");
|
|
688
|
+
}
|
|
689
|
+
this->closeEnv();
|
|
690
|
+
return info.Env().Undefined();
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
Napi::Value EnvWrap::stat(const CallbackInfo& info) {
|
|
694
|
+
if (!this->env) {
|
|
695
|
+
return throwError(info.Env(), "The environment is already closed.");
|
|
696
|
+
}
|
|
697
|
+
int rc;
|
|
698
|
+
MDB_stat stat;
|
|
699
|
+
|
|
700
|
+
rc = mdb_env_stat(this->env, &stat);
|
|
701
|
+
if (rc != 0) {
|
|
702
|
+
return throwLmdbError(info.Env(), rc);
|
|
703
|
+
}
|
|
704
|
+
Object stats = Object::New(info.Env());
|
|
705
|
+
stats.Set("pageSize", Number::New(info.Env(), stat.ms_psize));
|
|
706
|
+
stats.Set("treeDepth", Number::New(info.Env(), stat.ms_depth));
|
|
707
|
+
stats.Set("treeBranchPageCount", Number::New(info.Env(), stat.ms_branch_pages));
|
|
708
|
+
stats.Set("treeLeafPageCount", Number::New(info.Env(), stat.ms_leaf_pages));
|
|
709
|
+
stats.Set("entryCount", Number::New(info.Env(), stat.ms_entries));
|
|
710
|
+
stats.Set("overflowPages", Number::New(info.Env(), stat.ms_overflow_pages));
|
|
711
|
+
return stats;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
Napi::Value EnvWrap::freeStat(const CallbackInfo& info) {
|
|
715
|
+
if (!this->env) {
|
|
716
|
+
return throwError(info.Env(),"The environment is already closed.");
|
|
717
|
+
}
|
|
718
|
+
int rc;
|
|
719
|
+
MDB_stat stat;
|
|
720
|
+
MDB_txn *txn = getReadTxn();
|
|
721
|
+
rc = mdb_stat(txn, 0, &stat);
|
|
722
|
+
if (rc != 0) {
|
|
723
|
+
return throwLmdbError(info.Env(), rc);
|
|
724
|
+
}
|
|
725
|
+
Object stats = Object::New(info.Env());
|
|
726
|
+
stats.Set("pageSize", Number::New(info.Env(), stat.ms_psize));
|
|
727
|
+
stats.Set("treeDepth", Number::New(info.Env(), stat.ms_depth));
|
|
728
|
+
stats.Set("treeBranchPageCount", Number::New(info.Env(), stat.ms_branch_pages));
|
|
729
|
+
stats.Set("treeLeafPageCount", Number::New(info.Env(), stat.ms_leaf_pages));
|
|
730
|
+
stats.Set("entryCount", Number::New(info.Env(), stat.ms_entries));
|
|
731
|
+
stats.Set("overflowPages", Number::New(info.Env(), stat.ms_overflow_pages));
|
|
732
|
+
return stats;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
Napi::Value EnvWrap::info(const CallbackInfo& info) {
|
|
736
|
+
if (!this->env) {
|
|
737
|
+
return throwError(info.Env(),"The environment is already closed.");
|
|
738
|
+
}
|
|
739
|
+
int rc;
|
|
740
|
+
MDB_envinfo envinfo;
|
|
741
|
+
|
|
742
|
+
rc = mdb_env_info(this->env, &envinfo);
|
|
743
|
+
if (rc != 0) {
|
|
744
|
+
return throwLmdbError(info.Env(), rc);
|
|
745
|
+
}
|
|
746
|
+
Object stats = Object::New(info.Env());
|
|
747
|
+
stats.Set("mapSize", Number::New(info.Env(), envinfo.me_mapsize));
|
|
748
|
+
stats.Set("lastPageNumber", Number::New(info.Env(), envinfo.me_last_pgno));
|
|
749
|
+
stats.Set("lastTxnId", Number::New(info.Env(), envinfo.me_last_txnid));
|
|
750
|
+
stats.Set("maxReaders", Number::New(info.Env(), envinfo.me_maxreaders));
|
|
751
|
+
stats.Set("numReaders", Number::New(info.Env(), envinfo.me_numreaders));
|
|
752
|
+
#ifdef MDB_OVERLAPPINGSYNC
|
|
753
|
+
if (this->trackMetrics) {
|
|
754
|
+
MDB_metrics* metrics = (MDB_metrics*) mdb_env_get_userctx(this->env);
|
|
755
|
+
stats.Set("timeStartTxns", Number::New(info.Env(), (double) metrics->time_start_txns / TICKS_PER_SECOND));
|
|
756
|
+
stats.Set("timeDuringTxns", Number::New(info.Env(), (double) metrics->time_during_txns / TICKS_PER_SECOND));
|
|
757
|
+
stats.Set("timePageFlushes", Number::New(info.Env(), (double) metrics->time_page_flushes / TICKS_PER_SECOND));
|
|
758
|
+
stats.Set("timeSync", Number::New(info.Env(), (double) metrics->time_sync / TICKS_PER_SECOND));
|
|
759
|
+
stats.Set("timeTxnWaiting", Number::New(info.Env(), (double) timeTxnWaiting / TICKS_PER_SECOND));
|
|
760
|
+
stats.Set("txns", Number::New(info.Env(), metrics->txns));
|
|
761
|
+
stats.Set("pageFlushes", Number::New(info.Env(), metrics->page_flushes));
|
|
762
|
+
stats.Set("pagesWritten", Number::New(info.Env(), metrics->pages_written));
|
|
763
|
+
stats.Set("writes", Number::New(info.Env(), metrics->writes));
|
|
764
|
+
stats.Set("puts", Number::New(info.Env(), metrics->puts));
|
|
765
|
+
stats.Set("deletes", Number::New(info.Env(), metrics->deletes));
|
|
766
|
+
}
|
|
767
|
+
#endif
|
|
768
|
+
return stats;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
Napi::Value EnvWrap::readerCheck(const CallbackInfo& info) {
|
|
772
|
+
if (!this->env) {
|
|
773
|
+
return throwError(info.Env(), "The environment is already closed.");
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
int rc, dead;
|
|
777
|
+
rc = mdb_reader_check(this->env, &dead);
|
|
778
|
+
if (rc != 0) {
|
|
779
|
+
return throwLmdbError(info.Env(), rc);
|
|
780
|
+
}
|
|
781
|
+
return Number::New(info.Env(), dead);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
Array readerStrings;
|
|
785
|
+
MDB_msg_func* printReaders = ([](const char* message, void* env) -> int {
|
|
786
|
+
readerStrings.Set(readerStrings.Length(), String::New(*(Env*)env, message));
|
|
787
|
+
return 0;
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
Napi::Value EnvWrap::readerList(const CallbackInfo& info) {
|
|
791
|
+
if (!this->env) {
|
|
792
|
+
return throwError(info.Env(), "The environment is already closed.");
|
|
793
|
+
}
|
|
794
|
+
readerStrings = Array::New(info.Env());
|
|
795
|
+
int rc;
|
|
796
|
+
Napi::Env env = info.Env();
|
|
797
|
+
rc = mdb_reader_list(this->env, printReaders, &env);
|
|
798
|
+
if (rc != 0) {
|
|
799
|
+
return throwLmdbError(info.Env(), rc);
|
|
800
|
+
}
|
|
801
|
+
return readerStrings;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
|
|
805
|
+
Napi::Value EnvWrap::copy(const CallbackInfo& info) {
|
|
806
|
+
if (!this->env) {
|
|
807
|
+
return throwError(info.Env(), "The environment is already closed.");
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// Check that the correct number/type of arguments was given.
|
|
811
|
+
if (!info[0].IsString()) {
|
|
812
|
+
return throwError(info.Env(), "Call env.copy(path, compact?, callback) with a file path.");
|
|
813
|
+
}
|
|
814
|
+
if (!info[info.Length() - 1].IsFunction()) {
|
|
815
|
+
return throwError(info.Env(), "Call env.copy(path, compact?, callback) with a file path.");
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
int flags = 0;
|
|
819
|
+
if (info.Length() > 1 && info[1].IsBoolean() && info[1].ToBoolean()) {
|
|
820
|
+
flags = MDB_CP_COMPACT;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
CopyWorker* worker = new CopyWorker(
|
|
824
|
+
this->env, info[0].As<String>().Utf8Value(), flags, info[info.Length() > 2 ? 2 : 1].As<Function>()
|
|
825
|
+
);
|
|
826
|
+
worker->Queue();
|
|
827
|
+
return info.Env().Undefined();
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
Napi::Value EnvWrap::beginTxn(const CallbackInfo& info) {
|
|
831
|
+
int flags = info[0].As<Number>();
|
|
832
|
+
if (!(flags & MDB_RDONLY)) {
|
|
833
|
+
MDB_env *env = this->env;
|
|
834
|
+
unsigned int envFlags;
|
|
835
|
+
mdb_env_get_flags(env, &envFlags);
|
|
836
|
+
MDB_txn *txn;
|
|
837
|
+
|
|
838
|
+
if (this->writeTxn)
|
|
839
|
+
txn = this->writeTxn->txn;
|
|
840
|
+
else if (this->writeWorker) {
|
|
841
|
+
// try to acquire the txn from the current batch
|
|
842
|
+
txn = this->writeWorker->AcquireTxn(&flags);
|
|
843
|
+
} else {
|
|
844
|
+
pthread_mutex_lock(this->writingLock);
|
|
845
|
+
txn = nullptr;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
if (txn) {
|
|
849
|
+
if (flags & TXN_ABORTABLE) {
|
|
850
|
+
if (envFlags & MDB_WRITEMAP)
|
|
851
|
+
flags &= ~TXN_ABORTABLE;
|
|
852
|
+
else {
|
|
853
|
+
// child txn
|
|
854
|
+
mdb_txn_begin(env, txn, flags & 0xf0000, &txn);
|
|
855
|
+
TxnTracked* childTxn = new TxnTracked(txn, flags);
|
|
856
|
+
childTxn->parent = this->writeTxn;
|
|
857
|
+
this->writeTxn = childTxn;
|
|
858
|
+
return info.Env().Undefined();
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
} else {
|
|
862
|
+
mdb_txn_begin(env, nullptr, flags & 0xf0000, &txn);
|
|
863
|
+
flags |= TXN_ABORTABLE;
|
|
864
|
+
}
|
|
865
|
+
this->writeTxn = new TxnTracked(txn, flags);
|
|
866
|
+
return info.Env().Undefined();
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
if (info.Length() > 1) {
|
|
870
|
+
fprintf(stderr, "Invalid number of arguments");
|
|
871
|
+
} else {
|
|
872
|
+
fprintf(stderr, "Invalid number of arguments");
|
|
873
|
+
}
|
|
874
|
+
return info.Env().Undefined();
|
|
875
|
+
}
|
|
876
|
+
Napi::Value EnvWrap::commitTxn(const CallbackInfo& info) {
|
|
877
|
+
TxnTracked *currentTxn = this->writeTxn;
|
|
878
|
+
//fprintf(stderr, "commitTxn %p\n", currentTxn);
|
|
879
|
+
int rc = 0;
|
|
880
|
+
if (currentTxn->flags & TXN_ABORTABLE) {
|
|
881
|
+
//fprintf(stderr, "txn_commit\n");
|
|
882
|
+
rc = mdb_txn_commit(currentTxn->txn);
|
|
883
|
+
}
|
|
884
|
+
this->writeTxn = currentTxn->parent;
|
|
885
|
+
if (!this->writeTxn) {
|
|
886
|
+
//fprintf(stderr, "unlock txn\n");
|
|
887
|
+
if (this->writeWorker)
|
|
888
|
+
this->writeWorker->UnlockTxn();
|
|
889
|
+
else
|
|
890
|
+
pthread_mutex_unlock(this->writingLock);
|
|
891
|
+
}
|
|
892
|
+
delete currentTxn;
|
|
893
|
+
if (rc == 0) {
|
|
894
|
+
hasWrites = true;
|
|
895
|
+
return Napi::Boolean::New(info.Env(), true);
|
|
896
|
+
}
|
|
897
|
+
#ifdef MDB_EMPTY_TXN
|
|
898
|
+
else if (rc == MDB_EMPTY_TXN)
|
|
899
|
+
return Napi::Boolean::New(info.Env(), false);
|
|
900
|
+
#endif
|
|
901
|
+
else
|
|
902
|
+
return throwLmdbError(info.Env(), rc);
|
|
903
|
+
}
|
|
904
|
+
Napi::Value EnvWrap::abortTxn(const CallbackInfo& info) {
|
|
905
|
+
TxnTracked *currentTxn = this->writeTxn;
|
|
906
|
+
if (currentTxn->flags & TXN_ABORTABLE) {
|
|
907
|
+
mdb_txn_abort(currentTxn->txn);
|
|
908
|
+
} else {
|
|
909
|
+
throwError(info.Env(), "Can not abort this transaction");
|
|
910
|
+
}
|
|
911
|
+
this->writeTxn = currentTxn->parent;
|
|
912
|
+
if (!this->writeTxn) {
|
|
913
|
+
if (this->writeWorker)
|
|
914
|
+
this->writeWorker->UnlockTxn();
|
|
915
|
+
else
|
|
916
|
+
pthread_mutex_unlock(this->writingLock);
|
|
917
|
+
}
|
|
918
|
+
delete currentTxn;
|
|
919
|
+
return info.Env().Undefined();
|
|
920
|
+
}
|
|
921
|
+
/*Napi::Value EnvWrap::openDbi(const CallbackInfo& info) {
|
|
922
|
+
|
|
923
|
+
|
|
924
|
+
const unsigned argc = 5;
|
|
925
|
+
Local<Value> argv[argc] = { info.This(), info[0], info[1], info[2], info[3] };
|
|
926
|
+
Nan::MaybeLocal<Object> maybeInstance = Nan::NewInstance(Nan::New(*dbiCtor), argc, argv);
|
|
927
|
+
|
|
928
|
+
// Check if database could be opened
|
|
929
|
+
if ((maybeInstance.IsEmpty())) {
|
|
930
|
+
// The maybeInstance is empty because the dbiCtor called throwError.
|
|
931
|
+
// No need to call that here again, the user will get the error thrown there.
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
Local<Object> instance = maybeInstance.ToLocalChecked();
|
|
936
|
+
DbiWrap *dw = Nan::ObjectWrap::Unwrap<DbiWrap>(instance);
|
|
937
|
+
if (dw->dbi == (MDB_dbi) 0xffffffff)
|
|
938
|
+
info.GetReturnValue().Set(Nan::Undefined());
|
|
939
|
+
else
|
|
940
|
+
info.GetReturnValue().Set(instance);
|
|
941
|
+
}*/
|
|
942
|
+
|
|
943
|
+
Napi::Value EnvWrap::sync(const CallbackInfo& info) {
|
|
944
|
+
|
|
945
|
+
if (!this->env) {
|
|
946
|
+
return throwError(info.Env(), "The environment is already closed.");
|
|
947
|
+
}
|
|
948
|
+
if (info.Length() > 0) {
|
|
949
|
+
SyncWorker* worker = new SyncWorker(this, info[0].As<Function>());
|
|
950
|
+
worker->Queue();
|
|
951
|
+
} else {
|
|
952
|
+
int rc = mdb_env_sync(this->env, 1);
|
|
953
|
+
if (rc != 0) {
|
|
954
|
+
return throwLmdbError(info.Env(), rc);
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
return info.Env().Undefined();
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
int32_t writeFFI(double ewPointer, uint64_t instructionAddress) {
|
|
961
|
+
EnvWrap* ew = (EnvWrap*) (size_t) ewPointer;
|
|
962
|
+
int rc;
|
|
963
|
+
if (instructionAddress)
|
|
964
|
+
rc = WriteWorker::DoWrites(ew->writeTxn->txn, ew, (uint32_t*)instructionAddress, nullptr);
|
|
965
|
+
else {
|
|
966
|
+
pthread_cond_signal(ew->writingCond);
|
|
967
|
+
rc = 0;
|
|
968
|
+
}
|
|
969
|
+
return rc;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
|
|
973
|
+
void EnvWrap::setupExports(Napi::Env env, Object exports) {
|
|
974
|
+
// EnvWrap: Prepare constructor template
|
|
975
|
+
Function EnvClass = ObjectWrap<EnvWrap>::DefineClass(env, "Env", {
|
|
976
|
+
EnvWrap::InstanceMethod("open", &EnvWrap::open),
|
|
977
|
+
EnvWrap::InstanceMethod("getMaxKeySize", &EnvWrap::getMaxKeySize),
|
|
978
|
+
EnvWrap::InstanceMethod("close", &EnvWrap::close),
|
|
979
|
+
EnvWrap::InstanceMethod("beginTxn", &EnvWrap::beginTxn),
|
|
980
|
+
EnvWrap::InstanceMethod("commitTxn", &EnvWrap::commitTxn),
|
|
981
|
+
EnvWrap::InstanceMethod("abortTxn", &EnvWrap::abortTxn),
|
|
982
|
+
EnvWrap::InstanceMethod("sync", &EnvWrap::sync),
|
|
983
|
+
EnvWrap::InstanceMethod("resumeWriting", &EnvWrap::resumeWriting),
|
|
984
|
+
EnvWrap::InstanceMethod("startWriting", &EnvWrap::startWriting),
|
|
985
|
+
EnvWrap::InstanceMethod("stat", &EnvWrap::stat),
|
|
986
|
+
EnvWrap::InstanceMethod("freeStat", &EnvWrap::freeStat),
|
|
987
|
+
EnvWrap::InstanceMethod("info", &EnvWrap::info),
|
|
988
|
+
EnvWrap::InstanceMethod("readerCheck", &EnvWrap::readerCheck),
|
|
989
|
+
EnvWrap::InstanceMethod("readerList", &EnvWrap::readerList),
|
|
990
|
+
EnvWrap::InstanceMethod("copy", &EnvWrap::copy),
|
|
991
|
+
//EnvWrap::InstanceMethod("detachBuffer", &EnvWrap::detachBuffer),
|
|
992
|
+
});
|
|
993
|
+
EXPORT_NAPI_FUNCTION("compress", compress);
|
|
994
|
+
EXPORT_NAPI_FUNCTION("write", write);
|
|
995
|
+
EXPORT_NAPI_FUNCTION("onExit", onExit);
|
|
996
|
+
EXPORT_NAPI_FUNCTION("getEnvsPointer", getEnvsPointer);
|
|
997
|
+
EXPORT_NAPI_FUNCTION("setEnvsPointer", setEnvsPointer);
|
|
998
|
+
EXPORT_NAPI_FUNCTION("getEnvFlags", getEnvFlags);
|
|
999
|
+
EXPORT_NAPI_FUNCTION("setJSFlags", setJSFlags);
|
|
1000
|
+
EXPORT_NAPI_FUNCTION("getSharedBuffer", getSharedBuffer);
|
|
1001
|
+
EXPORT_NAPI_FUNCTION("setTestRef", setTestRef);
|
|
1002
|
+
EXPORT_NAPI_FUNCTION("getTestRef", getTestRef);
|
|
1003
|
+
EXPORT_FUNCTION_ADDRESS("writePtr", writeFFI);
|
|
1004
|
+
//envTpl->InstanceTemplate()->SetInternalFieldCount(1);
|
|
1005
|
+
exports.Set("Env", EnvClass);
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
// This file contains code from the node-lmdb project
|
|
1009
|
+
// Copyright (c) 2013-2017 Timur Kristóf
|
|
1010
|
+
// Copyright (c) 2021 Kristopher Tate
|
|
1011
|
+
// Licensed to you under the terms of the MIT license
|
|
1012
|
+
//
|
|
1013
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
1014
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
1015
|
+
// in the Software without restriction, including without limitation the rights
|
|
1016
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
1017
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
1018
|
+
// furnished to do so, subject to the following conditions:
|
|
1019
|
+
|
|
1020
|
+
// The above copyright notice and this permission notice shall be included in
|
|
1021
|
+
// all copies or substantial portions of the Software.
|
|
1022
|
+
|
|
1023
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
1024
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
1025
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
1026
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
1027
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
1028
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
1029
|
+
// THE SOFTWARE.
|
|
1030
|
+
|