@@ -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,29 @@ 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) const {
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 || encoding == Encoding::view ) {
348348 napi_create_buffer_copy (env, s->size (), s->data (), NULL , &result);
349349 } else {
350350 napi_create_string_utf8 (env, s->data (), s->size (), &result);
@@ -830,15 +830,15 @@ struct Iterator final : public BaseIterator {
830830 std::string* gt,
831831 std::string* gte,
832832 const bool fillCache,
833- const bool keyAsBuffer ,
834- const bool valueAsBuffer ,
833+ const Encoding keyEncoding ,
834+ const Encoding valueEncoding ,
835835 const uint32_t highWaterMarkBytes)
836836 : BaseIterator(database, reverse, lt, lte, gt, gte, limit, fillCache),
837837 id_ (id),
838838 keys_(keys),
839839 values_(values),
840- keyAsBuffer_(keyAsBuffer ),
841- valueAsBuffer_(valueAsBuffer ),
840+ keyEncoding_(keyEncoding ),
841+ valueEncoding_(valueEncoding ),
842842 highWaterMarkBytes_(highWaterMarkBytes),
843843 first_(true ),
844844 nexting_(false ),
@@ -897,8 +897,8 @@ struct Iterator final : public BaseIterator {
897897 const uint32_t id_;
898898 const bool keys_;
899899 const bool values_;
900- const bool keyAsBuffer_ ;
901- const bool valueAsBuffer_ ;
900+ const Encoding keyEncoding_ ;
901+ const Encoding valueEncoding_ ;
902902 const uint32_t highWaterMarkBytes_;
903903 bool first_;
904904 bool nexting_;
@@ -1151,11 +1151,11 @@ struct GetWorker final : public PriorityWorker {
11511151 Database* database,
11521152 napi_value callback,
11531153 leveldb::Slice key,
1154- const bool asBuffer ,
1154+ const Encoding encoding ,
11551155 const bool fillCache)
11561156 : PriorityWorker(env, database, callback, " classic_level.db.get" ),
11571157 key_ (key),
1158- asBuffer_(asBuffer ) {
1158+ encoding_(encoding ) {
11591159 options_.fill_cache = fillCache;
11601160 }
11611161
@@ -1170,15 +1170,15 @@ struct GetWorker final : public PriorityWorker {
11701170 void HandleOKCallback (napi_env env, napi_value callback) override {
11711171 napi_value argv[2 ];
11721172 napi_get_null (env, &argv[0 ]);
1173- Entry::Convert (env, &value_, asBuffer_ , argv[1 ]);
1173+ Entry::Convert (env, &value_, encoding_ , argv[1 ]);
11741174 CallFunction (env, callback, 2 , argv);
11751175 }
11761176
11771177private:
11781178 leveldb::ReadOptions options_;
11791179 leveldb::Slice key_;
11801180 std::string value_;
1181- const bool asBuffer_ ;
1181+ const Encoding encoding_ ;
11821182};
11831183
11841184/* *
@@ -1190,11 +1190,11 @@ NAPI_METHOD(db_get) {
11901190
11911191 leveldb::Slice key = ToSlice (env, argv[1 ]);
11921192 napi_value options = argv[2 ];
1193- const bool asBuffer = EncodingIsBuffer (env, options, " valueEncoding" );
1193+ const Encoding encoding = GetEncoding (env, options, " valueEncoding" );
11941194 const bool fillCache = BooleanProperty (env, options, " fillCache" , true );
11951195 napi_value callback = argv[3 ];
11961196
1197- GetWorker* worker = new GetWorker (env, database, callback, key, asBuffer ,
1197+ GetWorker* worker = new GetWorker (env, database, callback, key, encoding ,
11981198 fillCache);
11991199 worker->Queue (env);
12001200
@@ -1209,10 +1209,10 @@ struct GetManyWorker final : public PriorityWorker {
12091209 Database* database,
12101210 std::vector<std::string> keys,
12111211 napi_value callback,
1212- const bool valueAsBuffer ,
1212+ const Encoding valueEncoding ,
12131213 const bool fillCache)
12141214 : PriorityWorker(env, database, callback, " classic_level.get.many" ),
1215- keys_ (std::move(keys)), valueAsBuffer_(valueAsBuffer ) {
1215+ keys_ (std::move(keys)), valueEncoding_(valueEncoding ) {
12161216 options_.fill_cache = fillCache;
12171217 options_.snapshot = database->NewSnapshot ();
12181218 }
@@ -1250,7 +1250,7 @@ struct GetManyWorker final : public PriorityWorker {
12501250 for (size_t idx = 0 ; idx < size; idx++) {
12511251 std::string* value = cache_[idx];
12521252 napi_value element;
1253- Entry::Convert (env, value, valueAsBuffer_ , element);
1253+ Entry::Convert (env, value, valueEncoding_ , element);
12541254 napi_set_element (env, array, static_cast <uint32_t >(idx), element);
12551255 if (value != NULL ) delete value;
12561256 }
@@ -1264,7 +1264,7 @@ struct GetManyWorker final : public PriorityWorker {
12641264private:
12651265 leveldb::ReadOptions options_;
12661266 const std::vector<std::string> keys_;
1267- const bool valueAsBuffer_ ;
1267+ const Encoding valueEncoding_ ;
12681268 std::vector<std::string*> cache_;
12691269};
12701270
@@ -1277,12 +1277,12 @@ NAPI_METHOD(db_get_many) {
12771277
12781278 const auto keys = KeyArray (env, argv[1 ]);
12791279 napi_value options = argv[2 ];
1280- const bool asBuffer = EncodingIsBuffer (env, options, " valueEncoding" );
1280+ const Encoding valueEncoding = GetEncoding (env, options, " valueEncoding" );
12811281 const bool fillCache = BooleanProperty (env, options, " fillCache" , true );
12821282 napi_value callback = argv[3 ];
12831283
12841284 GetManyWorker* worker = new GetManyWorker (
1285- env, database, keys, callback, asBuffer , fillCache
1285+ env, database, keys, callback, valueEncoding , fillCache
12861286 );
12871287
12881288 worker->Queue (env);
@@ -1626,8 +1626,8 @@ NAPI_METHOD(iterator_init) {
16261626 const bool keys = BooleanProperty (env, options, " keys" , true );
16271627 const bool values = BooleanProperty (env, options, " values" , true );
16281628 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" );
16311631 const int limit = Int32Property (env, options, " limit" , -1 );
16321632 const uint32_t highWaterMarkBytes = Uint32Property (env, options, " highWaterMarkBytes" , 16 * 1024 );
16331633
@@ -1639,7 +1639,7 @@ NAPI_METHOD(iterator_init) {
16391639 const uint32_t id = database->currentIteratorId_ ++;
16401640 Iterator* iterator = new Iterator (database, id, reverse, keys,
16411641 values, limit, lt, lte, gt, gte, fillCache,
1642- keyAsBuffer, valueAsBuffer , highWaterMarkBytes);
1642+ keyEncoding, valueEncoding , highWaterMarkBytes);
16431643 napi_value result;
16441644
16451645 NAPI_STATUS_THROWS (napi_create_external (env, iterator,
@@ -1757,12 +1757,12 @@ struct NextWorker final : public BaseWorker {
17571757 napi_value jsArray;
17581758 napi_create_array_with_length (env, size, &jsArray);
17591759
1760- const bool kab = iterator_->keyAsBuffer_ ;
1761- const bool vab = iterator_->valueAsBuffer_ ;
1760+ const Encoding ke = iterator_->keyEncoding_ ;
1761+ const Encoding ve = iterator_->valueEncoding_ ;
17621762
17631763 for (uint32_t idx = 0 ; idx < size; idx++) {
17641764 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);
17661766 napi_set_element (env, jsArray, idx, element);
17671767 }
17681768
0 commit comments