You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							210 lines
						
					
					
						
							4.8 KiB
						
					
					
				
			
		
		
	
	
							210 lines
						
					
					
						
							4.8 KiB
						
					
					
				/**
 | 
						|
 * 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;
 |