Last active
February 23, 2019 19:18
-
-
Save haf/1fdde798bcdf9f3d7e5356ac6d1c9ad1 to your computer and use it in GitHub Desktop.
Circular buffer in JS
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
import { List } from "immutable"; | |
export default class CircularBuffer { | |
constructor(capacity) { | |
if (capacity < 1) { | |
throw new Error("Invalid argument: capacity; must ≥1") | |
} | |
this.len = 0 // tracks current size | |
this.pos = 0 // points to most recent value | |
this.capacity = capacity | |
this.buffer = new Array(capacity) | |
this.snap = List() | |
} | |
get length() { | |
return this.len | |
} | |
isFull() { | |
return this.length === this.capacity | |
} | |
push = item => { | |
const write = (this.pos + 1) % this.capacity | |
this.len = Math.max(this.len, Math.min(this.capacity, this.pos + 1)) | |
this.buffer[write] = item | |
this.pos = write | |
return this; | |
} | |
forEach = fn => { | |
let k = Math.abs((this.pos - this.len + 1) % this.capacity) | |
for (let i = 0; i < this.len; i++) { | |
fn(this.buffer[k], i) | |
k = (k + 1) % this.capacity | |
} | |
} | |
/** | |
* Returns a snapshot of the values in this buffer. If nothing has changed, returns the same reference | |
* as the last call. | |
*/ | |
snapshot = () => { | |
this.snap = this.snap.withMutations(xs => { | |
this.forEach((x, i) => { xs = xs.set(i, x) }); | |
}) | |
return this.snap; | |
} | |
} |
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
import CircularBuffer from "./circularBuffer"; | |
import { List } from "immutable"; | |
describe('circular buffer', () => { | |
let subject | |
beforeEach(() => { | |
subject = new CircularBuffer(2); | |
}) | |
it('has push', () => { | |
expect(subject).toHaveProperty("push") | |
}) | |
it('has forEach', () => { | |
expect(subject).toHaveProperty("forEach") | |
}) | |
it("has snapshot", () => { | |
expect(subject).toHaveProperty("snapshot") | |
}) | |
it("has isFull", () => { | |
expect(subject).toHaveProperty("isFull") | |
}) | |
describe("snapshot empty", () => { | |
it("equals empty", () => { | |
const empty = List() | |
expect(subject.snapshot()).toEqual(empty) | |
}) | |
it("immutable List equality", () => { | |
const one = List([ 1 ]), two = List([ 1 ]); | |
expect(one).toEqual(two) | |
const three = two.push(2); | |
expect(one).not.toEqual(three) | |
}) | |
}) | |
describe("protocol", () => { | |
it("invariants", () => { | |
expect(subject.snapshot() === subject.snapshot()).toBeTruthy() | |
subject.push(1); | |
expect(subject.length).toEqual(1) | |
expect(subject.isFull()).toBeFalsy() | |
expect(subject.snapshot() === subject.snapshot()).toBeTruthy() | |
subject.push(2) | |
expect(subject.length).toEqual(2) | |
expect(subject.isFull()).toBeTruthy() | |
expect(subject.snapshot() === subject.snapshot()).toBeTruthy() | |
subject.push(3) | |
expect(subject.length).toEqual(2) | |
expect(subject.isFull()).toBeTruthy() | |
expect(subject.snapshot() === subject.snapshot()).toBeTruthy() | |
}) | |
describe("forEach", () => { | |
it("on empty", () => { | |
let calls = 0 | |
subject.forEach(() => { calls++ }) | |
expect(calls).toEqual(0) | |
}) | |
it("one item", () => { | |
let calls = 0 | |
subject.push("apa") | |
subject.forEach(() => { calls++ }) | |
expect(calls).toEqual(1) | |
}) | |
it("two items", () => { | |
let calls = 0 | |
subject.push("apa") | |
subject.push("pap") | |
subject.forEach(() => { calls++ }) | |
expect(calls).toEqual(2) | |
}) | |
it("wraparound at three items", () => { | |
let calls = 0 | |
subject.push("apa") | |
subject.push("pap") | |
subject.push("aap") | |
subject.forEach(() => { calls++ }) | |
expect(calls).toEqual(2) | |
}) | |
}) | |
}) | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment