[Pkg-javascript-commits] [node-leveldown] 03/10: Imported Upstream version 1.0.0+dfsg

Andrew Kelley andrewrk-guest at moszumanska.debian.org
Fri Sep 12 17:01:00 UTC 2014


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

andrewrk-guest pushed a commit to branch master
in repository node-leveldown.

commit a90c230b7e2cea4dfb923e354438962759041d70
Author: Andrew Kelley <superjoe30 at gmail.com>
Date:   Fri Sep 12 15:41:14 2014 +0000

    Imported Upstream version 1.0.0+dfsg
---
 .dntrc                          |  12 ++-
 .npmignore                      |   9 ++-
 .travis.yml                     |   5 +-
 CHANGELOG.md                    |  11 +++
 LICENSE                         |  39 ----------
 LICENSE.md                      |  11 +++
 README.md                       |  26 ++++---
 binding.gyp                     |   2 +-
 buster.js                       |   7 --
 chained-batch.js                |  33 +++++++++
 index.js                        |   1 -
 iterator.js                     |  54 ++++++++++++++
 leveldown.js                    | 102 ++++++++++++++++++++++++++
 package.json                    |  23 +++---
 src/async.h                     |  12 +--
 src/batch.cc                    |  43 +++--------
 src/batch.h                     |   1 -
 src/batch_async.cc              |   4 +-
 src/batch_async.h               |   6 +-
 src/database.cc                 | 158 ++++++++++++++++++----------------------
 src/database.h                  |  25 ++++---
 src/database_async.cc           |  41 +++++------
 src/database_async.h            |   9 ++-
 src/iterator.cc                 | 148 +++++++++++++++++++------------------
 src/iterator.h                  |  12 ++-
 src/iterator_async.cc           |  66 ++++++++++-------
 src/iterator_async.h            |   9 +--
 src/leveldown.cc                |  46 +++---------
 src/leveldown.h                 |  47 ++++++------
 src/leveldown_async.cc          |  16 ++--
 src/leveldown_async.h           |  12 +--
 test/iterator-recursion-test.js |  72 ++++++++++++++++++
 test/stack-blower.js            |  30 ++++++++
 33 files changed, 662 insertions(+), 430 deletions(-)

diff --git a/.dntrc b/.dntrc
index 2389126..3f20ae2 100644
--- a/.dntrc
+++ b/.dntrc
@@ -2,18 +2,16 @@
 ## see https://github.com/rvagg/dnt
 
 NODE_VERSIONS="\
-  master   \
-  v0.11.9  \
-  v0.11.8  \
-  v0.10.22 \
-  v0.10.21 \
-  v0.8.26  \
+  v0.8.28      \
+  v0.10.31     \
+  v0.11.13     \
 "
 OUTPUT_PREFIX="leveldown-"
 TEST_CMD="\
   cd /dnt/ &&                                                    \
   npm install &&                                                 \
-  node_modules/.bin/node-gyp --nodedir /usr/src/node/ rebuild && \
+  node /usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js \
+      --nodedir /usr/src/node/ rebuild &&                        \
   node_modules/.bin/tap test/*-test.js;                          \
 "
 
diff --git a/.npmignore b/.npmignore
index 746f2e3..193ad72 100644
--- a/.npmignore
+++ b/.npmignore
@@ -2,10 +2,15 @@
 build/
 test-data.tar
 test-data.db.tar
-test/benchmarks/
 deps/leveldb/leveldb-basho/
 deps/leveldb/leveldb-hyper/
 deps/leveldb/leveldb-rocksdb/
-deps/snappy/snappy-1.1.0/testdata/
+deps/snappy/snappy-1.1.1/testdata/
 leakydb
 bench/
+test/
+deps/leveldb/leveldb-1.17.0/doc/
+README
+INSTALL
+NEWS
+AUTHORS
diff --git a/.travis.yml b/.travis.yml
index 93c220b..2dc68bc 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,5 @@
 language: node_js
 node_js:
-  - 0.8
   - "0.10"
 branches:
   only:
@@ -9,3 +8,7 @@ notifications:
   email:
     - rod at vagg.org
 script: npm test
