@colyseus/schema 3.0.0-alpha.34 → 3.0.0-alpha.36
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/bin/schema-debug +4 -3
- package/build/cjs/index.js +612 -408
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +612 -408
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +612 -408
- package/lib/Metadata.d.ts +6 -6
- package/lib/Metadata.js +48 -21
- package/lib/Metadata.js.map +1 -1
- package/lib/Reflection.d.ts +17 -2
- package/lib/Reflection.js +19 -6
- package/lib/Reflection.js.map +1 -1
- package/lib/Schema.js +24 -17
- package/lib/Schema.js.map +1 -1
- package/lib/annotations.d.ts +1 -1
- package/lib/annotations.js +13 -16
- package/lib/annotations.js.map +1 -1
- package/lib/bench_encode.js +12 -5
- package/lib/bench_encode.js.map +1 -1
- package/lib/debug.js +1 -2
- package/lib/debug.js.map +1 -1
- package/lib/decoder/Decoder.js +1 -1
- package/lib/decoder/Decoder.js.map +1 -1
- package/lib/encoder/ChangeTree.d.ts +23 -7
- package/lib/encoder/ChangeTree.js +183 -106
- package/lib/encoder/ChangeTree.js.map +1 -1
- package/lib/encoder/EncodeOperation.d.ts +2 -1
- package/lib/encoder/EncodeOperation.js +2 -2
- package/lib/encoder/EncodeOperation.js.map +1 -1
- package/lib/encoder/Encoder.d.ts +3 -5
- package/lib/encoder/Encoder.js +97 -61
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/encoder/Root.d.ts +12 -7
- package/lib/encoder/Root.js +41 -20
- package/lib/encoder/Root.js.map +1 -1
- package/lib/encoder/StateView.d.ts +5 -5
- package/lib/encoder/StateView.js +29 -23
- package/lib/encoder/StateView.js.map +1 -1
- package/lib/encoding/encode.js +12 -9
- package/lib/encoding/encode.js.map +1 -1
- package/lib/types/TypeContext.js +2 -1
- package/lib/types/TypeContext.js.map +1 -1
- package/lib/types/custom/ArraySchema.js +27 -13
- package/lib/types/custom/ArraySchema.js.map +1 -1
- package/lib/types/custom/MapSchema.d.ts +3 -1
- package/lib/types/custom/MapSchema.js +7 -4
- package/lib/types/custom/MapSchema.js.map +1 -1
- package/lib/types/symbols.d.ts +8 -6
- package/lib/types/symbols.js +9 -7
- package/lib/types/symbols.js.map +1 -1
- package/lib/utils.js +6 -3
- package/lib/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/Metadata.ts +51 -27
- package/src/Reflection.ts +21 -8
- package/src/Schema.ts +33 -25
- package/src/annotations.ts +14 -18
- package/src/bench_encode.ts +15 -6
- package/src/debug.ts +1 -2
- package/src/decoder/Decoder.ts +1 -1
- package/src/encoder/ChangeTree.ts +220 -115
- package/src/encoder/EncodeOperation.ts +5 -4
- package/src/encoder/Encoder.ts +115 -68
- package/src/encoder/Root.ts +41 -21
- package/src/encoder/StateView.ts +32 -28
- package/src/encoding/encode.ts +12 -9
- package/src/types/TypeContext.ts +2 -1
- package/src/types/custom/ArraySchema.ts +39 -17
- package/src/types/custom/MapSchema.ts +12 -5
- package/src/types/symbols.ts +10 -9
- package/src/utils.ts +7 -3
package/src/encoder/Encoder.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Schema } from "../Schema";
|
|
2
2
|
import { TypeContext } from "../types/TypeContext";
|
|
3
|
-
import { $changes, $encoder, $filter, $
|
|
3
|
+
import { $changes, $encoder, $filter, $onEncodeEnd } from "../types/symbols";
|
|
4
4
|
|
|
5
5
|
import * as encode from "../encoding/encode";
|
|
6
6
|
import type { Iterator } from "../encoding/decode";
|
|
@@ -11,7 +11,6 @@ import { getNextPowerOf2 } from "../utils";
|
|
|
11
11
|
|
|
12
12
|
import type { StateView } from "./StateView";
|
|
13
13
|
import type { Metadata } from "../Metadata";
|
|
14
|
-
import type { ChangeTree } from "./ChangeTree";
|
|
15
14
|
|
|
16
15
|
export class Encoder<T extends Schema = any> {
|
|
17
16
|
static BUFFER_SIZE = 8 * 1024;// 8KB
|
|
@@ -48,26 +47,34 @@ export class Encoder<T extends Schema = any> {
|
|
|
48
47
|
it: Iterator = { offset: 0 },
|
|
49
48
|
view?: StateView,
|
|
50
49
|
buffer = this.sharedBuffer,
|
|
51
|
-
|
|
52
|
-
isEncodeAll =
|
|
50
|
+
changeSetName: "changes" | "allChanges" | "filteredChanges" | "allFilteredChanges" = "changes",
|
|
51
|
+
isEncodeAll = changeSetName === "allChanges",
|
|
53
52
|
initialOffset = it.offset // cache current offset in case we need to resize the buffer
|
|
54
53
|
): Buffer {
|
|
55
54
|
const hasView = (view !== undefined);
|
|
56
55
|
const rootChangeTree = this.state[$changes];
|
|
57
56
|
|
|
58
|
-
const
|
|
57
|
+
const shouldDiscardChanges = !isEncodeAll && !hasView;
|
|
58
|
+
const changeTrees = this.root[changeSetName];
|
|
59
59
|
|
|
60
|
-
for (
|
|
60
|
+
for (let i = 0, numChangeTrees = changeTrees.length; i < numChangeTrees; i++) {
|
|
61
|
+
const changeTree = changeTrees[i];
|
|
62
|
+
|
|
63
|
+
// // Root#removeChangeFromChangeSet() is now removing instead of setting to "undefined"
|
|
64
|
+
// if (changeTree === undefined) { continue; }
|
|
65
|
+
|
|
66
|
+
const operations = changeTree[changeSetName];
|
|
61
67
|
const ref = changeTree.ref;
|
|
62
68
|
|
|
63
69
|
const ctor = ref.constructor;
|
|
64
70
|
const encoder = ctor[$encoder];
|
|
65
71
|
const filter = ctor[$filter];
|
|
72
|
+
const metadata = ctor[Symbol.metadata];
|
|
66
73
|
|
|
67
74
|
// try { throw new Error(); } catch (e) {
|
|
68
75
|
// // only print if not coming from Reflection.ts
|
|
69
76
|
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
70
|
-
// console.log("ChangeTree:", { ref: ref.constructor.name
|
|
77
|
+
// console.log("ChangeTree:", { refId: changeTree.refId, ref: ref.constructor.name });
|
|
71
78
|
// }
|
|
72
79
|
// }
|
|
73
80
|
|
|
@@ -88,7 +95,15 @@ export class Encoder<T extends Schema = any> {
|
|
|
88
95
|
encode.number(buffer, changeTree.refId, it);
|
|
89
96
|
}
|
|
90
97
|
|
|
91
|
-
for (
|
|
98
|
+
for (let j = 0, numChanges = operations.operations.length; j < numChanges; j++) {
|
|
99
|
+
const fieldIndex = operations.operations[j];
|
|
100
|
+
|
|
101
|
+
const operation = (fieldIndex < 0)
|
|
102
|
+
? Math.abs(fieldIndex) // "pure" operation without fieldIndex (e.g. CLEAR, REVERSE, etc.)
|
|
103
|
+
: (isEncodeAll)
|
|
104
|
+
? OPERATION.ADD
|
|
105
|
+
: changeTree.indexedOperations[fieldIndex];
|
|
106
|
+
|
|
92
107
|
//
|
|
93
108
|
// first pass (encodeAll), identify "filtered" operations without encoding them
|
|
94
109
|
// they will be encoded per client, based on their view.
|
|
@@ -96,7 +111,7 @@ export class Encoder<T extends Schema = any> {
|
|
|
96
111
|
// TODO: how can we optimize filtering out "encode all" operations?
|
|
97
112
|
// TODO: avoid checking if no view tags were defined
|
|
98
113
|
//
|
|
99
|
-
if (filter && !filter(ref, fieldIndex, view)) {
|
|
114
|
+
if (fieldIndex === undefined || operation === undefined || (filter && !filter(ref, fieldIndex, view))) {
|
|
100
115
|
// console.log("ADD AS INVISIBLE:", fieldIndex, changeTree.ref.constructor.name)
|
|
101
116
|
// view?.invisible.add(changeTree);
|
|
102
117
|
continue;
|
|
@@ -113,19 +128,17 @@ export class Encoder<T extends Schema = any> {
|
|
|
113
128
|
// }
|
|
114
129
|
// }
|
|
115
130
|
|
|
116
|
-
|
|
117
|
-
}
|
|
131
|
+
// console.log("encode...", { ref: changeTree.ref.constructor.name, refId: changeTree.refId, fieldIndex, operation });
|
|
118
132
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
// changeTree.changes.clear();
|
|
133
|
+
encoder(this, buffer, changeTree, fieldIndex, operation, it, isEncodeAll, hasView, metadata);
|
|
134
|
+
}
|
|
122
135
|
|
|
123
|
-
|
|
124
|
-
|
|
136
|
+
if (shouldDiscardChanges) {
|
|
137
|
+
changeTree.discard();
|
|
125
138
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
139
|
+
// Not a new instance anymore
|
|
140
|
+
changeTree.isNew = false;
|
|
141
|
+
}
|
|
129
142
|
}
|
|
130
143
|
|
|
131
144
|
if (it.offset > buffer.byteLength) {
|
|
@@ -146,38 +159,40 @@ export class Encoder<T extends Schema = any> {
|
|
|
146
159
|
this.sharedBuffer = buffer;
|
|
147
160
|
}
|
|
148
161
|
|
|
149
|
-
return this.encode({ offset: initialOffset }, view, buffer,
|
|
162
|
+
return this.encode({ offset: initialOffset }, view, buffer, changeSetName, isEncodeAll);
|
|
150
163
|
|
|
151
164
|
} else {
|
|
152
|
-
//
|
|
153
|
-
// only clear changes after making sure buffer resize is not required.
|
|
154
|
-
//
|
|
155
|
-
if (shouldClearChanges) {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
165
|
+
// //
|
|
166
|
+
// // only clear changes after making sure buffer resize is not required.
|
|
167
|
+
// //
|
|
168
|
+
// if (shouldClearChanges) {
|
|
169
|
+
// //
|
|
170
|
+
// // FIXME: avoid iterating over change trees twice.
|
|
171
|
+
// //
|
|
172
|
+
// this.onEndEncode(changeTrees);
|
|
173
|
+
// }
|
|
161
174
|
|
|
162
175
|
return buffer.subarray(0, it.offset);
|
|
163
176
|
}
|
|
164
177
|
}
|
|
165
178
|
|
|
166
179
|
encodeAll(it: Iterator = { offset: 0 }, buffer: Buffer = this.sharedBuffer) {
|
|
167
|
-
// console.log(`\nencodeAll(), this.root.allChanges (${this.root.allChanges.
|
|
180
|
+
// console.log(`\nencodeAll(), this.root.allChanges (${(Object.keys(this.root.allChanges).length)})`);
|
|
168
181
|
// this.debugChanges("allChanges");
|
|
169
182
|
|
|
170
|
-
return this.encode(it, undefined, buffer,
|
|
183
|
+
return this.encode(it, undefined, buffer, "allChanges", true);
|
|
171
184
|
}
|
|
172
185
|
|
|
173
186
|
encodeAllView(view: StateView, sharedOffset: number, it: Iterator, bytes = this.sharedBuffer) {
|
|
174
187
|
const viewOffset = it.offset;
|
|
175
188
|
|
|
176
|
-
// console.log(`\nencodeAllView(), this.root.allFilteredChanges (${this.root.allFilteredChanges.
|
|
189
|
+
// console.log(`\nencodeAllView(), this.root.allFilteredChanges (${(Object.keys(this.root.allFilteredChanges).length)})`);
|
|
177
190
|
// this.debugChanges("allFilteredChanges");
|
|
178
191
|
|
|
192
|
+
// console.log("\n\nENCODE ALL FOR VIEW...\n\n")
|
|
193
|
+
|
|
179
194
|
// try to encode "filtered" changes
|
|
180
|
-
this.encode(it, view, bytes,
|
|
195
|
+
this.encode(it, view, bytes, "allFilteredChanges", true, viewOffset);
|
|
181
196
|
|
|
182
197
|
return Buffer.concat([
|
|
183
198
|
bytes.subarray(0, sharedOffset),
|
|
@@ -185,23 +200,24 @@ export class Encoder<T extends Schema = any> {
|
|
|
185
200
|
]);
|
|
186
201
|
}
|
|
187
202
|
|
|
188
|
-
debugChanges(
|
|
189
|
-
|
|
190
|
-
) {
|
|
191
|
-
const changeSet = (typeof (field) === "string")
|
|
203
|
+
debugChanges(field: "changes" | "allFilteredChanges" | "allChanges" | "filteredChanges") {
|
|
204
|
+
const rootChangeSet = (typeof (field) === "string")
|
|
192
205
|
? this.root[field]
|
|
193
206
|
: field;
|
|
194
207
|
|
|
195
|
-
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
208
|
+
rootChangeSet.forEach((changeTree) => {
|
|
209
|
+
const changeSet = changeTree[field];
|
|
210
|
+
|
|
211
|
+
const metadata: Metadata = changeTree.ref.constructor[Symbol.metadata];
|
|
212
|
+
console.log("->", { ref: changeTree.ref.constructor.name, refId: changeTree.refId, changes: Object.keys(changeSet).length });
|
|
213
|
+
for (const index in changeSet) {
|
|
214
|
+
const op = changeSet[index];
|
|
199
215
|
console.log(" ->", {
|
|
200
216
|
index,
|
|
201
217
|
field: metadata?.[index],
|
|
202
218
|
op: OPERATION[op],
|
|
203
219
|
});
|
|
204
|
-
}
|
|
220
|
+
}
|
|
205
221
|
});
|
|
206
222
|
}
|
|
207
223
|
|
|
@@ -215,28 +231,38 @@ export class Encoder<T extends Schema = any> {
|
|
|
215
231
|
// this.debugChanges("filteredChanges");
|
|
216
232
|
|
|
217
233
|
// encode visibility changes (add/remove for this view)
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
234
|
+
const refIds = Object.keys(view.changes);
|
|
235
|
+
// console.log("ENCODE VIEW:", refIds);
|
|
236
|
+
for (let i = 0, numRefIds = refIds.length; i < numRefIds; i++) {
|
|
237
|
+
const refId = refIds[i];
|
|
238
|
+
const changes = view.changes[refId];
|
|
239
|
+
const changeTree = this.root.changeTrees[refId];
|
|
240
|
+
|
|
241
|
+
if (
|
|
242
|
+
changeTree === undefined ||
|
|
243
|
+
Object.keys(changes).length === 0 // FIXME: avoid having empty changes if no changes were made
|
|
244
|
+
) {
|
|
245
|
+
// console.log("changes.size === 0, skip", changeTree.ref.constructor.name);
|
|
223
246
|
continue;
|
|
224
247
|
}
|
|
225
248
|
|
|
226
249
|
const ref = changeTree.ref;
|
|
227
250
|
|
|
228
|
-
const ctor = ref
|
|
251
|
+
const ctor = ref.constructor;
|
|
229
252
|
const encoder = ctor[$encoder];
|
|
253
|
+
const metadata = ctor[Symbol.metadata];
|
|
230
254
|
|
|
231
255
|
bytes[it.offset++] = SWITCH_TO_STRUCTURE & 255;
|
|
232
256
|
encode.number(bytes, changeTree.refId, it);
|
|
233
257
|
|
|
234
|
-
const
|
|
258
|
+
const keys = Object.keys(changes);
|
|
259
|
+
for (let i = 0, numChanges = keys.length; i < numChanges; i++) {
|
|
260
|
+
const key = keys[i];
|
|
261
|
+
const operation = changes[key];
|
|
235
262
|
|
|
236
|
-
for (const [fieldIndex, operation] of changesIterator) {
|
|
237
263
|
// isEncodeAll = false
|
|
238
264
|
// hasView = true
|
|
239
|
-
encoder(this, bytes, changeTree,
|
|
265
|
+
encoder(this, bytes, changeTree, Number(key), operation, it, false, true, metadata);
|
|
240
266
|
}
|
|
241
267
|
}
|
|
242
268
|
|
|
@@ -245,10 +271,12 @@ export class Encoder<T extends Schema = any> {
|
|
|
245
271
|
// (to allow re-using StateView's for multiple clients)
|
|
246
272
|
//
|
|
247
273
|
// clear "view" changes after encoding
|
|
248
|
-
view.changes
|
|
274
|
+
view.changes = {};
|
|
275
|
+
|
|
276
|
+
// console.log("FILTERED CHANGES:", this.root.filteredChanges);
|
|
249
277
|
|
|
250
278
|
// try to encode "filtered" changes
|
|
251
|
-
this.encode(it, view, bytes,
|
|
279
|
+
this.encode(it, view, bytes, "filteredChanges", false, viewOffset);
|
|
252
280
|
|
|
253
281
|
return Buffer.concat([
|
|
254
282
|
bytes.subarray(0, sharedOffset),
|
|
@@ -257,30 +285,44 @@ export class Encoder<T extends Schema = any> {
|
|
|
257
285
|
}
|
|
258
286
|
|
|
259
287
|
onEndEncode(changeTrees = this.root.changes) {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
// changeTree.changes.clear();
|
|
288
|
+
// changeTrees.forEach(function(changeTree) {
|
|
289
|
+
// changeTree.endEncode();
|
|
290
|
+
// });
|
|
264
291
|
|
|
265
|
-
// // ArraySchema and MapSchema have a custom "encode end" method
|
|
266
|
-
// changeTree.ref[$onEncodeEnd]?.();
|
|
267
292
|
|
|
268
|
-
|
|
269
|
-
|
|
293
|
+
// for (const refId in changeTrees) {
|
|
294
|
+
// const changeTree = this.root.changeTrees[refId];
|
|
295
|
+
// changeTree.endEncode();
|
|
270
296
|
|
|
271
|
-
|
|
297
|
+
// // changeTree.changes.clear();
|
|
298
|
+
|
|
299
|
+
// // // ArraySchema and MapSchema have a custom "encode end" method
|
|
300
|
+
// // changeTree.ref[$onEncodeEnd]?.();
|
|
301
|
+
|
|
302
|
+
// // // Not a new instance anymore
|
|
303
|
+
// // delete changeTree[$isNew];
|
|
304
|
+
// }
|
|
272
305
|
}
|
|
273
306
|
|
|
274
307
|
discardChanges() {
|
|
308
|
+
// console.log("DISCARD CHANGES!");
|
|
309
|
+
|
|
275
310
|
// discard shared changes
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
311
|
+
let length = this.root.changes.length;
|
|
312
|
+
if (length > 0) {
|
|
313
|
+
while (length--) {
|
|
314
|
+
this.root.changes[length]?.endEncode();
|
|
315
|
+
}
|
|
316
|
+
this.root.changes.length = 0;
|
|
279
317
|
}
|
|
318
|
+
|
|
280
319
|
// discard filtered changes
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
320
|
+
length = this.root.filteredChanges.length;
|
|
321
|
+
if (length > 0) {
|
|
322
|
+
while (length--) {
|
|
323
|
+
this.root.filteredChanges[length]?.endEncode();
|
|
324
|
+
}
|
|
325
|
+
this.root.filteredChanges.length = 0;
|
|
284
326
|
}
|
|
285
327
|
}
|
|
286
328
|
|
|
@@ -288,6 +330,11 @@ export class Encoder<T extends Schema = any> {
|
|
|
288
330
|
const baseTypeId = this.context.getTypeId(baseType);
|
|
289
331
|
const targetTypeId = this.context.getTypeId(targetType);
|
|
290
332
|
|
|
333
|
+
if (targetTypeId === undefined) {
|
|
334
|
+
console.warn(`@colyseus/schema WARNING: Class "${targetType.name}" is not registered on TypeRegistry - Please either tag the class with @entity or define a @type() field.`);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
291
338
|
if (baseTypeId !== targetTypeId) {
|
|
292
339
|
bytes[it.offset++] = TYPE_ID & 255;
|
|
293
340
|
encode.number(bytes, targetTypeId, it);
|
package/src/encoder/Root.ts
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import { OPERATION } from "../encoding/spec";
|
|
2
2
|
import { TypeContext } from "../types/TypeContext";
|
|
3
|
-
import {
|
|
3
|
+
import { spliceOne } from "../types/utils";
|
|
4
|
+
import { ChangeTree, setOperationAtIndex } from "./ChangeTree";
|
|
4
5
|
|
|
5
6
|
export class Root {
|
|
6
7
|
protected nextUniqueId: number = 0;
|
|
7
|
-
|
|
8
|
+
|
|
9
|
+
refCount: {[id: number]: number} = {};
|
|
10
|
+
changeTrees: {[refId: number]: ChangeTree} = {};
|
|
8
11
|
|
|
9
12
|
// all changes
|
|
10
|
-
allChanges
|
|
11
|
-
allFilteredChanges =
|
|
13
|
+
allChanges: ChangeTree[] = [];
|
|
14
|
+
allFilteredChanges: ChangeTree[] = [];// TODO: do not initialize it if filters are not used
|
|
12
15
|
|
|
13
16
|
// pending changes to be encoded
|
|
14
|
-
changes
|
|
15
|
-
filteredChanges =
|
|
17
|
+
changes: ChangeTree[] = [];
|
|
18
|
+
filteredChanges: ChangeTree[] = [];// TODO: do not initialize it if filters are not used
|
|
16
19
|
|
|
17
20
|
constructor(public types: TypeContext) { }
|
|
18
21
|
|
|
@@ -21,45 +24,53 @@ export class Root {
|
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
add(changeTree: ChangeTree) {
|
|
24
|
-
|
|
27
|
+
// FIXME: move implementation of `ensureRefId` to `Root` class
|
|
28
|
+
changeTree.ensureRefId();
|
|
29
|
+
|
|
30
|
+
const isNewChangeTree = (this.changeTrees[changeTree.refId] === undefined);
|
|
31
|
+
if (isNewChangeTree) { this.changeTrees[changeTree.refId] = changeTree; }
|
|
25
32
|
|
|
33
|
+
const previousRefCount = this.refCount[changeTree.refId];
|
|
26
34
|
if (previousRefCount === 0) {
|
|
27
35
|
//
|
|
28
36
|
// When a ChangeTree is re-added, it means that it was previously removed.
|
|
29
37
|
// We need to re-add all changes to the `changes` map.
|
|
30
38
|
//
|
|
31
|
-
changeTree.allChanges.
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
const ops = changeTree.allChanges.operations;
|
|
40
|
+
let len = ops.length;
|
|
41
|
+
while (len--) {
|
|
42
|
+
changeTree.indexedOperations[ops[len]] = OPERATION.ADD;
|
|
43
|
+
setOperationAtIndex(changeTree.changes, len);
|
|
44
|
+
}
|
|
34
45
|
}
|
|
35
46
|
|
|
36
|
-
|
|
37
|
-
this.refCount.set(changeTree, refCount);
|
|
47
|
+
this.refCount[changeTree.refId] = (previousRefCount || 0) + 1;
|
|
38
48
|
|
|
39
|
-
return
|
|
49
|
+
return isNewChangeTree;
|
|
40
50
|
}
|
|
41
51
|
|
|
42
52
|
remove(changeTree: ChangeTree) {
|
|
43
|
-
const refCount = (this.refCount.
|
|
53
|
+
const refCount = (this.refCount[changeTree.refId]) - 1;
|
|
44
54
|
|
|
45
55
|
if (refCount <= 0) {
|
|
46
56
|
//
|
|
47
57
|
// Only remove "root" reference if it's the last reference
|
|
48
58
|
//
|
|
49
59
|
changeTree.root = undefined;
|
|
60
|
+
delete this.changeTrees[changeTree.refId];
|
|
50
61
|
|
|
51
|
-
this.allChanges
|
|
52
|
-
this.changes
|
|
62
|
+
this.removeChangeFromChangeSet("allChanges", changeTree);
|
|
63
|
+
this.removeChangeFromChangeSet("changes", changeTree);
|
|
53
64
|
|
|
54
65
|
if (changeTree.isFiltered || changeTree.isPartiallyFiltered) {
|
|
55
|
-
this.allFilteredChanges
|
|
56
|
-
this.filteredChanges
|
|
66
|
+
this.removeChangeFromChangeSet("allFilteredChanges", changeTree);
|
|
67
|
+
this.removeChangeFromChangeSet("filteredChanges", changeTree);
|
|
57
68
|
}
|
|
58
69
|
|
|
59
|
-
this.refCount.
|
|
70
|
+
this.refCount[changeTree.refId] = 0;
|
|
60
71
|
|
|
61
72
|
} else {
|
|
62
|
-
this.refCount.
|
|
73
|
+
this.refCount[changeTree.refId] = refCount;
|
|
63
74
|
}
|
|
64
75
|
|
|
65
76
|
changeTree.forEachChild((child, _) => this.remove(child));
|
|
@@ -67,7 +78,16 @@ export class Root {
|
|
|
67
78
|
return refCount;
|
|
68
79
|
}
|
|
69
80
|
|
|
81
|
+
removeChangeFromChangeSet(changeSetName: "allChanges" | "changes" | "filteredChanges" | "allFilteredChanges", changeTree: ChangeTree) {
|
|
82
|
+
const changeSet = this[changeSetName];
|
|
83
|
+
const index = changeSet.indexOf(changeTree);
|
|
84
|
+
if (index !== -1) {
|
|
85
|
+
spliceOne(changeSet, index);
|
|
86
|
+
// changeSet[index] = undefined;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
70
90
|
clear() {
|
|
71
|
-
this.changes.
|
|
91
|
+
this.changes.length = 0;
|
|
72
92
|
}
|
|
73
93
|
}
|
package/src/encoder/StateView.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { ChangeTree, Ref } from "./ChangeTree";
|
|
2
|
-
import { $changes } from "../types/symbols";
|
|
1
|
+
import { ChangeSet, ChangeTree, IndexedOperations, Ref } from "./ChangeTree";
|
|
2
|
+
import { $changes, $fieldIndexesByViewTag, $viewFieldIndexes } from "../types/symbols";
|
|
3
3
|
import { DEFAULT_VIEW_TAG } from "../annotations";
|
|
4
4
|
import { OPERATION } from "../encoding/spec";
|
|
5
5
|
import { Metadata } from "../Metadata";
|
|
6
|
-
import type { Schema } from "../Schema";
|
|
7
6
|
|
|
8
|
-
export function createView(
|
|
7
|
+
export function createView() {
|
|
8
|
+
return new StateView();
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export class StateView {
|
|
@@ -25,7 +25,7 @@ export class StateView {
|
|
|
25
25
|
* Manual "ADD" operations for changes per ChangeTree, specific to this view.
|
|
26
26
|
* (This is used to force encoding a property, even if it was not changed)
|
|
27
27
|
*/
|
|
28
|
-
changes
|
|
28
|
+
changes: { [refId: number]: IndexedOperations } = {};
|
|
29
29
|
|
|
30
30
|
// TODO: allow to set multiple tags at once
|
|
31
31
|
add(obj: Ref, tag: number = DEFAULT_VIEW_TAG, checkIncludeParent: boolean = true) {
|
|
@@ -50,10 +50,10 @@ export class StateView {
|
|
|
50
50
|
// TODO: when adding an item of a MapSchema, the changes may not
|
|
51
51
|
// be set (only the parent's changes are set)
|
|
52
52
|
//
|
|
53
|
-
let changes = this.changes.
|
|
53
|
+
let changes = this.changes[changeTree.refId];
|
|
54
54
|
if (changes === undefined) {
|
|
55
|
-
changes =
|
|
56
|
-
this.changes.
|
|
55
|
+
changes = {};
|
|
56
|
+
this.changes[changeTree.refId] = changes;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
// set tag
|
|
@@ -71,9 +71,9 @@ export class StateView {
|
|
|
71
71
|
tags.add(tag);
|
|
72
72
|
|
|
73
73
|
// Ref: add tagged properties
|
|
74
|
-
metadata?.[
|
|
74
|
+
metadata?.[$fieldIndexesByViewTag]?.[tag]?.forEach((index) => {
|
|
75
75
|
if (changeTree.getChange(index) !== OPERATION.DELETE) {
|
|
76
|
-
changes
|
|
76
|
+
changes[index] = OPERATION.ADD;
|
|
77
77
|
}
|
|
78
78
|
});
|
|
79
79
|
|
|
@@ -83,7 +83,11 @@ export class StateView {
|
|
|
83
83
|
? changeTree.allFilteredChanges
|
|
84
84
|
: changeTree.allChanges;
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
for (let i = 0, len = changeSet.operations.length; i < len; i++) {
|
|
87
|
+
const index = changeSet.operations[i];
|
|
88
|
+
if (index === undefined) { continue; } // skip "undefined" indexes
|
|
89
|
+
|
|
90
|
+
const op = changeTree.indexedOperations[index];
|
|
87
91
|
const tagAtIndex = metadata?.[index].tag;
|
|
88
92
|
if (
|
|
89
93
|
(
|
|
@@ -93,9 +97,9 @@ export class StateView {
|
|
|
93
97
|
) &&
|
|
94
98
|
op !== OPERATION.DELETE
|
|
95
99
|
) {
|
|
96
|
-
changes
|
|
100
|
+
changes[index] = op;
|
|
97
101
|
}
|
|
98
|
-
}
|
|
102
|
+
}
|
|
99
103
|
}
|
|
100
104
|
|
|
101
105
|
// Add children of this ChangeTree to this view
|
|
@@ -128,10 +132,10 @@ export class StateView {
|
|
|
128
132
|
// add parent's tag properties
|
|
129
133
|
if (changeTree.getChange(parentIndex) !== OPERATION.DELETE) {
|
|
130
134
|
|
|
131
|
-
let changes = this.changes.
|
|
135
|
+
let changes = this.changes[changeTree.refId];
|
|
132
136
|
if (changes === undefined) {
|
|
133
|
-
changes =
|
|
134
|
-
this.changes.
|
|
137
|
+
changes = {};
|
|
138
|
+
this.changes[changeTree.refId] = changes;
|
|
135
139
|
}
|
|
136
140
|
|
|
137
141
|
if (!this.tags) {
|
|
@@ -147,7 +151,7 @@ export class StateView {
|
|
|
147
151
|
}
|
|
148
152
|
tags.add(tag);
|
|
149
153
|
|
|
150
|
-
changes
|
|
154
|
+
changes[parentIndex] = OPERATION.ADD;
|
|
151
155
|
}
|
|
152
156
|
}
|
|
153
157
|
|
|
@@ -163,10 +167,10 @@ export class StateView {
|
|
|
163
167
|
const ref = changeTree.ref;
|
|
164
168
|
const metadata: Metadata = ref.constructor[Symbol.metadata];
|
|
165
169
|
|
|
166
|
-
let changes = this.changes.
|
|
170
|
+
let changes = this.changes[changeTree.refId];
|
|
167
171
|
if (changes === undefined) {
|
|
168
|
-
changes =
|
|
169
|
-
this.changes.
|
|
172
|
+
changes = {};
|
|
173
|
+
this.changes[changeTree.refId] = changes;
|
|
170
174
|
}
|
|
171
175
|
|
|
172
176
|
if (tag === DEFAULT_VIEW_TAG) {
|
|
@@ -174,25 +178,25 @@ export class StateView {
|
|
|
174
178
|
const parent = changeTree.parent;
|
|
175
179
|
if (!Metadata.isValidInstance(parent)) {
|
|
176
180
|
const parentChangeTree = parent[$changes];
|
|
177
|
-
let changes = this.changes.
|
|
181
|
+
let changes = this.changes[parentChangeTree.refId];
|
|
178
182
|
if (changes === undefined) {
|
|
179
|
-
changes =
|
|
180
|
-
this.changes.
|
|
183
|
+
changes = {};
|
|
184
|
+
this.changes[parentChangeTree.refId] = changes;
|
|
181
185
|
}
|
|
182
186
|
// DELETE / DELETE BY REF ID
|
|
183
|
-
changes
|
|
187
|
+
changes[changeTree.parentIndex] = OPERATION.DELETE;
|
|
184
188
|
|
|
185
189
|
} else {
|
|
186
190
|
// delete all "tagged" properties.
|
|
187
|
-
metadata[
|
|
188
|
-
changes
|
|
191
|
+
metadata[$viewFieldIndexes].forEach((index) =>
|
|
192
|
+
changes[index] = OPERATION.DELETE);
|
|
189
193
|
}
|
|
190
194
|
|
|
191
195
|
|
|
192
196
|
} else {
|
|
193
197
|
// delete only tagged properties
|
|
194
|
-
metadata[
|
|
195
|
-
changes
|
|
198
|
+
metadata[$fieldIndexesByViewTag][tag].forEach((index) =>
|
|
199
|
+
changes[index] = OPERATION.DELETE);
|
|
196
200
|
}
|
|
197
201
|
|
|
198
202
|
// remove tag
|
package/src/encoding/encode.ts
CHANGED
|
@@ -68,21 +68,24 @@ export function utf8Write(view: BufferLike, str: string, it: Iterator) {
|
|
|
68
68
|
view[it.offset++] = c;
|
|
69
69
|
}
|
|
70
70
|
else if (c < 0x800) {
|
|
71
|
-
view[it.offset
|
|
72
|
-
view[it.offset
|
|
71
|
+
view[it.offset] = 0xc0 | (c >> 6);
|
|
72
|
+
view[it.offset + 1] = 0x80 | (c & 0x3f);
|
|
73
|
+
it.offset += 2;
|
|
73
74
|
}
|
|
74
75
|
else if (c < 0xd800 || c >= 0xe000) {
|
|
75
|
-
view[it.offset
|
|
76
|
-
view[it.offset
|
|
77
|
-
view[it.offset
|
|
76
|
+
view[it.offset] = 0xe0 | (c >> 12);
|
|
77
|
+
view[it.offset+1] = 0x80 | (c >> 6 & 0x3f);
|
|
78
|
+
view[it.offset+2] = 0x80 | (c & 0x3f);
|
|
79
|
+
it.offset += 3;
|
|
78
80
|
}
|
|
79
81
|
else {
|
|
80
82
|
i++;
|
|
81
83
|
c = 0x10000 + (((c & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff));
|
|
82
|
-
view[it.offset
|
|
83
|
-
view[it.offset
|
|
84
|
-
view[it.offset
|
|
85
|
-
view[it.offset
|
|
84
|
+
view[it.offset] = 0xf0 | (c >> 18);
|
|
85
|
+
view[it.offset+1] = 0x80 | (c >> 12 & 0x3f);
|
|
86
|
+
view[it.offset+2] = 0x80 | (c >> 6 & 0x3f);
|
|
87
|
+
view[it.offset+3] = 0x80 | (c & 0x3f);
|
|
88
|
+
it.offset += 4;
|
|
86
89
|
}
|
|
87
90
|
}
|
|
88
91
|
}
|
package/src/types/TypeContext.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Metadata } from "../Metadata";
|
|
2
2
|
import { Schema } from "../Schema";
|
|
3
|
+
import { $viewFieldIndexes } from "./symbols";
|
|
3
4
|
|
|
4
5
|
export class TypeContext {
|
|
5
6
|
types: { [id: number]: typeof Schema; } = {};
|
|
@@ -76,7 +77,7 @@ export class TypeContext {
|
|
|
76
77
|
const metadata: Metadata = (klass[Symbol.metadata] ??= {});
|
|
77
78
|
|
|
78
79
|
// if any schema/field has filters, mark "context" as having filters.
|
|
79
|
-
if (metadata[
|
|
80
|
+
if (metadata[$viewFieldIndexes]) {
|
|
80
81
|
this.hasFilters = true;
|
|
81
82
|
}
|
|
82
83
|
|