Skip to content

Instantly share code, notes, and snippets.

@d1820
Last active March 4, 2017 19:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save d1820/cf807538a9c047f56cfc841fb24e195d to your computer and use it in GitHub Desktop.
Save d1820/cf807538a9c047f56cfc841fb24e195d to your computer and use it in GitHub Desktop.
Circular and Memory Buffer Example
//taken from https://github.com/tomsmeding/circular-buffer/blob/master/index.js and modified
//new features:
//find()
//getKeys()
//getValues()
//Persistant Buffer new features
//find() - finds a value by key
//getKeys() - gets only the keys available
//getValues() - gets the values
//load() - loads a buffer for use (useful if coming from server or localstorage)
import * as _ from 'lodash';
import {IBuffer } from '../interfaces';
interface IBufferValue {
key: string
value: any
}
//this is an internal class used to facility the buffers and lookups
class BufferValue implements IBufferValue {
constructor(public key: string, public value: any) { }
}
//this is a standard array that is used as a buffer in memory.
export class PersistantBuffer implements IBuffer{
private _buffer: Array<IBufferValue> = [];
constructor() {
}
load(bufferData: Array<IBufferValue>){
this._buffer = bufferData;
}
clear() {
this._buffer = [];
}
size(): number { return this._buffer.length; }
capacity(): number { return this._buffer.length; }
enque(key: string, data: any): void {
const matchedIdx = this._findIndex(key);
if (matchedIdx > -1) {
this._buffer[matchedIdx] = new BufferValue(key, data);
return;
}
this._buffer.unshift(new BufferValue(key, data));
}
push(key: string, data: any): void {
const matchedIdx = this._findIndex(key);
if (matchedIdx > -1) {
this._buffer[matchedIdx] = new BufferValue(key, data);
return;
}
this._buffer.push(new BufferValue(key, data));
}
deque(): any {
if (this._buffer.length == 0) {
return null;
}
return this._buffer.shift().value
}
remove(key: string) {
_.remove(this._buffer, function (item) {
return item.key === key;
});
}
getKeys(start, end): Array<any> {
let matchArray = this._getBuffer(start, end);
return matchArray.map(function (item) {
if (item == undefined) return null;
return item.key;
});
}
getValues(start, end): Array<any> {
let matchArray = this._getBuffer(start, end);
return matchArray.map(function (item) {
if (item == undefined) return null;
return item.value;
});
}
get(start, end): Array<any> {
let matchArray = this._getBuffer(start, end);
return matchArray;
}
toarray(): Array<any> {
if (this._buffer.length == 0) return [];
return this.get(0, this._buffer.length);
}
find(key: string): any {
const match = this._find(key);
if (match) {
return match.value;
}
return null;
}
private _find(key: string) {
const idx = this._findIndex(key);
if (idx > -1) {
return this._buffer[idx];
}
return null;
}
private _findIndex(key: string) {
const match = _.findIndex(this._buffer, function (item) {
if (item == undefined) return false;
return item.key === key
});
return match;
}
private _getBuffer(start: number, end?: number): Array<IBufferValue> {
if (this._buffer.length == 0) {
return [];
}
if (typeof start != "number" || start % 1 != 0 || start < 0) throw new TypeError("Invalid start");
if (start >= this._buffer.length) throw new RangeError("Index past end of buffer: " + start);
if (end == undefined) {
return this._buffer.slice(start, this._buffer.length)
}
if (typeof end != "number" || end % 1 != 0 || end < 0) throw new TypeError("Invalid end");
if (end > this._buffer.length) throw new RangeError("Index past end of buffer: " + end);
return this._buffer.slice(start, end);
}
}
//this is an implementation of a circular buffer, that will keep items on a rotation with a predefined array queue.
//this is used for temp storage when the reliablity of data is not important, very similar to shallow maps
export class ShallowBuffer implements IBuffer {
private _buffer: Array<IBufferValue>;
private _nextPosition: number = 0;
private _populatedSize: number = 0;
private _capacity: number;
constructor(capacity: number = 10) {
this._capacity = capacity;
this._buffer = new Array(capacity);
}
clear() {
this._buffer = new Array(this._capacity);
}
size(): number { return this._populatedSize; }
capacity(): number { return this._capacity; }
enque(key: string, data: any): void {
const matchedIdx = this._findIndex(key);
if (matchedIdx > -1) {
this._buffer[matchedIdx] = new BufferValue(key, data);
return;
}
if (this._nextPosition > 0) {
this._nextPosition--;
} else {
this._nextPosition = this._capacity - 1;
}
this._buffer[this._nextPosition] = new BufferValue(key, data);
if (this._populatedSize < this._capacity) {
this._populatedSize++;
}
}
push(key: string, data: any): void {
const matchedIdx = this._findIndex(key);
if (matchedIdx > -1) {
this._buffer[matchedIdx] = new BufferValue(key, data);
return;
}
//push a new item to the end of the current loop set
if (this._populatedSize == this._capacity) {
//if its max push to #1 spot
this._buffer[this._nextPosition] = new BufferValue(key, data);
this._nextPosition = (this._nextPosition + 1) % this._capacity;
} else {
//next open spot based on array postion in the cycle
this._buffer[(this._nextPosition + this._populatedSize) % this._capacity] = new BufferValue(key, data);
this._populatedSize++;
}
}
deque(): any {
if (this._populatedSize == 0) {
return null;
}
let item = this._buffer[(this._nextPosition + this._populatedSize - 1) % this._capacity];
this._populatedSize--;
return item.value;
}
remove(key: string) {
const matchedIdx = this._findIndex(key);
if (matchedIdx > -1) {
this._buffer[matchedIdx] = undefined;
}
}
getKeys(start, end): Array<any> {
let matchArray = this._getBuffer(start, end);
return matchArray.map(function (item) {
if (item == undefined) return null;
return item.key;
});
}
getValues(start, end): Array<any> {
let matchArray = this._getBuffer(start, end);
return matchArray.map(function (item) {
if (item == undefined) return null;
return item.value;
});
}
get(start, end): Array<any> {
let matchArray = this._getBuffer(start, end);
return matchArray;
}
toarray(): Array<any> {
if (this._populatedSize == 0) return [];
return this.get(0, this._populatedSize - 1);
}
find(key: string): any {
const matchedIdx = this._findIndex(key);
if (matchedIdx > -1) {
return this._buffer[matchedIdx].value;
}
return null;
}
private _findIndex(key: string) {
let items = this._getBuffer(0);
const matchedIdx = _.findIndex(items, function (item) {
if (item == undefined) return false;
return item.key === key
});
return matchedIdx
}
private _getBuffer(start: number, end?: number): Array<IBufferValue> {
if (this._populatedSize == 0 && start == 0) {
return [];
}
if (typeof start != "number" || start % 1 != 0 || start < 0) throw new TypeError("Invalid start");
if (start >= this._populatedSize) throw new RangeError("Index past end of buffer: " + start);
if (end == undefined) {
return this._buffer.slice(start, this._capacity)
}
if (typeof end != "number" || end % 1 != 0 || end < 0) throw new TypeError("Invalid end");
if (end > this._populatedSize) throw new RangeError("Index past end of buffer: " + end);
if (this._nextPosition + start >= this._capacity) {
//make sure first+start and first+end are in a normal range
start -= this._capacity; //becomes a negative number
end -= this._capacity;
}
let matchArray: Array<IBufferValue>;
if (this._nextPosition + end < this._capacity) {
matchArray = this._buffer.slice(this._nextPosition + start, this._nextPosition + end + 1);
}
else {
matchArray = this._buffer.slice(this._nextPosition + start, this._capacity).concat(this._buffer.slice(0, this._nextPosition + end + 1 - this._capacity));
}
return matchArray;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment