Skip to content
Browse files

Merge pull request #11 from my8bird/master

Merged in Iterator code
  • Loading branch information...
2 parents ef31944 + 83bc249 commit cc047d48e82c9837b713768012da6255f61fd07c @creationix committed
Showing with 1,025 additions and 239 deletions.
  1. +4 −0 README.markdown
  2. +47 −0 demo/async.js
  3. +46 −0 demo/dumpdata.js
  4. +83 −0 demo/iterator.coffee
  5. +52 −0 demo/million-async.js
  6. +0 −104 leveldb.js
  7. +6 −0 lib/index.js
  8. +27 −0 package.json
  9. +406 −82 src/DB.cc
  10. +68 −9 src/DB.h
  11. +167 −0 src/Iterator.cc
  12. +53 −1 src/Iterator.h
  13. +17 −21 src/WriteBatch.cc
  14. +8 −6 src/WriteBatch.h
  15. +31 −10 src/helpers.cc
  16. +10 −6 src/helpers.h
View
4 README.markdown
@@ -8,6 +8,10 @@ While implementing nStore, I realized there are a couple things that V8 and node
Since LevelDB provides good primitives like MVCC and binary support (It was designed to back IndexDB in the Chrome browser), then it can be used as a base to implement things like CouchDB.
+## TODO
+
+ 1. Trun Iterator code to be async
+
## Status
This project is still under heavy development in my free-time. It was started by a long night where I couldn't sleep. I am not a C++ coder and am learning as I go.
View
47 demo/async.js
@@ -0,0 +1,47 @@
+var leveldb = require('../build/default/leveldb.node'),
+ DB = leveldb.DB,
+ Iterator = leveldb.Iterator,
+ WriteBatch = leveldb.WriteBatch;
+
+var path = __dirname + "/testdb";
+
+var db = new DB();
+
+// Opening
+console.log("Opening...");
+db.open(path, {create_if_missing: true, paranoid_checks: true}, function(err) {
+ if (err) throw err;
+ console.log("ok");
+
+ // Putting
+ console.log("\nPutting...");
+ var key = "Hello";
+ var value = "World";
+ db.put(key, value, function(err) {
+ if (err) throw err;
+ console.log("ok");
+
+ // Getting
+ console.log("\nGetting...");
+ db.get(key, function(err, val) {
+ if (err) throw err;
+ if (val != value) throw "Expected: " + value + ", got: " + val;
+ console.log("ok");
+
+ // Deleting
+ console.log("\nDeleting...");
+ db.del(key, function(err) {
+ if (err) throw err;
+ console.log("ok");
+
+ // Closing
+ console.log("\nClosing...")
+ db.close(function(err) {
+ if (err) throw err;
+ console.log('ok');
+ });
+ });
+ });
+ });
+});
+
View
46 demo/dumpdata.js
@@ -0,0 +1,46 @@
+var leveldb = require('../build/default/leveldb.node'),
+ DB = leveldb.DB,
+ Iterator = leveldb.Iterator;
+
+var path = "/tmp/large.db";
+
+var index = 0;
+var in_flight = 0;
+var found = 0;
+var not_found = 0;
+
+var db = new DB();
+db.open(path, function(err) {
+ if (err) throw err;
+
+ setInterval(function() {
+ console.log('Index: ' + index + ' In flight: ' + in_flight + ' Found: ' + found + ' Not found: ' + not_found);
+ refresh();
+ }, 100);
+
+ refresh();
+});
+
+function refresh() {
+ while (in_flight < 10000) {
+ var key = "row" + index;
+
+ db.get(key, (function(index) { return function(err, value) {
+ in_flight -= 1;
+ if (value) {
+ var obj = JSON.parse(value);
+ if (obj.index != index) {
+ console.log('key: ' + key + ' = ' + value);
+ }
+ found += 1;
+ }
+ else {
+ not_found += 1;
+ }
+ }})(index));
+
+ in_flight += 1;
+ index += 1;
+ }
+}
+
View
83 demo/iterator.coffee
@@ -0,0 +1,83 @@
+DB = require('../build/default/leveldb.node').DB
+uuid = require('node-uuid')
+td = require("twisted-deferred")
+
+start = Date.now()
+
+entryCount = 1000
+readCount = 0
+
+defer = td.toDeferred
+DeferredList = td.DeferredList
+
+console.log("Creating test database")
+path = "/tmp/iterator.db"
+db = new DB()
+
+d = defer(db.open.bind(db), path, {create_if_missing: true})
+d.addCallback () ->
+ console.log('!! creating ' + entryCount + ' random key entries')
+ deferreds = []
+ for i in [0 .. entryCount]
+ id = uuid()
+
+ put_d = defer db.put.bind(db), id, JSON.stringify({id: id, name: 'Bob', age: 33})
+ deferreds.push(put_d)
+
+ return DeferredList(deferreds)
+
+d.addCallback () ->
+ console.log('created in ' + (Date.now() - start) + 'ms')
+ console.log('!! iterating db in key order')
+
+ # reset the start counter
+ start = Date.now()
+
+ # iterate over the test database
+ iterator = db.newIterator({})
+ deferred = new td.Deferred()
+
+ iterator.seekToFirst () ->
+ while iterator.valid()
+ key = iterator.key().toString('utf8')
+
+ if lastKey && lastKey > key
+ console.log('found sorting error')
+
+ lastKey = key
+ readCount++
+
+ iterator.next()
+
+ console.log('read sequential ' + readCount + ' db contents in ' + (Date.now() - start) + 'ms')
+ deferred.callback()
+
+ deferred
+
+d.addCallback () ->
+ console.log 'Start Seek test'
+
+ deferred = new td.Deferred()
+ iterator = db.newIterator({})
+ testUUID = uuid()
+ iterator.seek "" + testUUID, () ->
+ console.log('looking for first key after: ' + testUUID)
+ # if we found something the report
+ if (iterator.valid())
+ console.log('FOUND: ' + iterator.key().toString('utf-8'))
+
+ deferred.callback()
+
+ deferred
+
+d.addCallback () ->
+ console.log "Success"
+
+d.addErrback (err) ->
+ console.log err.message.stack
+
+d.addBoth () ->
+ db.close()
+ DB.destroyDB(path, {})
+ console.log "Database removed and cleaned up."
+
View
52 demo/million-async.js
@@ -0,0 +1,52 @@
+var leveldb = require('../build/default/leveldb.node'),
+ DB = leveldb.DB,
+ WriteBatch = leveldb.WriteBatch;
+
+console.log("Creating test database");
+var path = "/tmp/large.db";
+var db = new DB();
+
+var batchSize = 100;
+var totalSize = 1000000;
+
+db.open(path, {create_if_missing: true}, function() {
+
+ console.log("Serializing and inserting 1,000,000 rows...");
+ var start = Date.now();
+
+ function doBatch(i, cb) {
+ if (i % 10000 == 0)
+ console.log("i = " + i);
+
+ var wb = new WriteBatch();
+
+ for (var j = 0; j < batchSize; j += 1) {
+ var key = "row" + i;
+ var value = JSON.stringify({
+ index: i,
+ name: "Tim",
+ age: 28
+ });
+ wb.put(key, value);
+ i += 1;
+ }
+
+ db.write(wb, function() {
+ if (i < totalSize)
+ doBatch(i, cb);
+ else
+ cb();
+ });
+ }
+
+ doBatch(0, function() {
+ var delta = Date.now() - start;
+ console.log("Completed in %d ms", delta);
+ console.log("%s inserts per second", Math.floor(totalSize * 1000 / delta));
+
+ console.log("\nClosing...");
+ db.close(function() {
+ console.log("Done");
+ });
+ });
+});
View
104 leveldb.js
@@ -1,104 +0,0 @@
-// Mock implementation in JS to help guide API
-// This is meant to mirror the C++ API, but be JavaScripty
-
-function LevelDB() {}
-LevelDB.prototype.open = function open(name, options, callback) { /*...*/ };
-LevelDB.prototype.close = function close(callback) { /*...*/ };
-LevelDB.prototype.put = function put(key, value, options, callback) { /*...*/ };
-LevelDB.prototype.del = function del(key, options, callback) { /*...*/ };
-LevelDB.prototype.write = function write(updates, options, callback) { /*...*/ };
-LevelDB.prototype.get = function get(key, options, callback) { /*...*/ };
-LevelDB.prototype.newIterator = function newIterator(options) { /*...*/ };
-LevelDB.prototype.getSnapshot = function getSnapshot() { /*...*/ };
-LevelDB.prototype.releaseSnapshot = function releaseSnapshot(snapshot) { /*...*/ };
-LevelDB.prototype.getProperty = function getProperty(name) { /*...*/ };
-LevelDB.prototype.getApproximateSizes = function releaseSnapshot(ranges) { /*...*/ };
-LevelDB.openDB = function openDB(name, options, callback) { /*...*/ }; // Convience wrapper around constructor and open
-LevelDB.destroyDB = function destroyDB(name, options, callback) { /*...*/ };
-LevelDB.repairDB = function repairDB(name, options, callback) { /*...*/ };
-
-function WriteBatch() {}
-WriteBatch.prototype.put = function put(key, balue) { /*...*/ };
-WriteBatch.prototype.del = function del(key) { /*...*/ };
-WriteBatch.prototype.clear = function clear() { /*...*/ };
-
-function Iterator(options) { /*...*/ }
-Iterator.prototype.isValid = function isValid() { /*...*/ };
-Iterator.prototype.seekToFirst = function seekToFirst(callback) { /*...*/ };
-Iterator.prototype.seekToLast = function seekToLast(callback) { /*...*/ };
-Iterator.prototype.seek = function seek(target, callback) { /*...*/ };
-Iterator.prototype.next = function next(callback) { /*...*/ };
-Iterator.prototype.prev = function prev(callback) { /*...*/ };
-Iterator.prototype.getKey = function getKey(callback) { /*...*/ };
-Iterator.prototype.getValue = function getValue(callback) { /*...*/ };
-Iterator.prototype.getStatus = function getStatus() { /*...*/ };
-Iterator.prototype.registerCleanup = function registerCleanup(callback, arg1, arg2) { /*...*/ };
-
-
-// See options.h (in leveldb) for better descriptions
-
-// "Options" have the following fields
-// comparator - a callback used for sorting keys
-// create_if_missing - boolean
-// error_if_exists - boolean
-// paranoid_checks - boolean
-// env - hook to replace fs interaction
-// info_log - writable stream
-// write_buffer_size - integer bytes
-// max_open_files - integer
-// block_cache - buffer
-// block_size - integer bytes
-// block_restart_interval - integer
-// compression - flag to disable compression
-
-// "ReadOptions" have the follownig fields
-// verify_checksums - boolean
-// fill_cache - boolean
-// snapshot - snapshot object
-
-// "WriteOptions" have the following fields
-// sync - boolean (false is fire and forget)
-// post_write_snapshot - snapshot object
-
-// Sample usage ////////////////////////////////////////////////////////////////
-
-// Create a new LevelDB instance database
-var db = new LevelDB();
-
-// `options` is always optional and defaults are assumed if left out
-
-// Opening a database
-db.open("/tmp/testdb", options, function (err, status) {
- // err will be an exception object if there is a problem
- // otherwise status will be a string message
-});
-
-// Closing a database
-db.close(function (err, status) { /*...*/ });
-
-// Putting a value in the database
-db.put(key, value, options, function (err, status) { /*...*/ });
-
-// Deleting a value from the database
-db.del(key, options, function (err, status) { /*...*/ });
-
-// Write a batch of updates in one call
-db.write(updates, options, function (err, status) { /*...*/ });
-
-// Getting a value from the database
-db.get(key, options, function (err, value, status) { /*...*/ });
-
-// get an iterator
-var iterator = db.newIterator(options);
-
-// get a handle to the current state
-var snapshot = db.getSnapshot();
-// release it when done
-db.releaseSnapshot(snapshot);
-
-var value = db.getProperty(name);
-
-var sized = db.getApproximateSizes(ranges);
-
-LevelDB.destroyDB(name, options, function (err, status) { /*...*/ });
-LevelDB.repairDB(name, options, function (err, status) { /*...*/ });
View
6 lib/index.js
@@ -0,0 +1,6 @@
+lib = require('../build/default/leveldb.node');
+
+for (i in lib) {
+ exports[i] = lib[i];
+}
+
View
27 package.json
@@ -0,0 +1,27 @@
+{
+ "name": "leveldb",
+ "tags": ["database", "cache"],
+ "description": "Bindings for using LevelDB through node.",
+ "homepage": "https://github.com/my8bird/node-leveldb",
+ "version": "0.3.5",
+ "author": {
+ "name": "Nathan Landis",
+ "email": "my8bird@gmail.com"},
+ "main": "lib",
+ "scripts": {
+ "preinstall": "node-waf configure build"
+ },
+ "repository" : {
+ "type" : "git",
+ "url" : "https://github.com/my8bird/node-leveldb.git"
+ },
+ "keywords": ["database", "leveldb"],
+ "bugs" : { "web" : "https://github.com/my8bird/node-leveldb/issues" },
+ "dependencies":
+ {
+ },
+ "contributors": [
+ "Randall Leeds <randall.leeds@gmail.com> (http://claimid.com/randallleeds)",
+ "Damon Oehlman <damon.oehlman@sidelab.com> (http://www.sidelab.com/)"
+ ]
+}
View
488 src/DB.cc
@@ -1,4 +1,5 @@
#include "DB.h"
+#include "Iterator.h"
#include "WriteBatch.h"
#include <node_buffer.h>
@@ -8,14 +9,15 @@ using namespace node_leveldb;
Persistent<FunctionTemplate> DB::persistent_function_template;
-DB::DB() {
- this->db = NULL;
+DB::DB()
+ : db(NULL)
+{
}
DB::~DB() {
- if (this->db) {
- delete this->db;
- this->db = NULL;
+ if (db != NULL) {
+ delete db;
+ db = NULL;
}
}
@@ -52,157 +54,391 @@ void DB::Init(Handle<Object> target) {
Handle<Value> DB::New(const Arguments& args) {
HandleScope scope;
- DB* db = new DB();
- db->Wrap(args.This());
+ DB* self = new DB();
+ self->Wrap(args.This());
return args.This();
}
+
+//
+// Open
+//
+
Handle<Value> DB::Open(const Arguments& args) {
HandleScope scope;
- // Check args
- if (!(args.Length() == 2 && args[0]->IsObject() && args[1]->IsString())) {
- return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected (Object, String)")));
- }
-
// Get this and arguments
DB* self = ObjectWrap::Unwrap<DB>(args.This());
- if (self->db) {
+ // Required filename
+ if (args.Length() < 1 || !args[0]->IsString()) {
+ return ThrowException(Exception::TypeError(String::New("DB.open() expects a filename")));
+ }
+ String::Utf8Value name(args[0]);
+
+ int pos = 1;
+
+ // Optional options
+ leveldb::Options options = leveldb::Options();
+ if (pos < args.Length() && args[pos]->IsObject() && !args[pos]->IsFunction()) {
+ options = JsToOptions(args[pos]);
+ pos++;
+ }
+
+ // Optional callback
+ Local<Function> callback;
+ if (pos < args.Length() && args[pos]->IsFunction()) {
+ callback = Local<Function>::Cast(args[pos]);
+ pos++;
+ }
+
+ // Pass parameters to async function
+ OpenParams *params = new OpenParams(self, *name, options, callback);
+ EIO_BeforeOpen(params);
+
+ return args.This();
+}
+
+void DB::EIO_BeforeOpen(OpenParams *params) {
+ eio_custom(EIO_Open, EIO_PRI_DEFAULT, EIO_AfterOpen, params);
+}
+
+int DB::EIO_Open(eio_req *req) {
+ OpenParams *params = static_cast<OpenParams*>(req->data);
+ DB *self = params->self;
+
+ // Close old DB, if open() is called more than once
+ if (self->db != NULL) {
delete self->db;
self->db = NULL;
}
- leveldb::Options options = JsToOptions(args[0]);
- String::Utf8Value name(args[1]);
+ // Do the actual work
+ params->status = leveldb::DB::Open(params->options, params->name, &self->db);
+
+ return 0;
+}
+
+int DB::EIO_AfterOpen(eio_req *req) {
+ HandleScope scope;
- // Do actual work
- return processStatus(leveldb::DB::Open(options, *name, &(self->db)));
+ OpenParams *params = static_cast<OpenParams*>(req->data);
+ params->Callback();
+
+ delete params;
+ return 0;
}
+
+//
+// Close
+//
+
Handle<Value> DB::Close(const Arguments& args) {
HandleScope scope;
- // Check args
- if (!(args.Length() == 0)) {
- return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected ()")));
+ // Get this and arguments
+ DB* self = ObjectWrap::Unwrap<DB>(args.This());
+
+ // Optional callback
+ Local<Function> callback;
+ if (0 < args.Length() && args[0]->IsFunction()) {
+ callback = Local<Function>::Cast(args[0]);
}
- DB* self = ObjectWrap::Unwrap<DB>(args.This());
+ Params *params = new Params(self, callback);
+ EIO_BeforeClose(params);
+
+ return args.This();
+}
- delete self->db;
- self->db = NULL;
+void DB::EIO_BeforeClose(Params *params) {
+ eio_custom(EIO_Close, EIO_PRI_DEFAULT, EIO_AfterClose, params);
+}
+
+int DB::EIO_Close(eio_req *req) {
+ Params *params = static_cast<Params*>(req->data);
+ DB *self = params->self;
+
+ if (self->db != NULL) {
+ delete self->db;
+ self->db = NULL;
+ }
+
+ return 0;
+}
- return Undefined();
+int DB::EIO_AfterClose(eio_req *req) {
+ Params *params = static_cast<Params*>(req->data);
+ params->Callback();
+
+ delete params;
+ return 0;
}
-Handle<Value> DB::DestroyDB(const Arguments& args) {
- HandleScope scope;
+//
+// Put
+//
+
+Handle<Value> DB::Put(const Arguments& args) {
+ HandleScope scope;
+
+ // Get this and arguments
+ DB* self = ObjectWrap::Unwrap<DB>(args.This());
+ if (self->db == NULL) {
+ return ThrowException(Exception::Error(String::New("DB has not been opened")));
+ }
+
// Check args
- if (!(args.Length() == 2 && args[0]->IsString() && args[1]->IsObject())) {
- return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected (String, Object)")));
+ if (args.Length() < 2 || (!args[0]->IsString() && !Buffer::HasInstance(args[0])) || (!args[1]->IsString() && !Buffer::HasInstance(args[1]))) {
+ return ThrowException(Exception::TypeError(String::New("DB.put() expects key, value")));
}
- String::Utf8Value name(args[0]);
- leveldb::Options options = JsToOptions(args[1]);
+ // Use temporary WriteBatch to implement Put
+ WriteBatch *writeBatch = new WriteBatch();
+ leveldb::Slice key = JsToSlice(args[0], &writeBatch->strings);
+ leveldb::Slice value = JsToSlice(args[1], &writeBatch->strings);
+ writeBatch->wb.Put(key, value);
- return processStatus(leveldb::DestroyDB(*name, options));
+ int pos = 2;
+
+ // Optional write options
+ leveldb::WriteOptions options = leveldb::WriteOptions();
+ if (pos < args.Length() && args[pos]->IsObject() && !args[pos]->IsFunction()) {
+ options = JsToWriteOptions(args[pos]);
+ pos++;
+ }
+
+ // Optional callback
+ Local<Function> callback;
+ if (pos < args.Length() && args[pos]->IsFunction()) {
+ callback = Local<Function>::Cast(args[pos]);
+ pos++;
+ }
+
+ WriteParams *params = new WriteParams(self, writeBatch, options, callback);
+ params->disposeWriteBatch = true;
+ EIO_BeforeWrite(params);
+
+ return args.This();
}
-Handle<Value> DB::RepairDB(const Arguments& args) {
- HandleScope scope;
+//
+// Del
+//
+
+Handle<Value> DB::Del(const Arguments& args) {
+ HandleScope scope;
+
+ // Get this and arguments
+ DB* self = ObjectWrap::Unwrap<DB>(args.This());
+ if (self->db == NULL) {
+ return ThrowException(Exception::Error(String::New("DB has not been opened")));
+ }
+
// Check args
- if (!(args.Length() == 2 && args[0]->IsString() && args[1]->IsObject())) {
- return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected (String, Object)")));
+ if (args.Length() < 1 || (!args[0]->IsString() && !Buffer::HasInstance(args[0]))) {
+ return ThrowException(Exception::TypeError(String::New("DB.del() expects key")));
}
- String::Utf8Value name(args[0]);
- leveldb::Options options = JsToOptions(args[1]);
+ // Use temporary WriteBatch to implement Del
+ WriteBatch *writeBatch = new WriteBatch();
+ leveldb::Slice key = JsToSlice(args[0], &writeBatch->strings);
+ writeBatch->wb.Delete(key);
+
+ int pos = 1;
+
+ // Optional write options
+ leveldb::WriteOptions options = leveldb::WriteOptions();
+ if (pos < args.Length() && args[pos]->IsObject() && !args[pos]->IsFunction()) {
+ options = JsToWriteOptions(args[pos]);
+ pos++;
+ }
- return processStatus(leveldb::RepairDB(*name, options));
+ // Optional callback
+ Local<Function> callback;
+ if (pos < args.Length() && args[pos]->IsFunction()) {
+ callback = Local<Function>::Cast(args[pos]);
+ pos++;
+ }
+
+ WriteParams *params = new WriteParams(self, writeBatch, options, callback);
+ params->disposeWriteBatch = true;
+ EIO_BeforeWrite(params);
+
+ return args.This();
}
-Handle<Value> DB::Put(const Arguments& args) {
+
+//
+// Write
+//
+
+Handle<Value> DB::Write(const Arguments& args) {
HandleScope scope;
- // Check args
- if (!(args.Length() == 3 && args[0]->IsObject() && Buffer::HasInstance(args[1]) && Buffer::HasInstance(args[2]))) {
- return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected (Object, Buffer, Buffer)")));
+ // Get this and arguments
+ DB* self = ObjectWrap::Unwrap<DB>(args.This());
+ if (self->db == NULL) {
+ return ThrowException(Exception::Error(String::New("DB has not been opened")));
+ }
+
+ // Required WriteBatch
+ if (args.Length() < 1 || !args[0]->IsObject()) {
+ return ThrowException(Exception::TypeError(String::New("DB.write() expects a WriteBatch object")));
+ }
+ Local<Object> writeBatchObject = Object::Cast(*args[0]);
+ WriteBatch* writeBatch = ObjectWrap::Unwrap<WriteBatch>(writeBatchObject);
+
+ int pos = 1;
+
+ // Optional write options
+ leveldb::WriteOptions options = leveldb::WriteOptions();
+ if (pos < args.Length() && args[pos]->IsObject() && !args[pos]->IsFunction()) {
+ options = JsToWriteOptions(args[pos]);
+ pos++;
}
- DB* self = ObjectWrap::Unwrap<DB>(args.This());
- leveldb::WriteOptions options = JsToWriteOptions(args[0]);
- leveldb::Slice key = JsToSlice(args[1]);
- leveldb::Slice value = JsToSlice(args[2]);
+ // Optional callback
+ Local<Function> callback;
+ if (pos < args.Length() && args[pos]->IsFunction()) {
+ callback = Local<Function>::Cast(args[pos]);
+ pos++;
+ }
+
+ // Pass parameters to async function
+ WriteParams *params = new WriteParams(self, writeBatch, options, callback);
+ EIO_BeforeWrite(params);
+
+ return args.This();
+}
- return processStatus(self->db->Put(options, key, value));
+void DB::EIO_BeforeWrite(WriteParams *params) {
+ eio_custom(EIO_Write, EIO_PRI_DEFAULT, EIO_AfterWrite, params);
}
-Handle<Value> DB::Del(const Arguments& args) {
- HandleScope scope;
+int DB::EIO_Write(eio_req *req) {
+ WriteParams *params = static_cast<WriteParams*>(req->data);
+ DB *self = params->self;
- // Check args
- if (!(args.Length() == 2 && args[0]->IsObject() && Buffer::HasInstance(args[1]))) {
- return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected (Object, Buffer)")));
+ // Do the actual work
+ if (self->db != NULL) {
+ params->status = self->db->Write(params->options, &params->writeBatch->wb);
}
- DB* self = ObjectWrap::Unwrap<DB>(args.This());
- leveldb::WriteOptions options = JsToWriteOptions(args[0]);
- leveldb::Slice key = JsToSlice(args[1]);
+ return 0;
+}
+
+int DB::EIO_AfterWrite(eio_req *req) {
+ HandleScope scope;
+
+ WriteParams *params = static_cast<WriteParams*>(req->data);
+ params->Callback();
+
+ if (params->disposeWriteBatch) {
+ delete params->writeBatch;
+ }
- return processStatus(self->db->Delete(options, key));
+ delete params;
+ return 0;
}
+
+//
+// Get
+//
+
Handle<Value> DB::Get(const Arguments& args) {
HandleScope scope;
// Check args
- if (!(args.Length() == 2 && args[0]->IsObject() && Buffer::HasInstance(args[1]))) {
- return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected (Object, Buffer)")));
+ if (args.Length() < 1 || (!args[0]->IsString() && !Buffer::HasInstance(args[0]))) {
+ return ThrowException(Exception::TypeError(String::New("DB.get() expects key")));
}
DB* self = ObjectWrap::Unwrap<DB>(args.This());
- leveldb::ReadOptions options = JsToReadOptions(args[0]);
- leveldb::Slice key = JsToSlice(args[1]);
+ if (self->db == NULL) {
+ return ThrowException(Exception::Error(String::New("DB has not been opened")));
+ }
+
+ std::vector<std::string> *strings = NULL;
+ if (args[0]->IsString()) {
+ strings = new std::vector<std::string>(1);
+ }
+ leveldb::Slice key = JsToSlice(args[0], strings);
+
+ int pos = 1;
+
+ // Optional read options
+ leveldb::ReadOptions options = leveldb::ReadOptions();
+ if (pos < args.Length() && args[pos]->IsObject() && !args[pos]->IsFunction()) {
+ options = JsToReadOptions(args[pos]);
+ pos++;
+ }
- // Read value from database
- std::string value;
- leveldb::Status status = self->db->Get(options, key, &value);
- if (!status.ok()) return ThrowException(Exception::Error(String::New(status.ToString().c_str())));
+ // Optional callback
+ Local<Function> callback;
+ if (pos < args.Length() && args[pos]->IsFunction()) {
+ callback = Local<Function>::Cast(args[pos]);
+ pos++;
+ }
+
+ // Pass parameters to async function
+ ReadParams *params = new ReadParams(self, key, options, callback, strings);
+ EIO_BeforeRead(params);
- // Convert string to real JS Buffer
- int length = value.length();
- Buffer *slowBuffer = Buffer::New(length);
- memcpy(BufferData(slowBuffer), value.c_str(), length);
- Local<Function> bufferConstructor = Local<Function>::Cast(Context::GetCurrent()->Global()->Get(String::New("Buffer")));
- Handle<Value> constructorArgs[3] = { slowBuffer->handle_, Integer::New(length), Integer::New(0) };
- Local<Object> actualBuffer = bufferConstructor->NewInstance(3, constructorArgs);
+ return args.This();
+}
- return scope.Close(actualBuffer);
+void DB::EIO_BeforeRead(ReadParams *params) {
+ eio_custom(EIO_Read, EIO_PRI_DEFAULT, EIO_AfterRead, params);
}
-Handle<Value> DB::Write(const Arguments& args) {
- HandleScope scope;
+int DB::EIO_Read(eio_req *req) {
+ ReadParams *params = static_cast<ReadParams*>(req->data);
+ DB *self = params->self;
- if (!(args.Length() == 2 && args[0]->IsObject() && args[1]->IsObject())) {
- return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected (Object, WriteBatch)")));
+ // Do the actual work
+ if (self->db != NULL) {
+ params->status = self->db->Get(params->options, params->key, &params->result);
}
- DB* self = ObjectWrap::Unwrap<DB>(args.This());
- leveldb::WriteOptions options = JsToWriteOptions(args[0]);
- Local<Object> wbObject = Object::Cast(*args[1]);
+ return 0;
+}
- WriteBatch* wb = ObjectWrap::Unwrap<WriteBatch>(wbObject);
+int DB::EIO_AfterRead(eio_req *req) {
+ HandleScope scope;
+
+ ReadParams *params = static_cast<ReadParams*>(req->data);
+ params->Callback(String::New(params->result.data(), params->result.length()));
- return processStatus(self->db->Write(options, wb->wb));
+ delete params;
+ return 0;
}
+
Handle<Value> DB::NewIterator(const Arguments& args) {
HandleScope scope;
- return ThrowException(Exception::Error(String::New("TODO: IMPLEMENT ME")));
+
+ if (!(args.Length() == 1 && args[0]->IsObject())) {
+ return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected (Object)")));
+ } // if
+
+ DB* self = ObjectWrap::Unwrap<DB>(args.This());
+ leveldb::ReadOptions options = JsToReadOptions(args[0]);
+ leveldb::Iterator* it = self->db->NewIterator(options);
+
+ // DJO: Don't like writing code I don't understand, but I found this is how the node-gd library
+ // converts an actual instance to it's binding representation
+ // https://github.com/taggon/node-gd/blob/master/gd_bindings.cc#L79
+ // Guess, I'll understand it soon...
+ Local<Value> _arg_ = External::New(it);
+ Persistent<Object> _it_(Iterator::persistent_function_template->GetFunction()->NewInstance(1, &_arg_));
+
+ return scope.Close(_it_);
}
Handle<Value> DB::GetSnapshot(const Arguments& args) {
@@ -225,3 +461,91 @@ Handle<Value> DB::GetApproximateSizes(const Arguments& args) {
return ThrowException(Exception::Error(String::New("TODO: IMPLEMENT ME")));
}
+
+//
+// DestroyDB
+//
+
+Handle<Value> DB::DestroyDB(const Arguments& args) {
+ HandleScope scope;
+
+ // Check args
+ if (!(args.Length() == 2 && args[0]->IsString() && args[1]->IsObject())) {
+ return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected (String, Object)")));
+ }
+
+ String::Utf8Value name(args[0]);
+ leveldb::Options options = JsToOptions(args[1]);
+
+ return processStatus(leveldb::DestroyDB(*name, options));
+}
+
+
+//
+// RepairDB
+//
+
+Handle<Value> DB::RepairDB(const Arguments& args) {
+ HandleScope scope;
+
+ // Check args
+ if (!(args.Length() == 2 && args[0]->IsString() && args[1]->IsObject())) {
+ return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected (String, Object)")));
+ }
+
+ String::Utf8Value name(args[0]);
+ leveldb::Options options = JsToOptions(args[1]);
+
+ return processStatus(leveldb::RepairDB(*name, options));
+}
+
+
+//
+// Implementation of Params, which are passed from JS thread to EIO thread and back again
+//
+
+DB::Params::Params(DB* self, Handle<Function> cb)
+ : self(self)
+{
+ self->Ref();
+ ev_ref(EV_DEFAULT_UC);
+ callback = Persistent<Function>::New(cb);
+}
+
+DB::Params::~Params() {
+ self->Unref();
+ ev_unref(EV_DEFAULT_UC);
+ callback.Dispose();
+}
+
+void DB::Params::Callback(Handle<Value> result) {
+ if (!callback.IsEmpty()) {
+ TryCatch try_catch;
+ if (status.ok()) {
+ // no error, callback with no arguments, or result as second argument
+ if (result.IsEmpty()) {
+ callback->Call(self->handle_, 0, NULL);
+ } else {
+ Handle<Value> undef = Undefined();
+ Handle<Value> argv[] = { undef, result };
+ callback->Call(self->handle_, 2, argv);
+ }
+ } else {
+ // error, callback with first argument as error object
+ Handle<String> message = String::New(status.ToString().c_str());
+ Handle<Value> argv[] = { Exception::Error(message) };
+ callback->Call(self->handle_, 1, argv);
+ }
+ if (try_catch.HasCaught()) {
+ FatalException(try_catch);
+ }
+ }
+}
+
+DB::ReadParams::~ReadParams() {
+ if (strings != NULL) {
+ delete strings;
+ }
+}
+
+
View
77 src/DB.h
@@ -5,6 +5,7 @@
#include <node.h>
#include "leveldb/db.h"
+#include "WriteBatch.h"
using namespace v8;
using namespace node;
@@ -12,28 +13,24 @@ using namespace node;
namespace node_leveldb {
class DB : ObjectWrap {
-private:
- leveldb::DB* db;
- static Persistent<FunctionTemplate> persistent_function_template;
-
public:
DB();
- ~DB();
+ virtual ~DB();
static void Init(Handle<Object> target);
+
+private:
static Handle<Value> New(const Arguments& args);
static Handle<Value> Open(const Arguments& args);
static Handle<Value> Close(const Arguments& args);
- static Handle<Value> DestroyDB(const Arguments& args);
- static Handle<Value> RepairDB(const Arguments& args);
-
static Handle<Value> Put(const Arguments& args);
static Handle<Value> Del(const Arguments& args);
- static Handle<Value> Get(const Arguments& args);
static Handle<Value> Write(const Arguments& args);
+ static Handle<Value> Get(const Arguments& args);
+
static Handle<Value> NewIterator(const Arguments& args);
static Handle<Value> GetSnapshot(const Arguments& args);
@@ -41,6 +38,68 @@ class DB : ObjectWrap {
static Handle<Value> GetProperty(const Arguments& args);
static Handle<Value> GetApproximateSizes(const Arguments& args);
+
+ static Handle<Value> DestroyDB(const Arguments& args);
+ static Handle<Value> RepairDB(const Arguments& args);
+
+
+ struct Params {
+ Params(DB* self, Handle<Function> cb);
+ virtual ~Params();
+ virtual void Callback(Handle<Value> result = Handle<Value>());
+
+ DB* self;
+ Persistent<Function> callback;
+ leveldb::Status status;
+ };
+
+ struct OpenParams : Params {
+ OpenParams(DB *self, std::string name, leveldb::Options &options, Handle<Function> callback)
+ : Params(self, callback), name(name), options(options) {}
+
+ std::string name;
+ leveldb::Options options;
+ };
+
+ struct ReadParams : Params {
+ ReadParams(DB *self, leveldb::Slice key, leveldb::ReadOptions &options, Handle<Function> callback, std::vector<std::string> *strings = NULL)
+ : Params(self, callback), key(key), options(options), strings(strings) {}
+
+ virtual ~ReadParams();
+
+ leveldb::Slice key;
+ leveldb::ReadOptions options;
+ std::vector<std::string> *strings;
+ std::string result;
+ };
+
+ struct WriteParams : Params {
+ WriteParams(DB *self, WriteBatch *writeBatch, leveldb::WriteOptions &options, Handle<Function> callback)
+ : Params(self, callback), writeBatch(writeBatch), options(options), disposeWriteBatch(false) {}
+
+ WriteBatch *writeBatch;
+ leveldb::WriteOptions options;
+ bool disposeWriteBatch; // when set, EIO_AfterWrite will delete the writeBatch
+ };
+
+ static void EIO_BeforeOpen(OpenParams *params);
+ static int EIO_Open(eio_req *req);
+ static int EIO_AfterOpen(eio_req *req);
+
+ static void EIO_BeforeClose(Params *params);
+ static int EIO_Close(eio_req *req);
+ static int EIO_AfterClose(eio_req *req);
+
+ static void EIO_BeforeRead(ReadParams *params);
+ static int EIO_Read(eio_req *req);
+ static int EIO_AfterRead(eio_req *req);
+
+ static void EIO_BeforeWrite(WriteParams *params);
+ static int EIO_Write(eio_req *req);
+ static int EIO_AfterWrite(eio_req *req);
+
+ leveldb::DB* db;
+ static Persistent<FunctionTemplate> persistent_function_template;
};
} // node_leveldb
View
167 src/Iterator.cc
@@ -1,14 +1,21 @@
#include "Iterator.h"
#include "helpers.h"
+#include "iostream"
using namespace node_leveldb;
Persistent<FunctionTemplate> Iterator::persistent_function_template;
Iterator::Iterator() {
+ this->it = NULL;
}
Iterator::~Iterator() {
+ // cleanup the iterator instance
+ if (this->it) {
+ delete this->it;
+ this->it = NULL;
+ } // if
}
void Iterator::Init(Handle<Object> target) {
@@ -18,6 +25,17 @@ void Iterator::Init(Handle<Object> target) {
persistent_function_template = Persistent<FunctionTemplate>::New(local_function_template);
persistent_function_template->InstanceTemplate()->SetInternalFieldCount(1);
persistent_function_template->SetClassName(String::NewSymbol("Iterator"));
+
+
+ NODE_SET_PROTOTYPE_METHOD(persistent_function_template, "valid", Valid);
+ NODE_SET_PROTOTYPE_METHOD(persistent_function_template, "seekToFirst", SeekToFirst);
+ NODE_SET_PROTOTYPE_METHOD(persistent_function_template, "seekToLast", SeekToLast);
+ NODE_SET_PROTOTYPE_METHOD(persistent_function_template, "seek", Seek);
+ NODE_SET_PROTOTYPE_METHOD(persistent_function_template, "next", Next);
+ NODE_SET_PROTOTYPE_METHOD(persistent_function_template, "prev", Prev);
+ NODE_SET_PROTOTYPE_METHOD(persistent_function_template, "key", key);
+ NODE_SET_PROTOTYPE_METHOD(persistent_function_template, "value", value);
+ NODE_SET_PROTOTYPE_METHOD(persistent_function_template, "status", status);
target->Set(String::NewSymbol("Iterator"), persistent_function_template->GetFunction());
}
@@ -26,7 +44,156 @@ Handle<Value> Iterator::New(const Arguments& args) {
HandleScope scope;
Iterator* iterator = new Iterator();
+
+ if (args.Length() >= 1 && args[0]->IsExternal()) {
+ iterator->it = (leveldb::Iterator*) Local<External>::Cast(args[0])->Value();
+ } // if
+
iterator->Wrap(args.This());
return args.This();
}
+
+Handle<Value> Iterator::Valid(const Arguments& args) {
+ HandleScope scope;
+
+ Iterator* self = ObjectWrap::Unwrap<Iterator>(args.This());
+
+ return self->it->Valid() ? True() : False();
+}
+
+Handle<Value> Iterator::SeekToFirst(const Arguments& args) {
+ Iterator* self = ObjectWrap::Unwrap<Iterator>(args.This());
+ Local<Function> callback;
+ callback = Local<Function>::Cast(args[0]);
+
+ SeekParams *params = new SeekParams(self, leveldb::Slice(), callback);
+ EIO_BeforeSeekToFirst(params);
+
+ return Undefined();
+}
+
+void Iterator::EIO_BeforeSeekToFirst(SeekParams *params) {
+ eio_custom(EIO_SeekToFirst, EIO_PRI_DEFAULT, EIO_AfterSeek, params);
+}
+
+int Iterator::EIO_SeekToFirst(eio_req *req) {
+ SeekParams *params = static_cast<SeekParams*>(req->data);
+ Iterator *self = params->self;
+
+ self->it->SeekToFirst();
+
+ return 0;
+}
+
+Handle<Value> Iterator::SeekToLast(const Arguments& args) {
+ Iterator* self = ObjectWrap::Unwrap<Iterator>(args.This());
+ Local<Function> callback;
+ callback = Local<Function>::Cast(args[0]);
+
+ SeekParams *params = new SeekParams(self, leveldb::Slice(), callback);
+ EIO_BeforeSeekToLast(params);
+
+ return Undefined();
+}
+
+void Iterator::EIO_BeforeSeekToLast(SeekParams *params) {
+ eio_custom(EIO_SeekToLast, EIO_PRI_DEFAULT, EIO_AfterSeek, params);
+}
+
+int Iterator::EIO_SeekToLast(eio_req *req) {
+ SeekParams *params = static_cast<SeekParams*>(req->data);
+ Iterator *self = params->self;
+
+ self->it->SeekToLast();
+
+ return 0;
+}
+
+Handle<Value> Iterator::Seek(const Arguments& args) {
+ Iterator* self = ObjectWrap::Unwrap<Iterator>(args.This());
+ HandleScope scope;
+
+ // XXX: Throw away vector that makes JsToSlice work.
+ // the helper needs to be updated.
+ std::vector<std::string> strings;
+
+ leveldb::Slice key = JsToSlice(args[0], &strings);
+ Local<Function> callback;
+ callback = Local<Function>::Cast(args[1]);
+
+ SeekParams *params = new SeekParams(self, key, callback);
+ EIO_BeforeSeek(params);
+
+ return Undefined();
+}
+
+void Iterator::EIO_BeforeSeek(SeekParams *params) {
+ eio_custom(EIO_Seek, EIO_PRI_DEFAULT, EIO_AfterSeek, params);
+}
+
+int Iterator::EIO_Seek(eio_req *req) {
+ SeekParams *params = static_cast<SeekParams*>(req->data);
+ Iterator *self = params->self;
+
+ self->it->Seek(params->key);
+
+ return 0;
+}
+
+int Iterator::EIO_AfterSeek(eio_req *req) {
+ SeekParams *params = static_cast<SeekParams*>(req->data);
+ params->Callback();
+ delete params;
+ return 0;
+}
+
+Handle<Value> Iterator::Next(const Arguments& args) {
+ ObjectWrap::Unwrap<Iterator>(args.This())->it->Next();
+
+ return Undefined();
+}
+
+Handle<Value> Iterator::Prev(const Arguments& args) {
+ ObjectWrap::Unwrap<Iterator>(args.This())->it->Prev();
+
+ return Undefined();
+}
+
+Handle<Value> Iterator::key(const Arguments& args) {
+ HandleScope scope;
+ leveldb::Slice k = ObjectWrap::Unwrap<Iterator>(args.This())->it->key();
+
+ return scope.Close(Bufferize(k.ToString()));
+}
+
+Handle<Value> Iterator::value(const Arguments& args) {
+ HandleScope scope;
+ leveldb::Slice v = ObjectWrap::Unwrap<Iterator>(args.This())->it->value();
+
+ return scope.Close(Bufferize(v.ToString()));
+}
+
+Handle<Value> Iterator::status(const Arguments& args) {
+ leveldb::Status status = ObjectWrap::Unwrap<Iterator>(args.This())->it->status();
+
+ return processStatus(status);
+}
+
+void Iterator::SeekParams::Callback(Handle<Value> result) {
+ if (!callback.IsEmpty()) {
+ TryCatch try_catch;
+ // no error, callback with no arguments, or result as second argument
+ if (result.IsEmpty()) {
+ callback->Call(self->handle_, 0, NULL);
+ } else {
+ Handle<Value> undef = Undefined();
+ Handle<Value> argv[] = { undef, result };
+ callback->Call(self->handle_, 2, argv);
+ }
+ if (try_catch.HasCaught()) {
+ FatalException(try_catch);
+ }
+ }
+}
+
View
54 src/Iterator.h
@@ -4,6 +4,8 @@
#include <v8.h>
#include <node.h>
+#include "leveldb/iterator.h"
+
using namespace v8;
using namespace node;
@@ -17,8 +19,58 @@ class Iterator : ObjectWrap {
static Persistent<FunctionTemplate> persistent_function_template;
static void Init(Handle<Object> target);
static Handle<Value> New(const Arguments& args);
+
+ static Handle<Value> Valid(const Arguments& args);
+ static Handle<Value> SeekToFirst(const Arguments& args);
+ static Handle<Value> SeekToLast(const Arguments& args);
+ static Handle<Value> Seek(const Arguments& args);
+ static Handle<Value> Next(const Arguments& args);
+ static Handle<Value> Prev(const Arguments& args);
+
+ static Handle<Value> key(const Arguments& args);
+ static Handle<Value> value(const Arguments& args);
+ static Handle<Value> status(const Arguments& args);
+
+ private:
+ leveldb::Iterator* it;
+
+ struct SeekParams {
+ SeekParams(Iterator* it, leveldb::Slice k, Handle<Function> cb) {
+ self = it;
+ key = k;
+ callback = Persistent<Function>::New(cb);
+
+ self->Ref();
+ ev_ref(EV_DEFAULT_UC);
+ }
+
+ ~SeekParams() {
+ self->Unref();
+ ev_unref(EV_DEFAULT_UC);
+ callback.Dispose();
+ }
+
+ virtual void Callback(Handle<Value> result = Handle<Value>());
+
+ Iterator* self;
+ leveldb::Slice key;
+ Persistent<Function> callback;
+
+ };
+
+ static void EIO_BeforeSeek(SeekParams *params);
+ static int EIO_Seek(eio_req *req);
+
+ static void EIO_BeforeSeekToLast(SeekParams *params);
+ static int EIO_SeekToLast(eio_req *req);
+
+ static void EIO_BeforeSeekToFirst(SeekParams *params);
+ static int EIO_SeekToFirst(eio_req *req);
+
+ static int EIO_AfterSeek(eio_req *req);
};
} // node_leveldb
-#endif ITERATOR_H_
+#endif
+
View
38 src/WriteBatch.cc
@@ -1,5 +1,6 @@
#include "WriteBatch.h"
+#include <node_buffer.h>
#include "helpers.h"
using namespace node_leveldb;
@@ -7,11 +8,9 @@ using namespace node_leveldb;
Persistent<FunctionTemplate> WriteBatch::persistent_function_template;
WriteBatch::WriteBatch() {
- this->wb = new leveldb::WriteBatch();
}
WriteBatch::~WriteBatch() {
- delete this->wb;
}
void WriteBatch::Init(Handle<Object> target) {
@@ -42,45 +41,42 @@ Handle<Value> WriteBatch::Put(const Arguments& args) {
HandleScope scope;
// Check args
- if (!(args.Length() == 2 && Buffer::HasInstance(args[0]) && Buffer::HasInstance(args[1]))) {
- return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected (Buffer, Buffer)")));
+ if (args.Length() < 2 || (!args[0]->IsString() && !Buffer::HasInstance(args[0])) || (!args[1]->IsString() && !Buffer::HasInstance(args[1]))) {
+ return ThrowException(Exception::TypeError(String::New("WriteBatch.put() expects key, value")));
}
WriteBatch* self = ObjectWrap::Unwrap<WriteBatch>(args.This());
- leveldb::Slice key = JsToSlice(args[0]);
- leveldb::Slice value = JsToSlice(args[1]);
+
+ leveldb::Slice key = JsToSlice(args[0], &self->strings);
+ leveldb::Slice value = JsToSlice(args[1], &self->strings);
+
+ self->wb.Put(key, value);
- self->wb->Put(key, value);
-
- return Undefined();
+ return args.This();
}
Handle<Value> WriteBatch::Del(const Arguments& args) {
HandleScope scope;
// Check args
- if (!(args.Length() == 1 && Buffer::HasInstance(args[0]))) {
- return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected (Buffer)")));
+ if (args.Length() < 1 || (!args[0]->IsString() && !Buffer::HasInstance(args[0]))) {
+ return ThrowException(Exception::TypeError(String::New("WriteBatch.del() expects key")));
}
WriteBatch* self = ObjectWrap::Unwrap<WriteBatch>(args.This());
- leveldb::Slice key = JsToSlice(args[0]);
+ leveldb::Slice key = JsToSlice(args[0], &self->strings);
- self->wb->Delete(key);
+ self->wb.Delete(key);
- return Undefined();
+ return args.This();
}
Handle<Value> WriteBatch::Clear(const Arguments& args) {
HandleScope scope;
- if (!(args.Length() == 0)) {
- return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected none")));
- }
-
WriteBatch* self = ObjectWrap::Unwrap<WriteBatch>(args.This());
+ self->wb.Clear();
+ self->strings.clear();
- self->wb->Clear();
-
- return Undefined();
+ return args.This();
}
View
14 src/WriteBatch.h
@@ -3,7 +3,7 @@
#include <v8.h>
#include <node.h>
-#include <node_buffer.h>
+#include <vector>
#include "leveldb/write_batch.h"
@@ -13,13 +13,9 @@ using namespace node;
namespace node_leveldb {
class WriteBatch : ObjectWrap {
-private:
- leveldb::WriteBatch* wb;
- static Persistent<FunctionTemplate> persistent_function_template;
-
public:
WriteBatch();
- ~WriteBatch();
+ virtual ~WriteBatch();
static void Init(Handle<Object> target);
static Handle<Value> New(const Arguments& args);
@@ -27,6 +23,12 @@ class WriteBatch : ObjectWrap {
static Handle<Value> Put(const Arguments& args);
static Handle<Value> Del(const Arguments& args);
static Handle<Value> Clear(const Arguments& args);
+
+private:
+ leveldb::WriteBatch wb;
+ std::vector<std::string> strings;
+
+ static Persistent<FunctionTemplate> persistent_function_template;
friend class DB;
};
View
41 src/helpers.cc
@@ -2,8 +2,8 @@
namespace node_leveldb {
-leveldb::Options JsToOptions(Local<Value> val) {
- // Copy the v8 options over an leveldb options object
+leveldb::Options JsToOptions(Handle<Value> val) {
+ HandleScope scope;
Local<Object> obj = Object::Cast(*val);
leveldb::Options options;
if (obj->Has(String::New("create_if_missing"))) {
@@ -30,7 +30,8 @@ leveldb::Options JsToOptions(Local<Value> val) {
return options;
}
-leveldb::ReadOptions JsToReadOptions(Local<Value> val) {
+leveldb::ReadOptions JsToReadOptions(Handle<Value> val) {
+ HandleScope scope;
leveldb::ReadOptions options;
Local<Object> obj = Object::Cast(*val);
if (obj->Has(String::New("verify_checksums"))) {
@@ -42,7 +43,8 @@ leveldb::ReadOptions JsToReadOptions(Local<Value> val) {
return options;
}
-leveldb::WriteOptions JsToWriteOptions(Local<Value> val) {
+leveldb::WriteOptions JsToWriteOptions(Handle<Value> val) {
+ HandleScope scope;
leveldb::WriteOptions options;
Local<Object> obj = Object::Cast(*val);
if (obj->Has(String::New("sync"))) {
@@ -51,10 +53,20 @@ leveldb::WriteOptions JsToWriteOptions(Local<Value> val) {
return options;
}
-leveldb::Slice JsToSlice(Local<Value> val) {
- Local<Object> obj = Object::Cast(*val);
- leveldb::Slice slice(BufferData(obj), BufferLength(obj));
- return slice;
+leveldb::Slice JsToSlice(Handle<Value> val, std::vector<std::string> *strings) {
+ HandleScope scope;
+ if (val->IsString() && strings != NULL) {
+ std::string str(*String::Utf8Value(val));
+ strings->push_back(str);
+ return leveldb::Slice(str.data(), str.length());
+ }
+ else if (Buffer::HasInstance(val)) {
+ Local<Object> obj = Object::Cast(*val);
+ return leveldb::Slice(BufferData(obj), BufferLength(obj));
+ }
+ else {
+ return leveldb::Slice();
+ }
}
Handle<Value> processStatus(leveldb::Status status) {
@@ -62,6 +74,15 @@ Handle<Value> processStatus(leveldb::Status status) {
return ThrowException(Exception::Error(String::New(status.ToString().c_str())));
}
+Local<Object> Bufferize(std::string value) {
+ int length = value.length();
+ Buffer *slowBuffer = Buffer::New(length);
+ memcpy(BufferData(slowBuffer), value.c_str(), length);
+ Local<Function> bufferConstructor = Local<Function>::Cast(Context::GetCurrent()->Global()->Get(String::New("Buffer")));
+ Handle<Value> constructorArgs[3] = { slowBuffer->handle_, Integer::New(length), Integer::New(0) };
+
+ return bufferConstructor->NewInstance(3, constructorArgs);
+}
char* BufferData(Buffer *b) {
return Buffer::Data(b->handle_);
@@ -71,11 +92,11 @@ size_t BufferLength(Buffer *b) {
return Buffer::Length(b->handle_);
}
-char* BufferData(Local<Object> buf_obj) {
+char* BufferData(Handle<Object> buf_obj) {
return Buffer::Data(buf_obj);
}
-size_t BufferLength(Local<Object> buf_obj) {
+size_t BufferLength(Handle<Object> buf_obj) {
return Buffer::Length(buf_obj);
}
View
16 src/helpers.h
@@ -6,6 +6,9 @@
#include <node_buffer.h>
#include "leveldb/db.h"
+#include <vector>
+#include <string>
+#include <iostream> // for debugging
using namespace node;
using namespace v8;
@@ -13,19 +16,20 @@ using namespace v8;
namespace node_leveldb {
// Helper to convert vanilla JS objects into leveldb objects
- leveldb::Options JsToOptions(Local<Value> val);
- leveldb::ReadOptions JsToReadOptions(Local<Value> val);
- leveldb::WriteOptions JsToWriteOptions(Local<Value> val);
- leveldb::Slice JsToSlice(Local<Value> val);
+ leveldb::Options JsToOptions(Handle<Value> val);
+ leveldb::ReadOptions JsToReadOptions(Handle<Value> val);
+ leveldb::WriteOptions JsToWriteOptions(Handle<Value> val);
+ leveldb::Slice JsToSlice(Handle<Value> val, std::vector<std::string> *strings);
// Helper to convert a leveldb::Status instance to a V8 return value
Handle<Value> processStatus(leveldb::Status status);
// Helpers to work with buffers
+ Local<Object> Bufferize(std::string value);
char* BufferData(Buffer *b);
size_t BufferLength(Buffer *b);
- char* BufferData(Local<Object> buf_obj);
- size_t BufferLength(Local<Object> buf_obj);
+ char* BufferData(Handle<Object> buf_obj);
+ size_t BufferLength(Handle<Object> buf_obj);
} // node_leveldb

0 comments on commit cc047d4

Please sign in to comment.
Something went wrong with that request. Please try again.