Procházet zdrojové kódy

Chunked array tweaks

David Sehnal před 7 roky
rodič
revize
2af9f1af89

+ 8 - 2
src/mol-data/util/_spec/chunked-array.spec.ts

@@ -8,17 +8,23 @@ import { ChunkedArray } from '../chunked-array'
 
 describe('Chunked Array', () => {
     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, 3, 4);
         expect(ChunkedArray.compact(arr)).toEqual([1, 2, 3, 4]);
     });
 
     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, 2, 1);
         ChunkedArray.add2(arr, 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]);
+    });
 });

+ 39 - 10
src/mol-data/util/chunked-array.ts

@@ -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;

+ 1 - 1
src/mol-io/common/binary-cif/array-encoder.ts

@@ -357,7 +357,7 @@ export namespace ArrayEncoding {
         let map: any = Object.create(null);
         let strings: string[] = [];
         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));
         let output = new Int32Array(data.length);
 

+ 2 - 2
src/perf-tests/chunked-array-vs-native.ts

@@ -8,13 +8,13 @@ function testNative(size: 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);
     return ChunkedArray.compact(xs);
 }
 
 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);
     return ChunkedArray.compact(xs);
 }