Blob Blame Raw
From 58f5e0f3954ba3b4ee858e18f7cac0c8d71b3f8a Mon Sep 17 00:00:00 2001
From: Steve Vinoski <vinoski@ieee.org>
Date: Thu, 20 Dec 2012 16:14:11 -0500
Subject: [PATCH 6/6] 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*.
---
 db/db_impl.cc        | 23 +++++++++++++++++++++++
 db/db_impl.h         |  3 +++
 db/db_test.cc        |  5 +++++
 db/memtable.cc       |  2 +-
 db/memtable.h        |  2 +-
 db/version_set.cc    |  4 ++--
 db/version_set.h     |  2 +-
 include/leveldb/db.h | 13 +++++++++++++
 8 files changed, 49 insertions(+), 5 deletions(-)

diff --git a/db/db_impl.cc b/db/db_impl.cc
index 593a5b5..ca65fd1 100644
--- a/db/db_impl.cc
+++ b/db/db_impl.cc
@@ -81,6 +81,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) {
@@ -1071,6 +1087,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 9f3949c..268f003 100644
--- a/db/db_impl.h
+++ b/db/db_impl.h
@@ -35,6 +35,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 782be38..8c898e1 100644
--- a/db/db_test.cc
+++ b/db/db_test.cc
@@ -1816,6 +1816,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 == NULL) {
       KVMap* saved = new KVMap;
diff --git a/db/memtable.cc b/db/memtable.cc
index bfec0a7..82a875f 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 92e90bb..278f1f3 100644
--- a/db/memtable.h
+++ b/db/memtable.h
@@ -61,7 +61,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 4fd1dde..c5a1ca5 100644
--- a/db/version_set.cc
+++ b/db/version_set.cc
@@ -267,7 +267,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) {
@@ -291,7 +291,7 @@ static bool NewestFirst(FileMetaData* a, FileMetaData* b) {
 
 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 9d084fd..d1e22c8 100644
--- a/db/version_set.h
+++ b/db/version_set.h
@@ -70,7 +70,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 089707c..071f831 100644
--- a/include/leveldb/db.h
+++ b/include/leveldb/db.h
@@ -38,6 +38,17 @@ struct 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.
@@ -82,6 +93,8 @@ class 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
-- 
1.8.3.1