|
@@ -8,16 +8,17 @@
|
|
|
/**
|
|
|
* A generic chunked array builder.
|
|
|
*
|
|
|
- * When adding elements, the array growns by a specified number
|
|
|
- * of elements (either linear or exponential growth) and no copying
|
|
|
- * is done until ChunkedArray.compact is called.
|
|
|
+ * When adding elements, the array grows by a specified number
|
|
|
+ * of elements and no copying is done until ChunkedArray.compact
|
|
|
+ * is called.
|
|
|
*/
|
|
|
interface ChunkedArray<T> {
|
|
|
- ctor: (size: number) => any,
|
|
|
+ ctor: { new (size: number): ArrayLike<T> },
|
|
|
elementSize: number,
|
|
|
|
|
|
growBy: number,
|
|
|
allocatedSize: number,
|
|
|
+ /** current size of the array */
|
|
|
elementCount: number,
|
|
|
|
|
|
currentSize: number,
|
|
@@ -36,7 +37,7 @@ namespace ChunkedArray {
|
|
|
let nextSize = array.growBy * array.elementSize;
|
|
|
array.currentSize = nextSize;
|
|
|
array.currentIndex = 0;
|
|
|
- array.currentChunk = array.ctor(nextSize);
|
|
|
+ array.currentChunk = new array.ctor(nextSize);
|
|
|
array.allocatedSize += nextSize;
|
|
|
array.chunks[array.chunks.length] = array.currentChunk;
|
|
|
}
|
|
@@ -74,6 +75,20 @@ namespace ChunkedArray {
|
|
|
return array.elementCount++;
|
|
|
}
|
|
|
|
|
|
+ export function addMany<T>(array: ChunkedArray<T>, data: ArrayLike<T>) {
|
|
|
+ const { elementSize } = array;
|
|
|
+ for (let i = 0, _i = data.length; i < _i; i += elementSize) {
|
|
|
+ if (array.currentIndex >= array.currentSize) allocateNext(array);
|
|
|
+ const { currentChunk } = array;
|
|
|
+ for (let j = 0; j < elementSize; j++) {
|
|
|
+ currentChunk[array.currentIndex++] = data[i + j];
|
|
|
+ }
|
|
|
+ array.elementCount++;
|
|
|
+ }
|
|
|
+ return array.elementCount;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** If doNotResizeSingleton = true and the data fit into a single chunk, do not resize it. */
|
|
|
export function compact<T>(array: ChunkedArray<T>, doNotResizeSingleton = false): ArrayLike<T> {
|
|
|
return _compact(array, doNotResizeSingleton);
|
|
|
}
|
|
@@ -81,7 +96,7 @@ namespace ChunkedArray {
|
|
|
export function _compact<T>(array: ChunkedArray<T>, doNotResizeSingleton: boolean): ArrayLike<T> {
|
|
|
const { ctor, chunks, currentIndex } = array;
|
|
|
|
|
|
- if (!chunks.length) return ctor(0);
|
|
|
+ if (!chunks.length) return new ctor(0);
|
|
|
if (chunks.length === 1) {
|
|
|
if (doNotResizeSingleton || currentIndex === array.allocatedSize) {
|
|
|
return chunks[0];
|
|
@@ -92,7 +107,7 @@ namespace ChunkedArray {
|
|
|
for (let i = 0, _i = chunks.length - 1; i < _i; i++) size += chunks[i].length;
|
|
|
size += array.currentIndex;
|
|
|
|
|
|
- const ret = ctor(size);
|
|
|
+ const ret = new ctor(size) as any;
|
|
|
let offset = 0;
|
|
|
|
|
|
if (ret.buffer) {
|
|
@@ -118,12 +133,17 @@ namespace ChunkedArray {
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- export function create<T>(ctor: (size: number) => any, elementSize: number, growBy: number, initialChunk?: ArrayLike<T>): ChunkedArray<T> {
|
|
|
+ export function create<T>(ctor: { new (size: number): ArrayLike<T> }, elementSize: number, chunkSize: number): ChunkedArray<T>
|
|
|
+ /** The size of the initial chunk is elementSize * initialCount */
|
|
|
+ export function create<T>(ctor: { new (size: number): ArrayLike<T> }, elementSize: number, chunkSize: number, initialCount: number): ChunkedArray<T>
|
|
|
+ /** Use the provided array as the initial chunk. The size of the array must be divisible by the elementSize */
|
|
|
+ export function create<T>(ctor: { new (size: number): ArrayLike<T> }, elementSize: number, chunkSize: number, initialChunk: ArrayLike<T>): ChunkedArray<T>
|
|
|
+ export function create<T>(ctor: { new (size: number): ArrayLike<T> }, elementSize: number, chunkSize: number, initialChunkOrCount?: number | ArrayLike<T>): ChunkedArray<T> {
|
|
|
const ret: ChunkedArray<T> = {
|
|
|
ctor,
|
|
|
elementSize,
|
|
|
|
|
|
- growBy,
|
|
|
+ growBy: chunkSize,
|
|
|
allocatedSize: 0,
|
|
|
elementCount: 0,
|
|
|
|
|
@@ -134,8 +154,17 @@ namespace ChunkedArray {
|
|
|
chunks: []
|
|
|
};
|
|
|
|
|
|
- if (!initialChunk) return ret;
|
|
|
+ if (typeof initialChunkOrCount === 'undefined') return ret;
|
|
|
+
|
|
|
+ if (typeof initialChunkOrCount === 'number') {
|
|
|
+ ret.currentChunk = new ctor(initialChunkOrCount * elementSize);
|
|
|
+ ret.allocatedSize = initialChunkOrCount * elementSize;
|
|
|
+ ret.currentSize = ret.currentChunk.length;
|
|
|
+ ret.chunks[0] = ret.currentChunk;
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
|
|
|
+ const initialChunk = initialChunkOrCount;
|
|
|
if (initialChunk.length % elementSize !== 0) throw new Error('initialChunk length must be a multiple of the element size.');
|
|
|
ret.currentChunk = initialChunk;
|
|
|
ret.allocatedSize = initialChunk.length;
|