]> git.proxmox.com Git - mirror_xterm.js.git/commitdiff
Add CircularList and basic tests
authorDaniel Imms <daimms@microsoft.com>
Sun, 27 Nov 2016 00:57:15 +0000 (16:57 -0800)
committerDaniel Imms <daimms@microsoft.com>
Sun, 27 Nov 2016 00:57:15 +0000 (16:57 -0800)
Part of #361

src/utils/CircularList.test.ts [new file with mode: 0644]
src/utils/CircularList.ts [new file with mode: 0644]

diff --git a/src/utils/CircularList.test.ts b/src/utils/CircularList.test.ts
new file mode 100644 (file)
index 0000000..4a89aa3
--- /dev/null
@@ -0,0 +1,81 @@
+import { assert } from 'chai';
+import { CircularList } from './CircularList';
+
+describe('CircularList', () => {
+  describe('push', () => {
+    it('should push values onto the array', () => {
+      const list = new CircularList<string>(5);
+      list.push('1');
+      list.push('2');
+      list.push('3');
+      list.push('4');
+      list.push('5');
+      assert.equal(list.get(0), '1');
+      assert.equal(list.get(1), '2');
+      assert.equal(list.get(2), '3');
+      assert.equal(list.get(3), '4');
+      assert.equal(list.get(4), '5');
+    });
+
+    it('should push old values from the start out of the array when max length is reached', () => {
+      const list = new CircularList<string>(2);
+      list.push('1');
+      list.push('2');
+      assert.equal(list.get(0), '1');
+      assert.equal(list.get(1), '2');
+      list.push('3');
+      assert.equal(list.get(0), '2');
+      assert.equal(list.get(1), '3');
+      list.push('4');
+      assert.equal(list.get(0), '3');
+      assert.equal(list.get(1), '4');
+    });
+  });
+
+  describe('maxLength', () => {
+    it('should increase the size of the list', () => {
+      const list = new CircularList<string>(2);
+      list.push('1');
+      list.push('2');
+      assert.equal(list.get(0), '1');
+      assert.equal(list.get(1), '2');
+      list.maxLength = 4;
+      list.push('3');
+      list.push('4');
+      assert.equal(list.get(0), '1');
+      assert.equal(list.get(1), '2');
+      assert.equal(list.get(2), '3');
+      assert.equal(list.get(3), '4');
+      list.push('wrapped');
+      assert.equal(list.get(0), '2');
+      assert.equal(list.get(1), '3');
+      assert.equal(list.get(2), '4');
+      assert.equal(list.get(3), 'wrapped');
+    });
+
+    it('should return the maximum length of the list', () => {
+      const list = new CircularList<string>(2);
+      assert.equal(list.maxLength, 2);
+      list.push('1');
+      list.push('2');
+      assert.equal(list.maxLength, 2);
+      list.push('3');
+      assert.equal(list.maxLength, 2);
+      list.maxLength = 4;
+      assert.equal(list.maxLength, 4);
+    });
+  });
+
+  describe('length', () => {
+    it('should return the current length of the list, capped at the maximum length', () => {
+      const list = new CircularList<string>(2);
+      assert.equal(list.length, 0);
+      list.push('1');
+      assert.equal(list.length, 1);
+      list.push('2');
+      assert.equal(list.length, 2);
+      list.push('3');
+      assert.equal(list.length, 2);
+    });
+  });
+});
diff --git a/src/utils/CircularList.ts b/src/utils/CircularList.ts
new file mode 100644 (file)
index 0000000..51da0c6
--- /dev/null
@@ -0,0 +1,95 @@
+/**
+ * xterm.js: xterm, in the browser
+ * Copyright (c) 2016, SourceLair Private Company <www.sourcelair.com> (MIT License)
+ */
+
+/**
+ * Represents a circular list; a list with a maximum size that wraps around when push is called,
+ * overriding values at the start of the list.
+ */
+export class CircularList<T> {
+  private _array: T[];
+  private _startIndex: number;
+  private _length: number;
+
+  constructor(maxLength: number) {
+    this._array = new Array<T>(maxLength);
+    this._startIndex = 0;
+    this._length = 0;
+  }
+
+  public get maxLength(): number {
+    return this._array.length;
+  }
+
+  public set maxLength(newMaxLength: number) {
+    // Reconstruct array, starting at index 0. Only transfer values from the
+    // indexes 0 to length.
+    let newArray = new Array<T>(newMaxLength);
+    for (let i = 0; i < Math.min(newMaxLength, this.length); i++) {
+      newArray[i] = this._array[this._getCyclicIndex(i)];
+    }
+    this._array = newArray;
+    this._startIndex = 0;
+  }
+
+  public get length(): number {
+    return this._length;
+  }
+
+  public set length(newLength: number) {
+    // TODO: Is this auto fill is needed or can it be
+    if (newLength > this._length) {
+      for (let i = this._length; i < newLength; i++) {
+        this._array[i] = undefined;
+      }
+    }
+    this._length = newLength;
+  }
+
+  public get forEach(): (callbackfn: (value: T, index: number, array: T[]) => void) => void {
+    return this._array.forEach;
+  }
+
+  /**
+   * Gets the value at an index.
+   *
+   * Note that for performance reasons there is no bounds checking here, the index reference is
+   * circular so this should always return a value and never throw.
+   * @param index The index of the value to get.
+   * @return The value corresponding to the index.
+   */
+  public get(index: number): T {
+    return this._array[this._getCyclicIndex(index)];
+  }
+
+  /**
+   * Sets the value at an index.
+   *
+   * Note that for performance reasons there is no bounds checking here, the index reference is
+   * circular so this should always return a value and never throw.
+   * @param index The index to set.
+   * @param value The value to set.
+   */
+  public set(index: number, value: T): void {
+    this._array[this._getCyclicIndex(index)] = value;
+  }
+
+  /**
+   * Pushes a new value onto the list, wrapping around to the start of the array, overriding index 0
+   * if the maximum length is reached.
+   * @param value The value to push onto the list.
+   */
+  public push(value: T): void {
+    this._array[this._getCyclicIndex(this._length)] = value;
+    if (this._length === this.maxLength) {
+      this._startIndex++;
+    } else {
+      this._length++;
+    }
+  }
+
+  private _getCyclicIndex(index: number): number {
+    return (this._startIndex + index) % this.maxLength;
+  }
+}