From: Steve Vinoski <vinoski@ieee.org>
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 a3358d1..6f4ffe7 100644
--- a/db/db_impl.cc
+++ b/db/db_impl.cc
@@ -84,6 +84,22 @@ struct DBImpl::CompactionState {
}
};
+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 <class T, class V>
static void ClipToRange(T* ptr, V minvalue, V maxvalue) {
@@ -1121,6 +1137,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 07df200..eb04c83 100644
--- a/db/db_impl.h
+++ b/db/db_impl.h
@@ -38,6 +38,9 @@ class DBImpl : public DB {
virtual Status Get(const ReadOptions& options,
const Slice& key,
std::string* value);
+ virtual Status Get(const ReadOptions& options,
+ const Slice& key,
+ Value* value);
virtual Iterator* NewIterator(const ReadOptions&);
virtual const Snapshot* GetSnapshot();
virtual void ReleaseSnapshot(const Snapshot* snapshot);
diff --git a/db/db_test.cc b/db/db_test.cc
index 8730e4f..b5b6410 100644
--- a/db/db_test.cc
+++ b/db/db_test.cc
@@ -2057,6 +2057,11 @@ class ModelDB: public DB {
assert(false); // Not implemented
return Status::NotFound(key);
}
+ virtual Status Get(const ReadOptions& options,
+ const Slice& key, Value* value) {
+ assert(false); // Not implemented
+ return Status::NotFound(key);
+ }
virtual Iterator* NewIterator(const ReadOptions& options) {
if (options.snapshot == nullptr) {
KVMap* saved = new KVMap;
diff --git a/db/memtable.cc b/db/memtable.cc
index 287afdb..efd3d3d 100644
--- a/db/memtable.cc
+++ b/db/memtable.cc
@@ -105,7 +105,7 @@ void MemTable::Add(SequenceNumber s, ValueType type,
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 f2a6736..8531421 100644
--- a/db/memtable.h
+++ b/db/memtable.h
@@ -57,7 +57,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:
~MemTable(); // Private since only Unref() should be used to delete it
diff --git a/db/version_set.cc b/db/version_set.cc
index 156a007..1d1e316 100644
--- a/db/version_set.cc
+++ b/db/version_set.cc
@@ -262,7 +262,7 @@ struct Saver {
SaverState state;
const Comparator* ucmp;
Slice user_key;
- std::string* value;
+ Value* value;
};
}
static void SaveValue(void* arg, const Slice& ikey, const Slice& v) {
@@ -331,7 +331,7 @@ void Version::ForEachOverlapping(Slice user_key, Slice internal_key,
Status Version::Get(const ReadOptions& options,
const LookupKey& k,
- std::string* value,
+ Value* value,
GetStats* stats) {
Slice ikey = k.internal_key();
Slice user_key = k.user_key();
diff --git a/db/version_set.h b/db/version_set.h
index 77b9895..60d0cb7 100644
--- a/db/version_set.h
+++ b/db/version_set.h
@@ -69,7 +69,7 @@ class Version {
FileMetaData* seek_file;
int seek_file_level;
};
- 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 78abfb0..c18858c 100644
--- a/include/leveldb/db.h
+++ b/include/leveldb/db.h
@@ -39,6 +39,17 @@ struct LEVELDB_EXPORT Range {
Range(const Slice& s, const Slice& l) : start(s), limit(l) { }
};
+// 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.
@@ -87,6 +98,8 @@ class LEVELDB_EXPORT DB {
// May return some other Status on an error.
virtual Status Get(const ReadOptions& options,
const Slice& key, std::string* value) = 0;
+ virtual Status Get(const ReadOptions& options,
+ const Slice& key, Value* value) = 0;
// Return a heap-allocated iterator over the contents of the database.
// The result of NewIterator() is initially invalid (caller must