|
@@ -9,125 +9,129 @@
|
|
|
* A generic chunked array builder.
|
|
|
*
|
|
|
* When adding elements, the array growns by a specified number
|
|
|
- * of elements and no copying is done until ChunkedArray.compact
|
|
|
- * is called.
|
|
|
+ * of elements (either linear or exponential growth) and no copying
|
|
|
+ * is done until ChunkedArray.compact is called.
|
|
|
*/
|
|
|
-export interface ChunkedArray<T> {
|
|
|
- creator: (size: number) => any;
|
|
|
- elementSize: number;
|
|
|
- chunkSize: number;
|
|
|
- current: any;
|
|
|
- currentIndex: number;
|
|
|
-
|
|
|
- parts: any[];
|
|
|
- elementCount: number;
|
|
|
+interface ChunkedArray<T> {
|
|
|
+ ctor: (size: number) => any,
|
|
|
+ elementSize: number,
|
|
|
+
|
|
|
+ linearGrowth: boolean,
|
|
|
+
|
|
|
+ initialSize: number,
|
|
|
+ allocatedSize: number,
|
|
|
+ elementCount: number,
|
|
|
+
|
|
|
+ currentSize: number,
|
|
|
+ currentChunk: any,
|
|
|
+ currentIndex: number,
|
|
|
+
|
|
|
+ chunks: any[]
|
|
|
}
|
|
|
|
|
|
-export namespace ChunkedArray {
|
|
|
+// TODO: better api, write tests
|
|
|
+namespace ChunkedArray {
|
|
|
export function is(x: any): x is ChunkedArray<any> {
|
|
|
return x.creator && x.chunkSize;
|
|
|
}
|
|
|
|
|
|
- export function add4<T>(array: ChunkedArray<T>, x: T, y: T, z: T, w: T) {
|
|
|
- if (array.currentIndex >= array.chunkSize) {
|
|
|
- array.currentIndex = 0;
|
|
|
- array.current = array.creator(array.chunkSize);
|
|
|
- array.parts[array.parts.length] = array.current;
|
|
|
- }
|
|
|
+ function allocateNext(array: ChunkedArray<any>) {
|
|
|
+ let nextSize = !array.allocatedSize || array.linearGrowth
|
|
|
+ ? array.initialSize * array.elementSize
|
|
|
+ : Math.max(Math.ceil(0.61 * array.allocatedSize), 1);
|
|
|
+ if (nextSize % array.elementSize !== 0) nextSize += nextSize % array.elementSize;
|
|
|
+ array.currentSize = nextSize;
|
|
|
+ array.currentIndex = 0;
|
|
|
+ array.currentChunk = array.ctor(nextSize);
|
|
|
+ array.allocatedSize += nextSize;
|
|
|
+ array.chunks[array.chunks.length] = array.currentChunk;
|
|
|
+ }
|
|
|
|
|
|
- array.current[array.currentIndex++] = x;
|
|
|
- array.current[array.currentIndex++] = y;
|
|
|
- array.current[array.currentIndex++] = z;
|
|
|
- array.current[array.currentIndex++] = w;
|
|
|
+ export function add4<T>(array: ChunkedArray<T>, x: T, y: T, z: T, w: T) {
|
|
|
+ if (array.currentIndex >= array.currentSize) allocateNext(array);
|
|
|
+ const c = array.currentChunk;
|
|
|
+ c[array.currentIndex++] = x;
|
|
|
+ c[array.currentIndex++] = y;
|
|
|
+ c[array.currentIndex++] = z;
|
|
|
+ c[array.currentIndex++] = w;
|
|
|
return array.elementCount++;
|
|
|
}
|
|
|
|
|
|
export function add3<T>(array: ChunkedArray<T>, x: T, y: T, z: T) {
|
|
|
- if (array.currentIndex >= array.chunkSize) {
|
|
|
- array.currentIndex = 0;
|
|
|
- array.current = array.creator(array.chunkSize);
|
|
|
- array.parts[array.parts.length] = array.current;
|
|
|
- }
|
|
|
-
|
|
|
- array.current[array.currentIndex++] = x;
|
|
|
- array.current[array.currentIndex++] = y;
|
|
|
- array.current[array.currentIndex++] = z;
|
|
|
+ if (array.currentIndex >= array.currentSize) allocateNext(array);
|
|
|
+ const c = array.currentChunk;
|
|
|
+ c[array.currentIndex++] = x;
|
|
|
+ c[array.currentIndex++] = y;
|
|
|
+ c[array.currentIndex++] = z;
|
|
|
return array.elementCount++;
|
|
|
}
|
|
|
|
|
|
export function add2<T>(array: ChunkedArray<T>, x: T, y: T) {
|
|
|
- if (array.currentIndex >= array.chunkSize) {
|
|
|
- array.currentIndex = 0;
|
|
|
- array.current = array.creator(array.chunkSize);
|
|
|
- array.parts[array.parts.length] = array.current;
|
|
|
- }
|
|
|
-
|
|
|
- array.current[array.currentIndex++] = x;
|
|
|
- array.current[array.currentIndex++] = y;
|
|
|
+ if (array.currentIndex >= array.currentSize) allocateNext(array);
|
|
|
+ const c = array.currentChunk;
|
|
|
+ c[array.currentIndex++] = x;
|
|
|
+ c[array.currentIndex++] = y;
|
|
|
return array.elementCount++;
|
|
|
}
|
|
|
|
|
|
export function add<T>(array: ChunkedArray<T>, x: T) {
|
|
|
- if (array.currentIndex >= array.chunkSize) {
|
|
|
- array.currentIndex = 0;
|
|
|
- array.current = array.creator(array.chunkSize);
|
|
|
- array.parts[array.parts.length] = array.current;
|
|
|
- }
|
|
|
-
|
|
|
- array.current[array.currentIndex++] = x;
|
|
|
+ if (array.currentIndex >= array.currentSize) allocateNext(array);
|
|
|
+ array.currentChunk[array.currentIndex++] = x;
|
|
|
return array.elementCount++;
|
|
|
}
|
|
|
|
|
|
|
|
|
- export function compact<T>(array: ChunkedArray<T>): T[] {
|
|
|
- const ret = array.creator(array.elementSize * array.elementCount)
|
|
|
- const offset = (array.parts.length - 1) * array.chunkSize
|
|
|
- let offsetInner = 0
|
|
|
- let part: any
|
|
|
+ export function compact<T>(array: ChunkedArray<T>): ArrayLike<T> {
|
|
|
+ const { ctor, chunks, currentIndex } = array;
|
|
|
|
|
|
- if (array.parts.length > 1) {
|
|
|
- if (array.parts[0].buffer) {
|
|
|
- for (let i = 0; i < array.parts.length - 1; i++) {
|
|
|
- ret.set(array.parts[i], array.chunkSize * i);
|
|
|
- }
|
|
|
- } else {
|
|
|
+ if (!chunks.length) return ctor(0);
|
|
|
+ if (chunks.length === 1 && currentIndex === array.allocatedSize) {
|
|
|
+ return chunks[0];
|
|
|
+ }
|
|
|
|
|
|
- for (let i = 0; i < array.parts.length - 1; i++) {
|
|
|
- offsetInner = array.chunkSize * i;
|
|
|
- part = array.parts[i];
|
|
|
+ const ret = ctor(array.elementSize * array.elementCount);
|
|
|
+ let offset = 0;
|
|
|
|
|
|
- for (let j = 0; j < array.chunkSize; j++) {
|
|
|
- ret[offsetInner + j] = part[j];
|
|
|
- }
|
|
|
- }
|
|
|
+ if (ret.buffer) {
|
|
|
+ for (let i = 0, _i = chunks.length - 1; i < _i; i++) {
|
|
|
+ ret.set(chunks[i], offset);
|
|
|
+ offset += chunks[i].length;
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- if (array.current.buffer && array.currentIndex >= array.chunkSize) {
|
|
|
- ret.set(array.current, array.chunkSize * (array.parts.length - 1));
|
|
|
} else {
|
|
|
- for (let i = 0; i < array.currentIndex; i++) {
|
|
|
- ret[offset + i] = array.current[i];
|
|
|
+ for (let i = 0, _i = chunks.length - 1; i < _i; i++) {
|
|
|
+ const chunk = chunks[i];
|
|
|
+ for (let j = 0, _j = chunk.length; j < _j; j++) ret[offset + j] = chunk[j];
|
|
|
+ offset += chunk.length;
|
|
|
}
|
|
|
}
|
|
|
- return ret as any;
|
|
|
- }
|
|
|
|
|
|
- export function create<T>(creator: (size: number) => any, chunkElementCount: number, elementSize: number): ChunkedArray<T> {
|
|
|
- chunkElementCount = chunkElementCount | 0;
|
|
|
- if (chunkElementCount <= 0) chunkElementCount = 1;
|
|
|
+ const lastChunk = chunks[chunks.length - 1];
|
|
|
+ if (ret.buffer && currentIndex >= array.currentSize) {
|
|
|
+ ret.set(lastChunk, offset);
|
|
|
+ } else {
|
|
|
+ for (let j = 0, _j = lastChunk.length; j < _j; j++) ret[offset + j] = lastChunk[j];
|
|
|
+ }
|
|
|
|
|
|
- let chunkSize = chunkElementCount * elementSize;
|
|
|
- let current = creator(chunkSize)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
|
|
|
+ export function create<T>(ctor: (size: number) => any, elementSize: number, initialSize: number, linearGrowth: boolean): ChunkedArray<T> {
|
|
|
return {
|
|
|
+ ctor,
|
|
|
elementSize,
|
|
|
- chunkSize,
|
|
|
- creator,
|
|
|
- current,
|
|
|
- parts: [current],
|
|
|
+ linearGrowth,
|
|
|
+
|
|
|
+ initialSize,
|
|
|
+ allocatedSize: 0,
|
|
|
+ elementCount: 0,
|
|
|
+
|
|
|
+ currentSize: 0,
|
|
|
+ currentChunk: void 0,
|
|
|
currentIndex: 0,
|
|
|
- elementCount: 0
|
|
|
- } as ChunkedArray<T>
|
|
|
+
|
|
|
+ chunks: []
|
|
|
+ } as ChunkedArray<T>;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+export default ChunkedArray
|