@colyseus/schema 3.0.42 → 3.0.44
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/build/cjs/index.js +365 -219
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +365 -219
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +365 -219
- package/lib/Schema.d.ts +4 -3
- package/lib/Schema.js +22 -4
- package/lib/Schema.js.map +1 -1
- package/lib/bench_encode.d.ts +1 -0
- package/lib/bench_encode.js +130 -0
- package/lib/bench_encode.js.map +1 -0
- package/lib/debug.d.ts +1 -0
- package/lib/debug.js +51 -0
- package/lib/debug.js.map +1 -0
- package/lib/decoder/Decoder.js +8 -9
- package/lib/decoder/Decoder.js.map +1 -1
- package/lib/encoder/ChangeTree.d.ts +57 -7
- package/lib/encoder/ChangeTree.js +172 -106
- package/lib/encoder/ChangeTree.js.map +1 -1
- package/lib/encoder/Encoder.js +19 -20
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/encoder/Root.d.ts +9 -8
- package/lib/encoder/Root.js +84 -27
- package/lib/encoder/Root.js.map +1 -1
- package/lib/encoder/StateView.d.ts +1 -1
- package/lib/encoder/StateView.js +28 -23
- package/lib/encoder/StateView.js.map +1 -1
- package/lib/types/custom/ArraySchema.js +7 -5
- package/lib/types/custom/ArraySchema.js.map +1 -1
- package/lib/types/custom/CollectionSchema.js +1 -1
- package/lib/types/custom/CollectionSchema.js.map +1 -1
- package/lib/types/custom/MapSchema.js +9 -4
- package/lib/types/custom/MapSchema.js.map +1 -1
- package/lib/types/symbols.d.ts +14 -14
- package/lib/types/symbols.js +14 -14
- package/lib/types/symbols.js.map +1 -1
- package/lib/utils.js +7 -3
- package/lib/utils.js.map +1 -1
- package/package.json +2 -1
- package/src/Schema.ts +23 -7
- package/src/bench_encode.ts +108 -0
- package/src/debug.ts +55 -0
- package/src/decoder/Decoder.ts +9 -13
- package/src/encoder/ChangeTree.ts +203 -116
- package/src/encoder/Encoder.ts +21 -19
- package/src/encoder/Root.ts +90 -29
- package/src/encoder/StateView.ts +34 -26
- package/src/types/custom/ArraySchema.ts +8 -6
- package/src/types/custom/CollectionSchema.ts +1 -1
- package/src/types/custom/MapSchema.ts +10 -4
- package/src/types/symbols.ts +15 -15
- package/src/utils.ts +9 -3
|
@@ -36,17 +36,53 @@ export interface IndexedOperations {
|
|
|
36
36
|
[index: number]: OPERATION;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
// Linked list node for change trees
|
|
40
|
+
export interface ChangeTreeNode {
|
|
41
|
+
changeTree: ChangeTree;
|
|
42
|
+
next?: ChangeTreeNode;
|
|
43
|
+
prev?: ChangeTreeNode;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Linked list for change trees
|
|
47
|
+
export interface ChangeTreeList {
|
|
48
|
+
next?: ChangeTreeNode;
|
|
49
|
+
tail?: ChangeTreeNode;
|
|
50
|
+
length: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
39
53
|
export interface ChangeSet {
|
|
40
54
|
// field index -> operation index
|
|
41
55
|
indexes: { [index: number]: number };
|
|
42
56
|
operations: number[];
|
|
43
|
-
|
|
57
|
+
queueRootNode?: ChangeTreeNode; // direct reference to ChangeTreeNode in the linked list
|
|
44
58
|
}
|
|
45
59
|
|
|
46
60
|
function createChangeSet(): ChangeSet {
|
|
47
61
|
return { indexes: {}, operations: [] };
|
|
48
62
|
}
|
|
49
63
|
|
|
64
|
+
// Linked list helper functions
|
|
65
|
+
export function createChangeTreeList(): ChangeTreeList {
|
|
66
|
+
return { next: undefined, tail: undefined, length: 0 };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function addToChangeTreeList(list: ChangeTreeList, changeTree: ChangeTree): ChangeTreeNode {
|
|
70
|
+
const node: ChangeTreeNode = { changeTree, next: undefined, prev: undefined };
|
|
71
|
+
|
|
72
|
+
if (!list.next) {
|
|
73
|
+
list.next = node;
|
|
74
|
+
list.tail = node;
|
|
75
|
+
} else {
|
|
76
|
+
node.prev = list.tail;
|
|
77
|
+
list.tail!.next = node;
|
|
78
|
+
list.tail = node;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
list.length++;
|
|
82
|
+
|
|
83
|
+
return node;
|
|
84
|
+
}
|
|
85
|
+
|
|
50
86
|
export function setOperationAtIndex(changeSet: ChangeSet, index: number) {
|
|
51
87
|
const operationsIndex = changeSet.indexes[index];
|
|
52
88
|
if (operationsIndex === undefined) {
|
|
@@ -96,25 +132,32 @@ export function debugChangeSet(label: string, changeSet: ChangeSet) {
|
|
|
96
132
|
export function enqueueChangeTree(
|
|
97
133
|
root: Root,
|
|
98
134
|
changeTree: ChangeTree,
|
|
99
|
-
changeSet: 'changes' | 'filteredChanges' | 'allFilteredChanges',
|
|
100
|
-
|
|
135
|
+
changeSet: 'changes' | 'filteredChanges' | 'allFilteredChanges' | 'allChanges',
|
|
136
|
+
queueRootNode = changeTree[changeSet].queueRootNode
|
|
101
137
|
) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return;
|
|
138
|
+
// skip
|
|
139
|
+
if (!root) { return; }
|
|
105
140
|
|
|
106
|
-
|
|
107
|
-
|
|
141
|
+
if (queueRootNode) {
|
|
142
|
+
} else {
|
|
143
|
+
// Add to linked list if not already present
|
|
144
|
+
changeTree[changeSet].queueRootNode = addToChangeTreeList(root[changeSet], changeTree);
|
|
108
145
|
}
|
|
109
146
|
}
|
|
110
147
|
|
|
111
|
-
export
|
|
148
|
+
export interface ParentChain {
|
|
149
|
+
ref: Ref;
|
|
150
|
+
index: number;
|
|
151
|
+
next?: ParentChain;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export class ChangeTree<T extends Ref = any> {
|
|
112
155
|
ref: T;
|
|
113
156
|
refId: number;
|
|
157
|
+
metadata: Metadata;
|
|
114
158
|
|
|
115
159
|
root?: Root;
|
|
116
|
-
|
|
117
|
-
parentIndex?: number;
|
|
160
|
+
parentChain?: ParentChain; // Linked list for tracking parents
|
|
118
161
|
|
|
119
162
|
/**
|
|
120
163
|
* Whether this structure is parent of a filtered structure.
|
|
@@ -136,7 +179,7 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
136
179
|
filteredChanges: ChangeSet;
|
|
137
180
|
allFilteredChanges: ChangeSet;
|
|
138
181
|
|
|
139
|
-
indexes: {[index: string]: any}; // TODO: remove this, only used by MapSchema/SetSchema/CollectionSchema (`encodeKeyValueOperation`)
|
|
182
|
+
indexes: { [index: string]: any }; // TODO: remove this, only used by MapSchema/SetSchema/CollectionSchema (`encodeKeyValueOperation`)
|
|
140
183
|
|
|
141
184
|
/**
|
|
142
185
|
* Is this a new instance? Used on ArraySchema to determine OPERATION.MOVE_AND_ADD operation.
|
|
@@ -145,12 +188,12 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
145
188
|
|
|
146
189
|
constructor(ref: T) {
|
|
147
190
|
this.ref = ref;
|
|
191
|
+
this.metadata = ref.constructor[Symbol.metadata];
|
|
148
192
|
|
|
149
193
|
//
|
|
150
194
|
// Does this structure have "filters" declared?
|
|
151
195
|
//
|
|
152
|
-
|
|
153
|
-
if (metadata?.[$viewFieldIndexes]) {
|
|
196
|
+
if (this.metadata?.[$viewFieldIndexes]) {
|
|
154
197
|
this.allFilteredChanges = { indexes: {}, operations: [] };
|
|
155
198
|
this.filteredChanges = { indexes: {}, operations: [] };
|
|
156
199
|
}
|
|
@@ -158,35 +201,18 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
158
201
|
|
|
159
202
|
setRoot(root: Root) {
|
|
160
203
|
this.root = root;
|
|
161
|
-
this.checkIsFiltered(this.parent, this.parentIndex);
|
|
162
204
|
|
|
163
|
-
|
|
164
|
-
// TODO: refactor and possibly unify .setRoot() and .setParent()
|
|
165
|
-
//
|
|
205
|
+
const isNewChangeTree = this.root.add(this);
|
|
166
206
|
|
|
167
|
-
|
|
168
|
-
const metadata: Metadata = this.ref.constructor[Symbol.metadata];
|
|
169
|
-
if (metadata) {
|
|
170
|
-
metadata[$refTypeFieldIndexes]?.forEach((index) => {
|
|
171
|
-
const field = metadata[index as any as number];
|
|
172
|
-
const changeTree: ChangeTree = this.ref[field.name]?.[$changes];
|
|
173
|
-
if (changeTree) {
|
|
174
|
-
if (changeTree.root !== root) {
|
|
175
|
-
changeTree.setRoot(root);
|
|
176
|
-
} else {
|
|
177
|
-
root.add(changeTree); // increment refCount
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
});
|
|
207
|
+
this.checkIsFiltered(this.parent, this.parentIndex, isNewChangeTree);
|
|
181
208
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
changeTree.setRoot(root);
|
|
209
|
+
// Recursively set root on child structures
|
|
210
|
+
if (isNewChangeTree) {
|
|
211
|
+
this.forEachChild((child, _) => {
|
|
212
|
+
if (child.root !== root) {
|
|
213
|
+
child.setRoot(root);
|
|
188
214
|
} else {
|
|
189
|
-
root.add(
|
|
215
|
+
root.add(child); // increment refCount
|
|
190
216
|
}
|
|
191
217
|
});
|
|
192
218
|
}
|
|
@@ -197,63 +223,58 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
197
223
|
root?: Root,
|
|
198
224
|
parentIndex?: number,
|
|
199
225
|
) {
|
|
200
|
-
this.parent
|
|
201
|
-
this.parentIndex = parentIndex;
|
|
226
|
+
this.addParent(parent, parentIndex);
|
|
202
227
|
|
|
203
228
|
// avoid setting parents with empty `root`
|
|
204
229
|
if (!root) { return; }
|
|
205
230
|
|
|
231
|
+
const isNewChangeTree = root.add(this);
|
|
232
|
+
|
|
206
233
|
// skip if parent is already set
|
|
207
234
|
if (root !== this.root) {
|
|
208
235
|
this.root = root;
|
|
209
|
-
this.checkIsFiltered(parent, parentIndex);
|
|
210
|
-
|
|
211
|
-
} else {
|
|
212
|
-
root.add(this);
|
|
236
|
+
this.checkIsFiltered(parent, parentIndex, isNewChangeTree);
|
|
213
237
|
}
|
|
214
238
|
|
|
215
239
|
// assign same parent on child structures
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const changeTree: ChangeTree = value[$changes];
|
|
230
|
-
if (changeTree.root !== root) {
|
|
231
|
-
changeTree.setParent(this.ref, root, this.indexes[key] ?? key);
|
|
240
|
+
if (isNewChangeTree) {
|
|
241
|
+
//
|
|
242
|
+
// assign same parent on child structures
|
|
243
|
+
//
|
|
244
|
+
this.forEachChild((child, index) => {
|
|
245
|
+
if (child.root === root) {
|
|
246
|
+
//
|
|
247
|
+
// re-assigning a child of the same root, move it to the end
|
|
248
|
+
// of the changes queue so encoding order is preserved
|
|
249
|
+
//
|
|
250
|
+
root.add(child);
|
|
251
|
+
root.moveToEndOfChanges(child);
|
|
252
|
+
return;
|
|
232
253
|
}
|
|
254
|
+
child.setParent(this.ref, root, index);
|
|
233
255
|
});
|
|
234
256
|
}
|
|
235
|
-
|
|
236
257
|
}
|
|
237
258
|
|
|
238
|
-
forEachChild(callback: (change: ChangeTree,
|
|
259
|
+
forEachChild(callback: (change: ChangeTree, at: any) => void) {
|
|
239
260
|
//
|
|
240
261
|
// assign same parent on child structures
|
|
241
262
|
//
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
}
|
|
250
|
-
});
|
|
263
|
+
if (this.ref[$childType]) {
|
|
264
|
+
if (typeof (this.ref[$childType]) !== "string") {
|
|
265
|
+
// MapSchema / ArraySchema, etc.
|
|
266
|
+
for (const [key, value] of (this.ref as MapSchema).entries()) {
|
|
267
|
+
callback(value[$changes], key);
|
|
268
|
+
};
|
|
269
|
+
}
|
|
251
270
|
|
|
252
|
-
} else
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
271
|
+
} else {
|
|
272
|
+
for (const index of this.metadata?.[$refTypeFieldIndexes] ?? []) {
|
|
273
|
+
const field = this.metadata[index as any as number];
|
|
274
|
+
const value = this.ref[field.name];
|
|
275
|
+
if (!value) { continue; }
|
|
276
|
+
callback(value[$changes], index);
|
|
277
|
+
}
|
|
257
278
|
}
|
|
258
279
|
}
|
|
259
280
|
|
|
@@ -271,9 +292,7 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
271
292
|
}
|
|
272
293
|
|
|
273
294
|
change(index: number, operation: OPERATION = OPERATION.ADD) {
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
const isFiltered = this.isFiltered || (metadata?.[index]?.tag !== undefined);
|
|
295
|
+
const isFiltered = this.isFiltered || (this.metadata?.[index]?.tag !== undefined);
|
|
277
296
|
const changeSet = (isFiltered)
|
|
278
297
|
? this.filteredChanges
|
|
279
298
|
: this.changes;
|
|
@@ -376,19 +395,16 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
376
395
|
}
|
|
377
396
|
|
|
378
397
|
getType(index?: number) {
|
|
379
|
-
|
|
380
|
-
const metadata = this.ref.constructor[Symbol.metadata] as Metadata;
|
|
381
|
-
return metadata[index].type;
|
|
382
|
-
|
|
383
|
-
} else {
|
|
398
|
+
return (
|
|
384
399
|
//
|
|
385
400
|
// Get the child type from parent structure.
|
|
386
401
|
// - ["string"] => "string"
|
|
387
402
|
// - { map: "string" } => "string"
|
|
388
403
|
// - { set: "string" } => "string"
|
|
389
404
|
//
|
|
390
|
-
|
|
391
|
-
|
|
405
|
+
this.ref[$childType] || // ArraySchema | MapSchema | SetSchema | CollectionSchema
|
|
406
|
+
this.metadata[index].type // Schema
|
|
407
|
+
);
|
|
392
408
|
}
|
|
393
409
|
|
|
394
410
|
getChange(index: number) {
|
|
@@ -458,9 +474,7 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
458
474
|
this.indexedOperations = {};
|
|
459
475
|
|
|
460
476
|
// clear changeset
|
|
461
|
-
this[changeSetName]
|
|
462
|
-
this[changeSetName].operations.length = 0;
|
|
463
|
-
this[changeSetName].queueRootIndex = undefined;
|
|
477
|
+
this[changeSetName] = createChangeSet();
|
|
464
478
|
|
|
465
479
|
// ArraySchema and MapSchema have a custom "encode end" method
|
|
466
480
|
this.ref[$onEncodeEnd]?.();
|
|
@@ -478,24 +492,17 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
478
492
|
this.ref[$onEncodeEnd]?.();
|
|
479
493
|
|
|
480
494
|
this.indexedOperations = {};
|
|
481
|
-
|
|
482
|
-
this.changes.indexes = {};
|
|
483
|
-
this.changes.operations.length = 0;
|
|
484
|
-
this.changes.queueRootIndex = undefined;
|
|
495
|
+
this.changes = createChangeSet();
|
|
485
496
|
|
|
486
497
|
if (this.filteredChanges !== undefined) {
|
|
487
|
-
this.filteredChanges
|
|
488
|
-
this.filteredChanges.operations.length = 0;
|
|
489
|
-
this.filteredChanges.queueRootIndex = undefined;
|
|
498
|
+
this.filteredChanges = createChangeSet();
|
|
490
499
|
}
|
|
491
500
|
|
|
492
501
|
if (discardAll) {
|
|
493
|
-
this.allChanges
|
|
494
|
-
this.allChanges.operations.length = 0;
|
|
502
|
+
this.allChanges = createChangeSet();
|
|
495
503
|
|
|
496
504
|
if (this.allFilteredChanges !== undefined) {
|
|
497
|
-
this.allFilteredChanges
|
|
498
|
-
this.allFilteredChanges.operations.length = 0;
|
|
505
|
+
this.allFilteredChanges = createChangeSet();
|
|
499
506
|
}
|
|
500
507
|
}
|
|
501
508
|
}
|
|
@@ -517,22 +524,11 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
517
524
|
this.discard();
|
|
518
525
|
}
|
|
519
526
|
|
|
520
|
-
ensureRefId() {
|
|
521
|
-
// skip if refId is already set.
|
|
522
|
-
if (this.refId !== undefined) {
|
|
523
|
-
return;
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
this.refId = this.root.getNextUniqueId();
|
|
527
|
-
}
|
|
528
|
-
|
|
529
527
|
get changed() {
|
|
530
528
|
return (Object.entries(this.indexedOperations).length > 0);
|
|
531
529
|
}
|
|
532
530
|
|
|
533
|
-
protected checkIsFiltered(parent: Ref, parentIndex: number) {
|
|
534
|
-
const isNewChangeTree = this.root.add(this);
|
|
535
|
-
|
|
531
|
+
protected checkIsFiltered(parent: Ref, parentIndex: number, isNewChangeTree: boolean) {
|
|
536
532
|
if (this.root.types.hasFilters) {
|
|
537
533
|
//
|
|
538
534
|
// At Schema initialization, the "root" structure might not be available
|
|
@@ -545,7 +541,7 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
545
541
|
if (this.filteredChanges !== undefined) {
|
|
546
542
|
enqueueChangeTree(this.root, this, 'filteredChanges');
|
|
547
543
|
if (isNewChangeTree) {
|
|
548
|
-
this.root
|
|
544
|
+
enqueueChangeTree(this.root, this, 'allFilteredChanges');
|
|
549
545
|
}
|
|
550
546
|
}
|
|
551
547
|
}
|
|
@@ -553,7 +549,7 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
553
549
|
if (!this.isFiltered) {
|
|
554
550
|
enqueueChangeTree(this.root, this, 'changes');
|
|
555
551
|
if (isNewChangeTree) {
|
|
556
|
-
this.root
|
|
552
|
+
enqueueChangeTree(this.root, this, 'allChanges');
|
|
557
553
|
}
|
|
558
554
|
}
|
|
559
555
|
}
|
|
@@ -568,7 +564,7 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
568
564
|
//
|
|
569
565
|
const refType = Metadata.isValidInstance(this.ref)
|
|
570
566
|
? this.ref.constructor
|
|
571
|
-
:
|
|
567
|
+
: this.ref[$childType];
|
|
572
568
|
|
|
573
569
|
let parentChangeTree: ChangeTree;
|
|
574
570
|
|
|
@@ -627,4 +623,95 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
627
623
|
}
|
|
628
624
|
}
|
|
629
625
|
|
|
626
|
+
/**
|
|
627
|
+
* Get the immediate parent
|
|
628
|
+
*/
|
|
629
|
+
get parent(): Ref | undefined {
|
|
630
|
+
return this.parentChain?.ref;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Get the immediate parent index
|
|
635
|
+
*/
|
|
636
|
+
get parentIndex(): number | undefined {
|
|
637
|
+
return this.parentChain?.index;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Add a parent to the chain
|
|
642
|
+
*/
|
|
643
|
+
addParent(parent: Ref, index: number) {
|
|
644
|
+
// Check if this parent already exists in the chain
|
|
645
|
+
if (this.hasParent((p, i) => p[$changes] === parent[$changes] && i === index)) {
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
this.parentChain = {
|
|
650
|
+
ref: parent,
|
|
651
|
+
index,
|
|
652
|
+
next: this.parentChain
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* Remove a parent from the chain
|
|
658
|
+
* @param parent - The parent to remove
|
|
659
|
+
* @returns true if parent was removed
|
|
660
|
+
*/
|
|
661
|
+
removeParent(parent: Ref = this.parent): boolean {
|
|
662
|
+
let current = this.parentChain;
|
|
663
|
+
let previous = null;
|
|
664
|
+
while (current) {
|
|
665
|
+
//
|
|
666
|
+
// FIXME: it is required to check against `$changes` here because
|
|
667
|
+
// ArraySchema is instance of Proxy
|
|
668
|
+
//
|
|
669
|
+
if (current.ref[$changes] === parent[$changes]) {
|
|
670
|
+
if (previous) {
|
|
671
|
+
previous.next = current.next;
|
|
672
|
+
} else {
|
|
673
|
+
this.parentChain = current.next;
|
|
674
|
+
}
|
|
675
|
+
return true;
|
|
676
|
+
}
|
|
677
|
+
previous = current;
|
|
678
|
+
current = current.next;
|
|
679
|
+
}
|
|
680
|
+
return this.parentChain === undefined;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Find a specific parent in the chain
|
|
685
|
+
*/
|
|
686
|
+
findParent(predicate: (parent: Ref, index: number) => boolean): ParentChain | undefined {
|
|
687
|
+
let current = this.parentChain;
|
|
688
|
+
while (current) {
|
|
689
|
+
if (predicate(current.ref, current.index)) {
|
|
690
|
+
return current;
|
|
691
|
+
}
|
|
692
|
+
current = current.next;
|
|
693
|
+
}
|
|
694
|
+
return undefined;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Check if this ChangeTree has a specific parent
|
|
699
|
+
*/
|
|
700
|
+
hasParent(predicate: (parent: Ref, index: number) => boolean): boolean {
|
|
701
|
+
return this.findParent(predicate) !== undefined;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* Get all parents as an array (for debugging/testing)
|
|
706
|
+
*/
|
|
707
|
+
getAllParents(): Array<{ ref: Ref, index: number }> {
|
|
708
|
+
const parents: Array<{ ref: Ref, index: number }> = [];
|
|
709
|
+
let current = this.parentChain;
|
|
710
|
+
while (current) {
|
|
711
|
+
parents.push({ ref: current.ref, index: current.index });
|
|
712
|
+
current = current.next;
|
|
713
|
+
}
|
|
714
|
+
return parents;
|
|
715
|
+
}
|
|
716
|
+
|
|
630
717
|
}
|
package/src/encoder/Encoder.ts
CHANGED
|
@@ -10,7 +10,8 @@ import { Root } from "./Root";
|
|
|
10
10
|
|
|
11
11
|
import type { StateView } from "./StateView";
|
|
12
12
|
import type { Metadata } from "../Metadata";
|
|
13
|
-
import type { ChangeSetName, ChangeTree } from "./ChangeTree";
|
|
13
|
+
import type { ChangeSetName, ChangeTree, ChangeTreeList, ChangeTreeNode } from "./ChangeTree";
|
|
14
|
+
import { createChangeTreeList } from "./ChangeTree";
|
|
14
15
|
|
|
15
16
|
export class Encoder<T extends Schema = any> {
|
|
16
17
|
static BUFFER_SIZE = (typeof(Buffer) !== "undefined") && Buffer.poolSize || 8 * 1024; // 8KB
|
|
@@ -54,11 +55,11 @@ export class Encoder<T extends Schema = any> {
|
|
|
54
55
|
): Buffer {
|
|
55
56
|
const hasView = (view !== undefined);
|
|
56
57
|
const rootChangeTree = this.state[$changes];
|
|
57
|
-
const changeTrees = this.root[changeSetName];
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
let current: ChangeTreeList | ChangeTreeNode = this.root[changeSetName];
|
|
60
|
+
|
|
61
|
+
while (current = current.next) {
|
|
62
|
+
const changeTree = current.changeTree;
|
|
62
63
|
|
|
63
64
|
if (hasView) {
|
|
64
65
|
if (!view.isChangeTreeVisible(changeTree)) {
|
|
@@ -166,7 +167,9 @@ export class Encoder<T extends Schema = any> {
|
|
|
166
167
|
? this.root[field]
|
|
167
168
|
: field;
|
|
168
169
|
|
|
169
|
-
rootChangeSet.
|
|
170
|
+
let current = rootChangeSet.next;
|
|
171
|
+
while (current) {
|
|
172
|
+
const changeTree = current.changeTree;
|
|
170
173
|
const changeSet = changeTree[field];
|
|
171
174
|
|
|
172
175
|
const metadata: Metadata = changeTree.ref.constructor[Symbol.metadata];
|
|
@@ -179,7 +182,8 @@ export class Encoder<T extends Schema = any> {
|
|
|
179
182
|
op: OPERATION[op],
|
|
180
183
|
});
|
|
181
184
|
}
|
|
182
|
-
|
|
185
|
+
current = current.next;
|
|
186
|
+
}
|
|
183
187
|
}
|
|
184
188
|
|
|
185
189
|
encodeView(view: StateView, sharedOffset: number, it: Iterator, bytes = this.sharedBuffer) {
|
|
@@ -242,22 +246,20 @@ export class Encoder<T extends Schema = any> {
|
|
|
242
246
|
|
|
243
247
|
discardChanges() {
|
|
244
248
|
// discard shared changes
|
|
245
|
-
let
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
}
|
|
250
|
-
this.root.changes.length = 0;
|
|
249
|
+
let current = this.root.changes.next;
|
|
250
|
+
while (current) {
|
|
251
|
+
current.changeTree.endEncode('changes');
|
|
252
|
+
current = current.next;
|
|
251
253
|
}
|
|
254
|
+
this.root.changes = createChangeTreeList();
|
|
252
255
|
|
|
253
256
|
// discard filtered changes
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
this.root.filteredChanges.length = 0;
|
|
257
|
+
current = this.root.filteredChanges.next;
|
|
258
|
+
while (current) {
|
|
259
|
+
current.changeTree.endEncode('filteredChanges');
|
|
260
|
+
current = current.next;
|
|
260
261
|
}
|
|
262
|
+
this.root.filteredChanges = createChangeTreeList();
|
|
261
263
|
}
|
|
262
264
|
|
|
263
265
|
tryEncodeTypeId (bytes: Buffer, baseType: typeof Schema, targetType: typeof Schema, it: Iterator) {
|