chunked-array.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. /**
  2. * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * from https://github.com/dsehnal/CIFTools.js
  5. * @author David Sehnal <david.sehnal@gmail.com>
  6. */
  7. /**
  8. * A generic chunked array builder.
  9. *
  10. * When adding elements, the array growns by a specified number
  11. * of elements (either linear or exponential growth) and no copying
  12. * is done until ChunkedArray.compact is called.
  13. */
  14. interface ChunkedArray<T> {
  15. ctor: (size: number) => any,
  16. elementSize: number,
  17. growBy: number,
  18. allocatedSize: number,
  19. elementCount: number,
  20. currentSize: number,
  21. currentChunk: any,
  22. currentIndex: number,
  23. chunks: any[][]
  24. }
  25. namespace ChunkedArray {
  26. export function is(x: any): x is ChunkedArray<any> {
  27. return x.creator && x.chunkSize;
  28. }
  29. function allocateNext(array: ChunkedArray<any>) {
  30. let nextSize = array.growBy * array.elementSize;
  31. array.currentSize = nextSize;
  32. array.currentIndex = 0;
  33. array.currentChunk = array.ctor(nextSize);
  34. array.allocatedSize += nextSize;
  35. array.chunks[array.chunks.length] = array.currentChunk;
  36. }
  37. export function add4<T>(array: ChunkedArray<T>, x: T, y: T, z: T, w: T) {
  38. if (array.currentIndex >= array.currentSize) allocateNext(array);
  39. const c = array.currentChunk;
  40. c[array.currentIndex++] = x;
  41. c[array.currentIndex++] = y;
  42. c[array.currentIndex++] = z;
  43. c[array.currentIndex++] = w;
  44. return array.elementCount++;
  45. }
  46. export function add3<T>(array: ChunkedArray<T>, x: T, y: T, z: T) {
  47. if (array.currentIndex >= array.currentSize) allocateNext(array);
  48. const c = array.currentChunk;
  49. c[array.currentIndex++] = x;
  50. c[array.currentIndex++] = y;
  51. c[array.currentIndex++] = z;
  52. return array.elementCount++;
  53. }
  54. export function add2<T>(array: ChunkedArray<T>, x: T, y: T) {
  55. if (array.currentIndex >= array.currentSize) allocateNext(array);
  56. const c = array.currentChunk;
  57. c[array.currentIndex++] = x;
  58. c[array.currentIndex++] = y;
  59. return array.elementCount++;
  60. }
  61. export function add<T>(array: ChunkedArray<T>, x: T) {
  62. if (array.currentIndex >= array.currentSize) allocateNext(array);
  63. array.currentChunk[array.currentIndex++] = x;
  64. return array.elementCount++;
  65. }
  66. export function compact<T>(array: ChunkedArray<T>, doNotResizeSingleton = false): ArrayLike<T> {
  67. return _compact(array, doNotResizeSingleton);
  68. }
  69. export function _compact<T>(array: ChunkedArray<T>, doNotResizeSingleton: boolean): ArrayLike<T> {
  70. const { ctor, chunks, currentIndex } = array;
  71. if (!chunks.length) return ctor(0);
  72. if (chunks.length === 1) {
  73. if (doNotResizeSingleton || currentIndex === array.allocatedSize) {
  74. return chunks[0];
  75. }
  76. }
  77. let size = 0;
  78. for (let i = 0, _i = chunks.length - 1; i < _i; i++) size += chunks[i].length;
  79. size += array.currentIndex;
  80. const ret = ctor(size);
  81. let offset = 0;
  82. if (ret.buffer) {
  83. for (let i = 0, _i = chunks.length - 1; i < _i; i++) {
  84. ret.set(chunks[i], offset);
  85. offset += chunks[i].length;
  86. }
  87. } else {
  88. for (let i = 0, _i = chunks.length - 1; i < _i; i++) {
  89. const chunk = chunks[i];
  90. for (let j = 0, _j = chunk.length; j < _j; j++) ret[offset + j] = chunk[j];
  91. offset += chunk.length;
  92. }
  93. }
  94. const lastChunk = chunks[chunks.length - 1];
  95. if (ret.buffer && currentIndex >= array.currentSize) {
  96. ret.set(lastChunk, offset);
  97. } else {
  98. for (let j = 0, _j = lastChunk.length; j < _j; j++) ret[offset + j] = lastChunk[j];
  99. }
  100. return ret;
  101. }
  102. export function create<T>(ctor: (size: number) => any, elementSize: number, growBy: number, initialChunk?: ArrayLike<T>): ChunkedArray<T> {
  103. const ret: ChunkedArray<T> = {
  104. ctor,
  105. elementSize,
  106. growBy,
  107. allocatedSize: 0,
  108. elementCount: 0,
  109. currentSize: 0,
  110. currentChunk: void 0,
  111. currentIndex: 0,
  112. chunks: []
  113. };
  114. if (!initialChunk) return ret;
  115. if (initialChunk.length % elementSize !== 0) throw new Error('initialChunk length must be a multiple of the element size.');
  116. ret.currentChunk = initialChunk;
  117. ret.allocatedSize = initialChunk.length;
  118. ret.currentSize = initialChunk.length;
  119. ret.chunks[0] = initialChunk as any;
  120. return ret;
  121. }
  122. }
  123. export { ChunkedArray }