@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.
- package/.eslintrc.js +2 -0
- package/.vscode/SharedTree.code-workspace +15 -0
- package/.vscode/settings.json +6 -0
- package/dist/ChangeCompression.js +9 -9
- package/dist/ChangeCompression.js.map +1 -1
- package/dist/ChangeTypes.d.ts +1 -6
- package/dist/ChangeTypes.d.ts.map +1 -1
- package/dist/ChangeTypes.js +5 -5
- package/dist/ChangeTypes.js.map +1 -1
- package/dist/Checkout.js +14 -14
- package/dist/Checkout.js.map +1 -1
- package/dist/Common.d.ts +21 -3
- package/dist/Common.d.ts.map +1 -1
- package/dist/Common.js +29 -4
- package/dist/Common.js.map +1 -1
- package/dist/EditLog.js +26 -25
- package/dist/EditLog.js.map +1 -1
- package/dist/EditUtilities.js +17 -17
- package/dist/EditUtilities.js.map +1 -1
- package/dist/Forest.js +31 -31
- package/dist/Forest.js.map +1 -1
- package/dist/HistoryEditFactory.js +9 -9
- package/dist/HistoryEditFactory.js.map +1 -1
- package/dist/IdConversion.js +9 -9
- package/dist/IdConversion.js.map +1 -1
- package/dist/Identifiers.d.ts +4 -0
- package/dist/Identifiers.d.ts.map +1 -1
- package/dist/Identifiers.js.map +1 -1
- package/dist/LogViewer.d.ts +1 -5
- package/dist/LogViewer.d.ts.map +1 -1
- package/dist/LogViewer.js +11 -19
- package/dist/LogViewer.js.map +1 -1
- package/dist/MergeHealth.js +2 -2
- package/dist/MergeHealth.js.map +1 -1
- package/dist/NodeIdUtilities.js +2 -2
- package/dist/NodeIdUtilities.js.map +1 -1
- package/dist/PayloadUtilities.js +1 -1
- package/dist/PayloadUtilities.js.map +1 -1
- package/dist/RevisionValueCache.d.ts +13 -10
- package/dist/RevisionValueCache.d.ts.map +1 -1
- package/dist/RevisionValueCache.js +14 -11
- package/dist/RevisionValueCache.js.map +1 -1
- package/dist/RevisionView.js +4 -4
- package/dist/RevisionView.js.map +1 -1
- package/dist/SerializationUtilities.js +4 -4
- package/dist/SerializationUtilities.js.map +1 -1
- package/dist/SharedTree.d.ts +93 -31
- package/dist/SharedTree.d.ts.map +1 -1
- package/dist/SharedTree.js +160 -131
- package/dist/SharedTree.js.map +1 -1
- package/dist/SharedTreeEncoder.d.ts +3 -3
- package/dist/SharedTreeEncoder.d.ts.map +1 -1
- package/dist/SharedTreeEncoder.js +36 -36
- package/dist/SharedTreeEncoder.js.map +1 -1
- package/dist/StringInterner.js +1 -1
- package/dist/StringInterner.js.map +1 -1
- package/dist/Summary.js +1 -1
- package/dist/Summary.js.map +1 -1
- package/dist/SummaryBackCompatibility.js +8 -8
- package/dist/SummaryBackCompatibility.js.map +1 -1
- package/dist/Transaction.js +1 -1
- package/dist/Transaction.js.map +1 -1
- package/dist/TransactionInternal.js +17 -17
- package/dist/TransactionInternal.js.map +1 -1
- package/dist/TreeCompressor.d.ts.map +1 -1
- package/dist/TreeCompressor.js +6 -8
- package/dist/TreeCompressor.js.map +1 -1
- package/dist/TreeNodeHandle.js +4 -4
- package/dist/TreeNodeHandle.js.map +1 -1
- package/dist/TreeView.js +7 -7
- package/dist/TreeView.js.map +1 -1
- package/dist/TreeViewUtilities.js +2 -2
- package/dist/TreeViewUtilities.js.map +1 -1
- package/dist/UndoRedoHandler.js +1 -1
- package/dist/UndoRedoHandler.js.map +1 -1
- package/dist/UuidUtilities.d.ts +30 -0
- package/dist/UuidUtilities.d.ts.map +1 -0
- package/dist/UuidUtilities.js +106 -0
- package/dist/UuidUtilities.js.map +1 -0
- package/dist/id-compressor/AppendOnlySortedMap.d.ts +52 -28
- package/dist/id-compressor/AppendOnlySortedMap.d.ts.map +1 -1
- package/dist/id-compressor/AppendOnlySortedMap.js +167 -90
- package/dist/id-compressor/AppendOnlySortedMap.js.map +1 -1
- package/dist/id-compressor/IdCompressor.d.ts +43 -42
- package/dist/id-compressor/IdCompressor.d.ts.map +1 -1
- package/dist/id-compressor/IdCompressor.js +179 -177
- package/dist/id-compressor/IdCompressor.js.map +1 -1
- package/dist/id-compressor/IdRange.js +1 -1
- package/dist/id-compressor/IdRange.js.map +1 -1
- package/dist/id-compressor/NumericUuid.d.ts +6 -14
- package/dist/id-compressor/NumericUuid.d.ts.map +1 -1
- package/dist/id-compressor/NumericUuid.js +15 -76
- package/dist/id-compressor/NumericUuid.js.map +1 -1
- package/dist/id-compressor/SessionIdNormalizer.d.ts +122 -0
- package/dist/id-compressor/SessionIdNormalizer.d.ts.map +1 -0
- package/dist/id-compressor/SessionIdNormalizer.js +418 -0
- package/dist/id-compressor/SessionIdNormalizer.js.map +1 -0
- package/dist/id-compressor/persisted-types/0.0.1.d.ts +6 -13
- package/dist/id-compressor/persisted-types/0.0.1.d.ts.map +1 -1
- package/dist/id-compressor/persisted-types/0.0.1.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/persisted-types/0.1.1.d.ts +1 -6
- package/dist/persisted-types/0.1.1.d.ts.map +1 -1
- package/dist/persisted-types/0.1.1.js +3 -3
- package/dist/persisted-types/0.1.1.js.map +1 -1
- package/lib/ChangeTypes.d.ts +1 -6
- package/lib/ChangeTypes.d.ts.map +1 -1
- package/lib/Checkout.js.map +1 -1
- package/lib/Common.d.ts +21 -3
- package/lib/Common.d.ts.map +1 -1
- package/lib/Common.js +25 -3
- package/lib/Common.js.map +1 -1
- package/lib/EditLog.js +2 -1
- package/lib/EditLog.js.map +1 -1
- package/lib/EditUtilities.js.map +1 -1
- package/lib/Forest.js.map +1 -1
- package/lib/HistoryEditFactory.js.map +1 -1
- package/lib/Identifiers.d.ts +4 -0
- package/lib/Identifiers.d.ts.map +1 -1
- package/lib/Identifiers.js.map +1 -1
- package/lib/LogViewer.d.ts +1 -5
- package/lib/LogViewer.d.ts.map +1 -1
- package/lib/LogViewer.js +5 -13
- package/lib/LogViewer.js.map +1 -1
- package/lib/MergeHealth.js.map +1 -1
- package/lib/NodeIdUtilities.js.map +1 -1
- package/lib/RevisionValueCache.d.ts +13 -10
- package/lib/RevisionValueCache.d.ts.map +1 -1
- package/lib/RevisionValueCache.js +10 -7
- package/lib/RevisionValueCache.js.map +1 -1
- package/lib/RevisionView.js.map +1 -1
- package/lib/SharedTree.d.ts +93 -31
- package/lib/SharedTree.d.ts.map +1 -1
- package/lib/SharedTree.js +107 -78
- package/lib/SharedTree.js.map +1 -1
- package/lib/SharedTreeEncoder.d.ts +3 -3
- package/lib/SharedTreeEncoder.d.ts.map +1 -1
- package/lib/SharedTreeEncoder.js +4 -4
- package/lib/SharedTreeEncoder.js.map +1 -1
- package/lib/StringInterner.js.map +1 -1
- package/lib/Summary.js.map +1 -1
- package/lib/TreeCompressor.d.ts.map +1 -1
- package/lib/TreeCompressor.js +1 -3
- package/lib/TreeCompressor.js.map +1 -1
- package/lib/TreeNodeHandle.js.map +1 -1
- package/lib/TreeView.js.map +1 -1
- package/lib/TreeViewUtilities.js.map +1 -1
- package/lib/UuidUtilities.d.ts +30 -0
- package/lib/UuidUtilities.d.ts.map +1 -0
- package/lib/UuidUtilities.js +98 -0
- package/lib/UuidUtilities.js.map +1 -0
- package/lib/id-compressor/AppendOnlySortedMap.d.ts +52 -28
- package/lib/id-compressor/AppendOnlySortedMap.d.ts.map +1 -1
- package/lib/id-compressor/AppendOnlySortedMap.js +165 -88
- package/lib/id-compressor/AppendOnlySortedMap.js.map +1 -1
- package/lib/id-compressor/IdCompressor.d.ts +43 -42
- package/lib/id-compressor/IdCompressor.d.ts.map +1 -1
- package/lib/id-compressor/IdCompressor.js +97 -95
- package/lib/id-compressor/IdCompressor.js.map +1 -1
- package/lib/id-compressor/NumericUuid.d.ts +6 -14
- package/lib/id-compressor/NumericUuid.d.ts.map +1 -1
- package/lib/id-compressor/NumericUuid.js +11 -70
- package/lib/id-compressor/NumericUuid.js.map +1 -1
- package/lib/id-compressor/SessionIdNormalizer.d.ts +122 -0
- package/lib/id-compressor/SessionIdNormalizer.d.ts.map +1 -0
- package/lib/id-compressor/SessionIdNormalizer.js +414 -0
- package/lib/id-compressor/SessionIdNormalizer.js.map +1 -0
- package/lib/id-compressor/persisted-types/0.0.1.d.ts +6 -13
- package/lib/id-compressor/persisted-types/0.0.1.d.ts.map +1 -1
- package/lib/id-compressor/persisted-types/0.0.1.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/persisted-types/0.1.1.d.ts +1 -6
- package/lib/persisted-types/0.1.1.d.ts.map +1 -1
- package/lib/persisted-types/0.1.1.js.map +1 -1
- package/lib/test/AppendOnlySortedMap.perf.tests.d.ts +6 -0
- package/lib/test/AppendOnlySortedMap.perf.tests.d.ts.map +1 -0
- package/lib/test/AppendOnlySortedMap.perf.tests.js +49 -0
- package/lib/test/AppendOnlySortedMap.perf.tests.js.map +1 -0
- package/lib/test/AppendOnlySortedMap.tests.js +56 -14
- package/lib/test/AppendOnlySortedMap.tests.js.map +1 -1
- package/lib/test/Checkout.tests.js +2 -2
- package/lib/test/Checkout.tests.js.map +1 -1
- package/lib/test/Forest.tests.js.map +1 -1
- package/lib/test/IdCompressor.perf.tests.js +8 -2
- package/lib/test/IdCompressor.perf.tests.js.map +1 -1
- package/lib/test/IdCompressor.tests.js +75 -24
- package/lib/test/IdCompressor.tests.js.map +1 -1
- package/lib/test/LogViewer.tests.js +3 -5
- package/lib/test/LogViewer.tests.js.map +1 -1
- package/lib/test/NumericUuid.perf.tests.js +4 -4
- package/lib/test/NumericUuid.perf.tests.js.map +1 -1
- package/lib/test/NumericUuid.tests.js +5 -4
- package/lib/test/NumericUuid.tests.js.map +1 -1
- package/lib/test/RevisionValueCache.tests.js.map +1 -1
- package/lib/test/RevisionView.tests.js.map +1 -1
- package/lib/test/SessionIdNormalizer.tests.d.ts +6 -0
- package/lib/test/SessionIdNormalizer.tests.d.ts.map +1 -0
- package/lib/test/SessionIdNormalizer.tests.js +299 -0
- package/lib/test/SessionIdNormalizer.tests.js.map +1 -0
- package/lib/test/Summary.tests.js +1 -1
- package/lib/test/Summary.tests.js.map +1 -1
- package/lib/test/TreeCompression.tests.js +1 -1
- package/lib/test/TreeCompression.tests.js.map +1 -1
- package/lib/test/Virtualization.tests.js +1 -1
- package/lib/test/Virtualization.tests.js.map +1 -1
- package/lib/test/fuzz/Generators.d.ts +3 -14
- package/lib/test/fuzz/Generators.d.ts.map +1 -1
- package/lib/test/fuzz/Generators.js +60 -151
- package/lib/test/fuzz/Generators.js.map +1 -1
- package/lib/test/fuzz/SharedTreeFuzzTests.d.ts +10 -7
- package/lib/test/fuzz/SharedTreeFuzzTests.d.ts.map +1 -1
- package/lib/test/fuzz/SharedTreeFuzzTests.js +94 -104
- package/lib/test/fuzz/SharedTreeFuzzTests.js.map +1 -1
- package/lib/test/fuzz/Types.d.ts +2 -9
- package/lib/test/fuzz/Types.d.ts.map +1 -1
- package/lib/test/fuzz/Types.js +1 -1
- package/lib/test/fuzz/Types.js.map +1 -1
- package/lib/test/utilities/IdCompressorTestUtilities.d.ts +57 -11
- package/lib/test/utilities/IdCompressorTestUtilities.d.ts.map +1 -1
- package/lib/test/utilities/IdCompressorTestUtilities.js +112 -98
- package/lib/test/utilities/IdCompressorTestUtilities.js.map +1 -1
- package/lib/test/utilities/PendingLocalStateTests.d.ts.map +1 -1
- package/lib/test/utilities/PendingLocalStateTests.js +2 -1
- package/lib/test/utilities/PendingLocalStateTests.js.map +1 -1
- package/lib/test/utilities/SharedTreeTests.d.ts.map +1 -1
- package/lib/test/utilities/SharedTreeTests.js +30 -1
- package/lib/test/utilities/SharedTreeTests.js.map +1 -1
- package/lib/test/utilities/SharedTreeVersioningTests.d.ts.map +1 -1
- package/lib/test/utilities/SharedTreeVersioningTests.js +20 -0
- package/lib/test/utilities/SharedTreeVersioningTests.js.map +1 -1
- package/lib/test/utilities/SummaryLoadPerfTests.d.ts.map +1 -1
- package/lib/test/utilities/SummaryLoadPerfTests.js +6 -3
- package/lib/test/utilities/SummaryLoadPerfTests.js.map +1 -1
- package/lib/test/utilities/TestNode.js.map +1 -1
- package/lib/test/utilities/TestUtilities.d.ts +9 -1
- package/lib/test/utilities/TestUtilities.d.ts.map +1 -1
- package/lib/test/utilities/TestUtilities.js +27 -13
- package/lib/test/utilities/TestUtilities.js.map +1 -1
- package/package.json +19 -17
- package/src/Common.ts +42 -4
- package/src/EditLog.ts +1 -1
- package/src/Identifiers.ts +5 -0
- package/src/LogViewer.ts +4 -20
- package/src/RevisionValueCache.ts +11 -8
- package/src/SharedTree.ts +222 -75
- package/src/SharedTreeEncoder.ts +17 -11
- package/src/TreeCompressor.ts +2 -4
- package/src/UuidUtilities.ts +123 -0
- package/src/id-compressor/AppendOnlySortedMap.ts +183 -94
- package/src/id-compressor/IdCompressor.ts +144 -132
- package/src/id-compressor/NumericUuid.ts +11 -80
- package/src/id-compressor/SessionIdNormalizer.ts +497 -0
- package/src/id-compressor/persisted-types/0.0.1.ts +12 -15
- 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:
|
|
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]
|
|
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.
|
|
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
|
-
|
|
48
|
-
|
|
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
|
-
|
|
57
|
-
|
|
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
|
-
|
|
66
|
-
|
|
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
|
-
|
|
75
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
101
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
187
|
+
if (this.elements.length !== other.elements.length) {
|
|
131
188
|
return false;
|
|
132
189
|
}
|
|
133
190
|
|
|
134
|
-
for (let i = this.
|
|
135
|
-
const
|
|
136
|
-
const
|
|
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
|
|
156
|
-
if (
|
|
227
|
+
const keyIndexFrom = this.getKeyIndexOfOrNextHigher(from, this.comparator);
|
|
228
|
+
if (keyIndexFrom === undefined) {
|
|
157
229
|
return;
|
|
158
230
|
}
|
|
159
231
|
|
|
160
|
-
const
|
|
161
|
-
if (
|
|
232
|
+
const keyIndexTo = this.getKeyIndexOfOrNextLower(to, this.comparator);
|
|
233
|
+
if (keyIndexTo === undefined) {
|
|
162
234
|
return;
|
|
163
235
|
}
|
|
164
236
|
|
|
165
|
-
for (let 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,
|
|
244
|
+
comparator: (search: T, key: K, value: V) => number
|
|
173
245
|
): readonly [K, V] | undefined {
|
|
174
|
-
const
|
|
175
|
-
if (
|
|
246
|
+
const keyIndex = this.getKeyIndexOfOrNextLower(search, comparator);
|
|
247
|
+
if (keyIndex === undefined) {
|
|
176
248
|
return undefined;
|
|
177
249
|
}
|
|
178
250
|
|
|
179
|
-
return this.elements[
|
|
251
|
+
return [this.elements[keyIndex] as K, this.elements[keyIndex + 1] as V];
|
|
180
252
|
}
|
|
181
253
|
|
|
182
|
-
private
|
|
254
|
+
private getKeyIndexOfOrNextLower<T>(
|
|
183
255
|
search: T,
|
|
184
|
-
comparator: (search: T,
|
|
256
|
+
comparator: (search: T, key: K, value: V) => number
|
|
185
257
|
): number | undefined {
|
|
186
|
-
const {
|
|
187
|
-
if (
|
|
258
|
+
const { elements } = this;
|
|
259
|
+
if (elements.length === 0) {
|
|
188
260
|
return undefined;
|
|
189
261
|
}
|
|
190
|
-
let
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
if (
|
|
194
|
-
return
|
|
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
|
|
270
|
+
return keyIndex;
|
|
199
271
|
}
|
|
200
272
|
|
|
201
273
|
protected getPairOrNextHigherBy<T>(
|
|
202
274
|
search: T,
|
|
203
|
-
comparator: (search: T,
|
|
275
|
+
comparator: (search: T, key: K, value: V) => number
|
|
204
276
|
): readonly [K, V] | undefined {
|
|
205
|
-
const
|
|
206
|
-
if (
|
|
277
|
+
const keyIndex = this.getKeyIndexOfOrNextHigher(search, comparator);
|
|
278
|
+
if (keyIndex === undefined) {
|
|
207
279
|
return undefined;
|
|
208
280
|
}
|
|
209
281
|
|
|
210
|
-
return this.elements[
|
|
282
|
+
return [this.elements[keyIndex] as K, this.elements[keyIndex + 1] as V];
|
|
211
283
|
}
|
|
212
284
|
|
|
213
|
-
private
|
|
285
|
+
private getKeyIndexOfOrNextHigher<T>(
|
|
214
286
|
search: T,
|
|
215
|
-
comparator: (search: T,
|
|
287
|
+
comparator: (search: T, key: K, value: V) => number
|
|
216
288
|
): number | undefined {
|
|
217
|
-
const {
|
|
218
|
-
|
|
289
|
+
const { elements } = this;
|
|
290
|
+
const { length } = elements;
|
|
291
|
+
if (length === 0) {
|
|
219
292
|
return undefined;
|
|
220
293
|
}
|
|
221
|
-
let
|
|
222
|
-
if (
|
|
223
|
-
|
|
224
|
-
if (
|
|
225
|
-
return
|
|
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
|
|
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
|
-
* @
|
|
240
|
-
*
|
|
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
|
|
246
|
-
elements: readonly (
|
|
315
|
+
public static keyIndexOf<T, K, V>(
|
|
316
|
+
elements: readonly (K | V)[],
|
|
247
317
|
search: T,
|
|
248
|
-
comparator: (search: T,
|
|
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
|
|
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
|
|
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:
|
|
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.
|
|
286
|
-
this.valueComparator(this.extractSearchValue(value), this.
|
|
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,
|
|
294
|
-
return this.valueComparator(search,
|
|
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.
|
|
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
|
|
309
|
-
* exists, this method returns the next lower entry as determined by the value
|
|
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
|
}
|