Last active
March 4, 2017 19:40
-
-
Save d1820/cf807538a9c047f56cfc841fb24e195d to your computer and use it in GitHub Desktop.
Circular and Memory Buffer Example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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