@fluidframework/merge-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 +0 -1
- package/dist/client.d.ts +15 -4
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +60 -34
- package/dist/client.js.map +1 -1
- package/dist/collections.d.ts +7 -1
- package/dist/collections.d.ts.map +1 -1
- package/dist/collections.js +27 -1
- package/dist/collections.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/localReference.d.ts +104 -10
- package/dist/localReference.d.ts.map +1 -1
- package/dist/localReference.js +152 -96
- package/dist/localReference.js.map +1 -1
- package/dist/mergeTree.d.ts +28 -21
- package/dist/mergeTree.d.ts.map +1 -1
- package/dist/mergeTree.js +100 -88
- package/dist/mergeTree.js.map +1 -1
- package/dist/partialLengths.js +10 -10
- package/dist/partialLengths.js.map +1 -1
- package/dist/referencePositions.d.ts +55 -0
- package/dist/referencePositions.d.ts.map +1 -0
- package/dist/referencePositions.js +93 -0
- package/dist/referencePositions.js.map +1 -0
- package/dist/segmentGroupCollection.js +1 -1
- package/dist/segmentGroupCollection.js.map +1 -1
- package/dist/segmentPropertiesManager.js +5 -5
- package/dist/segmentPropertiesManager.js.map +1 -1
- package/dist/snapshotChunks.js.map +1 -1
- package/dist/snapshotLoader.d.ts.map +1 -1
- package/dist/snapshotLoader.js +9 -9
- package/dist/snapshotLoader.js.map +1 -1
- package/dist/snapshotV1.js +7 -7
- package/dist/snapshotV1.js.map +1 -1
- package/dist/snapshotlegacy.js +6 -6
- package/dist/snapshotlegacy.js.map +1 -1
- package/dist/sortedSegmentSet.d.ts.map +1 -1
- package/dist/sortedSegmentSet.js +1 -1
- package/dist/sortedSegmentSet.js.map +1 -1
- package/dist/textSegment.js +3 -2
- package/dist/textSegment.js.map +1 -1
- package/lib/client.d.ts +15 -4
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +31 -5
- package/lib/client.js.map +1 -1
- package/lib/collections.d.ts +7 -1
- package/lib/collections.d.ts.map +1 -1
- package/lib/collections.js +26 -1
- package/lib/collections.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/localReference.d.ts +104 -10
- package/lib/localReference.d.ts.map +1 -1
- package/lib/localReference.js +146 -90
- package/lib/localReference.js.map +1 -1
- package/lib/mergeTree.d.ts +28 -21
- package/lib/mergeTree.d.ts.map +1 -1
- package/lib/mergeTree.js +67 -51
- package/lib/mergeTree.js.map +1 -1
- package/lib/referencePositions.d.ts +55 -0
- package/lib/referencePositions.d.ts.map +1 -0
- package/lib/referencePositions.js +80 -0
- package/lib/referencePositions.js.map +1 -0
- package/lib/segmentPropertiesManager.js.map +1 -1
- package/lib/snapshotChunks.js.map +1 -1
- package/lib/snapshotLoader.d.ts.map +1 -1
- package/lib/snapshotLoader.js.map +1 -1
- package/lib/snapshotV1.js.map +1 -1
- package/lib/snapshotlegacy.js +1 -1
- package/lib/snapshotlegacy.js.map +1 -1
- package/lib/sortedSegmentSet.d.ts.map +1 -1
- package/lib/sortedSegmentSet.js +1 -1
- package/lib/sortedSegmentSet.js.map +1 -1
- package/lib/textSegment.js +3 -2
- package/lib/textSegment.js.map +1 -1
- package/package.json +161 -14
- package/src/client.ts +45 -19
- package/src/collections.ts +55 -54
- package/src/index.ts +1 -0
- package/src/localReference.ts +190 -100
- package/src/mergeTree.ts +107 -99
- package/src/referencePositions.ts +126 -0
- package/src/snapshotChunks.ts +8 -8
- package/src/snapshotLoader.ts +4 -4
- package/src/snapshotV1.ts +1 -1
- package/src/snapshotlegacy.ts +2 -2
- package/src/sortedSegmentSet.ts +4 -4
- package/src/textSegment.ts +2 -2
package/src/mergeTree.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
7
7
|
/* eslint-disable @typescript-eslint/consistent-type-assertions */
|
|
8
8
|
|
|
9
|
-
/* eslint-disable no-bitwise */
|
|
9
|
+
/* eslint-disable @typescript-eslint/prefer-optional-chain, no-bitwise */
|
|
10
10
|
|
|
11
11
|
import { assert } from "@fluidframework/common-utils";
|
|
12
12
|
import {
|
|
@@ -50,26 +50,20 @@ import {
|
|
|
50
50
|
matchProperties,
|
|
51
51
|
PropertySet,
|
|
52
52
|
} from "./properties";
|
|
53
|
+
import {
|
|
54
|
+
refTypeIncludesFlag,
|
|
55
|
+
RangeStackMap,
|
|
56
|
+
ReferencePosition,
|
|
57
|
+
refGetRangeLabels,
|
|
58
|
+
refGetTileLabels,
|
|
59
|
+
refHasRangeLabel,
|
|
60
|
+
refHasRangeLabels,
|
|
61
|
+
refHasTileLabel,
|
|
62
|
+
refHasTileLabels,
|
|
63
|
+
} from "./referencePositions";
|
|
53
64
|
import { SegmentGroupCollection } from "./segmentGroupCollection";
|
|
54
65
|
import { PropertiesManager } from "./segmentPropertiesManager";
|
|
55
|
-
|
|
56
|
-
export interface ReferencePosition {
|
|
57
|
-
properties?: PropertySet;
|
|
58
|
-
refType: ReferenceType;
|
|
59
|
-
// True if this reference is a segment.
|
|
60
|
-
isLeaf(): boolean;
|
|
61
|
-
getSegment(): ISegment | undefined;
|
|
62
|
-
getOffset(): number;
|
|
63
|
-
addProperties(newProps: PropertySet, op?: ICombiningOp): void;
|
|
64
|
-
hasTileLabels(): boolean;
|
|
65
|
-
hasRangeLabels(): boolean;
|
|
66
|
-
hasTileLabel(label: string): boolean;
|
|
67
|
-
hasRangeLabel(label: string): boolean;
|
|
68
|
-
getTileLabels(): string[] | undefined;
|
|
69
|
-
getRangeLabels(): string[] | undefined;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export type RangeStackMap = MapLike<Stack<ReferencePosition>>;
|
|
66
|
+
import { Client } from "./client";
|
|
73
67
|
|
|
74
68
|
export interface IMergeNodeCommon {
|
|
75
69
|
parent?: IMergeBlock;
|
|
@@ -265,7 +259,7 @@ export class MergeNode implements IMergeNodeCommon {
|
|
|
265
259
|
}
|
|
266
260
|
|
|
267
261
|
function addTile(tile: ReferencePosition, tiles: object) {
|
|
268
|
-
const tileLabels = tile
|
|
262
|
+
const tileLabels = refGetTileLabels(tile);
|
|
269
263
|
if (tileLabels) {
|
|
270
264
|
for (const tileLabel of tileLabels) {
|
|
271
265
|
tiles[tileLabel] = tile;
|
|
@@ -274,7 +268,7 @@ function addTile(tile: ReferencePosition, tiles: object) {
|
|
|
274
268
|
}
|
|
275
269
|
|
|
276
270
|
function addTileIfNotPresent(tile: ReferencePosition, tiles: object) {
|
|
277
|
-
const tileLabels = tile
|
|
271
|
+
const tileLabels = refGetTileLabels(tile);
|
|
278
272
|
if (tileLabels) {
|
|
279
273
|
for (const tileLabel of tileLabels) {
|
|
280
274
|
if (tiles[tileLabel] === undefined) {
|
|
@@ -302,14 +296,14 @@ function applyStackDelta(currentStackMap: RangeStackMap, deltaStackMap: RangeSta
|
|
|
302
296
|
}
|
|
303
297
|
|
|
304
298
|
function applyRangeReference(stack: Stack<ReferencePosition>, delta: ReferencePosition) {
|
|
305
|
-
if (delta
|
|
299
|
+
if (refTypeIncludesFlag(delta, ReferenceType.NestBegin)) {
|
|
306
300
|
stack.push(delta);
|
|
307
301
|
return true;
|
|
308
302
|
} else {
|
|
309
303
|
// Assume delta is end reference
|
|
310
304
|
const top = stack.top();
|
|
311
305
|
// TODO: match end with begin
|
|
312
|
-
if (top && (top
|
|
306
|
+
if (top && (refTypeIncludesFlag(top, ReferenceType.NestBegin))) {
|
|
313
307
|
stack.pop();
|
|
314
308
|
} else {
|
|
315
309
|
stack.push(delta);
|
|
@@ -340,14 +334,14 @@ function addNodeReferences(
|
|
|
340
334
|
if (markerId) {
|
|
341
335
|
mergeTree.mapIdToSegment(markerId, segment);
|
|
342
336
|
}
|
|
343
|
-
if (segment
|
|
337
|
+
if (refTypeIncludesFlag(segment, ReferenceType.Tile)) {
|
|
344
338
|
addTile(segment, rightmostTiles);
|
|
345
339
|
addTileIfNotPresent(segment, leftmostTiles);
|
|
346
340
|
}
|
|
347
341
|
if (segment.refType & (ReferenceType.NestBegin | ReferenceType.NestEnd)) {
|
|
348
|
-
const rangeLabels = segment
|
|
342
|
+
const rangeLabels = refGetRangeLabels(segment);
|
|
349
343
|
if (rangeLabels) {
|
|
350
|
-
for (const label of
|
|
344
|
+
for (const label of rangeLabels) {
|
|
351
345
|
updateRangeInfo(label, segment);
|
|
352
346
|
}
|
|
353
347
|
}
|
|
@@ -357,12 +351,12 @@ function addNodeReferences(
|
|
|
357
351
|
if (baseSegment.localRefs && (baseSegment.localRefs.hierRefCount !== undefined) &&
|
|
358
352
|
(baseSegment.localRefs.hierRefCount > 0)) {
|
|
359
353
|
for (const lref of baseSegment.localRefs) {
|
|
360
|
-
if (lref
|
|
354
|
+
if (refTypeIncludesFlag(lref, ReferenceType.Tile)) {
|
|
361
355
|
addTile(lref, rightmostTiles);
|
|
362
356
|
addTileIfNotPresent(lref, leftmostTiles);
|
|
363
357
|
}
|
|
364
358
|
if (lref.refType & (ReferenceType.NestBegin | ReferenceType.NestEnd)) {
|
|
365
|
-
for (const label of lref
|
|
359
|
+
for (const label of refGetRangeLabels(lref)!) {
|
|
366
360
|
updateRangeInfo(label, lref);
|
|
367
361
|
}
|
|
368
362
|
}
|
|
@@ -622,43 +616,9 @@ export abstract class BaseSegment extends MergeNode implements ISegment {
|
|
|
622
616
|
protected abstract createSplitSegmentAt(pos: number): BaseSegment | undefined;
|
|
623
617
|
}
|
|
624
618
|
|
|
625
|
-
export const reservedTileLabelsKey = "referenceTileLabels";
|
|
626
|
-
export const reservedRangeLabelsKey = "referenceRangeLabels";
|
|
627
619
|
export const reservedMarkerIdKey = "markerId";
|
|
628
620
|
export const reservedMarkerSimpleTypeKey = "markerSimpleType";
|
|
629
621
|
|
|
630
|
-
export const refGetTileLabels = (refPos: ReferencePosition) =>
|
|
631
|
-
(refPos.refType & ReferenceType.Tile)
|
|
632
|
-
&& refPos.properties ? refPos.properties[reservedTileLabelsKey] as string[] : undefined;
|
|
633
|
-
|
|
634
|
-
export const refGetRangeLabels = (refPos: ReferencePosition) =>
|
|
635
|
-
(refPos.refType & (ReferenceType.NestBegin | ReferenceType.NestEnd))
|
|
636
|
-
&& refPos.properties ? refPos.properties[reservedRangeLabelsKey] as string[] : undefined;
|
|
637
|
-
|
|
638
|
-
export function refHasTileLabel(refPos: ReferencePosition, label: string) {
|
|
639
|
-
const tileLabels = refPos.getTileLabels();
|
|
640
|
-
if (tileLabels) {
|
|
641
|
-
for (const refLabel of tileLabels) {
|
|
642
|
-
if (label === refLabel) {
|
|
643
|
-
return true;
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
return false;
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
export function refHasRangeLabel(refPos: ReferencePosition, label: string) {
|
|
651
|
-
const rangeLabels = refPos.getRangeLabels();
|
|
652
|
-
if (rangeLabels) {
|
|
653
|
-
for (const refLabel of rangeLabels) {
|
|
654
|
-
if (label === refLabel) {
|
|
655
|
-
return true;
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
return false;
|
|
660
|
-
}
|
|
661
|
-
|
|
662
622
|
export interface IJSONMarkerSegment extends IJSONSegment {
|
|
663
623
|
marker: IMarkerDef;
|
|
664
624
|
}
|
|
@@ -728,42 +688,55 @@ export class Marker extends BaseSegment implements ReferencePosition {
|
|
|
728
688
|
}
|
|
729
689
|
}
|
|
730
690
|
|
|
691
|
+
/**
|
|
692
|
+
* @deprecated - use refHasTileLabels
|
|
693
|
+
*/
|
|
731
694
|
hasTileLabels() {
|
|
732
|
-
return
|
|
695
|
+
return refHasTileLabels(this);
|
|
733
696
|
}
|
|
734
|
-
|
|
697
|
+
/**
|
|
698
|
+
* @deprecated - use refHasRangeLabels
|
|
699
|
+
*/
|
|
735
700
|
hasRangeLabels() {
|
|
736
|
-
return
|
|
701
|
+
return refHasRangeLabels(this);
|
|
737
702
|
}
|
|
738
|
-
|
|
703
|
+
/**
|
|
704
|
+
* @deprecated - use refHasTileLabel
|
|
705
|
+
*/
|
|
739
706
|
hasTileLabel(label: string): boolean {
|
|
740
707
|
return refHasTileLabel(this, label);
|
|
741
708
|
}
|
|
742
|
-
|
|
709
|
+
/**
|
|
710
|
+
* @deprecated - use refHasRangeLabel
|
|
711
|
+
*/
|
|
743
712
|
hasRangeLabel(label: string): boolean {
|
|
744
713
|
return refHasRangeLabel(this, label);
|
|
745
714
|
}
|
|
746
|
-
|
|
715
|
+
/**
|
|
716
|
+
* @deprecated - use refGetTileLabels
|
|
717
|
+
*/
|
|
747
718
|
getTileLabels(): string[] | undefined {
|
|
748
719
|
return refGetTileLabels(this);
|
|
749
720
|
}
|
|
750
|
-
|
|
721
|
+
/**
|
|
722
|
+
* @deprecated - use refGetRangeLabels
|
|
723
|
+
*/
|
|
751
724
|
getRangeLabels(): string[] | undefined {
|
|
752
725
|
return refGetRangeLabels(this);
|
|
753
726
|
}
|
|
754
727
|
|
|
755
728
|
toString() {
|
|
756
729
|
let bbuf = "";
|
|
757
|
-
if (this
|
|
730
|
+
if (refTypeIncludesFlag(this, ReferenceType.Tile)) {
|
|
758
731
|
bbuf += "Tile";
|
|
759
732
|
}
|
|
760
|
-
if (this
|
|
733
|
+
if (refTypeIncludesFlag(this, ReferenceType.NestBegin)) {
|
|
761
734
|
if (bbuf.length > 0) {
|
|
762
735
|
bbuf += "; ";
|
|
763
736
|
}
|
|
764
737
|
bbuf += "RangeBegin";
|
|
765
738
|
}
|
|
766
|
-
if (this
|
|
739
|
+
if (refTypeIncludesFlag(this, ReferenceType.NestEnd)) {
|
|
767
740
|
if (bbuf.length > 0) {
|
|
768
741
|
bbuf += "; ";
|
|
769
742
|
}
|
|
@@ -774,7 +747,7 @@ export class Marker extends BaseSegment implements ReferencePosition {
|
|
|
774
747
|
if (id) {
|
|
775
748
|
bbuf += ` (${id}) `;
|
|
776
749
|
}
|
|
777
|
-
const tileLabels = this
|
|
750
|
+
const tileLabels = refGetTileLabels(this);
|
|
778
751
|
if (tileLabels) {
|
|
779
752
|
lbuf += "tile -- ";
|
|
780
753
|
for (let i = 0, len = tileLabels.length; i < len; i++) {
|
|
@@ -785,10 +758,10 @@ export class Marker extends BaseSegment implements ReferencePosition {
|
|
|
785
758
|
lbuf += tileLabel;
|
|
786
759
|
}
|
|
787
760
|
}
|
|
788
|
-
const rangeLabels = this
|
|
761
|
+
const rangeLabels = refGetRangeLabels(this);
|
|
789
762
|
if (rangeLabels) {
|
|
790
763
|
let rangeKind = "begin";
|
|
791
|
-
if (this
|
|
764
|
+
if (refTypeIncludesFlag(this, ReferenceType.NestEnd)) {
|
|
792
765
|
rangeKind = "end";
|
|
793
766
|
}
|
|
794
767
|
if (tileLabels) {
|
|
@@ -931,7 +904,7 @@ interface IMarkerSearchRangeInfo {
|
|
|
931
904
|
|
|
932
905
|
function applyLeafRangeMarker(marker: Marker, searchInfo: IMarkerSearchRangeInfo) {
|
|
933
906
|
for (const rangeLabel of searchInfo.rangeLabels) {
|
|
934
|
-
if (marker
|
|
907
|
+
if (refHasRangeLabel(marker, rangeLabel)) {
|
|
935
908
|
let currentStack = searchInfo.stacks[rangeLabel];
|
|
936
909
|
if (currentStack === undefined) {
|
|
937
910
|
currentStack = new Stack<Marker>();
|
|
@@ -981,7 +954,7 @@ function recordTileStart(
|
|
|
981
954
|
end: number,
|
|
982
955
|
searchInfo: IReferenceSearchInfo) {
|
|
983
956
|
if (Marker.is(segment)) {
|
|
984
|
-
if (segment
|
|
957
|
+
if (refHasTileLabel(segment, searchInfo.tileLabel)) {
|
|
985
958
|
searchInfo.tile = segment;
|
|
986
959
|
}
|
|
987
960
|
}
|
|
@@ -994,7 +967,7 @@ function tileShift(
|
|
|
994
967
|
if (node.isLeaf()) {
|
|
995
968
|
const seg = node;
|
|
996
969
|
if ((searchInfo.mergeTree.localNetLength(seg) > 0) && Marker.is(seg)) {
|
|
997
|
-
if (seg
|
|
970
|
+
if (refHasTileLabel(seg, searchInfo.tileLabel)) {
|
|
998
971
|
searchInfo.tile = seg;
|
|
999
972
|
}
|
|
1000
973
|
}
|
|
@@ -1134,7 +1107,7 @@ export class MergeTree {
|
|
|
1134
1107
|
// and update the block's info.
|
|
1135
1108
|
for (let childIndex = 0;
|
|
1136
1109
|
childIndex < maxChildren && nodeIndex < nodes.length; // While we still have children & nodes left
|
|
1137
|
-
childIndex
|
|
1110
|
+
childIndex++, nodeIndex++ // Advance to next child & node
|
|
1138
1111
|
) {
|
|
1139
1112
|
// Insert the next node into the current block
|
|
1140
1113
|
this.addNode(block, nodes[nodeIndex]);
|
|
@@ -1202,11 +1175,13 @@ export class MergeTree {
|
|
|
1202
1175
|
} else {
|
|
1203
1176
|
// Notify maintenance event observers that the segment is being unlinked from the MergeTree
|
|
1204
1177
|
if (this.mergeTreeMaintenanceCallback) {
|
|
1205
|
-
this.mergeTreeMaintenanceCallback(
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1178
|
+
this.mergeTreeMaintenanceCallback(
|
|
1179
|
+
{
|
|
1180
|
+
operation: MergeTreeMaintenanceType.UNLINK,
|
|
1181
|
+
deltaSegments: [{ segment }],
|
|
1182
|
+
},
|
|
1183
|
+
undefined,
|
|
1184
|
+
);
|
|
1210
1185
|
}
|
|
1211
1186
|
|
|
1212
1187
|
segment.parent = undefined;
|
|
@@ -1223,11 +1198,13 @@ export class MergeTree {
|
|
|
1223
1198
|
if (canAppend) {
|
|
1224
1199
|
prevSegment!.append(segment);
|
|
1225
1200
|
if (this.mergeTreeMaintenanceCallback) {
|
|
1226
|
-
this.mergeTreeMaintenanceCallback(
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1201
|
+
this.mergeTreeMaintenanceCallback(
|
|
1202
|
+
{
|
|
1203
|
+
operation: MergeTreeMaintenanceType.APPEND,
|
|
1204
|
+
deltaSegments: [{ segment: prevSegment! }, { segment }],
|
|
1205
|
+
},
|
|
1206
|
+
undefined,
|
|
1207
|
+
);
|
|
1231
1208
|
}
|
|
1232
1209
|
segment.parent = undefined;
|
|
1233
1210
|
segment.trackingCollection.trackingGroups.forEach((tg) => tg.unlink(segment));
|
|
@@ -1461,7 +1438,7 @@ export class MergeTree {
|
|
|
1461
1438
|
const segment = node;
|
|
1462
1439
|
const removalInfo = toRemovalInfo(segment);
|
|
1463
1440
|
|
|
1464
|
-
if(removalInfo !== undefined
|
|
1441
|
+
if (removalInfo !== undefined
|
|
1465
1442
|
&& removalInfo.removedSeq !== UnassignedSequenceNumber
|
|
1466
1443
|
&& removalInfo.removedSeq <= refSeq) {
|
|
1467
1444
|
// this segment is a tombstone eligible for zamboni
|
|
@@ -1485,7 +1462,7 @@ export class MergeTree {
|
|
|
1485
1462
|
// the segment was inserted and removed before the
|
|
1486
1463
|
// this context, so it will never exist for this
|
|
1487
1464
|
// context
|
|
1488
|
-
if(removalInfo !== undefined
|
|
1465
|
+
if (removalInfo !== undefined
|
|
1489
1466
|
&& removalInfo.removedSeq !== UnassignedSequenceNumber) {
|
|
1490
1467
|
return undefined;
|
|
1491
1468
|
}
|
|
@@ -1707,10 +1684,10 @@ export class MergeTree {
|
|
|
1707
1684
|
nodesToUpdate.push(pendingSegment.parent!);
|
|
1708
1685
|
}
|
|
1709
1686
|
deltaSegments.push({
|
|
1710
|
-
segment:pendingSegment,
|
|
1687
|
+
segment: pendingSegment,
|
|
1711
1688
|
});
|
|
1712
1689
|
});
|
|
1713
|
-
if(this.mergeTreeMaintenanceCallback) {
|
|
1690
|
+
if (this.mergeTreeMaintenanceCallback) {
|
|
1714
1691
|
this.mergeTreeMaintenanceCallback(
|
|
1715
1692
|
{
|
|
1716
1693
|
deltaSegments,
|
|
@@ -1874,7 +1851,7 @@ export class MergeTree {
|
|
|
1874
1851
|
}
|
|
1875
1852
|
const backLen = this.nodeLength(backSeg, this.collabWindow.currentSeq, clientId);
|
|
1876
1853
|
// ignore removed segments
|
|
1877
|
-
if(backLen === undefined) {
|
|
1854
|
+
if (backLen === undefined) {
|
|
1878
1855
|
return true;
|
|
1879
1856
|
}
|
|
1880
1857
|
// Find the nearest 0 length seg we can insert over, as all other inserts
|
|
@@ -1940,7 +1917,7 @@ export class MergeTree {
|
|
|
1940
1917
|
remoteClientPosition: number,
|
|
1941
1918
|
remoteClientRefSeq: number,
|
|
1942
1919
|
remoteClientId: number): number | undefined {
|
|
1943
|
-
if(remoteClientRefSeq < this.collabWindow.minSeq) {
|
|
1920
|
+
if (remoteClientRefSeq < this.collabWindow.minSeq) {
|
|
1944
1921
|
return undefined;
|
|
1945
1922
|
}
|
|
1946
1923
|
|
|
@@ -2001,9 +1978,9 @@ export class MergeTree {
|
|
|
2001
1978
|
const saveIfLocal = (locSegment: ISegment) => {
|
|
2002
1979
|
// Save segment so can assign sequence number when acked by server
|
|
2003
1980
|
if (this.collabWindow.collaborating) {
|
|
2004
|
-
if ((locSegment.seq === UnassignedSequenceNumber) &&
|
|
2005
|
-
(clientId === this.collabWindow.clientId)) {
|
|
1981
|
+
if ((locSegment.seq === UnassignedSequenceNumber) && (clientId === this.collabWindow.clientId)) {
|
|
2006
1982
|
segmentGroup = this.addToPendingList(locSegment, segmentGroup, localSeq);
|
|
1983
|
+
// eslint-disable-next-line @typescript-eslint/brace-style
|
|
2007
1984
|
}
|
|
2008
1985
|
// LocSegment.seq === 0 when coming from SharedSegmentSequence.loadBody()
|
|
2009
1986
|
// In all other cases this has to be true (checked by addToLRUSet):
|
|
@@ -2070,7 +2047,7 @@ export class MergeTree {
|
|
|
2070
2047
|
operation: MergeTreeMaintenanceType.SPLIT,
|
|
2071
2048
|
deltaSegments: [{ segment }, { segment: next }],
|
|
2072
2049
|
},
|
|
2073
|
-
|
|
2050
|
+
undefined);
|
|
2074
2051
|
}
|
|
2075
2052
|
|
|
2076
2053
|
return { next };
|
|
@@ -2178,7 +2155,7 @@ export class MergeTree {
|
|
|
2178
2155
|
for (childIndex = 0; childIndex < block.childCount; childIndex++) {
|
|
2179
2156
|
child = children[childIndex];
|
|
2180
2157
|
const len = this.nodeLength(child, refSeq, clientId);
|
|
2181
|
-
if(len === undefined) {
|
|
2158
|
+
if (len === undefined) {
|
|
2182
2159
|
// if the seg len in undefined, the segment
|
|
2183
2160
|
// will be removed, so should just be skipped for now
|
|
2184
2161
|
continue;
|
|
@@ -2467,6 +2444,34 @@ export class MergeTree {
|
|
|
2467
2444
|
}
|
|
2468
2445
|
}
|
|
2469
2446
|
|
|
2447
|
+
public removeLocalReferencePosition(lref: ReferencePosition): ReferencePosition | undefined {
|
|
2448
|
+
const segment = lref.getSegment();
|
|
2449
|
+
if (segment) {
|
|
2450
|
+
const removedRefs = segment?.localRefs?.removeLocalRef(lref);
|
|
2451
|
+
if (removedRefs !== undefined) {
|
|
2452
|
+
this.blockUpdatePathLengths(segment.parent, TreeMaintenanceSequenceNumber,
|
|
2453
|
+
LocalClientId);
|
|
2454
|
+
}
|
|
2455
|
+
return removedRefs;
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
public createLocalReferencePosition(
|
|
2459
|
+
segment: ISegment, offset: number, refType: ReferenceType, properties: PropertySet | undefined,
|
|
2460
|
+
client: Client,
|
|
2461
|
+
): ReferencePosition {
|
|
2462
|
+
const localRefs = segment.localRefs ?? new LocalReferenceCollection(segment);
|
|
2463
|
+
segment.localRefs = localRefs;
|
|
2464
|
+
|
|
2465
|
+
const segRef = localRefs.createLocalRef(offset, refType, properties, client);
|
|
2466
|
+
|
|
2467
|
+
this.blockUpdatePathLengths(segment.parent, TreeMaintenanceSequenceNumber,
|
|
2468
|
+
LocalClientId);
|
|
2469
|
+
return segRef;
|
|
2470
|
+
}
|
|
2471
|
+
|
|
2472
|
+
/**
|
|
2473
|
+
* @deprecated - use removeLocalReferencePosition
|
|
2474
|
+
*/
|
|
2470
2475
|
public removeLocalReference(segment: ISegment, lref: LocalReference) {
|
|
2471
2476
|
if (segment.localRefs) {
|
|
2472
2477
|
const removedRef = segment.localRefs.removeLocalRef(lref);
|
|
@@ -2477,6 +2482,9 @@ export class MergeTree {
|
|
|
2477
2482
|
}
|
|
2478
2483
|
}
|
|
2479
2484
|
|
|
2485
|
+
/**
|
|
2486
|
+
* @deprecated - use createLocalReference
|
|
2487
|
+
*/
|
|
2480
2488
|
public addLocalReference(lref: LocalReference) {
|
|
2481
2489
|
const segment = lref.segment!;
|
|
2482
2490
|
let localRefs = segment.localRefs;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Stack } from "./collections";
|
|
7
|
+
import { ISegment } from "./mergeTree";
|
|
8
|
+
import { ReferenceType, ICombiningOp } from "./ops";
|
|
9
|
+
import { PropertySet, MapLike } from "./properties";
|
|
10
|
+
|
|
11
|
+
export const reservedTileLabelsKey = "referenceTileLabels";
|
|
12
|
+
export const reservedRangeLabelsKey = "referenceRangeLabels";
|
|
13
|
+
|
|
14
|
+
export function refTypeIncludesFlag(refPos: ReferencePosition, flags: ReferenceType): boolean {
|
|
15
|
+
// eslint-disable-next-line no-bitwise
|
|
16
|
+
return (refPos.refType & flags) !== 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const refGetTileLabels = (refPos: ReferencePosition): string[] | undefined =>
|
|
20
|
+
refTypeIncludesFlag(refPos, ReferenceType.Tile)
|
|
21
|
+
&& refPos.properties ? refPos.properties[reservedTileLabelsKey] as string[] : undefined;
|
|
22
|
+
|
|
23
|
+
export const refGetRangeLabels = (refPos: ReferencePosition): string[] | undefined =>
|
|
24
|
+
// eslint-disable-next-line no-bitwise
|
|
25
|
+
(refTypeIncludesFlag(refPos, ReferenceType.NestBegin | ReferenceType.NestEnd))
|
|
26
|
+
&& refPos.properties ? refPos.properties[reservedRangeLabelsKey] as string[] : undefined;
|
|
27
|
+
|
|
28
|
+
export function refHasTileLabel(refPos: ReferencePosition, label: string): boolean {
|
|
29
|
+
const tileLabels = refGetTileLabels(refPos);
|
|
30
|
+
if (tileLabels) {
|
|
31
|
+
for (const refLabel of tileLabels) {
|
|
32
|
+
if (label === refLabel) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function refHasRangeLabel(refPos: ReferencePosition, label: string): boolean {
|
|
41
|
+
const rangeLabels = refGetRangeLabels(refPos);
|
|
42
|
+
if (rangeLabels) {
|
|
43
|
+
for (const refLabel of rangeLabels) {
|
|
44
|
+
if (label === refLabel) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
export function refHasTileLabels(refPos: ReferencePosition): boolean {
|
|
52
|
+
return refGetTileLabels(refPos) !== undefined;
|
|
53
|
+
}
|
|
54
|
+
export function refHasRangeLabels(refPos: ReferencePosition): boolean {
|
|
55
|
+
return refGetRangeLabels(refPos) !== undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface ReferencePosition {
|
|
59
|
+
properties?: PropertySet;
|
|
60
|
+
refType: ReferenceType;
|
|
61
|
+
|
|
62
|
+
getSegment(): ISegment | undefined;
|
|
63
|
+
getOffset(): number;
|
|
64
|
+
addProperties(newProps: PropertySet, op?: ICombiningOp): void;
|
|
65
|
+
isLeaf(): boolean;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @deprecated - use refHasTileLabels
|
|
69
|
+
*/
|
|
70
|
+
hasTileLabels(): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* @deprecated - use refHasRangeLabels
|
|
73
|
+
*/
|
|
74
|
+
hasRangeLabels(): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* @deprecated - use refHasTileLabel
|
|
77
|
+
*/
|
|
78
|
+
hasTileLabel(label: string): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* @deprecated - use refHasRangeLabel
|
|
81
|
+
*/
|
|
82
|
+
hasRangeLabel(label: string): boolean;
|
|
83
|
+
/**
|
|
84
|
+
* @deprecated - use refGetTileLabels
|
|
85
|
+
*/
|
|
86
|
+
getTileLabels(): string[] | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* @deprecated - use refGetRangeLabels
|
|
89
|
+
*/
|
|
90
|
+
getRangeLabels(): string[] | undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export type RangeStackMap = MapLike<Stack<ReferencePosition>>;
|
|
94
|
+
export const DetachedReferencePosition = -1;
|
|
95
|
+
|
|
96
|
+
export function minReferencePosition<T extends ReferencePosition>(a: T, b: T): T {
|
|
97
|
+
if (compareReferencePositions(a, b) < 0) {
|
|
98
|
+
return a;
|
|
99
|
+
} else {
|
|
100
|
+
return b;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function maxReferencePosition<T extends ReferencePosition>(a: T, b: T): T {
|
|
105
|
+
if (compareReferencePositions(a, b) > 0) {
|
|
106
|
+
return a;
|
|
107
|
+
} else {
|
|
108
|
+
return b;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function compareReferencePositions(a: ReferencePosition, b: ReferencePosition): number {
|
|
113
|
+
const aSeg = a.getSegment();
|
|
114
|
+
const bSeg = b.getSegment();
|
|
115
|
+
if (aSeg === bSeg) {
|
|
116
|
+
return a.getOffset() - b.getOffset();
|
|
117
|
+
} else {
|
|
118
|
+
if (aSeg === undefined
|
|
119
|
+
|| (bSeg !== undefined &&
|
|
120
|
+
aSeg.ordinal < bSeg.ordinal)) {
|
|
121
|
+
return -1;
|
|
122
|
+
} else {
|
|
123
|
+
return 1;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
package/src/snapshotChunks.ts
CHANGED
|
@@ -20,7 +20,7 @@ export type JsonSegmentSpecs = IJSONSegment | IJSONSegmentWithMergeInfo;
|
|
|
20
20
|
|
|
21
21
|
export interface MergeTreeChunkLegacy extends VersionedMergeTreeChunk {
|
|
22
22
|
version: undefined;
|
|
23
|
-
chunkStartSegmentIndex: number
|
|
23
|
+
chunkStartSegmentIndex: number;
|
|
24
24
|
chunkSegmentCount: number;
|
|
25
25
|
chunkLengthChars: number;
|
|
26
26
|
totalLengthChars?: number;
|
|
@@ -32,19 +32,19 @@ export interface MergeTreeChunkLegacy extends VersionedMergeTreeChunk {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export interface MergeTreeHeaderChunkMetadata {
|
|
35
|
-
id: string
|
|
35
|
+
id: string;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export interface MergeTreeHeaderMetadata {
|
|
39
|
-
totalLength: number
|
|
40
|
-
totalSegmentCount: number
|
|
41
|
-
orderedChunkMetadata: MergeTreeHeaderChunkMetadata[]
|
|
42
|
-
sequenceNumber: number
|
|
43
|
-
minSequenceNumber: number
|
|
39
|
+
totalLength: number;
|
|
40
|
+
totalSegmentCount: number;
|
|
41
|
+
orderedChunkMetadata: MergeTreeHeaderChunkMetadata[];
|
|
42
|
+
sequenceNumber: number;
|
|
43
|
+
minSequenceNumber: number;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
export interface MergeTreeChunkV1 extends VersionedMergeTreeChunk {
|
|
47
|
-
version: "1"
|
|
47
|
+
version: "1";
|
|
48
48
|
startIndex: number;
|
|
49
49
|
segmentCount: number;
|
|
50
50
|
length: number;
|
package/src/snapshotLoader.ts
CHANGED
|
@@ -38,18 +38,18 @@ export class SnapshotLoader {
|
|
|
38
38
|
|
|
39
39
|
public async initialize(
|
|
40
40
|
services: IChannelStorageService,
|
|
41
|
-
): Promise<{ catchupOpsP: Promise<ISequencedDocumentMessage[]
|
|
41
|
+
): Promise<{ catchupOpsP: Promise<ISequencedDocumentMessage[]>; }> {
|
|
42
42
|
const headerLoadedP =
|
|
43
43
|
services.readBlob(SnapshotLegacy.header).then((header) => {
|
|
44
44
|
assert(!!header, 0x05f /* "Missing blob header on legacy snapshot!" */);
|
|
45
|
-
return this.loadHeader(bufferToString(header,"utf8"));
|
|
45
|
+
return this.loadHeader(bufferToString(header, "utf8"));
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
const catchupOpsP =
|
|
49
49
|
this.loadBodyAndCatchupOps(headerLoadedP, services);
|
|
50
50
|
|
|
51
51
|
catchupOpsP.catch(
|
|
52
|
-
(err)=>this.logger.sendErrorEvent({ eventName: "CatchupOpsLoadFailure" },err));
|
|
52
|
+
(err) => this.logger.sendErrorEvent({ eventName: "CatchupOpsLoadFailure" }, err));
|
|
53
53
|
|
|
54
54
|
await headerLoadedP;
|
|
55
55
|
|
|
@@ -111,7 +111,7 @@ export class SnapshotLoader {
|
|
|
111
111
|
}
|
|
112
112
|
if (spec.removedClientIds !== undefined) {
|
|
113
113
|
seg.removedClientIds = spec.removedClientIds?.map(
|
|
114
|
-
(sid)=> this.client.getOrAddShortClientId(sid));
|
|
114
|
+
(sid) => this.client.getOrAddShortClientId(sid));
|
|
115
115
|
}
|
|
116
116
|
} else {
|
|
117
117
|
seg = this.client.specToSegment(spec);
|
package/src/snapshotV1.ts
CHANGED
|
@@ -225,7 +225,7 @@ export class SnapshotV1 {
|
|
|
225
225
|
? this.getLongClientId(segment.removedClientIds[0])
|
|
226
226
|
: undefined;
|
|
227
227
|
|
|
228
|
-
raw.removedClientIds = segment.removedClientIds?.map((id)=>this.getLongClientId(id));
|
|
228
|
+
raw.removedClientIds = segment.removedClientIds?.map((id) => this.getLongClientId(id));
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
// Sanity check that we are preserving either the seq < minSeq or a removed segment's info.
|
package/src/snapshotlegacy.ts
CHANGED
|
@@ -131,7 +131,7 @@ export class SnapshotLegacy {
|
|
|
131
131
|
segments === chunk1.totalSegmentCount,
|
|
132
132
|
0x05e /* "emit: mismatch in totalSegmentCount" */);
|
|
133
133
|
|
|
134
|
-
if(catchUpMsgs !== undefined && catchUpMsgs.length > 0) {
|
|
134
|
+
if (catchUpMsgs !== undefined && catchUpMsgs.length > 0) {
|
|
135
135
|
builder.addBlob(
|
|
136
136
|
this.mergeTree.options?.catchUpBlobName ?? SnapshotLegacy.catchupOps,
|
|
137
137
|
serializer ? serializer.stringify(catchUpMsgs, bind) : JSON.stringify(catchUpMsgs));
|
|
@@ -157,7 +157,7 @@ export class SnapshotLegacy {
|
|
|
157
157
|
if ((segment.seq !== UnassignedSequenceNumber) && (segment.seq! <= this.seq!) &&
|
|
158
158
|
((segment.removedSeq === undefined) || (segment.removedSeq === UnassignedSequenceNumber) ||
|
|
159
159
|
(segment.removedSeq > this.seq!))) {
|
|
160
|
-
if (prev
|
|
160
|
+
if (prev?.canAppend(segment)
|
|
161
161
|
&& matchProperties(prev.properties, segment.properties)
|
|
162
162
|
) {
|
|
163
163
|
prev = prev.clone();
|