Skip to content

Instantly share code, notes, and snippets.

@haf
Last active February 23, 2019 19:18
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 haf/1fdde798bcdf9f3d7e5356ac6d1c9ad1 to your computer and use it in GitHub Desktop.
Save haf/1fdde798bcdf9f3d7e5356ac6d1c9ad1 to your computer and use it in GitHub Desktop.
Circular buffer in JS
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;
}
}
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