1313#include < vector>
1414#include < mutex>
1515#include < atomic>
16+ #include < optional>
1617
1718/* *
1819 * Forward declarations.
@@ -105,6 +106,8 @@ static std::map<std::string, LevelDbHandle> db_handles;
105106 } \
106107 }
107108
109+ #define undefined NULL
110+
108111/* *
109112 * Bit fields.
110113 */
@@ -162,6 +165,25 @@ static napi_value CreateCodeError (napi_env env, const char* code, const char* m
162165 return error;
163166}
164167
168+ static void ThrowError (napi_env env, leveldb::Status status) {
169+ if (status.IsCorruption ()) {
170+ napi_throw_error (env, " LEVEL_CORRUPTION" , status.ToString ().c_str ());
171+ } else if (!status.ok ()) {
172+ napi_throw_error (env, NULL , status.ToString ().c_str ());
173+ } else {
174+ napi_throw_error (env, NULL , " Operation failed" );
175+ }
176+ }
177+
178+ static napi_value ErrorOrNotFound (napi_env env, leveldb::Status status) {
179+ if (status.IsNotFound ()) {
180+ return undefined;
181+ } else {
182+ ThrowError (env, status);
183+ return undefined;
184+ }
185+ }
186+
165187/* *
166188 * Returns true if 'obj' has a property 'key'.
167189 */
@@ -354,6 +376,14 @@ static std::vector<std::string> KeyArray (napi_env env, napi_value arr) {
354376 return result;
355377}
356378
379+ // TODO: use in more places
380+ enum Flags : uint32_t {
381+ FILL_CACHE = 1 ,
382+ KEY_AS_BUFFER = 2 ,
383+ VALUE_AS_BUFFER = 4 ,
384+ SHARED_KEY = 8
385+ };
386+
357387/* *
358388 * Whether to yield entries, keys or values.
359389 */
@@ -538,11 +568,13 @@ struct BaseWorker {
538568struct Database {
539569 Database ()
540570 : db_(NULL ),
571+ sharedBuffer_ (NULL ),
541572 blockCache_(NULL ),
542573 filterPolicy_(leveldb::NewBloomFilterPolicy(10 )),
543574 resourceSequence_(0 ),
544575 pendingCloseWorker_(NULL ),
545576 ref_(NULL ),
577+ sharedBufferRef_(NULL ),
546578 priorityWork_(0 ) {}
547579
548580 ~Database () {
@@ -635,7 +667,40 @@ struct Database {
635667 return priorityWork_ > 0 ;
636668 }
637669
670+ bool SetSharedBuffer (napi_env env, napi_value value) {
671+ // Delete reference to previous buffer if any
672+ if (sharedBufferRef_) {
673+ napi_delete_reference (env, sharedBufferRef_);
674+ sharedBufferRef_ = NULL ;
675+ }
676+
677+ // Get underlying data (length is separately communicated, on use)
678+ if (napi_get_buffer_info (env, value, (void **)&sharedBuffer_, NULL ) != napi_ok) {
679+ sharedBuffer_ = NULL ;
680+ return false ;
681+ }
682+
683+ // Create reference in order to keep buffer alive
684+ if (napi_create_reference (env, value, 1 , &sharedBufferRef_) != napi_ok) {
685+ sharedBufferRef_ = NULL ;
686+ return false ;
687+ }
688+
689+ return true ;
690+ }
691+
692+ void ReleaseReferences (napi_env env) {
693+ if (ref_ != NULL ) napi_reference_unref (env, ref_, NULL );
694+ if (sharedBufferRef_ != NULL ) napi_reference_unref (env, sharedBufferRef_, NULL );
695+ }
696+
697+ void DeleteReferences (napi_env env) {
698+ if (ref_ != NULL ) napi_delete_reference (env, ref_);
699+ if (sharedBufferRef_ != NULL ) napi_delete_reference (env, sharedBufferRef_);
700+ }
701+
638702 leveldb::DB* db_;
703+ char * sharedBuffer_;
639704 leveldb::Cache* blockCache_;
640705 const leveldb::FilterPolicy* filterPolicy_;
641706 uint32_t resourceSequence_;
@@ -644,6 +709,7 @@ struct Database {
644709 napi_ref ref_;
645710
646711private:
712+ napi_ref sharedBufferRef_;
647713 std::atomic<uint32_t > priorityWork_;
648714 std::string location_;
649715
@@ -1098,7 +1164,7 @@ static void FinalizeDatabase (napi_env env, void* data, void* hint) {
10981164 if (data) {
10991165 Database* database = (Database*)data;
11001166 napi_remove_env_cleanup_hook (env, env_cleanup_hook, database);
1101- if ( database->ref_ != NULL ) napi_delete_reference (env, database-> ref_ );
1167+ database->DeleteReferences (env);
11021168 delete database;
11031169 }
11041170}
@@ -1224,7 +1290,7 @@ struct CloseWorker final : public BaseWorker {
12241290 }
12251291
12261292 void DoFinally (napi_env env) override {
1227- napi_reference_unref (env, database_->ref_ , NULL );
1293+ database_->ReleaseReferences (env );
12281294 BaseWorker::DoFinally (env);
12291295 }
12301296};
@@ -1309,14 +1375,15 @@ struct GetWorker final : public PriorityWorker {
13091375 GetWorker (napi_env env,
13101376 Database* database,
13111377 napi_deferred deferred,
1378+ uint32_t flags,
13121379 leveldb::Slice key,
1313- const Encoding encoding,
1314- const bool fillCache,
1380+ napi_ref keyRef,
13151381 ExplicitSnapshot* snapshot)
13161382 : PriorityWorker(env, database, deferred, " classic_level.db.get" ),
1383+ flags_ (flags),
13171384 key_(key),
1318- encoding_(encoding ) {
1319- options_.fill_cache = fillCache ;
1385+ keyRef_(keyRef ) {
1386+ options_.fill_cache = (flags & Flags::FILL_CACHE) != 0 ;
13201387
13211388 if (snapshot == NULL ) {
13221389 implicitSnapshot_ = database->NewSnapshot ();
@@ -1328,7 +1395,7 @@ struct GetWorker final : public PriorityWorker {
13281395 }
13291396
13301397 ~GetWorker () {
1331- DisposeSliceBuffer (key_);
1398+ if (!keyRef_) DisposeSliceBuffer (key_);
13321399 }
13331400
13341401 void DoExecute () override {
@@ -1340,43 +1407,166 @@ struct GetWorker final : public PriorityWorker {
13401407 }
13411408 }
13421409
1410+ void DoFinally (napi_env env) override {
1411+ if (keyRef_) napi_delete_reference (env, keyRef_);
1412+ PriorityWorker::DoFinally (env);
1413+ }
1414+
13431415 void HandleOKCallback (napi_env env, napi_deferred deferred) override {
13441416 napi_value argv;
1345- Entry::Convert (env, &value_, encoding_, argv);
1417+
1418+ if ((flags_ & Flags::VALUE_AS_BUFFER) != 0 ) {
1419+ napi_create_buffer_copy (env, value_.size (), value_.data (), NULL , &argv);
1420+ } else {
1421+ napi_create_string_utf8 (env, value_.data (), value_.size (), &argv);
1422+ }
1423+
13461424 napi_resolve_deferred (env, deferred, argv);
13471425 }
13481426
13491427private:
13501428 leveldb::ReadOptions options_;
1429+ uint32_t flags_;
13511430 leveldb::Slice key_;
1431+ napi_ref keyRef_;
13521432 std::string value_;
1353- const Encoding encoding_;
13541433 const leveldb::Snapshot* implicitSnapshot_;
13551434};
13561435
13571436/* *
13581437 * Gets a value from a database.
13591438 */
1360- NAPI_METHOD (db_get) {
1361- NAPI_ARGV (5 );
1439+ NAPI_METHOD (db_get) {
1440+ NAPI_ARGV (4 );
13621441 NAPI_DB_CONTEXT ();
13631442 NAPI_PROMISE ();
13641443
1365- leveldb::Slice key = ToSlice (env, argv[1 ]);
1366- const Encoding encoding = GetEncoding (env, argv[2 ]);
1367- const bool fillCache = BooleanValue (env, argv[3 ], true );
1444+ uint32_t flags;
1445+ NAPI_STATUS_THROWS (napi_get_value_uint32 (env, argv[1 ], &flags));
13681446
13691447 ExplicitSnapshot* snapshot = NULL ;
1370- napi_get_value_external (env, argv[4 ], (void **)&snapshot);
1448+ napi_get_value_external (env, argv[3 ], (void **)&snapshot);
13711449
1372- GetWorker* worker = new GetWorker (
1373- env, database, deferred, key, encoding, fillCache, snapshot
1374- );
1450+ char * keyBuffer;
1451+ size_t keySize;
1452+ GetWorker* worker;
1453+
1454+ if ((flags & Flags::KEY_AS_BUFFER) != 0 ) {
1455+ // Instead of copying the memory, create a reference so that it stays valid
1456+ napi_ref keyRef;
1457+ NAPI_STATUS_THROWS (napi_create_reference (env, argv[2 ], 1 , &keyRef));
1458+ NAPI_STATUS_THROWS (napi_get_typedarray_info (env, argv[2 ], NULL , &keySize, (void **)&keyBuffer, NULL , NULL ));
1459+ leveldb::Slice keySlice (keyBuffer, keySize);
1460+ worker = new GetWorker (env, database, deferred, flags, keySlice, keyRef, snapshot);
1461+ } else {
1462+ NAPI_STATUS_THROWS (napi_get_value_string_utf8 (env, argv[2 ], NULL , 0 , &keySize));
1463+ keyBuffer = new char [keySize + 1 ];
1464+ NAPI_STATUS_THROWS (napi_get_value_string_utf8 (env, argv[2 ], keyBuffer, keySize + 1 , NULL ));
1465+ keyBuffer[keySize] = ' \0 ' ;
1466+ leveldb::Slice keySlice (keyBuffer, keySize);
1467+
1468+ // A null keyRef implies that keyBuffer needs to be deleted after the read
1469+ // TODO: solve in a more obvious way like a subclass
1470+ worker = new GetWorker (env, database, deferred, flags, keySlice, NULL , snapshot);
1471+ }
13751472
13761473 worker->Queue (env);
13771474 return promise;
13781475}
13791476
1477+ struct NapiValueSink : public leveldb ::ValueSink {
1478+ NapiValueSink (napi_env env)
1479+ : leveldb::ValueSink(),
1480+ result (NULL ),
1481+ env_(env),
1482+ status_(napi_generic_failure) {}
1483+
1484+ virtual ~NapiValueSink () = default ;
1485+
1486+ const bool valid () {
1487+ return status_ == napi_ok;
1488+ }
1489+
1490+ napi_value result;
1491+
1492+ protected:
1493+ napi_env env_;
1494+ napi_status status_;
1495+ };
1496+
1497+ struct NapiStringValueSink : public NapiValueSink {
1498+ NapiStringValueSink (napi_env env)
1499+ : NapiValueSink(env) {}
1500+
1501+ public:
1502+ void assign (const char * data, size_t size) override {
1503+ status_ = napi_create_string_utf8 (env_, data, size, &result);
1504+ }
1505+ };
1506+
1507+ struct NapiBufferValueSink : public NapiValueSink {
1508+ NapiBufferValueSink (napi_env env)
1509+ : NapiValueSink(env) {}
1510+
1511+ public:
1512+ void assign (const char * data, size_t size) override {
1513+ status_ = napi_create_buffer_copy (env_, size, data, NULL , &result);
1514+ }
1515+ };
1516+
1517+ /* *
1518+ * Get a value from a database synchronously.
1519+ */
1520+ NAPI_METHOD (db_get_sync) {
1521+ NAPI_ARGV (4 );
1522+ NAPI_DB_CONTEXT ();
1523+
1524+ uint32_t flags;
1525+ NAPI_STATUS_THROWS (napi_get_value_uint32 (env, argv[1 ], &flags));
1526+
1527+ std::optional<leveldb::Slice> keySlice;
1528+
1529+ if ((flags & Flags::SHARED_KEY) != 0 ) {
1530+ uint32_t keySize;
1531+ NAPI_STATUS_THROWS (napi_get_value_uint32 (env, argv[2 ], &keySize));
1532+ keySlice.emplace (database->sharedBuffer_ , keySize);
1533+ } else {
1534+ char * keyBuffer;
1535+ size_t keySize;
1536+ NAPI_STATUS_THROWS (napi_get_typedarray_info (env, argv[2 ], NULL , &keySize, (void **)&keyBuffer, NULL , NULL ));
1537+ keySlice.emplace (keyBuffer, keySize);
1538+ }
1539+
1540+ ExplicitSnapshot* snapshot = NULL ;
1541+ napi_get_value_external (env, argv[3 ], (void **)&snapshot);
1542+
1543+ leveldb::ReadOptions options;
1544+ options.fill_cache = (flags & Flags::FILL_CACHE) != 0 ;
1545+ options.snapshot = snapshot != NULL ? snapshot->nut : NULL ;
1546+
1547+ if ((flags & Flags::VALUE_AS_BUFFER) != 0 ) {
1548+ NapiBufferValueSink valueSink (env);
1549+ leveldb::Status status = database->Get (options, *keySlice, valueSink);
1550+ return status.ok () && valueSink.valid () ? valueSink.result : ErrorOrNotFound (env, status);
1551+ } else {
1552+ NapiStringValueSink valueSink (env);
1553+ leveldb::Status status = database->Get (options, *keySlice, valueSink);
1554+ return status.ok () && valueSink.valid () ? valueSink.result : ErrorOrNotFound (env, status);
1555+ }
1556+ }
1557+
1558+ NAPI_METHOD (db_set_shared_buffer) {
1559+ NAPI_ARGV (2 );
1560+ NAPI_DB_CONTEXT ();
1561+
1562+ if (!database->SetSharedBuffer (env, argv[1 ])) {
1563+ napi_throw_error (env, NULL , " SetSharedBuffer failed" );
1564+ return undefined;
1565+ }
1566+
1567+ return undefined;
1568+ }
1569+
13801570/* *
13811571 * Worker class for getting many values.
13821572 */
@@ -2277,10 +2467,12 @@ NAPI_METHOD(snapshot_close) {
22772467 */
22782468NAPI_INIT () {
22792469 NAPI_EXPORT_FUNCTION (db_init);
2470+ NAPI_EXPORT_FUNCTION (db_set_shared_buffer)
22802471 NAPI_EXPORT_FUNCTION (db_open);
22812472 NAPI_EXPORT_FUNCTION (db_close);
22822473 NAPI_EXPORT_FUNCTION (db_put);
22832474 NAPI_EXPORT_FUNCTION (db_get);
2475+ NAPI_EXPORT_FUNCTION (db_get_sync);
22842476 NAPI_EXPORT_FUNCTION (db_get_many);
22852477 NAPI_EXPORT_FUNCTION (db_del);
22862478 NAPI_EXPORT_FUNCTION (db_clear);
0 commit comments