[Pkg-javascript-commits] [node-leveldown] 05/492: buffers, encoding, persistent handles

Andrew Kelley andrewrk-guest at moszumanska.debian.org
Sun Jul 6 17:13:38 UTC 2014


This is an automated email from the git hooks/post-receive script.

andrewrk-guest pushed a commit to annotated tag rocksdb-0.10.1
in repository node-leveldown.

commit 9a1271cd0619008a883fbf50215c7e12ac1ca22f
Author: Rod Vagg <rod at vagg.org>
Date:   Sat Jul 14 22:06:05 2012 +1000

    buffers, encoding, persistent handles
---
 lib/levelup.js      |  77 ++++++++++++++++++++++++++-----
 src/async.cc        |  74 +++++++-----------------------
 src/async.h         | 128 ++++++++++++++++++++++++++++++++++++++++++++--------
 src/batch.cc        |  12 +++--
 src/batch.h         |  25 ++++++++--
 src/database.cc     | 109 ++++++++++++++++++++++++--------------------
 src/database.h      |  12 ++---
 src/levelup.cc      |   3 ++
 src/levelup.h       |   1 +
 test/common.js      |  51 ++++++++++++++++++++-
 test/simple-test.js | 122 +++++++++++++++++++++++++++++++++++++++----------
 11 files changed, 439 insertions(+), 175 deletions(-)

diff --git a/lib/levelup.js b/lib/levelup.js
index 9c2bfdf..65b13b7 100644
--- a/lib/levelup.js
+++ b/lib/levelup.js
@@ -4,12 +4,47 @@ var bridge              = require('../build/Release/levelup')
   , defaultOptions = {
         createIfMissing : false
       , errorIfExists   : false
+      , encoding        : 'utf8'
     }
 
   , toString = function () {
       return "LevelUPDatabase"
     }
 
