@woosh/meep-engine 2.104.0 → 2.106.0
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/bundle-worker-image-decoder.js +1 -1
- package/build/bundle-worker-terrain.js +1 -1
- package/build/meep.cjs +131 -85
- package/build/meep.min.js +1 -1
- package/build/meep.module.js +131 -85
- package/editor/tools/v2/BlenderCameraOrientationGizmo.js +1 -1
- package/package.json +1 -1
- package/src/core/assert.d.ts.map +1 -1
- package/src/core/assert.js +3 -1
- package/src/core/bvh2/bvh3/build_triangle_morton_codes.d.ts +1 -1
- package/src/core/bvh2/bvh3/build_triangle_morton_codes.d.ts.map +1 -1
- package/src/core/bvh2/bvh3/build_triangle_morton_codes.js +3 -1
- package/src/core/bvh2/bvh3/ebvh_build_hierarchy.d.ts +1 -0
- package/src/core/bvh2/bvh3/ebvh_build_hierarchy.d.ts.map +1 -1
- package/src/core/bvh2/bvh3/ebvh_build_hierarchy.js +3 -2
- package/src/core/cache/Cache.d.ts +2 -1
- package/src/core/cache/Cache.d.ts.map +1 -1
- package/src/core/cache/Cache.js +68 -41
- package/src/core/cache/Cache.spec.js +42 -0
- package/src/core/cache/CacheElement.d.ts +1 -0
- package/src/core/cache/CacheElement.d.ts.map +1 -1
- package/src/core/cache/CacheElement.js +4 -0
- package/src/core/codegen/LineBuilder.d.ts +1 -24
- package/src/core/codegen/LineBuilder.d.ts.map +1 -1
- package/src/core/codegen/LineBuilder.js +19 -15
- package/src/core/collection/map/HashMap.d.ts.map +1 -1
- package/src/core/collection/map/HashMap.js +48 -30
- package/src/core/model/node-graph/NodeGraph.js +2 -2
- package/src/engine/graphics/ecs/mesh/Mesh.js +1 -1
- package/src/engine/graphics/geometry/buffered/query/GeometrySpatialQueryAccelerator.js +1 -1
package/src/core/cache/Cache.js
CHANGED
|
@@ -7,14 +7,21 @@ import { returnZero } from "../function/returnZero.js";
|
|
|
7
7
|
import { strictEquals } from "../function/strictEquals.js";
|
|
8
8
|
import { CacheElement } from "./CacheElement.js";
|
|
9
9
|
|
|
10
|
-
// TODO validate hashes of held elements to keep them up to date. Keys are assumed to be immutable, but this assumption may be broken by users
|
|
11
|
-
|
|
12
10
|
/**
|
|
13
11
|
* Hash-based cache, uses LRU (least-recently-used) eviction policy
|
|
12
|
+
* Make sure that keys being used are truly immutable when it comes to hash and equality calculation, otherwise cache corruption is inevitable
|
|
14
13
|
* @template Key, Value
|
|
15
14
|
* @extends Map<Key,Value>
|
|
16
15
|
*/
|
|
17
16
|
export class Cache {
|
|
17
|
+
#maxWeight = Number.POSITIVE_INFINITY;
|
|
18
|
+
/**
|
|
19
|
+
*
|
|
20
|
+
* @type {number}
|
|
21
|
+
* @private
|
|
22
|
+
*/
|
|
23
|
+
#weight = 0;
|
|
24
|
+
|
|
18
25
|
/**
|
|
19
26
|
* @param {number} [maxWeight=Number.POSITIVE_INFINITY]
|
|
20
27
|
* @param {function(key:Key):number} [keyWeigher= key=>0]
|
|
@@ -44,14 +51,8 @@ export class Cache {
|
|
|
44
51
|
* @type {number}
|
|
45
52
|
* @private
|
|
46
53
|
*/
|
|
47
|
-
this
|
|
54
|
+
this.#maxWeight = maxWeight;
|
|
48
55
|
|
|
49
|
-
/**
|
|
50
|
-
*
|
|
51
|
-
* @type {number}
|
|
52
|
-
* @private
|
|
53
|
-
*/
|
|
54
|
-
this.weight = 0;
|
|
55
56
|
|
|
56
57
|
/**
|
|
57
58
|
*
|
|
@@ -152,18 +153,37 @@ export class Cache {
|
|
|
152
153
|
return this.data.size;
|
|
153
154
|
}
|
|
154
155
|
|
|
156
|
+
/**
|
|
157
|
+
* @deprecated use {@link maxWeight} directly instead
|
|
158
|
+
*/
|
|
159
|
+
setMaxWeight(value) {
|
|
160
|
+
this.maxWeight = value;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Total weight of all elements currently in the cache
|
|
165
|
+
* @returns {number}
|
|
166
|
+
*/
|
|
167
|
+
get weight() {
|
|
168
|
+
return this.#weight;
|
|
169
|
+
}
|
|
170
|
+
|
|
155
171
|
/**
|
|
156
172
|
* Will cause evictions if current weight is smaller than what we're setting
|
|
157
173
|
* @param {number} value
|
|
158
174
|
*/
|
|
159
|
-
|
|
175
|
+
set maxWeight(value) {
|
|
160
176
|
if (typeof value !== "number" || value < 0) {
|
|
161
177
|
throw new Error(`Weight must be a non-negative number, instead was '${value}'`);
|
|
162
178
|
}
|
|
163
179
|
|
|
164
|
-
this
|
|
180
|
+
this.#maxWeight = value;
|
|
181
|
+
|
|
182
|
+
this.evictUntilWeight(this.#maxWeight);
|
|
183
|
+
}
|
|
165
184
|
|
|
166
|
-
|
|
185
|
+
get maxWeight() {
|
|
186
|
+
return this.#maxWeight;
|
|
167
187
|
}
|
|
168
188
|
|
|
169
189
|
/**
|
|
@@ -182,7 +202,8 @@ export class Cache {
|
|
|
182
202
|
result += weight;
|
|
183
203
|
}
|
|
184
204
|
|
|
185
|
-
this
|
|
205
|
+
this.#weight = result;
|
|
206
|
+
this.evictUntilWeight(this.#maxWeight);
|
|
186
207
|
}
|
|
187
208
|
|
|
188
209
|
/**
|
|
@@ -212,13 +233,13 @@ export class Cache {
|
|
|
212
233
|
|
|
213
234
|
const delta_weight = new_weight - old_weight;
|
|
214
235
|
|
|
215
|
-
this
|
|
236
|
+
this.#weight += delta_weight;
|
|
216
237
|
|
|
217
238
|
if (
|
|
218
|
-
this
|
|
219
|
-
&& new_weight <= this
|
|
239
|
+
this.#weight > this.#maxWeight
|
|
240
|
+
&& new_weight <= this.#maxWeight //make it less likely to drop entire cache
|
|
220
241
|
) {
|
|
221
|
-
this.evictUntilWeight(this
|
|
242
|
+
this.evictUntilWeight(this.#maxWeight);
|
|
222
243
|
}
|
|
223
244
|
|
|
224
245
|
return true;
|
|
@@ -259,7 +280,9 @@ export class Cache {
|
|
|
259
280
|
const victim = this.findEvictionVictim();
|
|
260
281
|
|
|
261
282
|
if (victim !== null) {
|
|
262
|
-
this.remove(victim.key);
|
|
283
|
+
const removed_from_hash_table = this.remove(victim.key);
|
|
284
|
+
|
|
285
|
+
assert.ok(removed_from_hash_table, `Failed to remove key '${victim.key}', likely reasons:\n\t1. key was mutated (keys must never be mutated)\n\t2. provided hashing function is unstable\n\t3. provided equality function is inconsistent`);
|
|
263
286
|
|
|
264
287
|
this.onEvicted.send2(victim.key, victim.value);
|
|
265
288
|
|
|
@@ -280,7 +303,7 @@ export class Cache {
|
|
|
280
303
|
|
|
281
304
|
const target = Math.max(targetWeight, 0);
|
|
282
305
|
|
|
283
|
-
while (this
|
|
306
|
+
while (this.#weight > target) {
|
|
284
307
|
this.evictOne();
|
|
285
308
|
}
|
|
286
309
|
}
|
|
@@ -294,6 +317,22 @@ export class Cache {
|
|
|
294
317
|
let element = this.data.get(key);
|
|
295
318
|
|
|
296
319
|
if (element === undefined) {
|
|
320
|
+
//compute weight
|
|
321
|
+
const elementWeight = this.computeElementWeight(key, value);
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* It's possible that element being added is larger than cache's capacity,
|
|
325
|
+
* in which case entire cache will be evicted, but there still won't be enough space
|
|
326
|
+
* @type {number}
|
|
327
|
+
*/
|
|
328
|
+
const weightTarget = this.#maxWeight - elementWeight;
|
|
329
|
+
|
|
330
|
+
if (weightTarget < 0) {
|
|
331
|
+
// Special case
|
|
332
|
+
// element does not fit into cache, attempting to insert it forcibly would result in a full flush and overflow
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
297
336
|
element = new CacheElement();
|
|
298
337
|
|
|
299
338
|
element.key = key;
|
|
@@ -311,25 +350,8 @@ export class Cache {
|
|
|
311
350
|
this.__last = element;
|
|
312
351
|
}
|
|
313
352
|
|
|
314
|
-
//compute weight
|
|
315
|
-
const elementWeight = this.computeElementWeight(key, value);
|
|
316
|
-
|
|
317
353
|
element.weight = elementWeight;
|
|
318
354
|
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* It's possible that element being added is larger than cache's capacity,
|
|
322
|
-
* in which case entire cache will be evicted, but there still won't be enough space
|
|
323
|
-
* @type {number}
|
|
324
|
-
*/
|
|
325
|
-
const weightTarget = this.maxWeight - elementWeight;
|
|
326
|
-
|
|
327
|
-
if (weightTarget < 0) {
|
|
328
|
-
// Special case
|
|
329
|
-
// element does not fit into cache, attempting to insert it forcibly would result in a full flush and overflow
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
355
|
//evict elements until there is enough space for the element
|
|
334
356
|
this.evictUntilWeight(weightTarget);
|
|
335
357
|
|
|
@@ -337,16 +359,21 @@ export class Cache {
|
|
|
337
359
|
this.data.set(key, element);
|
|
338
360
|
|
|
339
361
|
//update weight
|
|
340
|
-
this
|
|
362
|
+
this.#weight += elementWeight;
|
|
341
363
|
} else {
|
|
342
364
|
// check if value is the same
|
|
343
365
|
if (value === element.value) {
|
|
344
366
|
// same value, no action required
|
|
345
367
|
} else {
|
|
346
368
|
// replace value, adjust weight
|
|
347
|
-
this
|
|
348
|
-
|
|
369
|
+
this.#weight -= element.weight;
|
|
370
|
+
|
|
371
|
+
const elementWeight = this.computeElementWeight(key, value);
|
|
372
|
+
|
|
373
|
+
this.#weight += elementWeight;
|
|
349
374
|
|
|
375
|
+
// assign new values
|
|
376
|
+
element.weight = elementWeight;
|
|
350
377
|
element.value = value;
|
|
351
378
|
}
|
|
352
379
|
|
|
@@ -431,7 +458,7 @@ export class Cache {
|
|
|
431
458
|
this.data.delete(key);
|
|
432
459
|
|
|
433
460
|
//update weight
|
|
434
|
-
this
|
|
461
|
+
this.#weight -= element.weight;
|
|
435
462
|
}
|
|
436
463
|
|
|
437
464
|
/**
|
|
@@ -503,7 +530,7 @@ export class Cache {
|
|
|
503
530
|
this.__first = null;
|
|
504
531
|
this.__last = null;
|
|
505
532
|
|
|
506
|
-
this
|
|
533
|
+
this.#weight = 0;
|
|
507
534
|
}
|
|
508
535
|
|
|
509
536
|
/**
|
|
@@ -32,6 +32,19 @@ test("trying to insert an element into the cache that's larger than the cache ca
|
|
|
32
32
|
expect(cache.contains(2)).toBe(false);
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
+
test("Recently accessed element should not be targeted for eviction where possible", () => {
|
|
36
|
+
const cache = new Cache();
|
|
37
|
+
|
|
38
|
+
cache.put(2, "hello");
|
|
39
|
+
cache.put(7, "world");
|
|
40
|
+
|
|
41
|
+
cache.get(2);
|
|
42
|
+
|
|
43
|
+
cache.evictOne();
|
|
44
|
+
|
|
45
|
+
expect(cache.has(2)).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
|
|
35
48
|
test("removeListener is called when element is evicted", () => {
|
|
36
49
|
|
|
37
50
|
const removeListener = jest.fn();
|
|
@@ -233,3 +246,32 @@ test("silentRemove should not notify", () => {
|
|
|
233
246
|
|
|
234
247
|
expect(mock).not.toHaveBeenCalled();
|
|
235
248
|
});
|
|
249
|
+
|
|
250
|
+
test("setting maxWeight will drop data and update weight", () => {
|
|
251
|
+
|
|
252
|
+
const cache = new Cache();
|
|
253
|
+
|
|
254
|
+
cache.set("a", 1);
|
|
255
|
+
cache.set("b", 2);
|
|
256
|
+
cache.set("c", 3);
|
|
257
|
+
|
|
258
|
+
expect(cache.weight).toEqual(3);
|
|
259
|
+
|
|
260
|
+
cache.maxWeight = 1;
|
|
261
|
+
|
|
262
|
+
expect(cache.maxWeight).toEqual(1);
|
|
263
|
+
expect(cache.weight).toEqual(1);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test("setting maxWeight to 0 will empty out cache", () => {
|
|
267
|
+
|
|
268
|
+
const cache = new Cache();
|
|
269
|
+
|
|
270
|
+
cache.set("a", 1);
|
|
271
|
+
cache.set("b", 2);
|
|
272
|
+
|
|
273
|
+
cache.maxWeight = 0;
|
|
274
|
+
|
|
275
|
+
expect(cache.size()).toBe(0);
|
|
276
|
+
|
|
277
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CacheElement.d.ts","sourceRoot":"","sources":["../../../../src/core/cache/CacheElement.js"],"names":[],"mappings":"AAAA;;;;GAIG;AACH;IAMQ;;;OAGG;IACH,WAAe;IAEf;;;OAGG;IACH,eAAiB;IAEjB;;;OAGG;IACH,QAFU,MAAM,CAED;IAEf;;;OAGG;IACH,mCAAgB;IAEhB;;;OAGG;IACH,uCAAoB;IAGxB,eAWC;CACJ"}
|
|
1
|
+
{"version":3,"file":"CacheElement.d.ts","sourceRoot":"","sources":["../../../../src/core/cache/CacheElement.js"],"names":[],"mappings":"AAAA;;;;GAIG;AACH;IAMQ;;;OAGG;IACH,WAAe;IAEf;;;OAGG;IACH,eAAiB;IAEjB;;;OAGG;IACH,QAFU,MAAM,CAED;IAEf;;;OAGG;IACH,mCAAgB;IAEhB;;;OAGG;IACH,uCAAoB;IAGxB,eAWC;IAED,mBAEC;CACJ"}
|
|
@@ -9,16 +9,6 @@ declare class LineBuilder {
|
|
|
9
9
|
* @returns {LineBuilder}
|
|
10
10
|
*/
|
|
11
11
|
static fromText(text: string): LineBuilder;
|
|
12
|
-
/**
|
|
13
|
-
*
|
|
14
|
-
* @type {Line[]}
|
|
15
|
-
*/
|
|
16
|
-
lines: Line[];
|
|
17
|
-
/**
|
|
18
|
-
*
|
|
19
|
-
* @type {number}
|
|
20
|
-
*/
|
|
21
|
-
indentation: number;
|
|
22
12
|
/**
|
|
23
13
|
*
|
|
24
14
|
* @type {number}
|
|
@@ -62,19 +52,6 @@ declare class LineBuilder {
|
|
|
62
52
|
* @returns {string}
|
|
63
53
|
*/
|
|
64
54
|
build(): string;
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Created by Alex on 08/06/2015.
|
|
68
|
-
*/
|
|
69
|
-
declare class Line {
|
|
70
|
-
/**
|
|
71
|
-
*
|
|
72
|
-
* @param {string} text
|
|
73
|
-
* @param {number} indent
|
|
74
|
-
* @constructor
|
|
75
|
-
*/
|
|
76
|
-
constructor(text: string, indent: number);
|
|
77
|
-
text: string;
|
|
78
|
-
indentation: number;
|
|
55
|
+
#private;
|
|
79
56
|
}
|
|
80
57
|
//# sourceMappingURL=LineBuilder.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LineBuilder.d.ts","sourceRoot":"","sources":["../../../../src/core/codegen/LineBuilder.js"],"names":[],"mappings":";AAoBA;;GAEG;AACH;
|
|
1
|
+
{"version":3,"file":"LineBuilder.d.ts","sourceRoot":"","sources":["../../../../src/core/codegen/LineBuilder.js"],"names":[],"mappings":";AAoBA;;GAEG;AACH;IAoII;;;;OAIG;IACH,sBAHW,MAAM,GACJ,WAAW,CAoBvB;IA7ID;;;OAGG;IACH,cAFU,MAAM,CAEqB;IAErC;;;OAGG;IACH,oBAEC;IAED;;;;OAIG;IACH,wBAFW,MAAM,GADL,OAAO,CAiBlB;IAED;;;OAGG;IACH,UAFa,WAAW,CAKvB;IAED;;;OAGG;IACH,UAFa,WAAW,CAKvB;IAED;;;;OAIG;IACH,eAHW,MAAM,GACJ,WAAW,CASvB;IAED;;;OAGG;IACH,gBAFW,WAAW,QAgBrB;IAED,cAGC;IAED;;;OAGG;IACH,SAFa,MAAM,CAsBlB;;CA0BJ"}
|
|
@@ -27,12 +27,14 @@ class LineBuilder {
|
|
|
27
27
|
*
|
|
28
28
|
* @type {Line[]}
|
|
29
29
|
*/
|
|
30
|
-
lines = [];
|
|
30
|
+
#lines = [];
|
|
31
|
+
|
|
31
32
|
/**
|
|
32
33
|
*
|
|
33
34
|
* @type {number}
|
|
34
35
|
*/
|
|
35
|
-
indentation = 0;
|
|
36
|
+
#indentation = 0;
|
|
37
|
+
|
|
36
38
|
/**
|
|
37
39
|
*
|
|
38
40
|
* @type {number}
|
|
@@ -44,7 +46,7 @@ class LineBuilder {
|
|
|
44
46
|
* @return {number}
|
|
45
47
|
*/
|
|
46
48
|
get count() {
|
|
47
|
-
return this
|
|
49
|
+
return this.#lines.length;
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
/**
|
|
@@ -53,7 +55,7 @@ class LineBuilder {
|
|
|
53
55
|
* @param {string} term
|
|
54
56
|
*/
|
|
55
57
|
containsSubstring(term) {
|
|
56
|
-
const lines = this
|
|
58
|
+
const lines = this.#lines;
|
|
57
59
|
const n = lines.length;
|
|
58
60
|
for (let i = 0; i < n; i++) {
|
|
59
61
|
const line = lines[i];
|
|
@@ -73,7 +75,7 @@ class LineBuilder {
|
|
|
73
75
|
* @returns {LineBuilder}
|
|
74
76
|
*/
|
|
75
77
|
indent() {
|
|
76
|
-
this
|
|
78
|
+
this.#indentation++;
|
|
77
79
|
return this;
|
|
78
80
|
}
|
|
79
81
|
|
|
@@ -82,7 +84,7 @@ class LineBuilder {
|
|
|
82
84
|
* @returns {LineBuilder}
|
|
83
85
|
*/
|
|
84
86
|
dedent() {
|
|
85
|
-
this
|
|
87
|
+
this.#indentation--;
|
|
86
88
|
return this;
|
|
87
89
|
}
|
|
88
90
|
|
|
@@ -93,9 +95,9 @@ class LineBuilder {
|
|
|
93
95
|
*/
|
|
94
96
|
add(line_text) {
|
|
95
97
|
|
|
96
|
-
const line = new Line(line_text, this
|
|
98
|
+
const line = new Line(line_text, this.#indentation);
|
|
97
99
|
|
|
98
|
-
this
|
|
100
|
+
this.#lines.push(line);
|
|
99
101
|
|
|
100
102
|
return this;
|
|
101
103
|
}
|
|
@@ -106,23 +108,23 @@ class LineBuilder {
|
|
|
106
108
|
*/
|
|
107
109
|
addLines(lines) {
|
|
108
110
|
|
|
109
|
-
const other_lines = lines
|
|
111
|
+
const other_lines = lines.#lines;
|
|
110
112
|
|
|
111
113
|
const other_line_count = other_lines.length;
|
|
112
114
|
|
|
113
115
|
for (let i = 0; i < other_line_count; i++) {
|
|
114
116
|
const otherLine = other_lines[i];
|
|
115
117
|
|
|
116
|
-
const line = new Line(otherLine.text, otherLine.indentation + this
|
|
118
|
+
const line = new Line(otherLine.text, otherLine.indentation + this.#indentation);
|
|
117
119
|
|
|
118
|
-
this
|
|
120
|
+
this.#lines.push(line);
|
|
119
121
|
}
|
|
120
122
|
|
|
121
123
|
}
|
|
122
124
|
|
|
123
125
|
clear() {
|
|
124
|
-
this
|
|
125
|
-
this
|
|
126
|
+
this.#lines = [];
|
|
127
|
+
this.#indentation = 0;
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
/**
|
|
@@ -134,7 +136,7 @@ class LineBuilder {
|
|
|
134
136
|
|
|
135
137
|
let i, j, l;
|
|
136
138
|
|
|
137
|
-
const lines = this
|
|
139
|
+
const lines = this.#lines;
|
|
138
140
|
|
|
139
141
|
for (i = 0, l = lines.length; i < l; i++) {
|
|
140
142
|
const line = lines[i];
|
|
@@ -167,7 +169,9 @@ class LineBuilder {
|
|
|
167
169
|
|
|
168
170
|
for (let i = 0; i < n; i++) {
|
|
169
171
|
|
|
170
|
-
|
|
172
|
+
const line_text = lines[i];
|
|
173
|
+
|
|
174
|
+
r.add(line_text);
|
|
171
175
|
|
|
172
176
|
}
|
|
173
177
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HashMap.d.ts","sourceRoot":"","sources":["../../../../../src/core/collection/map/HashMap.js"],"names":[],"mappings":"AAiBA;;;;;;;;;;;;GAYG;AACH,uFAIC;
|
|
1
|
+
{"version":3,"file":"HashMap.d.ts","sourceRoot":"","sources":["../../../../../src/core/collection/map/HashMap.js"],"names":[],"mappings":"AAiBA;;;;;;;;;;;;GAYG;AACH,uFAIC;AA8FD;;;;;;;GAOG;AACH;IA+DI;;;;;;OAMG;IACH,4FALuB,MAAM,EAsC5B;IAlBG;;;;;OAKG;IACH,iCAAsC;IACtC;;;;;OAKG;IACH,qCAA8C;IAOlD,mBAEC;IAED;;;OAGG;IACH,kBAFa,MAAM,CAIlB;IAuJD;;;;OAIG;IACH,SAHW,CAAC,SACD,CAAC,QA0EX;IAED;;;;OAIG;IACH,SAHW,CAAC,GACC,CAAC,GAAC,SAAS,CA+BvB;IAED;;;;;;;;OAQG;IACH,kBALW,CAAC,kBACQ,CAAC,KAAE,CAAC,0BAEZ,CAAC,CAcZ;IAED;;;;;OAKG;IACH,cAJW,CAAC,SACD,CAAC,GACA,CAAC,CAYZ;IAuBD;;;;OAIG;IACH,YAHW,CAAC,GACC,OAAO,CA+CnB;IAED;;;;;OAKG;IACH,4CAFa,OAAO,CA+BnB;IAOD;;OAEG;IACH,gBAsDC;IAmBD,2CAwBC;IAED;;;;OAIG;IACH,SAHW,CAAC,GACC,OAAO,CAInB;IAED;;OAEG;IACH,cA6BC;IA+BD;;;OAGG;IACH,UAFa,SAAS,CAAC,CAAC,CAOvB;IAED;;;OAGG;IACH,QAFa,SAAS,CAAC,CAAC,CAMvB;IAhDD,yDA2BC;;CAsBJ"}
|
|
@@ -124,30 +124,6 @@ const RESERVED_HASH_SUBSTITUTE = 0;
|
|
|
124
124
|
const UNDEFINED_BIN_INDEX = ~0;
|
|
125
125
|
|
|
126
126
|
|
|
127
|
-
/**
|
|
128
|
-
* @template K,V
|
|
129
|
-
* @param {HashMapEntry<K,V>} record
|
|
130
|
-
* @param {number} hash
|
|
131
|
-
* @param {K} key
|
|
132
|
-
* @param {function(a:K,b:K):boolean} equality_op
|
|
133
|
-
*/
|
|
134
|
-
function entry_equality_check(record, hash, key, equality_op) {
|
|
135
|
-
if (record.hash !== hash) {
|
|
136
|
-
return false;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (record.key === key) {
|
|
140
|
-
return true;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const result = equality_op(record.key, key);
|
|
144
|
-
|
|
145
|
-
assert.isBoolean(result, `result(a=${record.key},b=${key})`);
|
|
146
|
-
|
|
147
|
-
return result;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
|
|
151
127
|
const EMPTY_BINS = new Uint32Array(0);
|
|
152
128
|
|
|
153
129
|
/**
|
|
@@ -215,6 +191,10 @@ export class HashMap {
|
|
|
215
191
|
*/
|
|
216
192
|
#load_factor = DEFAULT_LOAD_FACTOR;
|
|
217
193
|
|
|
194
|
+
/**
|
|
195
|
+
* Used to track modifications to prevent concurrent changes during iteration
|
|
196
|
+
* @type {number}
|
|
197
|
+
*/
|
|
218
198
|
#version = 0;
|
|
219
199
|
|
|
220
200
|
/**
|
|
@@ -291,6 +271,8 @@ export class HashMap {
|
|
|
291
271
|
this.#bin_count = 2 ** this.#bin_count_power_of_two;
|
|
292
272
|
this.#bin_count_mask = this.#bin_count - 1;
|
|
293
273
|
|
|
274
|
+
const old_entry_allocation_count = this.#entries_allocated_count;
|
|
275
|
+
|
|
294
276
|
this.#entries_allocated_count = 2 ** this.#entries_count_power_of_two;
|
|
295
277
|
|
|
296
278
|
const BinsArray = UintArrayForCount(this.#entries_allocated_count + ENTRY_BASE);
|
|
@@ -302,7 +284,7 @@ export class HashMap {
|
|
|
302
284
|
|
|
303
285
|
this.#entries = new_entries;
|
|
304
286
|
|
|
305
|
-
array_copy(old_entries, 0, new_entries, 0, min2(
|
|
287
|
+
array_copy(old_entries, 0, new_entries, 0, min2(old_entry_allocation_count, this.#entries_allocated_count));
|
|
306
288
|
|
|
307
289
|
if (this.#size > 0) {
|
|
308
290
|
// re-hash
|
|
@@ -339,6 +321,29 @@ export class HashMap {
|
|
|
339
321
|
return original === RESERVED_HASH ? RESERVED_HASH_SUBSTITUTE : original;
|
|
340
322
|
}
|
|
341
323
|
|
|
324
|
+
/**
|
|
325
|
+
* @param {HashMapEntry<K,V>} record
|
|
326
|
+
* @param {number} hash
|
|
327
|
+
* @param {K} key
|
|
328
|
+
*/
|
|
329
|
+
#entry_equality_check(record, hash, key) {
|
|
330
|
+
if (record.hash !== hash) {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (record.key === key) {
|
|
335
|
+
return true;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
assert.equal(record.hash, this.#build_key_hash(record.key), `Key hash has diverged for key ${record.key}, likely key was mutated or hash function is unstable`);
|
|
339
|
+
|
|
340
|
+
const result = this.keyEqualityFunction(record.key, key);
|
|
341
|
+
|
|
342
|
+
assert.isBoolean(result, `result(a=${record.key},b=${key})`);
|
|
343
|
+
|
|
344
|
+
return result;
|
|
345
|
+
}
|
|
346
|
+
|
|
342
347
|
/**
|
|
343
348
|
*
|
|
344
349
|
* @param {K} k
|
|
@@ -354,6 +359,9 @@ export class HashMap {
|
|
|
354
359
|
|
|
355
360
|
if (this.#entries[i] !== undefined) {
|
|
356
361
|
const entry = this.#entries[i];
|
|
362
|
+
|
|
363
|
+
assert.equal(entry.hash, RESERVED_HASH, 'Entry is occupied');
|
|
364
|
+
|
|
357
365
|
entry.hash = hash;
|
|
358
366
|
entry.key = k;
|
|
359
367
|
entry.value = v;
|
|
@@ -402,6 +410,7 @@ export class HashMap {
|
|
|
402
410
|
|
|
403
411
|
const raw_hash = this.#build_key_hash(key);
|
|
404
412
|
let bin_index = this.#compute_bin_index(raw_hash);
|
|
413
|
+
|
|
405
414
|
assert.isFiniteNumber(bin_index, 'hash');
|
|
406
415
|
|
|
407
416
|
let first_deleted_bin_index = UNDEFINED_BIN_INDEX;
|
|
@@ -415,11 +424,12 @@ export class HashMap {
|
|
|
415
424
|
// check if it's the entry that we're looking for
|
|
416
425
|
const entry = this.#entries[bin - ENTRY_BASE];
|
|
417
426
|
|
|
418
|
-
if (entry_equality_check(entry, raw_hash, key
|
|
427
|
+
if (this.#entry_equality_check(entry, raw_hash, key)) {
|
|
419
428
|
// found the right entry
|
|
420
429
|
entry.value = value;
|
|
421
430
|
return;
|
|
422
431
|
}
|
|
432
|
+
|
|
423
433
|
} else if (bin === BIN_RESERVED_VALUE_EMPTY) {
|
|
424
434
|
// bin is empty
|
|
425
435
|
|
|
@@ -436,7 +446,11 @@ export class HashMap {
|
|
|
436
446
|
assert.equal(this.#entries[entry_index].value, value, 'entry.value');
|
|
437
447
|
assert.equal(this.#entries[entry_index].key, key, 'entry.key');
|
|
438
448
|
|
|
439
|
-
|
|
449
|
+
const bin_value = entry_index + ENTRY_BASE;
|
|
450
|
+
|
|
451
|
+
this.#bins[bin_index] = bin_value;
|
|
452
|
+
|
|
453
|
+
assert.equal(bin_value, this.#bins[bin_index], 'Bin value write error');
|
|
440
454
|
|
|
441
455
|
break;
|
|
442
456
|
|
|
@@ -484,7 +498,7 @@ export class HashMap {
|
|
|
484
498
|
// check if the entry is what we're looking for
|
|
485
499
|
const entry = this.#entries[bin - ENTRY_BASE];
|
|
486
500
|
|
|
487
|
-
if (entry_equality_check(entry, raw_hash, key
|
|
501
|
+
if (this.#entry_equality_check(entry, raw_hash, key)) {
|
|
488
502
|
// found the right entry
|
|
489
503
|
return entry.value;
|
|
490
504
|
}
|
|
@@ -588,7 +602,7 @@ export class HashMap {
|
|
|
588
602
|
const entry_index = bin - ENTRY_BASE;
|
|
589
603
|
const entry = entries[entry_index];
|
|
590
604
|
|
|
591
|
-
if (entry_equality_check(entry, raw_hash, key
|
|
605
|
+
if (this.#entry_equality_check(entry, raw_hash, key)) {
|
|
592
606
|
// found the right entry
|
|
593
607
|
|
|
594
608
|
// record entry as dead
|
|
@@ -670,7 +684,11 @@ export class HashMap {
|
|
|
670
684
|
|
|
671
685
|
let written_entries = 0;
|
|
672
686
|
|
|
673
|
-
for (
|
|
687
|
+
for (
|
|
688
|
+
let existing_entry_index = this.#entries_start;
|
|
689
|
+
existing_entry_index < entries_bound;
|
|
690
|
+
existing_entry_index++
|
|
691
|
+
) {
|
|
674
692
|
const entry = entries[existing_entry_index];
|
|
675
693
|
|
|
676
694
|
const hash = entry.hash;
|
|
@@ -244,7 +244,7 @@ export class NodeGraph {
|
|
|
244
244
|
* @return {NodeInstance[]}
|
|
245
245
|
*/
|
|
246
246
|
getNodes() {
|
|
247
|
-
return this.nodes.
|
|
247
|
+
return this.nodes.asArray().slice();
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
/**
|
|
@@ -252,7 +252,7 @@ export class NodeGraph {
|
|
|
252
252
|
* @return {Connection[]}
|
|
253
253
|
*/
|
|
254
254
|
getConnections() {
|
|
255
|
-
return this.connections.
|
|
255
|
+
return this.connections.asArray().slice();
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
/**
|