From: Steve Vinoski Date: Thu, 20 Dec 2012 16:14:11 -0500 Subject: [PATCH] allow Get() calls to avoid copies into std::string Add a new abstract base class leveldb::Value that applications can easily derive from to supply their own memory management for values retrieved via Get(). Add an internal class derived from Value that provides std::string management to preserve backward compatibility. Overload DBImpl::Get() to accept a Value*, and to preserve backward compatibility also keep the original version taking a std::string*. diff --git a/db/db_impl.cc b/db/db_impl.cc index ae7b96d..5c3a05c 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -85,6 +85,22 @@ struct DBImpl::CompactionState { uint64_t total_bytes; }; +Value::~Value() {} + +class StringValue : public Value { + public: + explicit StringValue(std::string& val) : value_(val) {} + ~StringValue() {} + + StringValue& assign(const char* data, size_t size) { + value_.assign(data, size); + return *this; + } + + private: + std::string& value_; +}; + // Fix user-supplied options to be reasonable template static void ClipToRange(T* ptr, V minvalue, V maxvalue) { @@ -1117,6 +1133,13 @@ int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes() { Status DBImpl::Get(const ReadOptions& options, const Slice& key, std::string* value) { + StringValue stringvalue(*value); + return DBImpl::Get(options, key, &stringvalue); +} + +Status DBImpl::Get(const ReadOptions& options, + const Slice& key, + Value* value) { Status s; MutexLock l(&mutex_); SequenceNumber snapshot; diff --git a/db/db_impl.h b/db/db_impl.h index d955c2a..3127110 100644 --- a/db/db_impl.h +++ b/db/db_impl.h @@ -42,6 +42,9 @@ class DBImpl : public DB { Status Write(const WriteOptions& options, WriteBatch* updates) override; Status Get(const ReadOptions& options, const Slice& key, std::string* value) override; + virtual Status Get(const ReadOptions& options, + const Slice& key, + Value* value); Iterator* NewIterator(const ReadOptions&) override; const Snapshot* GetSnapshot() override; void ReleaseSnapshot(const Snapshot* snapshot) override; diff --git a/db/db_test.cc b/db/db_test.cc index 2e65370..db778d9 100644 --- a/db/db_test.cc +++ b/db/db_test.cc @@ -2065,6 +2065,11 @@ class ModelDB : public DB { assert(false); // Not implemented return Status::NotFound(key); } + Status Get(const ReadOptions& options, + const Slice& key, Value* value) override { + assert(false); // Not implemented + return Status::NotFound(key); + } Iterator* NewIterator(const ReadOptions& options) override { if (options.snapshot == nullptr) { KVMap* saved = new KVMap; diff --git a/db/memtable.cc b/db/memtable.cc index f42774d..4689e2d 100644 --- a/db/memtable.cc +++ b/db/memtable.cc @@ -98,7 +98,7 @@ void MemTable::Add(SequenceNumber s, ValueType type, const Slice& key, table_.Insert(buf); } -bool MemTable::Get(const LookupKey& key, std::string* value, Status* s) { +bool MemTable::Get(const LookupKey& key, Value* value, Status* s) { Slice memkey = key.memtable_key(); Table::Iterator iter(&table_); iter.Seek(memkey.data()); diff --git a/db/memtable.h b/db/memtable.h index 9d986b1..85c4cce 100644 --- a/db/memtable.h +++ b/db/memtable.h @@ -60,7 +60,7 @@ class MemTable { // If memtable contains a deletion for key, store a NotFound() error // in *status and return true. // Else, return false. - bool Get(const LookupKey& key, std::string* value, Status* s); + bool Get(const LookupKey& key, Value* value, Status* s); private: friend class MemTableIterator; diff --git a/db/version_set.cc b/db/version_set.cc index 1963353..c83a4d2 100644 --- a/db/version_set.cc +++ b/db/version_set.cc @@ -256,7 +256,7 @@ struct Saver { SaverState state; const Comparator* ucmp; Slice user_key; - std::string* value; + Value* value; }; } // namespace static void SaveValue(void* arg, const Slice& ikey, const Slice& v) { @@ -322,7 +322,7 @@ void Version::ForEachOverlapping(Slice user_key, Slice internal_key, void* arg, } Status Version::Get(const ReadOptions& options, const LookupKey& k, - std::string* value, GetStats* stats) { + Value* value, GetStats* stats) { stats->seek_file = nullptr; stats->seek_file_level = -1; diff --git a/db/version_set.h b/db/version_set.h index 69f3d70..0f0a463 100644 --- a/db/version_set.h +++ b/db/version_set.h @@ -72,7 +72,7 @@ class Version { // REQUIRES: This version has been saved (see VersionSet::SaveTo) void AddIterators(const ReadOptions&, std::vector* iters); - Status Get(const ReadOptions&, const LookupKey& key, std::string* val, + Status Get(const ReadOptions&, const LookupKey& key, Value* val, GetStats* stats); // Adds "stats" into the current state. Returns true if a new diff --git a/include/leveldb/db.h b/include/leveldb/db.h index 61c29c0..1a93feb 100644 --- a/include/leveldb/db.h +++ b/include/leveldb/db.h @@ -40,6 +40,17 @@ struct LEVELDB_EXPORT Range { Slice limit; // Not included in the range }; +// Abstract holder for a DB value. +// This allows callers to manage their own value buffers and have +// DB values copied directly into those buffers. +class Value { + public: + virtual Value& assign(const char* data, size_t size) = 0; + + protected: + virtual ~Value(); +}; + // A DB is a persistent ordered map from keys to values. // A DB is safe for concurrent access from multiple threads without // any external synchronization.