lowlevelup: refactor option handling.
This commit is contained in:
parent
f0cc8eb128
commit
27f89186a5
@ -15,50 +15,15 @@ var backends = require('./backends');
|
||||
* Create a database.
|
||||
* @alias module:db.LDB
|
||||
* @param {Object} options
|
||||
* @param {Boolean} options.compression
|
||||
* @param {Number} options.cacheSize
|
||||
* @param {Number} options.writeBufferSize
|
||||
* @param {Number} options.maxOpenFiles
|
||||
* @param {Boolean} options.sync
|
||||
* @param {Number} options.mapSize
|
||||
* @param {Boolean} options.writeMap
|
||||
* @param {String} options.db - Database backend (`"leveldb"` by default).
|
||||
* @param {String} options.name - Database name.
|
||||
* @param {String} options.location - Database location (overrides `name`).
|
||||
* @returns {LowlevelUp}
|
||||
*/
|
||||
|
||||
function LDB(options) {
|
||||
var target = LDB.getTarget(options);
|
||||
var cacheSize = options.cacheSize;
|
||||
var result = LDB.getBackend(options);
|
||||
var backend = result.backend;
|
||||
var location = result.location;
|
||||
|
||||
if (!cacheSize)
|
||||
cacheSize = 16 << 20;
|
||||
|
||||
return new LowlevelUp(target.location, {
|
||||
// Generic
|
||||
createIfMissing: options.createIfMissing !== false,
|
||||
errorIfExists: options.errorIfExists === true,
|
||||
|
||||
// LevelDB
|
||||
compression: options.compression === true,
|
||||
cacheSize: cacheSize / 2 | 0,
|
||||
writeBufferSize: cacheSize / 4 | 0,
|
||||
maxOpenFiles: options.maxFiles || 64,
|
||||
paranoidChecks: false,
|
||||
memory: false,
|
||||
|
||||
// LMDB
|
||||
sync: options.sync || false,
|
||||
mapSize: options.mapSize || 256 * (1024 << 20),
|
||||
writeMap: options.writeMap || false,
|
||||
noSubdir: options.noSubdir !== false,
|
||||
|
||||
// Browser
|
||||
bufferKeys: options.bufferKeys,
|
||||
|
||||
db: target.db
|
||||
});
|
||||
return new LowlevelUp(backend, location, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -67,7 +32,7 @@ function LDB(options) {
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
LDB.getBackend = function getBackend(db) {
|
||||
LDB.getName = function getName(db) {
|
||||
var name, ext;
|
||||
|
||||
if (!db)
|
||||
@ -103,7 +68,10 @@ LDB.getBackend = function getBackend(db) {
|
||||
break;
|
||||
}
|
||||
|
||||
return { name: name, ext: ext };
|
||||
return {
|
||||
name: name,
|
||||
ext: ext
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
@ -112,20 +80,19 @@ LDB.getBackend = function getBackend(db) {
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
LDB.getTarget = function getTarget(options) {
|
||||
var backend = LDB.getBackend(options.db);
|
||||
LDB.getBackend = function getBackend(options) {
|
||||
var result = LDB.getName(options.db);
|
||||
var backend = backends.get(result.name);
|
||||
var location = options.location;
|
||||
var db = backends.get(backend.name);
|
||||
|
||||
if (typeof location !== 'string') {
|
||||
assert(backend.name === 'memory', 'Location required.');
|
||||
assert(result.name === 'memory', 'Location required.');
|
||||
location = 'memory';
|
||||
}
|
||||
|
||||
return {
|
||||
db: db,
|
||||
backend: backend.name,
|
||||
location: location + '.' + backend.ext
|
||||
backend: backend,
|
||||
location: location + '.' + result.ext
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
var Level = require('level-js');
|
||||
|
||||
function DB(location) {
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var util = require('../utils/util');
|
||||
var Lock = require('../utils/lock');
|
||||
var co = require('../utils/co');
|
||||
var VERSION_ERROR;
|
||||
@ -23,43 +22,71 @@ var VERSION_ERROR;
|
||||
*
|
||||
* @alias module:db.LowlevelUp
|
||||
* @constructor
|
||||
* @param {String} file - Location.
|
||||
* @param {Object} options - Leveldown options.
|
||||
* @param {Function} backend - Database backend.
|
||||
* @param {String} location - File location.
|
||||
* @param {Object?} options - Leveldown options.
|
||||
*/
|
||||
|
||||
function LowlevelUp(file, options) {
|
||||
function LowlevelUp(backend, location, options) {
|
||||
if (!(this instanceof LowlevelUp))
|
||||
return new LowlevelUp(file, options);
|
||||
return new LowlevelUp(backend, location, options);
|
||||
|
||||
assert(typeof file === 'string', 'Filename is required.');
|
||||
assert(options, 'Options are required.');
|
||||
assert(options.db, 'Database backend is required.');
|
||||
assert(typeof backend === 'function', 'Backend is required.');
|
||||
assert(typeof location === 'string', 'Filename is required.');
|
||||
|
||||
this.options = options;
|
||||
this.backend = options.db;
|
||||
this.location = file;
|
||||
this.bufferKeys = options.bufferKeys === true;
|
||||
this.options = new LLUOptions(options);
|
||||
this.backend = backend;
|
||||
this.location = location;
|
||||
this.locker = new Lock();
|
||||
|
||||
this.loading = false;
|
||||
this.closing = false;
|
||||
this.loaded = false;
|
||||
|
||||
this.db = new options.db(file);
|
||||
this.db = null;
|
||||
this.binding = null;
|
||||
|
||||
// Stay as close to the metal as possible.
|
||||
// We want to make calls to C++ directly.
|
||||
while (this.db.db && this.db.db.put && this.db.db !== this.db)
|
||||
this.db = this.db.db;
|
||||
|
||||
this.binding = this.db;
|
||||
|
||||
if (this.db.binding)
|
||||
this.binding = this.db.binding;
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the database.
|
||||
* @method
|
||||
* @private
|
||||
*/
|
||||
|
||||
LowlevelUp.prototype.init = function init() {
|
||||
var backend = this.backend;
|
||||
var db = new backend(this.location);
|
||||
var binding = db;
|
||||
|
||||
// Stay as close to the metal as possible.
|
||||
// We want to make calls to C++ directly.
|
||||
while (db.db) {
|
||||
// Not a database.
|
||||
if (typeof db.db.put !== 'function')
|
||||
break;
|
||||
|
||||
// Recursive.
|
||||
if (db.db === db)
|
||||
break;
|
||||
|
||||
// Go deeper.
|
||||
db = db.db;
|
||||
binding = db;
|
||||
}
|
||||
|
||||
// A lower-level binding.
|
||||
if (db.binding)
|
||||
binding = db.binding;
|
||||
|
||||
this.db = db;
|
||||
this.binding = binding;
|
||||
};
|
||||
|
||||
/**
|
||||
* Open the database.
|
||||
* @method
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
@ -72,22 +99,9 @@ LowlevelUp.prototype.open = co(function* open() {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Close the database.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
LowlevelUp.prototype.close = co(function* close() {
|
||||
var unlock = yield this.locker.lock();
|
||||
try {
|
||||
return yield this._close();
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Open the database (without a lock).
|
||||
* @method
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
@ -112,8 +126,24 @@ LowlevelUp.prototype._open = co(function* open() {
|
||||
this.loaded = true;
|
||||
});
|
||||
|
||||
/**
|
||||
* Close the database.
|
||||
* @method
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
LowlevelUp.prototype.close = co(function* close() {
|
||||
var unlock = yield this.locker.lock();
|
||||
try {
|
||||
return yield this._close();
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Close the database (without a lock).
|
||||
* @method
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
@ -322,34 +352,10 @@ LowlevelUp.prototype.batch = function batch(ops) {
|
||||
*/
|
||||
|
||||
LowlevelUp.prototype.iterator = function iterator(options) {
|
||||
var opt;
|
||||
|
||||
if (!this.loaded)
|
||||
throw new Error('Database is closed.');
|
||||
|
||||
opt = {
|
||||
gte: options.gte,
|
||||
lte: options.lte,
|
||||
keys: options.keys !== false,
|
||||
values: options.values || false,
|
||||
fillCache: options.fillCache || false,
|
||||
keyAsBuffer: this.bufferKeys,
|
||||
valueAsBuffer: true,
|
||||
reverse: options.reverse || false,
|
||||
highWaterMark: options.highWaterMark || 16 * 1024
|
||||
};
|
||||
|
||||
// Workaround for a leveldown
|
||||
// bug I haven't fixed yet.
|
||||
if (options.limit != null)
|
||||
opt.limit = options.limit;
|
||||
|
||||
if (options.keyAsBuffer != null)
|
||||
opt.keyAsBuffer = options.keyAsBuffer;
|
||||
|
||||
assert(opt.keys || opt.values, 'Keys and/or values must be chosen.');
|
||||
|
||||
return new Iterator(this, opt);
|
||||
return new Iterator(this, options);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -370,8 +376,8 @@ LowlevelUp.prototype.getProperty = function getProperty(name) {
|
||||
|
||||
/**
|
||||
* Calculate approximate database size.
|
||||
* @param {String} start - Start key.
|
||||
* @param {String} end - End key.
|
||||
* @param {String|Buffer} start - Start key.
|
||||
* @param {String|Buffer} end - End key.
|
||||
* @returns {Promise} - Returns Number.
|
||||
*/
|
||||
|
||||
@ -393,6 +399,31 @@ LowlevelUp.prototype.approximateSize = function approximateSize(start, end) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Compact range of keys.
|
||||
* @param {String|Buffer} start - Start key.
|
||||
* @param {String|Buffer} end - End key.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
LowlevelUp.prototype.compactRange = function compactRange(start, end) {
|
||||
var self = this;
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (!self.loaded) {
|
||||
reject(new Error('Database is closed.'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self.binding.compactRange) {
|
||||
reject(new Error('Cannot compact range.'));
|
||||
return;
|
||||
}
|
||||
|
||||
self.binding.compactRange(start, end, co.wrap(resolve, reject));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether a key exists.
|
||||
* @method
|
||||
@ -588,8 +619,7 @@ LowlevelUp.prototype.checkVersion = co(function* checkVersion(key, version) {
|
||||
*/
|
||||
|
||||
LowlevelUp.prototype.clone = co(function* clone(path) {
|
||||
var options = util.merge({}, this.options);
|
||||
var opt = { keys: true, values: true };
|
||||
var options = new LLUOptions(this.options);
|
||||
var hwm = 256 << 20;
|
||||
var total = 0;
|
||||
var tmp, batch, iter, item;
|
||||
@ -600,12 +630,16 @@ LowlevelUp.prototype.clone = co(function* clone(path) {
|
||||
options.createIfMissing = true;
|
||||
options.errorIfExists = true;
|
||||
|
||||
tmp = new LowlevelUp(path, options);
|
||||
tmp = new LowlevelUp(this.backend, path, options);
|
||||
|
||||
yield tmp.open();
|
||||
|
||||
batch = tmp.batch();
|
||||
iter = this.iterator(opt);
|
||||
|
||||
iter = this.iterator({
|
||||
keys: true,
|
||||
values: true
|
||||
});
|
||||
|
||||
for (;;) {
|
||||
item = yield iter.next();
|
||||
@ -698,6 +732,9 @@ Batch.prototype.clear = function clear() {
|
||||
*/
|
||||
|
||||
function Iterator(db, options) {
|
||||
options = new IteratorOptions(options);
|
||||
options.keyAsBuffer = db.options.bufferKeys;
|
||||
|
||||
this.iter = db.db.iterator(options);
|
||||
}
|
||||
|
||||
@ -722,14 +759,14 @@ Iterator.prototype.next = function() {
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(new KeyValue(key, value));
|
||||
resolve(new IteratorItem(key, value));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Seek to an arbitrary key.
|
||||
* @param {String|Buffer}
|
||||
* @param {String|Buffer} key
|
||||
*/
|
||||
|
||||
Iterator.prototype.seek = function seek(key) {
|
||||
@ -748,15 +785,235 @@ Iterator.prototype.end = function end() {
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
/**
|
||||
* Iterator Item
|
||||
* @ignore
|
||||
* @constructor
|
||||
* @param {String|Buffer} key
|
||||
* @param {String|Buffer} value
|
||||
* @property {String|Buffer} key
|
||||
* @property {String|Buffer} value
|
||||
*/
|
||||
|
||||
function KeyValue(key, value) {
|
||||
function IteratorItem(key, value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* LowlevelUp Options
|
||||
* @constructor
|
||||
* @ignore
|
||||
* @param {Object} options
|
||||
*/
|
||||
|
||||
function LLUOptions(options) {
|
||||
this.createIfMissing = true;
|
||||
this.errorIfExists = false;
|
||||
this.compression = true;
|
||||
this.cacheSize = 8 << 20;
|
||||
this.writeBufferSize = 4 << 20;
|
||||
this.maxOpenFiles = 64;
|
||||
this.maxFileSize = 2 << 20;
|
||||
this.paranoidChecks = false;
|
||||
this.memory = false;
|
||||
this.sync = false;
|
||||
this.mapSize = 256 * (1024 << 20);
|
||||
this.writeMap = false;
|
||||
this.noSubdir = true;
|
||||
this.bufferKeys = true;
|
||||
|
||||
if (options)
|
||||
this.fromOptions(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject properties from options.
|
||||
* @private
|
||||
* @param {Object} options
|
||||
* @returns {LLUOptions}
|
||||
*/
|
||||
|
||||
LLUOptions.prototype.fromOptions = function fromOptions(options) {
|
||||
assert(options, 'Options are required.');
|
||||
|
||||
if (options.createIfMissing != null) {
|
||||
assert(typeof options.createIfMissing === 'boolean',
|
||||
'`createIfMissing` must be a boolean.');
|
||||
this.createIfMissing = options.createIfMissing;
|
||||
}
|
||||
|
||||
if (options.errorIfExists != null) {
|
||||
assert(typeof options.errorIfExists === 'boolean',
|
||||
'`errorIfExists` must be a boolean.');
|
||||
this.errorIfExists = options.errorIfExists;
|
||||
}
|
||||
|
||||
if (options.compression != null) {
|
||||
assert(typeof options.compression === 'boolean',
|
||||
'`compression` must be a boolean.');
|
||||
this.compression = options.compression;
|
||||
}
|
||||
|
||||
if (options.cacheSize != null) {
|
||||
assert(typeof options.cacheSize === 'number',
|
||||
'`cacheSize` must be a number.');
|
||||
assert(options.cacheSize >= 0);
|
||||
this.cacheSize = Math.floor(options.cacheSize / 2);
|
||||
this.writeBufferSize = Math.floor(options.cacheSize / 4);
|
||||
}
|
||||
|
||||
if (options.maxFiles != null) {
|
||||
assert(typeof options.maxFiles === 'number',
|
||||
'`maxFiles` must be a number.');
|
||||
assert(options.maxFiles >= 0);
|
||||
this.maxOpenFiles = options.maxFiles;
|
||||
}
|
||||
|
||||
if (options.maxFileSize != null) {
|
||||
assert(typeof options.maxFileSize === 'number',
|
||||
'`maxFileSize` must be a number.');
|
||||
assert(options.maxFileSize >= 0);
|
||||
this.maxFileSize = options.maxFileSize;
|
||||
}
|
||||
|
||||
if (options.paranoidChecks != null) {
|
||||
assert(typeof options.paranoidChecks === 'boolean',
|
||||
'`paranoidChecks` must be a boolean.');
|
||||
this.paranoidChecks = options.paranoidChecks;
|
||||
}
|
||||
|
||||
if (options.memory != null) {
|
||||
assert(typeof options.memory === 'boolean',
|
||||
'`memory` must be a boolean.');
|
||||
this.memory = options.memory;
|
||||
}
|
||||
|
||||
if (options.sync != null) {
|
||||
assert(typeof options.sync === 'boolean',
|
||||
'`sync` must be a boolean.');
|
||||
this.sync = options.sync;
|
||||
}
|
||||
|
||||
if (options.mapSize != null) {
|
||||
assert(typeof options.mapSize === 'number',
|
||||
'`mapSize` must be a number.');
|
||||
assert(options.mapSize >= 0);
|
||||
this.mapSize = options.mapSize;
|
||||
}
|
||||
|
||||
if (options.writeMap != null) {
|
||||
assert(typeof options.writeMap === 'boolean',
|
||||
'`writeMap` must be a boolean.');
|
||||
this.writeMap = options.writeMap;
|
||||
}
|
||||
|
||||
if (options.noSubdir != null) {
|
||||
assert(typeof options.noSubdir === 'boolean',
|
||||
'`noSubdir` must be a boolean.');
|
||||
this.noSubdir = options.noSubdir;
|
||||
}
|
||||
|
||||
if (options.bufferKeys != null) {
|
||||
assert(typeof options.bufferKeys === 'boolean',
|
||||
'`bufferKeys` must be a boolean.');
|
||||
this.bufferKeys = options.bufferKeys;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterator Options
|
||||
* @constructor
|
||||
* @ignore
|
||||
* @param {Object} options
|
||||
*/
|
||||
|
||||
function IteratorOptions(options) {
|
||||
this.gte = null;
|
||||
this.lte = null;
|
||||
this.keys = true;
|
||||
this.values = false;
|
||||
this.fillCache = false;
|
||||
this.keyAsBuffer = true;
|
||||
this.valueAsBuffer = true;
|
||||
this.reverse = false;
|
||||
this.highWaterMark = 16 * 1024;
|
||||
|
||||
// Note: do not add this property.
|
||||
// this.limit = null;
|
||||
|
||||
if (options)
|
||||
this.fromOptions(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject properties from options.
|
||||
* @private
|
||||
* @param {Object} options
|
||||
* @returns {IteratorOptions}
|
||||
*/
|
||||
|
||||
IteratorOptions.prototype.fromOptions = function fromOptions(options) {
|
||||
assert(options, 'Options are required.');
|
||||
|
||||
if (options.gte != null) {
|
||||
assert(Buffer.isBuffer(options.gte) || typeof options.gte === 'string');
|
||||
this.gte = options.gte;
|
||||
}
|
||||
|
||||
if (options.lte != null) {
|
||||
assert(Buffer.isBuffer(options.lte) || typeof options.lte === 'string');
|
||||
this.lte = options.lte;
|
||||
}
|
||||
|
||||
if (options.keys != null) {
|
||||
assert(typeof options.keys === 'boolean');
|
||||
this.keys = options.keys;
|
||||
}
|
||||
|
||||
if (options.values != null) {
|
||||
assert(typeof options.values === 'boolean');
|
||||
this.values = options.values;
|
||||
}
|
||||
|
||||
if (options.fillCache != null) {
|
||||
assert(typeof options.fillCache === 'boolean');
|
||||
this.fillCache = options.fillCache;
|
||||
}
|
||||
|
||||
if (options.keyAsBuffer != null) {
|
||||
assert(typeof options.keyAsBuffer === 'boolean');
|
||||
this.keyAsBuffer = options.keyAsBuffer;
|
||||
}
|
||||
|
||||
if (options.valueAsBuffer != null) {
|
||||
assert(typeof options.valueAsBuffer === 'boolean');
|
||||
this.valueAsBuffer = options.valueAsBuffer;
|
||||
}
|
||||
|
||||
if (options.reverse != null) {
|
||||
assert(typeof options.reverse === 'boolean');
|
||||
this.reverse = options.reverse;
|
||||
}
|
||||
|
||||
if (options.limit != null) {
|
||||
assert(typeof options.limit === 'number');
|
||||
assert(options.limit >= 0);
|
||||
this.limit = options.limit;
|
||||
}
|
||||
|
||||
if (!this.keys && !this.values)
|
||||
throw new Error('Keys and/or values must be chosen.');
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
function isNotFound(err) {
|
||||
if (!err)
|
||||
return false;
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"bcoin-native": "0.0.16",
|
||||
"leveldown": "1.5.0",
|
||||
"leveldown": "1.7.0-0",
|
||||
"secp256k1": "3.2.5",
|
||||
"socket.io": "1.7.3",
|
||||
"socket.io-client": "1.7.3"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user