@colyseus/schema 3.0.57 → 3.0.59
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 +78 -31
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +78 -31
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +78 -31
- package/lib/decoder/Decoder.js +1 -3
- package/lib/decoder/Decoder.js.map +1 -1
- package/lib/encoder/ChangeTree.d.ts +1 -1
- package/lib/encoder/ChangeTree.js +4 -4
- package/lib/encoder/ChangeTree.js.map +1 -1
- package/lib/encoder/Encoder.js +2 -2
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/encoder/Root.d.ts +5 -2
- package/lib/encoder/Root.js +72 -23
- package/lib/encoder/Root.js.map +1 -1
- package/package.json +1 -1
- package/src/decoder/Decoder.ts +1 -2
- package/src/encoder/ChangeTree.ts +5 -5
- package/src/encoder/Encoder.ts +3 -3
- package/src/encoder/Root.ts +81 -24
package/lib/encoder/Root.d.ts
CHANGED
|
@@ -17,9 +17,12 @@ export declare class Root {
|
|
|
17
17
|
getNextUniqueId(): number;
|
|
18
18
|
add(changeTree: ChangeTree): boolean;
|
|
19
19
|
remove(changeTree: ChangeTree): number;
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
recursivelyMoveNextToParent(changeTree: ChangeTree): void;
|
|
21
|
+
moveNextToParent(changeTree: ChangeTree): void;
|
|
22
|
+
moveNextToParentInChangeTreeList(changeSetName: ChangeSetName, changeTree: ChangeTree): void;
|
|
22
23
|
enqueueChangeTree(changeTree: ChangeTree, changeSet: 'changes' | 'filteredChanges' | 'allFilteredChanges' | 'allChanges', queueRootNode?: ChangeTreeNode): void;
|
|
23
24
|
protected addToChangeTreeList(list: ChangeTreeList, changeTree: ChangeTree): ChangeTreeNode;
|
|
25
|
+
protected updatePositionsAfterRemoval(list: ChangeTreeList, removedPosition: number): void;
|
|
26
|
+
protected updatePositionsAfterMove(list: ChangeTreeList, node: ChangeTreeNode, newPosition: number): void;
|
|
24
27
|
removeChangeFromChangeSet(changeSetName: ChangeSetName, changeTree: ChangeTree): boolean;
|
|
25
28
|
}
|
package/lib/encoder/Root.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Root = void 0;
|
|
4
4
|
const spec_1 = require("../encoding/spec");
|
|
5
5
|
const ChangeTree_1 = require("./ChangeTree");
|
|
6
|
+
const symbols_1 = require("../types/symbols");
|
|
6
7
|
class Root {
|
|
7
8
|
constructor(types) {
|
|
8
9
|
this.types = types;
|
|
@@ -69,8 +70,8 @@ class Root {
|
|
|
69
70
|
this.remove(child);
|
|
70
71
|
}
|
|
71
72
|
else if (child.parentChain) {
|
|
72
|
-
// re-assigning a child of the same root, move it to
|
|
73
|
-
this.
|
|
73
|
+
// re-assigning a child of the same root, move it next to parent
|
|
74
|
+
this.moveNextToParent(child);
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
});
|
|
@@ -79,34 +80,52 @@ class Root {
|
|
|
79
80
|
this.refCount[changeTree.refId] = refCount;
|
|
80
81
|
//
|
|
81
82
|
// When losing a reference to an instance, it is best to move the
|
|
82
|
-
// ChangeTree to
|
|
83
|
+
// ChangeTree next to its parent in the encoding queue.
|
|
83
84
|
//
|
|
84
85
|
// This way, at decoding time, the instance that contains the
|
|
85
86
|
// ChangeTree will be available before the ChangeTree itself. If the
|
|
86
87
|
// containing instance is not available, the Decoder will throw
|
|
87
88
|
// "refId not found" error.
|
|
88
89
|
//
|
|
89
|
-
this.
|
|
90
|
-
changeTree.forEachChild((child, _) => this.moveToEndOfChanges(child));
|
|
90
|
+
this.recursivelyMoveNextToParent(changeTree);
|
|
91
91
|
}
|
|
92
92
|
return refCount;
|
|
93
93
|
}
|
|
94
|
-
|
|
94
|
+
recursivelyMoveNextToParent(changeTree) {
|
|
95
|
+
this.moveNextToParent(changeTree);
|
|
96
|
+
changeTree.forEachChild((child, _) => this.recursivelyMoveNextToParent(child));
|
|
97
|
+
}
|
|
98
|
+
moveNextToParent(changeTree) {
|
|
95
99
|
if (changeTree.filteredChanges) {
|
|
96
|
-
this.
|
|
97
|
-
this.
|
|
100
|
+
this.moveNextToParentInChangeTreeList("filteredChanges", changeTree);
|
|
101
|
+
this.moveNextToParentInChangeTreeList("allFilteredChanges", changeTree);
|
|
98
102
|
}
|
|
99
103
|
else {
|
|
100
|
-
this.
|
|
101
|
-
this.
|
|
104
|
+
this.moveNextToParentInChangeTreeList("changes", changeTree);
|
|
105
|
+
this.moveNextToParentInChangeTreeList("allChanges", changeTree);
|
|
102
106
|
}
|
|
103
107
|
}
|
|
104
|
-
|
|
108
|
+
moveNextToParentInChangeTreeList(changeSetName, changeTree) {
|
|
105
109
|
const changeSet = this[changeSetName];
|
|
106
110
|
const node = changeTree[changeSetName].queueRootNode;
|
|
107
|
-
if (!node
|
|
111
|
+
if (!node)
|
|
112
|
+
return;
|
|
113
|
+
// Find the parent in the linked list
|
|
114
|
+
const parent = changeTree.parent;
|
|
115
|
+
if (!parent || !parent[symbols_1.$changes])
|
|
116
|
+
return;
|
|
117
|
+
const parentNode = parent[symbols_1.$changes][changeSetName]?.queueRootNode;
|
|
118
|
+
if (!parentNode || parentNode === node)
|
|
108
119
|
return;
|
|
109
|
-
//
|
|
120
|
+
// Use cached positions - no iteration needed!
|
|
121
|
+
const parentPosition = parentNode.position;
|
|
122
|
+
const childPosition = node.position;
|
|
123
|
+
// If child is already after parent, no need to move
|
|
124
|
+
if (childPosition > parentPosition)
|
|
125
|
+
return;
|
|
126
|
+
// Child is before parent, so we need to move it after parent
|
|
127
|
+
// This maintains decoding order (parent before child)
|
|
128
|
+
// Remove node from current position
|
|
110
129
|
if (node.prev) {
|
|
111
130
|
node.prev.next = node.next;
|
|
112
131
|
}
|
|
@@ -119,16 +138,18 @@ class Root {
|
|
|
119
138
|
else {
|
|
120
139
|
changeSet.tail = node.prev;
|
|
121
140
|
}
|
|
122
|
-
//
|
|
123
|
-
node.prev =
|
|
124
|
-
node.next =
|
|
125
|
-
if (
|
|
126
|
-
|
|
141
|
+
// Insert node right after parent
|
|
142
|
+
node.prev = parentNode;
|
|
143
|
+
node.next = parentNode.next;
|
|
144
|
+
if (parentNode.next) {
|
|
145
|
+
parentNode.next.prev = node;
|
|
127
146
|
}
|
|
128
147
|
else {
|
|
129
|
-
changeSet.
|
|
148
|
+
changeSet.tail = node;
|
|
130
149
|
}
|
|
131
|
-
|
|
150
|
+
parentNode.next = node;
|
|
151
|
+
// Update positions after the move
|
|
152
|
+
this.updatePositionsAfterMove(changeSet, node, parentPosition + 1);
|
|
132
153
|
}
|
|
133
154
|
enqueueChangeTree(changeTree, changeSet, queueRootNode = changeTree[changeSet].queueRootNode) {
|
|
134
155
|
// skip
|
|
@@ -139,7 +160,12 @@ class Root {
|
|
|
139
160
|
changeTree[changeSet].queueRootNode = this.addToChangeTreeList(this[changeSet], changeTree);
|
|
140
161
|
}
|
|
141
162
|
addToChangeTreeList(list, changeTree) {
|
|
142
|
-
const node = {
|
|
163
|
+
const node = {
|
|
164
|
+
changeTree,
|
|
165
|
+
next: undefined,
|
|
166
|
+
prev: undefined,
|
|
167
|
+
position: list.tail ? list.tail.position + 1 : 0
|
|
168
|
+
};
|
|
143
169
|
if (!list.next) {
|
|
144
170
|
list.next = node;
|
|
145
171
|
list.tail = node;
|
|
@@ -149,13 +175,35 @@ class Root {
|
|
|
149
175
|
list.tail.next = node;
|
|
150
176
|
list.tail = node;
|
|
151
177
|
}
|
|
152
|
-
list.length++;
|
|
153
178
|
return node;
|
|
154
179
|
}
|
|
180
|
+
updatePositionsAfterRemoval(list, removedPosition) {
|
|
181
|
+
// Update positions for all nodes after the removed position
|
|
182
|
+
let current = list.next;
|
|
183
|
+
let position = 0;
|
|
184
|
+
while (current) {
|
|
185
|
+
if (position >= removedPosition) {
|
|
186
|
+
current.position = position;
|
|
187
|
+
}
|
|
188
|
+
current = current.next;
|
|
189
|
+
position++;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
updatePositionsAfterMove(list, node, newPosition) {
|
|
193
|
+
// Recalculate all positions - this is more reliable than trying to be clever
|
|
194
|
+
let current = list.next;
|
|
195
|
+
let position = 0;
|
|
196
|
+
while (current) {
|
|
197
|
+
current.position = position;
|
|
198
|
+
current = current.next;
|
|
199
|
+
position++;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
155
202
|
removeChangeFromChangeSet(changeSetName, changeTree) {
|
|
156
203
|
const changeSet = this[changeSetName];
|
|
157
204
|
const node = changeTree[changeSetName].queueRootNode;
|
|
158
205
|
if (node && node.changeTree === changeTree) {
|
|
206
|
+
const removedPosition = node.position;
|
|
159
207
|
// Remove the node from the linked list
|
|
160
208
|
if (node.prev) {
|
|
161
209
|
node.prev.next = node.next;
|
|
@@ -169,7 +217,8 @@ class Root {
|
|
|
169
217
|
else {
|
|
170
218
|
changeSet.tail = node.prev;
|
|
171
219
|
}
|
|
172
|
-
|
|
220
|
+
// Update positions for nodes that came after the removed node
|
|
221
|
+
this.updatePositionsAfterRemoval(changeSet, removedPosition);
|
|
173
222
|
// Clear ChangeTree reference
|
|
174
223
|
changeTree[changeSetName].queueRootNode = undefined;
|
|
175
224
|
return true;
|
package/lib/encoder/Root.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Root.js","sourceRoot":"","sources":["../../src/encoder/Root.ts"],"names":[],"mappings":"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","sourcesContent":["import { OPERATION } from \"../encoding/spec\";\nimport { TypeContext } from \"../types/TypeContext\";\nimport { ChangeTree, setOperationAtIndex, ChangeTreeList, createChangeTreeList, ChangeSetName, type ChangeTreeNode } from \"./ChangeTree\";\n\nexport class Root {\n protected nextUniqueId: number = 0;\n\n refCount: {[id: number]: number} = {};\n changeTrees: {[refId: number]: ChangeTree} = {};\n\n // all changes\n allChanges: ChangeTreeList = createChangeTreeList();\n allFilteredChanges: ChangeTreeList = createChangeTreeList();// TODO: do not initialize it if filters are not used\n\n // pending changes to be encoded\n changes: ChangeTreeList = createChangeTreeList();\n filteredChanges: ChangeTreeList = createChangeTreeList();// TODO: do not initialize it if filters are not used\n\n constructor(public types: TypeContext) { }\n\n getNextUniqueId() {\n return this.nextUniqueId++;\n }\n\n add(changeTree: ChangeTree) {\n // Assign unique `refId` to changeTree if it doesn't have one yet.\n if (changeTree.refId === undefined) {\n changeTree.refId = this.getNextUniqueId();\n }\n\n const isNewChangeTree = (this.changeTrees[changeTree.refId] === undefined);\n if (isNewChangeTree) { this.changeTrees[changeTree.refId] = changeTree; }\n\n const previousRefCount = this.refCount[changeTree.refId];\n if (previousRefCount === 0) {\n //\n // When a ChangeTree is re-added, it means that it was previously removed.\n // We need to re-add all changes to the `changes` map.\n //\n const ops = changeTree.allChanges.operations;\n let len = ops.length;\n while (len--) {\n changeTree.indexedOperations[ops[len]] = OPERATION.ADD;\n setOperationAtIndex(changeTree.changes, len);\n }\n }\n\n this.refCount[changeTree.refId] = (previousRefCount || 0) + 1;\n\n // console.log(\"ADD\", { refId: changeTree.refId, ref: changeTree.ref.constructor.name, refCount: this.refCount[changeTree.refId], isNewChangeTree });\n\n return isNewChangeTree;\n }\n\n remove(changeTree: ChangeTree) {\n const refCount = (this.refCount[changeTree.refId]) - 1;\n\n // console.log(\"REMOVE\", { refId: changeTree.refId, ref: changeTree.ref.constructor.name, refCount, needRemove: refCount <= 0 });\n\n if (refCount <= 0) {\n //\n // Only remove \"root\" reference if it's the last reference\n //\n changeTree.root = undefined;\n delete this.changeTrees[changeTree.refId];\n\n this.removeChangeFromChangeSet(\"allChanges\", changeTree);\n this.removeChangeFromChangeSet(\"changes\", changeTree);\n\n if (changeTree.filteredChanges) {\n this.removeChangeFromChangeSet(\"allFilteredChanges\", changeTree);\n this.removeChangeFromChangeSet(\"filteredChanges\", changeTree);\n }\n\n this.refCount[changeTree.refId] = 0;\n\n changeTree.forEachChild((child, _) => {\n if (child.removeParent(changeTree.ref)) {\n if ((\n child.parentChain === undefined || // no parent, remove it\n (child.parentChain && this.refCount[child.refId] > 0) // parent is still in use, but has more than one reference, remove it\n )) {\n this.remove(child);\n\n } else if (child.parentChain) {\n // re-assigning a child of the same root, move it to the end\n this.moveToEndOfChanges(child);\n }\n }\n });\n\n } else {\n this.refCount[changeTree.refId] = refCount;\n\n //\n // When losing a reference to an instance, it is best to move the\n // ChangeTree to the end of the encoding queue.\n //\n // This way, at decoding time, the instance that contains the\n // ChangeTree will be available before the ChangeTree itself. If the\n // containing instance is not available, the Decoder will throw\n // \"refId not found\" error.\n //\n this.moveToEndOfChanges(changeTree);\n changeTree.forEachChild((child, _) => this.moveToEndOfChanges(child));\n }\n\n return refCount;\n }\n\n moveToEndOfChanges(changeTree: ChangeTree) {\n if (changeTree.filteredChanges) {\n this.moveToEndOfChangeTreeList(\"filteredChanges\", changeTree);\n this.moveToEndOfChangeTreeList(\"allFilteredChanges\", changeTree);\n } else {\n this.moveToEndOfChangeTreeList(\"changes\", changeTree);\n this.moveToEndOfChangeTreeList(\"allChanges\", changeTree);\n }\n }\n\n moveToEndOfChangeTreeList(changeSetName: ChangeSetName, changeTree: ChangeTree): void {\n const changeSet = this[changeSetName];\n const node = changeTree[changeSetName].queueRootNode;\n if (!node || node === changeSet.tail) return;\n\n // Remove from current position\n if (node.prev) {\n node.prev.next = node.next;\n } else {\n changeSet.next = node.next;\n }\n\n if (node.next) {\n node.next.prev = node.prev;\n } else {\n changeSet.tail = node.prev;\n }\n\n // Add to end\n node.prev = changeSet.tail;\n node.next = undefined;\n\n if (changeSet.tail) {\n changeSet.tail.next = node;\n } else {\n changeSet.next = node;\n }\n\n changeSet.tail = node;\n }\n\n public enqueueChangeTree(\n changeTree: ChangeTree,\n changeSet: 'changes' | 'filteredChanges' | 'allFilteredChanges' | 'allChanges',\n queueRootNode = changeTree[changeSet].queueRootNode\n ) {\n // skip\n if (queueRootNode) { return; }\n\n // Add to linked list if not already present\n changeTree[changeSet].queueRootNode = this.addToChangeTreeList(this[changeSet], changeTree);\n }\n\n protected addToChangeTreeList(list: ChangeTreeList, changeTree: ChangeTree): ChangeTreeNode {\n const node: ChangeTreeNode = { changeTree, next: undefined, prev: undefined };\n\n if (!list.next) {\n list.next = node;\n list.tail = node;\n } else {\n node.prev = list.tail;\n list.tail!.next = node;\n list.tail = node;\n }\n\n list.length++;\n\n return node;\n }\n\n public removeChangeFromChangeSet(changeSetName: ChangeSetName, changeTree: ChangeTree) {\n const changeSet = this[changeSetName];\n const node = changeTree[changeSetName].queueRootNode;\n\n if (node && node.changeTree === changeTree) {\n // Remove the node from the linked list\n if (node.prev) {\n node.prev.next = node.next;\n } else {\n changeSet.next = node.next;\n }\n\n if (node.next) {\n node.next.prev = node.prev;\n } else {\n changeSet.tail = node.prev;\n }\n\n changeSet.length--;\n\n // Clear ChangeTree reference\n changeTree[changeSetName].queueRootNode = undefined;\n return true;\n }\n\n return false;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"Root.js","sourceRoot":"","sources":["../../src/encoder/Root.ts"],"names":[],"mappings":"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","sourcesContent":["import { OPERATION } from \"../encoding/spec\";\nimport { TypeContext } from \"../types/TypeContext\";\nimport { ChangeTree, setOperationAtIndex, ChangeTreeList, createChangeTreeList, ChangeSetName, type ChangeTreeNode } from \"./ChangeTree\";\nimport { $changes } from \"../types/symbols\";\n\nexport class Root {\n protected nextUniqueId: number = 0;\n\n refCount: {[id: number]: number} = {};\n changeTrees: {[refId: number]: ChangeTree} = {};\n\n // all changes\n allChanges: ChangeTreeList = createChangeTreeList();\n allFilteredChanges: ChangeTreeList = createChangeTreeList();// TODO: do not initialize it if filters are not used\n\n // pending changes to be encoded\n changes: ChangeTreeList = createChangeTreeList();\n filteredChanges: ChangeTreeList = createChangeTreeList();// TODO: do not initialize it if filters are not used\n\n constructor(public types: TypeContext) { }\n\n getNextUniqueId() {\n return this.nextUniqueId++;\n }\n\n add(changeTree: ChangeTree) {\n // Assign unique `refId` to changeTree if it doesn't have one yet.\n if (changeTree.refId === undefined) {\n changeTree.refId = this.getNextUniqueId();\n }\n\n const isNewChangeTree = (this.changeTrees[changeTree.refId] === undefined);\n if (isNewChangeTree) { this.changeTrees[changeTree.refId] = changeTree; }\n\n const previousRefCount = this.refCount[changeTree.refId];\n if (previousRefCount === 0) {\n //\n // When a ChangeTree is re-added, it means that it was previously removed.\n // We need to re-add all changes to the `changes` map.\n //\n const ops = changeTree.allChanges.operations;\n let len = ops.length;\n while (len--) {\n changeTree.indexedOperations[ops[len]] = OPERATION.ADD;\n setOperationAtIndex(changeTree.changes, len);\n }\n }\n\n this.refCount[changeTree.refId] = (previousRefCount || 0) + 1;\n\n // console.log(\"ADD\", { refId: changeTree.refId, ref: changeTree.ref.constructor.name, refCount: this.refCount[changeTree.refId], isNewChangeTree });\n\n return isNewChangeTree;\n }\n\n remove(changeTree: ChangeTree) {\n const refCount = (this.refCount[changeTree.refId]) - 1;\n\n // console.log(\"REMOVE\", { refId: changeTree.refId, ref: changeTree.ref.constructor.name, refCount, needRemove: refCount <= 0 });\n\n if (refCount <= 0) {\n //\n // Only remove \"root\" reference if it's the last reference\n //\n changeTree.root = undefined;\n delete this.changeTrees[changeTree.refId];\n\n this.removeChangeFromChangeSet(\"allChanges\", changeTree);\n this.removeChangeFromChangeSet(\"changes\", changeTree);\n\n if (changeTree.filteredChanges) {\n this.removeChangeFromChangeSet(\"allFilteredChanges\", changeTree);\n this.removeChangeFromChangeSet(\"filteredChanges\", changeTree);\n }\n\n this.refCount[changeTree.refId] = 0;\n\n changeTree.forEachChild((child, _) => {\n if (child.removeParent(changeTree.ref)) {\n if ((\n child.parentChain === undefined || // no parent, remove it\n (child.parentChain && this.refCount[child.refId] > 0) // parent is still in use, but has more than one reference, remove it\n )) {\n this.remove(child);\n\n } else if (child.parentChain) {\n // re-assigning a child of the same root, move it next to parent\n this.moveNextToParent(child);\n }\n }\n });\n\n } else {\n this.refCount[changeTree.refId] = refCount;\n\n //\n // When losing a reference to an instance, it is best to move the\n // ChangeTree next to its parent in the encoding queue.\n //\n // This way, at decoding time, the instance that contains the\n // ChangeTree will be available before the ChangeTree itself. If the\n // containing instance is not available, the Decoder will throw\n // \"refId not found\" error.\n //\n this.recursivelyMoveNextToParent(changeTree);\n }\n\n return refCount;\n }\n\n recursivelyMoveNextToParent(changeTree: ChangeTree) {\n this.moveNextToParent(changeTree);\n changeTree.forEachChild((child, _) => this.recursivelyMoveNextToParent(child));\n }\n\n moveNextToParent(changeTree: ChangeTree) {\n if (changeTree.filteredChanges) {\n this.moveNextToParentInChangeTreeList(\"filteredChanges\", changeTree);\n this.moveNextToParentInChangeTreeList(\"allFilteredChanges\", changeTree);\n } else {\n this.moveNextToParentInChangeTreeList(\"changes\", changeTree);\n this.moveNextToParentInChangeTreeList(\"allChanges\", changeTree);\n }\n }\n\n moveNextToParentInChangeTreeList(changeSetName: ChangeSetName, changeTree: ChangeTree): void {\n const changeSet = this[changeSetName];\n const node = changeTree[changeSetName].queueRootNode;\n if (!node) return;\n\n // Find the parent in the linked list\n const parent = changeTree.parent;\n if (!parent || !parent[$changes]) return;\n\n const parentNode = parent[$changes][changeSetName]?.queueRootNode;\n if (!parentNode || parentNode === node) return;\n\n // Use cached positions - no iteration needed!\n const parentPosition = parentNode.position;\n const childPosition = node.position;\n\n // If child is already after parent, no need to move\n if (childPosition > parentPosition) return;\n\n // Child is before parent, so we need to move it after parent\n // This maintains decoding order (parent before child)\n\n // Remove node from current position\n if (node.prev) {\n node.prev.next = node.next;\n } else {\n changeSet.next = node.next;\n }\n\n if (node.next) {\n node.next.prev = node.prev;\n } else {\n changeSet.tail = node.prev;\n }\n\n // Insert node right after parent\n node.prev = parentNode;\n node.next = parentNode.next;\n\n if (parentNode.next) {\n parentNode.next.prev = node;\n } else {\n changeSet.tail = node;\n }\n\n parentNode.next = node;\n\n // Update positions after the move\n this.updatePositionsAfterMove(changeSet, node, parentPosition + 1);\n }\n\n public enqueueChangeTree(\n changeTree: ChangeTree,\n changeSet: 'changes' | 'filteredChanges' | 'allFilteredChanges' | 'allChanges',\n queueRootNode = changeTree[changeSet].queueRootNode\n ) {\n // skip\n if (queueRootNode) { return; }\n\n // Add to linked list if not already present\n changeTree[changeSet].queueRootNode = this.addToChangeTreeList(this[changeSet], changeTree);\n }\n\n protected addToChangeTreeList(list: ChangeTreeList, changeTree: ChangeTree): ChangeTreeNode {\n const node: ChangeTreeNode = {\n changeTree,\n next: undefined,\n prev: undefined,\n position: list.tail ? list.tail.position + 1 : 0\n };\n\n if (!list.next) {\n list.next = node;\n list.tail = node;\n } else {\n node.prev = list.tail;\n list.tail!.next = node;\n list.tail = node;\n }\n\n return node;\n }\n\n protected updatePositionsAfterRemoval(list: ChangeTreeList, removedPosition: number) {\n // Update positions for all nodes after the removed position\n let current = list.next;\n let position = 0;\n\n while (current) {\n if (position >= removedPosition) {\n current.position = position;\n }\n current = current.next;\n position++;\n }\n }\n\n protected updatePositionsAfterMove(list: ChangeTreeList, node: ChangeTreeNode, newPosition: number) {\n // Recalculate all positions - this is more reliable than trying to be clever\n let current = list.next;\n let position = 0;\n\n while (current) {\n current.position = position;\n current = current.next;\n position++;\n }\n }\n\n public removeChangeFromChangeSet(changeSetName: ChangeSetName, changeTree: ChangeTree) {\n const changeSet = this[changeSetName];\n const node = changeTree[changeSetName].queueRootNode;\n\n if (node && node.changeTree === changeTree) {\n const removedPosition = node.position;\n\n // Remove the node from the linked list\n if (node.prev) {\n node.prev.next = node.next;\n } else {\n changeSet.next = node.next;\n }\n\n if (node.next) {\n node.next.prev = node.prev;\n } else {\n changeSet.tail = node.prev;\n }\n\n // Update positions for nodes that came after the removed node\n this.updatePositionsAfterRemoval(changeSet, removedPosition);\n\n // Clear ChangeTree reference\n changeTree[changeSetName].queueRootNode = undefined;\n return true;\n }\n\n return false;\n }\n}\n"]}
|
package/package.json
CHANGED
package/src/decoder/Decoder.ts
CHANGED
|
@@ -69,8 +69,7 @@ export class Decoder<T extends Schema = any> {
|
|
|
69
69
|
if (!nextRef) {
|
|
70
70
|
// throw new Error(`"refId" not found: ${nextRefId}`);
|
|
71
71
|
console.error(`"refId" not found: ${nextRefId}`, { previousRef: ref, previousRefId: this.currentRefId });
|
|
72
|
-
console.warn("Please report this to the developers.
|
|
73
|
-
console.warn(Schema.debugRefIdsFromDecoder(this));
|
|
72
|
+
console.warn("Please report this issue to the developers.");
|
|
74
73
|
this.skipCurrentStructure(bytes, it, totalBytes);
|
|
75
74
|
|
|
76
75
|
} else {
|
|
@@ -41,13 +41,13 @@ export interface ChangeTreeNode {
|
|
|
41
41
|
changeTree: ChangeTree;
|
|
42
42
|
next?: ChangeTreeNode;
|
|
43
43
|
prev?: ChangeTreeNode;
|
|
44
|
+
position: number; // Cached position in the linked list for O(1) lookup
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
// Linked list for change trees
|
|
47
48
|
export interface ChangeTreeList {
|
|
48
49
|
next?: ChangeTreeNode;
|
|
49
50
|
tail?: ChangeTreeNode;
|
|
50
|
-
length: number;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
export interface ChangeSet {
|
|
@@ -63,7 +63,7 @@ function createChangeSet(queueRootNode?: ChangeTreeNode): ChangeSet {
|
|
|
63
63
|
|
|
64
64
|
// Linked list helper functions
|
|
65
65
|
export function createChangeTreeList(): ChangeTreeList {
|
|
66
|
-
return { next: undefined, tail: undefined
|
|
66
|
+
return { next: undefined, tail: undefined };
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
export function setOperationAtIndex(changeSet: ChangeSet, index: number) {
|
|
@@ -211,11 +211,11 @@ export class ChangeTree<T extends Ref = any> {
|
|
|
211
211
|
this.forEachChild((child, index) => {
|
|
212
212
|
if (child.root === root) {
|
|
213
213
|
//
|
|
214
|
-
// re-assigning a child of the same root, move it to
|
|
215
|
-
//
|
|
214
|
+
// re-assigning a child of the same root, move it next to parent
|
|
215
|
+
// so encoding order is preserved
|
|
216
216
|
//
|
|
217
217
|
root.add(child);
|
|
218
|
-
root.
|
|
218
|
+
root.moveNextToParent(child);
|
|
219
219
|
return;
|
|
220
220
|
}
|
|
221
221
|
child.setParent(this.ref, root, index);
|
package/src/encoder/Encoder.ts
CHANGED
|
@@ -59,7 +59,7 @@ export class Encoder<T extends Schema = any> {
|
|
|
59
59
|
let current: ChangeTreeList | ChangeTreeNode = this.root[changeSetName];
|
|
60
60
|
|
|
61
61
|
while (current = current.next) {
|
|
62
|
-
const changeTree = current.changeTree;
|
|
62
|
+
const changeTree = (current as ChangeTreeNode).changeTree;
|
|
63
63
|
|
|
64
64
|
if (hasView) {
|
|
65
65
|
if (!view.isChangeTreeVisible(changeTree)) {
|
|
@@ -284,8 +284,8 @@ export class Encoder<T extends Schema = any> {
|
|
|
284
284
|
|
|
285
285
|
get hasChanges() {
|
|
286
286
|
return (
|
|
287
|
-
this.root.changes.
|
|
288
|
-
this.root.filteredChanges.
|
|
287
|
+
this.root.changes.next !== undefined ||
|
|
288
|
+
this.root.filteredChanges.next !== undefined
|
|
289
289
|
);
|
|
290
290
|
}
|
|
291
291
|
}
|
package/src/encoder/Root.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { OPERATION } from "../encoding/spec";
|
|
2
2
|
import { TypeContext } from "../types/TypeContext";
|
|
3
3
|
import { ChangeTree, setOperationAtIndex, ChangeTreeList, createChangeTreeList, ChangeSetName, type ChangeTreeNode } from "./ChangeTree";
|
|
4
|
+
import { $changes } from "../types/symbols";
|
|
4
5
|
|
|
5
6
|
export class Root {
|
|
6
7
|
protected nextUniqueId: number = 0;
|
|
@@ -83,8 +84,8 @@ export class Root {
|
|
|
83
84
|
this.remove(child);
|
|
84
85
|
|
|
85
86
|
} else if (child.parentChain) {
|
|
86
|
-
// re-assigning a child of the same root, move it to
|
|
87
|
-
this.
|
|
87
|
+
// re-assigning a child of the same root, move it next to parent
|
|
88
|
+
this.moveNextToParent(child);
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
91
|
});
|
|
@@ -94,36 +95,57 @@ export class Root {
|
|
|
94
95
|
|
|
95
96
|
//
|
|
96
97
|
// When losing a reference to an instance, it is best to move the
|
|
97
|
-
// ChangeTree to
|
|
98
|
+
// ChangeTree next to its parent in the encoding queue.
|
|
98
99
|
//
|
|
99
100
|
// This way, at decoding time, the instance that contains the
|
|
100
101
|
// ChangeTree will be available before the ChangeTree itself. If the
|
|
101
102
|
// containing instance is not available, the Decoder will throw
|
|
102
103
|
// "refId not found" error.
|
|
103
104
|
//
|
|
104
|
-
this.
|
|
105
|
-
changeTree.forEachChild((child, _) => this.moveToEndOfChanges(child));
|
|
105
|
+
this.recursivelyMoveNextToParent(changeTree);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
return refCount;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
recursivelyMoveNextToParent(changeTree: ChangeTree) {
|
|
112
|
+
this.moveNextToParent(changeTree);
|
|
113
|
+
changeTree.forEachChild((child, _) => this.recursivelyMoveNextToParent(child));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
moveNextToParent(changeTree: ChangeTree) {
|
|
112
117
|
if (changeTree.filteredChanges) {
|
|
113
|
-
this.
|
|
114
|
-
this.
|
|
118
|
+
this.moveNextToParentInChangeTreeList("filteredChanges", changeTree);
|
|
119
|
+
this.moveNextToParentInChangeTreeList("allFilteredChanges", changeTree);
|
|
115
120
|
} else {
|
|
116
|
-
this.
|
|
117
|
-
this.
|
|
121
|
+
this.moveNextToParentInChangeTreeList("changes", changeTree);
|
|
122
|
+
this.moveNextToParentInChangeTreeList("allChanges", changeTree);
|
|
118
123
|
}
|
|
119
124
|
}
|
|
120
125
|
|
|
121
|
-
|
|
126
|
+
moveNextToParentInChangeTreeList(changeSetName: ChangeSetName, changeTree: ChangeTree): void {
|
|
122
127
|
const changeSet = this[changeSetName];
|
|
123
128
|
const node = changeTree[changeSetName].queueRootNode;
|
|
124
|
-
if (!node
|
|
129
|
+
if (!node) return;
|
|
130
|
+
|
|
131
|
+
// Find the parent in the linked list
|
|
132
|
+
const parent = changeTree.parent;
|
|
133
|
+
if (!parent || !parent[$changes]) return;
|
|
134
|
+
|
|
135
|
+
const parentNode = parent[$changes][changeSetName]?.queueRootNode;
|
|
136
|
+
if (!parentNode || parentNode === node) return;
|
|
137
|
+
|
|
138
|
+
// Use cached positions - no iteration needed!
|
|
139
|
+
const parentPosition = parentNode.position;
|
|
140
|
+
const childPosition = node.position;
|
|
125
141
|
|
|
126
|
-
//
|
|
142
|
+
// If child is already after parent, no need to move
|
|
143
|
+
if (childPosition > parentPosition) return;
|
|
144
|
+
|
|
145
|
+
// Child is before parent, so we need to move it after parent
|
|
146
|
+
// This maintains decoding order (parent before child)
|
|
147
|
+
|
|
148
|
+
// Remove node from current position
|
|
127
149
|
if (node.prev) {
|
|
128
150
|
node.prev.next = node.next;
|
|
129
151
|
} else {
|
|
@@ -136,17 +158,20 @@ export class Root {
|
|
|
136
158
|
changeSet.tail = node.prev;
|
|
137
159
|
}
|
|
138
160
|
|
|
139
|
-
//
|
|
140
|
-
node.prev =
|
|
141
|
-
node.next =
|
|
161
|
+
// Insert node right after parent
|
|
162
|
+
node.prev = parentNode;
|
|
163
|
+
node.next = parentNode.next;
|
|
142
164
|
|
|
143
|
-
if (
|
|
144
|
-
|
|
165
|
+
if (parentNode.next) {
|
|
166
|
+
parentNode.next.prev = node;
|
|
145
167
|
} else {
|
|
146
|
-
changeSet.
|
|
168
|
+
changeSet.tail = node;
|
|
147
169
|
}
|
|
148
170
|
|
|
149
|
-
|
|
171
|
+
parentNode.next = node;
|
|
172
|
+
|
|
173
|
+
// Update positions after the move
|
|
174
|
+
this.updatePositionsAfterMove(changeSet, node, parentPosition + 1);
|
|
150
175
|
}
|
|
151
176
|
|
|
152
177
|
public enqueueChangeTree(
|
|
@@ -162,7 +187,12 @@ export class Root {
|
|
|
162
187
|
}
|
|
163
188
|
|
|
164
189
|
protected addToChangeTreeList(list: ChangeTreeList, changeTree: ChangeTree): ChangeTreeNode {
|
|
165
|
-
const node: ChangeTreeNode = {
|
|
190
|
+
const node: ChangeTreeNode = {
|
|
191
|
+
changeTree,
|
|
192
|
+
next: undefined,
|
|
193
|
+
prev: undefined,
|
|
194
|
+
position: list.tail ? list.tail.position + 1 : 0
|
|
195
|
+
};
|
|
166
196
|
|
|
167
197
|
if (!list.next) {
|
|
168
198
|
list.next = node;
|
|
@@ -173,16 +203,42 @@ export class Root {
|
|
|
173
203
|
list.tail = node;
|
|
174
204
|
}
|
|
175
205
|
|
|
176
|
-
list.length++;
|
|
177
|
-
|
|
178
206
|
return node;
|
|
179
207
|
}
|
|
180
208
|
|
|
209
|
+
protected updatePositionsAfterRemoval(list: ChangeTreeList, removedPosition: number) {
|
|
210
|
+
// Update positions for all nodes after the removed position
|
|
211
|
+
let current = list.next;
|
|
212
|
+
let position = 0;
|
|
213
|
+
|
|
214
|
+
while (current) {
|
|
215
|
+
if (position >= removedPosition) {
|
|
216
|
+
current.position = position;
|
|
217
|
+
}
|
|
218
|
+
current = current.next;
|
|
219
|
+
position++;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
protected updatePositionsAfterMove(list: ChangeTreeList, node: ChangeTreeNode, newPosition: number) {
|
|
224
|
+
// Recalculate all positions - this is more reliable than trying to be clever
|
|
225
|
+
let current = list.next;
|
|
226
|
+
let position = 0;
|
|
227
|
+
|
|
228
|
+
while (current) {
|
|
229
|
+
current.position = position;
|
|
230
|
+
current = current.next;
|
|
231
|
+
position++;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
181
235
|
public removeChangeFromChangeSet(changeSetName: ChangeSetName, changeTree: ChangeTree) {
|
|
182
236
|
const changeSet = this[changeSetName];
|
|
183
237
|
const node = changeTree[changeSetName].queueRootNode;
|
|
184
238
|
|
|
185
239
|
if (node && node.changeTree === changeTree) {
|
|
240
|
+
const removedPosition = node.position;
|
|
241
|
+
|
|
186
242
|
// Remove the node from the linked list
|
|
187
243
|
if (node.prev) {
|
|
188
244
|
node.prev.next = node.next;
|
|
@@ -196,7 +252,8 @@ export class Root {
|
|
|
196
252
|
changeSet.tail = node.prev;
|
|
197
253
|
}
|
|
198
254
|
|
|
199
|
-
|
|
255
|
+
// Update positions for nodes that came after the removed node
|
|
256
|
+
this.updatePositionsAfterRemoval(changeSet, removedPosition);
|
|
200
257
|
|
|
201
258
|
// Clear ChangeTree reference
|
|
202
259
|
changeTree[changeSetName].queueRootNode = undefined;
|