@fluid-experimental/tree 0.59.2001 → 0.59.3000

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/.eslintrc.js +2 -0
  2. package/.vscode/SharedTree.code-workspace +15 -0
  3. package/.vscode/settings.json +6 -0
  4. package/dist/ChangeCompression.js +9 -9
  5. package/dist/ChangeCompression.js.map +1 -1
  6. package/dist/ChangeTypes.d.ts +1 -6
  7. package/dist/ChangeTypes.d.ts.map +1 -1
  8. package/dist/ChangeTypes.js +5 -5
  9. package/dist/ChangeTypes.js.map +1 -1
  10. package/dist/Checkout.js +14 -14
  11. package/dist/Checkout.js.map +1 -1
  12. package/dist/Common.d.ts +21 -3
  13. package/dist/Common.d.ts.map +1 -1
  14. package/dist/Common.js +29 -4
  15. package/dist/Common.js.map +1 -1
  16. package/dist/EditLog.js +26 -25
  17. package/dist/EditLog.js.map +1 -1
  18. package/dist/EditUtilities.js +17 -17
  19. package/dist/EditUtilities.js.map +1 -1
  20. package/dist/Forest.js +31 -31
  21. package/dist/Forest.js.map +1 -1
  22. package/dist/HistoryEditFactory.js +9 -9
  23. package/dist/HistoryEditFactory.js.map +1 -1
  24. package/dist/IdConversion.js +9 -9
  25. package/dist/IdConversion.js.map +1 -1
  26. package/dist/Identifiers.d.ts +4 -0
  27. package/dist/Identifiers.d.ts.map +1 -1
  28. package/dist/Identifiers.js.map +1 -1
  29. package/dist/LogViewer.d.ts +1 -5
  30. package/dist/LogViewer.d.ts.map +1 -1
  31. package/dist/LogViewer.js +11 -19
  32. package/dist/LogViewer.js.map +1 -1
  33. package/dist/MergeHealth.js +2 -2
  34. package/dist/MergeHealth.js.map +1 -1
  35. package/dist/NodeIdUtilities.js +2 -2
  36. package/dist/NodeIdUtilities.js.map +1 -1
  37. package/dist/PayloadUtilities.js +1 -1
  38. package/dist/PayloadUtilities.js.map +1 -1
  39. package/dist/RevisionValueCache.d.ts +13 -10
  40. package/dist/RevisionValueCache.d.ts.map +1 -1
  41. package/dist/RevisionValueCache.js +14 -11
  42. package/dist/RevisionValueCache.js.map +1 -1
  43. package/dist/RevisionView.js +4 -4
  44. package/dist/RevisionView.js.map +1 -1
  45. package/dist/SerializationUtilities.js +4 -4
  46. package/dist/SerializationUtilities.js.map +1 -1
  47. package/dist/SharedTree.d.ts +93 -31
  48. package/dist/SharedTree.d.ts.map +1 -1
  49. package/dist/SharedTree.js +160 -131
  50. package/dist/SharedTree.js.map +1 -1
  51. package/dist/SharedTreeEncoder.d.ts +3 -3
  52. package/dist/SharedTreeEncoder.d.ts.map +1 -1
  53. package/dist/SharedTreeEncoder.js +36 -36
  54. package/dist/SharedTreeEncoder.js.map +1 -1
  55. package/dist/StringInterner.js +1 -1
  56. package/dist/StringInterner.js.map +1 -1
  57. package/dist/Summary.js +1 -1
  58. package/dist/Summary.js.map +1 -1
  59. package/dist/SummaryBackCompatibility.js +8 -8
  60. package/dist/SummaryBackCompatibility.js.map +1 -1
  61. package/dist/Transaction.js +1 -1
  62. package/dist/Transaction.js.map +1 -1
  63. package/dist/TransactionInternal.js +17 -17
  64. package/dist/TransactionInternal.js.map +1 -1
  65. package/dist/TreeCompressor.d.ts.map +1 -1
  66. package/dist/TreeCompressor.js +6 -8
  67. package/dist/TreeCompressor.js.map +1 -1
  68. package/dist/TreeNodeHandle.js +4 -4
  69. package/dist/TreeNodeHandle.js.map +1 -1
  70. package/dist/TreeView.js +7 -7
  71. package/dist/TreeView.js.map +1 -1
  72. package/dist/TreeViewUtilities.js +2 -2
  73. package/dist/TreeViewUtilities.js.map +1 -1
  74. package/dist/UndoRedoHandler.js +1 -1
  75. package/dist/UndoRedoHandler.js.map +1 -1
  76. package/dist/UuidUtilities.d.ts +30 -0
  77. package/dist/UuidUtilities.d.ts.map +1 -0
  78. package/dist/UuidUtilities.js +106 -0
  79. package/dist/UuidUtilities.js.map +1 -0
  80. package/dist/id-compressor/AppendOnlySortedMap.d.ts +52 -28
  81. package/dist/id-compressor/AppendOnlySortedMap.d.ts.map +1 -1
  82. package/dist/id-compressor/AppendOnlySortedMap.js +167 -90
  83. package/dist/id-compressor/AppendOnlySortedMap.js.map +1 -1
  84. package/dist/id-compressor/IdCompressor.d.ts +43 -42
  85. package/dist/id-compressor/IdCompressor.d.ts.map +1 -1
  86. package/dist/id-compressor/IdCompressor.js +179 -177
  87. package/dist/id-compressor/IdCompressor.js.map +1 -1
  88. package/dist/id-compressor/IdRange.js +1 -1
  89. package/dist/id-compressor/IdRange.js.map +1 -1
  90. package/dist/id-compressor/NumericUuid.d.ts +6 -14
  91. package/dist/id-compressor/NumericUuid.d.ts.map +1 -1
  92. package/dist/id-compressor/NumericUuid.js +15 -76
  93. package/dist/id-compressor/NumericUuid.js.map +1 -1
  94. package/dist/id-compressor/SessionIdNormalizer.d.ts +122 -0
  95. package/dist/id-compressor/SessionIdNormalizer.d.ts.map +1 -0
  96. package/dist/id-compressor/SessionIdNormalizer.js +418 -0
  97. package/dist/id-compressor/SessionIdNormalizer.js.map +1 -0
  98. package/dist/id-compressor/persisted-types/0.0.1.d.ts +6 -13
  99. package/dist/id-compressor/persisted-types/0.0.1.d.ts.map +1 -1
  100. package/dist/id-compressor/persisted-types/0.0.1.js.map +1 -1
  101. package/dist/index.d.ts +2 -2
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/index.js.map +1 -1
  104. package/dist/persisted-types/0.1.1.d.ts +1 -6
  105. package/dist/persisted-types/0.1.1.d.ts.map +1 -1
  106. package/dist/persisted-types/0.1.1.js +3 -3
  107. package/dist/persisted-types/0.1.1.js.map +1 -1
  108. package/lib/ChangeTypes.d.ts +1 -6
  109. package/lib/ChangeTypes.d.ts.map +1 -1
  110. package/lib/Checkout.js.map +1 -1
  111. package/lib/Common.d.ts +21 -3
  112. package/lib/Common.d.ts.map +1 -1
  113. package/lib/Common.js +25 -3
  114. package/lib/Common.js.map +1 -1
  115. package/lib/EditLog.js +2 -1
  116. package/lib/EditLog.js.map +1 -1
  117. package/lib/EditUtilities.js.map +1 -1
  118. package/lib/Forest.js.map +1 -1
  119. package/lib/HistoryEditFactory.js.map +1 -1
  120. package/lib/Identifiers.d.ts +4 -0
  121. package/lib/Identifiers.d.ts.map +1 -1
  122. package/lib/Identifiers.js.map +1 -1
  123. package/lib/LogViewer.d.ts +1 -5
  124. package/lib/LogViewer.d.ts.map +1 -1
  125. package/lib/LogViewer.js +5 -13
  126. package/lib/LogViewer.js.map +1 -1
  127. package/lib/MergeHealth.js.map +1 -1
  128. package/lib/NodeIdUtilities.js.map +1 -1
  129. package/lib/RevisionValueCache.d.ts +13 -10
  130. package/lib/RevisionValueCache.d.ts.map +1 -1
  131. package/lib/RevisionValueCache.js +10 -7
  132. package/lib/RevisionValueCache.js.map +1 -1
  133. package/lib/RevisionView.js.map +1 -1
  134. package/lib/SharedTree.d.ts +93 -31
  135. package/lib/SharedTree.d.ts.map +1 -1
  136. package/lib/SharedTree.js +107 -78
  137. package/lib/SharedTree.js.map +1 -1
  138. package/lib/SharedTreeEncoder.d.ts +3 -3
  139. package/lib/SharedTreeEncoder.d.ts.map +1 -1
  140. package/lib/SharedTreeEncoder.js +4 -4
  141. package/lib/SharedTreeEncoder.js.map +1 -1
  142. package/lib/StringInterner.js.map +1 -1
  143. package/lib/Summary.js.map +1 -1
  144. package/lib/TreeCompressor.d.ts.map +1 -1
  145. package/lib/TreeCompressor.js +1 -3
  146. package/lib/TreeCompressor.js.map +1 -1
  147. package/lib/TreeNodeHandle.js.map +1 -1
  148. package/lib/TreeView.js.map +1 -1
  149. package/lib/TreeViewUtilities.js.map +1 -1
  150. package/lib/UuidUtilities.d.ts +30 -0
  151. package/lib/UuidUtilities.d.ts.map +1 -0
  152. package/lib/UuidUtilities.js +98 -0
  153. package/lib/UuidUtilities.js.map +1 -0
  154. package/lib/id-compressor/AppendOnlySortedMap.d.ts +52 -28
  155. package/lib/id-compressor/AppendOnlySortedMap.d.ts.map +1 -1
  156. package/lib/id-compressor/AppendOnlySortedMap.js +165 -88
  157. package/lib/id-compressor/AppendOnlySortedMap.js.map +1 -1
  158. package/lib/id-compressor/IdCompressor.d.ts +43 -42
  159. package/lib/id-compressor/IdCompressor.d.ts.map +1 -1
  160. package/lib/id-compressor/IdCompressor.js +97 -95
  161. package/lib/id-compressor/IdCompressor.js.map +1 -1
  162. package/lib/id-compressor/NumericUuid.d.ts +6 -14
  163. package/lib/id-compressor/NumericUuid.d.ts.map +1 -1
  164. package/lib/id-compressor/NumericUuid.js +11 -70
  165. package/lib/id-compressor/NumericUuid.js.map +1 -1
  166. package/lib/id-compressor/SessionIdNormalizer.d.ts +122 -0
  167. package/lib/id-compressor/SessionIdNormalizer.d.ts.map +1 -0
  168. package/lib/id-compressor/SessionIdNormalizer.js +414 -0
  169. package/lib/id-compressor/SessionIdNormalizer.js.map +1 -0
  170. package/lib/id-compressor/persisted-types/0.0.1.d.ts +6 -13
  171. package/lib/id-compressor/persisted-types/0.0.1.d.ts.map +1 -1
  172. package/lib/id-compressor/persisted-types/0.0.1.js.map +1 -1
  173. package/lib/index.d.ts +2 -2
  174. package/lib/index.d.ts.map +1 -1
  175. package/lib/index.js.map +1 -1
  176. package/lib/persisted-types/0.1.1.d.ts +1 -6
  177. package/lib/persisted-types/0.1.1.d.ts.map +1 -1
  178. package/lib/persisted-types/0.1.1.js.map +1 -1
  179. package/lib/test/AppendOnlySortedMap.perf.tests.d.ts +6 -0
  180. package/lib/test/AppendOnlySortedMap.perf.tests.d.ts.map +1 -0
  181. package/lib/test/AppendOnlySortedMap.perf.tests.js +49 -0
  182. package/lib/test/AppendOnlySortedMap.perf.tests.js.map +1 -0
  183. package/lib/test/AppendOnlySortedMap.tests.js +56 -14
  184. package/lib/test/AppendOnlySortedMap.tests.js.map +1 -1
  185. package/lib/test/Checkout.tests.js +2 -2
  186. package/lib/test/Checkout.tests.js.map +1 -1
  187. package/lib/test/Forest.tests.js.map +1 -1
  188. package/lib/test/IdCompressor.perf.tests.js +8 -2
  189. package/lib/test/IdCompressor.perf.tests.js.map +1 -1
  190. package/lib/test/IdCompressor.tests.js +75 -24
  191. package/lib/test/IdCompressor.tests.js.map +1 -1
  192. package/lib/test/LogViewer.tests.js +3 -5
  193. package/lib/test/LogViewer.tests.js.map +1 -1
  194. package/lib/test/NumericUuid.perf.tests.js +4 -4
  195. package/lib/test/NumericUuid.perf.tests.js.map +1 -1
  196. package/lib/test/NumericUuid.tests.js +5 -4
  197. package/lib/test/NumericUuid.tests.js.map +1 -1
  198. package/lib/test/RevisionValueCache.tests.js.map +1 -1
  199. package/lib/test/RevisionView.tests.js.map +1 -1
  200. package/lib/test/SessionIdNormalizer.tests.d.ts +6 -0
  201. package/lib/test/SessionIdNormalizer.tests.d.ts.map +1 -0
  202. package/lib/test/SessionIdNormalizer.tests.js +299 -0
  203. package/lib/test/SessionIdNormalizer.tests.js.map +1 -0
  204. package/lib/test/Summary.tests.js +1 -1
  205. package/lib/test/Summary.tests.js.map +1 -1
  206. package/lib/test/TreeCompression.tests.js +1 -1
  207. package/lib/test/TreeCompression.tests.js.map +1 -1
  208. package/lib/test/Virtualization.tests.js +1 -1
  209. package/lib/test/Virtualization.tests.js.map +1 -1
  210. package/lib/test/fuzz/Generators.d.ts +3 -14
  211. package/lib/test/fuzz/Generators.d.ts.map +1 -1
  212. package/lib/test/fuzz/Generators.js +60 -151
  213. package/lib/test/fuzz/Generators.js.map +1 -1
  214. package/lib/test/fuzz/SharedTreeFuzzTests.d.ts +10 -7
  215. package/lib/test/fuzz/SharedTreeFuzzTests.d.ts.map +1 -1
  216. package/lib/test/fuzz/SharedTreeFuzzTests.js +94 -104
  217. package/lib/test/fuzz/SharedTreeFuzzTests.js.map +1 -1
  218. package/lib/test/fuzz/Types.d.ts +2 -9
  219. package/lib/test/fuzz/Types.d.ts.map +1 -1
  220. package/lib/test/fuzz/Types.js +1 -1
  221. package/lib/test/fuzz/Types.js.map +1 -1
  222. package/lib/test/utilities/IdCompressorTestUtilities.d.ts +57 -11
  223. package/lib/test/utilities/IdCompressorTestUtilities.d.ts.map +1 -1
  224. package/lib/test/utilities/IdCompressorTestUtilities.js +112 -98
  225. package/lib/test/utilities/IdCompressorTestUtilities.js.map +1 -1
  226. package/lib/test/utilities/PendingLocalStateTests.d.ts.map +1 -1
  227. package/lib/test/utilities/PendingLocalStateTests.js +2 -1
  228. package/lib/test/utilities/PendingLocalStateTests.js.map +1 -1
  229. package/lib/test/utilities/SharedTreeTests.d.ts.map +1 -1
  230. package/lib/test/utilities/SharedTreeTests.js +30 -1
  231. package/lib/test/utilities/SharedTreeTests.js.map +1 -1
  232. package/lib/test/utilities/SharedTreeVersioningTests.d.ts.map +1 -1
  233. package/lib/test/utilities/SharedTreeVersioningTests.js +20 -0
  234. package/lib/test/utilities/SharedTreeVersioningTests.js.map +1 -1
  235. package/lib/test/utilities/SummaryLoadPerfTests.d.ts.map +1 -1
  236. package/lib/test/utilities/SummaryLoadPerfTests.js +6 -3
  237. package/lib/test/utilities/SummaryLoadPerfTests.js.map +1 -1
  238. package/lib/test/utilities/TestNode.js.map +1 -1
  239. package/lib/test/utilities/TestUtilities.d.ts +9 -1
  240. package/lib/test/utilities/TestUtilities.d.ts.map +1 -1
  241. package/lib/test/utilities/TestUtilities.js +27 -13
  242. package/lib/test/utilities/TestUtilities.js.map +1 -1
  243. package/package.json +19 -17
  244. package/src/Common.ts +42 -4
  245. package/src/EditLog.ts +1 -1
  246. package/src/Identifiers.ts +5 -0
  247. package/src/LogViewer.ts +4 -20
  248. package/src/RevisionValueCache.ts +11 -8
  249. package/src/SharedTree.ts +222 -75
  250. package/src/SharedTreeEncoder.ts +17 -11
  251. package/src/TreeCompressor.ts +2 -4
  252. package/src/UuidUtilities.ts +123 -0
  253. package/src/id-compressor/AppendOnlySortedMap.ts +183 -94
  254. package/src/id-compressor/IdCompressor.ts +144 -132
  255. package/src/id-compressor/NumericUuid.ts +11 -80
  256. package/src/id-compressor/SessionIdNormalizer.ts +497 -0
  257. package/src/id-compressor/persisted-types/0.0.1.ts +12 -15
  258. package/src/index.ts +5 -0
