serverless-spy
Version:
CDK-based library for writing elegant integration tests on AWS serverless architecture and an additional web console to monitor events in real time.
210 lines (167 loc) • 4.92 kB
JavaScript
/**
* Mnemonist HashedArrayTree
* ==========================
*
* Abstract implementation of a hashed array tree representing arrays growing
* dynamically.
*/
/**
* Defaults.
*/
var DEFAULT_BLOCK_SIZE = 1024;
/**
* Helpers.
*/
function powerOfTwo(x) {
return (x & (x - 1)) === 0;
}
/**
* HashedArrayTree.
*
* @constructor
* @param {function} ArrayClass - An array constructor.
* @param {number|object} initialCapacityOrOptions - Self-explanatory.
*/
function HashedArrayTree(ArrayClass, initialCapacityOrOptions) {
if (arguments.length < 1)
throw new Error('mnemonist/hashed-array-tree: expecting at least a byte array constructor.');
var initialCapacity = initialCapacityOrOptions || 0,
blockSize = DEFAULT_BLOCK_SIZE,
initialLength = 0;
if (typeof initialCapacityOrOptions === 'object') {
initialCapacity = initialCapacityOrOptions.initialCapacity || 0;
initialLength = initialCapacityOrOptions.initialLength || 0;
blockSize = initialCapacityOrOptions.blockSize || DEFAULT_BLOCK_SIZE;
}
if (!blockSize || !powerOfTwo(blockSize))
throw new Error('mnemonist/hashed-array-tree: block size should be a power of two.');
var capacity = Math.max(initialLength, initialCapacity),
initialBlocks = Math.ceil(capacity / blockSize);
this.ArrayClass = ArrayClass;
this.length = initialLength;
this.capacity = initialBlocks * blockSize;
this.blockSize = blockSize;
this.offsetMask = blockSize - 1;
this.blockMask = Math.log2(blockSize);
// Allocating initial blocks
this.blocks = new Array(initialBlocks);
for (var i = 0; i < initialBlocks; i++)
this.blocks[i] = new this.ArrayClass(this.blockSize);
}
/**
* Method used to set a value.
*
* @param {number} index - Index to edit.
* @param {any} value - Value.
* @return {HashedArrayTree}
*/
HashedArrayTree.prototype.set = function(index, value) {
// Out of bounds?
if (this.length < index)
throw new Error('HashedArrayTree(' + this.ArrayClass.name + ').set: index out of bounds.');
var block = index >> this.blockMask,
i = index & this.offsetMask;
this.blocks[block][i] = value;
return this;
};
/**
* Method used to get a value.
*
* @param {number} index - Index to retrieve.
* @return {any}
*/
HashedArrayTree.prototype.get = function(index) {
if (this.length < index)
return;
var block = index >> this.blockMask,
i = index & this.offsetMask;
return this.blocks[block][i];
};
/**
* Method used to grow the array.
*
* @param {number} capacity - Optional capacity to accomodate.
* @return {HashedArrayTree}
*/
HashedArrayTree.prototype.grow = function(capacity) {
if (typeof capacity !== 'number')
capacity = this.capacity + this.blockSize;
if (this.capacity >= capacity)
return this;
while (this.capacity < capacity) {
this.blocks.push(new this.ArrayClass(this.blockSize));
this.capacity += this.blockSize;
}
return this;
};
/**
* Method used to resize the array. Won't deallocate.
*
* @param {number} length - Target length.
* @return {HashedArrayTree}
*/
HashedArrayTree.prototype.resize = function(length) {
if (length === this.length)
return this;
if (length < this.length) {
this.length = length;
return this;
}
this.length = length;
this.grow(length);
return this;
};
/**
* Method used to push a value into the array.
*
* @param {any} value - Value to push.
* @return {number} - Length of the array.
*/
HashedArrayTree.prototype.push = function(value) {
if (this.capacity === this.length)
this.grow();
var index = this.length;
var block = index >> this.blockMask,
i = index & this.offsetMask;
this.blocks[block][i] = value;
return ++this.length;
};
/**
* Method used to pop the last value of the array.
*
* @return {number} - The popped value.
*/
HashedArrayTree.prototype.pop = function() {
if (this.length === 0)
return;
var lastBlock = this.blocks[this.blocks.length - 1];
var i = (--this.length) & this.offsetMask;
return lastBlock[i];
};
/**
* Convenience known methods.
*/
HashedArrayTree.prototype.inspect = function() {
var proxy = new this.ArrayClass(this.length),
block;
for (var i = 0, l = this.length; i < l; i++) {
block = i >> this.blockMask;
proxy[i] = this.blocks[block][i & this.offsetMask];
}
proxy.type = this.ArrayClass.name;
proxy.items = this.length;
proxy.capacity = this.capacity;
proxy.blockSize = this.blockSize;
// Trick so that node displays the name of the constructor
Object.defineProperty(proxy, 'constructor', {
value: HashedArrayTree,
enumerable: false
});
return proxy;
};
if (typeof Symbol !== 'undefined')
HashedArrayTree.prototype[Symbol.for('nodejs.util.inspect.custom')] = HashedArrayTree.prototype.inspect;
/**
* Exporting.
*/
module.exports = HashedArrayTree;