+  , encodingOpts = (function (enc) {
+      var eo = {}
+      enc.forEach(function (e) { eo[e] = { encoding: e } })
+      return eo
+    }('hex utf8 utf-8 ascii binary base64 ucs2 ucs-2 utf16le utf-16le'.split(' ')))
+
+  , getOptions = function (options_, defaultEncoding) {
+      var options
+      if (typeof options_ == 'string')
+        options = encodingOpts[options_] || encodingOpts[defaultOptions.encoding]
+      else if (typeof options_ == 'object') {
+        options = options_
+        if (!options.encoding
+            || !(options.keyEncoding
+                  && options.valueEncoding
+                  && encodingOpts[options.keyEncoding]
+                  && encodingOpts[options.valueEncoding])
+            || !encodingOpts[options.encoding])
+          options.encoding = defaultOptions.encoding
+      } else
+        options = encodingOpts[defaultEncoding] || encodingOpts[defaultOptions.encoding]
+      return options
+    }
+
+  , getCallback = function (options_, callback_) {
+      if (typeof options_ == 'function')
+        return options_
+      return callback_
+    }
+
+  , toBuffer = function (data, encoding) {
+      return Buffer.isBuffer(data) ? data : new Buffer('' + data, encoding)
+    }
+
   , Database = {
         open: function (callback) {
           var options = {}
@@ -49,61 +84,79 @@ var bridge              = require('../build/Release/levelup')
           return !!this.db
         }
 
-      , get: function (key, callback) {
+      , get: function (key, options_, callback_) {
+          var callback = getCallback(options_, callback_)
+            , options
           if (this.isOpen()) {
-            this.db.get(key, function (err, value) {
+            options  = getOptions(options_, this.encoding)
+            key      = toBuffer(key,   options.keyEncoding   || options.encoding)
+            this.db.get(key, options, function (err, value) {
               if (err) {
                 err = new errors.NotFoundError('Key not found in database [' + key + ']')
                 if (callback)
                   return callback(err)
                 throw err
               }
-              callback && callback(null, value)
+              callback && callback(null, value.toString(options.valueEncoding || options.encoding), key)
             })
           } else
             callback(new errors.ReadError('Database has not been opened'))
         }
 
-      , put: function (key, value, callback) {
+      , put: function (key, value, options_, callback_) {
+          var callback = getCallback(options_, callback_)
+            , options
           if (this.isOpen()) {
-            this.db.put(key, value, function (err) {
+            options  = getOptions(options_, this.encoding)
+            key      = toBuffer(key,   options.keyEncoding   || options.encoding)
+            value    = toBuffer(value, options.valueEncoding || options.encoding)
+            this.db.put(key, value, options, function (err) {
               if (err) {
                 err = new errors.WriteError(err)
                 if (callback)
                   return callback(err)
                 throw err
               }
-              callback && callback()
+              callback && callback(null, key, value)
             })
           } else
             callback(new errors.ReadError('Database has not been opened'))
         }
 
-      , del: function (key, callback) {
+      , del: function (key, options_, callback_) {
+          var callback = getCallback(options_, callback_)
+            , options
           if (this.isOpen()) {
-            this.db.del(key, function (err) {
+            options  = getOptions(options_, this.encoding)
+            key      = toBuffer(key,   options.keyEncoding   || options.encoding)
+            this.db.del(key, options, function (err) {
               if (err) {
                 err = new errors.WriteError(err)
                 if (callback)
                   return callback(err)
                 throw err
               }
-              callback && callback()
+              callback && callback(null, key)
             })
           } else
             callback(new errors.ReadError('Database has not been opened'))
         }
 
-      , batch: function (arr, callback) {
+      , batch: function (arr, options_, callback_) {
+          var callback = getCallback(options_, callback_)
+            , options
           if (this.isOpen()) {
-            this.db.batch(arr, function (err) {
+            options = getOptions(options_, this.encoding)
+            this.db.batch(arr, options, function (err) {
               if (err) {
                 err = new errors.WriteError(err)
                 if (callback)
                   return callback(err)
                 throw err
               }
-              callback && callback()
+              // reference to 'arr' important to keep from being garbage collected
+              // we don't keep a Persistent<T> reference in the bridge
+              callback && callback(null, arr)
             })
           } else
             callback(new errors.ReadError('Database has not been opened'))
diff --git a/src/async.cc b/src/async.cc
index c50c5e5..6f36f2d 100644
--- a/src/async.cc
+++ b/src/async.cc
@@ -1,5 +1,6 @@
 #include <cstdlib>
 #include <node.h>
+#include <node_buffer.h>
 #include <iostream>
 #include <pthread.h>
 
@@ -40,32 +41,12 @@ void AsyncWorker::HandleErrorCallback () {
 
 /** OPEN WORKER **/
 
-OpenWorker::OpenWorker (Database* database, Persistent<Function> callback, string location, bool createIfMissing, bool errorIfExists) {
-  request.data               = this;
-  this->database             = database;
-  this->location             = location;
-  this->callback             = callback;
-  options                    = new Options();
-  options->create_if_missing = createIfMissing;
-  options->error_if_exists   = errorIfExists;
-}
-
-OpenWorker::~OpenWorker () {
-  delete options;
-}
-
 void OpenWorker::Execute() {
   status = database->OpenDatabase(options, location);
 }
 
 /** CLOSE WORKER **/
 
-CloseWorker::CloseWorker (Database* database, Persistent<Function> callback) {
-  request.data               = this;
-  this->database             = database;
-  this->callback             = callback;
-}
-
 void CloseWorker::Execute() {
   database->CloseDatabase();
 }
@@ -76,63 +57,48 @@ void CloseWorker::WorkComplete () {
   callback.Dispose();
 }
 
-/** WRITE WORKER **/
+/** IO WORKER (abstract) **/
 
-WriteWorker::WriteWorker (Database* database, Persistent<Function> callback, string key, string value, bool sync) {
-  request.data   = this;
-  this->database = database;
-  this->callback = callback;
-  this->key      = key;
-  this->value    = value;
-  options        = new WriteOptions();
-  options->sync  = sync;
+void IOWorker::WorkComplete() {
+  AsyncWorker::WorkComplete();
+  keyPtr.Dispose();
 }
 
+/** WRITE WORKER **/
+
 WriteWorker::~WriteWorker () {
   delete options;
 }
 
 void WriteWorker::Execute() {
-  status = database->WriteToDatabase(options, key, value);
+  status = database->PutToDatabase(options, key, value);
 }
 
-/** READ WORKER **/
-
-ReadWorker::ReadWorker (Database* database, Persistent<Function> callback, string key) {
-  request.data   = this;
-  this->database = database;
-  this->callback = callback;
-  this->key      = key;
-  options        = new ReadOptions();
+void WriteWorker::WorkComplete() {
+  IOWorker::WorkComplete();
+  valuePtr.Dispose();
 }
 
+/** READ WORKER **/
+
 ReadWorker::~ReadWorker () {
   delete options;
 }
 
 void ReadWorker::Execute () {
-  status = database->ReadFromDatabase(options, key, value);
+  status = database->GetFromDatabase(options, key, value);
 }
 
 void ReadWorker::HandleOKCallback () {
   Local<Value> argv[] = {
       Local<Value>::New(Null())
-    , Local<Value>::New(String::New(value.c_str()))
+    , Local<Value>::New(Buffer::New((char*)value.data(), value.size())->handle_)
   };
   runCallback(callback, argv, 2);
 }
 
 /** DELETE WORKER **/
 
-DeleteWorker::DeleteWorker (Database* database, Persistent<Function> callback, string key, bool sync) {
-  request.data   = this;
-  this->database = database;
-  this->callback = callback;
-  this->key      = key;
-  options        = new WriteOptions();
-  options->sync  = sync;
-}
-
 DeleteWorker::~DeleteWorker () {
   delete options;
 }
@@ -143,15 +109,6 @@ void DeleteWorker::Execute() {
 
 /** BATCH WORKER **/
 
-BatchWorker::BatchWorker  (Database* database, Persistent<Function> callback, vector<BatchOp*> operations, bool sync) {
-  request.data     = this;
-  this->database   = database;
-  this->callback   = callback;
-  this->operations = operations;
-  options          = new WriteOptions();
-  options->sync    = sync;
-}
-
 BatchWorker::~BatchWorker () {
   for (unsigned int i = 0; i < operations.size(); i++)
     delete operations[i];
@@ -170,6 +127,7 @@ void BatchWorker::Execute() {
 
 void runCallback (Persistent<Function> callback, Local<Value> argv[], int length) {
   TryCatch try_catch;
+ 
   callback->Call(Context::GetCurrent()->Global(), length, argv);
   if (try_catch.HasCaught()) {
     FatalException(try_catch);
diff --git a/src/async.h b/src/async.h
index ac81ca1..1537723 100644
--- a/src/async.h
+++ b/src/async.h
@@ -13,11 +13,19 @@ using namespace leveldb;
 
 class AsyncWorker {
 public:
+  AsyncWorker (
+      Database* database
+    , Persistent<Function> callback
+  ) : database(database)
+    , callback(callback) {
+        request.data               = this;
+      };
+
   uv_work_t            request;
   Database*            database;
   Persistent<Function> callback;
   Status               status;
-  void                 WorkComplete ();
+  virtual void         WorkComplete ();
   virtual void         Execute () {};
 
 protected:
@@ -27,8 +35,23 @@ protected:
 
 class OpenWorker  : public AsyncWorker {
 public:
-  OpenWorker  (Database* database, Persistent<Function> callback, string location, bool createIfMissing, bool errorIfExists);
-  ~OpenWorker ();
+  OpenWorker (
+      Database* database
+    , Persistent<Function> callback
+    , string location
+    , bool createIfMissing
+    , bool errorIfExists
+  ) : AsyncWorker(database, callback)
+    , location(location)
+  {
+    options                    = new Options();
+    options->create_if_missing = createIfMissing;
+    options->error_if_exists   = errorIfExists;
+  };
+
+  ~OpenWorker () {
+    delete options;
+  }
 
   string               location;
   Options*             options;
@@ -37,7 +60,11 @@ public:
 
 class CloseWorker : public AsyncWorker {
 public:
-  CloseWorker  (Database* database, Persistent<Function> callback);
+  CloseWorker (
+      Database* database
+    , Persistent<Function> callback
+  ) : AsyncWorker(database, callback)
+  {};
 
   virtual void         Execute ();
 
@@ -47,23 +74,35 @@ private:
 
 class IOWorker    : public AsyncWorker {
 public:
-  string               key;
-  string               value;
-};
+  IOWorker (
+      Database* database
+    , Persistent<Function> callback
+    , Slice key
+    , Persistent<Object> keyPtr
+  ) : AsyncWorker(database, callback)
+    , key(key)
+    , keyPtr(keyPtr)
+  {};
 
-class WriteWorker : public IOWorker {
-public:
-  WriteWorker  (Database* database, Persistent<Function> callback, string key, string value, bool sync);
-  WriteWorker  () {};
-  ~WriteWorker ();
+  virtual void         WorkComplete ();
 
-  WriteOptions*        options;
-  virtual void         Execute ();
+protected:
+  Slice                key;
+  Persistent<Object>   keyPtr;
 };
 
 class ReadWorker : public IOWorker {
 public:
-  ReadWorker  (Database* database, Persistent<Function> callback, string key);
+  ReadWorker(
+      Database* database
+    , Persistent<Function> callback
+    , Slice key
+    , Persistent<Object> keyPtr
+  ) : IOWorker(database, callback, key, keyPtr)
+  {
+    options = new ReadOptions();
+  };
+
   ~ReadWorker ();
 
   ReadOptions*         options;
@@ -71,19 +110,72 @@ public:
 
 protected:
   virtual void         HandleOKCallback ();
+
+private:
+  string               value;
 };
 
-class DeleteWorker : public WriteWorker {
+class DeleteWorker : public IOWorker {
 public:
-  DeleteWorker  (Database* database, Persistent<Function> callback, string key, bool sync);
+  DeleteWorker(
+      Database* database
+    , Persistent<Function> callback
+    , Slice key
+    , bool sync
+    , Persistent<Object> keyPtr
+  ) : IOWorker(database, callback, key, keyPtr)
+  {
+    options        = new WriteOptions();
+    options->sync  = sync;
+  };
+
   ~DeleteWorker ();
 
   virtual void         Execute ();
+
+protected:
+  WriteOptions*        options;
+};
+
+class WriteWorker : public DeleteWorker {
+public:
+  WriteWorker (
+      Database* database
+    , Persistent<Function> callback
+    , Slice key
+    , Slice value
+    , bool sync
+    , Persistent<Object> keyPtr
+    , Persistent<Object> valuePtr
+  ) : DeleteWorker(database, callback, key, sync, keyPtr)
+    , value(value)
+    , valuePtr(valuePtr)
+  {};
+
+  ~WriteWorker ();
+
+  virtual void         Execute ();
+  virtual void         WorkComplete ();
+
+private:
+  Slice                value;
+  Persistent<Object>   valuePtr;
 };
 
 class BatchWorker : public AsyncWorker {
 public:
-  BatchWorker  (Database* database, Persistent<Function> callback, vector<BatchOp*> operations, bool sync);
+  BatchWorker(
+      Database* database
+    , Persistent<Function> callback
+    , vector<BatchOp*> operations
+    , bool sync
+  ) : AsyncWorker(database, callback)
+    , operations(operations)
+  {
+    options        = new WriteOptions();
+    options->sync  = sync;
+  };
+
   ~BatchWorker ();
 
   virtual void         Execute ();
diff --git a/src/batch.cc b/src/batch.cc
index 8ed7707..ef2309f 100644
--- a/src/batch.cc
+++ b/src/batch.cc
@@ -7,16 +7,20 @@
 
 using namespace std;
 
-BatchDelete::BatchDelete (string key) {
-  this->key   = key;
+BatchDelete::~BatchDelete () {
+  if (keyPtr != NULL)
+    delete keyPtr;
 }
 
 void BatchDelete::Execute (WriteBatch* batch) {
   batch->Delete(key);
 }
 
-BatchWrite::BatchWrite (string key, string value) : BatchDelete (key) {
-  this->value = value;
+BatchWrite::~BatchWrite () {
+  if (keyPtr != NULL)
+    delete keyPtr;
+  if (valuePtr != NULL)
+    delete valuePtr;
 }
 
 void BatchWrite::Execute (WriteBatch* batch) {
diff --git a/src/batch.h b/src/batch.h
index 0c0b2c4..eb1cd81 100644
--- a/src/batch.h
+++ b/src/batch.h
@@ -14,20 +14,37 @@ public:
 
 class BatchDelete : public BatchOp {
 public:
-  BatchDelete (string key);
+  BatchDelete (
+      Slice key
+    , string* keyPtr
+  ) : key(key)
+    , keyPtr(keyPtr)
+  {};
+  ~BatchDelete ();
   virtual void Execute (WriteBatch* batch);
 
 protected:
-  string key;
+  Slice key;
+  string* keyPtr;
 };
 
 class BatchWrite : public BatchDelete {
 public:
-  BatchWrite (string key, string value);
+  BatchWrite (
+      Slice key
+    , string* keyPtr
+    , Slice value
+    , string* valuePtr
+  ) : BatchDelete(key, keyPtr)
+    , value(value)
+    , valuePtr(valuePtr)
+  {};
+  ~BatchWrite ();
   virtual void Execute (WriteBatch* batch);
 
 private:
-  string value;
+  Slice value;
+  string* valuePtr;
 };
 
 #endif
\ No newline at end of file
diff --git a/src/database.cc b/src/database.cc
index 267924f..8ba9571 100644
--- a/src/database.cc
+++ b/src/database.cc
@@ -1,6 +1,7 @@
 #include <cstdlib>
 #include <vector>
 #include <node.h>
+#include <node_buffer.h>
 #include <iostream>
 #include <pthread.h>
 
@@ -40,15 +41,15 @@ Status Database::OpenDatabase (Options* options, string location) {
   return DB::Open(*options, location, &db);
 }
 
-Status Database::WriteToDatabase (WriteOptions* options, string key, string value) {
+Status Database::PutToDatabase (WriteOptions* options, Slice key, Slice value) {
   return db->Put(*options, key, value);
 }
 
-Status Database::ReadFromDatabase (ReadOptions* options, string key, string& value) {
+Status Database::GetFromDatabase (ReadOptions* options, Slice key, string& value) {
   return db->Get(*options, key, &value);
 }
 
-Status Database::DeleteFromDatabase (WriteOptions* options, string key) {
+Status Database::DeleteFromDatabase (WriteOptions* options, Slice key) {
   return db->Delete(*options, key);
 }
 
@@ -69,8 +70,8 @@ void Database::Init () {
   tpl->InstanceTemplate()->SetInternalFieldCount(5);
   tpl->PrototypeTemplate()->Set(String::NewSymbol("open")  , FunctionTemplate::New(Open)->GetFunction());
   tpl->PrototypeTemplate()->Set(String::NewSymbol("close") , FunctionTemplate::New(Close)->GetFunction());
-  tpl->PrototypeTemplate()->Set(String::NewSymbol("put")   , FunctionTemplate::New(Write)->GetFunction());
-  tpl->PrototypeTemplate()->Set(String::NewSymbol("get")   , FunctionTemplate::New(Read)->GetFunction());
+  tpl->PrototypeTemplate()->Set(String::NewSymbol("put")   , FunctionTemplate::New(Put)->GetFunction());
+  tpl->PrototypeTemplate()->Set(String::NewSymbol("get")   , FunctionTemplate::New(Get)->GetFunction());
   tpl->PrototypeTemplate()->Set(String::NewSymbol("del")   , FunctionTemplate::New(Delete)->GetFunction());
   tpl->PrototypeTemplate()->Set(String::NewSymbol("batch") , FunctionTemplate::New(Batch)->GetFunction());
   constructor = Persistent<Function>::New(tpl->GetFunction());
@@ -100,9 +101,7 @@ Handle<Value> Database::Open (const Arguments& args) {
   Database* database = ObjectWrap::Unwrap<Database>(args.This());
   String::Utf8Value location(args[0]->ToString());
   Local<Object> optionsObj = Local<Object>::Cast(args[1]);
-  Persistent<Function> callback;
-  if (args.Length() > 1)
-    callback = Persistent<Function>::New(Local<Function>::Cast(args[2]));
+  Persistent<Function> callback = Persistent<Function>::New(Local<Function>::Cast(args[2]));
 
   OpenWorker* worker = new OpenWorker(
       database
@@ -130,46 +129,48 @@ Handle<Value> Database::Close (const Arguments& args) {
   return Undefined();
 }
 
-Handle<Value> Database::Write (const Arguments& args) {
+Handle<Value> Database::Put (const Arguments& args) {
   HandleScope scope;
 
   Database* database = ObjectWrap::Unwrap<Database>(args.This());
-  String::Utf8Value key(args[0]->ToString());
-  String::Utf8Value value(args[1]->ToString());
-  Persistent<Function> callback;
-  if (args.Length() > 2)
-    callback = Persistent<Function>::New(Local<Function>::Cast(args[args.Length() > 3 ? 3 : 2]));
-  bool sync = false;
-  if (args.Length() > 3) {
-    Local<Object> optionsObj = Local<Object>::Cast(args[2]);
-    sync = optionsObj->Has(option_sync) && optionsObj->Get(option_sync)->BooleanValue();
-  }
+
+  Local<Object> keyBuffer = args[0]->ToObject();
+  Slice key(Buffer::Data(keyBuffer), Buffer::Length(keyBuffer));
+  Local<Object> valueBuffer = args[1]->ToObject();
+  Slice value(Buffer::Data(valueBuffer), Buffer::Length(valueBuffer));
+
+  Local<Object> optionsObj = Local<Object>::Cast(args[2]);
+  bool sync = optionsObj->Has(option_sync) && optionsObj->Get(option_sync)->BooleanValue();
+  Persistent<Function> callback = Persistent<Function>::New(Local<Function>::Cast(args[3]));
 
   WriteWorker* worker  = new WriteWorker(
       database
     , callback
-    , *key
-    , *value
+    , key
+    , value
     , sync
+    , Persistent<Object>::New(keyBuffer)
+    , Persistent<Object>::New(valueBuffer)
   );
   AsyncQueueWorker(worker);
 
   return Undefined();
 }
 
-Handle<Value> Database::Read (const Arguments& args) {
+Handle<Value> Database::Get (const Arguments& args) {
   HandleScope scope;
 
   Database* database = ObjectWrap::Unwrap<Database>(args.This());
-  String::Utf8Value key(args[0]->ToString());
-  Persistent<Function> callback;
-  if (args.Length() > 1)
-    callback = Persistent<Function>::New(Local<Function>::Cast(args[args.Length() > 2 ? 2 : 1]));
+  Local<Object> keyBuffer = args[0]->ToObject();
+  Slice key(Buffer::Data(keyBuffer), Buffer::Length(keyBuffer));
+  //Local<Object> optionsObj = Local<Object>::Cast(args[1]);
+  Persistent<Function> callback = Persistent<Function>::New(Local<Function>::Cast(args[2]));
 
   ReadWorker* worker = new ReadWorker(
       database
     , callback
-    , *key
+    , key
+    , Persistent<Object>::New(keyBuffer)
   );
   AsyncQueueWorker(worker);
 
@@ -180,21 +181,18 @@ Handle<Value> Database::Delete (const Arguments& args) {
   HandleScope scope;
 
   Database* database = ObjectWrap::Unwrap<Database>(args.This());
-  String::Utf8Value key(args[0]->ToString());
-  Persistent<Function> callback;
-  if (args.Length() > 1)
-    callback = Persistent<Function>::New(Local<Function>::Cast(args[args.Length() > 2 ? 2 : 1]));
-  bool sync = false;
-  if (args.Length() > 2) {
-    Local<Object> optionsObj = Local<Object>::Cast(args[1]);
-    sync = optionsObj->Has(option_sync) && optionsObj->Get(option_sync)->BooleanValue();
-  }
+  Local<Object> keyBuffer = args[0]->ToObject();
+  Slice key(Buffer::Data(keyBuffer), Buffer::Length(keyBuffer));
+  Local<Object> optionsObj = Local<Object>::Cast(args[1]);
+  bool sync = optionsObj->Has(option_sync) && optionsObj->Get(option_sync)->BooleanValue();
+  Persistent<Function> callback = Persistent<Function>::New(Local<Function>::Cast(args[2]));
 
   DeleteWorker* worker = new DeleteWorker(
       database
     , callback
-    , *key
+    , key
     , sync
+    , Persistent<Object>::New(keyBuffer)
   );
   AsyncQueueWorker(worker);
 
@@ -206,27 +204,40 @@ Handle<Value> Database::Batch (const Arguments& args) {
 
   Database* database = ObjectWrap::Unwrap<Database>(args.This());
   Local<Array> array = Local<Array>::Cast(args[0]);
-  Persistent<Function> callback;
-  if (args.Length() > 1)
-    callback = Persistent<Function>::New(Local<Function>::Cast(args[args.Length() > 2 ? 2 : 1]));
-  bool sync = false;
-  if (args.Length() > 2) {
-    Local<Object> optionsObj = Local<Object>::Cast(args[1]);
-    sync = optionsObj->Has(option_sync) && optionsObj->Get(option_sync)->BooleanValue();
-  }
+  Local<Object> optionsObj = Local<Object>::Cast(args[1]);
+  bool sync = optionsObj->Has(option_sync) && optionsObj->Get(option_sync)->BooleanValue();
+  Persistent<Function> callback = Persistent<Function>::New(Local<Function>::Cast(args[2]));
 
   vector<BatchOp*> operations;
   for (unsigned int i = 0; i < array->Length(); i++) {
     Local<Object> obj = Local<Object>::Cast(array->Get(i));
     if (!obj->Has(str_type) || !obj->Has(str_key))
       continue;
-    String::Utf8Value key(obj->Get(str_key)->ToString());
+
+    Local<Object> keyObj = obj->Get(str_key)->ToObject();
+    string* keyStr = NULL;
+    Slice key;
+    if (Buffer::HasInstance(keyObj))
+      key = Slice(Buffer::Data(keyObj), Buffer::Length(keyObj));
+    else {
+      keyStr = new string(ToCString(String::Utf8Value(keyObj->ToString())));
+      key = *keyStr;
+    }
+
     if (obj->Get(str_type)->StrictEquals(str_del)) {
-      operations.push_back(new BatchDelete(*key));
+      operations.push_back(new BatchDelete(key, keyStr));
     } else if (obj->Get(str_type)->StrictEquals(str_put)) {
       if (obj->Has(str_value)) {
-        String::Utf8Value value(obj->Get(str_value)->ToString());
-        operations.push_back(new BatchWrite(*key, *value));
+        Local<Object> valueObj = obj->Get(str_value)->ToObject();
+        string* valueStr = NULL;
+        Slice value;
+        if (Buffer::HasInstance(valueObj))
+          value = Slice(Buffer::Data(valueObj), Buffer::Length(valueObj));
+        else {
+          valueStr = new string(ToCString(String::Utf8Value(valueObj->ToString())));
+          value = *valueStr;
+        }
+        operations.push_back(new BatchWrite(key, keyStr, value, valueStr));
       }
     }
   }
diff --git a/src/database.h b/src/database.h
index 7438b9f..4a3f8a6 100644
--- a/src/database.h
+++ b/src/database.h
@@ -23,11 +23,11 @@ class Database : public node::ObjectWrap {
   static v8::Handle<v8::Value> NewInstance (const v8::Arguments& args);
 
   Status OpenDatabase         (Options* options, string location);
-  Status WriteToDatabase      (WriteOptions* options, string key, string value);
-  Status ReadFromDatabase     (ReadOptions* options, string key, string& value);
-  Status DeleteFromDatabase   (WriteOptions* options, string key);
+  Status PutToDatabase        (WriteOptions* options, Slice key, Slice value);
+  Status GetFromDatabase      (ReadOptions* options, Slice key, string& value);
+  Status DeleteFromDatabase   (WriteOptions* options, Slice key);
   Status WriteBatchToDatabase (WriteOptions* options, WriteBatch* batch);
-  void   CloseDatabase      ();
+  void   CloseDatabase        ();
 
  private:
   Database ();
@@ -40,9 +40,9 @@ class Database : public node::ObjectWrap {
   LU_V8_METHOD( New    )
   LU_V8_METHOD( Open   )
   LU_V8_METHOD( Close  )
-  LU_V8_METHOD( Write  )
+  LU_V8_METHOD( Put  )
   LU_V8_METHOD( Delete )
-  LU_V8_METHOD( Read   )
+  LU_V8_METHOD( Get   )
   LU_V8_METHOD( Batch  )
 };
 
diff --git a/src/levelup.cc b/src/levelup.cc
index f3a73b2..d96be0f 100644
--- a/src/levelup.cc
+++ b/src/levelup.cc
@@ -18,4 +18,7 @@ NODE_MODULE(levelup, Init)
 // Extracts a C string from a V8 Utf8Value.
 const char* ToCString(const v8::String::Utf8Value& value) {
   return *value ? *value : "<string conversion failed>";
+}
+const char* ToCString(const v8::String::AsciiValue& value) {
+  return *value ? *value : "<string conversion failed>";
 }
\ No newline at end of file
diff --git a/src/levelup.h b/src/levelup.h
index 16f460b..948957d 100644
--- a/src/levelup.h
+++ b/src/levelup.h
@@ -8,5 +8,6 @@
   static Persistent<String> option_ ## key = Persistent<String>::New(String::New(#key));
 
 const char* ToCString(const v8::String::Utf8Value& value);
+const char* ToCString(const v8::String::AsciiValue& value);
 
 #endif
\ No newline at end of file
diff --git a/test/common.js b/test/common.js
index 0f6e235..f68ec48 100644
--- a/test/common.js
+++ b/test/common.js
@@ -1,4 +1,10 @@
-var ba = require('buster').assertions
+var ba      = require('buster').assertions
+  , async   = require('async')
+  , rimraf  = require('rimraf')
+  , fs      = require('fs')
+  , path    = require('path')
+  , levelup = require('../lib/levelup.js')
+  , child_process = require('child_process')
 
 ba.add('isInstanceOf', {
     assert: function (actual, expected) {
@@ -21,3 +27,46 @@ ba.add('isUndefined', {
   , assertMessage: '${0} expected to be undefined'
   , refuteMessage: '${0} expected not to be undefined'
 })
+
+global.openTestDatabase = function (callback) {
+  var db = levelup.createDatabase(
+          this.cleanupDirs[0] = '/tmp/levelup_test_db'
+        , {
+              createIfMissing: true
+            , errorIfExists: true
+          }
+      )
+  this.closeableDatabases.push(db)
+  db.open(function (err) {
+    refute(err)
+    callback(db)
+  })
+}
+
+global.cleanUp = function (closeableDatabases, cleanupDirs, callback) {
+  async.forEach(
+      closeableDatabases
+    , function (db, callback) {
+        db.close(callback)
+      }
+    , function () {
+        async.forEach(cleanupDirs, rimraf, callback)
+      }.bind(this)
+  )
+}
+
+global.loadBinaryTestData = function (callback) {
+  fs.readFile(path.join(__dirname, 'testdata.bin'), callback)
+}
+
+global.binaryTestDataMD5Sum = '920725ef1a3b32af40ccd0b78f4a62fd'
+
+global.checkBinaryTestData = function (testData, callback) {
+  fs.writeFile('__tst.dat', testData, function (err) {
+    child_process.exec('md5sum __tst.dat', function (error, stdout, stderr) {
+      var md5Sum = stdout.split(' ')[0]
+      assert.equals(md5Sum, global.binaryTestDataMD5Sum)
+      rimraf('__tst.dat', callback)
+    })
+  })
+}
\ No newline at end of file
diff --git a/test/simple-test.js b/test/simple-test.js
index f960e03..6d84994 100644
--- a/test/simple-test.js
+++ b/test/simple-test.js
@@ -1,3 +1,5 @@
+/*global cleanUp:true, openTestDatabase:true*/
+
 var buster  = require('buster')
   , assert  = buster.assert
   , levelup = require('../lib/levelup.js')
@@ -10,32 +12,11 @@ buster.testCase('Basic API', {
     'setUp': function () {
       this.cleanupDirs = []
       this.closeableDatabases = []
-      this.openTestDatabase = function (callback) {
-        var db = levelup.createDatabase(
-                this.cleanupDirs[0] = '/tmp/levelup_test_db'
-              , {
-                    createIfMissing: true
-                  , errorIfExists: true
-                }
-            )
-        this.closeableDatabases.push(db)
-        db.open(function (err) {
-          refute(err)
-          callback(db)
-        })
-      }.bind(this)
+      this.openTestDatabase = openTestDatabase.bind(this)
     }
 
   , 'tearDown': function (done) {
-      async.forEach(
-          this.closeableDatabases
-        , function (db, callback) {
-            db.close(callback)
-          }
-        , function () {
-            async.forEach(this.cleanupDirs, rimraf, done)
-          }.bind(this)
-      )
+      cleanUp(this.closeableDatabases, this.cleanupDirs, done)
     }
 
   , 'createDatabase()': function () {
@@ -354,5 +335,100 @@ buster.testCase('Basic API', {
             )
           })
         }
+
+      , 'batch() with can manipulate data from put()': function (done) {
+          // checks encoding and whatnot
+          this.openTestDatabase(function (db) {
+            async.series(
+                [
+                    db.put.bind(db, '1', 'one')
+                  , db.put.bind(db, '2', 'two')
+                  , db.put.bind(db, '3', 'three')
+                  , function (callback) {
+                      db.batch(
+                          [
+                              { type: 'put', key: 'foo', value: 'afoovalue' }
+                            , { type: 'del', key: '1' }
+                            , { type: 'put', key: 'bar', value: 'abarvalue' }
+                            , { type: 'del', key: 'foo' }
+                            , { type: 'put', key: 'baz', value: 'abazvalue' }
+                          ]
+                        , callback
+                      )
+                    }
+                  , function (callback) {
+                      // these should exist
+                      async.forEach(
+                          ['2', '3', 'bar', 'baz']
+                        , function (key, callback) {
+                            db.get(key, function (err, value) {
+                              refute(err)
+                              callback()
+                            })
+                          }
+                        , callback
+                      )
+                    }
+                  , function (callback) {
+                      // these shouldn't exist
+                      async.forEach(
+                          ['1', 'foo']
+                        , function (key, callback) {
+                            db.get(key, function (err, value) {
+                              assert(err)
+                              assert.isInstanceOf(err, errors.NotFoundError)
+                              callback()
+                            })
+                          }
+                        , callback
+                      )
+                    }
+                ]
+              , done
+            )
+          })
+        }
+
+      , 'batch() data can be read with get() and del()': function (done) {
+          this.openTestDatabase(function (db) {
+            async.series(
+                [
+                    function (callback) {
+                      db.batch(
+                          [
+                              { type: 'put', key: '1', value: 'one' }
+                            , { type: 'put', key: '2', value: 'two' }
+                            , { type: 'put', key: '3', value: 'three' }
+                          ]
+                        , callback
+                      )
+                    }
+                  , db.del.bind(db, '1', 'one')
+                  , function (callback) {
+                      // these should exist
+                      async.forEach(
+                          ['2', '3']
+                        , function (key, callback) {
+                            db.get(key, function (err, value) {
+                              refute(err)
+                              callback()
+                            })
+                          }
+                        , callback
+                      )
+                    }
+                  , function (callback) {
+                      // this shouldn't exist
+                      db.get('1', function (err, value) {
+                        assert(err)
+                        assert.isInstanceOf(err, errors.NotFoundError)
+                        callback()
+                      })
+                    }
+                ]
+              , done
+            )
+          })
+        }
     }
 })
\ No newline at end of file

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-leveldown.git



More information about the Pkg-javascript-commits mailing list