@@ -48,6 +48,7 @@ static void iterator_close_do (napi_env env, Iterator* iterator, napi_value cb);
4848#define NAPI_ARGV_UTF8_NEW (name, i ) \
4949 NAPI_UTF8_NEW (name, argv[i])
5050
51+ // TODO: consider using encoding options instead of type checking
5152#define LD_STRING_OR_BUFFER_TO_COPY (env, from, to ) \
5253 char * to##Ch_ = 0 ; \
5354 size_t to##Sz_ = 0 ; \
@@ -61,6 +62,18 @@ static void iterator_close_do (napi_env env, Iterator* iterator, napi_value cb);
6162 napi_get_buffer_info (env, from, (void **)&buf, &to##Sz_); \
6263 to##Ch_ = new char [to##Sz_]; \
6364 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+ } \
6477 }
6578
6679/* ********************************************************************
@@ -149,20 +162,26 @@ static bool BooleanProperty (napi_env env, napi_value obj, const char* key,
149162 return DEFAULT;
150163}
151164
165+ enum Encoding { buffer, utf8, view };
166+
152167/* *
153- * Returns true if the options object contains an encoding option that is "buffer"
168+ * Returns internal Encoding enum matching the given encoding option.
154169 */
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) {
156171 napi_value value;
157- size_t size;
172+ size_t copied;
173+ char buf[2 ];
158174
159175 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+ }
163182 }
164183
165- return false ;
184+ return Encoding::utf8 ;
166185}
167186
168187/* *
@@ -234,35 +253,17 @@ static leveldb::Slice ToSlice (napi_env env, napi_value from) {
234253}
235254
236255/* *
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.
255258 */
256259static std::string* RangeOption (napi_env env, napi_value opts, const char * name) {
257260 if (HasProperty (env, opts, name)) {
258261 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;
266267 }
267268
268269 return NULL ;
@@ -281,8 +282,7 @@ static std::vector<std::string> KeyArray (napi_env env, napi_value arr) {
281282 for (uint32_t i = 0 ; i < length; i++) {
282283 napi_value element;
283284
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) {
286286 LD_STRING_OR_BUFFER_TO_COPY (env, element, to);
287287 result.emplace_back (toCh_, toSz_);
288288 delete [] toCh_;
@@ -322,29 +322,32 @@ struct Entry {
322322 : key_(key.data(), key.size()),
323323 value_ (value.data(), value.size()) {}
324324
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) {
326326 if (mode == Mode::entries) {
327327 napi_create_array_with_length (env, 2 , &result);
328328
329329 napi_value keyElement;
330330 napi_value valueElement;
331331
332- Convert (env, &key_, keyAsBuffer , keyElement);
333- Convert (env, &value_, valueAsBuffer , valueElement);
332+ Convert (env, &key_, keyEncoding , keyElement);
333+ Convert (env, &value_, valueEncoding , valueElement);
334334
335335 napi_set_element (env, result, 0 , keyElement);
336336 napi_set_element (env, result, 1 , valueElement);
337337 } else if (mode == Mode::keys) {
338- Convert (env, &key_, keyAsBuffer , result);
338+ Convert (env, &key_, keyEncoding , result);
339339 } else {
340- Convert (env, &value_, valueAsBuffer , result);
340+ Convert (env, &value_, valueEncoding , result);
341341 }
342342 }
343343
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) {
345345 if (s == NULL ) {
346346 napi_get_undefined (env, &result);
347- } else if (asBuffer) {
347+ } else if (encoding == Encoding::buffer) {
348+ napi_create_buffer_copy (env, s->size (), s->data (), NULL , &result);
349+ } else if (encoding == Encoding::view) {
350+ // TODO: use napi_create_typedarray if performance is equal or better
348351 napi_create_buffer_copy (env, s->size (), s->data (), NULL , &result);
349352 } else {
350353 napi_create_string_utf8 (env, s->data (), s->size (), &result);
@@ -830,15 +833,15 @@ struct Iterator final : public BaseIterator {
830833 std::string* gt,
831834 std::string* gte,
832835 const bool fillCache,
833- const bool keyAsBuffer ,
834- const bool valueAsBuffer ,
836+ const Encoding keyEncoding ,
837+ const Encoding valueEncoding ,
835838 const uint32_t highWaterMarkBytes)
836839 : BaseIterator(database, reverse, lt, lte, gt, gte, limit, fillCache),
837840 id_ (id),
838841 keys_(keys),
839842 values_(values),
840- keyAsBuffer_(keyAsBuffer ),
841- valueAsBuffer_(valueAsBuffer ),
843+ keyEncoding_(keyEncoding ),
844+ valueEncoding_(valueEncoding ),
842845 highWaterMarkBytes_(highWaterMarkBytes),
843846 first_(true ),
844847 nexting_(false ),
@@ -897,8 +900,8 @@ struct Iterator final : public BaseIterator {
897900 const uint32_t id_;
898901 const bool keys_;
899902 const bool values_;
900- const bool keyAsBuffer_ ;
901- const bool valueAsBuffer_ ;
903+ const Encoding keyEncoding_ ;
904+ const Encoding valueEncoding_ ;
902905 const uint32_t highWaterMarkBytes_;
903906 bool first_;
904907 bool nexting_;
@@ -1151,11 +1154,11 @@ struct GetWorker final : public PriorityWorker {
11511154 Database* database,
11521155 napi_value callback,
11531156 leveldb::Slice key,
1154- const bool asBuffer ,
1157+ const Encoding encoding ,
11551158 const bool fillCache)
11561159 : PriorityWorker(env, database, callback, " classic_level.db.get" ),
11571160 key_ (key),
1158- asBuffer_(asBuffer ) {
1161+ encoding_(encoding ) {
11591162 options_.fill_cache = fillCache;
11601163 }
11611164
@@ -1170,15 +1173,15 @@ struct GetWorker final : public PriorityWorker {
11701173 void HandleOKCallback (napi_env env, napi_value callback) override {
11711174 napi_value argv[2 ];
11721175 napi_get_null (env, &argv[0 ]);
1173- Entry::Convert (env, &value_, asBuffer_ , argv[1 ]);
1176+ Entry::Convert (env, &value_, encoding_ , argv[1 ]);
11741177 CallFunction (env, callback, 2 , argv);
11751178 }
11761179
11771180private:
11781181 leveldb::ReadOptions options_;
11791182 leveldb::Slice key_;
11801183 std::string value_;
1181- const bool asBuffer_ ;
1184+ const Encoding encoding_ ;
11821185};
11831186
11841187/* *
@@ -1190,11 +1193,11 @@ NAPI_METHOD(db_get) {
11901193
11911194 leveldb::Slice key = ToSlice (env, argv[1 ]);
11921195 napi_value options = argv[2 ];
1193- const bool asBuffer = EncodingIsBuffer (env, options, " valueEncoding" );
1196+ const Encoding encoding = GetEncoding (env, options, " valueEncoding" );
11941197 const bool fillCache = BooleanProperty (env, options, " fillCache" , true );
11951198 napi_value callback = argv[3 ];
11961199
1197- GetWorker* worker = new GetWorker (env, database, callback, key, asBuffer ,
1200+ GetWorker* worker = new GetWorker (env, database, callback, key, encoding ,
11981201 fillCache);
11991202 worker->Queue (env);
12001203
@@ -1209,10 +1212,10 @@ struct GetManyWorker final : public PriorityWorker {
12091212 Database* database,
12101213 std::vector<std::string> keys,
12111214 napi_value callback,
1212- const bool valueAsBuffer ,
1215+ const Encoding valueEncoding ,
12131216 const bool fillCache)
12141217 : PriorityWorker(env, database, callback, " classic_level.get.many" ),
1215- keys_ (std::move(keys)), valueAsBuffer_(valueAsBuffer ) {
1218+ keys_ (std::move(keys)), valueEncoding_(valueEncoding ) {
12161219 options_.fill_cache = fillCache;
12171220 options_.snapshot = database->NewSnapshot ();
12181221 }
@@ -1250,7 +1253,7 @@ struct GetManyWorker final : public PriorityWorker {
12501253 for (size_t idx = 0 ; idx < size; idx++) {
12511254 std::string* value = cache_[idx];
12521255 napi_value element;
1253- Entry::Convert (env, value, valueAsBuffer_ , element);
1256+ Entry::Convert (env, value, valueEncoding_ , element);
12541257 napi_set_element (env, array, static_cast <uint32_t >(idx), element);
12551258 if (value != NULL ) delete value;
12561259 }
@@ -1264,7 +1267,7 @@ struct GetManyWorker final : public PriorityWorker {
12641267private:
12651268 leveldb::ReadOptions options_;
12661269 const std::vector<std::string> keys_;
1267- const bool valueAsBuffer_ ;
1270+ const Encoding valueEncoding_ ;
12681271 std::vector<std::string*> cache_;
12691272};
12701273
@@ -1277,12 +1280,12 @@ NAPI_METHOD(db_get_many) {
12771280
12781281 const auto keys = KeyArray (env, argv[1 ]);
12791282 napi_value options = argv[2 ];
1280- const bool asBuffer = EncodingIsBuffer (env, options, " valueEncoding" );
1283+ const Encoding valueEncoding = GetEncoding (env, options, " valueEncoding" );
12811284 const bool fillCache = BooleanProperty (env, options, " fillCache" , true );
12821285 napi_value callback = argv[3 ];
12831286
12841287 GetManyWorker* worker = new GetManyWorker (
1285- env, database, keys, callback, asBuffer , fillCache
1288+ env, database, keys, callback, valueEncoding , fillCache
12861289 );
12871290
12881291 worker->Queue (env);
@@ -1626,8 +1629,8 @@ NAPI_METHOD(iterator_init) {
16261629 const bool keys = BooleanProperty (env, options, " keys" , true );
16271630 const bool values = BooleanProperty (env, options, " values" , true );
16281631 const bool fillCache = BooleanProperty (env, options, " fillCache" , false );
1629- const bool keyAsBuffer = EncodingIsBuffer (env, options, " keyEncoding" );
1630- const bool valueAsBuffer = EncodingIsBuffer (env, options, " valueEncoding" );
1632+ const Encoding keyEncoding = GetEncoding (env, options, " keyEncoding" );
1633+ const Encoding valueEncoding = GetEncoding (env, options, " valueEncoding" );
16311634 const int limit = Int32Property (env, options, " limit" , -1 );
16321635 const uint32_t highWaterMarkBytes = Uint32Property (env, options, " highWaterMarkBytes" , 16 * 1024 );
16331636
@@ -1639,7 +1642,7 @@ NAPI_METHOD(iterator_init) {
16391642 const uint32_t id = database->currentIteratorId_ ++;
16401643 Iterator* iterator = new Iterator (database, id, reverse, keys,
16411644 values, limit, lt, lte, gt, gte, fillCache,
1642- keyAsBuffer, valueAsBuffer , highWaterMarkBytes);
1645+ keyEncoding, valueEncoding , highWaterMarkBytes);
16431646 napi_value result;
16441647
16451648 NAPI_STATUS_THROWS (napi_create_external (env, iterator,
@@ -1757,12 +1760,12 @@ struct NextWorker final : public BaseWorker {
17571760 napi_value jsArray;
17581761 napi_create_array_with_length (env, size, &jsArray);
17591762
1760- const bool kab = iterator_->keyAsBuffer_ ;
1761- const bool vab = iterator_->valueAsBuffer_ ;
1763+ const Encoding ke = iterator_->keyEncoding_ ;
1764+ const Encoding ve = iterator_->valueEncoding_ ;
17621765
17631766 for (uint32_t idx = 0 ; idx < size; idx++) {
17641767 napi_value element;
1765- iterator_->cache_ [idx].ConvertByMode (env, Mode::entries, kab, vab , element);
1768+ iterator_->cache_ [idx].ConvertByMode (env, Mode::entries, ke, ve , element);
17661769 napi_set_element (env, jsArray, idx, element);
17671770 }
17681771
0 commit comments