+
+before_install:
+  - npm install -g npm at latest
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bddd968..a01aa84 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,14 @@
+### 1.0.0 Aug 26 2014
+  * NAN at 1.3 for Node 0.11.13+ support (@rvagg)
+  * Allow writing empty values: null, undefined, '', [] and Buffer(0). Entries come out as '' or Buffer(0) (@ggreer, @juliangruber, @rvagg)
+  * Fix clang build (@thlorenz)
+  * Massive speed up of iterators by chunking reads (@kesla)
+  * Wrap in abstract-leveldown for consistent type-checking across *DOWNs (@kesla)
+  * Upgrade to LevelDB 1.17.0 (@kesla)
+  * Minor memory leaks
+  * Remove compile option that borked EL5 compiles
+  * Switch to plain MIT license
+
 ### 0.10.2 @ Nov 30 2013
 
   * Apply fix by @rescrv for long-standing OSX corruption bug, https://groups.google.com/forum/#!topic/leveldb/GXhx8YvFiig (@rvagg / @rescrv)
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 76d07a0..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,39 +0,0 @@
-Copyright 2012, Rod Vagg (the "Original Author")
-All rights reserved.
-
-MIT +no-false-attribs License
-
-Permission is hereby granted, free of charge, to any person
-obtaining a copy of this software and associated documentation
-files (the "Software"), to deal in the Software without
-restriction, including without limitation the rights to use,
-copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following
-conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-Distributions of all or part of the Software intended to be used
-by the recipients as they would use the unmodified Software,
-containing modifications that substantially alter, remove, or
-disable functionality of the Software, outside of the documented
-configuration mechanisms provided by the Software, shall be
-modified such that the Original Author's bug reporting email
-addresses and urls are either replaced with the contact information
-of the parties responsible for the changes, or removed entirely.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-
-
-Except where noted, this license applies to any and all software
-programs and associated documentation files created by the
-Original Author, when distributed with the Software.
\ No newline at end of file
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..29b95e3
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,11 @@
+The MIT License (MIT)
+=====================
+
+Copyright (c) 2014 Rod Vagg
+---------------------------
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
index d9dd581..a5c7853 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ A Low-level Node.js LevelDB binding
 
 [![Build Status](https://secure.travis-ci.org/rvagg/node-leveldown.png)](http://travis-ci.org/rvagg/node-leveldown)
 
-[![NPM](https://nodei.co/npm/leveldown.png?stars&downloads)](https://nodei.co/npm/leveldown/) [![NPM](https://nodei.co/npm-dl/leveldown.png)](https://nodei.co/npm/leveldown/)
+[![NPM](https://nodei.co/npm/leveldown.png?stars&downloads&downloadRank)](https://nodei.co/npm/leveldown/) [![NPM](https://nodei.co/npm-dl/leveldown.png?months=6&height=3)](https://nodei.co/npm/leveldown/)
 
 LevelDOWN was extracted from [LevelUP](https://github.com/rvagg/node-levelup) and now serves as a stand-alone binding for LevelDB.
 
@@ -98,7 +98,9 @@ The following options are for advanced performance tuning. Modify them only if y
 ### leveldown#put(key, value[, options], callback)
 <code>put()</code> is an instance method on an existing database object, used to store new entries, or overwrite existing entries in the LevelDB store.
 
-The `key` and `value` objects may either be `String`s or Node.js `Buffer` objects and cannot be `undefined` or `null`. Other object types are converted to JavaScript `String`s with the `toString()` method and the resulting `String` *may not* be a zero-length. A richer set of data-types are catered for in LevelUP.
+The `key` and `value` objects may either be `String`s or Node.js `Buffer` objects. Other object types are converted to JavaScript `String`s with the `toString()` method. Keys may not be `null` or `undefined` and objects converted with `toString()` should not result in an empty-string. Values of `null`, `undefined`, `''`, `[]` and `new Buffer(0)` (and any object resulting in a `toString()` of one of these) will be stored as a zero-length character array and will therefore be retrieved as  [...]
+
+A richer set of data-types are catered for in LevelUP.
 
 #### `options`
 
@@ -114,6 +116,8 @@ The `callback` function will be called with no arguments if the operation is suc
 
 The `key` object may either be a `String` or a Node.js `Buffer` object and cannot be `undefined` or `null`. Other object types are converted to JavaScript `String`s with the `toString()` method and the resulting `String` *may not* be a zero-length. A richer set of data-types are catered for in LevelUP.
 
+Values fetched via `get()` that are stored as zero-length character arrays (`null`, `undefined`, `''`, `[]`, `new Buffer(0)`) will return as empty-`String` (`''`) or `new Buffer(0)` when fetched with `asBuffer: true` (see below).
+
 #### `options`
 
 The optional `options` object may contain:
@@ -142,7 +146,9 @@ The `callback` function will be called with no arguments if the operation is suc
 --------------------------------------------------------
 <a name="leveldown_batch"></a>
 ### leveldown#batch(operations[, options], callback)
-<code>batch()</code> is an instance method on an existing database object. Used for very fast bulk-write operations (both *put* and *delete*). The `operations` argument should be an `Array` containing a list of operations to be executed sequentially, although as a whole they are performed as an atomic operation inside LevelDB. Each operation is contained in an object having the following properties: `type`, `key`, `value`, where the *type* is either `'put'` or `'del'`. In the case of `'d [...]
+<code>batch()</code> is an instance method on an existing database object. Used for very fast bulk-write operations (both *put* and *delete*). The `operations` argument should be an `Array` containing a list of operations to be executed sequentially, although as a whole they are performed as an atomic operation inside LevelDB. Each operation is contained in an object having the following properties: `type`, `key`, `value`, where the *type* is either `'put'` or `'del'`. In the case of `'d [...]
+
+See [LevelUP](https://github.com/rvagg/node-levelup#batch) for full documentation on how this works in practice.
 
 #### `options`
 
@@ -184,9 +190,11 @@ Currently, the only valid properties are:
 
 The optional `options` object may contain:
 
-* `'start'`: the key you wish to start the read at. By default it will start at the beginning of the store. Note that the *start* doesn't have to be an actual key that exists, LevelDB will simply find the *next* key, greater than the key you provide.
+* `'gt'` (greater than), `'gte'` (greater than or equal) define the lower bound of the values to be fetched and will determine the starting point where `'reverse'` is not `true`. Only records where the key is greater than (or equal to) this option will be included in the range. When `'reverse'` is 'true` the order will be reversed, but the records returned will be the same.
+
+* `'lt'` (less than), `'lte'` (less than or equal) define the higher bound of the range to be fetched and will determine the starting poitn where `'reverse'` is *not* `true`. Only key / value pairs where the key is less than (or equal to) this option will be included in the range. When `'reverse'` is `true` the order will be reversed, but the records returned will be the same.
 
-* `'end'`: the key you wish to end the read on. By default it will continue until the end of the store. Again, the *end* doesn't have to be an actual key as an (inclusive) `<=`-type operation is performed to detect the end. You can also use the `destroy()` method instead of supplying an `'end'` parameter to achieve the same effect.
+* `'start', 'end'` legacy ranges - instead use `'gte', 'lte'`
 
 * `'reverse'` *(boolean, default: `false`)*: a boolean, set to true if you want the stream to go in reverse order. Beware that due to the way LevelDB works, a reverse seek will be slower than a forward seek.
 
@@ -302,12 +310,12 @@ LevelDOWN is only possible due to the excellent work of the following contributo
 A large portion of the Windows support comes from code by [Krzysztof Kowalczyk](http://blog.kowalczyk.info/) [@kjk](https://twitter.com/kjk), see his Windows LevelDB port [here](http://code.google.com/r/kkowalczyk-leveldb/). If you're using LevelUP on Windows, you should give him your thanks!
 
 
-<a name="licence"></a>
-Licence & copyright
+<a name="license"></a>
+License & copyright
 -------------------
 
-Copyright (c) 2012-2013 LevelDOWN contributors (listed above).
+Copyright (c) 2012-2014 LevelDOWN contributors (listed above).
 
-LevelDOWN is licensed under an MIT +no-false-attribs license. All rights not explicitly granted in the MIT license are reserved. See the included LICENSE file for more details.
+LevelDOWN is licensed under the MIT license. All rights not explicitly granted in the MIT license are reserved. See the included LICENSE.md file for more details.
 
 *LevelDOWN builds on the excellent work of the LevelDB and Snappy teams from Google and additional contributors. LevelDB and Snappy are both issued under the [New BSD Licence](http://opensource.org/licenses/BSD-3-Clause).*
diff --git a/binding.gyp b/binding.gyp
index 79e6130..d7f6f12 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -17,8 +17,8 @@
           }]
         , ['OS == "linux"', {
               'cflags': [
-                  '-Wno-unused-local-typedefs'
               ]
+            , 'cflags!': [ '-fno-tree-vrp' ]
           }]
         ]
       , "dependencies": [
diff --git a/buster.js b/buster.js
deleted file mode 100644
index 8ae1df1..0000000
--- a/buster.js
+++ /dev/null
@@ -1,7 +0,0 @@
-var config = module.exports
-
-config['unit'] = {
-    environment: 'node'
-  , tests: [ 'test/*-test.js' ]
-  , libs: []
-}
\ No newline at end of file
diff --git a/chained-batch.js b/chained-batch.js
new file mode 100644
index 0000000..693062d
--- /dev/null
+++ b/chained-batch.js
@@ -0,0 +1,33 @@
+const util                 = require('util')
+    , AbstractChainedBatch = require('abstract-leveldown').AbstractChainedBatch
+
+
+function ChainedBatch (db) {
+  AbstractChainedBatch.call(this, db)
+  this.binding = db.binding.batch()
+}
+
+
+ChainedBatch.prototype._put = function (key, value) {
+  this.binding.put(key, value)
+}
+
+
+ChainedBatch.prototype._del = function (key) {
+  this.binding.del(key)
+}
+
+
+ChainedBatch.prototype._clear = function (key) {
+  this.binding.clear(key)
+}
+
+
+ChainedBatch.prototype._write = function (options, callback) {
+  this.binding.write(options, callback)
+}
+
+util.inherits(ChainedBatch, AbstractChainedBatch)
+
+
+module.exports = ChainedBatch
\ No newline at end of file
diff --git a/index.js b/index.js
deleted file mode 100644
index 9193802..0000000
--- a/index.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = require('bindings')('leveldown.node').leveldown
\ No newline at end of file
diff --git a/iterator.js b/iterator.js
new file mode 100644
index 0000000..202c6d4
--- /dev/null
+++ b/iterator.js
@@ -0,0 +1,54 @@
+const util             = require('util')
+    , AbstractIterator = require('abstract-leveldown').AbstractIterator
+
+
+function Iterator (db, options) {
+  AbstractIterator.call(this, options)
+
+  this.binding    = db.binding.iterator(options)
+  this.cache      = null
+  this.finished   = false
+  this.fastFuture = require('fast-future')()
+}
+
+util.inherits(Iterator, AbstractIterator)
+
+
+Iterator.prototype._next = function (callback) {
+  var that = this
+    , key
+    , value
+
+  if (this.cache && this.cache.length) {
+    key   = this.cache.pop()
+    value = this.cache.pop()
+
+    this.fastFuture(function () {
+      callback(null, key, value)
+    })
+
+  } else if (this.finished) {
+    this.fastFuture(function () {
+      callback()
+    })
+  } else {
+    this.binding.next(function (err, array, finished) {
+      if (err) return callback(err)
+
+      that.cache    = array
+      that.finished = finished
+      that._next(callback)
+    })
+  }
+
+  return this
+}
+
+
+Iterator.prototype._end = function (callback) {
+  delete this.cache
+  this.binding.end(callback)
+}
+
+
+module.exports = Iterator
\ No newline at end of file
diff --git a/leveldown.js b/leveldown.js
new file mode 100644
index 0000000..a058088
--- /dev/null
+++ b/leveldown.js
@@ -0,0 +1,102 @@
+const util              = require('util')
+    , AbstractLevelDOWN = require('abstract-leveldown').AbstractLevelDOWN
+
+    , binding           = require('bindings')('leveldown.node').leveldown
+
+    , ChainedBatch      = require('./chained-batch')
+    , Iterator          = require('./iterator')
+
+
+function LevelDOWN (location) {
+  if (!(this instanceof LevelDOWN))
+    return new LevelDOWN(location)
+
+  AbstractLevelDOWN.call(this, location)
+  this.binding = binding(location)
+}
+
+util.inherits(LevelDOWN, AbstractLevelDOWN)
+
+
+LevelDOWN.prototype._open = function (options, callback) {
+  this.binding.open(options, callback)
+}
+
+
+LevelDOWN.prototype._close = function (callback) {
+  this.binding.close(callback)
+}
+
+
+LevelDOWN.prototype._put = function (key, value, options, callback) {
+  this.binding.put(key, value, options, callback)
+}
+
+
+LevelDOWN.prototype._get = function (key, options, callback) {
+  this.binding.get(key, options, callback)
+}
+
+
+LevelDOWN.prototype._del = function (key, options, callback) {
+  this.binding.del(key, options, callback)
+}
+
+
+LevelDOWN.prototype._chainedBatch = function () {
+  return new ChainedBatch(this)
+}
+
+
+LevelDOWN.prototype._batch = function (operations, options, callback) {
+  return this.binding.batch(operations, options, callback)
+}
+
+
+LevelDOWN.prototype._approximateSize = function (start, end, callback) {
+  this.binding.approximateSize(start, end, callback)
+}
+
+
+LevelDOWN.prototype.getProperty = function (property) {
+  if (typeof property != 'string')
+    throw new Error('getProperty() requires a valid `property` argument')
+
+  return this.binding.getProperty(property)
+}
+
+
+LevelDOWN.prototype._iterator = function (options) {
+  return new Iterator(this, options)
+}
+
+
+LevelDOWN.destroy = function (location, callback) {
+  if (arguments.length < 2)
+    throw new Error('destroy() requires `location` and `callback` arguments')
+
+  if (typeof location != 'string')
+    throw new Error('destroy() requires a location string argument')
+
+  if (typeof callback != 'function')
+    throw new Error('destroy() requires a callback function argument')
+
+  binding.destroy(location, callback)
+}
+
+
+LevelDOWN.repair = function (location, callback) {
+  if (arguments.length < 2)
+    throw new Error('repair() requires `location` and `callback` arguments')
+
+  if (typeof location != 'string')
+    throw new Error('repair() requires a location string argument')
+
+  if (typeof callback != 'function')
+    throw new Error('repair() requires a callback function argument')
+
+  binding.repair(location, callback)
+}
+
+
+module.exports = LevelDOWN
\ No newline at end of file
diff --git a/package.json b/package.json
index 30d9283..c7a0116 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "leveldown",
   "description": "A Node.js LevelDB binding, primary backend for LevelUP",
-  "version": "0.10.2",
+  "version": "1.0.0",
   "contributors": [
     "Rod Vagg <r at va.gg> (https://github.com/rvagg)",
     "John Chesley <john at chesl.es> (https://github.com/chesles/)",
@@ -26,20 +26,21 @@
     "leveldb",
     "level"
   ],
-  "main": "index.js",
+  "main": "leveldown.js",
   "dependencies": {
-    "bindings": "~1.1.1",
-    "nan": "~0.6.0"
+    "abstract-leveldown": "~2.0.0",
+    "bindings": "~1.2.1",
+    "fast-future": "~1.0.0",
+    "nan": "~1.3.0"
   },
   "devDependencies": {
-    "tap": "~0.4.1",
-    "rimraf": "~2.1.4",
-    "mkfiletree": "~0.0.0",
-    "readfiletree": "~0.0.0",
-    "abstract-leveldown": "~0.11.1",
+    "du": "~0.1.0",
+    "mkfiletree": "~0.0.1",
     "monotonic-timestamp": "~0.0.8",
-    "du": "~0.0.1",
-    "node-gyp": "~0.12.1"
+    "node-gyp": "~1.0.1",
+    "readfiletree": "~0.0.1",
+    "rimraf": "~2.2.8",
+    "tap": "~0.4.12"
   },
   "scripts": {
     "test": "tap test/*-test.js --stderr"
diff --git a/src/async.h b/src/async.h
index ddf5004..1be7ca2 100644
--- a/src/async.h
+++ b/src/async.h
@@ -1,13 +1,13 @@
-/* Copyright (c) 2012-2013 LevelDOWN contributors
+/* Copyright (c) 2012-2014 LevelDOWN contributors
  * See list at <https://github.com/rvagg/node-leveldown#contributing>
- * MIT +no-false-attribs License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE>
+ * MIT License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE.md>
  */
 
 #ifndef LD_ASYNC_H
 #define LD_ASYNC_H
 
 #include <node.h>
-#include "nan.h"
+#include <nan.h>
 #include "database.h"
 
 namespace leveldown {
@@ -21,15 +21,15 @@ public:
     , NanCallback *callback
   ) : NanAsyncWorker(callback), database(database) {
     NanScope();
-    v8::Local<v8::Object> obj = v8::Object::New();
-    NanAssignPersistent(v8::Object, persistentHandle, obj);
+    v8::Local<v8::Object> obj = NanNew<v8::Object>();
+    NanAssignPersistent(persistentHandle, obj);
   }
 
 protected:
   void SetStatus(leveldb::Status status) {
     this->status = status;
     if (!status.ok())
-      this->errmsg = strdup(status.ToString().c_str());
+      SetErrorMessage(status.ToString().c_str());
   }
   Database* database;
 private:
diff --git a/src/batch.cc b/src/batch.cc
index d0205b7..32dfbe3 100644
--- a/src/batch.cc
+++ b/src/batch.cc
@@ -1,8 +1,7 @@
 #include <node.h>
 #include <node_buffer.h>
+#include <nan.h>
 
-
-#include "nan.h"
 #include "database.h"
 #include "batch_async.h"
 #include "batch.h"
@@ -16,7 +15,6 @@ Batch::Batch (leveldown::Database* database, bool sync) : database(database) {
   options->sync = sync;
   batch = new leveldb::WriteBatch();
   hasData = false;
-  written = false;
 }
 
 Batch::~Batch () {
@@ -29,9 +27,9 @@ leveldb::Status Batch::Write () {
 }
 
 void Batch::Init () {
-  v8::Local<v8::FunctionTemplate> tpl = v8::FunctionTemplate::New(Batch::New);
-  NanAssignPersistent(v8::FunctionTemplate, batch_constructor, tpl);
-  tpl->SetClassName(NanSymbol("Batch"));
+  v8::Local<v8::FunctionTemplate> tpl = NanNew<v8::FunctionTemplate>(Batch::New);
+  NanAssignPersistent(batch_constructor, tpl);
+  tpl->SetClassName(NanNew("Batch"));
   tpl->InstanceTemplate()->SetInternalFieldCount(1);
   NODE_SET_PROTOTYPE_METHOD(tpl, "put", Batch::Put);
   NODE_SET_PROTOTYPE_METHOD(tpl, "del", Batch::Del);
@@ -49,7 +47,7 @@ NAN_METHOD(Batch::New) {
     optionsObj = v8::Local<v8::Object>::Cast(args[1]);
   }
 
-  bool sync = NanBooleanOptionValue(optionsObj, NanSymbol("sync"));
+  bool sync = NanBooleanOptionValue(optionsObj, NanNew("sync"));
 
   Batch* batch = new Batch(database, sync);
   batch->Wrap(args.This());
@@ -62,12 +60,12 @@ v8::Handle<v8::Value> Batch::NewInstance (
       , v8::Handle<v8::Object> optionsObj
     ) {
 
-  NanScope();
+  NanEscapableScope();
 
   v8::Local<v8::Object> instance;
 
   v8::Local<v8::FunctionTemplate> constructorHandle =
-      NanPersistentToLocal(batch_constructor);
+      NanNew<v8::FunctionTemplate>(batch_constructor);
 
   if (optionsObj.IsEmpty()) {
     v8::Handle<v8::Value> argv[1] = { database };
@@ -77,22 +75,15 @@ v8::Handle<v8::Value> Batch::NewInstance (
     instance = constructorHandle->GetFunction()->NewInstance(2, argv);
   }
 
-  return scope.Close(instance);
+  return NanEscapeScope(instance);
 }
 
 NAN_METHOD(Batch::Put) {
   NanScope();
 
   Batch* batch = ObjectWrap::Unwrap<Batch>(args.Holder());
-
-  if (batch->written)
-    return NanThrowError("write() already called on this batch");
-
   v8::Handle<v8::Function> callback; // purely for the error macros
 
-  LD_CB_ERR_IF_NULL_OR_UNDEFINED(args[0], key)
-  LD_CB_ERR_IF_NULL_OR_UNDEFINED(args[1], value)
-
   v8::Local<v8::Value> keyBuffer = args[0];
   v8::Local<v8::Value> valueBuffer = args[1];
   LD_STRING_OR_BUFFER_TO_SLICE(key, keyBuffer, key)
@@ -113,13 +104,8 @@ NAN_METHOD(Batch::Del) {
 
   Batch* batch = ObjectWrap::Unwrap<Batch>(args.Holder());
 
-  if (batch->written)
-    return NanThrowError("write() already called on this batch");
-
   v8::Handle<v8::Function> callback; // purely for the error macros
 
-  LD_CB_ERR_IF_NULL_OR_UNDEFINED(args[0], key)
-
   v8::Local<v8::Value> keyBuffer = args[0];
   LD_STRING_OR_BUFFER_TO_SLICE(key, keyBuffer, key)
 
@@ -137,9 +123,6 @@ NAN_METHOD(Batch::Clear) {
 
   Batch* batch = ObjectWrap::Unwrap<Batch>(args.Holder());
 
-  if (batch->written)
-    return NanThrowError("write() already called on this batch");
-
   batch->batch->Clear();
   batch->hasData = false;
 
@@ -151,21 +134,13 @@ NAN_METHOD(Batch::Write) {
 
   Batch* batch = ObjectWrap::Unwrap<Batch>(args.Holder());
 
-  if (batch->written)
-    return NanThrowError("write() already called on this batch");
-  
-  if (args.Length() == 0)
-    return NanThrowError("write() requires a callback argument");
-
-  batch->written = true;
-
   if (batch->hasData) {
     NanCallback *callback =
         new NanCallback(v8::Local<v8::Function>::Cast(args[0]));
     BatchWriteWorker* worker  = new BatchWriteWorker(batch, callback);
     // persist to prevent accidental GC
     v8::Local<v8::Object> _this = args.This();
-    worker->SavePersistent("batch", _this);
+    worker->SaveToPersistent("batch", _this);
     NanAsyncQueueWorker(worker);
   } else {
     LD_RUN_CALLBACK(v8::Local<v8::Function>::Cast(args[0]), 0, NULL);
diff --git a/src/batch.h b/src/batch.h
index c72d790..b2b1880 100644
--- a/src/batch.h
+++ b/src/batch.h
@@ -27,7 +27,6 @@ private:
   leveldb::WriteOptions* options;
   leveldb::WriteBatch* batch;
   bool hasData; // keep track of whether we're writing data or not
-  bool written;
 
   static NAN_METHOD(New);
   static NAN_METHOD(Put);
diff --git a/src/batch_async.cc b/src/batch_async.cc
index 4ff256c..92d911d 100644
--- a/src/batch_async.cc
+++ b/src/batch_async.cc
@@ -1,6 +1,6 @@
-/* Copyright (c) 2012-2013 LevelDOWN contributors
+/* Copyright (c) 2012-2014 LevelDOWN contributors
  * See list at <https://github.com/rvagg/node-leveldown#contributing>
- * MIT +no-false-attribs License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE>
+ * MIT License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE.md>
  */
 
 
diff --git a/src/batch_async.h b/src/batch_async.h
index 05fb4ad..5323d1a 100644
--- a/src/batch_async.h
+++ b/src/batch_async.h
@@ -1,14 +1,14 @@
-/* Copyright (c) 2012-2013 LevelDOWN contributors
+/* Copyright (c) 2012-2014 LevelDOWN contributors
  * See list at <https://github.com/rvagg/node-leveldown#contributing>
- * MIT +no-false-attribs License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE>
+ * MIT License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE.md>
  */
 
 #ifndef LD_BATCH_ASYNC_H
 #define LD_BATCH_ASYNC_H
 
 #include <node.h>
+#include <nan.h>
 
-#include "nan.h"
 #include "async.h"
 #include "batch.h"
 #include "database.h"
diff --git a/src/database.cc b/src/database.cc
index b96da74..bb5702a 100644
--- a/src/database.cc
+++ b/src/database.cc
@@ -1,14 +1,14 @@
-/* Copyright (c) 2012-2013 LevelDOWN contributors
+/* Copyright (c) 2012-2014 LevelDOWN contributors
  * See list at <https://github.com/rvagg/node-leveldown#contributing>
- * MIT +no-false-attribs License
- * <https://github.com/rvagg/node-leveldown/blob/master/LICENSE>
+ * MIT License
+ * <https://github.com/rvagg/node-leveldown/blob/master/LICENSE.md>
  */
 
 #include <node.h>
 #include <node_buffer.h>
 
-#include "leveldb/db.h"
-#include "leveldb/write_batch.h"
+#include <leveldb/db.h>
+#include <leveldb/write_batch.h>
 
 #include "leveldown.h"
 #include "database.h"
@@ -21,19 +21,21 @@ namespace leveldown {
 
 static v8::Persistent<v8::FunctionTemplate> database_constructor;
 
-Database::Database (char* location) : location(location) {
+Database::Database (NanUtf8String* location) : location(location) {
   db = NULL;
   currentIteratorId = 0;
   pendingCloseWorker = NULL;
+  blockCache = NULL;
+  filterPolicy = NULL;
 };
 
 Database::~Database () {
   if (db != NULL)
     delete db;
-  delete[] location;
+  delete location;
 };
 
-const char* Database::Location() const { return location; }
+NanUtf8String* Database::Location() { return location; }
 
 /* Calls from worker threads, NO V8 HERE *****************************/
 
@@ -115,6 +117,14 @@ void Database::ReleaseIterator (uint32_t id) {
 void Database::CloseDatabase () {
   delete db;
   db = NULL;
+  if (blockCache) {
+    delete blockCache;
+    blockCache = NULL;
+  }
+  if (filterPolicy) {
+    delete filterPolicy;
+    filterPolicy = NULL;
+  }
 }
 
 /* V8 exposed functions *****************************/
@@ -129,9 +139,9 @@ NAN_METHOD(LevelDOWN) {
 }
 
 void Database::Init () {
-  v8::Local<v8::FunctionTemplate> tpl = v8::FunctionTemplate::New(Database::New);
-  NanAssignPersistent(v8::FunctionTemplate, database_constructor, tpl);
-  tpl->SetClassName(NanSymbol("Database"));
+  v8::Local<v8::FunctionTemplate> tpl = NanNew<v8::FunctionTemplate>(Database::New);
+  NanAssignPersistent(database_constructor, tpl);
+  tpl->SetClassName(NanNew("Database"));
   tpl->InstanceTemplate()->SetInternalFieldCount(1);
   NODE_SET_PROTOTYPE_METHOD(tpl, "open", Database::Open);
   NODE_SET_PROTOTYPE_METHOD(tpl, "close", Database::Close);
@@ -147,13 +157,7 @@ void Database::Init () {
 NAN_METHOD(Database::New) {
   NanScope();
 
-  if (args.Length() == 0)
-    return NanThrowError("constructor requires at least a location argument");
-
-  if (!args[0]->IsString())
-    return NanThrowError("constructor requires a location string argument");
-
-  char* location = NanFromV8String(args[0].As<v8::Object>(), Nan::UTF8, NULL, NULL, 0, v8::String::NO_OPTIONS);
+  NanUtf8String* location = new NanUtf8String(args[0]);
 
   Database* obj = new Database(location);
   obj->Wrap(args.This());
@@ -162,12 +166,12 @@ NAN_METHOD(Database::New) {
 }
 
 v8::Handle<v8::Value> Database::NewInstance (v8::Local<v8::String> &location) {
-  NanScope();
+  NanEscapableScope();
 
   v8::Local<v8::Object> instance;
 
   v8::Local<v8::FunctionTemplate> constructorHandle =
-      NanPersistentToLocal(database_constructor);
+      NanNew<v8::FunctionTemplate>(database_constructor);
 
   if (location.IsEmpty()) {
     instance = constructorHandle->GetFunction()->NewInstance(0, NULL);
@@ -176,7 +180,7 @@ v8::Handle<v8::Value> Database::NewInstance (v8::Local<v8::String> &location) {
     instance = constructorHandle->GetFunction()->NewInstance(1, argv);
   }
 
-  return instance;
+  return NanEscapeScope(instance);
 }
 
 NAN_METHOD(Database::Open) {
@@ -186,47 +190,51 @@ NAN_METHOD(Database::Open) {
 
   bool createIfMissing = NanBooleanOptionValue(
       optionsObj
-    , NanSymbol("createIfMissing")
+    , NanNew("createIfMissing")
     , true
   );
   bool errorIfExists =
-      NanBooleanOptionValue(optionsObj, NanSymbol("errorIfExists"));
-  bool compression = 
-      NanBooleanOptionValue(optionsObj, NanSymbol("compression"), true);
+      NanBooleanOptionValue(optionsObj, NanNew("errorIfExists"));
+  bool compression =
+      NanBooleanOptionValue(optionsObj, NanNew("compression"), true);
 
   uint32_t cacheSize = NanUInt32OptionValue(
       optionsObj
-    , NanSymbol("cacheSize")
+    , NanNew("cacheSize")
     , 8 << 20
   );
   uint32_t writeBufferSize = NanUInt32OptionValue(
       optionsObj
-    , NanSymbol("writeBufferSize")
+    , NanNew("writeBufferSize")
     , 4 << 20
   );
   uint32_t blockSize = NanUInt32OptionValue(
       optionsObj
-    , NanSymbol("blockSize")
+    , NanNew("blockSize")
     , 4096
   );
   uint32_t maxOpenFiles = NanUInt32OptionValue(
       optionsObj
-    , NanSymbol("maxOpenFiles")
+    , NanNew("maxOpenFiles")
     , 1000
   );
   uint32_t blockRestartInterval = NanUInt32OptionValue(
       optionsObj
-    , NanSymbol("blockRestartInterval")
+    , NanNew("blockRestartInterval")
     , 16
   );
 
+  database->blockCache = leveldb::NewLRUCache(cacheSize);
+  database->filterPolicy = leveldb::NewBloomFilterPolicy(10);
+
   OpenWorker* worker = new OpenWorker(
       database
     , new NanCallback(callback)
+    , database->blockCache
+    , database->filterPolicy
     , createIfMissing
     , errorIfExists
     , compression
-    , cacheSize
     , writeBufferSize
     , blockSize
     , maxOpenFiles
@@ -234,7 +242,7 @@ NAN_METHOD(Database::Open) {
   );
   // persist to prevent accidental GC
   v8::Local<v8::Object> _this = args.This();
-  worker->SavePersistent("database", _this);
+  worker->SaveToPersistent("database", _this);
   NanAsyncQueueWorker(worker);
 
   NanReturnUndefined();
@@ -251,7 +259,7 @@ NAN_METHOD(Database::Close) {
   );
   // persist to prevent accidental GC
   v8::Local<v8::Object> _this = args.This();
-  worker->SavePersistent("database", _this);
+  worker->SaveToPersistent("database", _this);
 
   if (!database->iterators.empty()) {
     // yikes, we still have iterators open! naughty naughty.
@@ -272,21 +280,21 @@ NAN_METHOD(Database::Close) {
         // CloseWorker will be invoked
 
         /*
-        v8::Local<v8::Object> localHandle = NanPersistentToLocal(it->second);
+        v8::Local<v8::Object> localHandle = NanNew(it->second);
         leveldown::Iterator* iterator =
             node::ObjectWrap::Unwrap<leveldown::Iterator>(localHandle->
-                Get(NanSymbol("iterator")).As<v8::Object>());
+                Get(NanNew("iterator")).As<v8::Object>());
                 */
         leveldown::Iterator *iterator = it->second;
 
         if (!iterator->ended) {
           v8::Local<v8::Function> end =
               v8::Local<v8::Function>::Cast(NanObjectWrapHandle(iterator)->Get(
-                  v8::String::NewSymbol("end")));
+                  NanNew<v8::String>("end")));
           v8::Local<v8::Value> argv[] = {
-              v8::FunctionTemplate::New()->GetFunction() // empty callback
+              NanNew<v8::FunctionTemplate>()->GetFunction() // empty callback
           };
-          node::MakeCallback(
+          NanMakeCallback(
               NanObjectWrapHandle(iterator)
             , end
             , 1
@@ -306,15 +314,12 @@ NAN_METHOD(Database::Put) {
 
   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<v8::Object> keyHandle = args[0].As<v8::Object>();
   v8::Local<v8::Object> valueHandle = args[1].As<v8::Object>();
   LD_STRING_OR_BUFFER_TO_SLICE(key, keyHandle, key)
   LD_STRING_OR_BUFFER_TO_SLICE(value, valueHandle, value)
 
-  bool sync = NanBooleanOptionValue(optionsObj, NanSymbol("sync"));
+  bool sync = NanBooleanOptionValue(optionsObj, NanNew("sync"));
 
   WriteWorker* worker  = new WriteWorker(
       database
@@ -325,9 +330,10 @@ NAN_METHOD(Database::Put) {
     , keyHandle
     , valueHandle
   );
+
   // persist to prevent accidental GC
   v8::Local<v8::Object> _this = args.This();
-  worker->SavePersistent("database", _this);
+  worker->SaveToPersistent("database", _this);
   NanAsyncQueueWorker(worker);
 
   NanReturnUndefined();
@@ -338,13 +344,11 @@ NAN_METHOD(Database::Get) {
 
   LD_METHOD_SETUP_COMMON(get, 1, 2)
 
-  LD_CB_ERR_IF_NULL_OR_UNDEFINED(args[0], key)
-
   v8::Local<v8::Object> keyHandle = args[0].As<v8::Object>();
   LD_STRING_OR_BUFFER_TO_SLICE(key, keyHandle, key)
 
-  bool asBuffer = NanBooleanOptionValue(optionsObj, NanSymbol("asBuffer"), true);
-  bool fillCache = NanBooleanOptionValue(optionsObj, NanSymbol("fillCache"), true);
+  bool asBuffer = NanBooleanOptionValue(optionsObj, NanNew("asBuffer"), true);
+  bool fillCache = NanBooleanOptionValue(optionsObj, NanNew("fillCache"), true);
 
   ReadWorker* worker = new ReadWorker(
       database
@@ -356,7 +360,7 @@ NAN_METHOD(Database::Get) {
   );
   // persist to prevent accidental GC
   v8::Local<v8::Object> _this = args.This();
-  worker->SavePersistent("database", _this);
+  worker->SaveToPersistent("database", _this);
   NanAsyncQueueWorker(worker);
 
   NanReturnUndefined();
@@ -367,12 +371,10 @@ NAN_METHOD(Database::Delete) {
 
   LD_METHOD_SETUP_COMMON(del, 1, 2)
 
-  LD_CB_ERR_IF_NULL_OR_UNDEFINED(args[0], key)
-
   v8::Local<v8::Object> keyHandle = args[0].As<v8::Object>();
   LD_STRING_OR_BUFFER_TO_SLICE(key, keyHandle, key)
 
-  bool sync = NanBooleanOptionValue(optionsObj, NanSymbol("sync"));
+  bool sync = NanBooleanOptionValue(optionsObj, NanNew("sync"));
 
   DeleteWorker* worker = new DeleteWorker(
       database
@@ -383,7 +385,7 @@ NAN_METHOD(Database::Delete) {
   );
   // persist to prevent accidental GC
   v8::Local<v8::Object> _this = args.This();
-  worker->SavePersistent("database", _this);
+  worker->SaveToPersistent("database", _this);
   NanAsyncQueueWorker(worker);
 
   NanReturnUndefined();
@@ -402,7 +404,7 @@ NAN_METHOD(Database::Batch) {
 
   LD_METHOD_SETUP_COMMON(batch, 1, 2)
 
-  bool sync = NanBooleanOptionValue(optionsObj, NanSymbol("sync"));
+  bool sync = NanBooleanOptionValue(optionsObj, NanNew("sync"));
 
   v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(args[0]);
 
@@ -414,13 +416,10 @@ NAN_METHOD(Database::Batch) {
       continue;
 
     v8::Local<v8::Object> obj = v8::Local<v8::Object>::Cast(array->Get(i));
+    v8::Local<v8::Value> keyBuffer = obj->Get(NanNew("key"));
+    v8::Local<v8::Value> type = obj->Get(NanNew("type"));
 
-    LD_CB_ERR_IF_NULL_OR_UNDEFINED(obj->Get(NanSymbol("type")), type)
-
-    v8::Local<v8::Value> keyBuffer = obj->Get(NanSymbol("key"));
-    LD_CB_ERR_IF_NULL_OR_UNDEFINED(keyBuffer, key)
-
-    if (obj->Get(NanSymbol("type"))->StrictEquals(NanSymbol("del"))) {
+    if (type->StrictEquals(NanNew("del"))) {
       LD_STRING_OR_BUFFER_TO_SLICE(key, keyBuffer, key)
 
       batch->Delete(key);
@@ -428,13 +427,11 @@ NAN_METHOD(Database::Batch) {
         hasData = true;
 
       DisposeStringOrBufferFromSlice(keyBuffer, key);
-    } else if (obj->Get(NanSymbol("type"))->StrictEquals(NanSymbol("put"))) {
-      v8::Local<v8::Value> valueBuffer = obj->Get(NanSymbol("value"));
-      LD_CB_ERR_IF_NULL_OR_UNDEFINED(valueBuffer, value)
+    } else if (type->StrictEquals(NanNew("put"))) {
+      v8::Local<v8::Value> valueBuffer = obj->Get(NanNew("value"));
 
       LD_STRING_OR_BUFFER_TO_SLICE(key, keyBuffer, key)
       LD_STRING_OR_BUFFER_TO_SLICE(value, valueBuffer, value)
-
       batch->Put(key, value);
       if (!hasData)
         hasData = true;
@@ -454,7 +451,7 @@ NAN_METHOD(Database::Batch) {
     );
     // persist to prevent accidental GC
     v8::Local<v8::Object> _this = args.This();
-    worker->SavePersistent("database", _this);
+    worker->SaveToPersistent("database", _this);
     NanAsyncQueueWorker(worker);
   } else {
     LD_RUN_CALLBACK(callback, 0, NULL);
@@ -469,21 +466,8 @@ NAN_METHOD(Database::ApproximateSize) {
   v8::Local<v8::Object> startHandle = args[0].As<v8::Object>();
   v8::Local<v8::Object> endHandle = args[1].As<v8::Object>();
 
-  if (startHandle->IsNull()
-      || startHandle->IsUndefined()
-      || startHandle->IsFunction() // callback in pos 0?
-      || endHandle->IsNull()
-      || endHandle->IsUndefined()
-      || endHandle->IsFunction() // callback in pos 1?
-      ) {
-    return NanThrowError("approximateSize() requires valid `start`, `end` and `callback` arguments");
-  }
-
   LD_METHOD_SETUP_COMMON(approximateSize, -1, 2)
 
-  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, startHandle, start)
   LD_STRING_OR_BUFFER_TO_SLICE(end, endHandle, end)
 
@@ -497,7 +481,7 @@ NAN_METHOD(Database::ApproximateSize) {
   );
   // persist to prevent accidental GC
   v8::Local<v8::Object> _this = args.This();
-  worker->SavePersistent("database", _this);
+  worker->SaveToPersistent("database", _this);
   NanAsyncQueueWorker(worker);
 
   NanReturnUndefined();
@@ -507,12 +491,7 @@ NAN_METHOD(Database::GetProperty) {
   NanScope();
 
   v8::Local<v8::Value> propertyHandle = args[0].As<v8::Object>();
-  v8::Local<v8::Function> callback; // for LD_CB_ERR_IF_NULL_OR_UNDEFINED
-
-  if (!propertyHandle->IsString())
-    return NanThrowError("getProperty() requires a valid `property` argument");
-
-  LD_CB_ERR_IF_NULL_OR_UNDEFINED(propertyHandle, property)
+  v8::Local<v8::Function> callback; // for LD_STRING_OR_BUFFER_TO_SLICE
 
   LD_STRING_OR_BUFFER_TO_SLICE(property, propertyHandle, property)
 
@@ -522,7 +501,7 @@ NAN_METHOD(Database::GetProperty) {
   std::string* value = new std::string();
   database->GetPropertyFromDatabase(property, value);
   v8::Local<v8::String> returnValue
-      = v8::String::New(value->c_str(), value->length());
+      = NanNew<v8::String>(value->c_str(), value->length());
   delete value;
   delete[] property.data();
 
@@ -545,11 +524,12 @@ NAN_METHOD(Database::Iterator) {
   v8::TryCatch try_catch;
   v8::Local<v8::Object> iteratorHandle = Iterator::NewInstance(
       args.This()
-    , v8::Number::New(id)
+    , NanNew<v8::Number>(id)
     , optionsObj
   );
   if (try_catch.HasCaught()) {
-    node::FatalException(try_catch);
+    // NB: node::FatalException can segfault here if there is no room on stack.
+    return NanThrowError("Fatal Error in Database::Iterator!");
   }
 
   leveldown::Iterator *iterator =
@@ -559,8 +539,8 @@ NAN_METHOD(Database::Iterator) {
 
   // register our iterator
   /*
-  v8::Local<v8::Object> obj = v8::Object::New();
-  obj->Set(NanSymbol("iterator"), iteratorHandle);
+  v8::Local<v8::Object> obj = NanNew<v8::Object>();
+  obj->Set(NanNew("iterator"), iteratorHandle);
   v8::Persistent<v8::Object> persistent;
   persistent.Reset(nan_isolate, obj);
   database->iterators.insert(std::pair< uint32_t, v8::Persistent<v8::Object> & >
diff --git a/src/database.h b/src/database.h
index 2310f83..c466a7b 100644
--- a/src/database.h
+++ b/src/database.h
@@ -1,6 +1,6 @@
-/* Copyright (c) 2012-2013 LevelDOWN contributors
+/* Copyright (c) 2012-2014 LevelDOWN contributors
  * See list at <https://github.com/rvagg/node-leveldown#contributing>
- * MIT +no-false-attribs License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE>
+ * MIT License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE.md>
  */
 
 #ifndef LD_DATABASE_H
@@ -10,8 +10,11 @@
 #include <vector>
 #include <node.h>
 
-#include "leveldb/db.h"
-#include "nan.h"
+#include <leveldb/cache.h>
+#include <leveldb/db.h>
+#include <leveldb/filter_policy.h>
+#include <nan.h>
+
 #include "leveldown.h"
 #include "iterator.h"
 
@@ -24,9 +27,9 @@ struct Reference {
   leveldb::Slice slice;
 
   Reference(v8::Local<v8::Value> obj, leveldb::Slice slice) : slice(slice) {
-    v8::Local<v8::Object> _obj = v8::Object::New();
-    _obj->Set(NanSymbol("obj"), obj);
-    NanAssignPersistent(v8::Object, handle, _obj);
+    v8::Local<v8::Object> _obj = NanNew<v8::Object>();
+    _obj->Set(NanNew("obj"), obj);
+    NanAssignPersistent(handle, _obj);
   };
 };
 
@@ -70,15 +73,17 @@ public:
   const leveldb::Snapshot* NewSnapshot ();
   void ReleaseSnapshot (const leveldb::Snapshot* snapshot);
   void CloseDatabase ();
-  const char* Location() const;
+  NanUtf8String* Location();
   void ReleaseIterator (uint32_t id);
 
-  Database (char* location);
+  Database (NanUtf8String* location);
   ~Database ();
 
 private:
   leveldb::DB* db;
-  char* location;
+  const leveldb::FilterPolicy* filterPolicy;
+  leveldb::Cache* blockCache;
+  NanUtf8String* location;
   uint32_t currentIteratorId;
   void(*pendingCloseWorker);
 
diff --git a/src/database_async.cc b/src/database_async.cc
index 5c7cd03..96943c0 100644
--- a/src/database_async.cc
+++ b/src/database_async.cc
@@ -1,13 +1,13 @@
-/* Copyright (c) 2012-2013 LevelDOWN contributors
+/* Copyright (c) 2012-2014 LevelDOWN contributors
  * See list at <https://github.com/rvagg/node-leveldown#contributing>
- * MIT +no-false-attribs License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE>
+ * MIT License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE.md>
  */
 
 #include <node.h>
 #include <node_buffer.h>
 
-#include "leveldb/write_batch.h"
-#include "leveldb/filter_policy.h"
+#include <leveldb/write_batch.h>
+#include <leveldb/filter_policy.h>
 
 #include "database.h"
 #include "leveldown.h"
@@ -21,10 +21,11 @@ namespace leveldown {
 OpenWorker::OpenWorker (
     Database *database
   , NanCallback *callback
+  , leveldb::Cache* blockCache
+  , const leveldb::FilterPolicy* filterPolicy
   , bool createIfMissing
   , bool errorIfExists
   , bool compression
-  , uint32_t cacheSize
   , uint32_t writeBufferSize
   , uint32_t blockSize
   , uint32_t maxOpenFiles
@@ -32,17 +33,17 @@ OpenWorker::OpenWorker (
 ) : AsyncWorker(database, callback)
 {
   options = new leveldb::Options();
+  options->block_cache            = blockCache;
+  options->filter_policy          = filterPolicy;
   options->create_if_missing      = createIfMissing;
   options->error_if_exists        = errorIfExists;
   options->compression            = compression
       ? leveldb::kSnappyCompression
       : leveldb::kNoCompression;
-  options->block_cache            = leveldb::NewLRUCache(cacheSize);
   options->write_buffer_size      = writeBufferSize;
   options->block_size             = blockSize;
   options->max_open_files         = maxOpenFiles;
   options->block_restart_interval = blockRestartInterval;
-  options->filter_policy          = leveldb::NewBloomFilterPolicy(10);
 };
 
 OpenWorker::~OpenWorker () {
@@ -50,7 +51,7 @@ OpenWorker::~OpenWorker () {
 }
 
 void OpenWorker::Execute () {
-  SetStatus(database->OpenDatabase(options, database->Location()));
+  SetStatus(database->OpenDatabase(options, **(database->Location())));
 }
 
 /** CLOSE WORKER **/
@@ -86,7 +87,7 @@ IOWorker::IOWorker (
 {
   NanScope();
 
-  SavePersistent("key", keyHandle);
+  SaveToPersistent("key", keyHandle);
 };
 
 IOWorker::~IOWorker () {}
@@ -114,7 +115,7 @@ ReadWorker::ReadWorker (
 
   options = new leveldb::ReadOptions();
   options->fill_cache = fillCache;
-  SavePersistent("key", keyHandle);
+  SaveToPersistent("key", keyHandle);
 };
 
 ReadWorker::~ReadWorker () {
@@ -132,10 +133,10 @@ void ReadWorker::HandleOKCallback () {
   if (asBuffer) {
     returnValue = NanNewBufferHandle((char*)value.data(), value.size());
   } else {
-    returnValue = v8::String::New((char*)value.data(), value.size());
+    returnValue = NanNew<v8::String>((char*)value.data(), value.size());
   }
   v8::Local<v8::Value> argv[] = {
-      NanNewLocal<v8::Value>(v8::Null())
+      NanNull()
     , returnValue
   };
   callback->Call(2, argv);
@@ -155,7 +156,7 @@ DeleteWorker::DeleteWorker (
 
   options = new leveldb::WriteOptions();
   options->sync = sync;
-  SavePersistent("key", keyHandle);
+  SaveToPersistent("key", keyHandle);
 };
 
 DeleteWorker::~DeleteWorker () {
@@ -181,14 +182,13 @@ WriteWorker::WriteWorker (
 {
   NanScope();
 
-  SavePersistent("value", valueHandle);
+  SaveToPersistent("value", valueHandle);
 };
 
-WriteWorker::~WriteWorker () {}
+WriteWorker::~WriteWorker () { }
 
 void WriteWorker::Execute () {
   SetStatus(database->PutToDatabase(options, key, value));
-  //printf("WriteWorker::Execute\n");fflush(stdout);
 }
 
 void WriteWorker::WorkComplete () {
@@ -196,7 +196,6 @@ void WriteWorker::WorkComplete () {
 
   DisposeStringOrBufferFromSlice(GetFromPersistent("value"), value);
   IOWorker::WorkComplete();
-  //printf("WriteWorker::WorkComplete\n");fflush(stdout);
 }
 
 /** BATCH WORKER **/
@@ -236,8 +235,8 @@ ApproximateSizeWorker::ApproximateSizeWorker (
 {
   NanScope();
 
-  SavePersistent("start", startHandle);
-  SavePersistent("end", endHandle);
+  SaveToPersistent("start", startHandle);
+  SaveToPersistent("end", endHandle);
 };
 
 ApproximateSizeWorker::~ApproximateSizeWorker () {}
@@ -257,9 +256,9 @@ void ApproximateSizeWorker::WorkComplete() {
 void ApproximateSizeWorker::HandleOKCallback () {
   NanScope();
 
-  v8::Local<v8::Value> returnValue = v8::Number::New((double) size);
+  v8::Local<v8::Value> returnValue = NanNew<v8::Number>((double) size);
   v8::Local<v8::Value> argv[] = {
-      NanNewLocal<v8::Value>(v8::Null())
+      NanNull()
     , returnValue
   };
   callback->Call(2, argv);
diff --git a/src/database_async.h b/src/database_async.h
index 5209de3..3d8c040 100644
--- a/src/database_async.h
+++ b/src/database_async.h
@@ -1,6 +1,6 @@
-/* Copyright (c) 2012-2013 LevelDOWN contributors
+/* Copyright (c) 2012-2014 LevelDOWN contributors
  * See list at <https://github.com/rvagg/node-leveldown#contributing>
- * MIT +no-false-attribs License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE>
+ * MIT License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE.md>
  */
 
 #ifndef LD_DATABASE_ASYNC_H
@@ -9,7 +9,7 @@
 #include <vector>
 #include <node.h>
 
-#include "leveldb/cache.h"
+#include <leveldb/cache.h>
 
 #include "async.h"
 
@@ -20,10 +20,11 @@ public:
   OpenWorker (
       Database *database
     , NanCallback *callback
+    , leveldb::Cache* blockCache
+    , const leveldb::FilterPolicy* filterPolicy
     , bool createIfMissing
     , bool errorIfExists
     , bool compression
-    , uint32_t cacheSize
     , uint32_t writeBufferSize
     , uint32_t blockSize
     , uint32_t maxOpenFiles
diff --git a/src/iterator.cc b/src/iterator.cc
index 6a696eb..180de6d 100644
--- a/src/iterator.cc
+++ b/src/iterator.cc
@@ -1,6 +1,6 @@
-/* Copyright (c) 2012-2013 LevelDOWN contributors
+/* Copyright (c) 2012-2014 LevelDOWN contributors
  * See list at <https://github.com/rvagg/node-leveldown#contributing>
- * MIT +no-false-attribs License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE>
+ * MIT License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE.md>
  */
 
 #include <node.h>
@@ -31,6 +31,7 @@ Iterator::Iterator (
   , bool keyAsBuffer
   , bool valueAsBuffer
   , v8::Local<v8::Object> &startHandle
+  , size_t highWaterMark
 ) : database(database)
   , id(id)
   , start(start)
@@ -43,18 +44,21 @@ Iterator::Iterator (
   , lte(lte)
   , gt(gt)
   , gte(gte)
+  , highWaterMark(highWaterMark)
   , keyAsBuffer(keyAsBuffer)
   , valueAsBuffer(valueAsBuffer)
 {
   NanScope();
 
-  v8::Local<v8::Object> obj = v8::Object::New();
+  v8::Local<v8::Object> obj = NanNew<v8::Object>();
   if (!startHandle.IsEmpty())
-    obj->Set(NanSymbol("start"), startHandle);
-  NanAssignPersistent(v8::Object, persistentHandle, obj);
+    obj->Set(NanNew("start"), startHandle);
+  NanAssignPersistent(persistentHandle, obj);
 
   options    = new leveldb::ReadOptions();
   options->fill_cache = fillCache;
+  // get a snapshot of the current state
+  options->snapshot = database->NewSnapshot();
   dbIterator = NULL;
   count      = 0;
   nexting    = false;
@@ -65,7 +69,7 @@ Iterator::Iterator (
 Iterator::~Iterator () {
   delete options;
   if (!persistentHandle.IsEmpty())
-    NanDispose(persistentHandle);
+    NanDisposePersistent(persistentHandle);
   if (start != NULL)
     delete start;
   if (end != NULL)
@@ -118,7 +122,7 @@ bool Iterator::GetIterator () {
   return false;
 }
 
-bool Iterator::IteratorNext (std::string& key, std::string& value) {
+bool Iterator::Read (std::string& key, std::string& value) {
   // if it's not the first call, move to next item.
   if (!GetIterator()) {
     if (reverse)
@@ -154,6 +158,25 @@ bool Iterator::IteratorNext (std::string& key, std::string& value) {
   return false;
 }
 
+bool Iterator::IteratorNext (std::vector<std::pair<std::string, std::string> >& result) {
+  size_t size = 0;
+  while(true) {
+    std::string key, value;
+    bool ok = Read(key, value);
+
+    if (ok) {
+      result.push_back(std::make_pair(key, value));
+      size = size + key.size() + value.size();
+
+      if (size > highWaterMark)
+        return true;
+
+    } else {
+      return false;
+    }
+  }
+}
+
 leveldb::Status Iterator::IteratorStatus () {
   return dbIterator->status();
 }
@@ -181,19 +204,8 @@ NAN_METHOD(Iterator::Next) {
 
   Iterator* iterator = node::ObjectWrap::Unwrap<Iterator>(args.This());
 
-  if (args.Length() == 0 || !args[0]->IsFunction())
-    return NanThrowError("next() requires a callback argument");
-
   v8::Local<v8::Function> callback = args[0].As<v8::Function>();
 
-  if (iterator->ended) {
-    LD_RETURN_CALLBACK_OR_ERROR(callback, "cannot call next() after end()")
-  }
-
-  if (iterator->nexting) {
-    LD_RETURN_CALLBACK_OR_ERROR(callback, "cannot call next() before previous next() has completed")
-  }
-
   NextWorker* worker = new NextWorker(
       iterator
     , new NanCallback(callback)
@@ -201,7 +213,7 @@ NAN_METHOD(Iterator::Next) {
   );
   // persist to prevent accidental GC
   v8::Local<v8::Object> _this = args.This();
-  worker->SavePersistent("iterator", _this);
+  worker->SaveToPersistent("iterator", _this);
   iterator->nexting = true;
   NanAsyncQueueWorker(worker);
 
@@ -213,22 +225,15 @@ NAN_METHOD(Iterator::End) {
 
   Iterator* iterator = node::ObjectWrap::Unwrap<Iterator>(args.This());
 
-  if (args.Length() == 0 || !args[0]->IsFunction())
-    return NanThrowError("end() requires a callback argument");
-
   v8::Local<v8::Function> callback = v8::Local<v8::Function>::Cast(args[0]);
 
-  if (iterator->ended) {
-    LD_RETURN_CALLBACK_OR_ERROR(callback, "end() already called on iterator")
-  }
-
   EndWorker* worker = new EndWorker(
       iterator
     , new NanCallback(callback)
   );
   // persist to prevent accidental GC
   v8::Local<v8::Object> _this = args.This();
-  worker->SavePersistent("iterator", _this);
+  worker->SaveToPersistent("iterator", _this);
   iterator->ended = true;
 
   if (iterator->nexting) {
@@ -243,9 +248,9 @@ NAN_METHOD(Iterator::End) {
 
 void Iterator::Init () {
   v8::Local<v8::FunctionTemplate> tpl =
-      v8::FunctionTemplate::New(Iterator::New);
-  NanAssignPersistent(v8::FunctionTemplate, iterator_constructor, tpl);
-  tpl->SetClassName(NanSymbol("Iterator"));
+      NanNew<v8::FunctionTemplate>(Iterator::New);
+  NanAssignPersistent(iterator_constructor, tpl);
+  tpl->SetClassName(NanNew("Iterator"));
   tpl->InstanceTemplate()->SetInternalFieldCount(1);
   NODE_SET_PROTOTYPE_METHOD(tpl, "next", Iterator::Next);
   NODE_SET_PROTOTYPE_METHOD(tpl, "end", Iterator::End);
@@ -257,11 +262,11 @@ v8::Local<v8::Object> Iterator::NewInstance (
       , v8::Local<v8::Object> optionsObj
     ) {
 
-  NanScope();
+  NanEscapableScope();
 
   v8::Local<v8::Object> instance;
   v8::Local<v8::FunctionTemplate> constructorHandle =
-      NanPersistentToLocal(iterator_constructor);
+      NanNew<v8::FunctionTemplate>(iterator_constructor);
 
   if (optionsObj.IsEmpty()) {
     v8::Handle<v8::Value> argv[2] = { database, id };
@@ -271,7 +276,7 @@ v8::Local<v8::Object> Iterator::NewInstance (
     instance = constructorHandle->GetFunction()->NewInstance(3, argv);
   }
 
-  return instance;
+  return NanEscapeScope(instance);
 }
 
 NAN_METHOD(Iterator::New) {
@@ -286,6 +291,8 @@ NAN_METHOD(Iterator::New) {
   leveldb::Slice* start = NULL;
   std::string* end = NULL;
   int limit = -1;
+  // default highWaterMark from Readble-streams
+  size_t highWaterMark = 16 * 1024;
 
   v8::Local<v8::Value> id = args[1];
 
@@ -307,13 +314,13 @@ NAN_METHOD(Iterator::New) {
   if (args.Length() > 1 && args[2]->IsObject()) {
     optionsObj = v8::Local<v8::Object>::Cast(args[2]);
 
-    reverse = NanBooleanOptionValue(optionsObj, NanSymbol("reverse"));
+    reverse = NanBooleanOptionValue(optionsObj, NanNew("reverse"));
 
-    if (optionsObj->Has(NanSymbol("start"))
-        && (node::Buffer::HasInstance(optionsObj->Get(NanSymbol("start")))
-          || optionsObj->Get(NanSymbol("start"))->IsString())) {
+    if (optionsObj->Has(NanNew("start"))
+        && (node::Buffer::HasInstance(optionsObj->Get(NanNew("start")))
+          || optionsObj->Get(NanNew("start"))->IsString())) {
 
-      startHandle = optionsObj->Get(NanSymbol("start")).As<v8::Object>();
+      startHandle = optionsObj->Get(NanNew("start")).As<v8::Object>();
 
       // ignore start if it has size 0 since a Slice can't have length 0
       if (StringOrBufferLength(startHandle) > 0) {
@@ -322,12 +329,11 @@ NAN_METHOD(Iterator::New) {
       }
     }
 
-    if (optionsObj->Has(NanSymbol("end"))
-        && (node::Buffer::HasInstance(optionsObj->Get(NanSymbol("end")))
-          || optionsObj->Get(NanSymbol("end"))->IsString())) {
+    if (optionsObj->Has(NanNew("end"))
+        && (node::Buffer::HasInstance(optionsObj->Get(NanNew("end")))
+          || optionsObj->Get(NanNew("end"))->IsString())) {
 
-      v8::Local<v8::Value> endBuffer =
-          NanNewLocal<v8::Value>(optionsObj->Get(NanSymbol("end")));
+      v8::Local<v8::Value> endBuffer = optionsObj->Get(NanNew("end"));
 
       // ignore end if it has size 0 since a Slice can't have length 0
       if (StringOrBufferLength(endBuffer) > 0) {
@@ -336,17 +342,21 @@ NAN_METHOD(Iterator::New) {
       }
     }
 
-    if (!optionsObj.IsEmpty() && optionsObj->Has(NanSymbol("limit"))) {
+    if (!optionsObj.IsEmpty() && optionsObj->Has(NanNew("limit"))) {
       limit = v8::Local<v8::Integer>::Cast(optionsObj->Get(
-          NanSymbol("limit")))->Value();
+          NanNew("limit")))->Value();
+    }
+
+    if (optionsObj->Has(NanNew("highWaterMark"))) {
+      highWaterMark = v8::Local<v8::Integer>::Cast(optionsObj->Get(
+            NanNew("highWaterMark")))->Value();
     }
 
-    if (optionsObj->Has(NanSymbol("lt"))
-        && (node::Buffer::HasInstance(optionsObj->Get(NanSymbol("lt")))
-          || optionsObj->Get(NanSymbol("lt"))->IsString())) {
+    if (optionsObj->Has(NanNew("lt"))
+        && (node::Buffer::HasInstance(optionsObj->Get(NanNew("lt")))
+          || optionsObj->Get(NanNew("lt"))->IsString())) {
 
-      v8::Local<v8::Value> ltBuffer =
-          NanNewLocal<v8::Value>(optionsObj->Get(NanSymbol("lt")));
+      v8::Local<v8::Value> ltBuffer = optionsObj->Get(NanNew("lt"));
 
       // ignore end if it has size 0 since a Slice can't have length 0
       if (StringOrBufferLength(ltBuffer) > 0) {
@@ -357,12 +367,11 @@ NAN_METHOD(Iterator::New) {
       }
     }
 
-    if (optionsObj->Has(NanSymbol("lte"))
-        && (node::Buffer::HasInstance(optionsObj->Get(NanSymbol("lte")))
-          || optionsObj->Get(NanSymbol("lte"))->IsString())) {
+    if (optionsObj->Has(NanNew("lte"))
+        && (node::Buffer::HasInstance(optionsObj->Get(NanNew("lte")))
+          || optionsObj->Get(NanNew("lte"))->IsString())) {
 
-      v8::Local<v8::Value> lteBuffer =
-          NanNewLocal<v8::Value>(optionsObj->Get(NanSymbol("lte")));
+      v8::Local<v8::Value> lteBuffer = optionsObj->Get(NanNew("lte"));
 
       // ignore end if it has size 0 since a Slice can't have length 0
       if (StringOrBufferLength(lteBuffer) > 0) {
@@ -373,12 +382,11 @@ NAN_METHOD(Iterator::New) {
       }
     }
 
-    if (optionsObj->Has(NanSymbol("gt"))
-        && (node::Buffer::HasInstance(optionsObj->Get(NanSymbol("gt")))
-          || optionsObj->Get(NanSymbol("gt"))->IsString())) {
+    if (optionsObj->Has(NanNew("gt"))
+        && (node::Buffer::HasInstance(optionsObj->Get(NanNew("gt")))
+          || optionsObj->Get(NanNew("gt"))->IsString())) {
 
-      v8::Local<v8::Value> gtBuffer =
-          NanNewLocal<v8::Value>(optionsObj->Get(NanSymbol("gt")));
+      v8::Local<v8::Value> gtBuffer = optionsObj->Get(NanNew("gt"));
 
       // ignore end if it has size 0 since a Slice can't have length 0
       if (StringOrBufferLength(gtBuffer) > 0) {
@@ -389,12 +397,11 @@ NAN_METHOD(Iterator::New) {
       }
     }
 
-    if (optionsObj->Has(NanSymbol("gte"))
-        && (node::Buffer::HasInstance(optionsObj->Get(NanSymbol("gte")))
-          || optionsObj->Get(NanSymbol("gte"))->IsString())) {
+    if (optionsObj->Has(NanNew("gte"))
+        && (node::Buffer::HasInstance(optionsObj->Get(NanNew("gte")))
+          || optionsObj->Get(NanNew("gte"))->IsString())) {
 
-      v8::Local<v8::Value> gteBuffer =
-          NanNewLocal<v8::Value>(optionsObj->Get(NanSymbol("gte")));
+      v8::Local<v8::Value> gteBuffer = optionsObj->Get(NanNew("gte"));
 
       // ignore end if it has size 0 since a Slice can't have length 0
       if (StringOrBufferLength(gteBuffer) > 0) {
@@ -407,19 +414,19 @@ NAN_METHOD(Iterator::New) {
 
   }
 
-  bool keys = NanBooleanOptionValue(optionsObj, NanSymbol("keys"), true);
-  bool values = NanBooleanOptionValue(optionsObj, NanSymbol("values"), true);
+  bool keys = NanBooleanOptionValue(optionsObj, NanNew("keys"), true);
+  bool values = NanBooleanOptionValue(optionsObj, NanNew("values"), true);
   bool keyAsBuffer = NanBooleanOptionValue(
       optionsObj
-    , NanSymbol("keyAsBuffer")
+    , NanNew("keyAsBuffer")
     , true
   );
   bool valueAsBuffer = NanBooleanOptionValue(
       optionsObj
-    , NanSymbol("valueAsBuffer")
+    , NanNew("valueAsBuffer")
     , true
   );
-  bool fillCache = NanBooleanOptionValue(optionsObj, NanSymbol("fillCache"));
+  bool fillCache = NanBooleanOptionValue(optionsObj, NanNew("fillCache"));
 
   Iterator* iterator = new Iterator(
       database
@@ -438,6 +445,7 @@ NAN_METHOD(Iterator::New) {
     , keyAsBuffer
     , valueAsBuffer
     , startHandle
+    , highWaterMark
   );
   iterator->Wrap(args.This());
 
diff --git a/src/iterator.h b/src/iterator.h
index 8309d2f..d8ed5f2 100644
--- a/src/iterator.h
+++ b/src/iterator.h
@@ -1,14 +1,15 @@
-/* Copyright (c) 2012-2013 LevelDOWN contributors
+/* Copyright (c) 2012-2014 LevelDOWN contributors
  * See list at <https://github.com/rvagg/node-leveldown#contributing>
- * MIT +no-false-attribs License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE>
+ * MIT License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE.md>
  */
 
 #ifndef LD_ITERATOR_H
 #define LD_ITERATOR_H
 
 #include <node.h>
+#include <vector>
+#include <nan.h>
 
-#include "nan.h"
 #include "leveldown.h"
 #include "database.h"
 #include "async.h"
@@ -44,11 +45,12 @@ public:
     , bool keyAsBuffer
     , bool valueAsBuffer
     , v8::Local<v8::Object> &startHandle
+    , size_t highWaterMark
   );
 
   ~Iterator ();
 
-  bool IteratorNext (std::string& key, std::string& value);
+  bool IteratorNext (std::vector<std::pair<std::string, std::string> >& result);
   leveldb::Status IteratorStatus ();
   void IteratorEnd ();
   void Release ();
@@ -69,6 +71,7 @@ private:
   std::string* gt;
   std::string* gte;
   int count;
+  size_t highWaterMark;
 
 public:
   bool keyAsBuffer;
@@ -80,6 +83,7 @@ public:
 private:
   v8::Persistent<v8::Object> persistentHandle;
 
+  bool Read (std::string& key, std::string& value);
   bool GetIterator ();
 
   static NAN_METHOD(New);
diff --git a/src/iterator_async.cc b/src/iterator_async.cc
index efd0ccc..f3db790 100644
--- a/src/iterator_async.cc
+++ b/src/iterator_async.cc
@@ -1,6 +1,6 @@
-/* Copyright (c) 2012-2013 LevelDOWN contributors
+/* Copyright (c) 2012-2014 LevelDOWN contributors
  * See list at <https://github.com/rvagg/node-leveldown#contributing>
- * MIT +no-false-attribs License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE>
+ * MIT License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE.md>
  */
 
 #include <node.h>
@@ -13,7 +13,7 @@
 
 namespace leveldown {
 
-/** NEXT WORKER **/
+/** NEXT-MULTI WORKER **/
 
 NextWorker::NextWorker (
     Iterator* iterator
@@ -27,41 +27,51 @@ NextWorker::NextWorker (
 NextWorker::~NextWorker () {}
 
 void NextWorker::Execute () {
-  ok = iterator->IteratorNext(key, value);
+  ok = iterator->IteratorNext(result);
   if (!ok)
     SetStatus(iterator->IteratorStatus());
 }
 
 void NextWorker::HandleOKCallback () {
-  NanScope();
-
-  v8::Local<v8::Value> returnKey;
-  if (iterator->keyAsBuffer) {
-    returnKey = NanNewBufferHandle((char*)key.data(), key.size());
-  } else {
-    returnKey = v8::String::New((char*)key.data(), key.size());
-  }
-
-  v8::Local<v8::Value> returnValue;
-  if (iterator->valueAsBuffer) {
-    returnValue = NanNewBufferHandle((char*)value.data(), value.size());
-  } else {
-    returnValue = v8::String::New((char*)value.data(), value.size());
+  size_t idx = 0;
+
+  size_t arraySize = result.size() * 2;
+  v8::Local<v8::Array> returnArray = NanNew<v8::Array>(arraySize);
+
+  for(idx = 0; idx < result.size(); ++idx) {
+    std::pair<std::string, std::string> row = result[idx];
+    std::string key = row.first;
+    std::string value = row.second;
+
+    v8::Local<v8::Value> returnKey;
+    if (iterator->keyAsBuffer) {
+      returnKey = NanNewBufferHandle((char*)key.data(), key.size());
+    } else {
+      returnKey = NanNew<v8::String>((char*)key.data(), key.size());
+    }
+
+    v8::Local<v8::Value> returnValue;
+    if (iterator->valueAsBuffer) {
+      returnValue = NanNewBufferHandle((char*)value.data(), value.size());
+    } else {
+      returnValue = NanNew<v8::String>((char*)value.data(), value.size());
+    }
+
+    // put the key & value in a descending order, so that they can be .pop:ed in javascript-land
+    returnArray->Set(NanNew<v8::Integer>(static_cast<int>(arraySize - idx * 2 - 1)), returnKey);
+    returnArray->Set(NanNew<v8::Integer>(static_cast<int>(arraySize - idx * 2 - 2)), returnValue);
   }
 
   // clean up & handle the next/end state see iterator.cc/checkEndCallback
   localCallback(iterator);
 
-  if (ok) {
-    v8::Local<v8::Value> argv[] = {
-        NanNewLocal<v8::Value>(v8::Null())
-      , returnKey
-      , returnValue
-    };
-    callback->Call(3, argv);
-  } else {
-    callback->Call(0, NULL);
-  }
+  v8::Local<v8::Value> argv[] = {
+      NanNull()
+    , returnArray
+    // when ok === false all data has been read, so it's then finished
+    , NanNew<v8::Boolean>(!ok)
+  };
+  callback->Call(3, argv);
 }
 
 /** END WORKER **/
diff --git a/src/iterator_async.h b/src/iterator_async.h
index bbc1fd0..edb8f4d 100644
--- a/src/iterator_async.h
+++ b/src/iterator_async.h
@@ -1,14 +1,14 @@
-/* Copyright (c) 2012-2013 LevelDOWN contributors
+/* Copyright (c) 2012-2014 LevelDOWN contributors
  * See list at <https://github.com/rvagg/node-leveldown#contributing>
- * MIT +no-false-attribs License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE>
+ * MIT License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE.md>
  */
 
 #ifndef LD_ITERATOR_ASYNC_H
 #define LD_ITERATOR_ASYNC_H
 
 #include <node.h>
+#include <nan.h>
 
-#include "nan.h"
 #include "async.h"
 #include "iterator.h"
 
@@ -29,8 +29,7 @@ public:
 private:
   Iterator* iterator;
   void (*localCallback)(Iterator*);
-  std::string key;
-  std::string value;
+  std::vector<std::pair<std::string, std::string> > result;
   bool ok;
 };
 
diff --git a/src/leveldown.cc b/src/leveldown.cc
index cdfecc1..20baf3d 100644
--- a/src/leveldown.cc
+++ b/src/leveldown.cc
@@ -1,6 +1,6 @@
-/* Copyright (c) 2012-2013 LevelDOWN contributors
+/* Copyright (c) 2012-2014 LevelDOWN contributors
  * See list at <https://github.com/rvagg/node-leveldown#contributing>
- * MIT +no-false-attribs License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE>
+ * MIT License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE.md>
  */
 
 #include <node.h>
@@ -16,19 +16,7 @@ namespace leveldown {
 NAN_METHOD(DestroyDB) {
   NanScope();
 
-  if (args.Length() < 2) {
-    return NanThrowError("destroy() requires `location` and `callback` arguments");
-  }
-
-  if (!args[0]->IsString()) {
-    return NanThrowError("destroy() requires a location string argument");
-  }
-
-  if (!args[1]->IsFunction()) {
-    return NanThrowError("destroy() requires a callback function argument");
-  }
-
-  char* location = NanFromV8String(args[0].As<v8::Object>(), Nan::UTF8, NULL, NULL, 0, v8::String::NO_OPTIONS);
+  NanUtf8String* location = new NanUtf8String(args[0]);
 
   NanCallback* callback = new NanCallback(
       v8::Local<v8::Function>::Cast(args[1]));
@@ -46,21 +34,9 @@ NAN_METHOD(DestroyDB) {
 NAN_METHOD(RepairDB) {
   NanScope();
 
-  if (args.Length() < 2) {
-    return NanThrowError("repair() requires `location` and `callback` arguments");
-  }
-
-  if (!args[0]->IsString()) {
-    return NanThrowError("repair() requires a location string argument");
-  }
+  NanUtf8String* location = new NanUtf8String(args[0]);
 
-  if (!args[1]->IsFunction()) {
-    return NanThrowError("repair() requires a callback function argument");
-  }
-
-  char* location = NanFromV8String(args[0].As<v8::Object>(), Nan::UTF8, NULL, NULL, 0, v8::String::NO_OPTIONS);
-
- NanCallback* callback = new NanCallback(
+  NanCallback* callback = new NanCallback(
       v8::Local<v8::Function>::Cast(args[1]));
 
   RepairWorker* worker = new RepairWorker(
@@ -79,19 +55,19 @@ void Init (v8::Handle<v8::Object> target) {
   leveldown::Batch::Init();
 
   v8::Local<v8::Function> leveldown =
-      v8::FunctionTemplate::New(LevelDOWN)->GetFunction();
+      NanNew<v8::FunctionTemplate>(LevelDOWN)->GetFunction();
 
   leveldown->Set(
-      NanSymbol("destroy")
-    , v8::FunctionTemplate::New(DestroyDB)->GetFunction()
+      NanNew("destroy")
+    , NanNew<v8::FunctionTemplate>(DestroyDB)->GetFunction()
   );
 
   leveldown->Set(
-      NanSymbol("repair")
-    , v8::FunctionTemplate::New(RepairDB)->GetFunction()
+      NanNew("repair")
+    , NanNew<v8::FunctionTemplate>(RepairDB)->GetFunction()
   );
 
-  target->Set(NanSymbol("leveldown"), leveldown);
+  target->Set(NanNew("leveldown"), leveldown);
 }
 
 NODE_MODULE(leveldown, Init)
diff --git a/src/leveldown.h b/src/leveldown.h
index 2617ade..cdeee4f 100644
--- a/src/leveldown.h
+++ b/src/leveldown.h
@@ -1,6 +1,6 @@
-/* Copyright (c) 2012-2013 LevelDOWN contributors
+/* Copyright (c) 2012-2014 LevelDOWN contributors
  * See list at <https://github.com/rvagg/node-leveldown#contributing>
- * MIT +no-false-attribs License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE>
+ * MIT License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE.md>
  */
 #ifndef LD_LEVELDOWN_H
 #define LD_LEVELDOWN_H
@@ -8,11 +8,11 @@
 #include <node.h>
 #include <node_buffer.h>
 #include <leveldb/slice.h>
-
-#include "nan.h"
+#include <nan.h>
 
 static inline size_t StringOrBufferLength(v8::Local<v8::Value> obj) {
-  return node::Buffer::HasInstance(obj->ToObject())
+  return (!obj->ToObject().IsEmpty()
+    && node::Buffer::HasInstance(obj->ToObject()))
     ? node::Buffer::Length(obj->ToObject())
     : obj->ToString()->Utf8Length();
 }
@@ -23,40 +23,37 @@ static inline void DisposeStringOrBufferFromSlice(
         v8::Persistent<v8::Object> &handle
       , leveldb::Slice slice) {
 
-  if (!node::Buffer::HasInstance(NanPersistentToLocal(handle)->Get(NanSymbol("obj"))))
-    delete[] slice.data();
-  NanDispose(handle);
+  if (!slice.empty()) {
+    v8::Local<v8::Value> obj = NanNew<v8::Object>(handle)->Get(NanNew<v8::String>("obj"));
+    if (!node::Buffer::HasInstance(obj))
+      delete[] slice.data();
+  }
+
+  NanDisposePersistent(handle);
 }
 
 static inline void DisposeStringOrBufferFromSlice(
         v8::Local<v8::Value> handle
       , leveldb::Slice slice) {
 
-  if (!node::Buffer::HasInstance(handle))
+  if (!slice.empty() && !node::Buffer::HasInstance(handle))
     delete[] slice.data();
 }
 
-#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: must call DisposeStringOrBufferFromSlice() on objects created here
 #define LD_STRING_OR_BUFFER_TO_SLICE(to, from, name)                           \
   size_t to ## Sz_;                                                            \
   char* to ## Ch_;                                                             \
-  if (node::Buffer::HasInstance(from->ToObject())) {                           \
+  if (from->IsNull() || from->IsUndefined()) {                                 \
+    to ## Sz_ = 0;                                                             \
+    to ## Ch_ = 0;                                                             \
+  } else if (!from->ToObject().IsEmpty()                                       \
+      && node::Buffer::HasInstance(from->ToObject())) {                        \
     to ## Sz_ = node::Buffer::Length(from->ToObject());                        \
-    if (to ## Sz_ == 0) {                                                      \
-      LD_RETURN_CALLBACK_OR_ERROR(callback, #name " cannot be an empty Buffer") \
-    }                                                                          \
     to ## Ch_ = node::Buffer::Data(from->ToObject());                          \
   } else {                                                                     \
     v8::Local<v8::String> to ## Str = from->ToString();                        \
     to ## Sz_ = to ## Str->Utf8Length();                                       \
-    if (to ## Sz_ == 0) {                                                      \
-      LD_RETURN_CALLBACK_OR_ERROR(callback, #name " cannot be an empty String") \
-    }                                                                          \
     to ## Ch_ = new char[to ## Sz_];                                           \
     to ## Str->WriteUtf8(                                                      \
         to ## Ch_                                                              \
@@ -69,9 +66,7 @@ static inline void DisposeStringOrBufferFromSlice(
 #define LD_RETURN_CALLBACK_OR_ERROR(callback, msg)                             \
   if (!callback.IsEmpty() && callback->IsFunction()) {                         \
     v8::Local<v8::Value> argv[] = {                                            \
-      NanNewLocal<v8::Value>(v8::Exception::Error(                          \
-        v8::String::New(msg))                                                  \
-      )                                                                        \
+      NanError(msg)                                                            \
     };                                                                         \
     LD_RUN_CALLBACK(callback, 1, argv)                                         \
     NanReturnUndefined();                                                      \
@@ -79,8 +74,8 @@ static inline void DisposeStringOrBufferFromSlice(
   return NanThrowError(msg);
 
 #define LD_RUN_CALLBACK(callback, argc, argv)                                  \
-  node::MakeCallback(                                                          \
-      v8::Context::GetCurrent()->Global(), callback, argc, argv);
+  NanMakeCallback(                                                          \
+      NanGetCurrentContext()->Global(), callback, argc, argv);
 
 /* LD_METHOD_SETUP_COMMON setup the following objects:
  *  - Database* database
diff --git a/src/leveldown_async.cc b/src/leveldown_async.cc
index db84c1f..5872659 100644
--- a/src/leveldown_async.cc
+++ b/src/leveldown_async.cc
@@ -1,6 +1,6 @@
-/* Copyright (c) 2012-2013 LevelDOWN contributors
+/* Copyright (c) 2012-2014 LevelDOWN contributors
  * See list at <https://github.com/rvagg/node-leveldown#contributing>
- * MIT +no-false-attribs License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE>
+ * MIT License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE.md>
  */
 
 #include <leveldb/db.h>
@@ -13,37 +13,37 @@ namespace leveldown {
 /** DESTROY WORKER **/
 
 DestroyWorker::DestroyWorker (
-    char* location
+    NanUtf8String* location
   , NanCallback *callback
 ) : AsyncWorker(NULL, callback)
   , location(location)
 {};
 
 DestroyWorker::~DestroyWorker () {
-  delete[] location;
+  delete location;
 }
 
 void DestroyWorker::Execute () {
   leveldb::Options options;
-  SetStatus(leveldb::DestroyDB(location, options));
+  SetStatus(leveldb::DestroyDB(**location, options));
 }
 
 /** REPAIR WORKER **/
 
 RepairWorker::RepairWorker (
-    char* location
+    NanUtf8String* location
   , NanCallback *callback
 ) : AsyncWorker(NULL, callback)
   , location(location)
 {};
 
 RepairWorker::~RepairWorker () {
-  delete[] location;
+  delete location;
 }
 
 void RepairWorker::Execute () {
   leveldb::Options options;
-  SetStatus(leveldb::RepairDB(location, options));
+  SetStatus(leveldb::RepairDB(**location, options));
 }
 
 } // namespace leveldown
diff --git a/src/leveldown_async.h b/src/leveldown_async.h
index d045a81..eb1b844 100644
--- a/src/leveldown_async.h
+++ b/src/leveldown_async.h
@@ -1,6 +1,6 @@
-/* Copyright (c) 2012-2013 LevelDOWN contributors
+/* Copyright (c) 2012-2014 LevelDOWN contributors
  * See list at <https://github.com/rvagg/node-leveldown#contributing>
- * MIT +no-false-attribs License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE>
+ * MIT License <https://github.com/rvagg/node-leveldown/blob/master/LICENSE.md>
  */
 
 #ifndef LD_LEVELDOWN_ASYNC_H
@@ -15,7 +15,7 @@ namespace leveldown {
 class DestroyWorker : public AsyncWorker {
 public:
   DestroyWorker (
-      char* location
+      NanUtf8String* location
     , NanCallback *callback
   );
 
@@ -23,13 +23,13 @@ public:
   virtual void Execute ();
 
 private:
-  char* location;
+  NanUtf8String* location;
 };
 
 class RepairWorker : public AsyncWorker {
 public:
   RepairWorker (
-      char* location
+      NanUtf8String* location
     , NanCallback *callback
   );
 
@@ -37,7 +37,7 @@ public:
   virtual void Execute ();
 
 private:
-  char* location;
+  NanUtf8String* location;
 };
 
 } // namespace leveldown
diff --git a/test/iterator-recursion-test.js b/test/iterator-recursion-test.js
new file mode 100644
index 0000000..ff2a780
--- /dev/null
+++ b/test/iterator-recursion-test.js
@@ -0,0 +1,72 @@
+const test          = require('tap').test
+    , testCommon    = require('abstract-leveldown/testCommon')
+    , leveldown     = require('../')
+    , child_process = require('child_process') 
+
+var db
+  , sourceData = (function () {
+      var d = []
+        , i = 0
+        , k
+      for (; i <  100000; i++) {
+        k = (i < 10 ? '0' : '') + i
+        d.push({
+            type  : 'put'
+          , key   : k
+          , value : Math.random()
+        })
+      }
+      return d
+    }())
+
+test('setUp common', testCommon.setUp)
+
+test('setUp db', function (t) {
+  db = leveldown(testCommon.location())
+  db.open(function () {
+    db.batch(sourceData, t.end.bind(t))
+  })
+})
+
+test('try to create an iterator with a blown stack', function (t) {
+  // Reducing the stack size down from the default 984 for the child node
+  // process makes it easier to trigger the bug condition. But making it too low
+  // causes the child process to die for other reasons.
+  var opts  = { execArgv: ["--stack-size=128"] }
+  ,   child = child_process.fork(__dirname + '/stack-blower.js', ["run"], opts)
+  
+  child.on('message', function (m) {
+      t.ok(true, m)
+      child.disconnect()
+      
+      t.end()
+    })
+    .on('exit', function (code, sig) {
+      t.ok(false, "Child exited with code=" + code + " sig=" + sig)
+
+      t.end()
+    })
+})
+
+test('iterate over a large iterator with a large watermark', function (t) {
+  var iterator = db.iterator({
+        highWaterMark: 10000000
+    })
+    , count = 0
+    , read = function () {
+        iterator.next(function () {
+          count++
+
+          if (!arguments.length)
+            t.end()
+          else
+            read()
+        })
+      }
+
+  read()
+})
+
+test('tearDown', function (t) {
+  db.close(testCommon.tearDown.bind(null, t))
+})
\ No newline at end of file
diff --git a/test/stack-blower.js b/test/stack-blower.js
new file mode 100644
index 0000000..00c48e1
--- /dev/null
+++ b/test/stack-blower.js
@@ -0,0 +1,30 @@
+/**
+  * This test uses infinite recursion to test iterator creation with limited
+  * stack space. In order to isolate the test harness, we run in a different
+  * process. This is achieved through a fork() command in
+  * iterator-recursion-test.js. To prevent tap from trying to run this test
+  * directly, we check for a command-line argument.
+  */
+const testCommon = require('abstract-leveldown/testCommon')
+    , leveldown  = require('../')
+
+if (process.argv[2] == 'run') {
+  testCommon.cleanup(function () {
+    var db    = leveldown(testCommon.location())
+      , depth = 0
+
+    db.open(function () {
+      function recurse() {
+        db.iterator({ start: '0' })
+        depth++
+        recurse()
+      }
+
+      try {
+        recurse()
+      } catch (e) {
+        process.send("Catchable error at depth " + depth)
+      }
+    })
+  })
+}

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



More information about the Pkg-javascript-commits mailing list