@@ -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,32 @@ 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
172
size_t size;
173
+ char * buf = new char [2 ];
174
+ Encoding encoding = Encoding::utf8;
158
175
159
176
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 ;
177
+ napi_get_value_string_utf8 (env, value, buf, 2 , &size) == napi_ok && size == 1 ) {
178
+ // Value is either "buffer", "utf8" or "view" so we only have to read the first char
179
+ switch (buf[0 ]) {
180
+ case ' b' :
181
+ encoding = Encoding::buffer;
182
+ break ;
183
+ case ' v' :
184
+ encoding = Encoding::view;
185
+ break ;
186
+ }
163
187
}
164
188
165
- return false ;
189
+ delete[] buf;
190
+ return encoding;
166
191
}
167
192
168
193
/* *
@@ -234,35 +259,17 @@ static leveldb::Slice ToSlice (napi_env env, napi_value from) {
234
259
}
235
260
236
261
/* *
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.
262
+ * Takes a Buffer, string or Uint8Array property 'name' from 'opts'.
263
+ * Returns null if the property does not exist.
255
264
*/
256
265
static std::string* RangeOption (napi_env env, napi_value opts, const char * name) {
257
266
if (HasProperty (env, opts, name)) {
258
267
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
- }
268
+ // TODO: we can avoid a copy here
269
+ LD_STRING_OR_BUFFER_TO_COPY (env, value, to);
270
+ std::string* result = new std::string (toCh_, toSz_);
271
+ delete [] toCh_;
272
+ return result;
266
273
}
267
274
268
275
return NULL ;
@@ -281,8 +288,7 @@ static std::vector<std::string>* KeyArray (napi_env env, napi_value arr) {
281
288
for (uint32_t i = 0 ; i < length; i++) {
282
289
napi_value element;
283
290
284
- if (napi_get_element (env, arr, i, &element) == napi_ok &&
285
- StringOrBufferLength (env, element) >= 0 ) {
291
+ if (napi_get_element (env, arr, i, &element) == napi_ok) {
286
292
LD_STRING_OR_BUFFER_TO_COPY (env, element, to);
287
293
result->emplace_back (toCh_, toSz_);
288
294
delete [] toCh_;
@@ -322,29 +328,32 @@ struct Entry {
322
328
: key_(key->data (), key->size()),
323
329
value_(value->data (), value->size()) {}
324
330
325
- void ConvertByMode (napi_env env, Mode mode, const bool keyAsBuffer , const bool valueAsBuffer , napi_value* result) {
331
+ void ConvertByMode (napi_env env, Mode mode, const Encoding keyEncoding , const Encoding valueEncoding , napi_value* result) {
326
332
if (mode == Mode::entries) {
327
333
napi_create_array_with_length (env, 2 , result);
328
334
329
335
napi_value keyElement;
330
336
napi_value valueElement;
331
337
332
- Convert (env, &key_, keyAsBuffer , &keyElement);
333
- Convert (env, &value_, valueAsBuffer , &valueElement);
338
+ Convert (env, &key_, keyEncoding , &keyElement);
339
+ Convert (env, &value_, valueEncoding , &valueElement);
334
340
335
341
napi_set_element (env, *result, 0 , keyElement);
336
342
napi_set_element (env, *result, 1 , valueElement);
337
343
} else if (mode == Mode::keys) {
338
- Convert (env, &key_, keyAsBuffer , result);
344
+ Convert (env, &key_, keyEncoding , result);
339
345
} else {
340
- Convert (env, &value_, valueAsBuffer , result);
346
+ Convert (env, &value_, valueEncoding , result);
341
347
}
342
348
}
343
349
344
- static void Convert (napi_env env, const std::string* s, const bool asBuffer , napi_value* result) {
350
+ static void Convert (napi_env env, const std::string* s, const Encoding encoding , napi_value* result) {
345
351
if (s == NULL ) {
346
352
napi_get_undefined (env, result);
347
- } else if (asBuffer) {
353
+ } else if (encoding == Encoding::buffer) {
354
+ napi_create_buffer_copy (env, s->size (), s->data (), NULL , result);
355
+ } else if (encoding == Encoding::view) {
356
+ // TODO: use napi_create_typedarray if performance is equal or better
348
357
napi_create_buffer_copy (env, s->size (), s->data (), NULL , result);
349
358
} else {
350
359
napi_create_string_utf8 (env, s->data (), s->size (), result);
@@ -830,15 +839,15 @@ struct Iterator final : public BaseIterator {
830
839
std::string* gt,
831
840
std::string* gte,
832
841
const bool fillCache,
833
- const bool keyAsBuffer ,
834
- const bool valueAsBuffer ,
842
+ const Encoding keyEncoding ,
843
+ const Encoding valueEncoding ,
835
844
const uint32_t highWaterMarkBytes)
836
845
: BaseIterator(database, reverse, lt, lte, gt, gte, limit, fillCache),
837
846
id_ (id),
838
847
keys_(keys),
839
848
values_(values),
840
- keyAsBuffer_(keyAsBuffer ),
841
- valueAsBuffer_(valueAsBuffer ),
849
+ keyEncoding_(keyEncoding ),
850
+ valueEncoding_(valueEncoding ),
842
851
highWaterMarkBytes_(highWaterMarkBytes),
843
852
first_(true ),
844
853
nexting_(false ),
@@ -897,8 +906,8 @@ struct Iterator final : public BaseIterator {
897
906
const uint32_t id_;
898
907
const bool keys_;
899
908
const bool values_;
900
- const bool keyAsBuffer_ ;
901
- const bool valueAsBuffer_ ;
909
+ const Encoding keyEncoding_ ;
910
+ const Encoding valueEncoding_ ;
902
911
const uint32_t highWaterMarkBytes_;
903
912
bool first_;
904
913
bool nexting_;
@@ -1151,11 +1160,11 @@ struct GetWorker final : public PriorityWorker {
1151
1160
Database* database,
1152
1161
napi_value callback,
1153
1162
leveldb::Slice key,
1154
- const bool asBuffer ,
1163
+ const Encoding encoding ,
1155
1164
const bool fillCache)
1156
1165
: PriorityWorker(env, database, callback, " classic_level.db.get" ),
1157
1166
key_ (key),
1158
- asBuffer_(asBuffer ) {
1167
+ encoding_(encoding ) {
1159
1168
options_.fill_cache = fillCache;
1160
1169
}
1161
1170
@@ -1170,15 +1179,15 @@ struct GetWorker final : public PriorityWorker {
1170
1179
void HandleOKCallback (napi_env env, napi_value callback) override {
1171
1180
napi_value argv[2 ];
1172
1181
napi_get_null (env, &argv[0 ]);
1173
- Entry::Convert (env, &value_, asBuffer_ , &argv[1 ]);
1182
+ Entry::Convert (env, &value_, encoding_ , &argv[1 ]);
1174
1183
CallFunction (env, callback, 2 , argv);
1175
1184
}
1176
1185
1177
1186
private:
1178
1187
leveldb::ReadOptions options_;
1179
1188
leveldb::Slice key_;
1180
1189
std::string value_;
1181
- const bool asBuffer_ ;
1190
+ const Encoding encoding_ ;
1182
1191
};
1183
1192
1184
1193
/* *
@@ -1190,11 +1199,11 @@ NAPI_METHOD(db_get) {
1190
1199
1191
1200
leveldb::Slice key = ToSlice (env, argv[1 ]);
1192
1201
napi_value options = argv[2 ];
1193
- const bool asBuffer = EncodingIsBuffer (env, options, " valueEncoding" );
1202
+ const Encoding encoding = GetEncoding (env, options, " valueEncoding" );
1194
1203
const bool fillCache = BooleanProperty (env, options, " fillCache" , true );
1195
1204
napi_value callback = argv[3 ];
1196
1205
1197
- GetWorker* worker = new GetWorker (env, database, callback, key, asBuffer ,
1206
+ GetWorker* worker = new GetWorker (env, database, callback, key, encoding ,
1198
1207
fillCache);
1199
1208
worker->Queue (env);
1200
1209
@@ -1209,10 +1218,10 @@ struct GetManyWorker final : public PriorityWorker {
1209
1218
Database* database,
1210
1219
const std::vector<std::string>* keys,
1211
1220
napi_value callback,
1212
- const bool valueAsBuffer ,
1221
+ const Encoding valueEncoding ,
1213
1222
const bool fillCache)
1214
1223
: PriorityWorker(env, database, callback, " classic_level.get.many" ),
1215
- keys_ (keys), valueAsBuffer_(valueAsBuffer ) {
1224
+ keys_ (keys), valueEncoding_(valueEncoding ) {
1216
1225
options_.fill_cache = fillCache;
1217
1226
options_.snapshot = database->NewSnapshot ();
1218
1227
}
@@ -1254,7 +1263,7 @@ struct GetManyWorker final : public PriorityWorker {
1254
1263
for (size_t idx = 0 ; idx < size; idx++) {
1255
1264
std::string* value = cache_[idx];
1256
1265
napi_value element;
1257
- Entry::Convert (env, value, valueAsBuffer_ , &element);
1266
+ Entry::Convert (env, value, valueEncoding_ , &element);
1258
1267
napi_set_element (env, array, static_cast <uint32_t >(idx), element);
1259
1268
if (value != NULL ) delete value;
1260
1269
}
@@ -1268,7 +1277,7 @@ struct GetManyWorker final : public PriorityWorker {
1268
1277
private:
1269
1278
leveldb::ReadOptions options_;
1270
1279
const std::vector<std::string>* keys_;
1271
- const bool valueAsBuffer_ ;
1280
+ const Encoding valueEncoding_ ;
1272
1281
std::vector<std::string*> cache_;
1273
1282
};
1274
1283
@@ -1281,12 +1290,12 @@ NAPI_METHOD(db_get_many) {
1281
1290
1282
1291
const std::vector<std::string>* keys = KeyArray (env, argv[1 ]);
1283
1292
napi_value options = argv[2 ];
1284
- const bool asBuffer = EncodingIsBuffer (env, options, " valueEncoding" );
1293
+ const Encoding valueEncoding = GetEncoding (env, options, " valueEncoding" );
1285
1294
const bool fillCache = BooleanProperty (env, options, " fillCache" , true );
1286
1295
napi_value callback = argv[3 ];
1287
1296
1288
1297
GetManyWorker* worker = new GetManyWorker (
1289
- env, database, keys, callback, asBuffer , fillCache
1298
+ env, database, keys, callback, valueEncoding , fillCache
1290
1299
);
1291
1300
1292
1301
worker->Queue (env);
@@ -1630,8 +1639,8 @@ NAPI_METHOD(iterator_init) {
1630
1639
const bool keys = BooleanProperty (env, options, " keys" , true );
1631
1640
const bool values = BooleanProperty (env, options, " values" , true );
1632
1641
const bool fillCache = BooleanProperty (env, options, " fillCache" , false );
1633
- const bool keyAsBuffer = EncodingIsBuffer (env, options, " keyEncoding" );
1634
- const bool valueAsBuffer = EncodingIsBuffer (env, options, " valueEncoding" );
1642
+ const Encoding keyEncoding = GetEncoding (env, options, " keyEncoding" );
1643
+ const Encoding valueEncoding = GetEncoding (env, options, " valueEncoding" );
1635
1644
const int limit = Int32Property (env, options, " limit" , -1 );
1636
1645
const uint32_t highWaterMarkBytes = Uint32Property (env, options, " highWaterMarkBytes" , 16 * 1024 );
1637
1646
@@ -1643,7 +1652,7 @@ NAPI_METHOD(iterator_init) {
1643
1652
const uint32_t id = database->currentIteratorId_ ++;
1644
1653
Iterator* iterator = new Iterator (database, id, reverse, keys,
1645
1654
values, limit, lt, lte, gt, gte, fillCache,
1646
- keyAsBuffer, valueAsBuffer , highWaterMarkBytes);
1655
+ keyEncoding, valueEncoding , highWaterMarkBytes);
1647
1656
napi_value result;
1648
1657
1649
1658
NAPI_STATUS_THROWS (napi_create_external (env, iterator,
@@ -1761,12 +1770,12 @@ struct NextWorker final : public BaseWorker {
1761
1770
napi_value jsArray;
1762
1771
napi_create_array_with_length (env, size, &jsArray);
1763
1772
1764
- const bool kab = iterator_->keyAsBuffer_ ;
1765
- const bool vab = iterator_->valueAsBuffer_ ;
1773
+ const Encoding ke = iterator_->keyEncoding_ ;
1774
+ const Encoding ve = iterator_->valueEncoding_ ;
1766
1775
1767
1776
for (uint32_t idx = 0 ; idx < size; idx++) {
1768
1777
napi_value element;
1769
- iterator_->cache_ [idx].ConvertByMode (env, Mode::entries, kab, vab , &element);
1778
+ iterator_->cache_ [idx].ConvertByMode (env, Mode::entries, ke, ve , &element);
1770
1779
napi_set_element (env, jsArray, idx, element);
1771
1780
}
1772
1781
0 commit comments