PackedArray.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.PackedArray = void 0;
  4. /*
  5. * This is a TypeScript port of the original Java version, which was written by
  6. * Gil Tene as described in
  7. * https://github.com/HdrHistogram/HdrHistogram
  8. * and released to the public domain, as explained at
  9. * http://creativecommons.org/publicdomain/zero/1.0/
  10. */
  11. const PackedArrayContext_1 = require("./PackedArrayContext");
  12. const NUMBER_OF_SETS = 8;
  13. const { pow, floor } = Math;
  14. /**
  15. * A Packed array of signed 64 bit values, and supports {@link #get get()}, {@link #set set()},
  16. * {@link #add add()} and {@link #increment increment()} operations on the logical contents of the array.
  17. *
  18. * An {@link PackedLongArray} Uses {@link PackedArrayContext} to track
  19. * the array's logical contents. Contexts may be switched when a context requires resizing
  20. * to complete logical array operations (get, set, add, increment). Contexts are
  21. * established and used within critical sections in order to facilitate concurrent
  22. * implementors.
  23. *
  24. */
  25. class PackedArray {
  26. constructor(virtualLength, initialPhysicalLength = PackedArrayContext_1.MINIMUM_INITIAL_PACKED_ARRAY_CAPACITY) {
  27. this.arrayContext = new PackedArrayContext_1.PackedArrayContext(virtualLength, initialPhysicalLength);
  28. }
  29. setVirtualLength(newVirtualArrayLength) {
  30. if (newVirtualArrayLength < this.length()) {
  31. throw new Error("Cannot set virtual length, as requested length " +
  32. newVirtualArrayLength +
  33. " is smaller than the current virtual length " +
  34. this.length());
  35. }
  36. const currentArrayContext = this.arrayContext;
  37. if (currentArrayContext.isPacked &&
  38. currentArrayContext.determineTopLevelShiftForVirtualLength(newVirtualArrayLength) == currentArrayContext.getTopLevelShift()) {
  39. // No changes to the array context contents is needed. Just change the virtual length.
  40. currentArrayContext.setVirtualLength(newVirtualArrayLength);
  41. return;
  42. }
  43. this.arrayContext = currentArrayContext.copyAndIncreaseSize(this.getPhysicalLength(), newVirtualArrayLength);
  44. }
  45. /**
  46. * Get value at virtual index in the array
  47. * @param index the virtual array index
  48. * @return the array value at the virtual index given
  49. */
  50. get(index) {
  51. let value = 0;
  52. for (let byteNum = 0; byteNum < NUMBER_OF_SETS; byteNum++) {
  53. let byteValueAtPackedIndex = 0;
  54. // Deal with unpacked context:
  55. if (!this.arrayContext.isPacked) {
  56. return this.arrayContext.getAtUnpackedIndex(index);
  57. }
  58. // Context is packed:
  59. const packedIndex = this.arrayContext.getPackedIndex(byteNum, index, false);
  60. if (packedIndex < 0) {
  61. return value;
  62. }
  63. byteValueAtPackedIndex =
  64. this.arrayContext.getAtByteIndex(packedIndex) * pow(2, byteNum << 3);
  65. value += byteValueAtPackedIndex;
  66. }
  67. return value;
  68. }
  69. /**
  70. * Increment value at a virrual index in the array
  71. * @param index virtual index of value to increment
  72. */
  73. increment(index) {
  74. this.add(index, 1);
  75. }
  76. safeGetPackedIndexgetPackedIndex(setNumber, virtualIndex) {
  77. //do {
  78. //try {
  79. return this.arrayContext.getPackedIndex(setNumber, virtualIndex, true);
  80. /*} catch (ex) {
  81. if (ex instanceof ResizeError) {
  82. this.arrayContext.resizeArray(ex.newSize);
  83. } else {
  84. throw ex;
  85. }
  86. }*/
  87. //} while (true);
  88. }
  89. /**
  90. * Add to a value at a virtual index in the array
  91. * @param index the virtual index of the value to be added to
  92. * @param value the value to add
  93. */
  94. add(index, value) {
  95. let remainingValueToAdd = value;
  96. for (let byteNum = 0, byteShift = 0; byteNum < NUMBER_OF_SETS; byteNum++, byteShift += 8) {
  97. // Deal with unpacked context:
  98. if (!this.arrayContext.isPacked) {
  99. this.arrayContext.addAndGetAtUnpackedIndex(index, value);
  100. return;
  101. }
  102. // Context is packed:
  103. const packedIndex = this.safeGetPackedIndexgetPackedIndex(byteNum, index);
  104. const byteToAdd = remainingValueToAdd & 0xff;
  105. const afterAddByteValue = this.arrayContext.addAtByteIndex(packedIndex, byteToAdd);
  106. // Reduce remaining value to add by amount just added:
  107. remainingValueToAdd -= byteToAdd;
  108. remainingValueToAdd = remainingValueToAdd / pow(2, 8);
  109. // Account for carry:
  110. remainingValueToAdd += floor(afterAddByteValue / pow(2, 8));
  111. if (remainingValueToAdd == 0) {
  112. return; // nothing to add to higher magnitudes
  113. }
  114. }
  115. }
  116. /**
  117. * Set the value at a virtual index in the array
  118. * @param index the virtual index of the value to set
  119. * @param value the value to set
  120. */
  121. set(index, value) {
  122. let bytesAlreadySet = 0;
  123. let valueForNextLevels = value;
  124. for (let byteNum = 0; byteNum < NUMBER_OF_SETS; byteNum++) {
  125. // Establish context within: critical section
  126. // Deal with unpacked context:
  127. if (!this.arrayContext.isPacked) {
  128. this.arrayContext.setAtUnpackedIndex(index, value);
  129. return;
  130. }
  131. // Context is packed:
  132. if (valueForNextLevels == 0) {
  133. // Special-case zeros to avoid inflating packed array for no reason
  134. const packedIndex = this.arrayContext.getPackedIndex(byteNum, index, false);
  135. if (packedIndex < 0) {
  136. return; // no need to create entries for zero values if they don't already exist
  137. }
  138. }
  139. // Make sure byte is populated:
  140. const packedIndex = this.arrayContext.getPackedIndex(byteNum, index, true);
  141. // Determine value to write, and prepare for next levels
  142. const byteToWrite = valueForNextLevels & 0xff;
  143. valueForNextLevels = floor(valueForNextLevels / pow(2, 8));
  144. if (byteNum < bytesAlreadySet) {
  145. // We want to avoid writing to the same byte twice when not doing so for the
  146. // entire 64 bit value atomically, as doing so opens a race with e.g. concurrent
  147. // adders. So dobn't actually write the byte if has been written before.
  148. continue;
  149. }
  150. this.arrayContext.setAtByteIndex(packedIndex, byteToWrite);
  151. bytesAlreadySet++;
  152. }
  153. }
  154. /**
  155. * Get the current physical length (in longs) of the array's backing storage
  156. * @return the current physical length (in longs) of the array's current backing storage
  157. */
  158. getPhysicalLength() {
  159. return this.arrayContext.physicalLength;
  160. }
  161. /**
  162. * Get the (virtual) length of the array
  163. * @return the (virtual) length of the array
  164. */
  165. length() {
  166. return this.arrayContext.getVirtualLength();
  167. }
  168. /**
  169. * Clear the array contents
  170. */
  171. clear() {
  172. this.arrayContext.clear();
  173. }
  174. toString() {
  175. let output = "PackedArray:\n";
  176. output += this.arrayContext.toString();
  177. return output;
  178. }
  179. }
  180. exports.PackedArray = PackedArray;
  181. //# sourceMappingURL=PackedArray.js.map