@@ -5,17 +5,17 @@
5
5
 
6
6
  /* eslint-disable no-bitwise */
7
7
 
8
- import { fail } from '../Common';
8
+ import { assert, fail } from '../Common';
9
9
 
10
10
  /**
11
11
  * A map in which entries are always added in key-sorted order.
12
12
  * Supports appending and searching.
13
13
  */
14
14
  export class AppendOnlySortedMap<K, V> {
15
- protected readonly elements: [K, V][] = [];
15
+ protected readonly elements: (K | V)[] = [];
16
16
 
17
17
  /**
18
- * @param comparator a comparator for keys
18
+ * @param comparator - a comparator for keys
19
19
  */
20
20
  public constructor(protected readonly comparator: (a: K, b: K) => number) {}
21
21
 
@@ -23,29 +23,81 @@ export class AppendOnlySortedMap<K, V> {
23
23
  * @returns the number of entries in this map
24
24
  */
25
25
  public get size(): number {
26
- return this.elements.length;
26
+ return this.elements.length / 2;
27
27
  }
28
28
 
29
29
  /**
30
30
  * @returns the min key in the map.
31
31
  */
32
32
  public minKey(): K | undefined {
33
- return this.elements[0]?.[0];
33
+ return this.elements[0] as K | undefined;
34
34
  }
35
35
 
36
36
  /**
37
37
  * @returns the max key in the map.
38
38
  */
39
39
  public maxKey(): K | undefined {
40
- return this.elements[this.size - 1]?.[0];
40
+ return this.elements[this.elements.length - 2] as K | undefined;
41
+ }
42
+
43
+ /**
44
+ * @returns the min value in the map.
45
+ */
46
+ public minValue(): V | undefined {
47
+ return this.elements[1] as V | undefined;
48
+ }
49
+
50
+ /**
51
+ * @returns the min value in the map.
52
+ */
53
+ public maxValue(): V | undefined {
54
+ return this.elements[this.elements.length - 1] as V | undefined;
55
+ }
56
+
57
+ /**
58
+ * @returns the min key in the map.
59
+ */
60
+ public first(): [K, V] | undefined {
61
+ const { elements } = this;
62
+ const { length } = elements;
63
+ if (length === 0) {
64
+ return undefined;
65
+ }
66
+ return [elements[0] as K, elements[1] as V];
67
+ }
68
+
69
+ /**
70
+ * @returns the max key in the map.
71
+ */
72
+ public last(): [K, V] | undefined {
73
+ const { elements } = this;
74
+ const { length } = elements;
75
+ if (length === 0) {
76
+ return undefined;
77
+ }
78
+ const lastKeyIndex = length - 2;
79
+ return [elements[lastKeyIndex] as K, elements[lastKeyIndex + 1] as V];
80
+ }
81
+
82
+ /**
83
+ * Returns the element at the insertion index.
84
+ */
85
+ public getAtIndex(index: number): [K, V] | undefined {
86
+ const realIndex = index * 2;
87
+ const { elements } = this;
88
+ if (realIndex < 0 || realIndex > elements.length - 1) {
89
+ return undefined;
90
+ }
91
+ return [elements[realIndex] as K, elements[realIndex + 1] as V];
41
92
  }
42
93
 
43
94
  /**
44
95
  * @returns an iterable of the entries in the map.
45
96
  */
46
97
  public *entries(): IterableIterator<readonly [K, V]> {
47
- for (const entry of this.elements) {
48
- yield entry;
98
+ const { elements } = this;
99
+ for (let i = 0; i < elements.length; i += 2) {
100
+ yield [elements[i] as K, elements[i + 1] as V];
49
101
  }
50
102
  }
51
103
 
@@ -53,8 +105,9 @@ export class AppendOnlySortedMap<K, V> {
53
105
  * @returns an iterable of the keys in the map.
54
106
  */
55
107
  public *keys(): IterableIterator<K> {
56
- for (const entry of this.elements) {
57
- yield entry[0];
108
+ const { elements } = this;
109
+ for (let i = 0; i < elements.length; i += 2) {
110
+ yield elements[i] as K;
58
111
  }
59
112
  }
60
113
 
@@ -62,8 +115,9 @@ export class AppendOnlySortedMap<K, V> {
62
115
  * @returns an iterable of the values in the map.
63
116
  */
64
117
  public *values(): IterableIterator<V> {
65
- for (const entry of this.elements) {
66
- yield entry[1];
118
+ const { elements } = this;
119
+ for (let i = 0; i < elements.length; i += 2) {
120
+ yield elements[i + 1] as V;
67
121
  }
68
122
  }
69
123
 
@@ -71,52 +125,55 @@ export class AppendOnlySortedMap<K, V> {
71
125
  * @returns an iterable of the entries in the map, reversed.
72
126
  */
73
127
  public *entriesReversed(): IterableIterator<readonly [K, V]> {
74
- for (let i = this.size - 1; i >= 0; i--) {
75
- yield this.elements[i];
128
+ const { elements } = this;
129
+ for (let i = elements.length - 2; i >= 0; i -= 2) {
130
+ yield [elements[i] as K, elements[i + 1] as V];
76
131
  }
77
132
  }
78
133
 
79
134
  /**
80
- * Adds a new key/value pair to the map. `key` must be > to all keys in the map.
81
- * @param key the key to add.
82
- * @param value the value to add.
135
+ * Adds a new key/value pair to the map. `key` must be \> to all keys in the map.
136
+ * @param key - the key to add.
137
+ * @param value - the value to add.
83
138
  */
84
139
  public append(key: K, value: V): void {
85
- if (this.size !== 0 && this.comparator(key, this.elements[this.size - 1][0]) <= 0) {
140
+ const { elements } = this;
141
+ const { length } = elements;
142
+ if (length !== 0 && this.comparator(key, this.maxKey() as K) <= 0) {
86
143
  fail('Inserted key must be > all others in the map.');
87
144
  }
88
- this.elements.push([key, value]);
145
+ elements.push(key);
146
+ elements.push(value);
89
147
  }
90
148
 
91
- private readonly compareKeys = (search: K, element: readonly [K, V]): number => {
92
- return this.comparator(search, element[0]);
93
- };
94
-
95
149
  /**
96
- * @param key the key to lookup.
150
+ * @param key - the key to lookup.
97
151
  * @returns the value associated with `key` if such an entry exists, and undefined otherwise.
98
152
  */
99
153
  public get(key: K): V | undefined {
100
- const index = AppendOnlySortedMap.indexOf(this.elements, key, this.compareKeys);
101
- return this.elements[index]?.[1];
154
+ const index = AppendOnlySortedMap.keyIndexOf(this.elements, key, this.comparator);
155
+ if (index < 0) {
156
+ return undefined;
157
+ }
158
+ return this.elements[index + 1] as V;
102
159
  }
103
160
 
104
161
  /**
105
- * @param key the key to lookup.
162
+ * @param key - the key to lookup.
106
163
  * @returns the entry associated with `key` if such an entry exists, the entry associated with the next lower key if such an entry
107
164
  * exists, and undefined otherwise.
108
165
  */
109
166
  public getPairOrNextLower(key: K): readonly [K, V] | undefined {
110
- return this.getPairOrNextLowerBy(key, this.compareKeys);
167
+ return this.getPairOrNextLowerBy(key, this.comparator);
111
168
  }
112
169
 
113
170
  /**
114
- * @param key the key to lookup.
171
+ * @param key - the key to lookup.
115
172
  * @returns the entry associated with `key` if such an entry exists, the entry associated with the next higher key if such an entry
116
173
  * exists, and undefined otherwise.
117
174
  */
118
175
  public getPairOrNextHigher(key: K): readonly [K, V] | undefined {
119
- return this.getPairOrNextHigherBy(key, this.compareKeys);
176
+ return this.getPairOrNextHigherBy(key, this.comparator);
120
177
  }
121
178
 
122
179
  /**
@@ -127,13 +184,15 @@ export class AppendOnlySortedMap<K, V> {
127
184
  return true;
128
185
  }
129
186
 
130
- if (this.size !== other.size) {
187
+ if (this.elements.length !== other.elements.length) {
131
188
  return false;
132
189
  }
133
190
 
134
- for (let i = this.size - 1; i >= 0; i--) {
135
- const [keyThis, valueThis] = this.elements[i];
136
- const [keyOther, valueOther] = other.elements[i];
191
+ for (let i = this.elements.length - 2; i >= 0; i -= 2) {
192
+ const keyThis = this.elements[i] as K;
193
+ const valueThis = this.elements[i + 1] as V;
194
+ const keyOther = other.elements[i] as K;
195
+ const valueOther = other.elements[i + 1] as V;
137
196
  if (this.comparator(keyThis, keyOther) !== 0) {
138
197
  return false;
139
198
  }
@@ -145,88 +204,102 @@ export class AppendOnlySortedMap<K, V> {
145
204
  return true;
146
205
  }
147
206
 
207
+ /**
208
+ * Test-only expensive assertions to check the internal validity of the data structure.
209
+ */
210
+ public assertValid(): void {
211
+ let prev: readonly [K, unknown] | undefined;
212
+ for (const kv of this.entries()) {
213
+ if (prev !== undefined) {
214
+ assert(this.comparator(kv[0], prev[0]) > 0, 'Keys in map must be sorted.');
215
+ }
216
+ prev = kv;
217
+ }
218
+ }
219
+
148
220
  /**
149
221
  * Queries a range of entries.
150
- * @param from the key to start the range query at, inclusive.
151
- * @param to the key to end the range query at, inclusive.
222
+ * @param from - the key to start the range query at, inclusive.
223
+ * @param to - the key to end the range query at, inclusive.
152
224
  * @returns the range of entries.
153
225
  */
154
226
  public *getRange(from: K, to: K): IterableIterator<readonly [K, V]> {
155
- const indexFrom = this.getIndexOfOrNextHigher(from, this.compareKeys);
156
- if (indexFrom === undefined) {
227
+ const keyIndexFrom = this.getKeyIndexOfOrNextHigher(from, this.comparator);
228
+ if (keyIndexFrom === undefined) {
157
229
  return;
158
230
  }
159
231
 
160
- const indexTo = this.getIndexOfOrNextLower(to, this.compareKeys);
161
- if (indexTo === undefined) {
232
+ const keyIndexTo = this.getKeyIndexOfOrNextLower(to, this.comparator);
233
+ if (keyIndexTo === undefined) {
162
234
  return;
163
235
  }
164
236
 
165
- for (let i = indexFrom; i <= indexTo; i++) {
166
- yield this.elements[i];
237
+ for (let i = keyIndexFrom; i <= keyIndexTo; i += 2) {
238
+ yield [this.elements[i] as K, this.elements[i + 1] as V];
167
239
  }
168
240
  }
169
241
 
170
242
  protected getPairOrNextLowerBy<T>(
171
243
  search: T,
172
- comparator: (search: T, element: readonly [K, V]) => number
244
+ comparator: (search: T, key: K, value: V) => number
173
245
  ): readonly [K, V] | undefined {
174
- const index = this.getIndexOfOrNextLower(search, comparator);
175
- if (index === undefined) {
246
+ const keyIndex = this.getKeyIndexOfOrNextLower(search, comparator);
247
+ if (keyIndex === undefined) {
176
248
  return undefined;
177
249
  }
178
250
 
179
- return this.elements[index];
251
+ return [this.elements[keyIndex] as K, this.elements[keyIndex + 1] as V];
180
252
  }
181
253
 
182
- private getIndexOfOrNextLower<T>(
254
+ private getKeyIndexOfOrNextLower<T>(
183
255
  search: T,
184
- comparator: (search: T, element: readonly [K, V]) => number
256
+ comparator: (search: T, key: K, value: V) => number
185
257
  ): number | undefined {
186
- const { size } = this;
187
- if (size === 0) {
258
+ const { elements } = this;
259
+ if (elements.length === 0) {
188
260
  return undefined;
189
261
  }
190
- let index = AppendOnlySortedMap.indexOf(this.elements, search, comparator);
191
- if (index < 0) {
192
- index ^= AppendOnlySortedMap.failureXor;
193
- if (index > 0) {
194
- return index - 1;
262
+ let keyIndex = AppendOnlySortedMap.keyIndexOf(elements, search, comparator);
263
+ if (keyIndex < 0) {
264
+ keyIndex ^= AppendOnlySortedMap.failureXor;
265
+ if (keyIndex > 0) {
266
+ return keyIndex - 2;
195
267
  }
196
268
  return undefined;
197
269
  }
198
- return index;
270
+ return keyIndex;
199
271
  }
200
272
 
201
273
  protected getPairOrNextHigherBy<T>(
202
274
  search: T,
203
- comparator: (search: T, element: readonly [K, V]) => number
275
+ comparator: (search: T, key: K, value: V) => number
204
276
  ): readonly [K, V] | undefined {
205
- const index = this.getIndexOfOrNextHigher(search, comparator);
206
- if (index === undefined) {
277
+ const keyIndex = this.getKeyIndexOfOrNextHigher(search, comparator);
278
+ if (keyIndex === undefined) {
207
279
  return undefined;
208
280
  }
209
281
 
210
- return this.elements[index];
282
+ return [this.elements[keyIndex] as K, this.elements[keyIndex + 1] as V];
211
283
  }
212
284
 
213
- private getIndexOfOrNextHigher<T>(
285
+ private getKeyIndexOfOrNextHigher<T>(
214
286
  search: T,
215
- comparator: (search: T, element: readonly [K, V]) => number
287
+ comparator: (search: T, key: K, value: V) => number
216
288
  ): number | undefined {
217
- const { size } = this;
218
- if (size === 0) {
289
+ const { elements } = this;
290
+ const { length } = elements;
291
+ if (length === 0) {
219
292
  return undefined;
220
293
  }
221
- let index = AppendOnlySortedMap.indexOf(this.elements, search, comparator);
222
- if (index < 0) {
223
- index ^= AppendOnlySortedMap.failureXor;
224
- if (index < size) {
225
- return index;
294
+ let keyIndex = AppendOnlySortedMap.keyIndexOf(elements, search, comparator);
295
+ if (keyIndex < 0) {
296
+ keyIndex ^= AppendOnlySortedMap.failureXor;
297
+ if (keyIndex < length) {
298
+ return keyIndex;
226
299
  }
227
300
  return undefined;
228
301
  }
229
- return index;
302
+ return keyIndex;
230
303
  }
231
304
 
232
305
  /**
@@ -236,34 +309,33 @@ export class AppendOnlySortedMap<K, V> {
236
309
 
237
310
  /**
238
311
  * Performs a binary search on the sorted array.
239
- * @param elements
240
- * @param search
241
- * @param comparator
242
- * @returns the index of `search`, or (if not present) the index it would have been inserted into xor'd with `failureXor`. Note that
243
- * negating is not an adequate solution as that could result in -0.
312
+ * @returns the index of the key for `search`, or (if not present) the index it would have been inserted into xor'd
313
+ * with `failureXor`. Note that negating is not an adequate solution as that could result in -0.
244
314
  */
245
- public static indexOf<T, K, V>(
246
- elements: readonly (readonly [K, V])[],
315
+ public static keyIndexOf<T, K, V>(
316
+ elements: readonly (K | V)[],
247
317
  search: T,
248
- comparator: (search: T, element: readonly [K, V]) => number
318
+ comparator: (search: T, key: K, value: V) => number
249
319
  ): number {
320
+ // Low, high, and mid are addresses of [K,V] pairs and *not* key indices
250
321
  let low = 0;
251
- let high = elements.length;
322
+ let high = elements.length / 2;
252
323
  let mid = high >> 1;
253
324
  while (low < high) {
254
- const c = comparator(search, elements[mid]);
325
+ const keyIndex = mid * 2;
326
+ const c = comparator(search, elements[keyIndex] as K, elements[keyIndex + 1] as V);
255
327
  if (c > 0) {
256
328
  low = mid + 1;
257
329
  } else if (c < 0) {
258
330
  high = mid;
259
331
  } else if (c === 0) {
260
- return mid;
332
+ return keyIndex;
261
333
  } else {
262
334
  fail('Invalid comparator.');
263
335
  }
264
336
  mid = (low + high) >> 1;
265
337
  }
266
- return mid ^ AppendOnlySortedMap.failureXor;
338
+ return (mid * 2) ^ AppendOnlySortedMap.failureXor;
267
339
  }
268
340
  }
269
341
 
@@ -275,46 +347,46 @@ export class AppendOnlyDoublySortedMap<K, V, S> extends AppendOnlySortedMap<K, V
275
347
  public constructor(
276
348
  keyComparator: (a: K, b: K) => number,
277
349
  private readonly extractSearchValue: (value: V) => S,
278
- private readonly valueComparator: (search: S, value: V) => number
350
+ private readonly valueComparator: (search: S, value: S) => number
279
351
  ) {
280
352
  super(keyComparator);
281
353
  }
282
354
 
283
355
  public append(key: K, value: V): void {
284
356
  if (
285
- this.size !== 0 &&
286
- this.valueComparator(this.extractSearchValue(value), this.elements[this.size - 1][1]) <= 0
357
+ this.elements.length !== 0 &&
358
+ this.valueComparator(this.extractSearchValue(value), this.extractSearchValue(this.maxValue() as V)) <= 0
287
359
  ) {
288
360
  fail('Inserted value must be > all others in the map.');
289
361
  }
290
362
  super.append(key, value);
291
363
  }
292
364
 
293
- private readonly compareValues = (search: S, element: readonly [K, V]): number => {
294
- return this.valueComparator(search, element[1]);
365
+ private readonly compareValues = (search: S, _: K, value: V): number => {
366
+ return this.valueComparator(search, this.extractSearchValue(value));
295
367
  };
296
368
 
297
369
  /**
298
- * @param value the value to lookup.
370
+ * @param value - the value to lookup.
299
371
  * @returns the key associated with `value` if such an entry exists, and undefined otherwise.
300
372
  */
301
373
  public getByValue(value: S): K | undefined {
302
- const index = AppendOnlySortedMap.indexOf(this.elements, value, this.compareValues);
374
+ const index = AppendOnlySortedMap.keyIndexOf(this.elements, value, this.compareValues);
303
375
  return this.elements[index]?.[0];
304
376
  }
305
377
 
306
378
  /**
307
- * @param searchValue the search value to lookup.
308
- * @returns the entry who's value, when run through the extractor provided to the constructor, matches `searchValue`. If no such entry
309
- * exists, this method returns the next lower entry as determined by the value comparator provided to the constructor. If no such entry
310
- * exists, this method returns undefined.
379
+ * @param searchValue - the search value to lookup.
380
+ * @returns the entry who's value, when run through the extractor provided to the constructor, matches
381
+ * `searchValue`. If no such entry exists, this method returns the next lower entry as determined by the value
382
+ * comparator provided to the constructor. If no such entry exists, this method returns undefined.
311
383
  */
312
384
  public getPairOrNextLowerByValue(searchValue: S): readonly [K, V] | undefined {
313
385
  return this.getPairOrNextLowerBy(searchValue, this.compareValues);
314
386
  }
315
387
 
316
388
  /**
317
- * @param searchValue the search value to lookup.
389
+ * @param searchValue - the search value to lookup.
318
390
  * @returns the entry who's value, when run through the extractor provided to the constructor, matches `searchValue`. If no such entry
319
391
  * exists, this method returns the next higher entry as determined by the value comparator provided to the constructor. If no such entry
320
392
  * exists, this method returns undefined.
@@ -322,4 +394,21 @@ export class AppendOnlyDoublySortedMap<K, V, S> extends AppendOnlySortedMap<K, V
322
394
  public getPairOrNextHigherByValue(searchValue: S): readonly [K, V] | undefined {
323
395
  return this.getPairOrNextHigherBy(searchValue, this.compareValues);
324
396
  }
397
+
398
+ /**
399
+ * Test-only expensive assertions to check the internal validity of the data structure.
400
+ */
401
+ public assertValid(): void {
402
+ super.assertValid();
403
+ let prev: readonly [unknown, V] | undefined;
404
+ for (const kv of this.entries()) {
405
+ if (prev !== undefined) {
406
+ assert(
407
+ this.valueComparator(this.extractSearchValue(kv[1]), this.extractSearchValue(prev[1])) > 0,
408
+ 'Values in map must be sorted.'
409
+ );
410
+ }
411
+ prev = kv;
412
+ }
413
+ }
325
414
  }