This commit is contained in:
2018-11-16 19:03:22 +00:00
parent afc1e361fe
commit c7ee8923b6

View File

@@ -1,345 +0,0 @@
(function(global) {
class CBuffer {
constructor() {
// handle cases where "new" keyword wasn't used
if (!(this instanceof CBuffer)) {
// multiple conditions need to be checked to properly emulate Array
if (arguments.length > 1 || typeof arguments[0] !== "number") {
return CBuffer.apply(new CBuffer(arguments.length), arguments);
} else {
return new CBuffer(arguments[0]);
}
}
// if no arguments, then nothing needs to be set
if (arguments.length === 0)
throw new Error("Missing Argument: You must pass a valid buffer size");
// this is the same in either scenario
this.length = this.start = 0;
// set to callback fn if data is about to be overwritten
this.overflow = null;
// emulate Array based on passed arguments
if (arguments.length > 1 || typeof arguments[0] !== "number") {
this.data = new Array(arguments.length);
this.end = (this.size = arguments.length) - 1;
this.push.apply(this, arguments);
} else {
this.data = new Array(arguments[0]);
this.end = (this.size = arguments[0]) - 1;
}
// need to `return this` so `return CBuffer.apply` works
return this;
}
}
function defaultComparitor(a, b) {
return a == b ? 0 : a > b ? 1 : -1;
}
CBuffer.prototype = {
// properly set constructor
constructor: CBuffer,
/* mutator methods */
// pop last item
pop: function() {
var item;
if (this.length === 0) return;
item = this.data[this.end];
// remove the reference to the object so it can be garbage collected
delete this.data[this.end];
this.end = (this.end - 1 + this.size) % this.size;
this.length--;
return item;
},
// push item to the end
push: function() {
var i = 0;
// check if overflow is set, and if data is about to be overwritten
if (this.overflow && this.length + arguments.length > this.size) {
// call overflow function and send data that's about to be overwritten
for (; i < this.length + arguments.length - this.size; i++) {
this.overflow(this.data[(this.end + i + 1) % this.size], this);
}
}
// push items to the end, wrapping and erasing existing items
// using arguments variable directly to reduce gc footprint
for (i = 0; i < arguments.length; i++) {
this.data[(this.end + i + 1) % this.size] = arguments[i];
}
// recalculate length
if (this.length < this.size) {
if (this.length + i > this.size) this.length = this.size;
else this.length += i;
}
// recalculate end
this.end = (this.end + i) % this.size;
// recalculate start
this.start = (this.size + this.end - this.length + 1) % this.size;
// return number current number of items in CBuffer
return this.length;
},
// reverse order of the buffer
reverse: function() {
var i = 0,
tmp;
for (; i < ~~(this.length / 2); i++) {
tmp = this.data[(this.start + i) % this.size];
this.data[(this.start + i) % this.size] = this.data[
(this.start + (this.length - i - 1)) % this.size
];
this.data[(this.start + (this.length - i - 1)) % this.size] = tmp;
}
return this;
},
// rotate buffer to the left by cntr, or by 1
rotateLeft: function(cntr) {
if (typeof cntr === "undefined") cntr = 1;
if (typeof cntr !== "number")
throw new Error("Argument must be a number");
while (--cntr >= 0) {
this.push(this.shift());
}
return this;
},
// rotate buffer to the right by cntr, or by 1
rotateRight: function(cntr) {
if (typeof cntr === "undefined") cntr = 1;
if (typeof cntr !== "number")
throw new Error("Argument must be a number");
while (--cntr >= 0) {
this.unshift(this.pop());
}
return this;
},
// remove and return first item
shift: function() {
var item;
// check if there are any items in CBuff
if (this.length === 0) return;
// store first item for return
item = this.data[this.start];
// recalculate start of CBuffer
this.start = (this.start + 1) % this.size;
// decrement length
this.length--;
return item;
},
// sort items
sort: function(fn) {
this.data.sort(fn || defaultComparitor);
this.start = 0;
this.end = this.length - 1;
return this;
},
// add item to beginning of buffer
unshift: function() {
var i = 0;
// check if overflow is set, and if data is about to be overwritten
if (this.overflow && this.length + arguments.length > this.size) {
// call overflow function and send data that's about to be overwritten
for (; i < this.length + arguments.length - this.size; i++) {
this.overflow(this.data[this.end - (i % this.size)], this);
}
}
for (i = 0; i < arguments.length; i++) {
this.data[(this.size + this.start - (i % this.size) - 1) % this.size] =
arguments[i];
}
if (this.size - this.length - i < 0) {
this.end += this.size - this.length - i;
if (this.end < 0) this.end = this.size + (this.end % this.size);
}
if (this.length < this.size) {
if (this.length + i > this.size) this.length = this.size;
else this.length += i;
}
this.start -= arguments.length;
if (this.start < 0) this.start = this.size + (this.start % this.size);
return this.length;
},
/* accessor methods */
// return index of first matched element
indexOf: function(arg, idx) {
if (!idx) idx = 0;
for (; idx < this.length; idx++) {
if (this.data[(this.start + idx) % this.size] === arg) return idx;
}
return -1;
},
// return last index of the first match
lastIndexOf: function(arg, idx) {
if (!idx) idx = this.length - 1;
for (; idx >= 0; idx--) {
if (this.data[(this.start + idx) % this.size] === arg) return idx;
}
return -1;
},
// return the index an item would be inserted to if this
// is a sorted circular buffer
sortedIndex: function(value, comparitor, context) {
comparitor = comparitor || defaultComparitor;
var isFull = this.length === this.size,
low = this.start,
high = isFull ? this.length - 1 : this.length;
// Tricky part is finding if its before or after the pivot
// we can get this info by checking if the target is less than
// the last item. After that it's just a typical binary search.
if (low && comparitor.call(context, value, this.data[high]) > 0) {
(low = 0), (high = this.end);
}
while (low < high) {
var mid = (low + high) >>> 1;
if (comparitor.call(context, value, this.data[mid]) > 0) low = mid + 1;
else high = mid;
}
return !isFull
? low
: // http://stackoverflow.com/a/18618273/1517919
(((low - this.start) % this.length) + this.length) % this.length;
},
/* iteration methods */
// check every item in the array against a test
every: function(callback, context) {
var i = 0;
for (; i < this.length; i++) {
if (
!callback.call(
context,
this.data[(this.start + i) % this.size],
i,
this
)
)
return false;
}
return true;
},
// loop through each item in buffer
// TODO: figure out how to emulate Array use better
forEach: function(callback, context) {
var i = 0;
for (; i < this.length; i++) {
callback.call(
context,
this.data[(this.start + i) % this.size],
i,
this
);
}
},
// check items agains test until one returns true
// TODO: figure out how to emuldate Array use better
some: function(callback, context) {
var i = 0;
for (; i < this.length; i++) {
if (
callback.call(
context,
this.data[(this.start + i) % this.size],
i,
this
)
)
return true;
}
return false;
},
// calculate the average value of a circular buffer
avg: function() {
return this.length == 0 ? 0 : this.sum() / this.length;
},
// loop through each item in buffer and calculate sum
sum: function() {
var index = this.length;
var s = 0;
while (index--) s += this.data[index];
return s;
},
// loop through each item in buffer and calculate median
median: function() {
if (this.length === 0) return 0;
var values = this.slice().sort(defaultComparitor);
var half = Math.floor(values.length / 2);
if (values.length % 2) return values[half];
else return (values[half - 1] + values[half]) / 2.0;
},
/* utility methods */
// reset pointers to buffer with zero items
// note: this will not remove values in cbuffer, so if for security values
// need to be overwritten, run `.fill(null).empty()`
empty: function() {
//var i = 0;
this.length = this.start = 0;
this.end = this.size - 1;
return this;
},
// fill all places with passed value or function
fill: function(arg) {
var i = 0;
if (typeof arg === "function") {
while (((this.data[i] = arg()), ++i < this.size));
} else {
while (((this.data[i] = arg), ++i < this.size));
}
// reposition start/end
this.start = 0;
this.end = this.size - 1;
this.length = this.size;
return this;
},
// return first item in buffer
first: function() {
return this.data[this.start];
},
// return last item in buffer
last: function() {
return this.data[this.end];
},
// return specific index in buffer
get: function(arg) {
return this.data[(this.start + arg) % this.size];
},
isFull: function() {
return this.size === this.length;
},
// set value at specified index
set: function(idx, arg) {
return (this.data[(this.start + idx) % this.size] = arg);
},
// return clean array of values
toArray: function() {
return this.slice();
},
// slice the buffer to an arraay
slice: function(start, end) {
var size = this.length;
start = +start || 0;
if (start < 0) {
if (start >= end) return [];
start = -start > size ? 0 : size + start;
}
if (end == null || end > size) end = size;
else if (end < 0) end += size;
else end = +end || 0;
size = start < end ? end - start : 0;
var result = Array(size);
for (var index = 0; index < size; index++) {
result[index] = this.data[(this.start + start + index) % this.size];
}
return result;
}
};
if (typeof module === "object" && module.exports) module.exports = CBuffer;
else global.CBuffer = CBuffer;
})(this);