@nxtedition/rocksdb 5.2.2 → 5.2.13

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
@@ -17,7 +17,8 @@
17
17
  namespace leveldb = rocksdb;
18
18
 
19
19
  #include <set>
20
- #include <optional>
20
+ #include <memory>
21
+ #include <string>
21
22
  #include <vector>
22
23
 
23
24
  class NullLogger : public rocksdb::Logger {
@@ -63,28 +64,38 @@ static bool IsObject (napi_env env, napi_value value) {
63
64
 
64
65
  static napi_value CreateError (napi_env env, const std::string& str) {
65
66
  napi_value msg;
66
- napi_create_string_utf8(env, str.c_str(), str.size(), &msg);
67
+ napi_create_string_utf8(env, str.data(), str.size(), &msg);
67
68
  napi_value error;
68
69
  napi_create_error(env, nullptr, msg, &error);
69
70
  return error;
70
71
  }
71
72
 
72
- static bool HasProperty (napi_env env, napi_value obj, const char* key) {
73
+ static napi_value CreateCodeError (napi_env env, const std::string& code, const std::string& msg) {
74
+ napi_value codeValue;
75
+ napi_create_string_utf8(env, code.data(), code.size(), &codeValue);
76
+ napi_value msgValue;
77
+ napi_create_string_utf8(env, msg.data(), msg.size(), &msgValue);
78
+ napi_value error;
79
+ napi_create_error(env, codeValue, msgValue, &error);
80
+ return error;
81
+ }
82
+
83
+ static bool HasProperty (napi_env env, napi_value obj, const std::string& key) {
73
84
  bool has = false;
74
- napi_has_named_property(env, obj, key, &has);
85
+ napi_has_named_property(env, obj, key.data(), &has);
75
86
  return has;
76
87
  }
77
88
 
78
- static napi_value GetProperty (napi_env env, napi_value obj, const char* key) {
89
+ static napi_value GetProperty (napi_env env, napi_value obj, const std::string& key) {
79
90
  napi_value value;
80
- napi_get_named_property(env, obj, key, &value);
91
+ napi_get_named_property(env, obj, key.data(), &value);
81
92
  return value;
82
93
  }
83
94
 
84
- static bool BooleanProperty (napi_env env, napi_value obj, const char* key,
95
+ static bool BooleanProperty (napi_env env, napi_value obj, const std::string& key,
85
96
  bool defaultValue) {
86
- if (HasProperty(env, obj, key)) {
87
- const auto value = GetProperty(env, obj, key);
97
+ if (HasProperty(env, obj, key.data())) {
98
+ const auto value = GetProperty(env, obj, key.data());
88
99
  bool result;
89
100
  napi_get_value_bool(env, value, &result);
90
101
  return result;
@@ -93,10 +104,23 @@ static bool BooleanProperty (napi_env env, napi_value obj, const char* key,
93
104
  return defaultValue;
94
105
  }
95
106
 
96
- static uint32_t Uint32Property (napi_env env, napi_value obj, const char* key,
107
+ static bool EncodingIsBuffer (napi_env env, napi_value obj, const std::string& option) {
108
+ napi_value value;
109
+ size_t size;
110
+
111
+ if (napi_get_named_property(env, obj, option.data(), &value) == napi_ok &&
112
+ napi_get_value_string_utf8(env, value, nullptr, 0, &size) == napi_ok) {
113
+ // Value is either "buffer" or "utf8" so we can tell them apart just by size
114
+ return size == 6;
115
+ }
116
+
117
+ return false;
118
+ }
119
+
120
+ static uint32_t Uint32Property (napi_env env, napi_value obj, const std::string& key,
97
121
  uint32_t defaultValue) {
98
- if (HasProperty(env, obj, key)) {
99
- const auto value = GetProperty(env, obj, key);
122
+ if (HasProperty(env, obj, key.data())) {
123
+ const auto value = GetProperty(env, obj, key.data());
100
124
  uint32_t result;
101
125
  napi_get_value_uint32(env, value, &result);
102
126
  return result;
@@ -105,10 +129,10 @@ static uint32_t Uint32Property (napi_env env, napi_value obj, const char* key,
105
129
  return defaultValue;
106
130
  }
107
131
 
108
- static int Int32Property (napi_env env, napi_value obj, const char* key,
132
+ static int Int32Property (napi_env env, napi_value obj, const std::string& key,
109
133
  int defaultValue) {
110
- if (HasProperty(env, obj, key)) {
111
- const auto value = GetProperty(env, obj, key);
134
+ if (HasProperty(env, obj, key.data())) {
135
+ const auto value = GetProperty(env, obj, key.data());
112
136
  int result;
113
137
  napi_get_value_int32(env, value, &result);
114
138
  return result;
@@ -117,7 +141,7 @@ static int Int32Property (napi_env env, napi_value obj, const char* key,
117
141
  return defaultValue;
118
142
  }
119
143
 
120
- static std::string ToString (napi_env env, napi_value from) {
144
+ static std::string ToString (napi_env env, napi_value from, const std::string& defaultValue = "") {
121
145
  if (IsString(env, from)) {
122
146
  size_t length = 0;
123
147
  napi_get_value_string_utf8(env, from, nullptr, 0, &length);
@@ -131,10 +155,10 @@ static std::string ToString (napi_env env, napi_value from) {
131
155
  return std::string(buf, length);
132
156
  }
133
157
 
134
- return {};
158
+ return defaultValue;
135
159
  }
136
160
 
137
- static std::string StringProperty (napi_env env, napi_value obj, const char* key) {
161
+ static std::string StringProperty (napi_env env, napi_value obj, const std::string& key) {
138
162
  if (HasProperty(env, obj, key)) {
139
163
  napi_value value = GetProperty(env, obj, key);
140
164
  if (IsString(env, value)) {
@@ -158,16 +182,13 @@ static size_t StringOrBufferLength (napi_env env, napi_value value) {
158
182
  return size;
159
183
  }
160
184
 
161
- static std::optional<std::string> RangeOption (napi_env env, napi_value opts, const char* name) {
185
+ static std::string* RangeOption (napi_env env, napi_value opts, const std::string& name) {
162
186
  if (HasProperty(env, opts, name)) {
163
187
  const auto value = GetProperty(env, opts, name);
164
-
165
- if (StringOrBufferLength(env, value) > 0) {
166
- return ToString(env, value);
167
- }
188
+ return new std::string(ToString(env, value));
168
189
  }
169
190
 
170
- return {};
191
+ return nullptr;
171
192
  }
172
193
 
173
194
  static std::vector<std::string> KeyArray (napi_env env, napi_value arr) {
@@ -199,13 +220,12 @@ static napi_status CallFunction (napi_env env,
199
220
  return napi_call_function(env, global, callback, argc, argv, nullptr);
200
221
  }
201
222
 
202
- void Convert (napi_env env, const std::optional<std::string>& s, bool asBuffer, napi_value& result) {
203
- if (!s) {
204
- napi_get_undefined(env, &result);
205
- } else if (asBuffer) {
206
- napi_create_buffer_copy(env, s->size(), s->data(), nullptr, &result);
223
+ template <typename T>
224
+ void Convert (napi_env env, const T& s, bool asBuffer, napi_value& result) {
225
+ if (asBuffer) {
226
+ napi_create_buffer_copy(env, s.size(), s.data(), nullptr, &result);
207
227
  } else {
208
- napi_create_string_utf8(env, s->data(), s->size(), &result);
228
+ napi_create_string_utf8(env, s.data(), s.size(), &result);
209
229
  }
210
230
  }
211
231
 
@@ -247,8 +267,9 @@ struct BaseWorker {
247
267
  self->DoExecute();
248
268
  }
249
269
 
250
- void SetStatus (const leveldb::Status& status) {
270
+ bool SetStatus (const leveldb::Status& status) {
251
271
  status_ = status;
272
+ return status.ok();
252
273
  }
253
274
 
254
275
  virtual void DoExecute () = 0;
@@ -278,7 +299,28 @@ struct BaseWorker {
278
299
  }
279
300
 
280
301
  virtual void HandleErrorCallback (napi_env env, napi_value callback) {
281
- auto argv = CreateError(env, status_.ToString());
302
+ napi_value argv;
303
+
304
+ const auto msg = status_.ToString();
305
+
306
+ if (status_.IsNotFound()) {
307
+ argv = CreateCodeError(env, "LEVEL_NOT_FOUND", msg);
308
+ } else if (status_.IsCorruption()) {
309
+ argv = CreateCodeError(env, "LEVEL_CORRUPTION", msg);
310
+ } else if (status_.IsIOError()) {
311
+ if (msg.find("IO error: lock ") != std::string::npos) { // env_posix.cc
312
+ argv = CreateCodeError(env, "LEVEL_LOCKED", msg);
313
+ } else if (msg.find("IO error: LockFile ") != std::string::npos) { // env_win.cc
314
+ argv = CreateCodeError(env, "LEVEL_LOCKED", msg);
315
+ } else if (msg.find("IO error: While lock file") != std::string::npos) { // env_mac.cc
316
+ argv = CreateCodeError(env, "LEVEL_LOCKED", msg);
317
+ } else {
318
+ argv = CreateCodeError(env, "LEVEL_IO_ERROR", msg);
319
+ }
320
+ } else {
321
+ argv = CreateError(env, msg);
322
+ }
323
+
282
324
  CallFunction(env, callback, 1, &argv);
283
325
  }
284
326
 
@@ -333,18 +375,18 @@ struct Database {
333
375
  leveldb::Status Put (const leveldb::WriteOptions& options,
334
376
  const std::string& key,
335
377
  const std::string& value) {
336
- return db_->Put(options, key, value);
378
+ return db_->Put(options, db_->DefaultColumnFamily(), key, value);
337
379
  }
338
380
 
339
381
  leveldb::Status Get (const leveldb::ReadOptions& options,
340
382
  const std::string& key,
341
- std::string& value) {
342
- return db_->Get(options, key, &value);
383
+ rocksdb::PinnableSlice& value) {
384
+ return db_->Get(options, db_->DefaultColumnFamily(), key, &value);
343
385
  }
344
386
 
345
387
  leveldb::Status Del (const leveldb::WriteOptions& options,
346
388
  const std::string& key) {
347
- return db_->Delete(options, key);
389
+ return db_->Delete(options, db_->DefaultColumnFamily(), key);
348
390
  }
349
391
 
350
392
  leveldb::Status WriteBatch (const leveldb::WriteOptions& options,
@@ -352,6 +394,18 @@ struct Database {
352
394
  return db_->Write(options, batch);
353
395
  }
354
396
 
397
+ uint64_t ApproximateSize (const leveldb::Range* range) {
398
+ uint64_t size = 0;
399
+ db_->GetApproximateSizes(range, 1, &size);
400
+ return size;
401
+ }
402
+
403
+ void CompactRange (const leveldb::Slice* start,
404
+ const leveldb::Slice* end) {
405
+ rocksdb::CompactRangeOptions options;
406
+ db_->CompactRange(options, start, end);
407
+ }
408
+
355
409
  void GetProperty (const std::string& property, std::string& value) {
356
410
  db_->GetProperty(property, &value);
357
411
  }
@@ -424,10 +478,10 @@ struct PriorityWorker : public BaseWorker {
424
478
  struct BaseIterator {
425
479
  BaseIterator(Database* database,
426
480
  const bool reverse,
427
- const std::optional<std::string>& lt,
428
- const std::optional<std::string>& lte,
429
- const std::optional<std::string>& gt,
430
- const std::optional<std::string>& gte,
481
+ const std::string* lt,
482
+ const std::string* lte,
483
+ const std::string* gt,
484
+ const std::string* gte,
431
485
  const int limit,
432
486
  const bool fillCache)
433
487
  : database_(database),
@@ -451,6 +505,11 @@ struct BaseIterator {
451
505
 
452
506
  virtual ~BaseIterator () {
453
507
  assert(!dbIterator_);
508
+
509
+ delete lt_;
510
+ delete lte_;
511
+ delete gt_;
512
+ delete gte_;
454
513
  }
455
514
 
456
515
  bool DidSeek () const {
@@ -571,10 +630,19 @@ struct BaseIterator {
571
630
  }
572
631
 
573
632
  bool OutOfRange (const leveldb::Slice& target) const {
574
- return ((lt_ && target.compare(*lt_) >= 0) ||
575
- (lte_ && target.compare(*lte_) > 0) ||
576
- (gt_ && target.compare(*gt_) <= 0) ||
577
- (gte_ && target.compare(*gte_) < 0));
633
+ if (lte_) {
634
+ if (target.compare(*lte_) > 0) return true;
635
+ } else if (lt_) {
636
+ if (target.compare(*lt_) >= 0) return true;
637
+ }
638
+
639
+ if (gte_) {
640
+ if (target.compare(*gte_) < 0) return true;
641
+ } else if (gt_) {
642
+ if (target.compare(*gt_) <= 0) return true;
643
+ }
644
+
645
+ return false;
578
646
  }
579
647
 
580
648
  Database* database_;
@@ -584,10 +652,10 @@ private:
584
652
  leveldb::Iterator* dbIterator_;
585
653
  bool didSeek_;
586
654
  const bool reverse_;
587
- const std::optional<std::string> lt_;
588
- const std::optional<std::string> lte_;
589
- const std::optional<std::string> gt_;
590
- const std::optional<std::string> gte_;
655
+ const std::string* lt_;
656
+ const std::string* lte_;
657
+ const std::string* gt_;
658
+ const std::string* gte_;
591
659
  const int limit_;
592
660
  int count_;
593
661
  };
@@ -598,21 +666,21 @@ struct Iterator final : public BaseIterator {
598
666
  const bool keys,
599
667
  const bool values,
600
668
  const int limit,
601
- const std::optional<std::string>& lt,
602
- const std::optional<std::string>& lte,
603
- const std::optional<std::string>& gt,
604
- const std::optional<std::string>& gte,
669
+ const std::string* lt,
670
+ const std::string* lte,
671
+ const std::string* gt,
672
+ const std::string* gte,
605
673
  const bool fillCache,
606
674
  const bool keyAsBuffer,
607
675
  const bool valueAsBuffer,
608
- const uint32_t highWaterMark)
676
+ const uint32_t highWaterMarkBytes)
609
677
  : BaseIterator(database, reverse, lt, lte, gt, gte, limit, fillCache),
610
678
  keys_(keys),
611
679
  values_(values),
612
680
  keyAsBuffer_(keyAsBuffer),
613
681
  valueAsBuffer_(valueAsBuffer),
614
- highWaterMark_(highWaterMark),
615
- landed_(false),
682
+ highWaterMarkBytes_(highWaterMarkBytes),
683
+ first_(true),
616
684
  nexting_(false),
617
685
  isClosing_(false),
618
686
  closeWorker_(nullptr),
@@ -631,34 +699,34 @@ struct Iterator final : public BaseIterator {
631
699
 
632
700
  bool ReadMany (uint32_t size) {
633
701
  cache_.clear();
702
+ cache_.reserve(size * 2);
634
703
  size_t bytesRead = 0;
635
704
 
636
705
  while (true) {
637
- if (landed_) Next();
638
- if (!Valid() || !Increment()) break;
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
- }
706
+ if (!first_) Next();
707
+ else first_ = false;
655
708
 
656
- if (!landed_) {
657
- landed_ = true;
658
- return true;
709
+ if (!Valid() || !Increment()) break;
710
+
711
+ if (keys_ && values_) {
712
+ auto k = CurrentKey();
713
+ auto v = CurrentValue();
714
+ cache_.emplace_back(k.data(), k.size());
715
+ cache_.emplace_back(v.data(), v.size());
716
+ bytesRead += k.size() + v.size();
717
+ } else if (keys_) {
718
+ auto k = CurrentKey();
719
+ cache_.emplace_back(k.data(), k.size());
720
+ cache_.push_back({});
721
+ bytesRead += k.size();
722
+ } else if (values_) {
723
+ auto v = CurrentValue();
724
+ cache_.push_back({});
725
+ cache_.emplace_back(v.data(), v.size());
726
+ bytesRead += v.size();
659
727
  }
660
728
 
661
- if (bytesRead > highWaterMark_ || cache_.size() >= size * 2) {
729
+ if (bytesRead > highWaterMarkBytes_ || cache_.size() / 2 >= size) {
662
730
  return true;
663
731
  }
664
732
  }
@@ -670,8 +738,8 @@ struct Iterator final : public BaseIterator {
670
738
  const bool values_;
671
739
  const bool keyAsBuffer_;
672
740
  const bool valueAsBuffer_;
673
- const uint32_t highWaterMark_;
674
- bool landed_;
741
+ const uint32_t highWaterMarkBytes_;
742
+ bool first_;
675
743
  bool nexting_;
676
744
  bool isClosing_;
677
745
  BaseWorker* closeWorker_;
@@ -687,7 +755,7 @@ private:
687
755
  * the guarantee that no db operations will be in-flight at this time.
688
756
  */
689
757
  static void env_cleanup_hook (void* arg) {
690
- Database* database = (Database*)arg;
758
+ Database* database = reinterpret_cast<Database*>(arg);
691
759
 
692
760
  // Do everything that db_close() does but synchronously. We're expecting that GC
693
761
  // did not (yet) collect the database because that would be a user mistake (not
@@ -697,8 +765,8 @@ static void env_cleanup_hook (void* arg) {
697
765
  // be a safe noop if called before db_open() or after db_close().
698
766
  if (database && database->db_) {
699
767
  // TODO: does not do `napi_delete_reference(env, iterator->ref_)`. Problem?
700
- for (auto it = database->iterators_.begin(); it != database->iterators_.end(); ++it) {
701
- (*it)->Close();
768
+ for (auto it : database->iterators_) {
769
+ it->Close();
702
770
  }
703
771
 
704
772
  // Having closed the iterators (and released snapshots) we can safely close.
@@ -866,8 +934,8 @@ NAPI_METHOD(db_close) {
866
934
  napi_value noop;
867
935
  napi_create_function(env, nullptr, 0, noop_callback, nullptr, &noop);
868
936
 
869
- for (auto it = database->iterators_.begin(); it != database->iterators_.end(); ++it) {
870
- iterator_do_close(env, *it, noop);
937
+ for (auto it : database->iterators_) {
938
+ iterator_do_close(env, it, noop);
871
939
  }
872
940
 
873
941
  auto worker = new CloseWorker(env, database, callback);
@@ -881,6 +949,90 @@ NAPI_METHOD(db_close) {
881
949
  return 0;
882
950
  }
883
951
 
952
+ struct PutWorker final : public PriorityWorker {
953
+ PutWorker (napi_env env,
954
+ Database* database,
955
+ napi_value callback,
956
+ const std::string& key,
957
+ const std::string& value,
958
+ bool sync)
959
+ : PriorityWorker(env, database, callback, "rocks_level.db.put"),
960
+ key_(key), value_(value), sync_(sync) {
961
+ }
962
+
963
+ void DoExecute () override {
964
+ leveldb::WriteOptions options;
965
+ options.sync = sync_;
966
+ SetStatus(database_->Put(options, key_, value_));
967
+ }
968
+
969
+ const std::string key_;
970
+ const std::string value_;
971
+ const bool sync_;
972
+ };
973
+
974
+ NAPI_METHOD(db_put) {
975
+ NAPI_ARGV(5);
976
+ NAPI_DB_CONTEXT();
977
+
978
+ const auto key = ToString(env, argv[1]);
979
+ const auto value = ToString(env, argv[2]);
980
+ const auto sync = BooleanProperty(env, argv[3], "sync", false);
981
+ const auto callback = argv[4];
982
+
983
+ auto worker = new PutWorker(env, database, callback, key, value, sync);
984
+ worker->Queue(env);
985
+
986
+ return 0;
987
+ }
988
+
989
+ struct GetWorker final : public PriorityWorker {
990
+ GetWorker (napi_env env,
991
+ Database* database,
992
+ napi_value callback,
993
+ const std::string& key,
994
+ const bool asBuffer,
995
+ const bool fillCache)
996
+ : PriorityWorker(env, database, callback, "rocks_level.db.get"),
997
+ key_(key), asBuffer_(asBuffer), fillCache_(fillCache) {
998
+ }
999
+
1000
+ void DoExecute () override {
1001
+ leveldb::ReadOptions options;
1002
+ options.fill_cache = fillCache_;
1003
+ SetStatus(database_->Get(options, key_, value_));
1004
+ }
1005
+
1006
+ void HandleOKCallback (napi_env env, napi_value callback) override {
1007
+ napi_value argv[2];
1008
+ napi_get_null(env, &argv[0]);
1009
+ Convert(env, std::move(value_), asBuffer_, argv[1]);
1010
+ CallFunction(env, callback, 2, argv);
1011
+ }
1012
+
1013
+ private:
1014
+ const std::string key_;
1015
+ rocksdb::PinnableSlice value_;
1016
+ const bool asBuffer_;
1017
+ const bool fillCache_;
1018
+ };
1019
+
1020
+ NAPI_METHOD(db_get) {
1021
+ NAPI_ARGV(4);
1022
+ NAPI_DB_CONTEXT();
1023
+
1024
+ const auto key = ToString(env, argv[1]);
1025
+ const auto options = argv[2];
1026
+ const auto asBuffer = EncodingIsBuffer(env, options, "valueEncoding");
1027
+ const auto fillCache = BooleanProperty(env, options, "fillCache", true);
1028
+ const auto callback = argv[3];
1029
+
1030
+ auto worker = new GetWorker(env, database, callback, key, asBuffer, fillCache);
1031
+ worker->Queue(env);
1032
+
1033
+ return 0;
1034
+ }
1035
+
884
1036
  struct GetManyWorker final : public PriorityWorker {
885
1037
  GetManyWorker (napi_env env,
886
1038
  Database* database,
@@ -907,18 +1059,21 @@ struct GetManyWorker final : public PriorityWorker {
907
1059
  options.snapshot = snapshot_;
908
1060
  options.fill_cache = fillCache_;
909
1061
 
1062
+ rocksdb::PinnableSlice value;
1063
+
910
1064
  for (const auto& key: keys_) {
911
- std::string value;
912
1065
  const auto status = database_->Get(options, key, value);
913
1066
 
914
1067
  if (status.ok()) {
915
- cache_.push_back(std::move(value));
1068
+ cache_.emplace_back(std::move(value));
916
1069
  } else if (status.IsNotFound()) {
917
- cache_.push_back({});
1070
+ cache_.emplace_back(nullptr);
918
1071
  } else {
919
1072
  SetStatus(status);
920
1073
  break;
921
1074
  }
1075
+
1076
+ value.Reset();
922
1077
  }
923
1078
 
924
1079
  database_->ReleaseSnapshot(snapshot_);
@@ -933,7 +1088,11 @@ struct GetManyWorker final : public PriorityWorker {
933
1088
 
934
1089
  for (size_t idx = 0; idx < size; idx++) {
935
1090
  napi_value element;
936
- Convert(env, cache_[idx], valueAsBuffer_, element);
1091
+ if (cache_[idx].GetSelf() != nullptr) {
1092
+ Convert(env, cache_[idx], valueAsBuffer_, element);
1093
+ } else {
1094
+ napi_get_undefined(env, &element);
1095
+ }
937
1096
  napi_set_element(env, array, static_cast<uint32_t>(idx), element);
938
1097
  }
939
1098
 
@@ -946,7 +1105,7 @@ struct GetManyWorker final : public PriorityWorker {
946
1105
  private:
947
1106
  const std::vector<std::string> keys_;
948
1107
  const bool valueAsBuffer_;
949
- std::vector<std::optional<std::string>> cache_;
1108
+ std::vector<rocksdb::PinnableSlice> cache_;
950
1109
  const bool fillCache_;
951
1110
  const leveldb::Snapshot* snapshot_;
952
1111
  };
@@ -957,7 +1116,7 @@ NAPI_METHOD(db_get_many) {
957
1116
 
958
1117
  const auto keys = KeyArray(env, argv[1]);
959
1118
  const auto options = argv[2];
960
- const bool asBuffer = BooleanProperty(env, options, "asBuffer", true);
1119
+ const bool asBuffer = EncodingIsBuffer(env, options, "valueEncoding");
961
1120
  const bool fillCache = BooleanProperty(env, options, "fillCache", true);
962
1121
  const auto callback = argv[3];
963
1122
 
@@ -967,6 +1126,257 @@ NAPI_METHOD(db_get_many) {
967
1126
  return 0;
968
1127
  }
969
1128
 
1129
+ struct DelWorker final : public PriorityWorker {
1130
+ DelWorker (napi_env env,
1131
+ Database* database,
1132
+ napi_value callback,
1133
+ const std::string& key,
1134
+ bool sync)
1135
+ : PriorityWorker(env, database, callback, "rocks_level.db.del"),
1136
+ key_(key), sync_(sync) {
1137
+ }
1138
+
1139
+ void DoExecute () override {
1140
+ leveldb::WriteOptions options;
1141
+ options.sync = sync_;
1142
+ SetStatus(database_->Del(options, key_));
1143
+ }
1144
+
1145
+ const std::string key_;
1146
+ const bool sync_;
1147
+ };
1148
+
1149
+ NAPI_METHOD(db_del) {
1150
+ NAPI_ARGV(4);
1151
+ NAPI_DB_CONTEXT();
1152
+
1153
+ const auto key = ToString(env, argv[1]);
1154
+ const auto sync = BooleanProperty(env, argv[2], "sync", false);
1155
+ const auto callback = argv[3];
1156
+
1157
+ auto worker = new DelWorker(env, database, callback, key, sync);
1158
+ worker->Queue(env);
1159
+
1160
+ return 0;
1161
+ }
1162
+
1163
+ struct ClearWorker final : public PriorityWorker {
1164
+ ClearWorker (napi_env env,
1165
+ Database* database,
1166
+ napi_value callback,
1167
+ const bool reverse,
1168
+ const int limit,
1169
+ const std::string* lt,
1170
+ const std::string* lte,
1171
+ const std::string* gt,
1172
+ const std::string* gte)
1173
+ : PriorityWorker(env, database, callback, "rocks_level.db.clear"),
1174
+ iterator_(database, reverse, lt, lte, gt, gte, limit, false) {
1175
+ }
1176
+
1177
+ void DoExecute () override {
1178
+ iterator_.SeekToRange();
1179
+
1180
+ // TODO: add option
1181
+ const uint32_t hwm = 16 * 1024;
1182
+ leveldb::WriteBatch batch;
1183
+
1184
+ leveldb::WriteOptions options;
1185
+ options.sync = false;
1186
+
1187
+ while (true) {
1188
+ size_t bytesRead = 0;
1189
+
1190
+ while (bytesRead <= hwm && iterator_.Valid() && iterator_.Increment()) {
1191
+ const auto key = iterator_.CurrentKey();
1192
+ batch.Delete(key);
1193
+ bytesRead += key.size();
1194
+ iterator_.Next();
1195
+ }
1196
+
1197
+ if (!SetStatus(iterator_.Status()) || bytesRead == 0) {
1198
+ break;
1199
+ }
1200
+
1201
+ if (!SetStatus(database_->WriteBatch(options, &batch))) {
1202
+ break;
1203
+ }
1204
+
1205
+ batch.Clear();
1206
+ }
1207
+
1208
+ iterator_.Close();
1209
+ }
1210
+
1211
+ private:
1212
+ BaseIterator iterator_;
1213
+ };
1214
+
1215
+ NAPI_METHOD(db_clear) {
1216
+ NAPI_ARGV(3);
1217
+ NAPI_DB_CONTEXT();
1218
+
1219
+ napi_value options = argv[1];
1220
+ napi_value callback = argv[2];
1221
+
1222
+ const auto reverse = BooleanProperty(env, options, "reverse", false);
1223
+ const auto limit = Int32Property(env, options, "limit", -1);
1224
+
1225
+ const auto lt = RangeOption(env, options, "lt");
1226
+ const auto lte = RangeOption(env, options, "lte");
1227
+ const auto gt = RangeOption(env, options, "gt");
1228
+ const auto gte = RangeOption(env, options, "gte");
1229
+
1230
+ auto worker = new ClearWorker(env, database, callback, reverse, limit, lt, lte, gt, gte);
1231
+ worker->Queue(env);
1232
+
1233
+ return 0;
1234
+ }
1235
+
1236
+ struct ApproximateSizeWorker final : public PriorityWorker {
1237
+ ApproximateSizeWorker (napi_env env,
1238
+ Database* database,
1239
+ napi_value callback,
1240
+ const std::string& start,
1241
+ const std::string& end)
1242
+ : PriorityWorker(env, database, callback, "rocks_level.db.approximate_size"),
1243
+ start_(start), end_(end) {}
1244
+
1245
+ void DoExecute () override {
1246
+ leveldb::Range range(start_, end_);
1247
+ size_ = database_->ApproximateSize(&range);
1248
+ }
1249
+
1250
+ void HandleOKCallback (napi_env env, napi_value callback) override {
1251
+ napi_value argv[2];
1252
+ napi_get_null(env, &argv[0]);
1253
+ napi_create_int64(env, size_, &argv[1]);
1254
+ CallFunction(env, callback, 2, argv);
1255
+ }
1256
+
1257
+ std::string start_;
1258
+ std::string end_;
1259
+ uint64_t size_;
1260
+ };
1261
+
1262
+ NAPI_METHOD(db_approximate_size) {
1263
+ NAPI_ARGV(4);
1264
+ NAPI_DB_CONTEXT();
1265
+
1266
+ const auto start = ToString(env, argv[1]);
1267
+ const auto end = ToString(env, argv[2]);
1268
+ const auto callback = argv[3];
1269
+
1270
+ auto worker = new ApproximateSizeWorker(env, database, callback, start, end);
1271
+ worker->Queue(env);
1272
+
1273
+ return 0;
1274
+ }
1275
+
1276
+ struct CompactRangeWorker final : public PriorityWorker {
1277
+ CompactRangeWorker (napi_env env,
1278
+ Database* database,
1279
+ napi_value callback,
1280
+ const std::string& start,
1281
+ const std::string& end)
1282
+ : PriorityWorker(env, database, callback, "rocks_level.db.compact_range"),
1283
+ start_(start), end_(end) {}
1284
+
1285
+ void DoExecute () override {
1286
+ leveldb::Slice start = start_;
1287
+ leveldb::Slice end = end_;
1288
+ database_->CompactRange(&start, &end);
1289
+ }
1290
+
1291
+ const std::string start_;
1292
+ const std::string end_;
1293
+ };
1294
+
1295
+ NAPI_METHOD(db_compact_range) {
1296
+ NAPI_ARGV(4);
1297
+ NAPI_DB_CONTEXT();
1298
+
1299
+ const auto start = ToString(env, argv[1]);
1300
+ const auto end = ToString(env, argv[2]);
1301
+ const auto callback = argv[3];
1302
+
1303
+ auto worker = new CompactRangeWorker(env, database, callback, start, end);
1304
+ worker->Queue(env);
1305
+
1306
+ return 0;
1307
+ }
1308
+
1309
+ NAPI_METHOD(db_get_property) {
1310
+ NAPI_ARGV(2);
1311
+ NAPI_DB_CONTEXT();
1312
+
1313
+ const auto property = ToString(env, argv[1]);
1314
+
1315
+ std::string value;
1316
+ database->GetProperty(property, value);
1317
+
1318
+ napi_value result;
1319
+ napi_create_string_utf8(env, value.data(), value.size(), &result);
1320
+
1321
+ return result;
1322
+ }
1323
+
1324
+ struct DestroyWorker final : public BaseWorker {
1325
+ DestroyWorker (napi_env env,
1326
+ const std::string& location,
1327
+ napi_value callback)
1328
+ : BaseWorker(env, nullptr, callback, "rocks_level.destroy_db"),
1329
+ location_(location) {}
1330
+
1331
+ ~DestroyWorker () {}
1332
+
1333
+ void DoExecute () override {
1334
+ leveldb::Options options;
1335
+ SetStatus(leveldb::DestroyDB(location_, options));
1336
+ }
1337
+
1338
+ const std::string location_;
1339
+ };
1340
+
1341
+ NAPI_METHOD(destroy_db) {
1342
+ NAPI_ARGV(2);
1343
+
1344
+ const auto location = ToString(env, argv[0]);
1345
+ const auto callback = argv[1];
1346
+
1347
+ auto worker = new DestroyWorker(env, location, callback);
1348
+ worker->Queue(env);
1349
+
1350
+ return 0;
1351
+ }
1352
+
1353
+ struct RepairWorker final : public BaseWorker {
1354
+ RepairWorker (napi_env env,
1355
+ const std::string& location,
1356
+ napi_value callback)
1357
+ : BaseWorker(env, nullptr, callback, "rocks_level.repair_db"),
1358
+ location_(location) {}
1359
+
1360
+ void DoExecute () override {
1361
+ leveldb::Options options;
1362
+ SetStatus(leveldb::RepairDB(location_, options));
1363
+ }
1364
+
1365
+ const std::string location_;
1366
+ };
1367
+
1368
+ NAPI_METHOD(repair_db) {
1369
+ NAPI_ARGV(2);
1370
+
1371
+ const auto location = ToString(env, argv[1]);
1372
+ const auto callback = argv[1];
1373
+
1374
+ auto worker = new RepairWorker(env, location, callback);
1375
+ worker->Queue(env);
1376
+
1377
+ return 0;
1378
+ }
1379
+
970
1380
  static void FinalizeIterator (napi_env env, void* data, void* hint) {
971
1381
  if (data) {
972
1382
  delete reinterpret_cast<Iterator*>(data);
@@ -982,10 +1392,10 @@ NAPI_METHOD(iterator_init) {
982
1392
  const auto keys = BooleanProperty(env, options, "keys", true);
983
1393
  const auto values = BooleanProperty(env, options, "values", true);
984
1394
  const auto fillCache = BooleanProperty(env, options, "fillCache", false);
985
- const auto keyAsBuffer = BooleanProperty(env, options, "keyAsBuffer", true);
986
- const auto valueAsBuffer = BooleanProperty(env, options, "valueAsBuffer", true);
1395
+ const bool keyAsBuffer = EncodingIsBuffer(env, options, "keyEncoding");
1396
+ const bool valueAsBuffer = EncodingIsBuffer(env, options, "valueEncoding");
987
1397
  const auto limit = Int32Property(env, options, "limit", -1);
988
- const auto highWaterMark = Uint32Property(env, options, "highWaterMark", 16 * 1024);
1398
+ const auto highWaterMarkBytes = Uint32Property(env, options, "highWaterMarkBytes", 16 * 1024);
989
1399
 
990
1400
  const auto lt = RangeOption(env, options, "lt");
991
1401
  const auto lte = RangeOption(env, options, "lte");
@@ -994,7 +1404,7 @@ NAPI_METHOD(iterator_init) {
994
1404
 
995
1405
  auto iterator = new Iterator(database, reverse, keys,
996
1406
  values, limit, lt, lte, gt, gte, fillCache,
997
- keyAsBuffer, valueAsBuffer, highWaterMark);
1407
+ keyAsBuffer, valueAsBuffer, highWaterMarkBytes);
998
1408
  napi_value result;
999
1409
 
1000
1410
  NAPI_STATUS_THROWS(napi_create_external(env, iterator,
@@ -1017,7 +1427,7 @@ NAPI_METHOD(iterator_seek) {
1017
1427
  }
1018
1428
 
1019
1429
  const auto target = ToString(env, argv[1]);
1020
- iterator->landed_ = false;
1430
+ iterator->first_ = true;
1021
1431
  iterator->Seek(target);
1022
1432
 
1023
1433
  return 0;
@@ -1104,8 +1514,8 @@ struct NextWorker final : public BaseWorker {
1104
1514
  Convert(env, iterator_->cache_[idx + 0], iterator_->keyAsBuffer_, key);
1105
1515
  Convert(env, iterator_->cache_[idx + 1], iterator_->valueAsBuffer_, val);
1106
1516
 
1107
- napi_set_element(env, result, static_cast<int>(size - idx - 1), key);
1108
- napi_set_element(env, result, static_cast<int>(size - idx - 2), val);
1517
+ napi_set_element(env, result, static_cast<int>(idx + 0), key);
1518
+ napi_set_element(env, result, static_cast<int>(idx + 1), val);
1109
1519
  }
1110
1520
 
1111
1521
  iterator_->cache_.clear();
@@ -1146,9 +1556,8 @@ NAPI_METHOD(iterator_nextv) {
1146
1556
  const auto callback = argv[2];
1147
1557
 
1148
1558
  if (iterator->isClosing_) {
1149
- auto argv = CreateError(env, "iterator has closed");
1150
- CallFunction(env, callback, 1, &argv);
1151
-
1559
+ napi_value argv = CreateCodeError(env, "LEVEL_ITERATOR_NOT_OPEN", "Iterator is not open");
1560
+ NAPI_STATUS_THROWS(CallFunction(env, callback, 1, &argv));
1152
1561
  return 0;
1153
1562
  }
1154
1563
 
@@ -1159,6 +1568,83 @@ NAPI_METHOD(iterator_nextv) {
1159
1568
  return 0;
1160
1569
  }
1161
1570
 
1571
+ /**
1572
+ * Worker class for batch write operation.
1573
+ */
1574
+ struct BatchWorker final : public PriorityWorker {
1575
+ BatchWorker (napi_env env,
1576
+ Database* database,
1577
+ napi_value callback,
1578
+ leveldb::WriteBatch* batch,
1579
+ const bool sync,
1580
+ const bool hasData)
1581
+ : PriorityWorker(env, database, callback, "rocks_level.batch.do"),
1582
+ batch_(batch), hasData_(hasData) {
1583
+ options_.sync = sync;
1584
+ }
1585
+
1586
+ ~BatchWorker () {
1587
+ delete batch_;
1588
+ }
1589
+
1590
+ void DoExecute () override {
1591
+ if (hasData_) {
1592
+ SetStatus(database_->WriteBatch(options_, batch_));
1593
+ }
1594
+ }
1595
+
1596
+ private:
1597
+ leveldb::WriteOptions options_;
1598
+ leveldb::WriteBatch* batch_;
1599
+ const bool hasData_;
1600
+ };
1601
+
1602
+ NAPI_METHOD(batch_do) {
1603
+ NAPI_ARGV(4);
1604
+ NAPI_DB_CONTEXT();
1605
+
1606
+ const auto array = argv[1];
1607
+ const auto sync = BooleanProperty(env, argv[2], "sync", false);
1608
+ const auto callback = argv[3];
1609
+
1610
+ uint32_t length;
1611
+ napi_get_array_length(env, array, &length);
1612
+
1613
+ leveldb::WriteBatch* batch = new leveldb::WriteBatch();
1614
+ bool hasData = false;
1615
+
1616
+ for (uint32_t i = 0; i < length; i++) {
1617
+ napi_value element;
1618
+ napi_get_element(env, array, i, &element);
1619
+
1620
+ if (!IsObject(env, element)) continue;
1621
+
1622
+ std::string type = StringProperty(env, element, "type");
1623
+
1624
+ if (type == "del") {
1625
+ if (!HasProperty(env, element, "key")) continue;
1626
+ const auto key = ToString(env, GetProperty(env, element, "key"));
1627
+
1628
+ batch->Delete(key);
1629
+ if (!hasData) hasData = true;
1630
+ } else if (type == "put") {
1631
+ if (!HasProperty(env, element, "key")) continue;
1632
+ if (!HasProperty(env, element, "value")) continue;
1633
+
1634
+ const auto key = ToString(env, GetProperty(env, element, "key"));
1635
+ const auto value = ToString(env, GetProperty(env, element, "value"));
1636
+
1637
+ batch->Put(key, value);
1638
+ if (!hasData) hasData = true;
1639
+ }
1640
+ }
1641
+
1642
+ auto worker = new BatchWorker(env, database, callback, batch, sync, hasData);
1643
+ worker->Queue(env);
1644
+
1645
+ return 0;
1646
+ }
1647
+
1162
1648
  static void FinalizeBatch (napi_env env, void* data, void* hint) {
1163
1649
  if (data) {
1164
1650
  delete reinterpret_cast<leveldb::WriteBatch*>(data);
@@ -1261,13 +1747,24 @@ NAPI_INIT() {
1261
1747
  NAPI_EXPORT_FUNCTION(db_init);
1262
1748
  NAPI_EXPORT_FUNCTION(db_open);
1263
1749
  NAPI_EXPORT_FUNCTION(db_close);
1750
+ NAPI_EXPORT_FUNCTION(db_put);
1751
+ NAPI_EXPORT_FUNCTION(db_get);
1264
1752
  NAPI_EXPORT_FUNCTION(db_get_many);
1753
+ NAPI_EXPORT_FUNCTION(db_del);
1754
+ NAPI_EXPORT_FUNCTION(db_clear);
1755
+ NAPI_EXPORT_FUNCTION(db_approximate_size);
1756
+ NAPI_EXPORT_FUNCTION(db_compact_range);
1757
+ NAPI_EXPORT_FUNCTION(db_get_property);
1758
+
1759
+ NAPI_EXPORT_FUNCTION(destroy_db);
1760
+ NAPI_EXPORT_FUNCTION(repair_db);
1265
1761
 
1266
1762
  NAPI_EXPORT_FUNCTION(iterator_init);
1267
1763
  NAPI_EXPORT_FUNCTION(iterator_seek);
1268
1764
  NAPI_EXPORT_FUNCTION(iterator_close);
1269
1765
  NAPI_EXPORT_FUNCTION(iterator_nextv);
1270
1766
 
1767
+ NAPI_EXPORT_FUNCTION(batch_do);
1271
1768
  NAPI_EXPORT_FUNCTION(batch_init);
1272
1769
  NAPI_EXPORT_FUNCTION(batch_put);
1273
1770
  NAPI_EXPORT_FUNCTION(batch_del);