Skip to content
Snippets Groups Projects
Commit 2af9f1af authored by David Sehnal's avatar David Sehnal
Browse files

Chunked array tweaks

parent 5d342a30
No related branches found
No related tags found
No related merge requests found
...@@ -8,17 +8,23 @@ import { ChunkedArray } from '../chunked-array' ...@@ -8,17 +8,23 @@ import { ChunkedArray } from '../chunked-array'
describe('Chunked Array', () => { describe('Chunked Array', () => {
it('creation', () => { it('creation', () => {
const arr = ChunkedArray.create<number>(_ => [], 2, 2); const arr = ChunkedArray.create<number>(Array, 2, 2);
ChunkedArray.add2(arr, 1, 2); ChunkedArray.add2(arr, 1, 2);
ChunkedArray.add2(arr, 3, 4); ChunkedArray.add2(arr, 3, 4);
expect(ChunkedArray.compact(arr)).toEqual([1, 2, 3, 4]); expect(ChunkedArray.compact(arr)).toEqual([1, 2, 3, 4]);
}); });
it('initial', () => { it('initial', () => {
const arr = ChunkedArray.create<number>(s => new Int32Array(s), 2, 6, new Int32Array([1, 2, 3, 4])); const arr = ChunkedArray.create<number>(Int32Array, 2, 6, new Int32Array([1, 2, 3, 4]));
ChunkedArray.add2(arr, 4, 3); ChunkedArray.add2(arr, 4, 3);
ChunkedArray.add2(arr, 2, 1); ChunkedArray.add2(arr, 2, 1);
ChunkedArray.add2(arr, 5, 6); ChunkedArray.add2(arr, 5, 6);
expect(ChunkedArray.compact(arr)).toEqual(new Int32Array([4, 3, 2, 1, 5, 6])); expect(ChunkedArray.compact(arr)).toEqual(new Int32Array([4, 3, 2, 1, 5, 6]));
}); });
it('add many', () => {
const arr = ChunkedArray.create<number>(Array, 2, 2);
ChunkedArray.addMany(arr, [1, 2, 3, 4]);
expect(ChunkedArray.compact(arr)).toEqual([1, 2, 3, 4]);
});
}); });
\ No newline at end of file
...@@ -8,16 +8,17 @@ ...@@ -8,16 +8,17 @@
/** /**
* A generic chunked array builder. * A generic chunked array builder.
* *
* When adding elements, the array growns by a specified number * When adding elements, the array grows by a specified number
* of elements (either linear or exponential growth) and no copying * of elements and no copying is done until ChunkedArray.compact
* is done until ChunkedArray.compact is called. * is called.
*/ */
interface ChunkedArray<T> { interface ChunkedArray<T> {
ctor: (size: number) => any, ctor: { new (size: number): ArrayLike<T> },
elementSize: number, elementSize: number,
growBy: number, growBy: number,
allocatedSize: number, allocatedSize: number,
/** current size of the array */
elementCount: number, elementCount: number,
currentSize: number, currentSize: number,
...@@ -36,7 +37,7 @@ namespace ChunkedArray { ...@@ -36,7 +37,7 @@ namespace ChunkedArray {
let nextSize = array.growBy * array.elementSize; let nextSize = array.growBy * array.elementSize;
array.currentSize = nextSize; array.currentSize = nextSize;
array.currentIndex = 0; array.currentIndex = 0;
array.currentChunk = array.ctor(nextSize); array.currentChunk = new array.ctor(nextSize);
array.allocatedSize += nextSize; array.allocatedSize += nextSize;
array.chunks[array.chunks.length] = array.currentChunk; array.chunks[array.chunks.length] = array.currentChunk;
} }
...@@ -74,6 +75,20 @@ namespace ChunkedArray { ...@@ -74,6 +75,20 @@ namespace ChunkedArray {
return array.elementCount++; 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> { export function compact<T>(array: ChunkedArray<T>, doNotResizeSingleton = false): ArrayLike<T> {
return _compact(array, doNotResizeSingleton); return _compact(array, doNotResizeSingleton);
} }
...@@ -81,7 +96,7 @@ namespace ChunkedArray { ...@@ -81,7 +96,7 @@ namespace ChunkedArray {
export function _compact<T>(array: ChunkedArray<T>, doNotResizeSingleton: boolean): ArrayLike<T> { export function _compact<T>(array: ChunkedArray<T>, doNotResizeSingleton: boolean): ArrayLike<T> {
const { ctor, chunks, currentIndex } = array; const { ctor, chunks, currentIndex } = array;
if (!chunks.length) return ctor(0); if (!chunks.length) return new ctor(0);
if (chunks.length === 1) { if (chunks.length === 1) {
if (doNotResizeSingleton || currentIndex === array.allocatedSize) { if (doNotResizeSingleton || currentIndex === array.allocatedSize) {
return chunks[0]; return chunks[0];
...@@ -92,7 +107,7 @@ namespace ChunkedArray { ...@@ -92,7 +107,7 @@ namespace ChunkedArray {
for (let i = 0, _i = chunks.length - 1; i < _i; i++) size += chunks[i].length; for (let i = 0, _i = chunks.length - 1; i < _i; i++) size += chunks[i].length;
size += array.currentIndex; size += array.currentIndex;
const ret = ctor(size); const ret = new ctor(size) as any;
let offset = 0; let offset = 0;
if (ret.buffer) { if (ret.buffer) {
...@@ -118,12 +133,17 @@ namespace ChunkedArray { ...@@ -118,12 +133,17 @@ namespace ChunkedArray {
return ret; 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> = { const ret: ChunkedArray<T> = {
ctor, ctor,
elementSize, elementSize,
growBy, growBy: chunkSize,
allocatedSize: 0, allocatedSize: 0,
elementCount: 0, elementCount: 0,
...@@ -134,8 +154,17 @@ namespace ChunkedArray { ...@@ -134,8 +154,17 @@ namespace ChunkedArray {
chunks: [] 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.'); if (initialChunk.length % elementSize !== 0) throw new Error('initialChunk length must be a multiple of the element size.');
ret.currentChunk = initialChunk; ret.currentChunk = initialChunk;
ret.allocatedSize = initialChunk.length; ret.allocatedSize = initialChunk.length;
......
...@@ -357,7 +357,7 @@ export namespace ArrayEncoding { ...@@ -357,7 +357,7 @@ export namespace ArrayEncoding {
let map: any = Object.create(null); let map: any = Object.create(null);
let strings: string[] = []; let strings: string[] = [];
let accLength = 0; let accLength = 0;
let offsets = ChunkedArray.create<number>(s => new Int32Array(s), 1, let offsets = ChunkedArray.create<number>(Int32Array, 1,
Math.min(1024, data.length < 32 ? data.length + 1 : Math.round(data.length / 8) + 1)); Math.min(1024, data.length < 32 ? data.length + 1 : Math.round(data.length / 8) + 1));
let output = new Int32Array(data.length); let output = new Int32Array(data.length);
......
...@@ -8,13 +8,13 @@ function testNative(size: number) { ...@@ -8,13 +8,13 @@ function testNative(size: number) {
} }
function testChunkedTyped(size: number, chunk: number) { function testChunkedTyped(size: number, chunk: number) {
const xs = ChunkedArray.create(s => new Int32Array(s), 1, chunk); const xs = ChunkedArray.create(Int32Array, 1, chunk);
for (let i = 0; i < size; i++) ChunkedArray.add(xs, i * i); for (let i = 0; i < size; i++) ChunkedArray.add(xs, i * i);
return ChunkedArray.compact(xs); return ChunkedArray.compact(xs);
} }
function testChunkedNative(size: number, chunk: number) { function testChunkedNative(size: number, chunk: number) {
const xs = ChunkedArray.create(s => [], 1, chunk); const xs = ChunkedArray.create(Array, 1, chunk);
for (let i = 0; i < size; i++) ChunkedArray.add(xs, i * i); for (let i = 0; i < size; i++) ChunkedArray.add(xs, i * i);
return ChunkedArray.compact(xs); return ChunkedArray.compact(xs);
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment