diff --git a/binding.gyp b/binding.gyp index 6c20da54..ddbd5347 100644 --- a/binding.gyp +++ b/binding.gyp @@ -20,8 +20,7 @@ "<(module_root_dir)/deps/leveldb/leveldb.gyp:leveldb" ] , "sources": [ - "src/async.cc" - , "src/batch.cc" + "src/batch.cc" , "src/batch_async.cc" , "src/database.cc" , "src/database_async.cc" diff --git a/src/async.cc b/src/async.cc deleted file mode 100644 index 45c90c5c..00000000 --- a/src/async.cc +++ /dev/null @@ -1,73 +0,0 @@ -/* Copyright (c) 2012-2013 LevelDOWN contributors - * See list at - * MIT +no-false-attribs License - */ - -#include - -#include "database.h" -#include "leveldown.h" -#include "async.h" - -namespace leveldown { - -/** ASYNC BASE **/ - -AsyncWorker::AsyncWorker ( - Database* database - , v8::Persistent callback -) : database(database) - , callback(callback) -{ - request.data = this; -}; - -AsyncWorker::~AsyncWorker () {} - -void AsyncWorker::WorkComplete () { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE - - if (status.ok()) - HandleOKCallback(); - else - HandleErrorCallback(); - callback.Dispose(LD_NODE_ISOLATE); -} - -void AsyncWorker::HandleOKCallback () { - LD_RUN_CALLBACK(callback, NULL, 0); -} - -void AsyncWorker::HandleErrorCallback () { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE - - v8::Local argv[] = { - v8::Local::New( - v8::Exception::Error(v8::String::New(status.ToString().c_str())) - ) - }; - LD_RUN_CALLBACK(callback, argv, 1); -} - -void AsyncExecute (uv_work_t* req) { - static_cast(req->data)->Execute(); -} - -void AsyncExecuteComplete (uv_work_t* req) { - AsyncWorker* worker = static_cast(req->data); - worker->WorkComplete(); - delete worker; -} - -void AsyncQueueWorker (AsyncWorker* worker) { - uv_queue_work( - uv_default_loop() - , &worker->request - , AsyncExecute - , (uv_after_work_cb)AsyncExecuteComplete - ); -} - -} // namespace leveldown diff --git a/src/async.h b/src/async.h index dc1b1e62..ddf50042 100644 --- a/src/async.h +++ b/src/async.h @@ -7,34 +7,35 @@ #define LD_ASYNC_H #include +#include "nan.h" #include "database.h" namespace leveldown { -/* abstract */ class AsyncWorker { +class Database; + +/* abstract */ class AsyncWorker : public NanAsyncWorker { public: AsyncWorker ( leveldown::Database* database - , v8::Persistent callback - ); - - virtual ~AsyncWorker (); - uv_work_t request; - virtual void WorkComplete (); - virtual void Execute () =0; + , NanCallback *callback + ) : NanAsyncWorker(callback), database(database) { + NanScope(); + v8::Local obj = v8::Object::New(); + NanAssignPersistent(v8::Object, persistentHandle, obj); + } protected: + void SetStatus(leveldb::Status status) { + this->status = status; + if (!status.ok()) + this->errmsg = strdup(status.ToString().c_str()); + } Database* database; - v8::Persistent callback; +private: leveldb::Status status; - virtual void HandleOKCallback (); - virtual void HandleErrorCallback (); }; -void AsyncExecute (uv_work_t* req); -void AsyncExecuteComplete (uv_work_t* req); -void AsyncQueueWorker (AsyncWorker* worker); - } // namespace leveldown #endif diff --git a/src/batch.cc b/src/batch.cc index 3e68a52a..695dcb1b 100644 --- a/src/batch.cc +++ b/src/batch.cc @@ -1,19 +1,21 @@ #include #include + +#include "nan.h" #include "database.h" #include "batch_async.h" #include "batch.h" namespace leveldown { -v8::Persistent Batch::constructor; +static v8::Persistent batch_constructor; Batch::Batch (leveldown::Database* database, bool sync) : database(database) { options = new leveldb::WriteOptions(); options->sync = sync; batch = new leveldb::WriteBatch(); - references = new std::vector; + references = new std::vector; hasData = false; written = false; } @@ -29,34 +31,18 @@ leveldb::Status Batch::Write () { } void Batch::Init () { - LD_NODE_ISOLATE_DECL v8::Local tpl = v8::FunctionTemplate::New(Batch::New); - tpl->SetClassName(v8::String::NewSymbol("Batch")); + NanAssignPersistent(v8::FunctionTemplate, batch_constructor, tpl); + tpl->SetClassName(NanSymbol("Batch")); tpl->InstanceTemplate()->SetInternalFieldCount(1); - tpl->PrototypeTemplate()->Set( - v8::String::NewSymbol("put") - , v8::FunctionTemplate::New(Batch::Put)->GetFunction() - ); - tpl->PrototypeTemplate()->Set( - v8::String::NewSymbol("del") - , v8::FunctionTemplate::New(Batch::Del)->GetFunction() - ); - tpl->PrototypeTemplate()->Set( - v8::String::NewSymbol("clear") - , v8::FunctionTemplate::New(Batch::Clear)->GetFunction() - ); - tpl->PrototypeTemplate()->Set( - v8::String::NewSymbol("write") - , v8::FunctionTemplate::New(Batch::Write)->GetFunction() - ); - constructor = v8::Persistent::New( - LD_NODE_ISOLATE_PRE - tpl->GetFunction()); + NODE_SET_PROTOTYPE_METHOD(tpl, "put", Batch::Put); + NODE_SET_PROTOTYPE_METHOD(tpl, "del", Batch::Del); + NODE_SET_PROTOTYPE_METHOD(tpl, "clear", Batch::Clear); + NODE_SET_PROTOTYPE_METHOD(tpl, "write", Batch::Write); } -v8::Handle Batch::New (const v8::Arguments& args) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE +NAN_METHOD(Batch::New) { + NanScope(); Database* database = node::ObjectWrap::Unwrap(args[0]->ToObject()); v8::Local optionsObj; @@ -65,12 +51,12 @@ v8::Handle Batch::New (const v8::Arguments& args) { optionsObj = v8::Local::Cast(args[1]); } - bool sync = BooleanOptionValue(optionsObj, option_sync); + bool sync = NanBooleanOptionValue(optionsObj, NanSymbol("sync")); Batch* batch = new Batch(database, sync); batch->Wrap(args.This()); - return args.This(); + NanReturnValue(args.This()); } v8::Handle Batch::NewInstance ( @@ -78,31 +64,31 @@ v8::Handle Batch::NewInstance ( , v8::Handle optionsObj ) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE + NanScope(); v8::Local instance; + v8::Local constructorHandle = + NanPersistentToLocal(batch_constructor); + if (optionsObj.IsEmpty()) { v8::Handle argv[1] = { database }; - instance = constructor->NewInstance(1, argv); + instance = constructorHandle->GetFunction()->NewInstance(1, argv); } else { v8::Handle argv[2] = { database, optionsObj }; - instance = constructor->NewInstance(2, argv); + instance = constructorHandle->GetFunction()->NewInstance(2, argv); } return scope.Close(instance); } -v8::Handle Batch::Put (const v8::Arguments& args) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE +NAN_METHOD(Batch::Put) { + NanScope(); Batch* batch = ObjectWrap::Unwrap(args.Holder()); - if (batch->written) { - LD_THROW_RETURN(write() already called on this batch) - } + if (batch->written) + return NanThrowError("write() already called on this batch"); v8::Handle callback; // purely for the error macros @@ -114,31 +100,23 @@ v8::Handle Batch::Put (const v8::Arguments& args) { LD_STRING_OR_BUFFER_TO_SLICE(key, keyBuffer, key) LD_STRING_OR_BUFFER_TO_SLICE(value, valueBuffer, value) - batch->references->push_back(Reference( - v8::Persistent::New(LD_NODE_ISOLATE_PRE keyBuffer) - , key - )); - batch->references->push_back(Reference( - v8::Persistent::New(LD_NODE_ISOLATE_PRE valueBuffer) - , value - )); + batch->references->push_back(new Reference(keyBuffer, key)); + batch->references->push_back(new Reference(valueBuffer, value)); batch->batch->Put(key, value); if (!batch->hasData) batch->hasData = true; - return scope.Close(args.Holder()); + NanReturnValue(args.Holder()); } -v8::Handle Batch::Del (const v8::Arguments& args) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE +NAN_METHOD(Batch::Del) { + NanScope(); Batch* batch = ObjectWrap::Unwrap(args.Holder()); - if (batch->written) { - LD_THROW_RETURN(write() already called on this batch) - } + if (batch->written) + return NanThrowError("write() already called on this batch"); v8::Handle callback; // purely for the error macros @@ -147,62 +125,52 @@ v8::Handle Batch::Del (const v8::Arguments& args) { v8::Local keyBuffer = args[0]; LD_STRING_OR_BUFFER_TO_SLICE(key, keyBuffer, key) - batch->references->push_back(Reference( - v8::Persistent::New(LD_NODE_ISOLATE_PRE keyBuffer) - , key - )); + batch->references->push_back(new Reference(keyBuffer, key)); batch->batch->Delete(key); if (!batch->hasData) batch->hasData = true; - return scope.Close(args.Holder()); + NanReturnValue(args.Holder()); } -v8::Handle Batch::Clear (const v8::Arguments& args) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE +NAN_METHOD(Batch::Clear) { + NanScope(); Batch* batch = ObjectWrap::Unwrap(args.Holder()); - if (batch->written) { - LD_THROW_RETURN(write() already called on this batch) - } + if (batch->written) + return NanThrowError("write() already called on this batch"); batch->batch->Clear(); batch->hasData = false; - return scope.Close(args.Holder()); + NanReturnValue(args.Holder()); } -v8::Handle Batch::Write (const v8::Arguments& args) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE +NAN_METHOD(Batch::Write) { + NanScope(); Batch* batch = ObjectWrap::Unwrap(args.Holder()); - if (batch->written) { - LD_THROW_RETURN(write() already called on this batch) - } + if (batch->written) + return NanThrowError("write() already called on this batch"); - if (args.Length() == 0) { - LD_THROW_RETURN(write() requires a callback argument) - } + if (args.Length() == 0) + return NanThrowError("write() requires a callback argument"); batch->written = true; if (batch->hasData) { - v8::Persistent callback = v8::Persistent::New( - LD_NODE_ISOLATE_PRE - v8::Local::Cast(args[0])); - + NanCallback *callback = + new NanCallback(v8::Local::Cast(args[0])); BatchWriteWorker* worker = new BatchWriteWorker(batch, callback); - AsyncQueueWorker(worker); + NanAsyncQueueWorker(worker); } else { LD_RUN_CALLBACK(v8::Local::Cast(args[0]), NULL, 0); } - return v8::Undefined(); + NanReturnUndefined(); } } // namespace leveldown diff --git a/src/batch.h b/src/batch.h index 31f1f04c..7716d887 100644 --- a/src/batch.h +++ b/src/batch.h @@ -26,17 +26,15 @@ class Batch : public node::ObjectWrap { leveldown::Database* database; leveldb::WriteOptions* options; leveldb::WriteBatch* batch; - std::vector* references; + std::vector* references; bool hasData; // keep track of whether we're writing data or not bool written; - static v8::Persistent constructor; - - LD_V8_METHOD( New ) - LD_V8_METHOD( Put ) - LD_V8_METHOD( Del ) - LD_V8_METHOD( Clear ) - LD_V8_METHOD( Write ) + static NAN_METHOD(New); + static NAN_METHOD(Put); + static NAN_METHOD(Del); + static NAN_METHOD(Clear); + static NAN_METHOD(Write); }; } // namespace leveldown diff --git a/src/batch_async.cc b/src/batch_async.cc index 93f34e12..4ff256ca 100644 --- a/src/batch_async.cc +++ b/src/batch_async.cc @@ -14,7 +14,7 @@ namespace leveldown { BatchWriteWorker::BatchWriteWorker ( Batch* batch - , v8::Persistent callback + , NanCallback *callback ) : AsyncWorker(NULL, callback) , batch(batch) {}; @@ -22,7 +22,7 @@ BatchWriteWorker::BatchWriteWorker ( BatchWriteWorker::~BatchWriteWorker () {} void BatchWriteWorker::Execute () { - status = batch->Write(); + SetStatus(batch->Write()); } } // namespace leveldown diff --git a/src/batch_async.h b/src/batch_async.h index 46e459ba..05fb4add 100644 --- a/src/batch_async.h +++ b/src/batch_async.h @@ -8,6 +8,7 @@ #include +#include "nan.h" #include "async.h" #include "batch.h" #include "database.h" @@ -18,7 +19,7 @@ class BatchWriteWorker : public AsyncWorker { public: BatchWriteWorker ( Batch* batch - , v8::Persistent callback + , NanCallback *callback ); virtual ~BatchWriteWorker (); diff --git a/src/database.cc b/src/database.cc index fa976c3a..5db31099 100644 --- a/src/database.cc +++ b/src/database.cc @@ -19,6 +19,8 @@ namespace leveldown { +static v8::Persistent database_constructor; + Database::Database (char* location) : location(location) { db = NULL; currentIteratorId = 0; @@ -105,7 +107,7 @@ void Database::ReleaseIterator (uint32_t id) { // iterators to end before we can close them iterators.erase(id); if (iterators.empty() && pendingCloseWorker != NULL) { - AsyncQueueWorker((AsyncWorker*)pendingCloseWorker); + NanAsyncQueueWorker((AsyncWorker*)pendingCloseWorker); pendingCloseWorker = NULL; } } @@ -117,139 +119,109 @@ void Database::CloseDatabase () { /* V8 exposed functions *****************************/ -v8::Persistent Database::constructor; - -v8::Handle LevelDOWN (const v8::Arguments& args) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE +NAN_METHOD(LevelDOWN) { + NanScope(); - return scope.Close(Database::NewInstance(args)); + v8::Local location; + if (args.Length() != 0 && args[0]->IsString()) + location = args[0].As(); + NanReturnValue(Database::NewInstance(location)); } void Database::Init () { - LD_NODE_ISOLATE_DECL - v8::Local tpl = v8::FunctionTemplate::New(New); - tpl->SetClassName(v8::String::NewSymbol("Database")); + v8::Local tpl = v8::FunctionTemplate::New(Database::New); + NanAssignPersistent(v8::FunctionTemplate, database_constructor, tpl); + tpl->SetClassName(NanSymbol("Database")); tpl->InstanceTemplate()->SetInternalFieldCount(1); - tpl->PrototypeTemplate()->Set( - v8::String::NewSymbol("open") - , v8::FunctionTemplate::New(Open)->GetFunction() - ); - tpl->PrototypeTemplate()->Set( - v8::String::NewSymbol("close") - , v8::FunctionTemplate::New(Close)->GetFunction() - ); - tpl->PrototypeTemplate()->Set( - v8::String::NewSymbol("put") - , v8::FunctionTemplate::New(Put)->GetFunction() - ); - tpl->PrototypeTemplate()->Set( - v8::String::NewSymbol("get") - , v8::FunctionTemplate::New(Get)->GetFunction() - ); - tpl->PrototypeTemplate()->Set( - v8::String::NewSymbol("del") - , v8::FunctionTemplate::New(Delete)->GetFunction() - ); - tpl->PrototypeTemplate()->Set( - v8::String::NewSymbol("batch") - , v8::FunctionTemplate::New(Batch)->GetFunction() - ); - tpl->PrototypeTemplate()->Set( - v8::String::NewSymbol("approximateSize") - , v8::FunctionTemplate::New(ApproximateSize)->GetFunction() - ); - tpl->PrototypeTemplate()->Set( - v8::String::NewSymbol("getProperty") - , v8::FunctionTemplate::New(GetProperty)->GetFunction() - ); - tpl->PrototypeTemplate()->Set( - v8::String::NewSymbol("iterator") - , v8::FunctionTemplate::New(Iterator)->GetFunction() - ); - constructor = v8::Persistent::New( - LD_NODE_ISOLATE_PRE - tpl->GetFunction()); + NODE_SET_PROTOTYPE_METHOD(tpl, "open", Database::Open); + NODE_SET_PROTOTYPE_METHOD(tpl, "close", Database::Close); + NODE_SET_PROTOTYPE_METHOD(tpl, "put", Database::Put); + NODE_SET_PROTOTYPE_METHOD(tpl, "get", Database::Get); + NODE_SET_PROTOTYPE_METHOD(tpl, "del", Database::Delete); + NODE_SET_PROTOTYPE_METHOD(tpl, "batch", Database::Batch); + NODE_SET_PROTOTYPE_METHOD(tpl, "approximateSize", Database::ApproximateSize); + NODE_SET_PROTOTYPE_METHOD(tpl, "getProperty", Database::GetProperty); + NODE_SET_PROTOTYPE_METHOD(tpl, "iterator", Database::Iterator); } -v8::Handle Database::New (const v8::Arguments& args) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE +NAN_METHOD(Database::New) { + NanScope(); - if (args.Length() == 0) { - LD_THROW_RETURN(constructor requires at least a location argument) - } + if (args.Length() == 0) + return NanThrowError("constructor requires at least a location argument"); - if (!args[0]->IsString()) { - LD_THROW_RETURN(constructor requires a location string argument) - } + if (!args[0]->IsString()) + return NanThrowError("constructor requires a location string argument"); - char* location = FromV8String(args[0]); + char* location = NanFromV8String(args[0].As()); Database* obj = new Database(location); obj->Wrap(args.This()); - return args.This(); + NanReturnValue(args.This()); } -v8::Handle Database::NewInstance (const v8::Arguments& args) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE +v8::Handle Database::NewInstance (v8::Local &location) { + NanScope(); v8::Local instance; - if (args.Length() == 0) { - instance = constructor->NewInstance(0, NULL); + v8::Local constructorHandle = + NanPersistentToLocal(database_constructor); + + if (location.IsEmpty()) { + instance = constructorHandle->GetFunction()->NewInstance(0, NULL); } else { - v8::Handle argv[] = { args[0] }; - instance = constructor->NewInstance(1, argv); + v8::Handle argv[] = { location }; + instance = constructorHandle->GetFunction()->NewInstance(1, argv); } - return scope.Close(instance); + return instance; } -v8::Handle Database::Open (const v8::Arguments& args) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE +NAN_METHOD(Database::Open) { + NanScope(); LD_METHOD_SETUP_COMMON(open, 0, 1) - bool createIfMissing = BooleanOptionValueDefTrue( + bool createIfMissing = NanBooleanOptionValue( optionsObj - , option_createIfMissing + , NanSymbol("createIfMissing") + , true ); - bool errorIfExists = BooleanOptionValue(optionsObj, option_errorIfExists); - bool compression = BooleanOptionValue(optionsObj, option_compression); + bool errorIfExists = + NanBooleanOptionValue(optionsObj, NanSymbol("errorIfExists")); + bool compression = NanBooleanOptionValue(optionsObj, NanSymbol("compression")); - uint32_t cacheSize = UInt32OptionValue( + uint32_t cacheSize = NanUInt32OptionValue( optionsObj - , option_cacheSize + , NanSymbol("cacheSize") , 8 << 20 ); - uint32_t writeBufferSize = UInt32OptionValue( + uint32_t writeBufferSize = NanUInt32OptionValue( optionsObj - , option_writeBufferSize + , NanSymbol("writeBufferSize") , 4 << 20 ); - uint32_t blockSize = UInt32OptionValue( + uint32_t blockSize = NanUInt32OptionValue( optionsObj - , option_blockSize + , NanSymbol("blockSize") , 4096 ); - uint32_t maxOpenFiles = UInt32OptionValue( + uint32_t maxOpenFiles = NanUInt32OptionValue( optionsObj - , option_maxOpenFiles + , NanSymbol("maxOpenFiles") , 1000 ); - uint32_t blockRestartInterval = UInt32OptionValue( + uint32_t blockRestartInterval = NanUInt32OptionValue( optionsObj - , option_blockRestartInterval + , NanSymbol("blockRestartInterval") , 16 ); OpenWorker* worker = new OpenWorker( database - , v8::Persistent::New(LD_NODE_ISOLATE_PRE callback) + , new NanCallback(callback) , createIfMissing , errorIfExists , compression @@ -260,20 +232,19 @@ v8::Handle Database::Open (const v8::Arguments& args) { , blockRestartInterval ); - AsyncQueueWorker(worker); + NanAsyncQueueWorker(worker); - return v8::Undefined(); + NanReturnUndefined(); } -v8::Handle Database::Close (const v8::Arguments& args) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE +NAN_METHOD(Database::Close) { + NanScope(); LD_METHOD_SETUP_COMMON_ONEARG(close) CloseWorker* worker = new CloseWorker( database - , v8::Persistent::New(LD_NODE_ISOLATE_PRE callback) + , new NanCallback(callback) ); if (!database->iterators.empty()) { @@ -283,7 +254,7 @@ v8::Handle Database::Close (const v8::Arguments& args) { database->pendingCloseWorker = worker; for ( - std::map< uint32_t, v8::Persistent >::iterator it + std::map< uint32_t, leveldown::Iterator * >::iterator it = database->iterators.begin() ; it != database->iterators.end() ; ++it) { @@ -294,150 +265,132 @@ v8::Handle Database::Close (const v8::Arguments& args) { // function and wait for it to hit ReleaseIterator() where our // CloseWorker will be invoked + /* + v8::Local localHandle = NanPersistentToLocal(it->second); leveldown::Iterator* iterator = - node::ObjectWrap::Unwrap(it->second); + node::ObjectWrap::Unwrap(localHandle-> + Get(NanSymbol("iterator")).As()); + */ + leveldown::Iterator *iterator = it->second; if (!iterator->ended) { v8::Local end = - v8::Local::Cast(it->second->Get( + v8::Local::Cast(NanObjectWrapHandle(iterator)->Get( v8::String::NewSymbol("end"))); v8::Local argv[] = { v8::FunctionTemplate::New()->GetFunction() // empty callback }; v8::TryCatch try_catch; - end->Call(it->second, 1, argv); + end->Call(NanObjectWrapHandle(iterator), 1, argv); if (try_catch.HasCaught()) { node::FatalException(try_catch); } } } } else { - AsyncQueueWorker(worker); + NanAsyncQueueWorker(worker); } - return v8::Undefined(); + NanReturnUndefined(); } -v8::Handle Database::Put (const v8::Arguments& args) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE +NAN_METHOD(Database::Put) { + NanScope(); LD_METHOD_SETUP_COMMON(put, 2, 3) LD_CB_ERR_IF_NULL_OR_UNDEFINED(args[0], key) LD_CB_ERR_IF_NULL_OR_UNDEFINED(args[1], value) - v8::Local keyBufferV = args[0]; - v8::Local valueBufferV = args[1]; - LD_STRING_OR_BUFFER_TO_SLICE(key, keyBufferV, key) - LD_STRING_OR_BUFFER_TO_SLICE(value, valueBufferV, value) - - v8::Persistent keyBuffer = - v8::Persistent::New(LD_NODE_ISOLATE_PRE keyBufferV); - v8::Persistent valueBuffer = - v8::Persistent::New(LD_NODE_ISOLATE_PRE valueBufferV); + v8::Local keyHandle = args[0].As(); + v8::Local valueHandle = args[1].As(); + LD_STRING_OR_BUFFER_TO_SLICE(key, keyHandle, key) + LD_STRING_OR_BUFFER_TO_SLICE(value, valueHandle, value) - bool sync = BooleanOptionValue(optionsObj, option_sync); + bool sync = NanBooleanOptionValue(optionsObj, NanSymbol("sync")); WriteWorker* worker = new WriteWorker( database - , v8::Persistent::New(LD_NODE_ISOLATE_PRE callback) + , new NanCallback(callback) , key , value , sync - , keyBuffer - , valueBuffer + , keyHandle + , valueHandle ); - AsyncQueueWorker(worker); + NanAsyncQueueWorker(worker); - return v8::Undefined(); + NanReturnUndefined(); } -v8::Handle Database::Get (const v8::Arguments& args) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE +NAN_METHOD(Database::Get) { + NanScope(); LD_METHOD_SETUP_COMMON(get, 1, 2) LD_CB_ERR_IF_NULL_OR_UNDEFINED(args[0], key) - v8::Local keyBufferV = args[0]; - LD_STRING_OR_BUFFER_TO_SLICE(key, keyBufferV, key) + v8::Local keyHandle = args[0].As(); + LD_STRING_OR_BUFFER_TO_SLICE(key, keyHandle, key) - v8::Persistent keyBuffer = v8::Persistent::New( - LD_NODE_ISOLATE_PRE - keyBufferV); - - bool asBuffer = BooleanOptionValueDefTrue(optionsObj, option_asBuffer); - bool fillCache = BooleanOptionValueDefTrue(optionsObj, option_fillCache); + bool asBuffer = NanBooleanOptionValue(optionsObj, NanSymbol("asBuffer"), true); + bool fillCache = NanBooleanOptionValue(optionsObj, NanSymbol("fillCache"), true); ReadWorker* worker = new ReadWorker( database - , v8::Persistent::New(LD_NODE_ISOLATE_PRE callback) + , new NanCallback(callback) , key , asBuffer , fillCache - , keyBuffer + , keyHandle ); - AsyncQueueWorker(worker); + NanAsyncQueueWorker(worker); - return v8::Undefined(); + NanReturnUndefined(); } -v8::Handle Database::Delete (const v8::Arguments& args) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE +NAN_METHOD(Database::Delete) { + NanScope(); LD_METHOD_SETUP_COMMON(del, 1, 2) LD_CB_ERR_IF_NULL_OR_UNDEFINED(args[0], key) - v8::Local keyBufferV = args[0]; - LD_STRING_OR_BUFFER_TO_SLICE(key, keyBufferV, key) - - v8::Persistent keyBuffer = - v8::Persistent::New(LD_NODE_ISOLATE_PRE keyBufferV); + v8::Local keyHandle = args[0].As(); + LD_STRING_OR_BUFFER_TO_SLICE(key, keyHandle, key) - bool sync = BooleanOptionValue(optionsObj, option_sync); + bool sync = NanBooleanOptionValue(optionsObj, NanSymbol("sync")); DeleteWorker* worker = new DeleteWorker( database - , v8::Persistent::New(LD_NODE_ISOLATE_PRE callback) + , new NanCallback(callback) , key , sync - , keyBuffer + , keyHandle ); - AsyncQueueWorker(worker); + NanAsyncQueueWorker(worker); - return v8::Undefined(); + NanReturnUndefined(); } -/* property key & value strings for elements of the array sent to batch() */ -LD_SYMBOL ( str_key , key ); -LD_SYMBOL ( str_value , value ); -LD_SYMBOL ( str_type , type ); -LD_SYMBOL ( str_del , del ); -LD_SYMBOL ( str_put , put ); - -v8::Handle Database::Batch (const v8::Arguments& args) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE +NAN_METHOD(Database::Batch) { + NanScope(); if ((args.Length() == 0 || args.Length() == 1) && !args[0]->IsArray()) { v8::Local optionsObj; if (args.Length() > 0 && args[0]->IsObject()) { - optionsObj = v8::Local::Cast(args[0]); + optionsObj = args[0].As(); } - return scope.Close(Batch::NewInstance(args.This(), optionsObj)); + NanReturnValue(Batch::NewInstance(args.This(), optionsObj)); } LD_METHOD_SETUP_COMMON(batch, 1, 2) - bool sync = BooleanOptionValue(optionsObj, option_sync); + bool sync = NanBooleanOptionValue(optionsObj, NanSymbol("sync")); v8::Local array = v8::Local::Cast(args[0]); - std::vector< Reference >* references = new std::vector< Reference >; + std::vector< Reference *>* references = new std::vector< Reference *>; leveldb::WriteBatch* batch = new leveldb::WriteBatch(); bool hasData = false; @@ -447,24 +400,21 @@ v8::Handle Database::Batch (const v8::Arguments& args) { v8::Local obj = v8::Local::Cast(array->Get(i)); - LD_CB_ERR_IF_NULL_OR_UNDEFINED(obj->Get(str_type), type) + LD_CB_ERR_IF_NULL_OR_UNDEFINED(obj->Get(NanSymbol("type")), type) - v8::Local keyBuffer = obj->Get(str_key); + v8::Local keyBuffer = obj->Get(NanSymbol("key")); LD_CB_ERR_IF_NULL_OR_UNDEFINED(keyBuffer, key) - if (obj->Get(str_type)->StrictEquals(str_del)) { + if (obj->Get(NanSymbol("type"))->StrictEquals(NanSymbol("del"))) { LD_STRING_OR_BUFFER_TO_SLICE(key, keyBuffer, key) batch->Delete(key); if (!hasData) hasData = true; - references->push_back(Reference( - v8::Persistent::New(LD_NODE_ISOLATE_PRE keyBuffer) - , key - )); - } else if (obj->Get(str_type)->StrictEquals(str_put)) { - v8::Local valueBuffer = obj->Get(str_value); + references->push_back(new Reference(keyBuffer, key)); + } else if (obj->Get(NanSymbol("type"))->StrictEquals(NanSymbol("put"))) { + v8::Local valueBuffer = obj->Get(NanSymbol("value")); LD_CB_ERR_IF_NULL_OR_UNDEFINED(valueBuffer, value) LD_STRING_OR_BUFFER_TO_SLICE(key, keyBuffer, key) @@ -474,22 +424,16 @@ v8::Handle Database::Batch (const v8::Arguments& args) { if (!hasData) hasData = true; - references->push_back(Reference( - v8::Persistent::New(LD_NODE_ISOLATE_PRE keyBuffer) - , key - )); - references->push_back(Reference( - v8::Persistent::New(LD_NODE_ISOLATE_PRE valueBuffer) - , value - )); + references->push_back(new Reference(keyBuffer, key)); + references->push_back(new Reference(valueBuffer, value)); } } // don't allow an empty batch through if (hasData) { - AsyncQueueWorker(new BatchWorker( + NanAsyncQueueWorker(new BatchWorker( database - , v8::Persistent::New(LD_NODE_ISOLATE_PRE callback) + , new NanCallback(callback) , batch , references , sync @@ -499,24 +443,23 @@ v8::Handle Database::Batch (const v8::Arguments& args) { LD_RUN_CALLBACK(callback, NULL, 0); } - return v8::Undefined(); + NanReturnUndefined(); } -v8::Handle Database::ApproximateSize (const v8::Arguments& args) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE +NAN_METHOD(Database::ApproximateSize) { + NanScope(); - v8::Local startBufferV = args[0]; - v8::Local endBufferV = args[1]; + v8::Local startHandle = args[0].As(); + v8::Local endHandle = args[1].As(); - if (startBufferV->IsNull() - || startBufferV->IsUndefined() - || startBufferV->IsFunction() // callback in pos 0? - || endBufferV->IsNull() - || endBufferV->IsUndefined() - || endBufferV->IsFunction() // callback in pos 1? + if (startHandle->IsNull() + || startHandle->IsUndefined() + || startHandle->IsFunction() // callback in pos 0? + || endHandle->IsNull() + || endHandle->IsUndefined() + || endHandle->IsFunction() // callback in pos 1? ) { - LD_THROW_RETURN(approximateSize() requires valid `start`, `end` and `callback` arguments) + return NanThrowError("approximateSize() requires valid `start`, `end` and `callback` arguments"); } LD_METHOD_SETUP_COMMON(approximateSize, -1, 2) @@ -524,41 +467,34 @@ v8::Handle Database::ApproximateSize (const v8::Arguments& args) { LD_CB_ERR_IF_NULL_OR_UNDEFINED(args[0], start) LD_CB_ERR_IF_NULL_OR_UNDEFINED(args[1], end) - LD_STRING_OR_BUFFER_TO_SLICE(start, startBufferV, start) - LD_STRING_OR_BUFFER_TO_SLICE(end, endBufferV, end) - - v8::Persistent startBuffer = - v8::Persistent::New(LD_NODE_ISOLATE_PRE startBufferV); - v8::Persistent endBuffer = - v8::Persistent::New(LD_NODE_ISOLATE_PRE endBufferV); + LD_STRING_OR_BUFFER_TO_SLICE(start, startHandle, start) + LD_STRING_OR_BUFFER_TO_SLICE(end, endHandle, end) ApproximateSizeWorker* worker = new ApproximateSizeWorker( database - , v8::Persistent::New(LD_NODE_ISOLATE_PRE callback) + , new NanCallback(callback) , start , end - , startBuffer - , endBuffer + , startHandle + , endHandle ); - AsyncQueueWorker(worker); + NanAsyncQueueWorker(worker); - return v8::Undefined(); + NanReturnUndefined(); } -v8::Handle Database::GetProperty (const v8::Arguments& args) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE +NAN_METHOD(Database::GetProperty) { + NanScope(); - v8::Local propertyV = args[0]; + v8::Local propertyHandle = args[0].As(); v8::Local callback; // for LD_CB_ERR_IF_NULL_OR_UNDEFINED - if (!propertyV->IsString()) { - LD_THROW_RETURN(getProperty() requires a valid `property` argument) - } + if (!propertyHandle->IsString()) + return NanThrowError("getProperty() requires a valid `property` argument"); - LD_CB_ERR_IF_NULL_OR_UNDEFINED(propertyV, property) + LD_CB_ERR_IF_NULL_OR_UNDEFINED(propertyHandle, property) - LD_STRING_OR_BUFFER_TO_SLICE(property, propertyV, property) + LD_STRING_OR_BUFFER_TO_SLICE(property, propertyHandle, property) leveldown::Database* database = node::ObjectWrap::Unwrap(args.This()); @@ -570,12 +506,11 @@ v8::Handle Database::GetProperty (const v8::Arguments& args) { delete value; delete[] property.data(); - return returnValue; + NanReturnValue(returnValue); } -v8::Handle Database::Iterator (const v8::Arguments& args) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE +NAN_METHOD(Database::Iterator) { + NanScope(); Database* database = node::ObjectWrap::Unwrap(args.This()); @@ -588,7 +523,7 @@ v8::Handle Database::Iterator (const v8::Arguments& args) { // easily store & lookup on our `iterators` map uint32_t id = database->currentIteratorId++; v8::TryCatch try_catch; - v8::Handle iterator = Iterator::NewInstance( + v8::Local iteratorHandle = Iterator::NewInstance( args.This() , v8::Number::New(id) , optionsObj @@ -597,12 +532,22 @@ v8::Handle Database::Iterator (const v8::Arguments& args) { node::FatalException(try_catch); } - // register our iterator - database->iterators[id] = - v8::Persistent::New(LD_NODE_ISOLATE_PRE - node::ObjectWrap::Unwrap(iterator)->handle_); + leveldown::Iterator *iterator = + node::ObjectWrap::Unwrap(iteratorHandle); - return scope.Close(iterator); + database->iterators[id] = iterator; + + // register our iterator + /* + v8::Local obj = v8::Object::New(); + obj->Set(NanSymbol("iterator"), iteratorHandle); + v8::Persistent persistent; + persistent.Reset(nan_isolate, obj); + database->iterators.insert(std::pair< uint32_t, v8::Persistent & > + (id, persistent)); + */ + + NanReturnValue(iteratorHandle); } diff --git a/src/database.h b/src/database.h index 821edb17..2310f83c 100644 --- a/src/database.h +++ b/src/database.h @@ -11,38 +11,30 @@ #include #include "leveldb/db.h" - +#include "nan.h" #include "leveldown.h" +#include "iterator.h" namespace leveldown { -LD_SYMBOL ( option_createIfMissing , createIfMissing ); // for open() -LD_SYMBOL ( option_errorIfExists , errorIfExists ); // for open() -LD_SYMBOL ( option_compression , compression ); // for open() -LD_SYMBOL ( option_cacheSize , cacheSize ); // for open() -LD_SYMBOL ( option_writeBufferSize , writeBufferSize ); // for open() -LD_SYMBOL ( option_blockSize , blockSize ); // for open() -LD_SYMBOL ( option_maxOpenFiles , maxOpenFiles ); // for open() -LD_SYMBOL ( option_blockRestartInterval , blockRestartInterval ); // for open() -LD_SYMBOL ( option_sync , sync ); // for put() and delete() -LD_SYMBOL ( option_asBuffer , asBuffer ); // for get() -LD_SYMBOL ( option_fillCache , fillcache ); // for get() and readStream() - -v8::Handle LevelDOWN (const v8::Arguments& args); +NAN_METHOD(LevelDOWN); struct Reference { - v8::Persistent ptr; + v8::Persistent handle; leveldb::Slice slice; - Reference(v8::Persistent ptr, leveldb::Slice slice) : - ptr(ptr) - , slice(slice) { }; + + Reference(v8::Local obj, leveldb::Slice slice) : slice(slice) { + v8::Local _obj = v8::Object::New(); + _obj->Set(NanSymbol("obj"), obj); + NanAssignPersistent(v8::Object, handle, _obj); + }; }; -static inline void ClearReferences (std::vector* references) { - for (std::vector::iterator it = references->begin() +static inline void ClearReferences (std::vector *references) { + for (std::vector::iterator it = references->begin() ; it != references->end() ; ) { - DisposeStringOrBufferFromSlice(it->ptr, it->slice); + DisposeStringOrBufferFromSlice((*it)->handle, (*it)->slice); it = references->erase(it); } delete references; @@ -51,7 +43,7 @@ static inline void ClearReferences (std::vector* references) { class Database : public node::ObjectWrap { public: static void Init (); - static v8::Handle NewInstance (const v8::Arguments& args); + static v8::Handle NewInstance (v8::Local &location); leveldb::Status OpenDatabase (leveldb::Options* options, std::string location); leveldb::Status PutToDatabase ( @@ -90,23 +82,22 @@ class Database : public node::ObjectWrap { uint32_t currentIteratorId; void(*pendingCloseWorker); - std::map< uint32_t, v8::Persistent > iterators; + std::map< uint32_t, leveldown::Iterator * > iterators; - static v8::Persistent constructor; static void WriteDoing(uv_work_t *req); static void WriteAfter(uv_work_t *req); - LD_V8_METHOD( New ) - LD_V8_METHOD( Open ) - LD_V8_METHOD( Close ) - LD_V8_METHOD( Put ) - LD_V8_METHOD( Delete ) - LD_V8_METHOD( Get ) - LD_V8_METHOD( Batch ) - LD_V8_METHOD( Write ) - LD_V8_METHOD( Iterator ) - LD_V8_METHOD( ApproximateSize ) - LD_V8_METHOD( GetProperty ) + static NAN_METHOD(New); + static NAN_METHOD(Open); + static NAN_METHOD(Close); + static NAN_METHOD(Put); + static NAN_METHOD(Delete); + static NAN_METHOD(Get); + static NAN_METHOD(Batch); + static NAN_METHOD(Write); + static NAN_METHOD(Iterator); + static NAN_METHOD(ApproximateSize); + static NAN_METHOD(GetProperty); }; } // namespace leveldown diff --git a/src/database_async.cc b/src/database_async.cc index bc11424d..9f4c47da 100644 --- a/src/database_async.cc +++ b/src/database_async.cc @@ -16,8 +16,8 @@ namespace leveldown { /** OPEN WORKER **/ OpenWorker::OpenWorker ( - Database* database - , v8::Persistent callback + Database *database + , NanCallback *callback , bool createIfMissing , bool errorIfExists , bool compression @@ -46,14 +46,14 @@ OpenWorker::~OpenWorker () { } void OpenWorker::Execute () { - status = database->OpenDatabase(options, database->Location()); + SetStatus(database->OpenDatabase(options, database->Location())); } /** CLOSE WORKER **/ CloseWorker::CloseWorker ( - Database* database - , v8::Persistent callback + Database *database + , NanCallback *callback ) : AsyncWorker(database, callback) {}; @@ -64,46 +64,46 @@ void CloseWorker::Execute () { } void CloseWorker::WorkComplete () { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE + NanScope(); HandleOKCallback(); - callback.Dispose(LD_NODE_ISOLATE); } /** IO WORKER (abstract) **/ IOWorker::IOWorker ( - Database* database - , v8::Persistent callback + Database *database + , NanCallback *callback , leveldb::Slice key - , v8::Persistent keyPtr + , v8::Local &keyHandle ) : AsyncWorker(database, callback) , key(key) - , keyPtr(keyPtr) -{}; +{ + SavePersistent("key", keyHandle); +}; IOWorker::~IOWorker () {} void IOWorker::WorkComplete () { - DisposeStringOrBufferFromSlice(keyPtr, key); + DisposeStringOrBufferFromSlice(GetFromPersistent("key"), key); AsyncWorker::WorkComplete(); } /** READ WORKER **/ ReadWorker::ReadWorker ( - Database* database - , v8::Persistent callback + Database *database + , NanCallback *callback , leveldb::Slice key , bool asBuffer , bool fillCache - , v8::Persistent keyPtr -) : IOWorker(database, callback, key, keyPtr) + , v8::Local &keyHandle +) : IOWorker(database, callback, key, keyHandle) , asBuffer(asBuffer) { options = new leveldb::ReadOptions(); options->fill_cache = fillCache; + SavePersistent("key", keyHandle); }; ReadWorker::~ReadWorker () { @@ -111,16 +111,15 @@ ReadWorker::~ReadWorker () { } void ReadWorker::Execute () { - status = database->GetFromDatabase(options, key, value); + SetStatus(database->GetFromDatabase(options, key, value)); } void ReadWorker::HandleOKCallback () { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE + NanScope(); v8::Local returnValue; if (asBuffer) { - returnValue = LD_NEW_BUFFER_HANDLE((char*)value.data(), value.size()); + returnValue = NanNewBufferHandle((char*)value.data(), value.size()); } else { returnValue = v8::String::New((char*)value.data(), value.size()); } @@ -128,21 +127,22 @@ void ReadWorker::HandleOKCallback () { v8::Local::New(v8::Null()) , returnValue }; - LD_RUN_CALLBACK(callback, argv, 2); + callback->Run(2, argv); } /** DELETE WORKER **/ DeleteWorker::DeleteWorker ( - Database* database - , v8::Persistent callback + Database *database + , NanCallback *callback , leveldb::Slice key , bool sync - , v8::Persistent keyPtr -) : IOWorker(database, callback, key, keyPtr) + , v8::Local &keyHandle +) : IOWorker(database, callback, key, keyHandle) { options = new leveldb::WriteOptions(); options->sync = sync; + SavePersistent("key", keyHandle); }; DeleteWorker::~DeleteWorker () { @@ -150,42 +150,43 @@ DeleteWorker::~DeleteWorker () { } void DeleteWorker::Execute () { - status = database->DeleteFromDatabase(options, key); + SetStatus(database->DeleteFromDatabase(options, key)); } /** WRITE WORKER **/ WriteWorker::WriteWorker ( - Database* database - , v8::Persistent callback + Database *database + , NanCallback *callback , leveldb::Slice key , leveldb::Slice value , bool sync - , v8::Persistent keyPtr - , v8::Persistent valuePtr -) : DeleteWorker(database, callback, key, sync, keyPtr) + , v8::Local &keyHandle + , v8::Local &valueHandle +) : DeleteWorker(database, callback, key, sync, keyHandle) , value(value) - , valuePtr(valuePtr) -{}; +{ + SavePersistent("value", valueHandle); +}; WriteWorker::~WriteWorker () {} void WriteWorker::Execute () { - status = database->PutToDatabase(options, key, value); + SetStatus(database->PutToDatabase(options, key, value)); } void WriteWorker::WorkComplete () { - DisposeStringOrBufferFromSlice(valuePtr, value); + DisposeStringOrBufferFromSlice(GetFromPersistent("value"), value); IOWorker::WorkComplete(); } /** BATCH WORKER **/ BatchWorker::BatchWorker ( - Database* database - , v8::Persistent callback + Database *database + , NanCallback *callback , leveldb::WriteBatch* batch - , std::vector* references + , std::vector* references , bool sync ) : AsyncWorker(database, callback) , batch(batch) @@ -201,23 +202,24 @@ BatchWorker::~BatchWorker () { } void BatchWorker::Execute () { - status = database->WriteBatchToDatabase(options, batch); + SetStatus(database->WriteBatchToDatabase(options, batch)); } /** APPROXIMATE SIZE WORKER **/ ApproximateSizeWorker::ApproximateSizeWorker ( - Database* database - , v8::Persistent callback + Database *database + , NanCallback *callback , leveldb::Slice start , leveldb::Slice end - , v8::Persistent startPtr - , v8::Persistent endPtr + , v8::Local &startHandle + , v8::Local &endHandle ) : AsyncWorker(database, callback) , range(start, end) - , startPtr(startPtr) - , endPtr(endPtr) -{}; +{ + SavePersistent("start", startHandle); + SavePersistent("end", endHandle); +}; ApproximateSizeWorker::~ApproximateSizeWorker () {} @@ -226,21 +228,20 @@ void ApproximateSizeWorker::Execute () { } void ApproximateSizeWorker::WorkComplete() { - DisposeStringOrBufferFromSlice(startPtr, range.start); - DisposeStringOrBufferFromSlice(endPtr, range.limit); + DisposeStringOrBufferFromSlice(GetFromPersistent("start"), range.start); + DisposeStringOrBufferFromSlice(GetFromPersistent("end"), range.limit); AsyncWorker::WorkComplete(); } void ApproximateSizeWorker::HandleOKCallback () { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE + NanScope(); v8::Local returnValue = v8::Number::New((double) size); v8::Local argv[] = { v8::Local::New(v8::Null()) , returnValue }; - LD_RUN_CALLBACK(callback, argv, 2); + callback->Run(2, argv); } } // namespace leveldown diff --git a/src/database_async.h b/src/database_async.h index 80dafff6..909e7e61 100644 --- a/src/database_async.h +++ b/src/database_async.h @@ -18,8 +18,8 @@ namespace leveldown { class OpenWorker : public AsyncWorker { public: OpenWorker ( - Database* database - , v8::Persistent callback + Database *database + , NanCallback *callback , bool createIfMissing , bool errorIfExists , bool compression @@ -40,8 +40,8 @@ class OpenWorker : public AsyncWorker { class CloseWorker : public AsyncWorker { public: CloseWorker ( - Database* database - , v8::Persistent callback + Database *database + , NanCallback *callback ); virtual ~CloseWorker (); @@ -52,10 +52,10 @@ class CloseWorker : public AsyncWorker { class IOWorker : public AsyncWorker { public: IOWorker ( - Database* database - , v8::Persistent callback + Database *database + , NanCallback *callback , leveldb::Slice key - , v8::Persistent keyPtr + , v8::Local &keyHandle ); virtual ~IOWorker (); @@ -63,18 +63,17 @@ class IOWorker : public AsyncWorker { protected: leveldb::Slice key; - v8::Persistent keyPtr; }; class ReadWorker : public IOWorker { public: ReadWorker ( - Database* database - , v8::Persistent callback + Database *database + , NanCallback *callback , leveldb::Slice key , bool asBuffer , bool fillCache - , v8::Persistent keyPtr + , v8::Local &keyHandle ); virtual ~ReadWorker (); @@ -90,11 +89,11 @@ class ReadWorker : public IOWorker { class DeleteWorker : public IOWorker { public: DeleteWorker ( - Database* database - , v8::Persistent callback + Database *database + , NanCallback *callback , leveldb::Slice key , bool sync - , v8::Persistent keyPtr + , v8::Local &keyHandle ); virtual ~DeleteWorker (); @@ -107,13 +106,13 @@ class DeleteWorker : public IOWorker { class WriteWorker : public DeleteWorker { public: WriteWorker ( - Database* database - , v8::Persistent callback + Database *database + , NanCallback *callback , leveldb::Slice key , leveldb::Slice value , bool sync - , v8::Persistent keyPtr - , v8::Persistent valuePtr + , v8::Local &keyHandle + , v8::Local &valueHandle ); virtual ~WriteWorker (); @@ -122,16 +121,15 @@ class WriteWorker : public DeleteWorker { private: leveldb::Slice value; - v8::Persistent valuePtr; }; class BatchWorker : public AsyncWorker { public: BatchWorker ( - Database* database - , v8::Persistent callback + Database *database + , NanCallback *callback , leveldb::WriteBatch* batch - , std::vector* references + , std::vector* references , bool sync ); @@ -141,18 +139,18 @@ class BatchWorker : public AsyncWorker { private: leveldb::WriteOptions* options; leveldb::WriteBatch* batch; - std::vector* references; + std::vector* references; }; class ApproximateSizeWorker : public AsyncWorker { public: ApproximateSizeWorker ( - Database* database - , v8::Persistent callback + Database *database + , NanCallback *callback , leveldb::Slice start , leveldb::Slice end - , v8::Persistent startPtr - , v8::Persistent endPtr + , v8::Local &startHandle + , v8::Local &endHandle ); virtual ~ApproximateSizeWorker (); @@ -162,8 +160,6 @@ class ApproximateSizeWorker : public AsyncWorker { private: leveldb::Range range; - v8::Persistent startPtr; - v8::Persistent endPtr; uint64_t size; }; diff --git a/src/iterator.cc b/src/iterator.cc index 52a24936..545432ff 100644 --- a/src/iterator.cc +++ b/src/iterator.cc @@ -12,6 +12,8 @@ namespace leveldown { +static v8::Persistent iterator_constructor; + Iterator::Iterator ( Database* database , uint32_t id @@ -24,7 +26,7 @@ Iterator::Iterator ( , bool fillCache , bool keyAsBuffer , bool valueAsBuffer - , v8::Persistent startPtr + , v8::Local &startHandle ) : database(database) , id(id) , start(start) @@ -35,8 +37,14 @@ Iterator::Iterator ( , limit(limit) , keyAsBuffer(keyAsBuffer) , valueAsBuffer(valueAsBuffer) - , startPtr(startPtr) { + NanScope(); + + v8::Local obj = v8::Object::New(); + if (!startHandle.IsEmpty()) + obj->Set(NanSymbol("start"), startHandle); + NanAssignPersistent(v8::Object, persistentHandle, obj); + options = new leveldb::ReadOptions(); options->fill_cache = fillCache; dbIterator = NULL; @@ -48,9 +56,9 @@ Iterator::Iterator ( Iterator::~Iterator () { - LD_NODE_ISOLATE_DECL delete options; - startPtr.Dispose(LD_NODE_ISOLATE); + if (!persistentHandle.IsEmpty()) + NanDispose(persistentHandle); if (start != NULL) delete start; if (end != NULL) @@ -121,23 +129,21 @@ void Iterator::Release () { void checkEndCallback (Iterator* iterator) { iterator->nexting = false; if (iterator->endWorker != NULL) { - AsyncQueueWorker(iterator->endWorker); + NanAsyncQueueWorker(iterator->endWorker); iterator->endWorker = NULL; } } //void *ctx, void (*callback)(void *ctx, leveldb::Slice key, leveldb::Slice value) -v8::Handle Iterator::Next (const v8::Arguments& args) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE +NAN_METHOD(Iterator::Next) { + NanScope(); Iterator* iterator = node::ObjectWrap::Unwrap(args.This()); - if (args.Length() == 0 || !args[0]->IsFunction()) { - LD_THROW_RETURN(next() requires a callback argument) - } + if (args.Length() == 0 || !args[0]->IsFunction()) + return NanThrowError("next() requires a callback argument"); - v8::Local callback = v8::Local::Cast(args[0]); + v8::Local callback = args[0].As(); if (iterator->ended) { LD_RETURN_CALLBACK_OR_ERROR(callback, "cannot call next() after end()") @@ -149,24 +155,22 @@ v8::Handle Iterator::Next (const v8::Arguments& args) { NextWorker* worker = new NextWorker( iterator - , v8::Persistent::New(LD_NODE_ISOLATE_PRE callback) + , new NanCallback(callback) , checkEndCallback ); iterator->nexting = true; - AsyncQueueWorker(worker); + NanAsyncQueueWorker(worker); - return scope.Close(args.Holder()); + NanReturnValue(args.Holder()); } -v8::Handle Iterator::End (const v8::Arguments& args) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE +NAN_METHOD(Iterator::End) { + NanScope(); Iterator* iterator = node::ObjectWrap::Unwrap(args.This()); - if (args.Length() == 0 || !args[0]->IsFunction()) { - LD_THROW_RETURN(end() requires a callback argument) - } + if (args.Length() == 0 || !args[0]->IsFunction()) + return NanThrowError("end() requires a callback argument"); v8::Local callback = v8::Local::Cast(args[0]); @@ -176,7 +180,7 @@ v8::Handle Iterator::End (const v8::Arguments& args) { EndWorker* worker = new EndWorker( iterator - , v8::Persistent::New(LD_NODE_ISOLATE_PRE callback) + , new NanCallback(callback) ); iterator->ended = true; @@ -184,64 +188,54 @@ v8::Handle Iterator::End (const v8::Arguments& args) { // waiting for a next() to return, queue the end iterator->endWorker = worker; } else { - AsyncQueueWorker(worker); + NanAsyncQueueWorker(worker); } - return scope.Close(args.Holder()); + NanReturnValue(args.Holder()); } -v8::Persistent Iterator::constructor; - void Iterator::Init () { - LD_NODE_ISOLATE_DECL - v8::Local tpl = v8::FunctionTemplate::New(New); - tpl->SetClassName(v8::String::NewSymbol("Iterator")); + v8::Local tpl = + v8::FunctionTemplate::New(Iterator::New); + NanAssignPersistent(v8::FunctionTemplate, iterator_constructor, tpl); + tpl->SetClassName(NanSymbol("Iterator")); tpl->InstanceTemplate()->SetInternalFieldCount(1); - tpl->PrototypeTemplate()->Set( - v8::String::NewSymbol("next") - , v8::FunctionTemplate::New(Next)->GetFunction() - ); - tpl->PrototypeTemplate()->Set( - v8::String::NewSymbol("end") - , v8::FunctionTemplate::New(End)->GetFunction() - ); - constructor = v8::Persistent::New( - LD_NODE_ISOLATE_PRE - tpl->GetFunction()); + NODE_SET_PROTOTYPE_METHOD(tpl, "next", Iterator::Next); + NODE_SET_PROTOTYPE_METHOD(tpl, "end", Iterator::End); } -v8::Handle Iterator::NewInstance ( - v8::Handle database - , v8::Handle id - , v8::Handle optionsObj +v8::Local Iterator::NewInstance ( + v8::Local database + , v8::Local id + , v8::Local optionsObj ) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE + NanScope(); v8::Local instance; + v8::Local constructorHandle = + NanPersistentToLocal(iterator_constructor); if (optionsObj.IsEmpty()) { v8::Handle argv[2] = { database, id }; - instance = constructor->NewInstance(2, argv); + instance = constructorHandle->GetFunction()->NewInstance(2, argv); } else { v8::Handle argv[3] = { database, id, optionsObj }; - instance = constructor->NewInstance(3, argv); + instance = constructorHandle->GetFunction()->NewInstance(3, argv); } - return scope.Close(instance); + return instance; } -v8::Handle Iterator::New (const v8::Arguments& args) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE +NAN_METHOD(Iterator::New) { + NanScope(); Database* database = node::ObjectWrap::Unwrap(args[0]->ToObject()); //TODO: remove this, it's only here to make LD_STRING_OR_BUFFER_TO_SLICE happy v8::Handle callback; - v8::Local startBuffer; + v8::Local startHandle; leveldb::Slice* start = NULL; std::string* end = NULL; int limit = -1; @@ -253,25 +247,25 @@ v8::Handle Iterator::New (const v8::Arguments& args) { if (args.Length() > 1 && args[2]->IsObject()) { optionsObj = v8::Local::Cast(args[2]); - if (optionsObj->Has(option_start) - && (node::Buffer::HasInstance(optionsObj->Get(option_start)) - || optionsObj->Get(option_start)->IsString())) { + if (optionsObj->Has(NanSymbol("start")) + && (node::Buffer::HasInstance(optionsObj->Get(NanSymbol("start"))) + || optionsObj->Get(NanSymbol("start"))->IsString())) { - startBuffer = v8::Local::New(optionsObj->Get(option_start)); + startHandle = optionsObj->Get(NanSymbol("start")).As(); // ignore start if it has size 0 since a Slice can't have length 0 - if (StringOrBufferLength(startBuffer) > 0) { - LD_STRING_OR_BUFFER_TO_SLICE(_start, startBuffer, start) + if (StringOrBufferLength(startHandle) > 0) { + LD_STRING_OR_BUFFER_TO_SLICE(_start, startHandle, start) start = new leveldb::Slice(_start.data(), _start.size()); } } - if (optionsObj->Has(option_end) - && (node::Buffer::HasInstance(optionsObj->Get(option_end)) - || optionsObj->Get(option_end)->IsString())) { + if (optionsObj->Has(NanSymbol("end")) + && (node::Buffer::HasInstance(optionsObj->Get(NanSymbol("end"))) + || optionsObj->Get(NanSymbol("end"))->IsString())) { v8::Local endBuffer = - v8::Local::New(optionsObj->Get(option_end)); + v8::Local::New(optionsObj->Get(NanSymbol("end"))); // ignore end if it has size 0 since a Slice can't have length 0 if (StringOrBufferLength(endBuffer) > 0) { @@ -280,21 +274,26 @@ v8::Handle Iterator::New (const v8::Arguments& args) { } } - if (!optionsObj.IsEmpty() && optionsObj->Has(option_limit)) { - limit = - v8::Local::Cast(optionsObj->Get(option_limit))->Value(); + if (!optionsObj.IsEmpty() && optionsObj->Has(NanSymbol("limit"))) { + limit = v8::Local::Cast(optionsObj->Get( + NanSymbol("limit")))->Value(); } } - bool reverse = BooleanOptionValue(optionsObj, option_reverse); - bool keys = BooleanOptionValueDefTrue(optionsObj, option_keys); - bool values = BooleanOptionValueDefTrue(optionsObj, option_values); - bool keyAsBuffer = BooleanOptionValueDefTrue(optionsObj, option_keyAsBuffer); - bool valueAsBuffer = BooleanOptionValueDefTrue( + bool reverse = NanBooleanOptionValue(optionsObj, NanSymbol("reverse")); + bool keys = NanBooleanOptionValue(optionsObj, NanSymbol("keys"), true); + bool values = NanBooleanOptionValue(optionsObj, NanSymbol("values"), true); + bool keyAsBuffer = NanBooleanOptionValue( + optionsObj + , NanSymbol("keyAsBuffer") + , true + ); + bool valueAsBuffer = NanBooleanOptionValue( optionsObj - , option_valueAsBuffer + , NanSymbol("valueAsBuffer") + , true ); - bool fillCache = BooleanOptionValue(optionsObj, option_fillCache); + bool fillCache = NanBooleanOptionValue(optionsObj, NanSymbol("fillCache")); Iterator* iterator = new Iterator( database @@ -308,11 +307,11 @@ v8::Handle Iterator::New (const v8::Arguments& args) { , fillCache , keyAsBuffer , valueAsBuffer - , v8::Persistent::New(LD_NODE_ISOLATE_PRE startBuffer) + , startHandle ); iterator->Wrap(args.This()); - return args.This(); + NanReturnValue(args.This()); } } // namespace leveldown diff --git a/src/iterator.h b/src/iterator.h index fe9545c6..4893deae 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -8,30 +8,25 @@ #include +#include "nan.h" #include "leveldown.h" #include "database.h" #include "async.h" namespace leveldown { -LD_SYMBOL ( option_start , start ); -LD_SYMBOL ( option_end , end ); -LD_SYMBOL ( option_limit , limit ); -LD_SYMBOL ( option_reverse , reverse ); -LD_SYMBOL ( option_keys , keys ); -LD_SYMBOL ( option_values , values ); -LD_SYMBOL ( option_keyAsBuffer , keyAsBuffer ); -LD_SYMBOL ( option_valueAsBuffer , valueAsBuffer ); +class Database; +class AsyncWorker; -v8::Handle CreateIterator (const v8::Arguments& args); +v8::Local CreateIterator (const v8::Arguments& args); class Iterator : public node::ObjectWrap { public: static void Init (); - static v8::Handle NewInstance ( - v8::Handle database - , v8::Handle id - , v8::Handle optionsObj + static v8::Local NewInstance ( + v8::Local database + , v8::Local id + , v8::Local optionsObj ); Iterator ( @@ -46,7 +41,7 @@ class Iterator : public node::ObjectWrap { , bool fillCache , bool keyAsBuffer , bool valueAsBuffer - , v8::Persistent startPtr + , v8::Local &startHandle ); ~Iterator (); @@ -77,15 +72,13 @@ class Iterator : public node::ObjectWrap { AsyncWorker* endWorker; private: - v8::Persistent startPtr; + v8::Persistent persistentHandle; bool GetIterator (); - static v8::Persistent constructor; - - LD_V8_METHOD( New ) - LD_V8_METHOD( Next ) - LD_V8_METHOD( End ) + static NAN_METHOD(New); + static NAN_METHOD(Next); + static NAN_METHOD(End); }; } // namespace leveldown diff --git a/src/iterator_async.cc b/src/iterator_async.cc index 5f0e0c8c..f8a58739 100644 --- a/src/iterator_async.cc +++ b/src/iterator_async.cc @@ -17,7 +17,7 @@ namespace leveldown { NextWorker::NextWorker ( Iterator* iterator - , v8::Persistent callback + , NanCallback *callback , void (*localCallback)(Iterator*) ) : AsyncWorker(NULL, callback) , iterator(iterator) @@ -29,23 +29,22 @@ NextWorker::~NextWorker () {} void NextWorker::Execute () { ok = iterator->IteratorNext(key, value); if (!ok) - status = iterator->IteratorStatus(); + SetStatus(iterator->IteratorStatus()); } void NextWorker::HandleOKCallback () { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE + NanScope(); v8::Local returnKey; if (iterator->keyAsBuffer) { - returnKey = LD_NEW_BUFFER_HANDLE((char*)key.data(), key.size()) + returnKey = NanNewBufferHandle((char*)key.data(), key.size()); } else { returnKey = v8::String::New((char*)key.data(), key.size()); } v8::Local returnValue; if (iterator->valueAsBuffer) { - returnValue = LD_NEW_BUFFER_HANDLE((char*)value.data(), value.size()); + returnValue = NanNewBufferHandle((char*)value.data(), value.size()); } else { returnValue = v8::String::New((char*)value.data(), value.size()); } @@ -59,9 +58,9 @@ void NextWorker::HandleOKCallback () { , returnKey , returnValue }; - LD_RUN_CALLBACK(callback, argv, 3); + callback->Run(3, argv); } else { - LD_RUN_CALLBACK(callback, NULL, 0); + callback->Run(0, NULL); } } @@ -69,7 +68,7 @@ void NextWorker::HandleOKCallback () { EndWorker::EndWorker ( Iterator* iterator - , v8::Persistent callback + , NanCallback *callback ) : AsyncWorker(NULL, callback) , iterator(iterator) {}; @@ -82,7 +81,7 @@ void EndWorker::Execute () { void EndWorker::HandleOKCallback () { iterator->Release(); - LD_RUN_CALLBACK(callback, NULL, 0); + callback->Run(0, NULL); } } // namespace leveldown diff --git a/src/iterator_async.h b/src/iterator_async.h index 1c5e3fbb..bbc1fd0b 100644 --- a/src/iterator_async.h +++ b/src/iterator_async.h @@ -8,6 +8,7 @@ #include +#include "nan.h" #include "async.h" #include "iterator.h" @@ -17,7 +18,7 @@ class NextWorker : public AsyncWorker { public: NextWorker ( Iterator* iterator - , v8::Persistent callback + , NanCallback *callback , void (*localCallback)(Iterator*) ); @@ -37,7 +38,7 @@ class EndWorker : public AsyncWorker { public: EndWorker ( Iterator* iterator - , v8::Persistent callback + , NanCallback *callback ); virtual ~EndWorker (); diff --git a/src/leveldown.cc b/src/leveldown.cc index 5a3fc470..28dbf9d6 100644 --- a/src/leveldown.cc +++ b/src/leveldown.cc @@ -13,70 +13,64 @@ namespace leveldown { -v8::Handle DestroyDB (const v8::Arguments& args) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE +NAN_METHOD(DestroyDB) { + NanScope(); if (args.Length() < 2) { - LD_THROW_RETURN(destroy() requires `location` and `callback` arguments) + return NanThrowError("destroy() requires `location` and `callback` arguments"); } if (!args[0]->IsString()) { - LD_THROW_RETURN(destroy() requires a location string argument) + return NanThrowError("destroy() requires a location string argument"); } if (!args[1]->IsFunction()) { - LD_THROW_RETURN(destroy() requires a callback function argument) + return NanThrowError("destroy() requires a callback function argument"); } - char* location = FromV8String(args[0]); + char* location = NanFromV8String(args[0].As()); - v8::Persistent callback = v8::Persistent::New( - LD_NODE_ISOLATE_PRE - v8::Local::Cast(args[1]) - ); + NanCallback* callback = new NanCallback( + v8::Local::Cast(args[1])); DestroyWorker* worker = new DestroyWorker( location , callback ); - AsyncQueueWorker(worker); + NanAsyncQueueWorker(worker); - return scope.Close(v8::Undefined()); + NanReturnUndefined(); } -v8::Handle RepairDB (const v8::Arguments& args) { - LD_NODE_ISOLATE_DECL - LD_HANDLESCOPE +NAN_METHOD(RepairDB) { + NanScope(); if (args.Length() < 2) { - LD_THROW_RETURN(repair() requires `location` and `callback` arguments) + return NanThrowError("repair() requires `location` and `callback` arguments"); } if (!args[0]->IsString()) { - LD_THROW_RETURN(repair() requires a location string argument) + return NanThrowError("repair() requires a location string argument"); } if (!args[1]->IsFunction()) { - LD_THROW_RETURN(repair() requires a callback function argument) + return NanThrowError("repair() requires a callback function argument"); } - char* location = FromV8String(args[0]); + char* location = NanFromV8String(args[0].As()); - v8::Persistent callback = v8::Persistent::New( - LD_NODE_ISOLATE_PRE - v8::Local::Cast(args[1]) - ); + NanCallback* callback = new NanCallback( + v8::Local::Cast(args[1])); RepairWorker* worker = new RepairWorker( location , callback ); - AsyncQueueWorker(worker); + NanAsyncQueueWorker(worker); - return scope.Close(v8::Undefined()); + NanReturnUndefined(); } void Init (v8::Handle target) { @@ -88,16 +82,16 @@ void Init (v8::Handle target) { v8::FunctionTemplate::New(LevelDOWN)->GetFunction(); leveldown->Set( - v8::String::NewSymbol("destroy") + NanSymbol("destroy") , v8::FunctionTemplate::New(DestroyDB)->GetFunction() ); leveldown->Set( - v8::String::NewSymbol("repair") + NanSymbol("repair") , v8::FunctionTemplate::New(RepairDB)->GetFunction() ); - target->Set(v8::String::NewSymbol("leveldown"), leveldown); + target->Set(NanSymbol("leveldown"), leveldown); } NODE_MODULE(leveldown, Init) diff --git a/src/leveldown.h b/src/leveldown.h index 371be349..b4e06ec7 100644 --- a/src/leveldown.h +++ b/src/leveldown.h @@ -9,15 +9,7 @@ #include #include -static inline char* FromV8String(v8::Local from) { - size_t sz_; - char* to; - v8::Local toStr = from->ToString(); - sz_ = toStr->Utf8Length(); - to = new char[sz_ + 1]; - toStr->WriteUtf8(to, -1, NULL, v8::String::NO_OPTIONS); - return to; -} +#include "nan.h" static inline size_t StringOrBufferLength(v8::Local obj) { return node::Buffer::HasInstance(obj->ToObject()) @@ -25,86 +17,30 @@ static inline size_t StringOrBufferLength(v8::Local obj) { : obj->ToString()->Utf8Length(); } -static inline bool BooleanOptionValue( - v8::Local optionsObj - , v8::Handle opt) { - - return !optionsObj.IsEmpty() - && optionsObj->Has(opt) - && optionsObj->Get(opt)->BooleanValue(); -} - -static inline bool BooleanOptionValueDefTrue( - v8::Local optionsObj - , v8::Handle opt) { +// NOTE: this MUST be called on objects created by +// LD_STRING_OR_BUFFER_TO_SLICE +static inline void DisposeStringOrBufferFromSlice( + v8::Persistent &handle + , leveldb::Slice slice) { - return optionsObj.IsEmpty() - || !optionsObj->Has(opt) - || optionsObj->Get(opt)->BooleanValue(); + if (!node::Buffer::HasInstance(NanPersistentToLocal(handle)->Get(NanSymbol("obj")))) + delete[] slice.data(); + NanDispose(handle); } -static inline uint32_t UInt32OptionValue( - v8::Local optionsObj - , v8::Handle opt - , uint32_t def) { +static inline void DisposeStringOrBufferFromSlice( + v8::Local handle + , leveldb::Slice slice) { - return !optionsObj.IsEmpty() - && optionsObj->Has(opt) - && optionsObj->Get(opt)->IsUint32() - ? optionsObj->Get(opt)->Uint32Value() - : def; + if (!node::Buffer::HasInstance(handle)) + delete[] slice.data(); } -// V8 Isolate stuff introduced with V8 upgrade, see https://github.com/joyent/node/pull/5077 -#if (NODE_MODULE_VERSION > 0x000B) -# define LD_NODE_ISOLATE_GET v8::Isolate::GetCurrent() -# define LD_NODE_ISOLATE_DECL v8::Isolate* isolate = LD_NODE_ISOLATE_GET; -# define LD_NODE_ISOLATE isolate -# define LD_NODE_ISOLATE_PRE isolate, -# define LD_NODE_ISOLATE_POST , isolate -#else -# define LD_NODE_ISOLATE_GET -# define LD_NODE_ISOLATE_DECL -# define LD_NODE_ISOLATE -# define LD_NODE_ISOLATE_PRE -# define LD_NODE_ISOLATE_POST -#endif - -#if (NODE_MODULE_VERSION > 0x000B) -# define LD_SYMBOL(var, key) \ - static const v8::Persistent var = \ - v8::Persistent::New( \ - LD_NODE_ISOLATE_GET, v8::String::NewSymbol(#key)); -# define LD_HANDLESCOPE v8::HandleScope scope(LD_NODE_ISOLATE); -# define LD_NEW_BUFFER_HANDLE(data, size) node::Buffer::New(data, size); -#else -# define LD_SYMBOL(var, key) \ - static const v8::Persistent var = \ - v8::Persistent::New(v8::String::NewSymbol(#key)); -# define LD_HANDLESCOPE v8::HandleScope scope; -# define LD_NEW_BUFFER_HANDLE(data, size) \ - v8::Local::New(node::Buffer::New(data, size)->handle_); -#endif - -#define LD_V8_METHOD(name) \ - static v8::Handle name (const v8::Arguments& args); - #define LD_CB_ERR_IF_NULL_OR_UNDEFINED(thing, name) \ if (thing->IsNull() || thing->IsUndefined()) { \ LD_RETURN_CALLBACK_OR_ERROR(callback, #name " cannot be `null` or `undefined`") \ } -// NOTE: this MUST be called on objects created by -// LD_STRING_OR_BUFFER_TO_SLICE -static inline void DisposeStringOrBufferFromSlice(v8::Persistent ptr - , leveldb::Slice slice) { - - LD_NODE_ISOLATE_DECL - if (!node::Buffer::HasInstance(ptr)) - delete[] slice.data(); - ptr.Dispose(LD_NODE_ISOLATE); -} - // NOTE: must call DisposeStringOrBufferFromSlice() on objects created here #define LD_STRING_OR_BUFFER_TO_SLICE(to, from, name) \ size_t to ## Sz_; \ @@ -138,10 +74,9 @@ static inline void DisposeStringOrBufferFromSlice(v8::Persistent ptr ) \ }; \ LD_RUN_CALLBACK(callback, argv, 1) \ - return v8::Undefined(); \ + NanReturnUndefined(); \ } \ - v8::ThrowException(v8::Exception::Error(v8::String::New(msg))); \ - return v8::Undefined(); + return NanThrowError(msg); #define LD_RUN_CALLBACK(callback, argv, length) \ v8::TryCatch try_catch; \ @@ -150,35 +85,30 @@ static inline void DisposeStringOrBufferFromSlice(v8::Persistent ptr node::FatalException(try_catch); \ } -#define LD_THROW_RETURN(...) \ - v8::ThrowException(v8::Exception::Error(v8::String::New(#__VA_ARGS__))); \ - return v8::Undefined(); - /* LD_METHOD_SETUP_COMMON setup the following objects: * - Database* database * - v8::Local optionsObj (may be empty) * - v8::Persistent callback (won't be empty) - * Will LD_THROW_RETURN if there isn't a callback in arg 0 or 1 + * Will throw/return if there isn't a callback in arg 0 or 1 */ #define LD_METHOD_SETUP_COMMON(name, optionPos, callbackPos) \ - if (args.Length() == 0) { \ - LD_THROW_RETURN(name() requires a callback argument) \ - } \ + if (args.Length() == 0) \ + return NanThrowError(#name "() requires a callback argument"); \ leveldown::Database* database = \ node::ObjectWrap::Unwrap(args.This()); \ v8::Local optionsObj; \ v8::Local callback; \ if (optionPos == -1 && args[callbackPos]->IsFunction()) { \ - callback = v8::Local::Cast(args[callbackPos]); \ + callback = args[callbackPos].As(); \ } else if (optionPos != -1 && args[callbackPos - 1]->IsFunction()) { \ - callback = v8::Local::Cast(args[callbackPos - 1]); \ + callback = args[callbackPos - 1].As(); \ } else if (optionPos != -1 \ && args[optionPos]->IsObject() \ && args[callbackPos]->IsFunction()) { \ - optionsObj = v8::Local::Cast(args[optionPos]); \ - callback = v8::Local::Cast(args[callbackPos]); \ + optionsObj = args[optionPos].As(); \ + callback = args[callbackPos].As(); \ } else { \ - LD_THROW_RETURN(name() requires a callback argument) \ + return NanThrowError(#name "() requires a callback argument"); \ } #define LD_METHOD_SETUP_COMMON_ONEARG(name) LD_METHOD_SETUP_COMMON(name, -1, 0) diff --git a/src/leveldown_async.cc b/src/leveldown_async.cc index 008f4250..db84c1ff 100644 --- a/src/leveldown_async.cc +++ b/src/leveldown_async.cc @@ -14,7 +14,7 @@ namespace leveldown { DestroyWorker::DestroyWorker ( char* location - , v8::Persistent callback + , NanCallback *callback ) : AsyncWorker(NULL, callback) , location(location) {}; @@ -25,14 +25,14 @@ DestroyWorker::~DestroyWorker () { void DestroyWorker::Execute () { leveldb::Options options; - status = leveldb::DestroyDB(location, options); + SetStatus(leveldb::DestroyDB(location, options)); } /** REPAIR WORKER **/ RepairWorker::RepairWorker ( char* location - , v8::Persistent callback + , NanCallback *callback ) : AsyncWorker(NULL, callback) , location(location) {}; @@ -43,7 +43,7 @@ RepairWorker::~RepairWorker () { void RepairWorker::Execute () { leveldb::Options options; - status = leveldb::RepairDB(location, options); + SetStatus(leveldb::RepairDB(location, options)); } } // namespace leveldown diff --git a/src/leveldown_async.h b/src/leveldown_async.h index 60337695..d045a81c 100644 --- a/src/leveldown_async.h +++ b/src/leveldown_async.h @@ -16,7 +16,7 @@ class DestroyWorker : public AsyncWorker { public: DestroyWorker ( char* location - , v8::Persistent callback + , NanCallback *callback ); virtual ~DestroyWorker (); @@ -30,7 +30,7 @@ class RepairWorker : public AsyncWorker { public: RepairWorker ( char* location - , v8::Persistent callback + , NanCallback *callback ); virtual ~RepairWorker (); diff --git a/src/nan.h b/src/nan.h new file mode 100644 index 00000000..c3a6592a --- /dev/null +++ b/src/nan.h @@ -0,0 +1,728 @@ +/********************************************************************************** + * NAN - Native Abstractions for Node.js + * + * Copyright (c) 2013 NAN contributors: + * - Rod Vagg + * - Benjamin Byholm + * - Trevor Norris + * + * MIT +no-false-attribs License + * + * Version 0.2.1 (current Node unstable: 0.11.4) + * + * ChangeLog: + * * 0.2.1 Aug 5 2013 + * - Fixed 0.8 breakage, node::BUFFER encoding type not available in 0.8 for + * NanFromV8String() + * + * * 0.2.0 Aug 5 2013 + * - Added NAN_PROPERTY_GETTER, NAN_PROPERTY_SETTER, NAN_PROPERTY_ENUMERATOR, + * NAN_PROPERTY_DELETER, NAN_PROPERTY_QUERY + * - Extracted _NAN_METHOD_ARGS, _NAN_GETTER_ARGS, _NAN_SETTER_ARGS, + * _NAN_PROPERTY_GETTER_ARGS, _NAN_PROPERTY_SETTER_ARGS, + * _NAN_PROPERTY_ENUMERATOR_ARGS, _NAN_PROPERTY_DELETER_ARGS, + * _NAN_PROPERTY_QUERY_ARGS + * - Added NanGetInternalFieldPointer, NanSetInternalFieldPointer + * - Added NAN_WEAK_CALLBACK, NAN_WEAK_CALLBACK_OBJECT, + * NAN_WEAK_CALLBACK_DATA, NanMakeWeak + * - Renamed THROW_ERROR to _NAN_THROW_ERROR + * - Added NanNewBufferHandle(char*, size_t, node::smalloc::FreeCallback, void*) + * - Added NanBufferUse(char*, uint32_t) + * - Added NanNewContextHandle(v8::ExtensionConfiguration*, + * v8::Handle, v8::Handle) + * - Fixed broken NanCallback#GetFunction() + * - Added optional encoding and size arguments to NanFromV8String() + * - Added NanGetPointerSafe() and NanSetPointerSafe() + * - Added initial test suite (to be expanded) + * - Allow NanUInt32OptionValue to convert any Number object + * + * * 0.1.0 Jul 21 2013 + * - Added `NAN_GETTER`, `NAN_SETTER` + * - Added `NanThrowError` with single Local argument + * - Added `NanNewBufferHandle` with single uint32_t argument + * - Added `NanHasInstance(Persistent&, Handle)` + * - Added `Local NanCallback#GetFunction()` + * - Added `NanCallback#Call(int, Local[])` + * - Deprecated `NanCallback#Run(int, Local[])` in favour of Call + * + * See https://github.com/rvagg/nan for the latest update to this file + **********************************************************************************/ + +#ifndef NAN_H +#define NAN_H + +#include +#include +#include + +// some generic helpers + +template static inline bool NanSetPointerSafe(T *var, T val) { + if (var) { + *var = val; + return true; + } else { + return false; + } +} + +template static inline T NanGetPointerSafe(T *var, T fallback = reinterpret_cast(0)) { + if (var) { + return *var; + } else { + return fallback; + } +} + +#define NanSymbol(value) v8::String::NewSymbol(value) + +static inline bool NanBooleanOptionValue( + v8::Local optionsObj + , v8::Handle opt, bool def) { + + if (def) { + return optionsObj.IsEmpty() + || !optionsObj->Has(opt) + || optionsObj->Get(opt)->BooleanValue(); + } else { + return !optionsObj.IsEmpty() + && optionsObj->Has(opt) + && optionsObj->Get(opt)->BooleanValue(); + } +} + +static inline bool NanBooleanOptionValue( + v8::Local optionsObj + , v8::Handle opt) { + return NanBooleanOptionValue(optionsObj, opt, false); +} + +static inline uint32_t NanUInt32OptionValue( + v8::Local optionsObj + , v8::Handle opt + , uint32_t def) { + + return !optionsObj.IsEmpty() + && optionsObj->Has(opt) + && optionsObj->Get(opt)->IsNumber() + ? optionsObj->Get(opt)->Uint32Value() + : def; +} + +#if (NODE_MODULE_VERSION > 0x000B) +// Node 0.11+ (0.11.3 and below won't compile with these) + +static v8::Isolate* nan_isolate = v8::Isolate::GetCurrent(); + +# define _NAN_METHOD_ARGS const v8::FunctionCallbackInfo& args +# define NAN_METHOD(name) void name(_NAN_METHOD_ARGS) +# define _NAN_GETTER_ARGS const v8::PropertyCallbackInfo& args +# define NAN_GETTER(name) \ + void name(v8::Local property, _NAN_GETTER_ARGS) +# define _NAN_SETTER_ARGS const v8::PropertyCallbackInfo& args +# define NAN_SETTER(name) \ + void name( \ + v8::Local property \ + , v8::Local value \ + , _NAN_SETTER_ARGS) +# define _NAN_PROPERTY_GETTER_ARGS \ + const v8::PropertyCallbackInfo& args +# define NAN_PROPERTY_GETTER(name) \ + void name(v8::Local property \ + , _NAN_PROPERTY_GETTER_ARGS) +# define _NAN_PROPERTY_SETTER_ARGS \ + const v8::PropertyCallbackInfo& args +# define NAN_PROPERTY_SETTER(name) \ + void name(v8::Local property \ + , v8::Local value \ + , _NAN_PROPERTY_SETTER_ARGS) +# define _NAN_PROPERTY_ENUMERATOR_ARGS \ + const v8::PropertyCallbackInfo& args +# define NAN_PROPERTY_ENUMERATOR(name) \ + void name(_NAN_PROPERTY_ENUMERATOR_ARGS) +# define _NAN_PROPERTY_DELETER_ARGS \ + const v8::PropertyCallbackInfo& args +# define NAN_PROPERTY_DELETER(name) \ + void name( \ + v8::Local property \ + , _NAN_PROPERTY_DELETER_ARGS) +# define _NAN_PROPERTY_QUERY_ARGS \ + const v8::PropertyCallbackInfo& args +# define NAN_PROPERTY_QUERY(name) \ + void name(v8::Local property, _NAN_PROPERTY_QUERY_ARGS) +# define NanGetInternalFieldPointer(object, index) \ + object->GetAlignedPointerFromInternalField(index) +# define NanSetInternalFieldPointer(object, index, value) \ + object->SetAlignedPointerInInternalField(index, value) + +# define NAN_WEAK_CALLBACK(type, name) \ + void name( \ + v8::Isolate* isolate, \ + v8::Persistent* object, \ + type data) +# define NAN_WEAK_CALLBACK_OBJECT (*object) +# define NAN_WEAK_CALLBACK_DATA(type) ((type) data) + +# define NanScope() v8::HandleScope scope(nan_isolate) +# define NanReturnValue(value) return args.GetReturnValue().Set(value) +# define NanReturnUndefined() return +# define NanAssignPersistent(type, handle, obj) handle.Reset(nan_isolate, obj) +# define NanObjectWrapHandle(obj) obj->handle() +# define NanMakeWeak(handle, parameter, callback) \ + handle.MakeWeak(nan_isolate, parameter, callback) + +# define _NAN_THROW_ERROR(fun, errmsg) \ + do { \ + NanScope(); \ + v8::ThrowException(fun(v8::String::New(errmsg))); \ + } while (0); + + inline static void NanThrowError(const char* errmsg) { + _NAN_THROW_ERROR(v8::Exception::Error, errmsg); + } + + inline static void NanThrowError(v8::Local error) { + NanScope(); + v8::ThrowException(error); + } + + inline static void NanThrowTypeError(const char* errmsg) { + _NAN_THROW_ERROR(v8::Exception::TypeError, errmsg); + } + + inline static void NanThrowRangeError(const char* errmsg) { + _NAN_THROW_ERROR(v8::Exception::RangeError, errmsg); + } + + template static inline void NanDispose(v8::Persistent &handle) { + handle.Dispose(nan_isolate); + } + + static inline v8::Local NanNewBufferHandle ( + char *data, + size_t length, + node::smalloc::FreeCallback callback, + void *hint) { + return node::Buffer::New(data, length, callback, hint); + } + + static inline v8::Local NanNewBufferHandle ( + char *data, uint32_t size) { + return node::Buffer::New(data, size); + } + + static inline v8::Local NanNewBufferHandle (uint32_t size) { + return node::Buffer::New(size); + } + + static inline v8::Local NanBufferUse(char* data, uint32_t size) { + return node::Buffer::Use(data, size); + } + + template + inline v8::Local NanPersistentToLocal( + const v8::Persistent& persistent) { + if (persistent.IsWeak()) { + return v8::Local::New(nan_isolate, persistent); + } else { + return *reinterpret_cast*>( + const_cast*>(&persistent)); + } + } + + inline bool NanHasInstance( + v8::Persistent& function_template + , v8::Handle value) { + return NanPersistentToLocal(function_template)->HasInstance(value); + } + + static inline v8::Local NanNewContextHandle( + v8::ExtensionConfiguration* extensions = NULL, + v8::Handle g_template = v8::Handle(), + v8::Handle g_object = v8::Handle()) { + return v8::Local::New(nan_isolate, v8::Context::New( + nan_isolate, extensions, g_template, g_object)); + } + +#else +// Node 0.8 and 0.10 + +# define _NAN_METHOD_ARGS const v8::Arguments& args +# define NAN_METHOD(name) v8::Handle name(_NAN_METHOD_ARGS) +# define _NAN_GETTER_ARGS const v8::AccessorInfo &args +# define NAN_GETTER(name) \ + v8::Handle name(v8::Local property, _NAN_GETTER_ARGS) +# define _NAN_SETTER_ARGS const v8::AccessorInfo &args +# define NAN_SETTER(name) \ + void name( \ + v8::Local property \ + , v8::Local value \ + , _NAN_SETTER_ARGS) +# define _NAN_PROPERTY_GETTER_ARGS const v8::AccessorInfo& args +# define NAN_PROPERTY_GETTER(name) \ + v8::Handle name(v8::Local property \ + , _NAN_PROPERTY_GETTER_ARGS) +# define _NAN_PROPERTY_SETTER_ARGS const v8::AccessorInfo& args +# define NAN_PROPERTY_SETTER(name) \ + v8::Handle name(v8::Local property \ + , v8::Local value \ + , _NAN_PROPERTY_SETTER_ARGS) +# define _NAN_PROPERTY_ENUMERATOR_ARGS const v8::AccessorInfo& args +# define NAN_PROPERTY_ENUMERATOR(name) \ + v8::Handle name(_NAN_PROPERTY_ENUMERATOR_ARGS) +# define _NAN_PROPERTY_DELETER_ARGS const v8::AccessorInfo& args +# define NAN_PROPERTY_DELETER(name) \ + v8::Handle name( \ + v8::Local property \ + , _NAN_PROPERTY_DELETER_ARGS) +# define _NAN_PROPERTY_QUERY_ARGS const v8::AccessorInfo& args +# define NAN_PROPERTY_QUERY(name) \ + v8::Handle name( \ + v8::Local property \ + , _NAN_PROPERTY_QUERY_ARGS) + +# define NanGetInternalFieldPointer(object, index) \ + object->GetPointerFromInternalField(index) +# define NanSetInternalFieldPointer(object, index, value) \ + object->SetPointerInInternalField(index, value) +# define NAN_WEAK_CALLBACK(type, name) void name( \ + v8::Persistent object, \ + void *data) +# define NAN_WEAK_CALLBACK_OBJECT object +# define NAN_WEAK_CALLBACK_DATA(type) ((type) data) + +# define NanScope() v8::HandleScope scope +# define NanReturnValue(value) return scope.Close(value) +# define NanReturnUndefined() return v8::Undefined() +# define NanAssignPersistent(type, handle, obj) \ + handle = v8::Persistent::New(obj) +# define NanObjectWrapHandle(obj) obj->handle_ +# define NanMakeWeak(handle, parameters, callback) \ + handle.MakeWeak(parameters, callback) + +# define _NAN_THROW_ERROR(fun, errmsg) \ + do { \ + NanScope(); \ + return v8::ThrowException(fun(v8::String::New(errmsg))); \ + } while (0); + + inline static v8::Handle NanThrowError(const char* errmsg) { + _NAN_THROW_ERROR(v8::Exception::Error, errmsg); + } + + inline static v8::Handle NanThrowError( + v8::Local error) { + NanScope(); + return v8::ThrowException(error); + } + + inline static v8::Handle NanThrowTypeError(const char* errmsg) { + _NAN_THROW_ERROR(v8::Exception::TypeError, errmsg); + } + + inline static v8::Handle NanThrowRangeError(const char* errmsg) { + _NAN_THROW_ERROR(v8::Exception::RangeError, errmsg); + } + + template static inline void NanDispose(v8::Persistent &handle) { + handle.Dispose(); + } + + static inline v8::Local NanNewBufferHandle ( + char *data, + size_t length, + node::Buffer::free_callback callback, + void *hint) { + return v8::Local::New( + node::Buffer::New(data, length, callback, hint)->handle_); + } + + static inline v8::Local NanNewBufferHandle ( + char *data, uint32_t size) { + return v8::Local::New(node::Buffer::New(data, size)->handle_); + } + + static inline v8::Local NanNewBufferHandle (uint32_t size) { + return v8::Local::New(node::Buffer::New(size)->handle_); + } + + static inline void FreeData(char *data, void *hint) { + delete[] data; + } + + static inline v8::Local NanBufferUse(char* data, uint32_t size) { + return v8::Local::New( + node::Buffer::New(data, size, FreeData, NULL)->handle_); + } + + template + inline v8::Local NanPersistentToLocal( + const v8::Persistent& persistent) { + if (persistent.IsWeak()) { + return v8::Local::New(persistent); + } else { + return *reinterpret_cast*>( + const_cast*>(&persistent)); + } + } + + inline bool NanHasInstance( + v8::Persistent& function_template + , v8::Handle value) { + return function_template->HasInstance(value); + } + + static inline v8::Local NanNewContextHandle( + v8::ExtensionConfiguration* extensions = NULL + , v8::Handle g_template = + v8::Handle() + , v8::Handle g_object = v8::Handle() + ) { + v8::Persistent ctx = + v8::Context::New(extensions, g_template, g_object); + v8::Local lctx = v8::Local::New(ctx); + ctx.Dispose(); + return lctx; + } + +#endif // node version + +class NanCallback { + public: + NanCallback(const v8::Local &fn) { + NanScope(); + v8::Local obj = v8::Object::New(); + obj->Set(NanSymbol("callback"), fn); + NanAssignPersistent(v8::Object, handle, obj); + } + + ~NanCallback() { + if (handle.IsEmpty()) return; + handle.Dispose(); + } + + inline v8::Local GetFunction () { + return NanPersistentToLocal(handle)->Get(NanSymbol("callback")) + .As(); + } + + // deprecated + void Run(int argc, v8::Local argv[]) { + Call(argc, argv); + } + + void Call(int argc, v8::Local argv[]) { + NanScope(); + v8::Local callback = NanPersistentToLocal(handle)-> + Get(NanSymbol("callback")).As(); + v8::TryCatch try_catch; + callback->Call(v8::Context::GetCurrent()->Global(), argc, argv); + if (try_catch.HasCaught()) { + node::FatalException(try_catch); + } + } + + private: + v8::Persistent handle; +}; + +/* abstract */ class NanAsyncWorker { +public: + NanAsyncWorker (NanCallback *callback) : callback(callback) { + request.data = this; + errmsg = NULL; + } + + virtual ~NanAsyncWorker () { + if (!persistentHandle.IsEmpty()) + NanDispose(persistentHandle); + if (callback) + delete callback; + if (errmsg) + delete errmsg; + } + + virtual void WorkComplete () { + NanScope(); + + if (errmsg == NULL) + HandleOKCallback(); + else + HandleErrorCallback(); + delete callback; + callback = NULL; + } + + virtual void Execute () =0; + + uv_work_t request; + +protected: + v8::Persistent persistentHandle; + NanCallback *callback; + const char *errmsg; + + void SavePersistent(const char *key, v8::Local &obj) { + v8::Local handle = NanPersistentToLocal(persistentHandle); + handle->Set(NanSymbol(key), obj); + } + + v8::Local GetFromPersistent(const char *key) { + v8::Local handle = NanPersistentToLocal(persistentHandle); + return handle->Get(NanSymbol(key)).As(); + } + + virtual void HandleOKCallback () { + NanScope(); + + callback->Call(0, NULL); + }; + + virtual void HandleErrorCallback () { + NanScope(); + + v8::Local argv[] = { + v8::Exception::Error(v8::String::New(errmsg)) + }; + callback->Call(1, argv); + } +}; + +inline void NanAsyncExecute (uv_work_t* req) { + NanAsyncWorker *worker = static_cast(req->data); + worker->Execute(); +} + +inline void NanAsyncExecuteComplete (uv_work_t* req) { + NanAsyncWorker* worker = static_cast(req->data); + worker->WorkComplete(); + delete worker; +} + +inline void NanAsyncQueueWorker (NanAsyncWorker* worker) { + uv_queue_work( + uv_default_loop() + , &worker->request + , NanAsyncExecute + , (uv_after_work_cb)NanAsyncExecuteComplete + ); +} + +//// Base 64 //// + +#define _nan_base64_encoded_size(size) ((size + 2 - ((size + 2) % 3)) / 3 * 4) + + +// Doesn't check for padding at the end. Can be 1-2 bytes over. +static inline size_t _nan_base64_decoded_size_fast(size_t size) { + size_t remainder = size % 4; + + size = (size / 4) * 3; + if (remainder) { + if (size == 0 && remainder == 1) { + // special case: 1-byte input cannot be decoded + size = 0; + } else { + // non-padded input, add 1 or 2 extra bytes + size += 1 + (remainder == 3); + } + } + + return size; +} + +template +static size_t _nan_base64_decoded_size(const TypeName* src, size_t size) { + if (size == 0) + return 0; + + if (src[size - 1] == '=') + size--; + if (size > 0 && src[size - 1] == '=') + size--; + + return _nan_base64_decoded_size_fast(size); +} + + +// supports regular and URL-safe base64 +static const int _nan_unbase64_table[] = + { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -1, -1, -2, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + +#define _nan_unbase64(x) _nan_unbase64_table[(uint8_t)(x)] + + +template +static size_t _nan_base64_decode(char* buf, + size_t len, + const TypeName* src, + const size_t srcLen) { + char a, b, c, d; + char* dst = buf; + char* dstEnd = buf + len; + const TypeName* srcEnd = src + srcLen; + + while (src < srcEnd && dst < dstEnd) { + int remaining = srcEnd - src; + + while (_nan_unbase64(*src) < 0 && src < srcEnd) src++, remaining--; + if (remaining == 0 || *src == '=') break; + a = _nan_unbase64(*src++); + + while (_nan_unbase64(*src) < 0 && src < srcEnd) src++, remaining--; + if (remaining <= 1 || *src == '=') break; + b = _nan_unbase64(*src++); + + *dst++ = (a << 2) | ((b & 0x30) >> 4); + if (dst == dstEnd) break; + + while (_nan_unbase64(*src) < 0 && src < srcEnd) src++, remaining--; + if (remaining <= 2 || *src == '=') break; + c = _nan_unbase64(*src++); + + *dst++ = ((b & 0x0F) << 4) | ((c & 0x3C) >> 2); + if (dst == dstEnd) break; + + while (_nan_unbase64(*src) < 0 && src < srcEnd) src++, remaining--; + if (remaining <= 3 || *src == '=') break; + d = _nan_unbase64(*src++); + + *dst++ = ((c & 0x03) << 6) | (d & 0x3F); + } + + return dst - buf; +} + +//// HEX //// + +template +unsigned _nan_hex2bin(TypeName c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'A' && c <= 'F') return 10 + (c - 'A'); + if (c >= 'a' && c <= 'f') return 10 + (c - 'a'); + return static_cast(-1); +} + + +template +static size_t _nan_hex_decode(char* buf, + size_t len, + const TypeName* src, + const size_t srcLen) { + size_t i; + for (i = 0; i < len && i * 2 + 1 < srcLen; ++i) { + unsigned a = _nan_hex2bin(src[i * 2 + 0]); + unsigned b = _nan_hex2bin(src[i * 2 + 1]); + if (!~a || !~b) return i; + buf[i] = a * 16 + b; + } + + return i; +} + +static bool _NanGetExternalParts(v8::Handle val, const char** data, size_t* len) { + if (node::Buffer::HasInstance(val)) { + *data = node::Buffer::Data(val); + *len = node::Buffer::Length(val); + return true; + + } + + assert(val->IsString()); + v8::Local str = v8::Local::New(val.As()); + + if (str->IsExternalAscii()) { + const v8::String::ExternalAsciiStringResource* ext; + ext = str->GetExternalAsciiStringResource(); + *data = ext->data(); + *len = ext->length(); + return true; + + } else if (str->IsExternal()) { + const v8::String::ExternalStringResource* ext; + ext = str->GetExternalStringResource(); + *data = reinterpret_cast(ext->data()); + *len = ext->length(); + return true; + } + + return false; +} + +static inline char* NanFromV8String( + v8::Local from + , enum node::encoding encoding = node::UTF8 + , size_t *datalen = NULL) { + + NanScope(); + + size_t sz_; + char *data = NULL; + size_t len; + bool is_extern = _NanGetExternalParts(from, const_cast(&data), &len); + + if (is_extern) { + NanSetPointerSafe(datalen, len); + return data; + } + + assert(from->IsString()); + v8::Local toStr = from.As(); + + int flags = v8::String::NO_NULL_TERMINATION | v8::String::HINT_MANY_WRITES_EXPECTED; + + char *to; + v8::String::AsciiValue value(toStr); + switch(encoding) { + case node::ASCII: + case node::BINARY: + #if (NODE_MODULE_VERSION > 0x000B) + case node::BUFFER: + sz_ = toStr->Length(); + to = new char[sz_]; + NanSetPointerSafe(datalen, toStr->WriteOneByte(reinterpret_cast(to), 0, sz_, flags)); + return to; + #endif + case node::UTF8: + sz_ = toStr->Utf8Length(); + to = new char[sz_]; + NanSetPointerSafe(datalen, toStr->WriteUtf8(to)); + return to; + case node::BASE64: + sz_ = _nan_base64_decoded_size(*value, sz_); + to = new char[sz_]; + NanSetPointerSafe(datalen, _nan_base64_decode(to, sz_, *value, value.length())); + return to; + case node::UCS2: + sz_ = toStr->Length(); + to = new char[sz_ * 2]; + NanSetPointerSafe(datalen, toStr->Write(reinterpret_cast(to), 0, sz_, flags)); + return to; + case node::HEX: + sz_ = toStr->Length(); + assert(!(sz_ & 1) && "bad hex data"); + to = new char[sz_ / 2]; + NanSetPointerSafe(datalen, _nan_hex_decode(to, sz_ / 2, *value, value.length())); + return to; + default: + assert(0 && "unknown encoding"); + } +} + +#endif