@nxtedition/rocksdb 5.2.26 → 5.2.27

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 CHANGED
@@ -16,10 +16,12 @@
16
16
 
17
17
  namespace leveldb = rocksdb;
18
18
 
19
+ #include <array>
19
20
  #include <set>
20
21
  #include <memory>
21
22
  #include <string>
22
23
  #include <vector>
24
+ #include <optional>
23
25
 
24
26
  class NullLogger : public rocksdb::Logger {
25
27
  public:
@@ -61,7 +63,7 @@ static bool IsObject (napi_env env, napi_value value) {
61
63
  return type == napi_object;
62
64
  }
63
65
 
64
- static napi_value CreateError (napi_env env, const std::string& str) {
66
+ static napi_value CreateError (napi_env env, const std::string_view& str) {
65
67
  napi_value msg;
66
68
  napi_create_string_utf8(env, str.data(), str.size(), &msg);
67
69
  napi_value error;
@@ -69,7 +71,7 @@ static napi_value CreateError (napi_env env, const std::string& str) {
69
71
  return error;
70
72
  }
71
73
 
72
- static napi_value CreateCodeError (napi_env env, const std::string& code, const std::string& msg) {
74
+ static napi_value CreateCodeError (napi_env env, const std::string_view& code, const std::string_view& msg) {
73
75
  napi_value codeValue;
74
76
  napi_create_string_utf8(env, code.data(), code.size(), &codeValue);
75
77
  napi_value msgValue;
@@ -79,19 +81,19 @@ static napi_value CreateCodeError (napi_env env, const std::string& code, const
79
81
  return error;
80
82
  }
81
83
 
82
- static bool HasProperty (napi_env env, napi_value obj, const std::string& key) {
84
+ static bool HasProperty (napi_env env, napi_value obj, const std::string_view& key) {
83
85
  bool has = false;
84
86
  napi_has_named_property(env, obj, key.data(), &has);
85
87
  return has;
86
88
  }
87
89
 
88
- static napi_value GetProperty (napi_env env, napi_value obj, const std::string& key) {
90
+ static napi_value GetProperty (napi_env env, napi_value obj, const std::string_view& key) {
89
91
  napi_value value;
90
92
  napi_get_named_property(env, obj, key.data(), &value);
91
93
  return value;
92
94
  }
93
95
 
94
- static bool BooleanProperty (napi_env env, napi_value obj, const std::string& key, bool defaultValue) {
96
+ static bool BooleanProperty (napi_env env, napi_value obj, const std::string_view& key, bool defaultValue) {
95
97
  if (HasProperty(env, obj, key.data())) {
96
98
  const auto value = GetProperty(env, obj, key.data());
97
99
  bool result;
@@ -102,7 +104,7 @@ static bool BooleanProperty (napi_env env, napi_value obj, const std::string& ke
102
104
  return defaultValue;
103
105
  }
104
106
 
105
- static bool EncodingIsBuffer (napi_env env, napi_value obj, const std::string& option) {
107
+ static bool EncodingIsBuffer (napi_env env, napi_value obj, const std::string_view& option) {
106
108
  napi_value value;
107
109
  size_t size;
108
110
 
@@ -115,7 +117,7 @@ static bool EncodingIsBuffer (napi_env env, napi_value obj, const std::string& o
115
117
  return false;
116
118
  }
117
119
 
118
- static uint32_t Uint32Property (napi_env env, napi_value obj, const std::string& key, uint32_t defaultValue) {
120
+ static uint32_t Uint32Property (napi_env env, napi_value obj, const std::string_view& key, uint32_t defaultValue) {
119
121
  if (HasProperty(env, obj, key.data())) {
120
122
  const auto value = GetProperty(env, obj, key.data());
121
123
  uint32_t result;
@@ -126,7 +128,7 @@ static uint32_t Uint32Property (napi_env env, napi_value obj, const std::string&
126
128
  return defaultValue;
127
129
  }
128
130
 
129
- static int Int32Property (napi_env env, napi_value obj, const std::string& key, int defaultValue) {
131
+ static int Int32Property (napi_env env, napi_value obj, const std::string_view& key, int defaultValue) {
130
132
  if (HasProperty(env, obj, key.data())) {
131
133
  const auto value = GetProperty(env, obj, key.data());
132
134
  int result;
@@ -142,7 +144,7 @@ static std::string ToString (napi_env env, napi_value from, const std::string& d
142
144
  size_t length = 0;
143
145
  napi_get_value_string_utf8(env, from, nullptr, 0, &length);
144
146
  std::string value(length, '\0');
145
- napi_get_value_string_utf8( env, from, &value[0], value.length() + 1, &length);
147
+ napi_get_value_string_utf8(env, from, &value[0], value.length() + 1, &length);
146
148
  return value;
147
149
  } else if (IsBuffer(env, from)) {
148
150
  char* buf = nullptr;
@@ -154,7 +156,7 @@ static std::string ToString (napi_env env, napi_value from, const std::string& d
154
156
  return defaultValue;
155
157
  }
156
158
 
157
- static std::string StringProperty (napi_env env, napi_value obj, const std::string& key, const std::string& defaultValue = "") {
159
+ static std::string StringProperty (napi_env env, napi_value obj, const std::string_view& key, const std::string& defaultValue = "") {
158
160
  if (HasProperty(env, obj, key)) {
159
161
  napi_value value = GetProperty(env, obj, key);
160
162
  if (IsString(env, value)) {
@@ -178,13 +180,13 @@ static size_t StringOrBufferLength (napi_env env, napi_value value) {
178
180
  return size;
179
181
  }
180
182
 
181
- static std::string* RangeOption (napi_env env, napi_value opts, const std::string& name) {
183
+ static std::optional<std::string> RangeOption (napi_env env, napi_value opts, const std::string_view& name) {
182
184
  if (HasProperty(env, opts, name)) {
183
185
  const auto value = GetProperty(env, opts, name);
184
- return new std::string(ToString(env, value));
186
+ return std::string(ToString(env, value));
185
187
  }
186
188
 
187
- return nullptr;
189
+ return {};
188
190
  }
189
191
 
190
192
  static std::vector<std::string> KeyArray (napi_env env, napi_value arr) {
@@ -213,6 +215,32 @@ static napi_status CallFunction (napi_env env, napi_value callback, const int ar
213
215
  return napi_call_function(env, global, callback, argc, argv, nullptr);
214
216
  }
215
217
 
218
+ static napi_value ToError(napi_env env, const rocksdb::Status& status) {
219
+ if (status.ok()) {
220
+ return 0;
221
+ }
222
+
223
+ const auto msg = status.ToString();
224
+
225
+ if (status.IsNotFound()) {
226
+ return CreateCodeError(env, "LEVEL_NOT_FOUND", msg);
227
+ } else if (status.IsCorruption()) {
228
+ return CreateCodeError(env, "LEVEL_CORRUPTION", msg);
229
+ } else if (status.IsIOError()) {
230
+ if (msg.find("IO error: lock ") != std::string::npos) { // env_posix.cc
231
+ return CreateCodeError(env, "LEVEL_LOCKED", msg);
232
+ } else if (msg.find("IO error: LockFile ") != std::string::npos) { // env_win.cc
233
+ return CreateCodeError(env, "LEVEL_LOCKED", msg);
234
+ } else if (msg.find("IO error: While lock file") != std::string::npos) { // env_mac.cc
235
+ return CreateCodeError(env, "LEVEL_LOCKED", msg);
236
+ } else {
237
+ return CreateCodeError(env, "LEVEL_IO_ERROR", msg);
238
+ }
239
+ }
240
+
241
+ return CreateError(env, msg);
242
+ }
243
+
216
244
  template <typename T>
217
245
  void Convert (napi_env env, const T& s, bool asBuffer, napi_value& result) {
218
246
  if (asBuffer) {
@@ -222,14 +250,39 @@ void Convert (napi_env env, const T& s, bool asBuffer, napi_value& result) {
222
250
  }
223
251
  }
224
252
 
253
+ struct NapiSlice : public rocksdb::Slice {
254
+ NapiSlice (napi_env env, napi_value from) {
255
+ if (IsString(env, from)) {
256
+ napi_get_value_string_utf8(env, from, nullptr, 0, &size_);
257
+ char* data;
258
+ if (size_ + 1 < stack_.size()) {
259
+ data = stack_.data();
260
+ } else {
261
+ heap_.reset(new char[size_ + 1]);
262
+ data = heap_.get();
263
+ }
264
+ data[size_] = 0;
265
+ napi_get_value_string_utf8(env, from, data, size_ + 1, &size_);
266
+ data_ = data;
267
+ } else if (IsBuffer(env, from)) {
268
+ void* data;
269
+ napi_get_buffer_info(env, from, &data, &size_);
270
+ data_ = static_cast<char*>(data);
271
+ }
272
+ }
273
+
274
+ std::unique_ptr<char[]> heap_;
275
+ std::array<char, 8192> stack_;
276
+ };
277
+
225
278
  /**
226
279
  * Base worker class. Handles the async work. Derived classes can override the
227
280
  * following virtual methods (listed in the order in which they're called):
228
281
  *
229
282
  * - Execute (abstract, worker pool thread): main work
230
- * - Then (main thread): call JS callback on success
231
- * - Catch (main thread): call JS callback on error
232
- * - Finally (main thread): do cleanup regardless of success
283
+ * - OnOk (main thread): call JS callback on success
284
+ * - OnError (main thread): call JS callback on error
285
+ * - Destroy (main thread): do cleanup regardless of success
233
286
  */
234
287
  struct BaseWorker {
235
288
  BaseWorker (napi_env env,
@@ -253,7 +306,7 @@ struct BaseWorker {
253
306
 
254
307
  static void Execute (napi_env env, void* data) {
255
308
  auto self = reinterpret_cast<BaseWorker*>(data);
256
- self->status_ = self->Execute();
309
+ self->status_ = self->Execute(*self->database_);
257
310
  }
258
311
 
259
312
  static void Complete (napi_env env, napi_status status, void* data) {
@@ -263,53 +316,32 @@ struct BaseWorker {
263
316
  napi_get_reference_value(env, self->callbackRef_, &callback);
264
317
 
265
318
  if (self->status_.ok()) {
266
- self->Then(env, callback);
319
+ self->OnOk(env, callback);
267
320
  } else {
268
- self->Catch(env, callback);
321
+ self->OnError(env, callback, ToError(env, self->status_));
269
322
  }
270
323
 
271
- self->Finally(env);
324
+ self->Destroy(env);
325
+
326
+ napi_delete_reference(env, self->callbackRef_);
327
+ napi_delete_async_work(env, self->asyncWork_);
328
+
329
+ delete self;
272
330
  }
273
331
 
274
- virtual rocksdb::Status Execute () = 0;
332
+ virtual rocksdb::Status Execute (Database& database) = 0;
275
333
 
276
- virtual void Then (napi_env env, napi_value callback) {
334
+ virtual void OnOk (napi_env env, napi_value callback) {
277
335
  napi_value argv;
278
336
  napi_get_null(env, &argv);
279
337
  CallFunction(env, callback, 1, &argv);
280
338
  }
281
339
 
282
- virtual void Catch (napi_env env, napi_value callback) {
283
- napi_value argv;
284
-
285
- const auto msg = status_.ToString();
286
-
287
- if (status_.IsNotFound()) {
288
- argv = CreateCodeError(env, "LEVEL_NOT_FOUND", msg);
289
- } else if (status_.IsCorruption()) {
290
- argv = CreateCodeError(env, "LEVEL_CORRUPTION", msg);
291
- } else if (status_.IsIOError()) {
292
- if (msg.find("IO error: lock ") != std::string::npos) { // env_posix.cc
293
- argv = CreateCodeError(env, "LEVEL_LOCKED", msg);
294
- } else if (msg.find("IO error: LockFile ") != std::string::npos) { // env_win.cc
295
- argv = CreateCodeError(env, "LEVEL_LOCKED", msg);
296
- } else if (msg.find("IO error: While lock file") != std::string::npos) { // env_mac.cc
297
- argv = CreateCodeError(env, "LEVEL_LOCKED", msg);
298
- } else {
299
- argv = CreateCodeError(env, "LEVEL_IO_ERROR", msg);
300
- }
301
- } else {
302
- argv = CreateError(env, msg);
303
- }
304
-
305
- CallFunction(env, callback, 1, &argv);
340
+ virtual void OnError (napi_env env, napi_value callback, napi_value err) {
341
+ CallFunction(env, callback, 1, &err);
306
342
  }
307
343
 
308
- virtual void Finally (napi_env env) {
309
- napi_delete_reference(env, callbackRef_);
310
- napi_delete_async_work(env, asyncWork_);
311
-
312
- delete this;
344
+ virtual void Destroy (napi_env env) {
313
345
  }
314
346
 
315
347
  void Queue (napi_env env) {
@@ -317,7 +349,6 @@ struct BaseWorker {
317
349
  }
318
350
 
319
351
  Database* database_;
320
-
321
352
  private:
322
353
  napi_ref callbackRef_;
323
354
  napi_async_work asyncWork_;
@@ -345,56 +376,6 @@ struct Database {
345
376
  db_.reset();
346
377
  }
347
378
 
348
- rocksdb::Status Put (const rocksdb::WriteOptions& options,
349
- const std::string& key,
350
- const std::string& value) {
351
- return db_->Put(options, db_->DefaultColumnFamily(), key, value);
352
- }
353
-
354
- rocksdb::Status Get (const rocksdb::ReadOptions& options,
355
- const std::string& key,
356
- rocksdb::PinnableSlice& value) {
357
- return db_->Get(options, db_->DefaultColumnFamily(), key, &value);
358
- }
359
-
360
- rocksdb::Status Del (const rocksdb::WriteOptions& options,
361
- const std::string& key) {
362
- return db_->Delete(options, db_->DefaultColumnFamily(), key);
363
- }
364
-
365
- rocksdb::Status WriteBatch (const rocksdb::WriteOptions& options,
366
- rocksdb::WriteBatch* batch) {
367
- return db_->Write(options, batch);
368
- }
369
-
370
- uint64_t ApproximateSize (const rocksdb::Range* range) {
371
- uint64_t size = 0;
372
- db_->GetApproximateSizes(range, 1, &size);
373
- return size;
374
- }
375
-
376
- void CompactRange (const rocksdb::Slice* start,
377
- const rocksdb::Slice* end) {
378
- rocksdb::CompactRangeOptions options;
379
- db_->CompactRange(options, start, end);
380
- }
381
-
382
- void GetProperty (const std::string& property, std::string& value) {
383
- db_->GetProperty(property, &value);
384
- }
385
-
386
- const rocksdb::Snapshot* NewSnapshot () {
387
- return db_->GetSnapshot();
388
- }
389
-
390
- rocksdb::Iterator* NewIterator (const rocksdb::ReadOptions& options) {
391
- return db_->NewIterator(options);
392
- }
393
-
394
- void ReleaseSnapshot (const rocksdb::Snapshot* snapshot) {
395
- return db_->ReleaseSnapshot(snapshot);
396
- }
397
-
398
379
  void AttachIterator (napi_env env, Iterator* iterator) {
399
380
  iterators_.insert(iterator);
400
381
  IncrementPriorityWork(env);
@@ -442,19 +423,19 @@ struct PriorityWorker : public BaseWorker {
442
423
 
443
424
  virtual ~PriorityWorker () {}
444
425
 
445
- void Finally (napi_env env) override {
426
+ void Destroy (napi_env env) override {
446
427
  database_->DecrementPriorityWork(env);
447
- BaseWorker::Finally(env);
428
+ BaseWorker::Destroy(env);
448
429
  }
449
430
  };
450
431
 
451
432
  struct BaseIterator {
452
433
  BaseIterator(Database* database,
453
434
  const bool reverse,
454
- const std::string* lt,
455
- const std::string* lte,
456
- const std::string* gt,
457
- const std::string* gte,
435
+ const std::optional<std::string>& lt,
436
+ const std::optional<std::string>& lte,
437
+ const std::optional<std::string>& gt,
438
+ const std::optional<std::string>& gte,
458
439
  const int limit,
459
440
  const bool fillCache)
460
441
  : database_(database),
@@ -462,10 +443,10 @@ struct BaseIterator {
462
443
  lte_(lte),
463
444
  gt_(gt),
464
445
  gte_(gte),
465
- snapshot_(database->NewSnapshot(), [this](const rocksdb::Snapshot* ptr) {
466
- database_->ReleaseSnapshot(ptr);
446
+ snapshot_(database_->db_->GetSnapshot(), [this](const rocksdb::Snapshot* ptr) {
447
+ database_->db_->ReleaseSnapshot(ptr);
467
448
  }),
468
- iterator_(database->NewIterator([&]{
449
+ iterator_(database->db_->NewIterator([&]{
469
450
  rocksdb::ReadOptions options;
470
451
  if (lt_ && !lte_) {
471
452
  upper_bound_ = rocksdb::Slice(lt_->data(), lt_->size());
@@ -501,13 +482,7 @@ struct BaseIterator {
501
482
  iterator_->Next();
502
483
  }
503
484
  } else if (reverse_ && lte_) {
504
- iterator_->Seek(*lte_);
505
-
506
- if (!iterator_->Valid()) {
507
- iterator_->SeekToLast();
508
- } else if (iterator_->key().compare(*lte_) > 0) {
509
- iterator_->Prev();
510
- }
485
+ iterator_->SeekForPrev(*lte_);
511
486
  } else if (reverse_) {
512
487
  iterator_->SeekToLast();
513
488
  } else {
@@ -518,25 +493,16 @@ struct BaseIterator {
518
493
  void Seek (const std::string& target) {
519
494
  didSeek_ = true;
520
495
 
521
- if (OutOfRange(target)) {
522
- return SeekToEnd();
523
- }
524
-
525
- iterator_->Seek(target);
496
+ // TODO (fix): Only check for (gt && !gte) and lte.
497
+ // See, https://github.com/facebook/rocksdb/issues/9904.
526
498
 
527
- if (iterator_->Valid()) {
528
- const auto cmp = iterator_->key().compare(target);
529
- if (reverse_ ? cmp > 0 : cmp < 0) {
530
- Next();
531
- }
499
+ if (OutOfRange(target)) {
500
+ SeekToLast();
501
+ Next();
502
+ } else if (reverse_) {
503
+ iterator_->SeekForPrev(target);
532
504
  } else {
533
- SeekToFirst();
534
- if (iterator_->Valid()) {
535
- const auto cmp = iterator_->key().compare(target);
536
- if (reverse_ ? cmp > 0 : cmp < 0) {
537
- SeekToEnd();
538
- }
539
- }
505
+ iterator_->Seek(target);
540
506
  }
541
507
  }
542
508
 
@@ -580,11 +546,6 @@ struct BaseIterator {
580
546
  else iterator_->SeekToLast();
581
547
  }
582
548
 
583
- void SeekToEnd () {
584
- SeekToLast();
585
- Next();
586
- }
587
-
588
549
  rocksdb::Slice CurrentKey () const {
589
550
  return iterator_->key();
590
551
  }
@@ -616,10 +577,10 @@ struct BaseIterator {
616
577
  Database* database_;
617
578
 
618
579
  private:
619
- const std::unique_ptr<const std::string> lt_;
620
- const std::unique_ptr<const std::string> lte_;
621
- const std::unique_ptr<const std::string> gt_;
622
- const std::unique_ptr<const std::string> gte_;
580
+ const std::optional<std::string> lt_;
581
+ const std::optional<std::string> lte_;
582
+ const std::optional<std::string> gt_;
583
+ const std::optional<std::string> gte_;
623
584
  rocksdb::Slice lower_bound_;
624
585
  rocksdb::Slice upper_bound_;
625
586
  std::shared_ptr<const rocksdb::Snapshot> snapshot_;
@@ -636,10 +597,10 @@ struct Iterator final : public BaseIterator {
636
597
  const bool keys,
637
598
  const bool values,
638
599
  const int limit,
639
- const std::string* lt,
640
- const std::string* lte,
641
- const std::string* gt,
642
- const std::string* gte,
600
+ const std::optional<std::string>& lt,
601
+ const std::optional<std::string>& lte,
602
+ const std::optional<std::string>& gt,
603
+ const std::optional<std::string>& gte,
643
604
  const bool fillCache,
644
605
  const bool keyAsBuffer,
645
606
  const bool valueAsBuffer,
@@ -790,8 +751,8 @@ struct OpenWorker final : public PriorityWorker {
790
751
  );
791
752
  }
792
753
 
793
- rocksdb::Status Execute () override {
794
- return database_->Open(options_, readOnly_, location_.c_str());
754
+ rocksdb::Status Execute (Database& database) override {
755
+ return database.Open(options_, readOnly_, location_.c_str());
795
756
  }
796
757
 
797
758
  rocksdb::Options options_;
@@ -839,9 +800,8 @@ struct CloseWorker final : public BaseWorker {
839
800
  napi_value callback)
840
801
  : BaseWorker(env, database, callback, "leveldown.db.close") {}
841
802
 
842
- rocksdb::Status Execute () override {
843
- database_->CloseDatabase();
844
- return rocksdb::Status::OK();
803
+ rocksdb::Status Execute (Database& database) override {
804
+ return database.db_->Close();
845
805
  }
846
806
  };
847
807
 
@@ -866,41 +826,15 @@ NAPI_METHOD(db_close) {
866
826
  return 0;
867
827
  }
868
828
 
869
- struct PutWorker final : public PriorityWorker {
870
- PutWorker (napi_env env,
871
- Database* database,
872
- napi_value callback,
873
- const std::string& key,
874
- const std::string& value,
875
- bool sync)
876
- : PriorityWorker(env, database, callback, "rocks_level.db.put"),
877
- key_(key), value_(value), sync_(sync) {
878
- }
879
-
880
- rocksdb::Status Execute () override {
881
- rocksdb::WriteOptions options;
882
- options.sync = sync_;
883
- return database_->Put(options, key_, value_);
884
- }
885
-
886
- const std::string key_;
887
- const std::string value_;
888
- const bool sync_;
889
- };
890
-
891
829
  NAPI_METHOD(db_put) {
892
- NAPI_ARGV(5);
830
+ NAPI_ARGV(4);
893
831
  NAPI_DB_CONTEXT();
894
832
 
895
833
  const auto key = ToString(env, argv[1]);
896
834
  const auto value = ToString(env, argv[2]);
897
- const auto sync = BooleanProperty(env, argv[3], "sync", false);
898
- const auto callback = argv[4];
899
-
900
- auto worker = new PutWorker(env, database, callback, key, value, sync);
901
- worker->Queue(env);
902
835
 
903
- return 0;
836
+ rocksdb::WriteOptions options;
837
+ return ToError(env, database->db_->Put(options, key, value));
904
838
  }
905
839
 
906
840
  struct GetWorker final : public PriorityWorker {
@@ -911,16 +845,22 @@ struct GetWorker final : public PriorityWorker {
911
845
  const bool asBuffer,
912
846
  const bool fillCache)
913
847
  : PriorityWorker(env, database, callback, "rocks_level.db.get"),
914
- key_(key), asBuffer_(asBuffer), fillCache_(fillCache) {
848
+ key_(key), asBuffer_(asBuffer), fillCache_(fillCache),
849
+ snapshot_(database_->db_->GetSnapshot(), [this](const rocksdb::Snapshot* ptr) {
850
+ database_->db_->ReleaseSnapshot(ptr);
851
+ }) {
915
852
  }
916
853
 
917
- rocksdb::Status Execute () override {
854
+ rocksdb::Status Execute (Database& database) override {
918
855
  rocksdb::ReadOptions options;
919
856
  options.fill_cache = fillCache_;
920
- return database_->Get(options, key_, value_);
857
+ options.snapshot = snapshot_.get();
858
+ auto status = database.db_->Get(options, database.db_->DefaultColumnFamily(), key_, &value_);
859
+ snapshot_.reset();
860
+ return status;
921
861
  }
922
862
 
923
- void Then (napi_env env, napi_value callback) override {
863
+ void OnOk (napi_env env, napi_value callback) override {
924
864
  napi_value argv[2];
925
865
  napi_get_null(env, &argv[0]);
926
866
  Convert(env, std::move(value_), asBuffer_, argv[1]);
@@ -932,6 +872,7 @@ private:
932
872
  rocksdb::PinnableSlice value_;
933
873
  const bool asBuffer_;
934
874
  const bool fillCache_;
875
+ std::shared_ptr<const rocksdb::Snapshot> snapshot_;
935
876
  };
936
877
 
937
878
  NAPI_METHOD(db_get) {
@@ -959,28 +900,21 @@ struct GetManyWorker final : public PriorityWorker {
959
900
  const bool fillCache)
960
901
  : PriorityWorker(env, database, callback, "leveldown.get.many"),
961
902
  keys_(keys), valueAsBuffer_(valueAsBuffer), fillCache_(fillCache),
962
- snapshot_(database->NewSnapshot()) {
903
+ snapshot_(database_->db_->GetSnapshot(), [this](const rocksdb::Snapshot* ptr) {
904
+ database_->db_->ReleaseSnapshot(ptr);
905
+ }) {
963
906
  }
964
907
 
965
- ~GetManyWorker () {
966
- if (snapshot_) {
967
- database_->ReleaseSnapshot(snapshot_);
968
- snapshot_ = nullptr;
969
- }
970
- }
971
-
972
- rocksdb::Status Execute () override {
908
+ rocksdb::Status Execute (Database& database) override {
973
909
  rocksdb::ReadOptions options;
974
- options.snapshot = snapshot_;
975
910
  options.fill_cache = fillCache_;
976
-
977
- status_ = database_->db_->MultiGet(
911
+ options.snapshot = snapshot_.get();
912
+
913
+ status_ = database.db_->MultiGet(
978
914
  options,
979
915
  std::vector<rocksdb::Slice>(keys_.begin(), keys_.end()),
980
916
  &values_
981
917
  );
982
-
983
- database_->ReleaseSnapshot(snapshot_);
984
918
  snapshot_ = nullptr;
985
919
 
986
920
  for (auto status : status_) {
@@ -992,7 +926,7 @@ struct GetManyWorker final : public PriorityWorker {
992
926
  return rocksdb::Status::OK();
993
927
  }
994
928
 
995
- void Then (napi_env env, napi_value callback) override {
929
+ void OnOk (napi_env env, napi_value callback) override {
996
930
  const auto size = values_.size();
997
931
 
998
932
  napi_value array;
@@ -1020,7 +954,7 @@ private:
1020
954
  std::vector<rocksdb::Status> status_;
1021
955
  const bool valueAsBuffer_;
1022
956
  const bool fillCache_;
1023
- const rocksdb::Snapshot* snapshot_;
957
+ std::shared_ptr<const rocksdb::Snapshot> snapshot_;
1024
958
  };
1025
959
 
1026
960
  NAPI_METHOD(db_get_many) {
@@ -1039,190 +973,65 @@ NAPI_METHOD(db_get_many) {
1039
973
  return 0;
1040
974
  }
1041
975
 
1042
- struct DelWorker final : public PriorityWorker {
1043
- DelWorker (napi_env env,
1044
- Database* database,
1045
- napi_value callback,
1046
- const std::string& key,
1047
- bool sync)
1048
- : PriorityWorker(env, database, callback, "rocks_level.db.del"),
1049
- key_(key), sync_(sync) {
1050
- }
1051
-
1052
- rocksdb::Status Execute () override {
1053
- rocksdb::WriteOptions options;
1054
- options.sync = sync_;
1055
- return database_->Del(options, key_);
1056
- }
1057
-
1058
- const std::string key_;
1059
- const bool sync_;
1060
- };
1061
-
1062
976
  NAPI_METHOD(db_del) {
1063
- NAPI_ARGV(4);
977
+ NAPI_ARGV(3);
1064
978
  NAPI_DB_CONTEXT();
1065
979
 
1066
980
  const auto key = ToString(env, argv[1]);
1067
- const auto sync = BooleanProperty(env, argv[2], "sync", false);
1068
- const auto callback = argv[3];
1069
981
 
1070
- auto worker = new DelWorker(env, database, callback, key, sync);
1071
- worker->Queue(env);
1072
-
1073
- return 0;
982
+ rocksdb::WriteOptions options;
983
+ return ToError(env, database->db_->Delete(options, key));
1074
984
  }
1075
985
 
1076
- struct ClearWorker final : public PriorityWorker {
1077
- ClearWorker (napi_env env,
1078
- Database* database,
1079
- napi_value callback,
1080
- const bool reverse,
1081
- const int limit,
1082
- const std::string* lt,
1083
- const std::string* lte,
1084
- const std::string* gt,
1085
- const std::string* gte)
1086
- : PriorityWorker(env, database, callback, "rocks_level.db.clear"),
1087
- iterator_(database, reverse, lt, lte, gt, gte, limit, false) {
1088
- }
1089
-
1090
- rocksdb::Status Execute () override {
1091
- iterator_.SeekToRange();
1092
-
1093
- // TODO: add option
1094
- const uint32_t hwm = 16 * 1024;
1095
-
1096
- rocksdb::WriteBatch batch;
1097
- rocksdb::WriteOptions options;
1098
- rocksdb::Status status;
1099
-
1100
- while (true) {
1101
- size_t bytesRead = 0;
1102
-
1103
- while (bytesRead <= hwm && iterator_.Valid() && iterator_.Increment()) {
1104
- const auto key = iterator_.CurrentKey();
1105
- batch.Delete(key);
1106
- bytesRead += key.size();
1107
- iterator_.Next();
1108
- }
1109
-
1110
- status = iterator_.Status();
1111
- if (!status.ok() || bytesRead == 0) {
1112
- break;
1113
- }
1114
-
1115
- status = database_->WriteBatch(options, &batch);
1116
- if (!status.ok()) {
1117
- break;
1118
- }
1119
-
1120
- batch.Clear();
1121
- }
1122
-
1123
- iterator_.Close();
1124
-
1125
- return status;
1126
- }
1127
-
1128
- private:
1129
- BaseIterator iterator_;
1130
- };
1131
-
1132
986
  NAPI_METHOD(db_clear) {
1133
- NAPI_ARGV(3);
987
+ NAPI_ARGV(2);
1134
988
  NAPI_DB_CONTEXT();
1135
989
 
1136
- napi_value options = argv[1];
1137
- napi_value callback = argv[2];
990
+ const auto reverse = BooleanProperty(env, argv[1], "reverse", false);
991
+ const auto limit = Int32Property(env, argv[1], "limit", -1);
1138
992
 
1139
- const auto reverse = BooleanProperty(env, options, "reverse", false);
1140
- const auto limit = Int32Property(env, options, "limit", -1);
1141
-
1142
- const auto lt = RangeOption(env, options, "lt");
1143
- const auto lte = RangeOption(env, options, "lte");
1144
- const auto gt = RangeOption(env, options, "gt");
1145
- const auto gte = RangeOption(env, options, "gte");
993
+ const auto lt = RangeOption(env, argv[1], "lt");
994
+ const auto lte = RangeOption(env, argv[1], "lte");
995
+ const auto gt = RangeOption(env, argv[1], "gt");
996
+ const auto gte = RangeOption(env, argv[1], "gte");
1146
997
 
1147
- auto worker = new ClearWorker(env, database, callback, reverse, limit, lt, lte, gt, gte);
1148
- worker->Queue(env);
998
+ BaseIterator it(database, reverse, lt, lte, gt, gte, limit, false);
1149
999
 
1150
- return 0;
1151
- }
1000
+ it.SeekToRange();
1152
1001
 
1153
- struct ApproximateSizeWorker final : public PriorityWorker {
1154
- ApproximateSizeWorker (napi_env env,
1155
- Database* database,
1156
- napi_value callback,
1157
- const std::string& start,
1158
- const std::string& end)
1159
- : PriorityWorker(env, database, callback, "rocks_level.db.approximate_size"),
1160
- start_(start), end_(end) {}
1161
-
1162
- rocksdb::Status Execute () override {
1163
- rocksdb::Range range(start_, end_);
1164
- size_ = database_->ApproximateSize(&range);
1165
- return rocksdb::Status::OK();
1166
- }
1002
+ // TODO: add option
1003
+ const uint32_t hwm = 16 * 1024;
1167
1004
 
1168
- void Then (napi_env env, napi_value callback) override {
1169
- napi_value argv[2];
1170
- napi_get_null(env, &argv[0]);
1171
- napi_create_int64(env, size_, &argv[1]);
1172
- CallFunction(env, callback, 2, argv);
1173
- }
1174
-
1175
- std::string start_;
1176
- std::string end_;
1177
- uint64_t size_;
1178
- };
1179
-
1180
- NAPI_METHOD(db_approximate_size) {
1181
- NAPI_ARGV(4);
1182
- NAPI_DB_CONTEXT();
1005
+ rocksdb::WriteBatch batch;
1006
+ rocksdb::WriteOptions options;
1007
+ rocksdb::Status status;
1008
+
1009
+ while (true) {
1010
+ size_t bytesRead = 0;
1183
1011
 
1184
- const auto start = ToString(env, argv[1]);
1185
- const auto end = ToString(env, argv[2]);
1186
- const auto callback = argv[3];
1012
+ while (bytesRead <= hwm && it.Valid() && it.Increment()) {
1013
+ const auto key = it.CurrentKey();
1014
+ batch.Delete(key);
1015
+ bytesRead += key.size();
1016
+ it.Next();
1017
+ }
1187
1018
 
1188
- auto worker = new ApproximateSizeWorker(env, database, callback, start, end);
1189
- worker->Queue(env);
1019
+ status = it.Status();
1020
+ if (!status.ok() || bytesRead == 0) {
1021
+ break;
1022
+ }
1190
1023
 
1191
- return 0;
1192
- }
1024
+ status = database->db_->Write(options, &batch);
1025
+ if (!status.ok()) {
1026
+ break;
1027
+ }
1193
1028
 
1194
- struct CompactRangeWorker final : public PriorityWorker {
1195
- CompactRangeWorker (napi_env env,
1196
- Database* database,
1197
- napi_value callback,
1198
- const std::string& start,
1199
- const std::string& end)
1200
- : PriorityWorker(env, database, callback, "rocks_level.db.compact_range"),
1201
- start_(start), end_(end) {}
1202
-
1203
- rocksdb::Status Execute () override {
1204
- rocksdb::Slice start = start_;
1205
- rocksdb::Slice end = end_;
1206
- database_->CompactRange(&start, &end);
1207
- return rocksdb::Status::OK();
1029
+ batch.Clear();
1208
1030
  }
1209
1031
 
1210
- const std::string start_;
1211
- const std::string end_;
1212
- };
1213
-
1214
- NAPI_METHOD(db_compact_range) {
1215
- NAPI_ARGV(4);
1216
- NAPI_DB_CONTEXT();
1217
-
1218
- const auto start = ToString(env, argv[1]);
1219
- const auto end = ToString(env, argv[2]);
1220
- const auto callback = argv[3];
1221
-
1222
- auto worker = new CompactRangeWorker(env, database, callback, start, end);
1223
- worker->Queue(env);
1224
-
1225
- return 0;
1032
+ it.Close();
1033
+
1034
+ return ToError(env, status);
1226
1035
  }
1227
1036
 
1228
1037
  NAPI_METHOD(db_get_property) {
@@ -1232,7 +1041,7 @@ NAPI_METHOD(db_get_property) {
1232
1041
  const auto property = ToString(env, argv[1]);
1233
1042
 
1234
1043
  std::string value;
1235
- database->GetProperty(property, value);
1044
+ database->db_->GetProperty(property, &value);
1236
1045
 
1237
1046
  napi_value result;
1238
1047
  napi_create_string_utf8(env, value.data(), value.size(), &result);
@@ -1240,62 +1049,6 @@ NAPI_METHOD(db_get_property) {
1240
1049
  return result;
1241
1050
  }
1242
1051
 
1243
- struct DestroyWorker final : public BaseWorker {
1244
- DestroyWorker (napi_env env,
1245
- const std::string& location,
1246
- napi_value callback)
1247
- : BaseWorker(env, nullptr, callback, "rocks_level.destroy_db"),
1248
- location_(location) {}
1249
-
1250
- ~DestroyWorker () {}
1251
-
1252
- rocksdb::Status Execute () override {
1253
- rocksdb::Options options;
1254
- return rocksdb::DestroyDB(location_, options);
1255
- }
1256
-
1257
- const std::string location_;
1258
- };
1259
-
1260
- NAPI_METHOD(destroy_db) {
1261
- NAPI_ARGV(2);
1262
-
1263
- const auto location = ToString(env, argv[0]);
1264
- const auto callback = argv[1];
1265
-
1266
- auto worker = new DestroyWorker(env, location, callback);
1267
- worker->Queue(env);
1268
-
1269
- return 0;
1270
- }
1271
-
1272
- struct RepairWorker final : public BaseWorker {
1273
- RepairWorker (napi_env env,
1274
- const std::string& location,
1275
- napi_value callback)
1276
- : BaseWorker(env, nullptr, callback, "rocks_level.repair_db"),
1277
- location_(location) {}
1278
-
1279
- rocksdb::Status Execute () override {
1280
- rocksdb::Options options;
1281
- return rocksdb::RepairDB(location_, options);
1282
- }
1283
-
1284
- const std::string location_;
1285
- };
1286
-
1287
- NAPI_METHOD(repair_db) {
1288
- NAPI_ARGV(2);
1289
-
1290
- const auto location = ToString(env, argv[1]);
1291
- const auto callback = argv[1];
1292
-
1293
- auto worker = new RepairWorker(env, location, callback);
1294
- worker->Queue(env);
1295
-
1296
- return 0;
1297
- }
1298
-
1299
1052
  static void FinalizeIterator (napi_env env, void* data, void* hint) {
1300
1053
  if (data) {
1301
1054
  delete reinterpret_cast<Iterator*>(data);
@@ -1326,9 +1079,7 @@ NAPI_METHOD(iterator_init) {
1326
1079
  keyAsBuffer, valueAsBuffer, highWaterMarkBytes);
1327
1080
  napi_value result;
1328
1081
 
1329
- NAPI_STATUS_THROWS(napi_create_external(env, iterator,
1330
- FinalizeIterator,
1331
- nullptr, &result));
1082
+ NAPI_STATUS_THROWS(napi_create_external(env, iterator, FinalizeIterator, nullptr, &result));
1332
1083
 
1333
1084
  // Prevent GC of JS object before the iterator is closed (explicitly or on
1334
1085
  // db close) and keep track of non-closed iterators to end them on db close.
@@ -1355,14 +1106,14 @@ struct CloseIteratorWorker final : public BaseWorker {
1355
1106
  : BaseWorker(env, iterator->database_, callback, "leveldown.iterator.end"),
1356
1107
  iterator_(iterator) {}
1357
1108
 
1358
- rocksdb::Status Execute () override {
1109
+ rocksdb::Status Execute (Database& database) override {
1359
1110
  iterator_->Close();
1360
1111
  return rocksdb::Status::OK();
1361
1112
  }
1362
1113
 
1363
- void Finally (napi_env env) override {
1114
+ void Destroy (napi_env env) override {
1364
1115
  iterator_->Detach(env);
1365
- BaseWorker::Finally(env);
1116
+ BaseWorker::Destroy(env);
1366
1117
  }
1367
1118
 
1368
1119
  private:
@@ -1390,7 +1141,7 @@ struct NextWorker final : public BaseWorker {
1390
1141
  "leveldown.iterator.next"),
1391
1142
  iterator_(iterator), size_(size) {}
1392
1143
 
1393
- rocksdb::Status Execute () override {
1144
+ rocksdb::Status Execute (Database& database) override {
1394
1145
  if (!iterator_->DidSeek()) {
1395
1146
  iterator_->SeekToRange();
1396
1147
  }
@@ -1398,7 +1149,6 @@ struct NextWorker final : public BaseWorker {
1398
1149
  // Limit the size of the cache to prevent starving the event loop
1399
1150
  // in JS-land while we're recursively calling process.nextTick().
1400
1151
 
1401
- finished_ = false;
1402
1152
  cache_.reserve(size_ * 2);
1403
1153
  size_t bytesRead = 0;
1404
1154
 
@@ -1435,7 +1185,7 @@ struct NextWorker final : public BaseWorker {
1435
1185
  return iterator_->Status();
1436
1186
  }
1437
1187
 
1438
- void Then (napi_env env, napi_value callback) override {
1188
+ void OnOk (napi_env env, napi_value callback) override {
1439
1189
  const auto size = cache_.size();
1440
1190
  napi_value result;
1441
1191
  napi_create_array_with_length(env, size, &result);
@@ -1483,71 +1233,44 @@ NAPI_METHOD(iterator_nextv) {
1483
1233
  return 0;
1484
1234
  }
1485
1235
 
1486
- struct BatchWorker final : public PriorityWorker {
1487
- BatchWorker (napi_env env,
1488
- Database* database,
1489
- napi_value callback,
1490
- napi_value array,
1491
- const bool sync)
1492
- : PriorityWorker(env, database, callback, "rocks_level.batch.do"), sync_(sync) {
1493
- uint32_t length;
1494
- NAPI_STATUS_THROWS_VOID(napi_get_array_length(env, array, &length));
1495
-
1496
- for (uint32_t i = 0; i < length; i++) {
1497
- napi_value element;
1498
- NAPI_STATUS_THROWS_VOID(napi_get_element(env, array, i, &element));
1499
-
1500
- if (!IsObject(env, element)) continue;
1236
+ NAPI_METHOD(batch_do) {
1237
+ NAPI_ARGV(3);
1238
+ NAPI_DB_CONTEXT();
1501
1239
 
1502
- const auto type = StringProperty(env, element, "type");
1240
+ const auto operations = argv[1];
1503
1241
 
1504
- if (type == "del") {
1505
- if (!HasProperty(env, element, "key")) continue;
1506
- const auto key = ToString(env, GetProperty(env, element, "key"));
1242
+ rocksdb::WriteBatch batch;
1507
1243
 
1508
- batch_.Delete(key);
1509
- if (!hasData_) hasData_ = true;
1510
- } else if (type == "put") {
1511
- if (!HasProperty(env, element, "key")) continue;
1512
- if (!HasProperty(env, element, "value")) continue;
1244
+ uint32_t length;
1245
+ NAPI_STATUS_THROWS(napi_get_array_length(env, operations, &length));
1513
1246
 
1514
- const auto key = ToString(env, GetProperty(env, element, "key"));
1515
- const auto value = ToString(env, GetProperty(env, element, "value"));
1247
+ for (uint32_t i = 0; i < length; i++) {
1248
+ napi_value element;
1249
+ NAPI_STATUS_THROWS(napi_get_element(env, operations, i, &element));
1516
1250
 
1517
- batch_.Put(key, value);
1518
- if (!hasData_) hasData_ = true;
1519
- }
1520
- }
1521
- }
1251
+ if (!IsObject(env, element)) continue;
1522
1252
 
1523
- rocksdb::Status Execute () override {
1524
- if (!hasData_) {
1525
- return rocksdb::Status::OK();
1526
- }
1253
+ const auto type = StringProperty(env, element, "type");
1527
1254
 
1528
- rocksdb::WriteOptions options;
1529
- options.sync = sync_;
1530
- return database_->WriteBatch(options, &batch_);
1531
- }
1255
+ if (type == "del") {
1256
+ if (!HasProperty(env, element, "key")) continue;
1532
1257
 
1533
- private:
1534
- rocksdb::WriteBatch batch_;
1535
- const bool sync_;
1536
- bool hasData_;
1537
- };
1258
+ const auto key = NapiSlice(env, GetProperty(env, element, "key"));
1538
1259
 
1539
- NAPI_METHOD(batch_do) {
1540
- NAPI_ARGV(4);
1541
- NAPI_DB_CONTEXT();
1260
+ batch.Delete(key);
1261
+ } else if (type == "put") {
1262
+ if (!HasProperty(env, element, "key")) continue;
1263
+ if (!HasProperty(env, element, "value")) continue;
1542
1264
 
1543
- const auto array = argv[1];
1544
- const auto sync = BooleanProperty(env, argv[2], "sync", false);
1545
- const auto callback = argv[3];
1265
+ const auto key = NapiSlice(env, GetProperty(env, element, "key"));
1266
+ const auto value = NapiSlice(env, GetProperty(env, element, "value"));
1546
1267
 
1547
- auto worker = new BatchWorker(env, database, callback, array, sync);
1548
- worker->Queue(env);
1268
+ batch.Put(key, value);
1269
+ }
1270
+ }
1549
1271
 
1550
- return 0;
1272
+ rocksdb::WriteOptions options;
1273
+ return ToError(env, database->db_->Write(options, &batch));
1551
1274
  }
1552
1275
 
1553
1276
  static void FinalizeBatch (napi_env env, void* data, void* hint) {
@@ -1571,8 +1294,8 @@ NAPI_METHOD(batch_put) {
1571
1294
  NAPI_ARGV(3);
1572
1295
  NAPI_BATCH_CONTEXT();
1573
1296
 
1574
- const auto key = ToString(env, argv[1]);
1575
- const auto value = ToString(env, argv[2]);
1297
+ const auto key = NapiSlice(env, argv[1]);
1298
+ const auto value = NapiSlice(env, argv[2]);
1576
1299
 
1577
1300
  batch->Put(key, value);
1578
1301
 
@@ -1583,7 +1306,7 @@ NAPI_METHOD(batch_del) {
1583
1306
  NAPI_ARGV(2);
1584
1307
  NAPI_BATCH_CONTEXT();
1585
1308
 
1586
- const auto key = ToString(env, argv[1]);
1309
+ const auto key = NapiSlice(env, argv[1]);
1587
1310
 
1588
1311
  batch->Delete(key);
1589
1312
 
@@ -1599,51 +1322,15 @@ NAPI_METHOD(batch_clear) {
1599
1322
  return 0;
1600
1323
  }
1601
1324
 
1602
- struct BatchWriteWorker final : public PriorityWorker {
1603
- BatchWriteWorker (napi_env env,
1604
- Database* database,
1605
- napi_value batch,
1606
- napi_value callback,
1607
- const bool sync)
1608
- : PriorityWorker(env, database, callback, "leveldown.batch.write"),
1609
- sync_(sync) {
1610
-
1611
- NAPI_STATUS_THROWS_VOID(napi_get_value_external(env, batch, reinterpret_cast<void**>(&batch_)));
1612
-
1613
- // Prevent GC of batch object before we execute
1614
- NAPI_STATUS_THROWS_VOID(napi_create_reference(env, batch, 1, &batchRef_));
1615
- }
1616
-
1617
- rocksdb::Status Execute () override {
1618
- rocksdb::WriteOptions options;
1619
- options.sync = sync_;
1620
- return database_->WriteBatch(options, batch_);
1621
- }
1622
-
1623
- void Finally (napi_env env) override {
1624
- napi_delete_reference(env, batchRef_);
1625
- PriorityWorker::Finally(env);
1626
- }
1627
-
1628
- private:
1629
- rocksdb::WriteBatch* batch_;
1630
- const bool sync_;
1631
- napi_ref batchRef_;
1632
- };
1633
-
1634
1325
  NAPI_METHOD(batch_write) {
1635
1326
  NAPI_ARGV(4);
1636
1327
  NAPI_DB_CONTEXT();
1637
1328
 
1638
- const auto batch = argv[1];
1639
- const auto options = argv[2];
1640
- const auto sync = BooleanProperty(env, options, "sync", false);
1641
- const auto callback = argv[3];
1329
+ rocksdb::WriteBatch* batch;
1330
+ NAPI_STATUS_THROWS(napi_get_value_external(env, argv[1], reinterpret_cast<void**>(&batch)));
1642
1331
 
1643
- auto worker = new BatchWriteWorker(env, database, batch, callback, sync);
1644
- worker->Queue(env);
1645
-
1646
- return 0;
1332
+ rocksdb::WriteOptions options;
1333
+ return ToError(env, database->db_->Write(options, batch));
1647
1334
  }
1648
1335
 
1649
1336
  NAPI_INIT() {
@@ -1655,13 +1342,8 @@ NAPI_INIT() {
1655
1342
  NAPI_EXPORT_FUNCTION(db_get_many);
1656
1343
  NAPI_EXPORT_FUNCTION(db_del);
1657
1344
  NAPI_EXPORT_FUNCTION(db_clear);
1658
- NAPI_EXPORT_FUNCTION(db_approximate_size);
1659
- NAPI_EXPORT_FUNCTION(db_compact_range);
1660
1345
  NAPI_EXPORT_FUNCTION(db_get_property);
1661
1346
 
1662
- NAPI_EXPORT_FUNCTION(destroy_db);
1663
- NAPI_EXPORT_FUNCTION(repair_db);
1664
-
1665
1347
  NAPI_EXPORT_FUNCTION(iterator_init);
1666
1348
  NAPI_EXPORT_FUNCTION(iterator_seek);
1667
1349
  NAPI_EXPORT_FUNCTION(iterator_close);