Projektstart
This commit is contained in:
447
backend/node_modules/mnemonist/multi-array.js
generated
vendored
Normal file
447
backend/node_modules/mnemonist/multi-array.js
generated
vendored
Normal file
@@ -0,0 +1,447 @@
|
||||
/**
|
||||
* Mnemonist MultiArray
|
||||
* =====================
|
||||
*
|
||||
* Memory-efficient representation of an array of arrays. In JavaScript and
|
||||
* most high-level languages, creating objects has a cost. This implementation
|
||||
* is therefore able to represent nested containers without needing to create
|
||||
* objects. This works by storing singly linked lists in a single flat array.
|
||||
* However, this means that this structure comes with some read/write
|
||||
* overhead but consume very few memory.
|
||||
*
|
||||
* This structure should be particularly suited to indices that will need to
|
||||
* merge arrays anyway when queried and that are quite heavily hit (such as
|
||||
* an inverted index or a quad tree).
|
||||
*
|
||||
* Note: the implementation does not require to keep track of head pointers
|
||||
* but this comes with some advantages such as not needing to offset pointers
|
||||
* by 1 and being able to perform in-order iteration. This remains quite lean
|
||||
* in memory and does not hinder performance whatsoever.
|
||||
*/
|
||||
var typed = require('./utils/typed-arrays.js'),
|
||||
Vector = require('./vector.js'),
|
||||
Iterator = require('obliterator/iterator');
|
||||
|
||||
var PointerVector = Vector.PointerVector;
|
||||
|
||||
/**
|
||||
* MultiArray.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function MultiArray(Container, capacity) {
|
||||
this.capacity = capacity || null;
|
||||
this.Container = Container || Array;
|
||||
this.hasFixedCapacity = this.capacity !== null;
|
||||
|
||||
if (typeof this.Container !== 'function')
|
||||
throw new Error('mnemonist/multi-array.constructor: container should be a function.');
|
||||
|
||||
this.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to clear the structure.
|
||||
*
|
||||
* @return {undefined}
|
||||
*/
|
||||
MultiArray.prototype.clear = function() {
|
||||
|
||||
// Properties
|
||||
this.size = 0;
|
||||
this.dimension = 0;
|
||||
|
||||
// NOTE: #.heads, #.tails & #.lengths have a length equal to the dimension of
|
||||
// the array, while #.pointers has a length equal to its size.
|
||||
|
||||
// Storage
|
||||
if (this.hasFixedCapacity) {
|
||||
var capacity = this.capacity;
|
||||
|
||||
var PointerArray = typed.getPointerArray(capacity);
|
||||
|
||||
var policy = function(currentCapacity) {
|
||||
var newCapacity = Math.max(1, Math.ceil(currentCapacity * 1.5));
|
||||
|
||||
// Clamping max allocation
|
||||
return Math.min(newCapacity, capacity);
|
||||
};
|
||||
|
||||
var initialCapacity = Math.max(8, capacity);
|
||||
|
||||
this.tails = new Vector(PointerArray, {policy: policy, initialCapacity: initialCapacity});
|
||||
this.lengths = new Vector(PointerArray, {policy: policy, initialCapacity: initialCapacity});
|
||||
this.pointers = new PointerArray(capacity);
|
||||
|
||||
this.items = new this.Container(capacity);
|
||||
}
|
||||
else {
|
||||
|
||||
this.tails = new PointerVector();
|
||||
this.lengths = new PointerVector();
|
||||
this.pointers = new PointerVector();
|
||||
|
||||
this.items = new this.Container();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Method used to add an item to the container at the given index.
|
||||
*
|
||||
* @param {number} index - Index of the container.
|
||||
* @param {any} item - Item to add.
|
||||
* @return {MultiArray}
|
||||
*/
|
||||
MultiArray.prototype.set = function(index, item) {
|
||||
var pointer = this.size;
|
||||
|
||||
// TODO: this can be factorized!
|
||||
|
||||
if (this.hasFixedCapacity) {
|
||||
|
||||
if (index >= this.capacity || this.size === this.capacity)
|
||||
throw new Error('mnemonist/multi-array: attempting to allocate further than capacity.');
|
||||
|
||||
// This linked list does not exist yet. Let's create it
|
||||
if (index >= this.dimension) {
|
||||
|
||||
// We may be required to grow the vectors
|
||||
this.dimension = index + 1;
|
||||
this.tails.grow(this.dimension);
|
||||
this.lengths.grow(this.dimension);
|
||||
|
||||
this.tails.resize(this.dimension);
|
||||
this.lengths.resize(this.dimension);
|
||||
|
||||
this.lengths.array[index] = 1;
|
||||
}
|
||||
|
||||
// Appending to the list
|
||||
else {
|
||||
this.pointers[pointer] = this.tails.array[index];
|
||||
this.lengths.array[index]++;
|
||||
}
|
||||
|
||||
this.tails.array[index] = pointer;
|
||||
this.items[pointer] = item;
|
||||
}
|
||||
else {
|
||||
|
||||
// This linked list does not exist yet. Let's create it
|
||||
if (index >= this.dimension) {
|
||||
|
||||
// We may be required to grow the vectors
|
||||
this.dimension = index + 1;
|
||||
this.tails.grow(this.dimension);
|
||||
this.lengths.grow(this.dimension);
|
||||
|
||||
this.tails.resize(this.dimension);
|
||||
this.lengths.resize(this.dimension);
|
||||
|
||||
this.pointers.push(0);
|
||||
this.lengths.array[index] = 1;
|
||||
}
|
||||
|
||||
// Appending to the list
|
||||
else {
|
||||
this.pointers.push(this.tails.array[index]);
|
||||
this.lengths.array[index]++;
|
||||
}
|
||||
|
||||
this.tails.array[index] = pointer;
|
||||
this.items.push(item);
|
||||
}
|
||||
|
||||
this.size++;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Method used to push a new container holding the given value.
|
||||
* Note: it might be useful to make this function able to take an iterable
|
||||
* or variadic someday. For the time being it's just a convenience for
|
||||
* implementing compact multi maps and such.
|
||||
*
|
||||
* @param {any} item - Item to add.
|
||||
* @return {MultiArray}
|
||||
*/
|
||||
MultiArray.prototype.push = function(item) {
|
||||
var pointer = this.size,
|
||||
index = this.dimension;
|
||||
|
||||
if (this.hasFixedCapacity) {
|
||||
|
||||
if (index >= this.capacity || this.size === this.capacity)
|
||||
throw new Error('mnemonist/multi-array: attempting to allocate further than capacity.');
|
||||
|
||||
this.items[pointer] = item;
|
||||
}
|
||||
else {
|
||||
this.items.push(item);
|
||||
this.pointers.push(0);
|
||||
}
|
||||
|
||||
this.lengths.push(1);
|
||||
this.tails.push(pointer);
|
||||
|
||||
this.dimension++;
|
||||
this.size++;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Method used to get the desired container.
|
||||
*
|
||||
* @param {number} index - Index of the container.
|
||||
* @return {array}
|
||||
*/
|
||||
MultiArray.prototype.get = function(index) {
|
||||
if (index >= this.dimension)
|
||||
return;
|
||||
|
||||
var pointers = this.hasFixedCapacity ? this.pointers : this.pointers.array;
|
||||
|
||||
var pointer = this.tails.array[index],
|
||||
length = this.lengths.array[index],
|
||||
i = length;
|
||||
|
||||
var array = new this.Container(length);
|
||||
|
||||
while (i !== 0) {
|
||||
array[--i] = this.items[pointer];
|
||||
pointer = pointers[pointer];
|
||||
}
|
||||
|
||||
return array;
|
||||
};
|
||||
|
||||
/**
|
||||
* Method used to check if a container exists at the given index.
|
||||
*
|
||||
* @param {number} index - Index of the container.
|
||||
* @return {boolean}
|
||||
*/
|
||||
MultiArray.prototype.has = function(index) {
|
||||
return index < this.dimension;
|
||||
};
|
||||
|
||||
/**
|
||||
* Method used to get the size of the container stored at given index.
|
||||
*
|
||||
* @param {number} index - Index of the container.
|
||||
* @return {number}
|
||||
*/
|
||||
MultiArray.prototype.multiplicity = function(index) {
|
||||
if (index >= this.dimension)
|
||||
return 0;
|
||||
|
||||
return this.lengths.array[index];
|
||||
};
|
||||
MultiArray.prototype.count = MultiArray.prototype.multiplicity;
|
||||
|
||||
/**
|
||||
* Method used to iterate over the structure's containers.
|
||||
*
|
||||
* @return {Iterator}
|
||||
*/
|
||||
MultiArray.prototype.containers = function() {
|
||||
var self = this,
|
||||
l = this.dimension,
|
||||
i = 0;
|
||||
|
||||
return new Iterator(function() {
|
||||
if (i >= l)
|
||||
return {done: true};
|
||||
|
||||
return {value: self.get(i++)};
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Method used to iterate over the structure's associations.
|
||||
*
|
||||
* @return {Iterator}
|
||||
*/
|
||||
MultiArray.prototype.associations = function() {
|
||||
var self = this,
|
||||
l = this.dimension,
|
||||
i = 0;
|
||||
|
||||
return new Iterator(function() {
|
||||
if (i >= l)
|
||||
return {done: true};
|
||||
|
||||
var data = {value: [i, self.get(i)]};
|
||||
|
||||
i++;
|
||||
|
||||
return data;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Method used to iterate over the structure's values in the global insertion
|
||||
* order.
|
||||
*
|
||||
* @param {number} [index] - Optionally, iterate over the values of a single
|
||||
* container at index.
|
||||
* @return {Iterator}
|
||||
*/
|
||||
MultiArray.prototype.values = function(index) {
|
||||
var items = this.items,
|
||||
length,
|
||||
i = 0;
|
||||
|
||||
if (typeof index === 'number') {
|
||||
if (index >= this.dimension)
|
||||
return Iterator.empty();
|
||||
|
||||
length = this.lengths.array[index];
|
||||
items = this.items;
|
||||
|
||||
var pointers = this.hasFixedCapacity ? this.pointers : this.pointers.array;
|
||||
|
||||
if (length === 0)
|
||||
return Iterator.empty();
|
||||
|
||||
var pointer = this.tails.array[index],
|
||||
v;
|
||||
|
||||
return new Iterator(function() {
|
||||
if (i === length)
|
||||
return {done: true};
|
||||
|
||||
i++;
|
||||
v = items[pointer];
|
||||
pointer = pointers[pointer];
|
||||
|
||||
return {done: false, value: v};
|
||||
});
|
||||
}
|
||||
|
||||
length = this.size;
|
||||
|
||||
return new Iterator(function() {
|
||||
if (i >= length)
|
||||
return {done: true};
|
||||
|
||||
return {done: false, value: items[i++]};
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Method used to iterate over the structure's entries.
|
||||
*
|
||||
* @return {Iterator}
|
||||
*/
|
||||
MultiArray.prototype.entries = function() {
|
||||
if (this.size === 0)
|
||||
return Iterator.empty();
|
||||
|
||||
var inContainer = false,
|
||||
pointer,
|
||||
length,
|
||||
i = 0,
|
||||
j = 0,
|
||||
l = this.dimension,
|
||||
v;
|
||||
|
||||
var pointers = this.hasFixedCapacity ? this.pointers : this.pointers.array,
|
||||
items = this.items,
|
||||
tails = this.tails.array,
|
||||
lengths = this.lengths.array;
|
||||
|
||||
var iterator = new Iterator(function next() {
|
||||
if (!inContainer) {
|
||||
|
||||
if (i >= l)
|
||||
return {done: true};
|
||||
|
||||
length = lengths[i];
|
||||
pointer = tails[i];
|
||||
i++;
|
||||
|
||||
if (length === 0)
|
||||
return next();
|
||||
|
||||
j = 0;
|
||||
inContainer = true;
|
||||
}
|
||||
|
||||
if (j === length) {
|
||||
inContainer = false;
|
||||
return next();
|
||||
}
|
||||
|
||||
v = items[pointer];
|
||||
|
||||
// TODO: guard for out-of-bounds
|
||||
pointer = pointers[pointer];
|
||||
|
||||
j++;
|
||||
|
||||
return {
|
||||
done: false,
|
||||
value: [i - 1, v]
|
||||
};
|
||||
});
|
||||
|
||||
return iterator;
|
||||
};
|
||||
|
||||
/**
|
||||
* Method used to iterate over the structure's keys.
|
||||
*
|
||||
* @return {Iterator}
|
||||
*/
|
||||
MultiArray.prototype.keys = function() {
|
||||
var i = 0,
|
||||
l = this.dimension;
|
||||
|
||||
return new Iterator(function() {
|
||||
if (i >= l)
|
||||
return {done: true};
|
||||
|
||||
return {done: false, value: i++};
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience known methods.
|
||||
*/
|
||||
MultiArray.prototype.inspect = function() {
|
||||
var proxy = new Array(this.dimension),
|
||||
i,
|
||||
l;
|
||||
|
||||
for (i = 0, l = this.dimension; i < l; i++)
|
||||
proxy[i] = Array.from(this.get(i));
|
||||
|
||||
if (this.hasFixedCapacity) {
|
||||
proxy.type = this.Container.name;
|
||||
proxy.capacity = this.capacity;
|
||||
}
|
||||
|
||||
proxy.size = this.size;
|
||||
proxy.dimension = this.dimension;
|
||||
|
||||
// Trick so that node displays the name of the constructor
|
||||
Object.defineProperty(proxy, 'constructor', {
|
||||
value: MultiArray,
|
||||
enumerable: false
|
||||
});
|
||||
|
||||
return proxy;
|
||||
};
|
||||
|
||||
if (typeof Symbol !== 'undefined')
|
||||
MultiArray.prototype[Symbol.for('nodejs.util.inspect.custom')] = MultiArray.prototype.inspect;
|
||||
|
||||
// TODO: .from
|
||||
|
||||
/**
|
||||
* Exporting.
|
||||
*/
|
||||
module.exports = MultiArray;
|
||||
Reference in New Issue
Block a user