IntegerBufferSet.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. /**
  2. * Copyright (c) 2013-present, Facebook, Inc.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * @typechecks
  8. */
  9. 'use strict';
  10. var Heap = require("./Heap");
  11. var invariant = require("./invariant"); // Data structure that allows to store values and assign positions to them
  12. // in a way to minimize changing positions of stored values when new ones are
  13. // added or when some values are replaced. Stored elements are alwasy assigned
  14. // a consecutive set of positoins startin from 0 up to count of elements less 1
  15. // Following actions can be executed
  16. // * get position assigned to given value (null if value is not stored)
  17. // * create new entry for new value and get assigned position back
  18. // * replace value that is furthest from specified value range with new value
  19. // and get it's position back
  20. // All operations take amortized log(n) time where n is number of elements in
  21. // the set.
  22. var IntegerBufferSet =
  23. /*#__PURE__*/
  24. function () {
  25. function IntegerBufferSet() {
  26. this._valueToPositionMap = {};
  27. this._size = 0;
  28. this._smallValues = new Heap([], // Initial data in the heap
  29. this._smallerComparator);
  30. this._largeValues = new Heap([], // Initial data in the heap
  31. this._greaterComparator);
  32. this.getNewPositionForValue = this.getNewPositionForValue.bind(this);
  33. this.getValuePosition = this.getValuePosition.bind(this);
  34. this.getSize = this.getSize.bind(this);
  35. this.replaceFurthestValuePosition = this.replaceFurthestValuePosition.bind(this);
  36. }
  37. var _proto = IntegerBufferSet.prototype;
  38. _proto.getSize = function getSize()
  39. /*number*/
  40. {
  41. return this._size;
  42. };
  43. _proto.getValuePosition = function getValuePosition(
  44. /*number*/
  45. value)
  46. /*?number*/
  47. {
  48. if (this._valueToPositionMap[value] === undefined) {
  49. return null;
  50. }
  51. return this._valueToPositionMap[value];
  52. };
  53. _proto.getNewPositionForValue = function getNewPositionForValue(
  54. /*number*/
  55. value)
  56. /*number*/
  57. {
  58. !(this._valueToPositionMap[value] === undefined) ? process.env.NODE_ENV !== "production" ? invariant(false, "Shouldn't try to find new position for value already stored in BufferSet") : invariant(false) : void 0;
  59. var newPosition = this._size;
  60. this._size++;
  61. this._pushToHeaps(newPosition, value);
  62. this._valueToPositionMap[value] = newPosition;
  63. return newPosition;
  64. };
  65. _proto.replaceFurthestValuePosition = function replaceFurthestValuePosition(
  66. /*number*/
  67. lowValue,
  68. /*number*/
  69. highValue,
  70. /*number*/
  71. newValue)
  72. /*?number*/
  73. {
  74. !(this._valueToPositionMap[newValue] === undefined) ? process.env.NODE_ENV !== "production" ? invariant(false, "Shouldn't try to replace values with value already stored value in " + "BufferSet") : invariant(false) : void 0;
  75. this._cleanHeaps();
  76. if (this._smallValues.empty() || this._largeValues.empty()) {
  77. // Threre are currently no values stored. We will have to create new
  78. // position for this value.
  79. return null;
  80. }
  81. var minValue = this._smallValues.peek().value;
  82. var maxValue = this._largeValues.peek().value;
  83. if (minValue >= lowValue && maxValue <= highValue) {
  84. // All values currently stored are necessary, we can't reuse any of them.
  85. return null;
  86. }
  87. var valueToReplace;
  88. if (lowValue - minValue > maxValue - highValue) {
  89. // minValue is further from provided range. We will reuse it's position.
  90. valueToReplace = minValue;
  91. this._smallValues.pop();
  92. } else {
  93. valueToReplace = maxValue;
  94. this._largeValues.pop();
  95. }
  96. var position = this._valueToPositionMap[valueToReplace];
  97. delete this._valueToPositionMap[valueToReplace];
  98. this._valueToPositionMap[newValue] = position;
  99. this._pushToHeaps(position, newValue);
  100. return position;
  101. };
  102. _proto._pushToHeaps = function _pushToHeaps(
  103. /*number*/
  104. position,
  105. /*number*/
  106. value) {
  107. var element = {
  108. position: position,
  109. value: value
  110. }; // We can reuse the same object in both heaps, because we don't mutate them
  111. this._smallValues.push(element);
  112. this._largeValues.push(element);
  113. };
  114. _proto._cleanHeaps = function _cleanHeaps() {
  115. // We not usually only remove object from one heap while moving value.
  116. // Here we make sure that there is no stale data on top of heaps.
  117. this._cleanHeap(this._smallValues);
  118. this._cleanHeap(this._largeValues);
  119. var minHeapSize = Math.min(this._smallValues.size(), this._largeValues.size());
  120. var maxHeapSize = Math.max(this._smallValues.size(), this._largeValues.size());
  121. if (maxHeapSize > 10 * minHeapSize) {
  122. // There are many old values in one of heaps. We nned to get rid of them
  123. // to not use too avoid memory leaks
  124. this._recreateHeaps();
  125. }
  126. };
  127. _proto._recreateHeaps = function _recreateHeaps() {
  128. var sourceHeap = this._smallValues.size() < this._largeValues.size() ? this._smallValues : this._largeValues;
  129. var newSmallValues = new Heap([], // Initial data in the heap
  130. this._smallerComparator);
  131. var newLargeValues = new Heap([], // Initial datat in the heap
  132. this._greaterComparator);
  133. while (!sourceHeap.empty()) {
  134. var element = sourceHeap.pop(); // Push all stil valid elements to new heaps
  135. if (this._valueToPositionMap[element.value] !== undefined) {
  136. newSmallValues.push(element);
  137. newLargeValues.push(element);
  138. }
  139. }
  140. this._smallValues = newSmallValues;
  141. this._largeValues = newLargeValues;
  142. };
  143. _proto._cleanHeap = function _cleanHeap(
  144. /*object*/
  145. heap) {
  146. while (!heap.empty() && this._valueToPositionMap[heap.peek().value] === undefined) {
  147. heap.pop();
  148. }
  149. };
  150. _proto._smallerComparator = function _smallerComparator(
  151. /*object*/
  152. lhs,
  153. /*object*/
  154. rhs)
  155. /*boolean*/
  156. {
  157. return lhs.value < rhs.value;
  158. };
  159. _proto._greaterComparator = function _greaterComparator(
  160. /*object*/
  161. lhs,
  162. /*object*/
  163. rhs)
  164. /*boolean*/
  165. {
  166. return lhs.value > rhs.value;
  167. };
  168. return IntegerBufferSet;
  169. }();
  170. module.exports = IntegerBufferSet;