@nxtedition/rocksdb 7.0.63 → 7.0.64
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/binding.cc +431 -905
- package/deps/rocksdb/rocksdb/db/wal_manager.cc +4 -0
- package/index.js +47 -131
- package/package.json +1 -1
- package/prebuilds/darwin-arm64/node.napi.node +0 -0
- package/util.h +338 -0
|
@@ -120,6 +120,10 @@ Status WalManager::GetUpdatesSince(
|
|
|
120
120
|
return s;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
std::sort(std::begin(*wal_files), std::end(*wal_files), [](auto& lhs, auto& rhs) {
|
|
124
|
+
return lhs->StartSequence() > rhs->StartSequence();
|
|
125
|
+
});
|
|
126
|
+
|
|
123
127
|
s = RetainProbableWalFiles(*wal_files, seq);
|
|
124
128
|
if (!s.ok()) {
|
|
125
129
|
return s;
|
package/index.js
CHANGED
|
@@ -17,6 +17,10 @@ const kColumns = Symbol('columns')
|
|
|
17
17
|
const kLocation = Symbol('location')
|
|
18
18
|
const kPromise = Symbol('promise')
|
|
19
19
|
const kUpdates = Symbol('updates')
|
|
20
|
+
const kRef = Symbol('ref')
|
|
21
|
+
const kUnref = Symbol('unref')
|
|
22
|
+
const kRefs = Symbol('refs')
|
|
23
|
+
const kPendingClose = Symbol('pendingClose')
|
|
20
24
|
|
|
21
25
|
const EMPTY = {}
|
|
22
26
|
|
|
@@ -66,6 +70,9 @@ class RocksLevel extends AbstractLevel {
|
|
|
66
70
|
this[kContext] = binding.db_init()
|
|
67
71
|
this[kColumns] = {}
|
|
68
72
|
|
|
73
|
+
this[kRefs] = 0
|
|
74
|
+
this[kPendingClose] = null
|
|
75
|
+
|
|
69
76
|
// .updates(...) uses 'update' listener.
|
|
70
77
|
this.setMaxListeners(100)
|
|
71
78
|
}
|
|
@@ -101,8 +108,23 @@ class RocksLevel extends AbstractLevel {
|
|
|
101
108
|
}
|
|
102
109
|
}
|
|
103
110
|
|
|
111
|
+
[kRef] () {
|
|
112
|
+
this[kRefs]++
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
[kUnref] () {
|
|
116
|
+
this[kRefs]--
|
|
117
|
+
if (this[kRefs] === 0 && this[kPendingClose]) {
|
|
118
|
+
this[kPendingClose]()
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
104
122
|
_close (callback) {
|
|
105
|
-
|
|
123
|
+
if (this[kRefs]) {
|
|
124
|
+
this[kPendingClose] = callback
|
|
125
|
+
} else {
|
|
126
|
+
binding.db_close(this[kContext], callback)
|
|
127
|
+
}
|
|
106
128
|
}
|
|
107
129
|
|
|
108
130
|
_put (key, value, options, callback) {
|
|
@@ -137,9 +159,14 @@ class RocksLevel extends AbstractLevel {
|
|
|
137
159
|
callback = fromCallback(callback, kPromise)
|
|
138
160
|
|
|
139
161
|
try {
|
|
140
|
-
|
|
162
|
+
this[kRef]()
|
|
163
|
+
binding.db_get_many(this[kContext], keys, options ?? EMPTY, (err, val) => {
|
|
164
|
+
callback(err, val)
|
|
165
|
+
this[kUnref]()
|
|
166
|
+
})
|
|
141
167
|
} catch (err) {
|
|
142
168
|
process.nextTick(callback, err)
|
|
169
|
+
this[kUnref]()
|
|
143
170
|
}
|
|
144
171
|
|
|
145
172
|
return callback[kPromise]
|
|
@@ -222,36 +249,6 @@ class RocksLevel extends AbstractLevel {
|
|
|
222
249
|
return binding.db_get_property(this[kContext], property)
|
|
223
250
|
}
|
|
224
251
|
|
|
225
|
-
async getCurrentWALFile () {
|
|
226
|
-
if (this.status !== 'open') {
|
|
227
|
-
throw new ModuleError('Database is not open', {
|
|
228
|
-
code: 'LEVEL_DATABASE_NOT_OPEN'
|
|
229
|
-
})
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
return binding.db_get_current_wal_file(this[kContext])
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
async getSortedWALFiles () {
|
|
236
|
-
if (this.status !== 'open') {
|
|
237
|
-
throw new ModuleError('Database is not open', {
|
|
238
|
-
code: 'LEVEL_DATABASE_NOT_OPEN'
|
|
239
|
-
})
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
return binding.db_get_sorted_wal_files(this[kContext])
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
async flushWAL (options) {
|
|
246
|
-
if (this.status !== 'open') {
|
|
247
|
-
throw new ModuleError('Database is not open', {
|
|
248
|
-
code: 'LEVEL_DATABASE_NOT_OPEN'
|
|
249
|
-
})
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
binding.db_flush_wal(this[kContext], options)
|
|
253
|
-
}
|
|
254
|
-
|
|
255
252
|
async query (options) {
|
|
256
253
|
if (this.status !== 'open') {
|
|
257
254
|
throw new ModuleError('Database is not open', {
|
|
@@ -259,17 +256,9 @@ class RocksLevel extends AbstractLevel {
|
|
|
259
256
|
})
|
|
260
257
|
}
|
|
261
258
|
|
|
262
|
-
const context = binding.iterator_init(this[kContext], options)
|
|
263
|
-
const resource = {
|
|
264
|
-
callback: null,
|
|
265
|
-
close (callback) {
|
|
266
|
-
this.callback = callback
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
259
|
+
const context = binding.iterator_init(this[kContext], options ?? {})
|
|
270
260
|
try {
|
|
271
|
-
this
|
|
272
|
-
|
|
261
|
+
this[kRef]()
|
|
273
262
|
const limit = options.limit ?? 1000
|
|
274
263
|
return await new Promise((resolve, reject) => binding.iterator_nextv(context, limit, (err, rows, finished) => {
|
|
275
264
|
if (err) {
|
|
@@ -283,11 +272,8 @@ class RocksLevel extends AbstractLevel {
|
|
|
283
272
|
}
|
|
284
273
|
}))
|
|
285
274
|
} finally {
|
|
286
|
-
this.detachResource(resource)
|
|
287
275
|
binding.iterator_close(context)
|
|
288
|
-
|
|
289
|
-
resource.callback()
|
|
290
|
-
}
|
|
276
|
+
this[kUnref]()
|
|
291
277
|
}
|
|
292
278
|
}
|
|
293
279
|
|
|
@@ -347,29 +333,6 @@ class RocksLevel extends AbstractLevel {
|
|
|
347
333
|
|
|
348
334
|
const db = this
|
|
349
335
|
|
|
350
|
-
async function * _updates (options) {
|
|
351
|
-
let first = true
|
|
352
|
-
for await (const update of db[kUpdates]({ ...options, since: Math.max(0, options.since - 1024) })) {
|
|
353
|
-
if (first) {
|
|
354
|
-
if (update.sequence > options.since) {
|
|
355
|
-
// HACK
|
|
356
|
-
db.emit('warning', `Invalid update sequence ${update.sequence} > ${options.since}. Starting from 0.`)
|
|
357
|
-
first = null
|
|
358
|
-
break
|
|
359
|
-
} else {
|
|
360
|
-
first = false
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
yield update
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
if (first === null) {
|
|
367
|
-
for await (const update of db[kUpdates]({ ...options, since: 0 })) {
|
|
368
|
-
yield update
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
336
|
try {
|
|
374
337
|
let since = options.since
|
|
375
338
|
while (true) {
|
|
@@ -401,15 +364,12 @@ class RocksLevel extends AbstractLevel {
|
|
|
401
364
|
try {
|
|
402
365
|
if (since <= db.sequence) {
|
|
403
366
|
let first = true
|
|
404
|
-
for await (const update of
|
|
367
|
+
for await (const update of db[kUpdates]({
|
|
405
368
|
...options,
|
|
369
|
+
signal: ac.signal,
|
|
406
370
|
// HACK: https://github.com/facebook/rocksdb/issues/10476
|
|
407
|
-
since: Math.max(0, options.since
|
|
371
|
+
since: Math.max(0, options.since)
|
|
408
372
|
})) {
|
|
409
|
-
if (ac.signal.aborted) {
|
|
410
|
-
throw new AbortError()
|
|
411
|
-
}
|
|
412
|
-
|
|
413
373
|
if (first) {
|
|
414
374
|
if (update.sequence > since) {
|
|
415
375
|
db.emit('warning', `Invalid updates sequence ${update.sequence} > ${options.since}.`)
|
|
@@ -435,10 +395,6 @@ class RocksLevel extends AbstractLevel {
|
|
|
435
395
|
|
|
436
396
|
let first = true
|
|
437
397
|
for await (const update of buffer) {
|
|
438
|
-
if (ac.signal.aborted) {
|
|
439
|
-
throw new AbortError()
|
|
440
|
-
}
|
|
441
|
-
|
|
442
398
|
if (first) {
|
|
443
399
|
if (update.sequence > since) {
|
|
444
400
|
db.emit('warning', `Invalid batch sequence ${update.sequence} > ${options.since}.`)
|
|
@@ -457,72 +413,32 @@ class RocksLevel extends AbstractLevel {
|
|
|
457
413
|
}
|
|
458
414
|
}
|
|
459
415
|
|
|
460
|
-
async * [kUpdates] (options) {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
this.db.attachResource(this)
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
async next () {
|
|
471
|
-
if (this.closed) {
|
|
472
|
-
return {}
|
|
416
|
+
async * [kUpdates] ({ signal, ...options }) {
|
|
417
|
+
const context = binding.updates_init(this[kContext], options)
|
|
418
|
+
this[kRef]()
|
|
419
|
+
try {
|
|
420
|
+
while (true) {
|
|
421
|
+
if (signal?.aborted) {
|
|
422
|
+
throw new AbortError()
|
|
473
423
|
}
|
|
474
424
|
|
|
475
|
-
|
|
476
|
-
this.promise = null
|
|
425
|
+
const entry = await new Promise((resolve, reject) => binding.updates_next(context, (err, rows, sequence, count) => {
|
|
477
426
|
if (err) {
|
|
478
|
-
|
|
427
|
+
reject(err)
|
|
479
428
|
} else {
|
|
480
429
|
resolve({ rows, sequence, count })
|
|
481
430
|
}
|
|
482
431
|
}))
|
|
483
432
|
|
|
484
|
-
return this.promise
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
async close (callback) {
|
|
488
|
-
try {
|
|
489
|
-
await this.promise
|
|
490
|
-
} catch {
|
|
491
|
-
// Do nothing...
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
try {
|
|
495
|
-
if (!this.closed) {
|
|
496
|
-
this.closed = true
|
|
497
|
-
binding.updates_close(this.context)
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
if (callback) {
|
|
501
|
-
process.nextTick(callback)
|
|
502
|
-
}
|
|
503
|
-
} catch (err) {
|
|
504
|
-
if (callback) {
|
|
505
|
-
process.nextTick(callback, err)
|
|
506
|
-
} else {
|
|
507
|
-
throw err
|
|
508
|
-
}
|
|
509
|
-
} finally {
|
|
510
|
-
this.db.detachResource(this)
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
const updates = new Updates(this, options)
|
|
516
|
-
try {
|
|
517
|
-
while (true) {
|
|
518
|
-
const entry = await updates.next()
|
|
519
433
|
if (!entry.rows) {
|
|
520
434
|
return
|
|
521
435
|
}
|
|
436
|
+
|
|
522
437
|
yield entry
|
|
523
438
|
}
|
|
524
439
|
} finally {
|
|
525
|
-
|
|
440
|
+
binding.updates_close(context)
|
|
441
|
+
this[kUnref]()
|
|
526
442
|
}
|
|
527
443
|
}
|
|
528
444
|
}
|
package/package.json
CHANGED
|
Binary file
|
package/util.h
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <assert.h>
|
|
4
|
+
#include <napi-macros.h>
|
|
5
|
+
#include <node_api.h>
|
|
6
|
+
|
|
7
|
+
#include <rocksdb/db.h>
|
|
8
|
+
#include <rocksdb/slice.h>
|
|
9
|
+
#include <rocksdb/status.h>
|
|
10
|
+
|
|
11
|
+
#include <array>
|
|
12
|
+
#include <optional>
|
|
13
|
+
#include <string>
|
|
14
|
+
|
|
15
|
+
#define NAPI_STATUS_RETURN(call) \
|
|
16
|
+
{ \
|
|
17
|
+
auto _status = (call); \
|
|
18
|
+
if (_status != napi_ok) { \
|
|
19
|
+
return _status; \
|
|
20
|
+
} \
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#define ROCKS_STATUS_THROWS_NAPI(call) \
|
|
24
|
+
{ \
|
|
25
|
+
auto _status = (call); \
|
|
26
|
+
if (!_status.ok()) { \
|
|
27
|
+
napi_throw(env, ToError(env, _status)); \
|
|
28
|
+
return NULL; \
|
|
29
|
+
} \
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
#define ROCKS_STATUS_RETURN_NAPI(call) \
|
|
33
|
+
{ \
|
|
34
|
+
auto _status = (call); \
|
|
35
|
+
if (!_status.ok()) { \
|
|
36
|
+
napi_throw(env, ToError(env, _status)); \
|
|
37
|
+
return napi_pending_exception; \
|
|
38
|
+
} \
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
template <typename T>
|
|
42
|
+
static void Finalize(napi_env env, void* data, void* hint) {
|
|
43
|
+
if (hint) {
|
|
44
|
+
delete reinterpret_cast<T*>(hint);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static napi_value CreateError(napi_env env, const std::optional<std::string_view>& code, const std::string_view& msg) {
|
|
49
|
+
napi_value codeValue = nullptr;
|
|
50
|
+
if (code) {
|
|
51
|
+
NAPI_STATUS_THROWS(napi_create_string_utf8(env, code->data(), code->size(), &codeValue));
|
|
52
|
+
}
|
|
53
|
+
napi_value msgValue;
|
|
54
|
+
NAPI_STATUS_THROWS(napi_create_string_utf8(env, msg.data(), msg.size(), &msgValue));
|
|
55
|
+
napi_value error;
|
|
56
|
+
NAPI_STATUS_THROWS(napi_create_error(env, codeValue, msgValue, &error));
|
|
57
|
+
return error;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static napi_value ToError(napi_env env, const rocksdb::Status& status) {
|
|
61
|
+
if (status.ok()) {
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const auto msg = status.ToString();
|
|
66
|
+
|
|
67
|
+
if (status.IsNotFound()) {
|
|
68
|
+
return CreateError(env, "LEVEL_NOT_FOUND", msg);
|
|
69
|
+
} else if (status.IsCorruption()) {
|
|
70
|
+
return CreateError(env, "LEVEL_CORRUPTION", msg);
|
|
71
|
+
} else if (status.IsTryAgain()) {
|
|
72
|
+
return CreateError(env, "LEVEL_TRYAGAIN", msg);
|
|
73
|
+
} else if (status.IsIOError()) {
|
|
74
|
+
if (msg.find("IO error: lock ") != std::string::npos) { // env_posix.cc
|
|
75
|
+
return CreateError(env, "LEVEL_LOCKED", msg);
|
|
76
|
+
} else if (msg.find("IO error: LockFile ") != std::string::npos) { // env_win.cc
|
|
77
|
+
return CreateError(env, "LEVEL_LOCKED", msg);
|
|
78
|
+
} else if (msg.find("IO error: While lock file") != std::string::npos) { // env_mac.cc
|
|
79
|
+
return CreateError(env, "LEVEL_LOCKED", msg);
|
|
80
|
+
} else {
|
|
81
|
+
return CreateError(env, "LEVEL_IO_ERROR", msg);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return CreateError(env, {}, msg);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
static napi_status GetString(napi_env env, napi_value from, std::string& to) {
|
|
89
|
+
napi_valuetype type;
|
|
90
|
+
NAPI_STATUS_RETURN(napi_typeof(env, from, &type));
|
|
91
|
+
|
|
92
|
+
if (type == napi_string) {
|
|
93
|
+
size_t length = 0;
|
|
94
|
+
NAPI_STATUS_RETURN(napi_get_value_string_utf8(env, from, nullptr, 0, &length));
|
|
95
|
+
to.resize(length, '\0');
|
|
96
|
+
NAPI_STATUS_RETURN(napi_get_value_string_utf8(env, from, &to[0], length + 1, &length));
|
|
97
|
+
} else {
|
|
98
|
+
bool isBuffer;
|
|
99
|
+
NAPI_STATUS_RETURN(napi_is_buffer(env, from, &isBuffer));
|
|
100
|
+
|
|
101
|
+
if (isBuffer) {
|
|
102
|
+
char* buf = nullptr;
|
|
103
|
+
size_t length = 0;
|
|
104
|
+
NAPI_STATUS_RETURN(napi_get_buffer_info(env, from, reinterpret_cast<void**>(&buf), &length));
|
|
105
|
+
to.assign(buf, length);
|
|
106
|
+
} else {
|
|
107
|
+
return napi_invalid_arg;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return napi_ok;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
void DestroyReference(void* arg1, void* arg2) {
|
|
115
|
+
auto env = reinterpret_cast<napi_env>(arg1);
|
|
116
|
+
auto ref = reinterpret_cast<napi_ref>(arg2);
|
|
117
|
+
napi_delete_reference(env, ref);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
static napi_status GetString(napi_env env, napi_value from, rocksdb::PinnableSlice& to) {
|
|
121
|
+
napi_valuetype type;
|
|
122
|
+
NAPI_STATUS_RETURN(napi_typeof(env, from, &type));
|
|
123
|
+
|
|
124
|
+
if (type == napi_string) {
|
|
125
|
+
size_t length = 0;
|
|
126
|
+
NAPI_STATUS_RETURN(napi_get_value_string_utf8(env, from, nullptr, 0, &length));
|
|
127
|
+
to.GetSelf()->resize(length, '\0');
|
|
128
|
+
NAPI_STATUS_RETURN(napi_get_value_string_utf8(env, from, &(*to.GetSelf())[0], length + 1, &length));
|
|
129
|
+
to.PinSelf();
|
|
130
|
+
} else {
|
|
131
|
+
bool isBuffer;
|
|
132
|
+
NAPI_STATUS_RETURN(napi_is_buffer(env, from, &isBuffer));
|
|
133
|
+
|
|
134
|
+
if (isBuffer) {
|
|
135
|
+
char* buf = nullptr;
|
|
136
|
+
size_t length = 0;
|
|
137
|
+
NAPI_STATUS_RETURN(napi_get_buffer_info(env, from, reinterpret_cast<void**>(&buf), &length));
|
|
138
|
+
|
|
139
|
+
napi_ref ref;
|
|
140
|
+
NAPI_STATUS_RETURN(napi_create_reference(env, from, 1, &ref));
|
|
141
|
+
to.PinSlice(rocksdb::Slice(buf, length), DestroyReference, env, ref);
|
|
142
|
+
} else {
|
|
143
|
+
return napi_invalid_arg;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return napi_ok;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
enum class Encoding { Invalid, Buffer, String };
|
|
151
|
+
|
|
152
|
+
static napi_status GetValue(napi_env env, napi_value value, bool& result) {
|
|
153
|
+
return napi_get_value_bool(env, value, &result);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
static napi_status GetValue(napi_env env, napi_value value, uint32_t& result) {
|
|
157
|
+
return napi_get_value_uint32(env, value, &result);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
static napi_status GetValue(napi_env env, napi_value value, int32_t& result) {
|
|
161
|
+
return napi_get_value_int32(env, value, &result);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
static napi_status GetValue(napi_env env, napi_value value, int64_t& result) {
|
|
165
|
+
return napi_get_value_int64(env, value, &result);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
static napi_status GetValue(napi_env env, napi_value value, uint64_t& result) {
|
|
169
|
+
int64_t result2;
|
|
170
|
+
NAPI_STATUS_RETURN(napi_get_value_int64(env, value, &result2));
|
|
171
|
+
result = static_cast<uint64_t>(result2);
|
|
172
|
+
return napi_ok;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
static napi_status GetValue(napi_env env, napi_value value, unsigned long& result) {
|
|
176
|
+
int64_t result2;
|
|
177
|
+
NAPI_STATUS_RETURN(napi_get_value_int64(env, value, &result2));
|
|
178
|
+
result = static_cast<unsigned long>(result2);
|
|
179
|
+
return napi_ok;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
static napi_status GetValue(napi_env env, napi_value value, std::string& result) {
|
|
183
|
+
return GetString(env, value, result);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
static napi_status GetValue(napi_env env, napi_value value, rocksdb::PinnableSlice& result) {
|
|
187
|
+
return GetString(env, value, result);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
static napi_status GetValue(napi_env env, napi_value value, rocksdb::ColumnFamilyHandle*& result) {
|
|
191
|
+
return napi_get_value_external(env, value, reinterpret_cast<void**>(&result));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
static napi_status GetValue(napi_env env, napi_value value, Encoding& result) {
|
|
195
|
+
size_t size;
|
|
196
|
+
NAPI_STATUS_RETURN(napi_get_value_string_utf8(env, value, nullptr, 0, &size));
|
|
197
|
+
|
|
198
|
+
if (size == 6) {
|
|
199
|
+
result = Encoding::Buffer;
|
|
200
|
+
} else {
|
|
201
|
+
result = Encoding::String;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return napi_ok;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
template <typename T>
|
|
208
|
+
static napi_status GetValue(napi_env env, napi_value value, std::optional<T>& result) {
|
|
209
|
+
result = T{};
|
|
210
|
+
return GetValue(env, value, *result);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
template <typename T>
|
|
214
|
+
static napi_status GetProperty(napi_env env,
|
|
215
|
+
napi_value obj,
|
|
216
|
+
const std::string_view& key,
|
|
217
|
+
T& result,
|
|
218
|
+
bool required = false) {
|
|
219
|
+
bool has = false;
|
|
220
|
+
NAPI_STATUS_RETURN(napi_has_named_property(env, obj, key.data(), &has));
|
|
221
|
+
|
|
222
|
+
if (!has) {
|
|
223
|
+
return required ? napi_invalid_arg : napi_ok;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
napi_value value;
|
|
227
|
+
NAPI_STATUS_RETURN(napi_get_named_property(env, obj, key.data(), &value));
|
|
228
|
+
|
|
229
|
+
bool nully = false;
|
|
230
|
+
|
|
231
|
+
napi_value nullVal;
|
|
232
|
+
NAPI_STATUS_RETURN(napi_get_null(env, &nullVal));
|
|
233
|
+
NAPI_STATUS_RETURN(napi_strict_equals(env, nullVal, value, &nully));
|
|
234
|
+
if (nully) {
|
|
235
|
+
return required ? napi_invalid_arg : napi_ok;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
napi_value undefinedVal;
|
|
239
|
+
NAPI_STATUS_RETURN(napi_get_undefined(env, &undefinedVal));
|
|
240
|
+
NAPI_STATUS_RETURN(napi_strict_equals(env, undefinedVal, value, &nully));
|
|
241
|
+
if (nully) {
|
|
242
|
+
return required ? napi_invalid_arg : napi_ok;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return GetValue(env, value, result);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
template <typename T>
|
|
249
|
+
napi_status Convert(napi_env env, T&& s, Encoding encoding, napi_value& result) {
|
|
250
|
+
if (!s) {
|
|
251
|
+
return napi_get_null(env, &result);
|
|
252
|
+
} else if (encoding == Encoding::Buffer) {
|
|
253
|
+
// napi_create_external_buffer would be nice but is unsafe since node
|
|
254
|
+
// buffers are not read-only.
|
|
255
|
+
return napi_create_buffer_copy(env, s->size(), s->data(), nullptr, &result);
|
|
256
|
+
} else if (encoding == Encoding::String) {
|
|
257
|
+
return napi_create_string_utf8(env, s->data(), s->size(), &result);
|
|
258
|
+
} else {
|
|
259
|
+
return napi_invalid_arg;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
template <typename State, typename T1, typename T2>
|
|
264
|
+
napi_status runAsync(const std::string& name, napi_env env, napi_value callback, T1&& execute, T2&& then) {
|
|
265
|
+
struct Worker final {
|
|
266
|
+
static void Execute(napi_env env, void* data) {
|
|
267
|
+
auto worker = reinterpret_cast<Worker*>(data);
|
|
268
|
+
worker->status = worker->execute(worker->state);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
static void Complete(napi_env env, napi_status status, void* data) {
|
|
272
|
+
auto worker = std::unique_ptr<Worker>(reinterpret_cast<Worker*>(data));
|
|
273
|
+
|
|
274
|
+
napi_value callback;
|
|
275
|
+
NAPI_STATUS_THROWS_VOID(napi_get_reference_value(env, worker->callbackRef, &callback));
|
|
276
|
+
|
|
277
|
+
napi_value global;
|
|
278
|
+
NAPI_STATUS_THROWS_VOID(napi_get_global(env, &global));
|
|
279
|
+
|
|
280
|
+
if (worker->status.ok()) {
|
|
281
|
+
std::vector<napi_value> argv;
|
|
282
|
+
|
|
283
|
+
argv.resize(1);
|
|
284
|
+
NAPI_STATUS_THROWS_VOID(napi_get_null(env, &argv[0]));
|
|
285
|
+
|
|
286
|
+
const auto ret = worker->then(worker->state, env, argv);
|
|
287
|
+
|
|
288
|
+
if (ret == napi_ok) {
|
|
289
|
+
NAPI_STATUS_THROWS_VOID(napi_call_function(env, global, callback, argv.size(), argv.data(), nullptr));
|
|
290
|
+
} else {
|
|
291
|
+
const napi_extended_error_info* errInfo = nullptr;
|
|
292
|
+
NAPI_STATUS_THROWS_VOID(napi_get_last_error_info(env, &errInfo));
|
|
293
|
+
auto err = CreateError(env, std::nullopt,
|
|
294
|
+
!errInfo || !errInfo->error_message ? "empty error message" : errInfo->error_message);
|
|
295
|
+
NAPI_STATUS_THROWS_VOID(napi_call_function(env, global, callback, 1, &err, nullptr));
|
|
296
|
+
}
|
|
297
|
+
} else {
|
|
298
|
+
auto err = ToError(env, worker->status);
|
|
299
|
+
NAPI_STATUS_THROWS_VOID(napi_call_function(env, global, callback, 1, &err, nullptr));
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
~Worker() {
|
|
304
|
+
if (callbackRef) {
|
|
305
|
+
napi_delete_reference(env, callbackRef);
|
|
306
|
+
callbackRef = nullptr;
|
|
307
|
+
}
|
|
308
|
+
if (asyncWork) {
|
|
309
|
+
napi_delete_async_work(env, asyncWork);
|
|
310
|
+
asyncWork = nullptr;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
napi_env env = nullptr;
|
|
315
|
+
|
|
316
|
+
typename std::decay<T1>::type execute;
|
|
317
|
+
typename std::decay<T2>::type then;
|
|
318
|
+
|
|
319
|
+
napi_ref callbackRef = nullptr;
|
|
320
|
+
napi_async_work asyncWork = nullptr;
|
|
321
|
+
rocksdb::Status status = rocksdb::Status::OK();
|
|
322
|
+
State state = State();
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
auto worker = std::unique_ptr<Worker>(new Worker{env, std::forward<T1>(execute), std::forward<T2>(then)});
|
|
326
|
+
|
|
327
|
+
NAPI_STATUS_RETURN(napi_create_reference(env, callback, 1, &worker->callbackRef));
|
|
328
|
+
napi_value asyncResourceName;
|
|
329
|
+
NAPI_STATUS_RETURN(napi_create_string_utf8(env, name.data(), name.size(), &asyncResourceName));
|
|
330
|
+
NAPI_STATUS_RETURN(napi_create_async_work(env, callback, asyncResourceName, Worker::Execute, Worker::Complete,
|
|
331
|
+
worker.get(), &worker->asyncWork));
|
|
332
|
+
|
|
333
|
+
NAPI_STATUS_RETURN(napi_queue_async_work(env, worker->asyncWork));
|
|
334
|
+
|
|
335
|
+
worker.release();
|
|
336
|
+
|
|
337
|
+
return napi_ok;
|
|
338
|
+
}
|