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.
132 lines
3.0 KiB
132 lines
3.0 KiB
3 years ago
|
/**
|
||
|
* Mnemonist CircularBuffer
|
||
|
* =========================
|
||
|
*
|
||
|
* Circular buffer implementation fit to use as a finite deque.
|
||
|
*/
|
||
|
var iterables = require('./utils/iterables.js'),
|
||
|
FixedDeque = require('./fixed-deque');
|
||
|
|
||
|
/**
|
||
|
* CircularBuffer.
|
||
|
*
|
||
|
* @constructor
|
||
|
*/
|
||
|
function CircularBuffer(ArrayClass, capacity) {
|
||
|
|
||
|
if (arguments.length < 2)
|
||
|
throw new Error('mnemonist/circular-buffer: expecting an Array class and a capacity.');
|
||
|
|
||
|
if (typeof capacity !== 'number' || capacity <= 0)
|
||
|
throw new Error('mnemonist/circular-buffer: `capacity` should be a positive number.');
|
||
|
|
||
|
this.ArrayClass = ArrayClass;
|
||
|
this.capacity = capacity;
|
||
|
this.items = new ArrayClass(this.capacity);
|
||
|
this.clear();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Pasting most of the prototype from FixedDeque.
|
||
|
*/
|
||
|
function paste(name) {
|
||
|
CircularBuffer.prototype[name] = FixedDeque.prototype[name];
|
||
|
}
|
||
|
|
||
|
Object.keys(FixedDeque.prototype).forEach(paste);
|
||
|
|
||
|
if (typeof Symbol !== 'undefined')
|
||
|
Object.getOwnPropertySymbols(FixedDeque.prototype).forEach(paste);
|
||
|
|
||
|
/**
|
||
|
* Method used to append a value to the buffer.
|
||
|
*
|
||
|
* @param {any} item - Item to append.
|
||
|
* @return {number} - Returns the new size of the buffer.
|
||
|
*/
|
||
|
CircularBuffer.prototype.push = function(item) {
|
||
|
var index = (this.start + this.size) % this.capacity;
|
||
|
|
||
|
this.items[index] = item;
|
||
|
|
||
|
// Overwriting?
|
||
|
if (this.size === this.capacity) {
|
||
|
|
||
|
// If start is at the end, we wrap around the buffer
|
||
|
this.start = (index + 1) % this.capacity;
|
||
|
|
||
|
return this.size;
|
||
|
}
|
||
|
|
||
|
return ++this.size;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Method used to prepend a value to the buffer.
|
||
|
*
|
||
|
* @param {any} item - Item to prepend.
|
||
|
* @return {number} - Returns the new size of the buffer.
|
||
|
*/
|
||
|
CircularBuffer.prototype.unshift = function(item) {
|
||
|
var index = this.start - 1;
|
||
|
|
||
|
if (this.start === 0)
|
||
|
index = this.capacity - 1;
|
||
|
|
||
|
this.items[index] = item;
|
||
|
|
||
|
// Overwriting
|
||
|
if (this.size === this.capacity) {
|
||
|
|
||
|
this.start = index;
|
||
|
|
||
|
return this.size;
|
||
|
}
|
||
|
|
||
|
this.start = index;
|
||
|
|
||
|
return ++this.size;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Static @.from function taking an arbitrary iterable & converting it into
|
||
|
* a circular buffer.
|
||
|
*
|
||
|
* @param {Iterable} iterable - Target iterable.
|
||
|
* @param {function} ArrayClass - Array class to use.
|
||
|
* @param {number} capacity - Desired capacity.
|
||
|
* @return {FiniteStack}
|
||
|
*/
|
||
|
CircularBuffer.from = function(iterable, ArrayClass, capacity) {
|
||
|
if (arguments.length < 3) {
|
||
|
capacity = iterables.guessLength(iterable);
|
||
|
|
||
|
if (typeof capacity !== 'number')
|
||
|
throw new Error('mnemonist/circular-buffer.from: could not guess iterable length. Please provide desired capacity as last argument.');
|
||
|
}
|
||
|
|
||
|
var buffer = new CircularBuffer(ArrayClass, capacity);
|
||
|
|
||
|
if (iterables.isArrayLike(iterable)) {
|
||
|
var i, l;
|
||
|
|
||
|
for (i = 0, l = iterable.length; i < l; i++)
|
||
|
buffer.items[i] = iterable[i];
|
||
|
|
||
|
buffer.size = l;
|
||
|
|
||
|
return buffer;
|
||
|
}
|
||
|
|
||
|
iterables.forEach(iterable, function(value) {
|
||
|
buffer.push(value);
|
||
|
});
|
||
|
|
||
|
return buffer;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Exporting.
|
||
|
*/
|
||
|
module.exports = CircularBuffer;
|