@fluidframework/sequence 2.0.0-dev.5.2.0.169897 → 2.0.0-dev.6.4.0.191258

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 (231) hide show
  1. package/CHANGELOG.md +142 -0
  2. package/README.md +4 -3
  3. package/dist/defaultMap.d.ts +1 -1
  4. package/dist/defaultMap.d.ts.map +1 -1
  5. package/dist/defaultMap.js +9 -10
  6. package/dist/defaultMap.js.map +1 -1
  7. package/dist/defaultMapInterfaces.d.ts +1 -1
  8. package/dist/defaultMapInterfaces.d.ts.map +1 -1
  9. package/dist/defaultMapInterfaces.js.map +1 -1
  10. package/dist/index.d.ts +3 -2
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +15 -9
  13. package/dist/index.js.map +1 -1
  14. package/dist/intervalCollection.d.ts +14 -437
  15. package/dist/intervalCollection.d.ts.map +1 -1
  16. package/dist/intervalCollection.js +96 -916
  17. package/dist/intervalCollection.js.map +1 -1
  18. package/dist/intervalIndex/endpointInRangeIndex.d.ts +20 -0
  19. package/dist/intervalIndex/endpointInRangeIndex.d.ts.map +1 -0
  20. package/dist/intervalIndex/endpointInRangeIndex.js +60 -0
  21. package/dist/intervalIndex/endpointInRangeIndex.js.map +1 -0
  22. package/dist/intervalIndex/endpointIndex.d.ts +21 -0
  23. package/dist/intervalIndex/endpointIndex.d.ts.map +1 -0
  24. package/dist/intervalIndex/endpointIndex.js +42 -0
  25. package/dist/intervalIndex/endpointIndex.js.map +1 -0
  26. package/dist/intervalIndex/idIntervalIndex.d.ts +12 -0
  27. package/dist/intervalIndex/idIntervalIndex.d.ts.map +1 -0
  28. package/dist/intervalIndex/idIntervalIndex.js +41 -0
  29. package/dist/intervalIndex/idIntervalIndex.js.map +1 -0
  30. package/dist/intervalIndex/index.d.ts +13 -0
  31. package/dist/intervalIndex/index.d.ts.map +1 -0
  32. package/dist/intervalIndex/index.js +20 -0
  33. package/dist/intervalIndex/index.js.map +1 -0
  34. package/dist/intervalIndex/intervalIndex.d.ts +29 -0
  35. package/dist/intervalIndex/intervalIndex.d.ts.map +1 -0
  36. package/dist/intervalIndex/intervalIndex.js +7 -0
  37. package/dist/intervalIndex/intervalIndex.js.map +1 -0
  38. package/dist/intervalIndex/intervalIndexUtils.d.ts +17 -0
  39. package/dist/intervalIndex/intervalIndexUtils.d.ts.map +1 -0
  40. package/dist/intervalIndex/intervalIndexUtils.js +22 -0
  41. package/dist/intervalIndex/intervalIndexUtils.js.map +1 -0
  42. package/dist/intervalIndex/overlappingIntervalsIndex.d.ts +33 -0
  43. package/dist/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -0
  44. package/dist/intervalIndex/overlappingIntervalsIndex.js +103 -0
  45. package/dist/intervalIndex/overlappingIntervalsIndex.js.map +1 -0
  46. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts +8 -0
  47. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +1 -0
  48. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js +33 -0
  49. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js.map +1 -0
  50. package/dist/intervalIndex/sequenceIntervalIndexes.d.ts +33 -0
  51. package/dist/intervalIndex/sequenceIntervalIndexes.d.ts.map +1 -0
  52. package/dist/intervalIndex/sequenceIntervalIndexes.js +7 -0
  53. package/dist/intervalIndex/sequenceIntervalIndexes.js.map +1 -0
  54. package/dist/intervalIndex/startpointInRangeIndex.d.ts +20 -0
  55. package/dist/intervalIndex/startpointInRangeIndex.d.ts.map +1 -0
  56. package/dist/intervalIndex/startpointInRangeIndex.js +62 -0
  57. package/dist/intervalIndex/startpointInRangeIndex.js.map +1 -0
  58. package/dist/intervalTree.d.ts +2 -56
  59. package/dist/intervalTree.d.ts.map +1 -1
  60. package/dist/intervalTree.js +2 -11
  61. package/dist/intervalTree.js.map +1 -1
  62. package/dist/intervals/index.d.ts +8 -0
  63. package/dist/intervals/index.d.ts.map +1 -0
  64. package/dist/intervals/index.js +23 -0
  65. package/dist/intervals/index.js.map +1 -0
  66. package/dist/intervals/interval.d.ts +88 -0
  67. package/dist/intervals/interval.d.ts.map +1 -0
  68. package/dist/intervals/interval.js +180 -0
  69. package/dist/intervals/interval.js.map +1 -0
  70. package/dist/intervals/intervalUtils.d.ts +200 -0
  71. package/dist/intervals/intervalUtils.d.ts.map +1 -0
  72. package/dist/intervals/intervalUtils.js +79 -0
  73. package/dist/intervals/intervalUtils.js.map +1 -0
  74. package/dist/intervals/sequenceInterval.d.ts +132 -0
  75. package/dist/intervals/sequenceInterval.d.ts.map +1 -0
  76. package/dist/intervals/sequenceInterval.js +313 -0
  77. package/dist/intervals/sequenceInterval.js.map +1 -0
  78. package/dist/packageVersion.d.ts +1 -1
  79. package/dist/packageVersion.js +1 -1
  80. package/dist/packageVersion.js.map +1 -1
  81. package/dist/revertibles.d.ts +1 -1
  82. package/dist/revertibles.d.ts.map +1 -1
  83. package/dist/revertibles.js +85 -52
  84. package/dist/revertibles.js.map +1 -1
  85. package/dist/sequence.d.ts +33 -4
  86. package/dist/sequence.d.ts.map +1 -1
  87. package/dist/sequence.js +91 -47
  88. package/dist/sequence.js.map +1 -1
  89. package/dist/sequenceDeltaEvent.d.ts +8 -3
  90. package/dist/sequenceDeltaEvent.d.ts.map +1 -1
  91. package/dist/sequenceDeltaEvent.js +3 -4
  92. package/dist/sequenceDeltaEvent.js.map +1 -1
  93. package/dist/sharedIntervalCollection.d.ts +2 -1
  94. package/dist/sharedIntervalCollection.d.ts.map +1 -1
  95. package/dist/sharedIntervalCollection.js +2 -2
  96. package/dist/sharedIntervalCollection.js.map +1 -1
  97. package/dist/sharedSequence.d.ts +9 -0
  98. package/dist/sharedSequence.d.ts.map +1 -1
  99. package/dist/sharedSequence.js +9 -6
  100. package/dist/sharedSequence.js.map +1 -1
  101. package/dist/sharedString.d.ts.map +1 -1
  102. package/dist/sharedString.js +9 -29
  103. package/dist/sharedString.js.map +1 -1
  104. package/lib/defaultMap.d.ts +1 -1
  105. package/lib/defaultMap.d.ts.map +1 -1
  106. package/lib/defaultMap.js +5 -6
  107. package/lib/defaultMap.js.map +1 -1
  108. package/lib/defaultMapInterfaces.d.ts +1 -1
  109. package/lib/defaultMapInterfaces.d.ts.map +1 -1
  110. package/lib/defaultMapInterfaces.js.map +1 -1
  111. package/lib/index.d.ts +3 -2
  112. package/lib/index.d.ts.map +1 -1
  113. package/lib/index.js +3 -1
  114. package/lib/index.js.map +1 -1
  115. package/lib/intervalCollection.d.ts +14 -437
  116. package/lib/intervalCollection.d.ts.map +1 -1
  117. package/lib/intervalCollection.js +64 -877
  118. package/lib/intervalCollection.js.map +1 -1
  119. package/lib/intervalIndex/endpointInRangeIndex.d.ts +20 -0
  120. package/lib/intervalIndex/endpointInRangeIndex.d.ts.map +1 -0
  121. package/lib/intervalIndex/endpointInRangeIndex.js +56 -0
  122. package/lib/intervalIndex/endpointInRangeIndex.js.map +1 -0
  123. package/lib/intervalIndex/endpointIndex.d.ts +21 -0
  124. package/lib/intervalIndex/endpointIndex.d.ts.map +1 -0
  125. package/lib/intervalIndex/endpointIndex.js +38 -0
  126. package/lib/intervalIndex/endpointIndex.js.map +1 -0
  127. package/lib/intervalIndex/idIntervalIndex.d.ts +12 -0
  128. package/lib/intervalIndex/idIntervalIndex.d.ts.map +1 -0
  129. package/lib/intervalIndex/idIntervalIndex.js +37 -0
  130. package/lib/intervalIndex/idIntervalIndex.js.map +1 -0
  131. package/lib/intervalIndex/index.d.ts +13 -0
  132. package/lib/intervalIndex/index.d.ts.map +1 -0
  133. package/lib/intervalIndex/index.js +11 -0
  134. package/lib/intervalIndex/index.js.map +1 -0
  135. package/lib/intervalIndex/intervalIndex.d.ts +29 -0
  136. package/lib/intervalIndex/intervalIndex.d.ts.map +1 -0
  137. package/lib/intervalIndex/intervalIndex.js +6 -0
  138. package/lib/intervalIndex/intervalIndex.js.map +1 -0
  139. package/lib/intervalIndex/intervalIndexUtils.d.ts +17 -0
  140. package/lib/intervalIndex/intervalIndexUtils.d.ts.map +1 -0
  141. package/lib/intervalIndex/intervalIndexUtils.js +18 -0
  142. package/lib/intervalIndex/intervalIndexUtils.js.map +1 -0
  143. package/lib/intervalIndex/overlappingIntervalsIndex.d.ts +33 -0
  144. package/lib/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -0
  145. package/lib/intervalIndex/overlappingIntervalsIndex.js +98 -0
  146. package/lib/intervalIndex/overlappingIntervalsIndex.js.map +1 -0
  147. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.ts +8 -0
  148. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +1 -0
  149. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js +29 -0
  150. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js.map +1 -0
  151. package/lib/intervalIndex/sequenceIntervalIndexes.d.ts +33 -0
  152. package/lib/intervalIndex/sequenceIntervalIndexes.d.ts.map +1 -0
  153. package/lib/intervalIndex/sequenceIntervalIndexes.js +6 -0
  154. package/lib/intervalIndex/sequenceIntervalIndexes.js.map +1 -0
  155. package/lib/intervalIndex/startpointInRangeIndex.d.ts +20 -0
  156. package/lib/intervalIndex/startpointInRangeIndex.d.ts.map +1 -0
  157. package/lib/intervalIndex/startpointInRangeIndex.js +58 -0
  158. package/lib/intervalIndex/startpointInRangeIndex.js.map +1 -0
  159. package/lib/intervalTree.d.ts +2 -56
  160. package/lib/intervalTree.d.ts.map +1 -1
  161. package/lib/intervalTree.js +2 -11
  162. package/lib/intervalTree.js.map +1 -1
  163. package/lib/intervals/index.d.ts +8 -0
  164. package/lib/intervals/index.d.ts.map +1 -0
  165. package/lib/intervals/index.js +8 -0
  166. package/lib/intervals/index.js.map +1 -0
  167. package/lib/intervals/interval.d.ts +88 -0
  168. package/lib/intervals/interval.d.ts.map +1 -0
  169. package/lib/intervals/interval.js +175 -0
  170. package/lib/intervals/interval.js.map +1 -0
  171. package/lib/intervals/intervalUtils.d.ts +200 -0
  172. package/lib/intervals/intervalUtils.d.ts.map +1 -0
  173. package/lib/intervals/intervalUtils.js +74 -0
  174. package/lib/intervals/intervalUtils.js.map +1 -0
  175. package/lib/intervals/sequenceInterval.d.ts +132 -0
  176. package/lib/intervals/sequenceInterval.d.ts.map +1 -0
  177. package/lib/intervals/sequenceInterval.js +305 -0
  178. package/lib/intervals/sequenceInterval.js.map +1 -0
  179. package/lib/packageVersion.d.ts +1 -1
  180. package/lib/packageVersion.js +1 -1
  181. package/lib/packageVersion.js.map +1 -1
  182. package/lib/revertibles.d.ts +1 -1
  183. package/lib/revertibles.d.ts.map +1 -1
  184. package/lib/revertibles.js +69 -36
  185. package/lib/revertibles.js.map +1 -1
  186. package/lib/sequence.d.ts +33 -4
  187. package/lib/sequence.d.ts.map +1 -1
  188. package/lib/sequence.js +86 -41
  189. package/lib/sequence.js.map +1 -1
  190. package/lib/sequenceDeltaEvent.d.ts +8 -3
  191. package/lib/sequenceDeltaEvent.d.ts.map +1 -1
  192. package/lib/sequenceDeltaEvent.js +2 -3
  193. package/lib/sequenceDeltaEvent.js.map +1 -1
  194. package/lib/sharedIntervalCollection.d.ts +2 -1
  195. package/lib/sharedIntervalCollection.d.ts.map +1 -1
  196. package/lib/sharedIntervalCollection.js +1 -1
  197. package/lib/sharedIntervalCollection.js.map +1 -1
  198. package/lib/sharedSequence.d.ts +9 -0
  199. package/lib/sharedSequence.d.ts.map +1 -1
  200. package/lib/sharedSequence.js +8 -5
  201. package/lib/sharedSequence.js.map +1 -1
  202. package/lib/sharedString.d.ts.map +1 -1
  203. package/lib/sharedString.js +9 -29
  204. package/lib/sharedString.js.map +1 -1
  205. package/package.json +31 -34
  206. package/src/defaultMap.ts +2 -1
  207. package/src/defaultMapInterfaces.ts +1 -1
  208. package/src/index.ts +21 -9
  209. package/src/intervalCollection.ts +118 -1403
  210. package/src/intervalIndex/endpointInRangeIndex.ts +104 -0
  211. package/src/intervalIndex/endpointIndex.ts +78 -0
  212. package/src/intervalIndex/idIntervalIndex.ts +58 -0
  213. package/src/intervalIndex/index.ts +16 -0
  214. package/src/intervalIndex/intervalIndex.ts +31 -0
  215. package/src/intervalIndex/intervalIndexUtils.ts +27 -0
  216. package/src/intervalIndex/overlappingIntervalsIndex.ts +162 -0
  217. package/src/intervalIndex/overlappingSequenceIntervalsIndex.ts +71 -0
  218. package/src/intervalIndex/sequenceIntervalIndexes.ts +32 -0
  219. package/src/intervalIndex/startpointInRangeIndex.ts +109 -0
  220. package/src/intervalTree.ts +3 -75
  221. package/src/intervals/index.ts +25 -0
  222. package/src/intervals/interval.ts +230 -0
  223. package/src/intervals/intervalUtils.ts +256 -0
  224. package/src/intervals/sequenceInterval.ts +494 -0
  225. package/src/packageVersion.ts +1 -1
  226. package/src/revertibles.ts +81 -16
  227. package/src/sequence.ts +100 -35
  228. package/src/sequenceDeltaEvent.ts +12 -4
  229. package/src/sharedIntervalCollection.ts +2 -3
  230. package/src/sharedSequence.ts +11 -5
  231. package/src/sharedString.ts +8 -25
