j-templates 7.0.72 → 7.0.74
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/DOM/index.d.ts +0 -2
- package/DOM/index.js +2 -2
- package/Node/vNode.js +0 -4
- package/Node/vNode.types.d.ts +0 -2
- package/Store/Tree/observableScope.js +112 -160
- package/Utils/bitSet.d.ts +23 -0
- package/Utils/bitSet.js +139 -0
- package/Utils/emitter.d.ts +0 -3
- package/Utils/emitter.js +6 -29
- package/package.json +1 -1
package/DOM/index.d.ts
CHANGED
package/DOM/index.js
CHANGED
|
@@ -16,5 +16,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./elements"), exports);
|
|
18
18
|
// export * from "./svgElements";
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
// export * from "./createPropertyAssignment";
|
|
20
|
+
// export * from "./createEventAssignment";
|
package/Node/vNode.js
CHANGED
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.vNode = void 0;
|
|
4
4
|
const Store_1 = require("../Store");
|
|
5
5
|
const observableScope_1 = require("../Store/Tree/observableScope");
|
|
6
|
-
const emitter_1 = require("../Utils/emitter");
|
|
7
6
|
const functions_1 = require("../Utils/functions");
|
|
8
7
|
const injector_1 = require("../Utils/injector");
|
|
9
8
|
const thread_1 = require("../Utils/thread");
|
|
@@ -22,7 +21,6 @@ var vNode;
|
|
|
22
21
|
node: definition.node ?? null,
|
|
23
22
|
children: null,
|
|
24
23
|
destroyed: false,
|
|
25
|
-
onDestroyed: null,
|
|
26
24
|
component: null,
|
|
27
25
|
scopes: [],
|
|
28
26
|
};
|
|
@@ -36,7 +34,6 @@ var vNode;
|
|
|
36
34
|
node: nodeConfig_1.NodeConfig.createTextNode(text),
|
|
37
35
|
children: null,
|
|
38
36
|
destroyed: false,
|
|
39
|
-
onDestroyed: null,
|
|
40
37
|
component: null,
|
|
41
38
|
scopes: [],
|
|
42
39
|
};
|
|
@@ -59,7 +56,6 @@ var vNode;
|
|
|
59
56
|
vnode.destroyed = true;
|
|
60
57
|
vnode.component?.Destroy();
|
|
61
58
|
Store_1.ObservableScope.DestroyAll(vnode.scopes);
|
|
62
|
-
vnode.onDestroyed && emitter_1.Emitter.Emit(vnode.onDestroyed);
|
|
63
59
|
for (let x = 0; vnode.children && x < vnode.children.length; x++) {
|
|
64
60
|
DestroyAll(vnode.children[x][1]);
|
|
65
61
|
Store_1.ObservableScope.Destroy(vnode.children[x][2]);
|
package/Node/vNode.types.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { Component } from "./component";
|
|
|
2
2
|
import { IObservableScope } from "../Store/Tree/observableScope";
|
|
3
3
|
import { Injector } from "../Utils/injector";
|
|
4
4
|
import { RecursivePartial } from "../Utils/utils.types";
|
|
5
|
-
import { Emitter } from "../Utils/emitter";
|
|
6
5
|
export type FunctionOr<T> = {
|
|
7
6
|
(): T | Promise<T>;
|
|
8
7
|
} | T;
|
|
@@ -21,7 +20,6 @@ export type vNode = {
|
|
|
21
20
|
node: Node | null;
|
|
22
21
|
children: [any, vNode[], IObservableScope<string | vNode | vNode[]> | null][] | null;
|
|
23
22
|
destroyed: boolean;
|
|
24
|
-
onDestroyed: Emitter | null;
|
|
25
23
|
scopes: IObservableScope<unknown>[];
|
|
26
24
|
component: Component;
|
|
27
25
|
};
|
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ObservableScope = void 0;
|
|
4
4
|
exports.CalcScope = CalcScope;
|
|
5
|
-
const
|
|
5
|
+
const bitSet_1 = require("../../Utils/bitSet");
|
|
6
6
|
const emitter_1 = require("../../Utils/emitter");
|
|
7
7
|
const functions_1 = require("../../Utils/functions");
|
|
8
|
-
const list_1 = require("../../Utils/list");
|
|
9
8
|
/**
|
|
10
9
|
* Creates a dynamic (reactive) observable scope.
|
|
11
10
|
* @template T The type of value stored in the scope.
|
|
@@ -46,6 +45,11 @@ function CreateStaticScope(initialValue) {
|
|
|
46
45
|
value: initialValue,
|
|
47
46
|
};
|
|
48
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Queue of scopes pending batched update processing.
|
|
50
|
+
* Scopes are added when their dependencies change and greedy batching is enabled.
|
|
51
|
+
* The queue is processed in a microtask to batch multiple updates efficiently.
|
|
52
|
+
*/
|
|
49
53
|
let scopeQueue = [];
|
|
50
54
|
/**
|
|
51
55
|
* Processes all queued scopes, recomputing dirty scopes and emitting changes.
|
|
@@ -90,93 +94,78 @@ function OnSet() {
|
|
|
90
94
|
emitter_1.Emitter.Emit(this.emitter, this);
|
|
91
95
|
}
|
|
92
96
|
/**
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* @param state The current watch state.
|
|
97
|
-
* @param emitter The emitter to register.
|
|
98
|
-
*/
|
|
99
|
-
function RegisterSame(state, emitter) {
|
|
100
|
-
if (state.emitterIndex < state.emitters.length &&
|
|
101
|
-
state.emitters[state.emitterIndex] === emitter) {
|
|
102
|
-
state.emitterIndex++;
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
state.emitters = state.emitters.slice(0, state.emitterIndex);
|
|
106
|
-
state.strategy = SORTED_STRATEGY;
|
|
107
|
-
RegisterSorted(state, emitter);
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Registers an emitter while maintaining sorted order by emitter ID.
|
|
111
|
-
* If insertion would break sort order, falls back to PUSH_STRATEGY.
|
|
112
|
-
* @param state The current watch state.
|
|
113
|
-
* @param emitter The emitter to register.
|
|
97
|
+
* Strategy where emitters are accessed in the same order as the previous execution.
|
|
98
|
+
* This allows for efficient reconciliation by tracking which emitters were accessed
|
|
99
|
+
* at the same positions in the array.
|
|
114
100
|
*/
|
|
115
|
-
|
|
116
|
-
if (state.emitters.length === 0 ||
|
|
117
|
-
state.emitters[state.emitters.length - 1][0] < emitter[0])
|
|
118
|
-
state.emitters.push(emitter);
|
|
119
|
-
else {
|
|
120
|
-
state.strategy = PUSH_STRATEGY;
|
|
121
|
-
RegisterPush(state, emitter);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Registers an emitter using PUSH_STRATEGY.
|
|
126
|
-
* Switches to DISTINCT_STRATEGY when the emitter count exceeds 50 to optimize memory.
|
|
127
|
-
* @param state The current watch state.
|
|
128
|
-
* @param emitter The emitter to register.
|
|
129
|
-
*/
|
|
130
|
-
function RegisterPush(state, emitter) {
|
|
131
|
-
state.emitters.push(emitter);
|
|
132
|
-
if (state.emitters.length > 50) {
|
|
133
|
-
const idSet = (state.emitterIds = new Set([state.emitters[0][0]]));
|
|
134
|
-
let writePos = 0;
|
|
135
|
-
for (let x = 1; x < state.emitters.length; x++) {
|
|
136
|
-
if (!idSet.has(state.emitters[x][0])) {
|
|
137
|
-
state.emitters[++writePos] = state.emitters[x];
|
|
138
|
-
idSet.add(state.emitters[x][0]);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
writePos++;
|
|
142
|
-
if (writePos < state.emitters.length)
|
|
143
|
-
state.emitters.splice(writePos);
|
|
144
|
-
state.strategy = DISTINCT_STRATEGY;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
101
|
+
const SAME_STRATEGY = 1;
|
|
147
102
|
/**
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
* @param state The current watch state.
|
|
151
|
-
* @param emitter The emitter to register.
|
|
103
|
+
* Strategy where new emitters are pushed to the array when accessed.
|
|
104
|
+
* Used when the access pattern changes from the previous execution.
|
|
152
105
|
*/
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
106
|
+
const PUSH_STRATEGY = 2;
|
|
107
|
+
function GetBitSetFor(emitters) {
|
|
108
|
+
const bitSet = bitSet_1.BitSetPool.Get(BIT_SET_POOL);
|
|
109
|
+
for (let x = 0; x < emitters.length; x++)
|
|
110
|
+
bitSet_1.BitSet.set(bitSet, emitters[x][0]);
|
|
111
|
+
return bitSet;
|
|
158
112
|
}
|
|
159
113
|
/**
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
114
|
+
* Registers an emitter as a dependency during a watch operation.
|
|
115
|
+
*
|
|
116
|
+
* This function tracks which emitters are accessed while executing a scope's getFunction.
|
|
117
|
+
* It uses two strategies for efficiency:
|
|
118
|
+
*
|
|
119
|
+
* - **SAME_STRATEGY**: When emitters are accessed in the same order as the previous execution,
|
|
120
|
+
* it tracks this via emitterIndex and skips array operations. Only switches to PUSH_STRATEGY
|
|
121
|
+
* when the order changes.
|
|
122
|
+
*
|
|
123
|
+
* - **PUSH_STRATEGY**: When access order differs or is new, emitters are pushed to the array.
|
|
124
|
+
* The BitSet prevents duplicates (same emitter accessed multiple times).
|
|
125
|
+
*
|
|
126
|
+
* The function does nothing if called outside a watch context (watchState is null).
|
|
127
|
+
*
|
|
128
|
+
* @param emitter The emitter to register as a dependency. Must have a unique ID at index 0.
|
|
163
129
|
*/
|
|
164
130
|
function RegisterEmitter(emitter) {
|
|
165
131
|
if (watchState === null)
|
|
166
132
|
return;
|
|
167
133
|
switch (watchState.strategy) {
|
|
168
|
-
case SAME_STRATEGY:
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
134
|
+
case SAME_STRATEGY: {
|
|
135
|
+
if (watchState.emitterIndex < watchState.emitters.length &&
|
|
136
|
+
watchState.emitters[watchState.emitterIndex][0] === emitter[0]) {
|
|
137
|
+
watchState.emitterIndex++;
|
|
138
|
+
// BitSet.set(watchState.emitterIds, emitter[0]);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
watchState.strategy = PUSH_STRATEGY;
|
|
142
|
+
watchState.emitters = watchState.emitters.slice(0, watchState.emitterIndex);
|
|
143
|
+
RegisterEmitter(emitter);
|
|
144
|
+
}
|
|
176
145
|
break;
|
|
177
|
-
|
|
178
|
-
|
|
146
|
+
}
|
|
147
|
+
case PUSH_STRATEGY: {
|
|
148
|
+
switch (watchState.emitters.length) {
|
|
149
|
+
case 0: {
|
|
150
|
+
watchState.emitters.push(emitter);
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
case 1: {
|
|
154
|
+
if (watchState.emitters[0][0] !== emitter[0])
|
|
155
|
+
watchState.emitters.push(emitter);
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
default: {
|
|
159
|
+
if (watchState.emitters[watchState.emitters.length - 1][0] !== emitter[0]) {
|
|
160
|
+
watchState.emitterIds ??= GetBitSetFor(watchState.emitters);
|
|
161
|
+
if (bitSet_1.BitSet.add(watchState.emitterIds, emitter[0]))
|
|
162
|
+
watchState.emitters.push(emitter);
|
|
163
|
+
}
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
179
167
|
break;
|
|
168
|
+
}
|
|
180
169
|
}
|
|
181
170
|
}
|
|
182
171
|
/**
|
|
@@ -201,47 +190,16 @@ function GetScopeValue(scope) {
|
|
|
201
190
|
return scope.value;
|
|
202
191
|
}
|
|
203
192
|
/**
|
|
204
|
-
*
|
|
205
|
-
*
|
|
193
|
+
* Global watch state representing the current dependency tracking context.
|
|
194
|
+
* This is set during scope execution and tracks which emitters/scopes are accessed.
|
|
195
|
+
* Nested watch contexts are supported via the parent reference pattern.
|
|
206
196
|
*/
|
|
207
|
-
const SAME_STRATEGY = 1;
|
|
208
|
-
const PUSH_STRATEGY = 2;
|
|
209
|
-
const SORTED_STRATEGY = 3;
|
|
210
|
-
const DISTINCT_STRATEGY = 4;
|
|
211
|
-
const SHRINK_STRATEGY = 5;
|
|
212
197
|
let watchState = null;
|
|
213
|
-
const watchPool = list_1.List.Create();
|
|
214
|
-
/**
|
|
215
|
-
* Creates a new watch state object, reusing from pool if available.
|
|
216
|
-
* Object pooling reduces GC pressure during frequent watch operations.
|
|
217
|
-
* @returns A new or recycled watch state object.
|
|
218
|
-
*/
|
|
219
|
-
function CreateWatchState() {
|
|
220
|
-
return (list_1.List.Pop(watchPool) ?? {
|
|
221
|
-
emitterIndex: 0,
|
|
222
|
-
value: null,
|
|
223
|
-
emitters: null,
|
|
224
|
-
emitterIds: null,
|
|
225
|
-
currentCalc: null,
|
|
226
|
-
nextCalc: null,
|
|
227
|
-
strategy: PUSH_STRATEGY,
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
198
|
/**
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
* @param state The watch state to return to the pool.
|
|
199
|
+
* Pool for recycling BitSet objects to reduce garbage collection overhead.
|
|
200
|
+
* BitSets are borrowed during watch operations and returned after use.
|
|
234
201
|
*/
|
|
235
|
-
|
|
236
|
-
state.emitterIndex = 0;
|
|
237
|
-
state.value = null;
|
|
238
|
-
state.emitters = null;
|
|
239
|
-
state.emitterIds = null;
|
|
240
|
-
state.currentCalc = null;
|
|
241
|
-
state.nextCalc = null;
|
|
242
|
-
state.strategy = SORTED_STRATEGY;
|
|
243
|
-
list_1.List.Push(watchPool, state);
|
|
244
|
-
}
|
|
202
|
+
const BIT_SET_POOL = bitSet_1.BitSetPool.Create();
|
|
245
203
|
/**
|
|
246
204
|
* Executes a callback while tracking all scope and emitter dependencies.
|
|
247
205
|
* Creates a watch context that records what was accessed during execution.
|
|
@@ -251,19 +209,18 @@ function ReturnWatchState(state) {
|
|
|
251
209
|
*/
|
|
252
210
|
function WatchFunction(callback, currentCalc, initialEmitters) {
|
|
253
211
|
const parent = watchState;
|
|
254
|
-
watchState =
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
212
|
+
watchState = {
|
|
213
|
+
emitterIndex: 0,
|
|
214
|
+
strategy: initialEmitters === null ? PUSH_STRATEGY : SAME_STRATEGY,
|
|
215
|
+
value: null,
|
|
216
|
+
emitters: initialEmitters ?? [],
|
|
217
|
+
emitterIds: null, // BitSetPool.Get(BIT_SET_POOL),
|
|
218
|
+
currentCalc,
|
|
219
|
+
nextCalc: null,
|
|
220
|
+
};
|
|
259
221
|
watchState.value = callback();
|
|
260
222
|
const resultState = watchState;
|
|
261
223
|
watchState = parent;
|
|
262
|
-
if (resultState.strategy === SAME_STRATEGY &&
|
|
263
|
-
resultState.emitterIndex < resultState.emitters.length) {
|
|
264
|
-
resultState.emitters = resultState.emitters.slice(0, resultState.emitterIndex);
|
|
265
|
-
resultState.strategy = SHRINK_STRATEGY;
|
|
266
|
-
}
|
|
267
224
|
return resultState;
|
|
268
225
|
}
|
|
269
226
|
/**
|
|
@@ -274,7 +231,11 @@ function WatchFunction(callback, currentCalc, initialEmitters) {
|
|
|
274
231
|
function ExecuteScope(scope) {
|
|
275
232
|
scope.dirty = false;
|
|
276
233
|
const state = WatchFunction(scope.getFunction, scope.calcScopes, scope.emitters);
|
|
277
|
-
|
|
234
|
+
if (state.strategy === SAME_STRATEGY &&
|
|
235
|
+
state.emitterIndex < state.emitters.length)
|
|
236
|
+
state.emitters = state.emitters.slice(0, state.emitterIndex);
|
|
237
|
+
UpdateEmitters(scope, state);
|
|
238
|
+
state.emitterIds && bitSet_1.BitSetPool.Return(BIT_SET_POOL, state.emitterIds);
|
|
278
239
|
const calcScopes = state.currentCalc;
|
|
279
240
|
scope.calcScopes = state.nextCalc;
|
|
280
241
|
for (const key in calcScopes)
|
|
@@ -286,7 +247,6 @@ function ExecuteScope(scope) {
|
|
|
286
247
|
});
|
|
287
248
|
else
|
|
288
249
|
scope.value = state.value;
|
|
289
|
-
ReturnWatchState(state);
|
|
290
250
|
}
|
|
291
251
|
/**
|
|
292
252
|
* Creates a scope from a function, choosing between static and dynamic based on dependencies.
|
|
@@ -302,17 +262,16 @@ function ExecuteFunction(callback, greedy, allowStatic) {
|
|
|
302
262
|
if (!allowStatic || async || state.emitters !== null) {
|
|
303
263
|
const scope = CreateDynamicScope(callback, greedy, async ? null : state.value);
|
|
304
264
|
scope.calcScopes = state.nextCalc;
|
|
305
|
-
UpdateEmitters(scope, state
|
|
265
|
+
UpdateEmitters(scope, state);
|
|
266
|
+
state.emitterIds && bitSet_1.BitSetPool.Return(BIT_SET_POOL, state.emitterIds);
|
|
306
267
|
if (async)
|
|
307
268
|
state.value.then(function (result) {
|
|
308
269
|
scope.value = result;
|
|
309
270
|
emitter_1.Emitter.Emit(scope.emitter, scope);
|
|
310
271
|
});
|
|
311
|
-
ReturnWatchState(state);
|
|
312
272
|
return scope;
|
|
313
273
|
}
|
|
314
274
|
const value = state.value;
|
|
315
|
-
ReturnWatchState(state);
|
|
316
275
|
return CreateStaticScope(value);
|
|
317
276
|
}
|
|
318
277
|
/**
|
|
@@ -335,7 +294,7 @@ function CalcScope(callback, idOverride) {
|
|
|
335
294
|
return callback();
|
|
336
295
|
const nextScopes = (watchState.nextCalc ??= {});
|
|
337
296
|
const id = idOverride ?? "default";
|
|
338
|
-
if (nextScopes
|
|
297
|
+
if (Object.hasOwn(nextScopes, id)) {
|
|
339
298
|
RegisterScope(nextScopes[id]);
|
|
340
299
|
return GetScopeValue(nextScopes[id]);
|
|
341
300
|
}
|
|
@@ -347,39 +306,31 @@ function CalcScope(callback, idOverride) {
|
|
|
347
306
|
return GetScopeValue(nextScopes[id]);
|
|
348
307
|
}
|
|
349
308
|
/**
|
|
350
|
-
* Updates a scope's
|
|
351
|
-
*
|
|
352
|
-
*
|
|
353
|
-
*
|
|
354
|
-
*
|
|
309
|
+
* Updates a scope's emitter subscriptions based on the new watch state.
|
|
310
|
+
* Efficiently reconciles old and new emitter lists starting from emitterIndex.
|
|
311
|
+
*
|
|
312
|
+
* Emitters before emitterIndex are in the same position and don't need updates.
|
|
313
|
+
* Emitters at/after emitterIndex are removed if no longer accessed or added if newly accessed.
|
|
314
|
+
*
|
|
315
|
+
* @param scope The scope to update emitter subscriptions for.
|
|
316
|
+
* @param state The watch state containing the new emitter list.
|
|
355
317
|
*/
|
|
356
|
-
function UpdateEmitters(scope,
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
318
|
+
function UpdateEmitters(scope, state) {
|
|
319
|
+
const currentEmitters = scope.emitters;
|
|
320
|
+
const nextEmitters = state.emitters;
|
|
321
|
+
const nextEmitterIds = state.emitterIds;
|
|
322
|
+
const sameIndex = state.emitterIndex;
|
|
323
|
+
if (currentEmitters !== null) {
|
|
324
|
+
for (let x = sameIndex; x < currentEmitters.length; x++) {
|
|
325
|
+
if (nextEmitterIds === null || !bitSet_1.BitSet.remove(nextEmitterIds, currentEmitters[x][0]))
|
|
326
|
+
emitter_1.Emitter.Remove(currentEmitters[x], scope.setCallback);
|
|
362
327
|
}
|
|
363
|
-
case PUSH_STRATEGY:
|
|
364
|
-
case DISTINCT_STRATEGY:
|
|
365
|
-
strategy === PUSH_STRATEGY
|
|
366
|
-
? emitter_1.Emitter.Distinct(right)
|
|
367
|
-
: emitter_1.Emitter.Sort(right);
|
|
368
|
-
case SORTED_STRATEGY:
|
|
369
|
-
if (scope.emitters === null || scope.emitters.length === 0) {
|
|
370
|
-
for (let x = 0; x < right.length; x++)
|
|
371
|
-
emitter_1.Emitter.On(right[x], scope.setCallback);
|
|
372
|
-
}
|
|
373
|
-
else {
|
|
374
|
-
(0, array_1.ReconcileSortedEmitters)(scope.emitters, right, function (emitter) {
|
|
375
|
-
emitter_1.Emitter.On(emitter, scope.setCallback);
|
|
376
|
-
}, function (emitter) {
|
|
377
|
-
emitter_1.Emitter.Remove(emitter, scope.setCallback);
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
break;
|
|
381
328
|
}
|
|
382
|
-
|
|
329
|
+
for (let x = sameIndex; x < nextEmitters.length; x++) {
|
|
330
|
+
if (nextEmitterIds === null || bitSet_1.BitSet.has(nextEmitterIds, nextEmitters[x][0]))
|
|
331
|
+
emitter_1.Emitter.On(nextEmitters[x], scope.setCallback);
|
|
332
|
+
}
|
|
333
|
+
scope.emitters = nextEmitters;
|
|
383
334
|
}
|
|
384
335
|
/**
|
|
385
336
|
* Destroys multiple scopes, cleaning up their resources.
|
|
@@ -411,6 +362,7 @@ function DestroyScope(scope) {
|
|
|
411
362
|
scope.setCallback = null;
|
|
412
363
|
scope.destroyed = true;
|
|
413
364
|
scope.onDestroyed && emitter_1.Emitter.Emit(scope.onDestroyed, scope);
|
|
365
|
+
scope.onDestroyed && emitter_1.Emitter.Destroy(scope.onDestroyed);
|
|
414
366
|
}
|
|
415
367
|
var ObservableScope;
|
|
416
368
|
(function (ObservableScope) {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type BitSet = {
|
|
2
|
+
init: boolean;
|
|
3
|
+
buffer: Uint32Array | null;
|
|
4
|
+
activeFrames: Set<number>;
|
|
5
|
+
minFrame: number;
|
|
6
|
+
maxFrame: number;
|
|
7
|
+
};
|
|
8
|
+
export type BitSetPool = {
|
|
9
|
+
pool: BitSet[];
|
|
10
|
+
index: number;
|
|
11
|
+
};
|
|
12
|
+
export declare namespace BitSetPool {
|
|
13
|
+
function Create(): BitSetPool;
|
|
14
|
+
function Get(pool: BitSetPool): BitSet;
|
|
15
|
+
function Return(pool: BitSetPool, bitSet: BitSet): void;
|
|
16
|
+
}
|
|
17
|
+
export declare namespace BitSet {
|
|
18
|
+
function Create(): BitSet;
|
|
19
|
+
function set(set: BitSet, num: number): void;
|
|
20
|
+
function has(set: BitSet, num: number): boolean;
|
|
21
|
+
function add(set: BitSet, num: number): boolean;
|
|
22
|
+
function remove(set: BitSet, num: number): boolean;
|
|
23
|
+
}
|
package/Utils/bitSet.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BitSet = exports.BitSetPool = void 0;
|
|
4
|
+
// 100000 = 31
|
|
5
|
+
const FRAME_BIT_SHIFT = 5;
|
|
6
|
+
const DEFAULT_FRAME_COUNT = 1024;
|
|
7
|
+
const DEFAULT_FRAME_HALF = DEFAULT_FRAME_COUNT / 2;
|
|
8
|
+
function GetFrame(number) {
|
|
9
|
+
// Effectively equivalent to Math.floor(number / 32)
|
|
10
|
+
return number >>> FRAME_BIT_SHIFT;
|
|
11
|
+
}
|
|
12
|
+
function GetBit(number) {
|
|
13
|
+
// JavaScript only uses rightmost 5 bits of num for this operation
|
|
14
|
+
// Effectively equivalent to number % 32
|
|
15
|
+
return 1 << number;
|
|
16
|
+
}
|
|
17
|
+
function Resize(set, frame) {
|
|
18
|
+
if (set.minFrame <= frame && frame < set.maxFrame)
|
|
19
|
+
return;
|
|
20
|
+
const currentSize = set.maxFrame - set.minFrame;
|
|
21
|
+
let newMin = Math.min(set.minFrame, frame);
|
|
22
|
+
let newMax = Math.max(set.maxFrame, frame + 1);
|
|
23
|
+
let newSize = newMax - newMin;
|
|
24
|
+
const minimumNewSize = currentSize * 2;
|
|
25
|
+
if (newSize < minimumNewSize) {
|
|
26
|
+
const growBy = minimumNewSize - newSize;
|
|
27
|
+
const growByHalf = growBy / 2;
|
|
28
|
+
newMin = newMin - Math.floor(growByHalf);
|
|
29
|
+
newMax = newMax + Math.ceil(growByHalf);
|
|
30
|
+
if (newMin < 0) {
|
|
31
|
+
newMax -= newMin;
|
|
32
|
+
newMin = 0;
|
|
33
|
+
}
|
|
34
|
+
newSize = newMax - newMin;
|
|
35
|
+
}
|
|
36
|
+
const newBuffer = new Uint32Array(newSize);
|
|
37
|
+
newBuffer.set(set.buffer, set.minFrame - newMin);
|
|
38
|
+
set.buffer = newBuffer;
|
|
39
|
+
set.minFrame = newMin;
|
|
40
|
+
set.maxFrame = newMax;
|
|
41
|
+
}
|
|
42
|
+
function Initialize(set, initFrameNumber) {
|
|
43
|
+
if (set.init)
|
|
44
|
+
return;
|
|
45
|
+
set.init = true;
|
|
46
|
+
set.activeFrames.clear();
|
|
47
|
+
const frame = GetFrame(initFrameNumber);
|
|
48
|
+
const sizeHalf = (set.buffer?.length ?? DEFAULT_FRAME_COUNT) / 2;
|
|
49
|
+
set.buffer ??= new Uint32Array(DEFAULT_FRAME_COUNT);
|
|
50
|
+
set.minFrame = frame - Math.floor(sizeHalf);
|
|
51
|
+
set.maxFrame = frame + Math.ceil(sizeHalf);
|
|
52
|
+
if (set.minFrame < 0) {
|
|
53
|
+
set.maxFrame -= set.minFrame;
|
|
54
|
+
set.minFrame = 0;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
var BitSetPool;
|
|
58
|
+
(function (BitSetPool) {
|
|
59
|
+
function Create() {
|
|
60
|
+
return {
|
|
61
|
+
pool: [],
|
|
62
|
+
index: 0
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
BitSetPool.Create = Create;
|
|
66
|
+
function Get(pool) {
|
|
67
|
+
return pool.index === 0 ? BitSet.Create() : pool.pool[--pool.index];
|
|
68
|
+
}
|
|
69
|
+
BitSetPool.Get = Get;
|
|
70
|
+
function Return(pool, bitSet) {
|
|
71
|
+
bitSet.init = false;
|
|
72
|
+
pool.pool[pool.index++] = bitSet;
|
|
73
|
+
}
|
|
74
|
+
BitSetPool.Return = Return;
|
|
75
|
+
})(BitSetPool || (exports.BitSetPool = BitSetPool = {}));
|
|
76
|
+
var BitSet;
|
|
77
|
+
(function (BitSet) {
|
|
78
|
+
function Create() {
|
|
79
|
+
return {
|
|
80
|
+
init: false,
|
|
81
|
+
buffer: null,
|
|
82
|
+
activeFrames: new Set(),
|
|
83
|
+
minFrame: -1,
|
|
84
|
+
maxFrame: -1,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
BitSet.Create = Create;
|
|
88
|
+
function set(set, num) {
|
|
89
|
+
Initialize(set, num);
|
|
90
|
+
const frame = GetFrame(num);
|
|
91
|
+
Resize(set, frame);
|
|
92
|
+
if (!set.activeFrames.has(frame)) {
|
|
93
|
+
set.buffer[frame] = 0;
|
|
94
|
+
set.activeFrames.add(frame);
|
|
95
|
+
}
|
|
96
|
+
const frameOffset = frame - set.minFrame;
|
|
97
|
+
set.buffer[frameOffset] |= GetBit(num);
|
|
98
|
+
}
|
|
99
|
+
BitSet.set = set;
|
|
100
|
+
function has(set, num) {
|
|
101
|
+
if (set.buffer === null)
|
|
102
|
+
return false;
|
|
103
|
+
const frame = GetFrame(num);
|
|
104
|
+
if (!set.activeFrames.has(frame))
|
|
105
|
+
return false;
|
|
106
|
+
if (frame < set.minFrame || set.maxFrame <= frame)
|
|
107
|
+
return false;
|
|
108
|
+
const frameOffset = frame - set.minFrame;
|
|
109
|
+
const frameValue = set.buffer[frameOffset] & GetBit(num);
|
|
110
|
+
return frameValue !== 0;
|
|
111
|
+
}
|
|
112
|
+
BitSet.has = has;
|
|
113
|
+
function add(set, num) {
|
|
114
|
+
Initialize(set, num);
|
|
115
|
+
const frame = GetFrame(num);
|
|
116
|
+
Resize(set, frame);
|
|
117
|
+
if (!set.activeFrames.has(frame)) {
|
|
118
|
+
set.buffer[frame] = 0;
|
|
119
|
+
set.activeFrames.add(frame);
|
|
120
|
+
}
|
|
121
|
+
const frameOffset = frame - set.minFrame;
|
|
122
|
+
const frameStart = set.buffer[frameOffset];
|
|
123
|
+
set.buffer[frameOffset] |= GetBit(num);
|
|
124
|
+
return frameStart !== set.buffer[frameOffset];
|
|
125
|
+
}
|
|
126
|
+
BitSet.add = add;
|
|
127
|
+
function remove(set, num) {
|
|
128
|
+
if (set.buffer === null)
|
|
129
|
+
return false;
|
|
130
|
+
const frame = GetFrame(num);
|
|
131
|
+
if (!set.activeFrames.has(frame) || frame < set.minFrame || set.maxFrame <= frame)
|
|
132
|
+
return false;
|
|
133
|
+
const frameOffset = frame - set.minFrame;
|
|
134
|
+
const frameStart = set.buffer[frameOffset];
|
|
135
|
+
set.buffer[frameOffset] ^= GetBit(num);
|
|
136
|
+
return frameStart !== set.buffer[frameOffset];
|
|
137
|
+
}
|
|
138
|
+
BitSet.remove = remove;
|
|
139
|
+
})(BitSet || (exports.BitSet = BitSet = {}));
|
package/Utils/emitter.d.ts
CHANGED
|
@@ -8,7 +8,4 @@ export declare namespace Emitter {
|
|
|
8
8
|
function Destroy(emitter: Emitter): void;
|
|
9
9
|
function Remove(emitter: Emitter, callback: EmitterCallback): void;
|
|
10
10
|
function Clear(emitter: Emitter): void;
|
|
11
|
-
function Distinct(emitters: Emitter[]): void;
|
|
12
|
-
function Sort(emitters: Emitter[]): void;
|
|
13
|
-
function Compare(a: Emitter, b: Emitter): number;
|
|
14
11
|
}
|
package/Utils/emitter.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Emitter = void 0;
|
|
4
|
-
const
|
|
4
|
+
const idPool = [];
|
|
5
|
+
let idPoolIndex = 0;
|
|
6
|
+
let globalId = 0;
|
|
5
7
|
var Emitter;
|
|
6
8
|
(function (Emitter) {
|
|
7
|
-
let globalId = 0;
|
|
8
9
|
function Create() {
|
|
9
|
-
const
|
|
10
|
+
const emitterId = idPoolIndex === 0 ? ++globalId : idPool[--idPoolIndex];
|
|
11
|
+
const emitter = [emitterId];
|
|
10
12
|
return emitter;
|
|
11
13
|
}
|
|
12
14
|
Emitter.Create = Create;
|
|
@@ -33,6 +35,7 @@ var Emitter;
|
|
|
33
35
|
}
|
|
34
36
|
Emitter.Emit = Emit;
|
|
35
37
|
function Destroy(emitter) {
|
|
38
|
+
idPool[idPoolIndex++] = emitter[0];
|
|
36
39
|
emitter[0] = -1;
|
|
37
40
|
}
|
|
38
41
|
Emitter.Destroy = Destroy;
|
|
@@ -48,30 +51,4 @@ var Emitter;
|
|
|
48
51
|
emitter.splice(1);
|
|
49
52
|
}
|
|
50
53
|
Emitter.Clear = Clear;
|
|
51
|
-
function Distinct(emitters) {
|
|
52
|
-
if (emitters.length < 2)
|
|
53
|
-
return;
|
|
54
|
-
Sort(emitters);
|
|
55
|
-
let writePos = 0;
|
|
56
|
-
for (let x = 1; x < emitters.length; x++) {
|
|
57
|
-
if (emitters[x][0] !== emitters[writePos][0]) {
|
|
58
|
-
emitters[++writePos] = emitters[x];
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
writePos++;
|
|
62
|
-
if (writePos < emitters.length)
|
|
63
|
-
emitters.splice(writePos);
|
|
64
|
-
}
|
|
65
|
-
Emitter.Distinct = Distinct;
|
|
66
|
-
function Sort(emitters) {
|
|
67
|
-
if (emitters.length < 11)
|
|
68
|
-
(0, array_1.InsertionSortTuples)(emitters);
|
|
69
|
-
else
|
|
70
|
-
emitters.sort(Compare);
|
|
71
|
-
}
|
|
72
|
-
Emitter.Sort = Sort;
|
|
73
|
-
function Compare(a, b) {
|
|
74
|
-
return a[0] - b[0];
|
|
75
|
-
}
|
|
76
|
-
Emitter.Compare = Compare;
|
|
77
54
|
})(Emitter || (exports.Emitter = Emitter = {}));
|