@nxtedition/rocksdb 5.2.3 → 5.2.10
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 +425 -85
- package/binding.gyp +4 -3
- package/chained-batch.js +13 -18
- package/deps/rocksdb/rocksdb.gyp +44 -62
- package/index.js +92 -0
- package/iterator.js +88 -24
- package/package.json +21 -16
- package/leveldown.js +0 -113
package/binding.cc
CHANGED
|
@@ -18,6 +18,9 @@ namespace leveldb = rocksdb;
|
|
|
18
18
|
|
|
19
19
|
#include <set>
|
|
20
20
|
#include <optional>
|
|
21
|
+
#include <memory>
|
|
22
|
+
#include <string>
|
|
23
|
+
#include <string_view>
|
|
21
24
|
#include <vector>
|
|
22
25
|
|
|
23
26
|
class NullLogger : public rocksdb::Logger {
|
|
@@ -61,30 +64,40 @@ static bool IsObject (napi_env env, napi_value value) {
|
|
|
61
64
|
return type == napi_object;
|
|
62
65
|
}
|
|
63
66
|
|
|
64
|
-
static napi_value CreateError (napi_env env, const std::
|
|
67
|
+
static napi_value CreateError (napi_env env, const std::string_view& str) {
|
|
65
68
|
napi_value msg;
|
|
66
|
-
napi_create_string_utf8(env, str.
|
|
69
|
+
napi_create_string_utf8(env, str.data(), str.size(), &msg);
|
|
67
70
|
napi_value error;
|
|
68
71
|
napi_create_error(env, nullptr, msg, &error);
|
|
69
72
|
return error;
|
|
70
73
|
}
|
|
71
74
|
|
|
72
|
-
static
|
|
75
|
+
static napi_value CreateCodeError (napi_env env, const std::string_view& code, const std::string_view& msg) {
|
|
76
|
+
napi_value codeValue;
|
|
77
|
+
napi_create_string_utf8(env, code.data(), code.size(), &codeValue);
|
|
78
|
+
napi_value msgValue;
|
|
79
|
+
napi_create_string_utf8(env, msg.data(), msg.size(), &msgValue);
|
|
80
|
+
napi_value error;
|
|
81
|
+
napi_create_error(env, codeValue, msgValue, &error);
|
|
82
|
+
return error;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
static bool HasProperty (napi_env env, napi_value obj, const std::string_view& key) {
|
|
73
86
|
bool has = false;
|
|
74
|
-
napi_has_named_property(env, obj, key, &has);
|
|
87
|
+
napi_has_named_property(env, obj, key.data(), &has);
|
|
75
88
|
return has;
|
|
76
89
|
}
|
|
77
90
|
|
|
78
|
-
static napi_value GetProperty (napi_env env, napi_value obj, const
|
|
91
|
+
static napi_value GetProperty (napi_env env, napi_value obj, const std::string_view& key) {
|
|
79
92
|
napi_value value;
|
|
80
|
-
napi_get_named_property(env, obj, key, &value);
|
|
93
|
+
napi_get_named_property(env, obj, key.data(), &value);
|
|
81
94
|
return value;
|
|
82
95
|
}
|
|
83
96
|
|
|
84
|
-
static bool BooleanProperty (napi_env env, napi_value obj, const
|
|
97
|
+
static bool BooleanProperty (napi_env env, napi_value obj, const std::string_view& key,
|
|
85
98
|
bool defaultValue) {
|
|
86
|
-
if (HasProperty(env, obj, key)) {
|
|
87
|
-
const auto value = GetProperty(env, obj, key);
|
|
99
|
+
if (HasProperty(env, obj, key.data())) {
|
|
100
|
+
const auto value = GetProperty(env, obj, key.data());
|
|
88
101
|
bool result;
|
|
89
102
|
napi_get_value_bool(env, value, &result);
|
|
90
103
|
return result;
|
|
@@ -93,10 +106,23 @@ static bool BooleanProperty (napi_env env, napi_value obj, const char* key,
|
|
|
93
106
|
return defaultValue;
|
|
94
107
|
}
|
|
95
108
|
|
|
96
|
-
static
|
|
109
|
+
static bool EncodingIsBuffer (napi_env env, napi_value obj, const std::string_view& option) {
|
|
110
|
+
napi_value value;
|
|
111
|
+
size_t size;
|
|
112
|
+
|
|
113
|
+
if (napi_get_named_property(env, obj, option.data(), &value) == napi_ok &&
|
|
114
|
+
napi_get_value_string_utf8(env, value, NULL, 0, &size) == napi_ok) {
|
|
115
|
+
// Value is either "buffer" or "utf8" so we can tell them apart just by size
|
|
116
|
+
return size == 6;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
static uint32_t Uint32Property (napi_env env, napi_value obj, const std::string_view& key,
|
|
97
123
|
uint32_t defaultValue) {
|
|
98
|
-
if (HasProperty(env, obj, key)) {
|
|
99
|
-
const auto value = GetProperty(env, obj, key);
|
|
124
|
+
if (HasProperty(env, obj, key.data())) {
|
|
125
|
+
const auto value = GetProperty(env, obj, key.data());
|
|
100
126
|
uint32_t result;
|
|
101
127
|
napi_get_value_uint32(env, value, &result);
|
|
102
128
|
return result;
|
|
@@ -105,10 +131,10 @@ static uint32_t Uint32Property (napi_env env, napi_value obj, const char* key,
|
|
|
105
131
|
return defaultValue;
|
|
106
132
|
}
|
|
107
133
|
|
|
108
|
-
static int Int32Property (napi_env env, napi_value obj, const
|
|
134
|
+
static int Int32Property (napi_env env, napi_value obj, const std::string_view& key,
|
|
109
135
|
int defaultValue) {
|
|
110
|
-
if (HasProperty(env, obj, key)) {
|
|
111
|
-
const auto value = GetProperty(env, obj, key);
|
|
136
|
+
if (HasProperty(env, obj, key.data())) {
|
|
137
|
+
const auto value = GetProperty(env, obj, key.data());
|
|
112
138
|
int result;
|
|
113
139
|
napi_get_value_int32(env, value, &result);
|
|
114
140
|
return result;
|
|
@@ -117,7 +143,7 @@ static int Int32Property (napi_env env, napi_value obj, const char* key,
|
|
|
117
143
|
return defaultValue;
|
|
118
144
|
}
|
|
119
145
|
|
|
120
|
-
static std::string ToString (napi_env env, napi_value from) {
|
|
146
|
+
static std::optional<std::string> ToString (napi_env env, napi_value from) {
|
|
121
147
|
if (IsString(env, from)) {
|
|
122
148
|
size_t length = 0;
|
|
123
149
|
napi_get_value_string_utf8(env, from, nullptr, 0, &length);
|
|
@@ -134,11 +160,11 @@ static std::string ToString (napi_env env, napi_value from) {
|
|
|
134
160
|
return {};
|
|
135
161
|
}
|
|
136
162
|
|
|
137
|
-
static std::string StringProperty (napi_env env, napi_value obj, const
|
|
163
|
+
static std::string StringProperty (napi_env env, napi_value obj, const std::string_view& key) {
|
|
138
164
|
if (HasProperty(env, obj, key)) {
|
|
139
165
|
napi_value value = GetProperty(env, obj, key);
|
|
140
166
|
if (IsString(env, value)) {
|
|
141
|
-
return ToString(env, value);
|
|
167
|
+
return ToString(env, value).value_or(std::string());
|
|
142
168
|
}
|
|
143
169
|
}
|
|
144
170
|
|
|
@@ -158,13 +184,10 @@ static size_t StringOrBufferLength (napi_env env, napi_value value) {
|
|
|
158
184
|
return size;
|
|
159
185
|
}
|
|
160
186
|
|
|
161
|
-
static std::optional<std::string> RangeOption (napi_env env, napi_value opts, const
|
|
187
|
+
static std::optional<std::string> RangeOption (napi_env env, napi_value opts, const std::string& name) {
|
|
162
188
|
if (HasProperty(env, opts, name)) {
|
|
163
189
|
const auto value = GetProperty(env, opts, name);
|
|
164
|
-
|
|
165
|
-
if (StringOrBufferLength(env, value) > 0) {
|
|
166
|
-
return ToString(env, value);
|
|
167
|
-
}
|
|
190
|
+
return ToString(env, value);
|
|
168
191
|
}
|
|
169
192
|
|
|
170
193
|
return {};
|
|
@@ -182,7 +205,7 @@ static std::vector<std::string> KeyArray (napi_env env, napi_value arr) {
|
|
|
182
205
|
|
|
183
206
|
if (napi_get_element(env, arr, i, &element) == napi_ok &&
|
|
184
207
|
StringOrBufferLength(env, element) > 0) {
|
|
185
|
-
result.push_back(ToString(env, element));
|
|
208
|
+
result.push_back(ToString(env, element).value_or(std::string()));
|
|
186
209
|
}
|
|
187
210
|
}
|
|
188
211
|
}
|
|
@@ -199,7 +222,8 @@ static napi_status CallFunction (napi_env env,
|
|
|
199
222
|
return napi_call_function(env, global, callback, argc, argv, nullptr);
|
|
200
223
|
}
|
|
201
224
|
|
|
202
|
-
|
|
225
|
+
template <typename T>
|
|
226
|
+
void Convert (napi_env env, const std::optional<T>& s, bool asBuffer, napi_value& result) {
|
|
203
227
|
if (!s) {
|
|
204
228
|
napi_get_undefined(env, &result);
|
|
205
229
|
} else if (asBuffer) {
|
|
@@ -209,6 +233,15 @@ void Convert (napi_env env, const std::optional<std::string>& s, bool asBuffer,
|
|
|
209
233
|
}
|
|
210
234
|
}
|
|
211
235
|
|
|
236
|
+
template <typename T>
|
|
237
|
+
void Convert (napi_env env, const T& s, bool asBuffer, napi_value& result) {
|
|
238
|
+
if (asBuffer) {
|
|
239
|
+
napi_create_buffer_copy(env, s.size(), s.data(), nullptr, &result);
|
|
240
|
+
} else {
|
|
241
|
+
napi_create_string_utf8(env, s.data(), s.size(), &result);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
212
245
|
/**
|
|
213
246
|
* Base worker class. Handles the async work. Derived classes can override the
|
|
214
247
|
* following virtual methods (listed in the order in which they're called):
|
|
@@ -247,8 +280,9 @@ struct BaseWorker {
|
|
|
247
280
|
self->DoExecute();
|
|
248
281
|
}
|
|
249
282
|
|
|
250
|
-
|
|
283
|
+
bool SetStatus (const leveldb::Status& status) {
|
|
251
284
|
status_ = status;
|
|
285
|
+
return status.ok();
|
|
252
286
|
}
|
|
253
287
|
|
|
254
288
|
virtual void DoExecute () = 0;
|
|
@@ -278,7 +312,28 @@ struct BaseWorker {
|
|
|
278
312
|
}
|
|
279
313
|
|
|
280
314
|
virtual void HandleErrorCallback (napi_env env, napi_value callback) {
|
|
281
|
-
|
|
315
|
+
napi_value argv;
|
|
316
|
+
|
|
317
|
+
const auto msg = status_.ToString();
|
|
318
|
+
|
|
319
|
+
if (status_.IsNotFound()) {
|
|
320
|
+
argv = CreateCodeError(env, "LEVEL_NOT_FOUND", msg);
|
|
321
|
+
} else if (status_.IsCorruption()) {
|
|
322
|
+
argv = CreateCodeError(env, "LEVEL_CORRUPTION", msg);
|
|
323
|
+
} else if (status_.IsIOError()) {
|
|
324
|
+
if (msg.find("IO error: lock ") != std::string::npos) { // env_posix.cc
|
|
325
|
+
argv = CreateCodeError(env, "LEVEL_LOCKED", msg);
|
|
326
|
+
} else if (msg.find("IO error: LockFile ") != std::string::npos) { // env_win.cc
|
|
327
|
+
argv = CreateCodeError(env, "LEVEL_LOCKED", msg);
|
|
328
|
+
} else if (msg.find("IO error: While lock file") != std::string::npos) { // env_mac.cc
|
|
329
|
+
argv = CreateCodeError(env, "LEVEL_LOCKED", msg);
|
|
330
|
+
} else {
|
|
331
|
+
argv = CreateCodeError(env, "LEVEL_IO_ERROR", msg);
|
|
332
|
+
}
|
|
333
|
+
} else {
|
|
334
|
+
argv = CreateError(env, msg);
|
|
335
|
+
}
|
|
336
|
+
|
|
282
337
|
CallFunction(env, callback, 1, &argv);
|
|
283
338
|
}
|
|
284
339
|
|
|
@@ -333,18 +388,18 @@ struct Database {
|
|
|
333
388
|
leveldb::Status Put (const leveldb::WriteOptions& options,
|
|
334
389
|
const std::string& key,
|
|
335
390
|
const std::string& value) {
|
|
336
|
-
return db_->Put(options, key, value);
|
|
391
|
+
return db_->Put(options, db_->DefaultColumnFamily(), key, value);
|
|
337
392
|
}
|
|
338
393
|
|
|
339
394
|
leveldb::Status Get (const leveldb::ReadOptions& options,
|
|
340
395
|
const std::string& key,
|
|
341
|
-
|
|
342
|
-
return db_->Get(options, key, &value);
|
|
396
|
+
rocksdb::PinnableSlice& value) {
|
|
397
|
+
return db_->Get(options, db_->DefaultColumnFamily(), key, &value);
|
|
343
398
|
}
|
|
344
399
|
|
|
345
400
|
leveldb::Status Del (const leveldb::WriteOptions& options,
|
|
346
401
|
const std::string& key) {
|
|
347
|
-
return db_->Delete(options, key);
|
|
402
|
+
return db_->Delete(options, db_->DefaultColumnFamily(), key);
|
|
348
403
|
}
|
|
349
404
|
|
|
350
405
|
leveldb::Status WriteBatch (const leveldb::WriteOptions& options,
|
|
@@ -571,10 +626,19 @@ struct BaseIterator {
|
|
|
571
626
|
}
|
|
572
627
|
|
|
573
628
|
bool OutOfRange (const leveldb::Slice& target) const {
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
629
|
+
if (lte_) {
|
|
630
|
+
if (target.compare(*lte_) > 0) return true;
|
|
631
|
+
} else if (lt_) {
|
|
632
|
+
if (target.compare(*lt_) >= 0) return true;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
if (gte_) {
|
|
636
|
+
if (target.compare(*gte_) < 0) return true;
|
|
637
|
+
} else if (gt_) {
|
|
638
|
+
if (target.compare(*gt_) <= 0) return true;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
return false;
|
|
578
642
|
}
|
|
579
643
|
|
|
580
644
|
Database* database_;
|
|
@@ -605,14 +669,14 @@ struct Iterator final : public BaseIterator {
|
|
|
605
669
|
const bool fillCache,
|
|
606
670
|
const bool keyAsBuffer,
|
|
607
671
|
const bool valueAsBuffer,
|
|
608
|
-
const uint32_t
|
|
672
|
+
const uint32_t highWaterMarkBytes)
|
|
609
673
|
: BaseIterator(database, reverse, lt, lte, gt, gte, limit, fillCache),
|
|
610
674
|
keys_(keys),
|
|
611
675
|
values_(values),
|
|
612
676
|
keyAsBuffer_(keyAsBuffer),
|
|
613
677
|
valueAsBuffer_(valueAsBuffer),
|
|
614
|
-
|
|
615
|
-
|
|
678
|
+
highWaterMarkBytes_(highWaterMarkBytes),
|
|
679
|
+
first_(true),
|
|
616
680
|
nexting_(false),
|
|
617
681
|
isClosing_(false),
|
|
618
682
|
closeWorker_(nullptr),
|
|
@@ -631,34 +695,34 @@ struct Iterator final : public BaseIterator {
|
|
|
631
695
|
|
|
632
696
|
bool ReadMany (uint32_t size) {
|
|
633
697
|
cache_.clear();
|
|
698
|
+
cache_.reserve(size * 2);
|
|
634
699
|
size_t bytesRead = 0;
|
|
635
700
|
|
|
636
701
|
while (true) {
|
|
637
|
-
if (
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
if (keys_) {
|
|
641
|
-
const auto& slice = CurrentKey();
|
|
642
|
-
cache_.emplace_back(slice.data(), slice.size());
|
|
643
|
-
bytesRead += slice.size();
|
|
644
|
-
} else {
|
|
645
|
-
cache_.emplace_back("");
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
if (values_) {
|
|
649
|
-
const auto& slice = CurrentValue();
|
|
650
|
-
cache_.emplace_back(slice.data(), slice.size());
|
|
651
|
-
bytesRead += slice.size();
|
|
652
|
-
} else {
|
|
653
|
-
cache_.emplace_back("");
|
|
654
|
-
}
|
|
702
|
+
if (!first_) Next();
|
|
703
|
+
else first_ = false;
|
|
655
704
|
|
|
656
|
-
if (!
|
|
657
|
-
|
|
658
|
-
|
|
705
|
+
if (!Valid() || !Increment()) break;
|
|
706
|
+
|
|
707
|
+
if (keys_ && values_) {
|
|
708
|
+
auto k = CurrentKey();
|
|
709
|
+
auto v = CurrentValue();
|
|
710
|
+
cache_.emplace_back(k.data(), k.size());
|
|
711
|
+
cache_.emplace_back(v.data(), v.size());
|
|
712
|
+
bytesRead += k.size() + v.size();
|
|
713
|
+
} else if (keys_) {
|
|
714
|
+
auto k = CurrentKey();
|
|
715
|
+
cache_.emplace_back(k.data(), k.size());
|
|
716
|
+
cache_.push_back({});
|
|
717
|
+
bytesRead += k.size();
|
|
718
|
+
} else if (values_) {
|
|
719
|
+
auto v = CurrentValue();
|
|
720
|
+
cache_.push_back({});
|
|
721
|
+
cache_.emplace_back(v.data(), v.size());
|
|
722
|
+
bytesRead += v.size();
|
|
659
723
|
}
|
|
660
724
|
|
|
661
|
-
if (bytesRead >
|
|
725
|
+
if (bytesRead > highWaterMarkBytes_ || cache_.size() / 2 >= size) {
|
|
662
726
|
return true;
|
|
663
727
|
}
|
|
664
728
|
}
|
|
@@ -670,8 +734,8 @@ struct Iterator final : public BaseIterator {
|
|
|
670
734
|
const bool values_;
|
|
671
735
|
const bool keyAsBuffer_;
|
|
672
736
|
const bool valueAsBuffer_;
|
|
673
|
-
const uint32_t
|
|
674
|
-
bool
|
|
737
|
+
const uint32_t highWaterMarkBytes_;
|
|
738
|
+
bool first_;
|
|
675
739
|
bool nexting_;
|
|
676
740
|
bool isClosing_;
|
|
677
741
|
BaseWorker* closeWorker_;
|
|
@@ -687,7 +751,7 @@ private:
|
|
|
687
751
|
* the guarantee that no db operations will be in-flight at this time.
|
|
688
752
|
*/
|
|
689
753
|
static void env_cleanup_hook (void* arg) {
|
|
690
|
-
Database* database = (
|
|
754
|
+
Database* database = reinterpret_cast<Database*>(arg);
|
|
691
755
|
|
|
692
756
|
// Do everything that db_close() does but synchronously. We're expecting that GC
|
|
693
757
|
// did not (yet) collect the database because that would be a user mistake (not
|
|
@@ -697,8 +761,8 @@ static void env_cleanup_hook (void* arg) {
|
|
|
697
761
|
// be a safe noop if called before db_open() or after db_close().
|
|
698
762
|
if (database && database->db_) {
|
|
699
763
|
// TODO: does not do `napi_delete_reference(env, iterator->ref_)`. Problem?
|
|
700
|
-
for (auto it
|
|
701
|
-
|
|
764
|
+
for (auto it : database->iterators_) {
|
|
765
|
+
it->Close();
|
|
702
766
|
}
|
|
703
767
|
|
|
704
768
|
// Having closed the iterators (and released snapshots) we can safely close.
|
|
@@ -812,7 +876,7 @@ NAPI_METHOD(db_open) {
|
|
|
812
876
|
NAPI_ARGV(4);
|
|
813
877
|
NAPI_DB_CONTEXT();
|
|
814
878
|
|
|
815
|
-
const auto location = ToString(env, argv[1]);
|
|
879
|
+
const auto location = ToString(env, argv[1]).value_or(std::string());
|
|
816
880
|
const auto options = argv[2];
|
|
817
881
|
const auto createIfMissing = BooleanProperty(env, options, "createIfMissing", true);
|
|
818
882
|
const auto errorIfExists = BooleanProperty(env, options, "errorIfExists", false);
|
|
@@ -866,8 +930,8 @@ NAPI_METHOD(db_close) {
|
|
|
866
930
|
napi_value noop;
|
|
867
931
|
napi_create_function(env, nullptr, 0, noop_callback, nullptr, &noop);
|
|
868
932
|
|
|
869
|
-
for (auto it
|
|
870
|
-
iterator_do_close(env,
|
|
933
|
+
for (auto it : database->iterators_) {
|
|
934
|
+
iterator_do_close(env, it, noop);
|
|
871
935
|
}
|
|
872
936
|
|
|
873
937
|
auto worker = new CloseWorker(env, database, callback);
|
|
@@ -881,6 +945,91 @@ NAPI_METHOD(db_close) {
|
|
|
881
945
|
return 0;
|
|
882
946
|
}
|
|
883
947
|
|
|
948
|
+
struct PutWorker final : public PriorityWorker {
|
|
949
|
+
PutWorker (napi_env env,
|
|
950
|
+
Database* database,
|
|
951
|
+
napi_value callback,
|
|
952
|
+
const std::string& key,
|
|
953
|
+
const std::string& value,
|
|
954
|
+
bool sync)
|
|
955
|
+
: PriorityWorker(env, database, callback, "classic_level.db.put"),
|
|
956
|
+
key_(key), value_(value), sync_(sync) {
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
void DoExecute () override {
|
|
960
|
+
leveldb::WriteOptions options;
|
|
961
|
+
options.sync = sync_;
|
|
962
|
+
SetStatus(database_->Put(options, key_, value_));
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
const std::string key_;
|
|
966
|
+
const std::string value_;
|
|
967
|
+
const bool sync_;
|
|
968
|
+
};
|
|
969
|
+
|
|
970
|
+
NAPI_METHOD(db_put) {
|
|
971
|
+
NAPI_ARGV(5);
|
|
972
|
+
NAPI_DB_CONTEXT();
|
|
973
|
+
|
|
974
|
+
const auto key = ToString(env, argv[1]).value_or(std::string());
|
|
975
|
+
const auto value = ToString(env, argv[2]).value_or(std::string());
|
|
976
|
+
const auto sync = BooleanProperty(env, argv[3], "sync", false);
|
|
977
|
+
const auto callback = argv[4];
|
|
978
|
+
|
|
979
|
+
auto worker = new PutWorker(env, database, callback, key, value, sync);
|
|
980
|
+
worker->Queue(env);
|
|
981
|
+
|
|
982
|
+
return 0;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
struct GetWorker final : public PriorityWorker {
|
|
986
|
+
GetWorker (napi_env env,
|
|
987
|
+
Database* database,
|
|
988
|
+
napi_value callback,
|
|
989
|
+
const std::string& key,
|
|
990
|
+
const bool asBuffer,
|
|
991
|
+
const bool fillCache)
|
|
992
|
+
: PriorityWorker(env, database, callback, "classic_level.db.get"),
|
|
993
|
+
key_(key), asBuffer_(asBuffer), fillCache_(fillCache) {
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
void DoExecute () override {
|
|
997
|
+
leveldb::ReadOptions options;
|
|
998
|
+
options.fill_cache = fillCache_;
|
|
999
|
+
SetStatus(database_->Get(options, key_, value_));
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
void HandleOKCallback (napi_env env, napi_value callback) override {
|
|
1003
|
+
napi_value argv[2];
|
|
1004
|
+
napi_get_null(env, &argv[0]);
|
|
1005
|
+
Convert(env, std::move(value_), asBuffer_, argv[1]);
|
|
1006
|
+
CallFunction(env, callback, 2, argv);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
private:
|
|
1010
|
+
const std::string key_;
|
|
1011
|
+
rocksdb::PinnableSlice value_;
|
|
1012
|
+
const bool asBuffer_;
|
|
1013
|
+
const bool fillCache_;
|
|
1014
|
+
};
|
|
1015
|
+
|
|
1016
|
+
NAPI_METHOD(db_get) {
|
|
1017
|
+
NAPI_ARGV(4);
|
|
1018
|
+
NAPI_DB_CONTEXT();
|
|
1019
|
+
|
|
1020
|
+
const auto key = ToString(env, argv[1]).value_or(std::string());
|
|
1021
|
+
const auto options = argv[2];
|
|
1022
|
+
const auto asBuffer = EncodingIsBuffer(env, options, "valueEncoding");
|
|
1023
|
+
const auto fillCache = BooleanProperty(env, options, "fillCache", true);
|
|
1024
|
+
const auto callback = argv[3];
|
|
1025
|
+
|
|
1026
|
+
auto worker = new GetWorker(env, database, callback, key, asBuffer, fillCache);
|
|
1027
|
+
worker->Queue(env);
|
|
1028
|
+
|
|
1029
|
+
return 0;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
|
|
884
1033
|
struct GetManyWorker final : public PriorityWorker {
|
|
885
1034
|
GetManyWorker (napi_env env,
|
|
886
1035
|
Database* database,
|
|
@@ -907,8 +1056,9 @@ struct GetManyWorker final : public PriorityWorker {
|
|
|
907
1056
|
options.snapshot = snapshot_;
|
|
908
1057
|
options.fill_cache = fillCache_;
|
|
909
1058
|
|
|
1059
|
+
rocksdb::PinnableSlice value;
|
|
1060
|
+
|
|
910
1061
|
for (const auto& key: keys_) {
|
|
911
|
-
std::string value;
|
|
912
1062
|
const auto status = database_->Get(options, key, value);
|
|
913
1063
|
|
|
914
1064
|
if (status.ok()) {
|
|
@@ -919,6 +1069,8 @@ struct GetManyWorker final : public PriorityWorker {
|
|
|
919
1069
|
SetStatus(status);
|
|
920
1070
|
break;
|
|
921
1071
|
}
|
|
1072
|
+
|
|
1073
|
+
value.Reset();
|
|
922
1074
|
}
|
|
923
1075
|
|
|
924
1076
|
database_->ReleaseSnapshot(snapshot_);
|
|
@@ -946,7 +1098,7 @@ struct GetManyWorker final : public PriorityWorker {
|
|
|
946
1098
|
private:
|
|
947
1099
|
const std::vector<std::string> keys_;
|
|
948
1100
|
const bool valueAsBuffer_;
|
|
949
|
-
std::vector<std::optional<
|
|
1101
|
+
std::vector<std::optional<rocksdb::PinnableSlice>> cache_;
|
|
950
1102
|
const bool fillCache_;
|
|
951
1103
|
const leveldb::Snapshot* snapshot_;
|
|
952
1104
|
};
|
|
@@ -957,7 +1109,7 @@ NAPI_METHOD(db_get_many) {
|
|
|
957
1109
|
|
|
958
1110
|
const auto keys = KeyArray(env, argv[1]);
|
|
959
1111
|
const auto options = argv[2];
|
|
960
|
-
const bool asBuffer =
|
|
1112
|
+
const bool asBuffer = EncodingIsBuffer(env, options, "valueEncoding");
|
|
961
1113
|
const bool fillCache = BooleanProperty(env, options, "fillCache", true);
|
|
962
1114
|
const auto callback = argv[3];
|
|
963
1115
|
|
|
@@ -967,6 +1119,113 @@ NAPI_METHOD(db_get_many) {
|
|
|
967
1119
|
return 0;
|
|
968
1120
|
}
|
|
969
1121
|
|
|
1122
|
+
struct DelWorker final : public PriorityWorker {
|
|
1123
|
+
DelWorker (napi_env env,
|
|
1124
|
+
Database* database,
|
|
1125
|
+
napi_value callback,
|
|
1126
|
+
const std::string& key,
|
|
1127
|
+
bool sync)
|
|
1128
|
+
: PriorityWorker(env, database, callback, "classic_level.db.del"),
|
|
1129
|
+
key_(key), sync_(sync) {
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
void DoExecute () override {
|
|
1133
|
+
leveldb::WriteOptions options;
|
|
1134
|
+
options.sync = sync_;
|
|
1135
|
+
SetStatus(database_->Del(options, key_));
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
const std::string key_;
|
|
1139
|
+
const bool sync_;
|
|
1140
|
+
};
|
|
1141
|
+
|
|
1142
|
+
NAPI_METHOD(db_del) {
|
|
1143
|
+
NAPI_ARGV(4);
|
|
1144
|
+
NAPI_DB_CONTEXT();
|
|
1145
|
+
|
|
1146
|
+
const auto key = ToString(env, argv[1]).value_or(std::string());
|
|
1147
|
+
const auto sync = BooleanProperty(env, argv[2], "sync", false);
|
|
1148
|
+
const auto callback = argv[3];
|
|
1149
|
+
|
|
1150
|
+
auto worker = new DelWorker(env, database, callback, key, sync);
|
|
1151
|
+
worker->Queue(env);
|
|
1152
|
+
|
|
1153
|
+
return 0;
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
struct ClearWorker final : public PriorityWorker {
|
|
1157
|
+
ClearWorker (napi_env env,
|
|
1158
|
+
Database* database,
|
|
1159
|
+
napi_value callback,
|
|
1160
|
+
const bool reverse,
|
|
1161
|
+
const int limit,
|
|
1162
|
+
const std::optional<std::string>& lt,
|
|
1163
|
+
const std::optional<std::string>& lte,
|
|
1164
|
+
const std::optional<std::string>& gt,
|
|
1165
|
+
const std::optional<std::string>& gte)
|
|
1166
|
+
: PriorityWorker(env, database, callback, "classic_level.db.clear"),
|
|
1167
|
+
iterator_(database, reverse, lt, lte, gt, gte, limit, false) {
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
void DoExecute () override {
|
|
1171
|
+
iterator_.SeekToRange();
|
|
1172
|
+
|
|
1173
|
+
// TODO: add option
|
|
1174
|
+
const uint32_t hwm = 16 * 1024;
|
|
1175
|
+
leveldb::WriteBatch batch;
|
|
1176
|
+
|
|
1177
|
+
leveldb::WriteOptions options;
|
|
1178
|
+
options.sync = false;
|
|
1179
|
+
|
|
1180
|
+
while (true) {
|
|
1181
|
+
size_t bytesRead = 0;
|
|
1182
|
+
|
|
1183
|
+
while (bytesRead <= hwm && iterator_.Valid() && iterator_.Increment()) {
|
|
1184
|
+
const auto key = iterator_.CurrentKey();
|
|
1185
|
+
batch.Delete(key);
|
|
1186
|
+
bytesRead += key.size();
|
|
1187
|
+
iterator_.Next();
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
if (!SetStatus(iterator_.Status()) || bytesRead == 0) {
|
|
1191
|
+
break;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
if (!SetStatus(database_->WriteBatch(options, &batch))) {
|
|
1195
|
+
break;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
batch.Clear();
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
iterator_.Close();
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
private:
|
|
1205
|
+
BaseIterator iterator_;
|
|
1206
|
+
};
|
|
1207
|
+
|
|
1208
|
+
NAPI_METHOD(db_clear) {
|
|
1209
|
+
NAPI_ARGV(3);
|
|
1210
|
+
NAPI_DB_CONTEXT();
|
|
1211
|
+
|
|
1212
|
+
napi_value options = argv[1];
|
|
1213
|
+
napi_value callback = argv[2];
|
|
1214
|
+
|
|
1215
|
+
const auto reverse = BooleanProperty(env, options, "reverse", false);
|
|
1216
|
+
const auto limit = Int32Property(env, options, "limit", -1);
|
|
1217
|
+
|
|
1218
|
+
const auto lt = RangeOption(env, options, "lt");
|
|
1219
|
+
const auto lte = RangeOption(env, options, "lte");
|
|
1220
|
+
const auto gt = RangeOption(env, options, "gt");
|
|
1221
|
+
const auto gte = RangeOption(env, options, "gte");
|
|
1222
|
+
|
|
1223
|
+
auto worker = new ClearWorker(env, database, callback, reverse, limit, lt, lte, gt, gte);
|
|
1224
|
+
worker->Queue(env);
|
|
1225
|
+
|
|
1226
|
+
return 0;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
970
1229
|
static void FinalizeIterator (napi_env env, void* data, void* hint) {
|
|
971
1230
|
if (data) {
|
|
972
1231
|
delete reinterpret_cast<Iterator*>(data);
|
|
@@ -982,10 +1241,10 @@ NAPI_METHOD(iterator_init) {
|
|
|
982
1241
|
const auto keys = BooleanProperty(env, options, "keys", true);
|
|
983
1242
|
const auto values = BooleanProperty(env, options, "values", true);
|
|
984
1243
|
const auto fillCache = BooleanProperty(env, options, "fillCache", false);
|
|
985
|
-
const
|
|
986
|
-
const
|
|
1244
|
+
const bool keyAsBuffer = EncodingIsBuffer(env, options, "keyEncoding");
|
|
1245
|
+
const bool valueAsBuffer = EncodingIsBuffer(env, options, "valueEncoding");
|
|
987
1246
|
const auto limit = Int32Property(env, options, "limit", -1);
|
|
988
|
-
const auto
|
|
1247
|
+
const auto highWaterMarkBytes = Uint32Property(env, options, "highWaterMarkBytes", 16 * 1024);
|
|
989
1248
|
|
|
990
1249
|
const auto lt = RangeOption(env, options, "lt");
|
|
991
1250
|
const auto lte = RangeOption(env, options, "lte");
|
|
@@ -994,7 +1253,7 @@ NAPI_METHOD(iterator_init) {
|
|
|
994
1253
|
|
|
995
1254
|
auto iterator = new Iterator(database, reverse, keys,
|
|
996
1255
|
values, limit, lt, lte, gt, gte, fillCache,
|
|
997
|
-
keyAsBuffer, valueAsBuffer,
|
|
1256
|
+
keyAsBuffer, valueAsBuffer, highWaterMarkBytes);
|
|
998
1257
|
napi_value result;
|
|
999
1258
|
|
|
1000
1259
|
NAPI_STATUS_THROWS(napi_create_external(env, iterator,
|
|
@@ -1016,8 +1275,8 @@ NAPI_METHOD(iterator_seek) {
|
|
|
1016
1275
|
napi_throw_error(env, nullptr, "iterator has closed");
|
|
1017
1276
|
}
|
|
1018
1277
|
|
|
1019
|
-
const auto target = ToString(env, argv[1]);
|
|
1020
|
-
iterator->
|
|
1278
|
+
const auto target = ToString(env, argv[1]).value_or(std::string());
|
|
1279
|
+
iterator->first_ = true;
|
|
1021
1280
|
iterator->Seek(target);
|
|
1022
1281
|
|
|
1023
1282
|
return 0;
|
|
@@ -1104,8 +1363,8 @@ struct NextWorker final : public BaseWorker {
|
|
|
1104
1363
|
Convert(env, iterator_->cache_[idx + 0], iterator_->keyAsBuffer_, key);
|
|
1105
1364
|
Convert(env, iterator_->cache_[idx + 1], iterator_->valueAsBuffer_, val);
|
|
1106
1365
|
|
|
1107
|
-
napi_set_element(env, result, static_cast<int>(
|
|
1108
|
-
napi_set_element(env, result, static_cast<int>(
|
|
1366
|
+
napi_set_element(env, result, static_cast<int>(idx + 0), key);
|
|
1367
|
+
napi_set_element(env, result, static_cast<int>(idx + 1), val);
|
|
1109
1368
|
}
|
|
1110
1369
|
|
|
1111
1370
|
iterator_->cache_.clear();
|
|
@@ -1146,9 +1405,8 @@ NAPI_METHOD(iterator_nextv) {
|
|
|
1146
1405
|
const auto callback = argv[2];
|
|
1147
1406
|
|
|
1148
1407
|
if (iterator->isClosing_) {
|
|
1149
|
-
|
|
1150
|
-
CallFunction(env, callback, 1, &argv);
|
|
1151
|
-
|
|
1408
|
+
napi_value argv = CreateCodeError(env, "LEVEL_ITERATOR_NOT_OPEN", "Iterator is not open");
|
|
1409
|
+
NAPI_STATUS_THROWS(CallFunction(env, callback, 1, &argv));
|
|
1152
1410
|
return 0;
|
|
1153
1411
|
}
|
|
1154
1412
|
|
|
@@ -1159,6 +1417,83 @@ NAPI_METHOD(iterator_nextv) {
|
|
|
1159
1417
|
return 0;
|
|
1160
1418
|
}
|
|
1161
1419
|
|
|
1420
|
+
/**
|
|
1421
|
+
* Worker class for batch write operation.
|
|
1422
|
+
*/
|
|
1423
|
+
struct BatchWorker final : public PriorityWorker {
|
|
1424
|
+
BatchWorker (napi_env env,
|
|
1425
|
+
Database* database,
|
|
1426
|
+
napi_value callback,
|
|
1427
|
+
leveldb::WriteBatch* batch,
|
|
1428
|
+
const bool sync,
|
|
1429
|
+
const bool hasData)
|
|
1430
|
+
: PriorityWorker(env, database, callback, "classic_level.batch.do"),
|
|
1431
|
+
batch_(batch), hasData_(hasData) {
|
|
1432
|
+
options_.sync = sync;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
~BatchWorker () {
|
|
1436
|
+
delete batch_;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
void DoExecute () override {
|
|
1440
|
+
if (hasData_) {
|
|
1441
|
+
SetStatus(database_->WriteBatch(options_, batch_));
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
private:
|
|
1446
|
+
leveldb::WriteOptions options_;
|
|
1447
|
+
leveldb::WriteBatch* batch_;
|
|
1448
|
+
const bool hasData_;
|
|
1449
|
+
};
|
|
1450
|
+
|
|
1451
|
+
NAPI_METHOD(batch_do) {
|
|
1452
|
+
NAPI_ARGV(4);
|
|
1453
|
+
NAPI_DB_CONTEXT();
|
|
1454
|
+
|
|
1455
|
+
const auto array = argv[1];
|
|
1456
|
+
const auto sync = BooleanProperty(env, argv[2], "sync", false);
|
|
1457
|
+
const auto callback = argv[3];
|
|
1458
|
+
|
|
1459
|
+
uint32_t length;
|
|
1460
|
+
napi_get_array_length(env, array, &length);
|
|
1461
|
+
|
|
1462
|
+
leveldb::WriteBatch* batch = new leveldb::WriteBatch();
|
|
1463
|
+
bool hasData = false;
|
|
1464
|
+
|
|
1465
|
+
for (uint32_t i = 0; i < length; i++) {
|
|
1466
|
+
napi_value element;
|
|
1467
|
+
napi_get_element(env, array, i, &element);
|
|
1468
|
+
|
|
1469
|
+
if (!IsObject(env, element)) continue;
|
|
1470
|
+
|
|
1471
|
+
std::string type = StringProperty(env, element, "type");
|
|
1472
|
+
|
|
1473
|
+
if (type == "del") {
|
|
1474
|
+
if (!HasProperty(env, element, "key")) continue;
|
|
1475
|
+
const auto key = ToString(env, GetProperty(env, element, "key")).value_or(std::string());
|
|
1476
|
+
|
|
1477
|
+
batch->Delete(key);
|
|
1478
|
+
if (!hasData) hasData = true;
|
|
1479
|
+
} else if (type == "put") {
|
|
1480
|
+
if (!HasProperty(env, element, "key")) continue;
|
|
1481
|
+
if (!HasProperty(env, element, "value")) continue;
|
|
1482
|
+
|
|
1483
|
+
const auto key = ToString(env, GetProperty(env, element, "key")).value_or(std::string());
|
|
1484
|
+
const auto value = ToString(env, GetProperty(env, element, "value")).value_or(std::string());
|
|
1485
|
+
|
|
1486
|
+
batch->Put(key, value);
|
|
1487
|
+
if (!hasData) hasData = true;
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
auto worker = new BatchWorker(env, database, callback, batch, sync, hasData);
|
|
1492
|
+
worker->Queue(env);
|
|
1493
|
+
|
|
1494
|
+
return 0;
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1162
1497
|
static void FinalizeBatch (napi_env env, void* data, void* hint) {
|
|
1163
1498
|
if (data) {
|
|
1164
1499
|
delete reinterpret_cast<leveldb::WriteBatch*>(data);
|
|
@@ -1182,8 +1517,8 @@ NAPI_METHOD(batch_put) {
|
|
|
1182
1517
|
NAPI_ARGV(3);
|
|
1183
1518
|
NAPI_BATCH_CONTEXT();
|
|
1184
1519
|
|
|
1185
|
-
const auto key = ToString(env, argv[1]);
|
|
1186
|
-
const auto value = ToString(env, argv[2]);
|
|
1520
|
+
const auto key = ToString(env, argv[1]).value_or(std::string());
|
|
1521
|
+
const auto value = ToString(env, argv[2]).value_or(std::string());
|
|
1187
1522
|
|
|
1188
1523
|
batch->Put(key, value);
|
|
1189
1524
|
|
|
@@ -1194,7 +1529,7 @@ NAPI_METHOD(batch_del) {
|
|
|
1194
1529
|
NAPI_ARGV(2);
|
|
1195
1530
|
NAPI_BATCH_CONTEXT();
|
|
1196
1531
|
|
|
1197
|
-
const auto key = ToString(env, argv[1]);
|
|
1532
|
+
const auto key = ToString(env, argv[1]).value_or(std::string());
|
|
1198
1533
|
|
|
1199
1534
|
batch->Delete(key);
|
|
1200
1535
|
|
|
@@ -1261,13 +1596,18 @@ NAPI_INIT() {
|
|
|
1261
1596
|
NAPI_EXPORT_FUNCTION(db_init);
|
|
1262
1597
|
NAPI_EXPORT_FUNCTION(db_open);
|
|
1263
1598
|
NAPI_EXPORT_FUNCTION(db_close);
|
|
1599
|
+
NAPI_EXPORT_FUNCTION(db_put);
|
|
1600
|
+
NAPI_EXPORT_FUNCTION(db_get);
|
|
1264
1601
|
NAPI_EXPORT_FUNCTION(db_get_many);
|
|
1602
|
+
NAPI_EXPORT_FUNCTION(db_del);
|
|
1603
|
+
NAPI_EXPORT_FUNCTION(db_clear);
|
|
1265
1604
|
|
|
1266
1605
|
NAPI_EXPORT_FUNCTION(iterator_init);
|
|
1267
1606
|
NAPI_EXPORT_FUNCTION(iterator_seek);
|
|
1268
1607
|
NAPI_EXPORT_FUNCTION(iterator_close);
|
|
1269
1608
|
NAPI_EXPORT_FUNCTION(iterator_nextv);
|
|
1270
1609
|
|
|
1610
|
+
NAPI_EXPORT_FUNCTION(batch_do);
|
|
1271
1611
|
NAPI_EXPORT_FUNCTION(batch_init);
|
|
1272
1612
|
NAPI_EXPORT_FUNCTION(batch_put);
|
|
1273
1613
|
NAPI_EXPORT_FUNCTION(batch_del);
|