@@ -0,0 +1,104 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { Client, PropertyAction, RedBlackTree } from "@fluidframework/merge-tree";
7
+ import { IIntervalHelpers, ISerializableInterval, IntervalType } from "../intervals";
8
+ import { IntervalIndex } from "./intervalIndex";
9
+ import { HasComparisonOverride, compareOverrideables, forceCompare } from "./intervalIndexUtils";
10
+
11
+ /**
12
+ * Collection of intervals.
13
+ *
14
+ * Provide additional APIs to support efficiently querying a collection of intervals whose endpoints fall within a specified range.
15
+ */
16
+ export interface IEndpointInRangeIndex<TInterval extends ISerializableInterval>
17
+ extends IntervalIndex<TInterval> {
18
+ /**
19
+ * @returns an array of all intervals contained in this collection whose endpoints locate in the range [start, end] (includes both ends)
20
+ */
21
+ findIntervalsWithEndpointInRange(start: number, end: number);
22
+ }
23
+
24
+ class EndpointInRangeIndex<TInterval extends ISerializableInterval>
25
+ implements IEndpointInRangeIndex<TInterval>
26
+ {
27
+ private readonly intervalTree;
28
+
29
+ constructor(
30
+ private readonly helpers: IIntervalHelpers<TInterval>,
31
+ private readonly client: Client,
32
+ ) {
33
+ this.intervalTree = new RedBlackTree<TInterval, TInterval>((a: TInterval, b: TInterval) => {
34
+ const compareEndsResult = helpers.compareEnds(a, b);
35
+ if (compareEndsResult !== 0) {
36
+ return compareEndsResult;
37
+ }
38
+
39
+ const overrideablesComparison = compareOverrideables(
40
+ a as Partial<HasComparisonOverride>,
41
+ b as Partial<HasComparisonOverride>,
42
+ );
43
+ if (overrideablesComparison !== 0) {
44
+ return overrideablesComparison;
45
+ }
46
+
47
+ const aId = a.getIntervalId();
48
+ const bId = b.getIntervalId();
49
+ if (aId !== undefined && bId !== undefined) {
50
+ return aId.localeCompare(bId);
51
+ }
52
+ return 0;
53
+ });
54
+ }
55
+
56
+ public add(interval: TInterval): void {
57
+ this.intervalTree.put(interval, interval);
58
+ }
59
+
60
+ public remove(interval: TInterval): void {
61
+ this.intervalTree.remove(interval);
62
+ }
63
+
64
+ public findIntervalsWithEndpointInRange(start: number, end: number) {
65
+ if (start <= 0 || start > end || this.intervalTree.isEmpty()) {
66
+ return [];
67
+ }
68
+ const results: TInterval[] = [];
69
+ const action: PropertyAction<TInterval, TInterval> = (node) => {
70
+ results.push(node.data);
71
+ return true;
72
+ };
73
+
74
+ const transientStartInterval = this.helpers.create(
75
+ "transient",
76
+ start,
77
+ start,
78
+ this.client,
79
+ IntervalType.Transient,
80
+ );
81
+
82
+ const transientEndInterval = this.helpers.create(
83
+ "transient",
84
+ end,
85
+ end,
86
+ this.client,
87
+ IntervalType.Transient,
88
+ );
89
+
90
+ // Add comparison overrides to the transient intervals
91
+ (transientStartInterval as Partial<HasComparisonOverride>)[forceCompare] = -1;
92
+ (transientEndInterval as Partial<HasComparisonOverride>)[forceCompare] = 1;
93
+
94
+ this.intervalTree.mapRange(action, results, transientStartInterval, transientEndInterval);
95
+ return results;
96
+ }
97
+ }
98
+
99
+ export function createEndpointInRangeIndex<TInterval extends ISerializableInterval>(
100
+ helpers: IIntervalHelpers<TInterval>,
101
+ client: Client,
102
+ ): IEndpointInRangeIndex<TInterval> {
103
+ return new EndpointInRangeIndex<TInterval>(helpers, client);
104
+ }
@@ -0,0 +1,78 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { Client, RedBlackTree } from "@fluidframework/merge-tree";
7
+ import { IIntervalHelpers, ISerializableInterval, IntervalType } from "../intervals";
8
+ import { IntervalIndex } from "./intervalIndex";
9
+
10
+ export interface IEndpointIndex<TInterval extends ISerializableInterval>
11
+ extends IntervalIndex<TInterval> {
12
+ /**
13
+ * @returns the previous interval based on the given position number.
14
+ * If no such interval exists in this index, returns `undefined`
15
+ */
16
+ previousInterval(pos: number): TInterval | undefined;
17
+
18
+ /**
19
+ * @returns the next interval based on the given position number.
20
+ * If no such interval exists in this index, returns `undefined`
21
+ */
22
+ nextInterval(pos: number): TInterval | undefined;
23
+ }
24
+
25
+ class EndpointIndex<TInterval extends ISerializableInterval> implements IEndpointIndex<TInterval> {
26
+ private readonly endIntervalTree: RedBlackTree<TInterval, TInterval>;
27
+
28
+ constructor(
29
+ private readonly client: Client,
30
+ private readonly helpers: IIntervalHelpers<TInterval>,
31
+ ) {
32
+ // eslint-disable-next-line @typescript-eslint/unbound-method
33
+ this.endIntervalTree = new RedBlackTree<TInterval, TInterval>(helpers.compareEnds);
34
+ }
35
+
36
+ public previousInterval(pos: number): TInterval | undefined {
37
+ const transientInterval = this.helpers.create(
38
+ "transient",
39
+ pos,
40
+ pos,
41
+ this.client,
42
+ IntervalType.Transient,
43
+ );
44
+ const rbNode = this.endIntervalTree.floor(transientInterval);
45
+ if (rbNode) {
46
+ return rbNode.data;
47
+ }
48
+ }
49
+
50
+ public nextInterval(pos: number): TInterval | undefined {
51
+ const transientInterval = this.helpers.create(
52
+ "transient",
53
+ pos,
54
+ pos,
55
+ this.client,
56
+ IntervalType.Transient,
57
+ );
58
+ const rbNode = this.endIntervalTree.ceil(transientInterval);
59
+ if (rbNode) {
60
+ return rbNode.data;
61
+ }
62
+ }
63
+
64
+ public add(interval: TInterval): void {
65
+ this.endIntervalTree.put(interval, interval);
66
+ }
67
+
68
+ public remove(interval: TInterval): void {
69
+ this.endIntervalTree.remove(interval);
70
+ }
71
+ }
72
+
73
+ export function createEndpointIndex<TInterval extends ISerializableInterval>(
74
+ client: Client,
75
+ helpers: IIntervalHelpers<TInterval>,
76
+ ): IEndpointIndex<TInterval> {
77
+ return new EndpointIndex<TInterval>(client, helpers);
78
+ }
@@ -0,0 +1,58 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { assert } from "@fluidframework/core-utils";
7
+ import { ISerializableInterval } from "../intervals";
8
+ import { IntervalIndex } from "./intervalIndex";
9
+
10
+ const reservedIntervalIdKey = "intervalId";
11
+
12
+ export interface IIdIntervalIndex<TInterval extends ISerializableInterval>
13
+ extends IntervalIndex<TInterval>,
14
+ Iterable<TInterval> {
15
+ getIntervalById(id: string): TInterval | undefined;
16
+
17
+ [Symbol.iterator](): Iterator<TInterval>;
18
+ }
19
+ class IdIntervalIndex<TInterval extends ISerializableInterval>
20
+ implements IIdIntervalIndex<TInterval>, Iterable<TInterval>
21
+ {
22
+ private readonly intervalIdMap: Map<string, TInterval> = new Map();
23
+
24
+ public add(interval: TInterval) {
25
+ const id = interval.getIntervalId();
26
+ assert(
27
+ id !== undefined,
28
+ 0x2c0 /* "ID must be created before adding interval to collection" */,
29
+ );
30
+ // Make the ID immutable.
31
+ Object.defineProperty(interval.properties, reservedIntervalIdKey, {
32
+ configurable: false,
33
+ enumerable: true,
34
+ writable: false,
35
+ });
36
+ this.intervalIdMap.set(id, interval);
37
+ }
38
+
39
+ public remove(interval: TInterval) {
40
+ const id = interval.getIntervalId();
41
+ assert(id !== undefined, 0x311 /* expected id to exist on interval */);
42
+ this.intervalIdMap.delete(id);
43
+ }
44
+
45
+ public getIntervalById(id: string): TInterval | undefined {
46
+ return this.intervalIdMap.get(id);
47
+ }
48
+
49
+ public [Symbol.iterator](): IterableIterator<TInterval> {
50
+ return this.intervalIdMap.values();
51
+ }
52
+ }
53
+
54
+ export function createIdIntervalIndex<
55
+ TInterval extends ISerializableInterval,
56
+ >(): IIdIntervalIndex<TInterval> {
57
+ return new IdIntervalIndex<TInterval>();
58
+ }
@@ -0,0 +1,16 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ export { IntervalIndex } from "./intervalIndex";
7
+ export { IIdIntervalIndex, createIdIntervalIndex } from "./idIntervalIndex";
8
+ export { IEndpointIndex, createEndpointIndex } from "./endpointIndex";
9
+ export { IEndpointInRangeIndex, createEndpointInRangeIndex } from "./endpointInRangeIndex";
10
+ export { IStartpointInRangeIndex, createStartpointInRangeIndex } from "./startpointInRangeIndex";
11
+ export { SequenceIntervalIndexes } from "./sequenceIntervalIndexes";
12
+ export {
13
+ IOverlappingIntervalsIndex,
14
+ createOverlappingIntervalsIndex,
15
+ } from "./overlappingIntervalsIndex";
16
+ export { createOverlappingSequenceIntervalsIndex } from "./overlappingSequenceIntervalsIndex";
@@ -0,0 +1,31 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { ISerializableInterval } from "../intervals";
7
+
8
+ /**
9
+ * Collection of intervals.
10
+ *
11
+ * Implementers of this interface will typically implement additional APIs to support efficiently querying a collection
12
+ * of intervals in some manner, for example:
13
+ * - "find all intervals with start endpoint between these two points"
14
+ * - "find all intervals which overlap this range"
15
+ * etc.
16
+ */
17
+ export interface IntervalIndex<TInterval extends ISerializableInterval> {
18
+ /**
19
+ * Adds an interval to the index.
20
+ * @remarks Application code should never need to invoke this method on their index for production scenarios:
21
+ * Fluid handles adding and removing intervals from an index in response to sequence or interval changes.
22
+ */
23
+ add(interval: TInterval): void;
24
+
25
+ /**
26
+ * Removes an interval from the index.
27
+ * @remarks Application code should never need to invoke this method on their index for production scenarios:
28
+ * Fluid handles adding and removing intervals from an index in response to sequence or interval changes.
29
+ */
30
+ remove(interval: TInterval): void;
31
+ }
@@ -0,0 +1,27 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ /**
7
+ * Interface for intervals that have comparison override properties.
8
+ */
9
+ export const forceCompare = Symbol();
10
+
11
+ export interface HasComparisonOverride {
12
+ [forceCompare]: number;
13
+ }
14
+
15
+ /**
16
+ * Compares two objects based on their comparison override properties.
17
+ * @returns A number indicating the order of the intervals (negative for a is lower than b, 0 for tie, positive for a is greater than b).
18
+ */
19
+ export function compareOverrideables(
20
+ a: Partial<HasComparisonOverride>,
21
+ b: Partial<HasComparisonOverride>,
22
+ ): number {
23
+ const forceCompareA = a[forceCompare] ?? 0;
24
+ const forceCompareB = b[forceCompare] ?? 0;
25
+
26
+ return forceCompareA - forceCompareB;
27
+ }
@@ -0,0 +1,162 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { Client } from "@fluidframework/merge-tree";
7
+ import { IntervalType, IIntervalHelpers, ISerializableInterval } from "../intervals";
8
+ import { IntervalNode, IntervalTree } from "../intervalTree";
9
+ import { IntervalIndex } from "./intervalIndex";
10
+
11
+ export interface IOverlappingIntervalsIndex<TInterval extends ISerializableInterval>
12
+ extends IntervalIndex<TInterval> {
13
+ /**
14
+ * @returns an array of all intervals contained in this collection that overlap the range
15
+ * `[start end]`.
16
+ */
17
+ findOverlappingIntervals(start: number, end: number): TInterval[];
18
+
19
+ /**
20
+ * Gathers the interval results based on specified parameters.
21
+ */
22
+ gatherIterationResults(
23
+ results: TInterval[],
24
+ iteratesForward: boolean,
25
+ start?: number,
26
+ end?: number,
27
+ ): void;
28
+ }
29
+
30
+ export class OverlappingIntervalsIndex<TInterval extends ISerializableInterval>
31
+ implements IOverlappingIntervalsIndex<TInterval>
32
+ {
33
+ protected readonly intervalTree = new IntervalTree<TInterval>();
34
+ protected readonly client: Client;
35
+ protected readonly helpers: IIntervalHelpers<TInterval>;
36
+
37
+ constructor(client: Client, helpers: IIntervalHelpers<TInterval>) {
38
+ this.client = client;
39
+ this.helpers = helpers;
40
+ }
41
+
42
+ public map(fn: (interval: TInterval) => void) {
43
+ this.intervalTree.map(fn);
44
+ }
45
+
46
+ public mapUntil(fn: (interval: TInterval) => boolean) {
47
+ this.intervalTree.mapUntil(fn);
48
+ }
49
+
50
+ public gatherIterationResults(
51
+ results: TInterval[],
52
+ iteratesForward: boolean,
53
+ start?: number,
54
+ end?: number,
55
+ ): void {
56
+ if (this.intervalTree.intervals.isEmpty()) {
57
+ return;
58
+ }
59
+
60
+ if (start === undefined && end === undefined) {
61
+ // No start/end provided. Gather the whole tree in the specified order.
62
+ if (iteratesForward) {
63
+ this.intervalTree.map((interval: TInterval) => {
64
+ results.push(interval);
65
+ });
66
+ } else {
67
+ this.intervalTree.mapBackward((interval: TInterval) => {
68
+ results.push(interval);
69
+ });
70
+ }
71
+ } else {
72
+ const transientInterval: TInterval = this.helpers.create(
73
+ "transient",
74
+ start,
75
+ end,
76
+ this.client,
77
+ IntervalType.Transient,
78
+ );
79
+
80
+ if (start === undefined) {
81
+ // Only end position provided. Since the tree is not sorted by end position,
82
+ // walk the whole tree in the specified order, gathering intervals that match the end.
83
+ if (iteratesForward) {
84
+ this.intervalTree.map((interval: TInterval) => {
85
+ if (transientInterval.compareEnd(interval) === 0) {
86
+ results.push(interval);
87
+ }
88
+ });
89
+ } else {
90
+ this.intervalTree.mapBackward((interval: TInterval) => {
91
+ if (transientInterval.compareEnd(interval) === 0) {
92
+ results.push(interval);
93
+ }
94
+ });
95
+ }
96
+ } else {
97
+ // Start and (possibly) end provided. Walk the subtrees that may contain
98
+ // this start position.
99
+ const compareFn =
100
+ end === undefined
101
+ ? (node: IntervalNode<TInterval>) => {
102
+ return transientInterval.compareStart(node.key);
103
+ }
104
+ : (node: IntervalNode<TInterval>) => {
105
+ return transientInterval.compare(node.key);
106
+ };
107
+ const continueLeftFn = (cmpResult: number) => cmpResult <= 0;
108
+ const continueRightFn = (cmpResult: number) => cmpResult >= 0;
109
+ const actionFn = (node: IntervalNode<TInterval>) => {
110
+ results.push(node.key);
111
+ };
112
+
113
+ if (iteratesForward) {
114
+ this.intervalTree.intervals.walkExactMatchesForward(
115
+ compareFn,
116
+ actionFn,
117
+ continueLeftFn,
118
+ continueRightFn,
119
+ );
120
+ } else {
121
+ this.intervalTree.intervals.walkExactMatchesBackward(
122
+ compareFn,
123
+ actionFn,
124
+ continueLeftFn,
125
+ continueRightFn,
126
+ );
127
+ }
128
+ }
129
+ }
130
+ }
131
+
132
+ public findOverlappingIntervals(start: number, end: number): TInterval[] {
133
+ if (end < start || this.intervalTree.intervals.isEmpty()) {
134
+ return [];
135
+ }
136
+ const transientInterval = this.helpers.create(
137
+ "transient",
138
+ start,
139
+ end,
140
+ this.client,
141
+ IntervalType.Transient,
142
+ );
143
+
144
+ const overlappingIntervalNodes = this.intervalTree.match(transientInterval);
145
+ return overlappingIntervalNodes.map((node) => node.key);
146
+ }
147
+
148
+ public remove(interval: TInterval) {
149
+ this.intervalTree.removeExisting(interval);
150
+ }
151
+
152
+ public add(interval: TInterval) {
153
+ this.intervalTree.put(interval);
154
+ }
155
+ }
156
+
157
+ export function createOverlappingIntervalsIndex<TInterval extends ISerializableInterval>(
158
+ client: Client,
159
+ helpers: IIntervalHelpers<TInterval>,
160
+ ): IOverlappingIntervalsIndex<TInterval> {
161
+ return new OverlappingIntervalsIndex<TInterval>(client, helpers);
162
+ }
@@ -0,0 +1,71 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import {
7
+ Client,
8
+ ISegment,
9
+ ReferenceType,
10
+ compareReferencePositions,
11
+ reservedRangeLabelsKey,
12
+ } from "@fluidframework/merge-tree";
13
+ import {
14
+ sequenceIntervalHelpers,
15
+ IntervalType,
16
+ SequenceInterval,
17
+ createPositionReferenceFromSegoff,
18
+ } from "../intervals";
19
+ import { SequenceIntervalIndexes } from "./sequenceIntervalIndexes";
20
+ import { OverlappingIntervalsIndex } from "./overlappingIntervalsIndex";
21
+
22
+ class OverlappingSequenceIntervalsIndex
23
+ extends OverlappingIntervalsIndex<SequenceInterval>
24
+ implements SequenceIntervalIndexes.Overlapping
25
+ {
26
+ constructor(client: Client) {
27
+ super(client, sequenceIntervalHelpers);
28
+ }
29
+
30
+ public findOverlappingIntervalsBySegoff(
31
+ startSegoff: { segment: ISegment | undefined; offset: number | undefined },
32
+ endSegoff: { segment: ISegment | undefined; offset: number | undefined },
33
+ ): Iterable<SequenceInterval> {
34
+ if (this.intervalTree.intervals.isEmpty()) {
35
+ return [];
36
+ }
37
+
38
+ const startLref = createPositionReferenceFromSegoff(
39
+ this.client,
40
+ startSegoff,
41
+ ReferenceType.Transient,
42
+ );
43
+
44
+ const endLref = createPositionReferenceFromSegoff(
45
+ this.client,
46
+ endSegoff,
47
+ ReferenceType.Transient,
48
+ );
49
+
50
+ if (compareReferencePositions(startLref, endLref) > 0) {
51
+ return [];
52
+ }
53
+
54
+ const transientInterval = new SequenceInterval(
55
+ this.client,
56
+ startLref,
57
+ endLref,
58
+ IntervalType.Transient,
59
+ { [reservedRangeLabelsKey]: ["transient"] },
60
+ );
61
+
62
+ const overlappingIntervalNodes = this.intervalTree.match(transientInterval);
63
+ return overlappingIntervalNodes.map((node) => node.key);
64
+ }
65
+ }
66
+
67
+ export function createOverlappingSequenceIntervalsIndex(
68
+ client: Client,
69
+ ): SequenceIntervalIndexes.Overlapping {
70
+ return new OverlappingSequenceIntervalsIndex(client);
71
+ }
@@ -0,0 +1,32 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { ISegment } from "@fluidframework/merge-tree";
7
+ import { SequenceInterval } from "../intervals";
8
+ import { IOverlappingIntervalsIndex } from "./overlappingIntervalsIndex";
9
+
10
+ /**
11
+ * This namespace contains specialiazations of indexes which support spatial queries
12
+ * specifically for `SequenceInterval`s.
13
+ */
14
+ // eslint-disable-next-line @typescript-eslint/no-namespace
15
+ export namespace SequenceIntervalIndexes {
16
+ /**
17
+ * Collection of intervals.
18
+ *
19
+ * Provides additional APIs to support efficiently querying a collection of intervals based on segments and offset.
20
+ */
21
+ export interface Overlapping extends IOverlappingIntervalsIndex<SequenceInterval> {
22
+ /**
23
+ * Finds overlapping intervals within the specified range.
24
+ *
25
+ * @returns an array of all intervals that overlap with the specified SegOff range (includes both ends)
26
+ */
27
+ findOverlappingIntervalsBySegoff(
28
+ startSegoff: { segment: ISegment | undefined; offset: number | undefined },
29
+ endSegoff: { segment: ISegment | undefined; offset: number | undefined },
30
+ );
31
+ }
32
+ }
@@ -0,0 +1,109 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { Client, PropertyAction, RedBlackTree } from "@fluidframework/merge-tree";
7
+ import { assert } from "@fluidframework/core-utils";
8
+ import { IIntervalHelpers, ISerializableInterval, IntervalType } from "../intervals";
9
+ import { IntervalIndex } from "./intervalIndex";
10
+ import { HasComparisonOverride, compareOverrideables, forceCompare } from "./intervalIndexUtils";
11
+
12
+ /**
13
+ * Collection of intervals.
14
+ *
15
+ * Provide additional APIs to support efficiently querying a collection of intervals whose startpoints fall within a specified range.
16
+ */
17
+ export interface IStartpointInRangeIndex<TInterval extends ISerializableInterval>
18
+ extends IntervalIndex<TInterval> {
19
+ /**
20
+ * @returns an array of all intervals contained in this collection whose startpoints locate in the range [start, end] (includes both ends)
21
+ */
22
+ findIntervalsWithStartpointInRange(start: number, end: number);
23
+ }
24
+
25
+ class StartpointInRangeIndex<TInterval extends ISerializableInterval>
26
+ implements IStartpointInRangeIndex<TInterval>
27
+ {
28
+ private readonly intervalTree;
29
+
30
+ constructor(
31
+ private readonly helpers: IIntervalHelpers<TInterval>,
32
+ private readonly client: Client,
33
+ ) {
34
+ this.intervalTree = new RedBlackTree<TInterval, TInterval>((a: TInterval, b: TInterval) => {
35
+ assert(
36
+ typeof helpers.compareStarts === "function",
37
+ 0x6d1 /* compareStarts does not exist in the helpers */,
38
+ );
39
+
40
+ const compareStartsResult = helpers.compareStarts(a, b);
41
+ if (compareStartsResult !== 0) {
42
+ return compareStartsResult;
43
+ }
44
+
45
+ const overrideablesComparison = compareOverrideables(
46
+ a as Partial<HasComparisonOverride>,
47
+ b as Partial<HasComparisonOverride>,
48
+ );
49
+ if (overrideablesComparison !== 0) {
50
+ return overrideablesComparison;
51
+ }
52
+ const aId = a.getIntervalId();
53
+ const bId = b.getIntervalId();
54
+ if (aId !== undefined && bId !== undefined) {
55
+ return aId.localeCompare(bId);
56
+ }
57
+ return 0;
58
+ });
59
+ }
60
+
61
+ public add(interval: TInterval): void {
62
+ this.intervalTree.put(interval, interval);
63
+ }
64
+
65
+ public remove(interval: TInterval): void {
66
+ this.intervalTree.remove(interval);
67
+ }
68
+
69
+ public findIntervalsWithStartpointInRange(start: number, end: number) {
70
+ if (start <= 0 || start > end || this.intervalTree.isEmpty()) {
71
+ return [];
72
+ }
73
+ const results: TInterval[] = [];
74
+ const action: PropertyAction<TInterval, TInterval> = (node) => {
75
+ results.push(node.data);
76
+ return true;
77
+ };
78
+
79
+ const transientStartInterval = this.helpers.create(
80
+ "transient",
81
+ start,
82
+ start,
83
+ this.client,
84
+ IntervalType.Transient,
85
+ );
86
+
87
+ const transientEndInterval = this.helpers.create(
88
+ "transient",
89
+ end,
90
+ end,
91
+ this.client,
92
+ IntervalType.Transient,
93
+ );
94
+
95
+ // Add comparison overrides to the transient intervals
96
+ (transientStartInterval as Partial<HasComparisonOverride>)[forceCompare] = -1;
97
+ (transientEndInterval as Partial<HasComparisonOverride>)[forceCompare] = 1;
98
+
99
+ this.intervalTree.mapRange(action, results, transientStartInterval, transientEndInterval);
100
+ return results;
101
+ }
102
+ }
103
+
104
+ export function createStartpointInRangeIndex<TInterval extends ISerializableInterval>(
105
+ helpers: IIntervalHelpers<TInterval>,
106
+ client: Client,
107
+ ): IStartpointInRangeIndex<TInterval> {
108
+ return new StartpointInRangeIndex<TInterval>(helpers, client);
109
+ }