@@ -48,6 +48,7 @@ static void iterator_close_do (napi_env env, Iterator* iterator, napi_value cb);
48
48
#define NAPI_ARGV_UTF8_NEW (name, i ) \
49
49
NAPI_UTF8_NEW (name, argv[i])
50
50
51
+ // TODO: consider using encoding options instead of type checking
51
52
#define LD_STRING_OR_BUFFER_TO_COPY (env, from, to ) \
52
53
char * to##Ch_ = 0 ; \
53
54
size_t to##Sz_ = 0 ; \
@@ -61,6 +62,18 @@ static void iterator_close_do (napi_env env, Iterator* iterator, napi_value cb);
61
62
napi_get_buffer_info (env, from, (void **)&buf, &to##Sz_); \
62
63
to##Ch_ = new char [to##Sz_]; \
63
64
memcpy (to##Ch_, buf, to##Sz_); \
65
+ } else { \
66
+ char * buf = 0 ; \
67
+ napi_typedarray_type type; \
68
+ napi_status status = napi_get_typedarray_info (env, from, &type, &to##Sz_, (void **)&buf, NULL , NULL ); \
69
+ if (status != napi_ok || type != napi_typedarray_type::napi_uint8_array) { \
70
+ /* TODO: refactor so that we can napi_throw_type_error() here */ \
71
+ to##Sz_ = 0 ; \
72
+ to##Ch_ = new char [to##Sz_]; \
73
+ } else { \
74
+ to##Ch_ = new char [to##Sz_]; \
75
+ memcpy (to##Ch_, buf, to##Sz_); \
76
+ } \
64
77
}
65
78
66
79
/* ********************************************************************
@@ -149,20 +162,26 @@ static bool BooleanProperty (napi_env env, napi_value obj, const char* key,
149
162
return DEFAULT;
150
163
}
151
164
165
+ enum Encoding { buffer, utf8, view };
166
+
152
167
/* *
153
- * Returns true if the options object contains an encoding option that is "buffer"
168
+ * Returns internal Encoding enum matching the given encoding option.
154
169
*/
155
- static bool EncodingIsBuffer (napi_env env, napi_value options, const char * option) {
170
+ static Encoding GetEncoding (napi_env env, napi_value options, const char * option) {
156
171
napi_value value;
157
- size_t size;
172
+ size_t copied;
173
+ char buf[2 ];
158
174
159
175
if (napi_get_named_property (env, options, option, &value) == napi_ok &&
160
- napi_get_value_string_utf8 (env, value, NULL , 0 , &size) == napi_ok) {
161
- // Value is either "buffer" or "utf8" so we can tell them apart just by size
162
- return size == 6 ;
176
+ napi_get_value_string_utf8 (env, value, buf, 2 , &copied) == napi_ok && copied == 1 ) {
177
+ // Value is either "buffer", "utf8" or "view" so we only have to read the first char
178
+ switch (buf[0 ]) {
179
+ case ' b' : return Encoding::buffer;
180
+ case ' v' : return Encoding::view;
181
+ }
163
182
}
164
183
165
- return false ;
184
+ return Encoding::utf8 ;
166
185
}
167
186
168
187
/* *
@@ -234,35 +253,17 @@ static leveldb::Slice ToSlice (napi_env env, napi_value from) {
234
253
}
235
254
236
255
/* *
237
- * Returns length of string or buffer
238
- */
239
- static size_t StringOrBufferLength (napi_env env, napi_value value) {
240
- size_t size = 0 ;
241
-
242
- if (IsString (env, value)) {
243
- napi_get_value_string_utf8 (env, value, NULL , 0 , &size);
244
- } else if (IsBuffer (env, value)) {
245
- char * buf;
246
- napi_get_buffer_info (env, value, (void **)&buf, &size);
247
- }
248
-
249
- return size;
250
- }
251
-
252
- /* *
253
- * Takes a Buffer or string property 'name' from 'opts'.
254
- * Returns null if the property does not exist or is zero-length.
256
+ * Takes a Buffer, string or Uint8Array property 'name' from 'opts'.
257
+ * Returns null if the property does not exist.
255
258
*/
256
259
static std::string* RangeOption (napi_env env, napi_value opts, const char * name) {
257
260
if (HasProperty (env, opts, name)) {
258
261
napi_value value = GetProperty (env, opts, name);
259
-
260
- if (StringOrBufferLength (env, value) >= 0 ) {
261
- LD_STRING_OR_BUFFER_TO_COPY (env, value, to);
262
- std::string* result = new std::string (toCh_, toSz_);
263
- delete [] toCh_;
264
- return result;
265
- }
262
+ // TODO: we can avoid a copy here
263
+ LD_STRING_OR_BUFFER_TO_COPY (env, value, to);
264
+ std::string* result = new std::string (toCh_, toSz_);
265
+ delete [] toCh_;
266
+ return result;
266
267
}
267
268
268
269
return NULL ;
@@ -281,8 +282,7 @@ static std::vector<std::string> KeyArray (napi_env env, napi_value arr) {
281
282
for (uint32_t i = 0 ; i < length; i++) {
282
283
napi_value element;
283
284
284
- if (napi_get_element (env, arr, i, &element) == napi_ok &&
285
- StringOrBufferLength (env, element) >= 0 ) {
285
+ if (napi_get_element (env, arr, i, &element) == napi_ok) {
286
286
LD_STRING_OR_BUFFER_TO_COPY (env, element, to);
287
287
result.emplace_back (toCh_, toSz_);
288
288
delete [] toCh_;
@@ -322,29 +322,29 @@ struct Entry {
322
322
: key_(key.data(), key.size()),
323
323
value_ (value.data(), value.size()) {}
324
324
325
- void ConvertByMode (napi_env env, Mode mode, const bool keyAsBuffer , const bool valueAsBuffer , napi_value& result) {
325
+ void ConvertByMode (napi_env env, Mode mode, const Encoding keyEncoding , const Encoding valueEncoding , napi_value& result) const {
326
326
if (mode == Mode::entries) {
327
327
napi_create_array_with_length (env, 2 , &result);
328
328
329
329
napi_value keyElement;
330
330
napi_value valueElement;
331
331
332
- Convert (env, &key_, keyAsBuffer , keyElement);
333
- Convert (env, &value_, valueAsBuffer , valueElement);
332
+ Convert (env, &key_, keyEncoding , keyElement);
333
+ Convert (env, &value_, valueEncoding , valueElement);
334
334
335
335
napi_set_element (env, result, 0 , keyElement);
336
336
napi_set_element (env, result, 1 , valueElement);
337
337
} else if (mode == Mode::keys) {
338
- Convert (env, &key_, keyAsBuffer , result);
338
+ Convert (env, &key_, keyEncoding , result);
339
339
} else {
340
- Convert (env, &value_, valueAsBuffer , result);
340
+ Convert (env, &value_, valueEncoding , result);
341
341
}
342
342
}
343
343
344
- static void Convert (napi_env env, const std::string* s, const bool asBuffer , napi_value& result) {
344
+ static void Convert (napi_env env, const std::string* s, const Encoding encoding , napi_value& result) {
345
345
if (s == NULL ) {
346
346
napi_get_undefined (env, &result);
347
- } else if (asBuffer ) {
347
+ } else if (encoding == Encoding::buffer || encoding == Encoding::view ) {
348
348
napi_create_buffer_copy (env, s->size (), s->data (), NULL , &result);
349
349
} else {
350
350
napi_create_string_utf8 (env, s->data (), s->size (), &result);
@@ -830,15 +830,15 @@ struct Iterator final : public BaseIterator {
830
830
std::string* gt,
831
831
std::string* gte,
832
832
const bool fillCache,
833
- const bool keyAsBuffer ,
834
- const bool valueAsBuffer ,
833
+ const Encoding keyEncoding ,
834
+ const Encoding valueEncoding ,
835
835
const uint32_t highWaterMarkBytes)
836
836
: BaseIterator(database, reverse, lt, lte, gt, gte, limit, fillCache),
837
837
id_ (id),
838
838
keys_(keys),
839
839
values_(values),
840
- keyAsBuffer_(keyAsBuffer ),
841
- valueAsBuffer_(valueAsBuffer ),
840
+ keyEncoding_(keyEncoding ),
841
+ valueEncoding_(valueEncoding ),
842
842
highWaterMarkBytes_(highWaterMarkBytes),
843
843
first_(true ),
844
844
nexting_(false ),
@@ -897,8 +897,8 @@ struct Iterator final : public BaseIterator {
897
897
const uint32_t id_;
898
898
const bool keys_;
899
899
const bool values_;
900
- const bool keyAsBuffer_ ;
901
- const bool valueAsBuffer_ ;
900
+ const Encoding keyEncoding_ ;
901
+ const Encoding valueEncoding_ ;
902
902
const uint32_t highWaterMarkBytes_;
903
903
bool first_;
904
904
bool nexting_;
@@ -1151,11 +1151,11 @@ struct GetWorker final : public PriorityWorker {
1151
1151
Database* database,
1152
1152
napi_value callback,
1153
1153
leveldb::Slice key,
1154
- const bool asBuffer ,
1154
+ const Encoding encoding ,
1155
1155
const bool fillCache)
1156
1156
: PriorityWorker(env, database, callback, " classic_level.db.get" ),
1157
1157
key_ (key),
1158
- asBuffer_(asBuffer ) {
1158
+ encoding_(encoding ) {
1159
1159
options_.fill_cache = fillCache;
1160
1160
}
1161
1161
@@ -1170,15 +1170,15 @@ struct GetWorker final : public PriorityWorker {
1170
1170
void HandleOKCallback (napi_env env, napi_value callback) override {
1171
1171
napi_value argv[2 ];
1172
1172
napi_get_null (env, &argv[0 ]);
1173
- Entry::Convert (env, &value_, asBuffer_ , argv[1 ]);
1173
+ Entry::Convert (env, &value_, encoding_ , argv[1 ]);
1174
1174
CallFunction (env, callback, 2 , argv);
1175
1175
}
1176
1176
1177
1177
private:
1178
1178
leveldb::ReadOptions options_;
1179
1179
leveldb::Slice key_;
1180
1180
std::string value_;
1181
- const bool asBuffer_ ;
1181
+ const Encoding encoding_ ;
1182
1182
};
1183
1183
1184
1184
/* *
@@ -1190,11 +1190,11 @@ NAPI_METHOD(db_get) {
1190
1190
1191
1191
leveldb::Slice key = ToSlice (env, argv[1 ]);
1192
1192
napi_value options = argv[2 ];
1193
- const bool asBuffer = EncodingIsBuffer (env, options, " valueEncoding" );
1193
+ const Encoding encoding = GetEncoding (env, options, " valueEncoding" );
1194
1194
const bool fillCache = BooleanProperty (env, options, " fillCache" , true );
1195
1195
napi_value callback = argv[3 ];
1196
1196
1197
- GetWorker* worker = new GetWorker (env, database, callback, key, asBuffer ,
1197
+ GetWorker* worker = new GetWorker (env, database, callback, key, encoding ,
1198
1198
fillCache);
1199
1199
worker->Queue (env);
1200
1200
@@ -1209,10 +1209,10 @@ struct GetManyWorker final : public PriorityWorker {
1209
1209
Database* database,
1210
1210
std::vector<std::string> keys,
1211
1211
napi_value callback,
1212
- const bool valueAsBuffer ,
1212
+ const Encoding valueEncoding ,
1213
1213
const bool fillCache)
1214
1214
: PriorityWorker(env, database, callback, " classic_level.get.many" ),
1215
- keys_ (std::move(keys)), valueAsBuffer_(valueAsBuffer ) {
1215
+ keys_ (std::move(keys)), valueEncoding_(valueEncoding ) {
1216
1216
options_.fill_cache = fillCache;
1217
1217
options_.snapshot = database->NewSnapshot ();
1218
1218
}
@@ -1250,7 +1250,7 @@ struct GetManyWorker final : public PriorityWorker {
1250
1250
for (size_t idx = 0 ; idx < size; idx++) {
1251
1251
std::string* value = cache_[idx];
1252
1252
napi_value element;
1253
- Entry::Convert (env, value, valueAsBuffer_ , element);
1253
+ Entry::Convert (env, value, valueEncoding_ , element);
1254
1254
napi_set_element (env, array, static_cast <uint32_t >(idx), element);
1255
1255
if (value != NULL ) delete value;
1256
1256
}
@@ -1264,7 +1264,7 @@ struct GetManyWorker final : public PriorityWorker {
1264
1264
private:
1265
1265
leveldb::ReadOptions options_;
1266
1266
const std::vector<std::string> keys_;
1267
- const bool valueAsBuffer_ ;
1267
+ const Encoding valueEncoding_ ;
1268
1268
std::vector<std::string*> cache_;
1269
1269
};
1270
1270
@@ -1277,12 +1277,12 @@ NAPI_METHOD(db_get_many) {
1277
1277
1278
1278
const auto keys = KeyArray (env, argv[1 ]);
1279
1279
napi_value options = argv[2 ];
1280
- const bool asBuffer = EncodingIsBuffer (env, options, " valueEncoding" );
1280
+ const Encoding valueEncoding = GetEncoding (env, options, " valueEncoding" );
1281
1281
const bool fillCache = BooleanProperty (env, options, " fillCache" , true );
1282
1282
napi_value callback = argv[3 ];
1283
1283
1284
1284
GetManyWorker* worker = new GetManyWorker (
1285
- env, database, keys, callback, asBuffer , fillCache
1285
+ env, database, keys, callback, valueEncoding , fillCache
1286
1286
);
1287
1287
1288
1288
worker->Queue (env);
@@ -1626,8 +1626,8 @@ NAPI_METHOD(iterator_init) {
1626
1626
const bool keys = BooleanProperty (env, options, " keys" , true );
1627
1627
const bool values = BooleanProperty (env, options, " values" , true );
1628
1628
const bool fillCache = BooleanProperty (env, options, " fillCache" , false );
1629
- const bool keyAsBuffer = EncodingIsBuffer (env, options, " keyEncoding" );
1630
- const bool valueAsBuffer = EncodingIsBuffer (env, options, " valueEncoding" );
1629
+ const Encoding keyEncoding = GetEncoding (env, options, " keyEncoding" );
1630
+ const Encoding valueEncoding = GetEncoding (env, options, " valueEncoding" );
1631
1631
const int limit = Int32Property (env, options, " limit" , -1 );
1632
1632
const uint32_t highWaterMarkBytes = Uint32Property (env, options, " highWaterMarkBytes" , 16 * 1024 );
1633
1633
@@ -1639,7 +1639,7 @@ NAPI_METHOD(iterator_init) {
1639
1639
const uint32_t id = database->currentIteratorId_ ++;
1640
1640
Iterator* iterator = new Iterator (database, id, reverse, keys,
1641
1641
values, limit, lt, lte, gt, gte, fillCache,
1642
- keyAsBuffer, valueAsBuffer , highWaterMarkBytes);
1642
+ keyEncoding, valueEncoding , highWaterMarkBytes);
1643
1643
napi_value result;
1644
1644
1645
1645
NAPI_STATUS_THROWS (napi_create_external (env, iterator,
@@ -1757,12 +1757,12 @@ struct NextWorker final : public BaseWorker {
1757
1757
napi_value jsArray;
1758
1758
napi_create_array_with_length (env, size, &jsArray);
1759
1759
1760
- const bool kab = iterator_->keyAsBuffer_ ;
1761
- const bool vab = iterator_->valueAsBuffer_ ;
1760
+ const Encoding ke = iterator_->keyEncoding_ ;
1761
+ const Encoding ve = iterator_->valueEncoding_ ;
1762
1762
1763
1763
for (uint32_t idx = 0 ; idx < size; idx++) {
1764
1764
napi_value element;
1765
- iterator_->cache_ [idx].ConvertByMode (env, Mode::entries, kab, vab , element);
1765
+ iterator_->cache_ [idx].ConvertByMode (env, Mode::entries, ke, ve , element);
1766
1766
napi_set_element (env, jsArray, idx, element);
1767
1767
}
1768
1768
0 commit comments