@parcel/graph 2.8.4-nightly.2926 → 2.8.4-nightly.2937
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/lib/AdjacencyList.js +104 -265
- package/lib/ContentGraph.js +6 -28
- package/lib/Graph.js +18 -105
- package/lib/index.js +0 -6
- package/lib/shared-buffer.js +6 -7
- package/lib/types.js +0 -2
- package/package.json +2 -2
package/lib/AdjacencyList.js
CHANGED
@@ -4,62 +4,45 @@ Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
5
5
|
});
|
6
6
|
exports.default = exports.SharedTypeMap = exports.NodeTypeMap = exports.EdgeTypeMap = void 0;
|
7
|
-
|
8
7
|
function _assert() {
|
9
8
|
const data = _interopRequireDefault(require("assert"));
|
10
|
-
|
11
9
|
_assert = function () {
|
12
10
|
return data;
|
13
11
|
};
|
14
|
-
|
15
12
|
return data;
|
16
13
|
}
|
17
|
-
|
18
14
|
function _nullthrows() {
|
19
15
|
const data = _interopRequireDefault(require("nullthrows"));
|
20
|
-
|
21
16
|
_nullthrows = function () {
|
22
17
|
return data;
|
23
18
|
};
|
24
|
-
|
25
19
|
return data;
|
26
20
|
}
|
27
|
-
|
28
21
|
var _sharedBuffer = require("./shared-buffer");
|
29
|
-
|
30
22
|
var _types = require("./types");
|
31
|
-
|
32
23
|
var _Graph = require("./Graph");
|
33
|
-
|
34
24
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
35
|
-
|
25
|
+
/** The address of the node in the nodes map. */
|
26
|
+
/** The address of the edge in the edges map. */
|
27
|
+
// eslint-disable-next-line no-unused-vars
|
28
|
+
// eslint-disable-next-line no-unused-vars
|
36
29
|
/** The upper bound above which capacity should be increased. */
|
37
30
|
const LOAD_FACTOR = 0.7;
|
38
31
|
/** The lower bound below which capacity should be decreased. */
|
39
|
-
|
40
32
|
const UNLOAD_FACTOR = 0.3;
|
41
33
|
/** The max amount by which to grow the capacity. */
|
42
|
-
|
43
34
|
const MAX_GROW_FACTOR = 8;
|
44
35
|
/** The min amount by which to grow the capacity. */
|
45
|
-
|
46
36
|
const MIN_GROW_FACTOR = 2;
|
47
37
|
/** The amount by which to shrink the capacity. */
|
48
|
-
|
49
38
|
const SHRINK_FACTOR = 0.5;
|
50
|
-
|
51
39
|
class AdjacencyList {
|
52
|
-
#nodes
|
53
|
-
/*:
|
54
|
-
;
|
55
|
-
#edges
|
56
|
-
/*: EdgeTypeMap<TEdgeType | NullEdgeType> */
|
57
|
-
;
|
40
|
+
#nodes /*: NodeTypeMap<TEdgeType | NullEdgeType> */;
|
41
|
+
#edges /*: EdgeTypeMap<TEdgeType | NullEdgeType> */;
|
58
42
|
|
59
43
|
constructor(opts) {
|
60
44
|
let nodes;
|
61
45
|
let edges;
|
62
|
-
|
63
46
|
if (opts !== null && opts !== void 0 && opts.nodes) {
|
64
47
|
({
|
65
48
|
nodes,
|
@@ -78,29 +61,25 @@ class AdjacencyList {
|
|
78
61
|
this.#edges = new EdgeTypeMap(edgeCapacity);
|
79
62
|
}
|
80
63
|
}
|
64
|
+
|
81
65
|
/**
|
82
66
|
* Create a new `AdjacencyList` from the given options.
|
83
67
|
*/
|
84
|
-
|
85
|
-
|
86
68
|
static deserialize(opts) {
|
87
69
|
return new AdjacencyList(opts);
|
88
70
|
}
|
71
|
+
|
89
72
|
/**
|
90
73
|
* Returns a serializable object of the nodes and edges in the graph.
|
91
74
|
*/
|
92
|
-
|
93
|
-
|
94
75
|
serialize() {
|
95
76
|
return {
|
96
77
|
nodes: this.#nodes.data,
|
97
78
|
edges: this.#edges.data
|
98
79
|
};
|
99
80
|
}
|
100
|
-
|
101
81
|
get stats() {
|
102
82
|
let buckets = new Map();
|
103
|
-
|
104
83
|
for (let {
|
105
84
|
from,
|
106
85
|
to,
|
@@ -113,17 +92,14 @@ class AdjacencyList {
|
|
113
92
|
bucket.add(key);
|
114
93
|
buckets.set(hash, bucket);
|
115
94
|
}
|
116
|
-
|
117
95
|
let maxCollisions = 0;
|
118
96
|
let collisions = 0;
|
119
97
|
let distribution = 0;
|
120
|
-
|
121
98
|
for (let bucket of buckets.values()) {
|
122
99
|
maxCollisions = Math.max(maxCollisions, bucket.size - 1);
|
123
100
|
collisions += bucket.size - 1;
|
124
101
|
distribution += bucket.size * (bucket.size + 1) / 2;
|
125
102
|
}
|
126
|
-
|
127
103
|
let uniformity = distribution / (this.#edges.count / (2 * this.#edges.capacity) * (this.#edges.count + 2 * this.#edges.capacity - 1));
|
128
104
|
return {
|
129
105
|
nodes: (0, _types.fromNodeId)(this.#nodes.nextId),
|
@@ -143,83 +119,82 @@ class AdjacencyList {
|
|
143
119
|
uniformity: Math.round(uniformity * 100) / 100 || 0
|
144
120
|
};
|
145
121
|
}
|
122
|
+
|
146
123
|
/**
|
147
124
|
* Resize the internal nodes array.
|
148
125
|
*
|
149
126
|
* This is used in `addNode` when the `numNodes` meets or exceeds
|
150
127
|
* the allocated size of the `nodes` array.
|
151
128
|
*/
|
152
|
-
|
153
|
-
|
154
129
|
resizeNodes(size) {
|
155
|
-
let nodes = this.#nodes;
|
156
|
-
|
157
|
-
this.#nodes = new NodeTypeMap(size);
|
158
|
-
|
130
|
+
let nodes = this.#nodes;
|
131
|
+
// Allocate the required space for a `nodes` map of the given `size`.
|
132
|
+
this.#nodes = new NodeTypeMap(size);
|
133
|
+
// Copy the existing nodes into the new array.
|
159
134
|
this.#nodes.set(nodes.data);
|
160
135
|
}
|
136
|
+
|
161
137
|
/**
|
162
138
|
* Resize the internal edges array.
|
163
139
|
*
|
164
140
|
* This is used in `addEdge` when the `numEdges` meets or exceeds
|
165
141
|
* the allocated size of the `edges` array.
|
166
142
|
*/
|
167
|
-
|
168
|
-
|
169
143
|
resizeEdges(size) {
|
170
144
|
// Allocate the required space for new `nodes` and `edges` maps.
|
171
145
|
let copy = new AdjacencyList({
|
172
146
|
nodeCapacity: this.#nodes.capacity,
|
173
147
|
edgeCapacity: size
|
174
|
-
});
|
148
|
+
});
|
175
149
|
|
150
|
+
// Copy the existing edges into the new array.
|
176
151
|
copy.#nodes.nextId = this.#nodes.nextId;
|
177
|
-
this.#edges.forEach(edge => void copy.addEdge(this.#edges.from(edge), this.#edges.to(edge), this.#edges.typeOf(edge)));
|
152
|
+
this.#edges.forEach(edge => void copy.addEdge(this.#edges.from(edge), this.#edges.to(edge), this.#edges.typeOf(edge)));
|
178
153
|
|
179
|
-
|
154
|
+
// We expect to preserve the same number of edges.
|
155
|
+
(0, _assert().default)(this.#edges.count === copy.#edges.count, `Edge mismatch! ${this.#edges.count} does not match ${copy.#edges.count}.`);
|
180
156
|
|
157
|
+
// Finally, copy the new data arrays over to this graph.
|
181
158
|
this.#nodes = copy.#nodes;
|
182
159
|
this.#edges = copy.#edges;
|
183
160
|
}
|
161
|
+
|
184
162
|
/**
|
185
163
|
* Adds a node to the graph.
|
186
164
|
*
|
187
165
|
* Returns the id of the added node.
|
188
166
|
*/
|
189
|
-
|
190
|
-
|
191
167
|
addNode() {
|
192
|
-
let id = this.#nodes.getId();
|
193
|
-
|
168
|
+
let id = this.#nodes.getId();
|
169
|
+
// If we're in danger of overflowing the `nodes` array, resize it.
|
194
170
|
if (this.#nodes.load > LOAD_FACTOR) {
|
195
171
|
this.resizeNodes(increaseNodeCapacity(this.#nodes.capacity));
|
196
172
|
}
|
197
|
-
|
198
173
|
return id;
|
199
174
|
}
|
175
|
+
|
200
176
|
/**
|
201
177
|
* Adds an edge to the graph.
|
202
178
|
*
|
203
179
|
* Returns `true` if the edge was added,
|
204
180
|
* or `false` if the edge already exists.
|
205
181
|
*/
|
206
|
-
|
207
|
-
|
208
182
|
addEdge(from, to, type = 1) {
|
209
183
|
(0, _assert().default)(type > 0, `Unsupported edge type ${type}`);
|
210
184
|
let hash = this.#edges.hash(from, to, type);
|
211
|
-
let edge = this.#edges.addressOf(hash, from, to, type);
|
185
|
+
let edge = this.#edges.addressOf(hash, from, to, type);
|
212
186
|
|
187
|
+
// The edge is already in the graph; do nothing.
|
213
188
|
if (edge !== null) return false;
|
214
|
-
let capacity = this.#edges.capacity;
|
215
|
-
|
216
|
-
let count = this.#edges.count + 1;
|
189
|
+
let capacity = this.#edges.capacity;
|
190
|
+
// We add 1 to account for the edge we are adding.
|
191
|
+
let count = this.#edges.count + 1;
|
192
|
+
// Since the space occupied by deleted edges isn't reclaimed,
|
217
193
|
// we include them in our count to avoid overflowing the `edges` array.
|
218
|
-
|
219
194
|
let deletes = this.#edges.deletes;
|
220
|
-
let total = count + deletes;
|
195
|
+
let total = count + deletes;
|
196
|
+
// If we have enough space to keep adding edges, we can
|
221
197
|
// put off reclaiming the deleted space until the next resize.
|
222
|
-
|
223
198
|
if (this.#edges.getLoad(total) > LOAD_FACTOR) {
|
224
199
|
if (this.#edges.getLoad(deletes) > UNLOAD_FACTOR) {
|
225
200
|
// If we have a significant number of deletes, we compute our new
|
@@ -229,38 +204,36 @@ class AdjacencyList {
|
|
229
204
|
this.resizeEdges(getNextEdgeCapacity(capacity, count, this.#edges.getLoad(count)));
|
230
205
|
} else {
|
231
206
|
this.resizeEdges(getNextEdgeCapacity(capacity, total, this.#edges.getLoad(total)));
|
232
|
-
}
|
233
|
-
|
234
|
-
|
207
|
+
}
|
208
|
+
// We must rehash because the capacity has changed.
|
235
209
|
hash = this.#edges.hash(from, to, type);
|
236
210
|
}
|
237
|
-
|
238
211
|
let toNode = this.#nodes.addressOf(to, type);
|
239
212
|
let fromNode = this.#nodes.addressOf(from, type);
|
240
|
-
|
241
213
|
if (toNode === null || fromNode === null) {
|
242
214
|
// If we're in danger of overflowing the `nodes` array, resize it.
|
243
215
|
if (this.#nodes.load >= LOAD_FACTOR) {
|
244
|
-
this.resizeNodes(increaseNodeCapacity(this.#nodes.capacity));
|
245
|
-
|
216
|
+
this.resizeNodes(increaseNodeCapacity(this.#nodes.capacity));
|
217
|
+
// We need to update our indices since the `nodes` array has changed.
|
246
218
|
toNode = this.#nodes.addressOf(to, type);
|
247
219
|
fromNode = this.#nodes.addressOf(from, type);
|
248
220
|
}
|
249
221
|
}
|
250
|
-
|
251
222
|
if (toNode === null) toNode = this.#nodes.add(to, type);
|
252
|
-
if (fromNode === null) fromNode = this.#nodes.add(from, type);
|
223
|
+
if (fromNode === null) fromNode = this.#nodes.add(from, type);
|
253
224
|
|
254
|
-
|
225
|
+
// Add our new edge to its hash bucket.
|
226
|
+
edge = this.#edges.add(hash, from, to, type);
|
255
227
|
|
228
|
+
// Link this edge to the node's list of incoming edges.
|
256
229
|
let prevIn = this.#nodes.linkIn(toNode, edge);
|
257
|
-
if (prevIn !== null) this.#edges.linkIn(prevIn, edge);
|
230
|
+
if (prevIn !== null) this.#edges.linkIn(prevIn, edge);
|
258
231
|
|
232
|
+
// Link this edge to the node's list of outgoing edges.
|
259
233
|
let prevOut = this.#nodes.linkOut(fromNode, edge);
|
260
234
|
if (prevOut !== null) this.#edges.linkOut(prevOut, edge);
|
261
235
|
return true;
|
262
236
|
}
|
263
|
-
|
264
237
|
*getAllEdges() {
|
265
238
|
for (let edge of this.#edges) {
|
266
239
|
yield {
|
@@ -270,68 +243,62 @@ class AdjacencyList {
|
|
270
243
|
};
|
271
244
|
}
|
272
245
|
}
|
246
|
+
|
273
247
|
/**
|
274
248
|
* Check if the graph has an edge connecting the `from` and `to` nodes.
|
275
249
|
*/
|
276
|
-
|
277
|
-
|
278
250
|
hasEdge(from, to, type = 1) {
|
279
251
|
let hasEdge = type => {
|
280
252
|
let hash = this.#edges.hash(from, to, type);
|
281
253
|
return this.#edges.addressOf(hash, from, to, type) !== null;
|
282
254
|
};
|
283
|
-
|
284
255
|
if (Array.isArray(type)) {
|
285
256
|
return type.some(hasEdge);
|
286
257
|
}
|
287
|
-
|
288
258
|
return hasEdge(type);
|
289
259
|
}
|
260
|
+
|
290
261
|
/**
|
291
262
|
*
|
292
263
|
*/
|
293
|
-
|
294
|
-
|
295
264
|
removeEdge(from, to, type = 1) {
|
296
265
|
let hash = this.#edges.hash(from, to, type);
|
297
|
-
let edge = this.#edges.addressOf(hash, from, to, type);
|
266
|
+
let edge = this.#edges.addressOf(hash, from, to, type);
|
298
267
|
|
268
|
+
// The edge is not in the graph; do nothing.
|
299
269
|
if (edge === null) return;
|
300
270
|
let toNode = (0, _nullthrows().default)(this.#nodes.addressOf(to, type));
|
301
|
-
let fromNode = (0, _nullthrows().default)(this.#nodes.addressOf(from, type));
|
302
|
-
|
303
|
-
this.#nodes.unlinkIn(toNode, edge, this.#edges.prevIn(edge), this.#edges.nextIn(edge)); // Update the originating node's first and last outgoing edges.
|
271
|
+
let fromNode = (0, _nullthrows().default)(this.#nodes.addressOf(from, type));
|
304
272
|
|
305
|
-
|
273
|
+
// Update the terminating node's first and last incoming edges.
|
274
|
+
this.#nodes.unlinkIn(toNode, edge, this.#edges.prevIn(edge), this.#edges.nextIn(edge));
|
306
275
|
|
307
|
-
|
308
|
-
|
309
|
-
this.#edges.unlinkIn(edge); // Splice the removed edge out of the linked list of outgoing edges.
|
310
|
-
|
311
|
-
this.#edges.unlinkOut(edge); // Finally, delete the edge.
|
276
|
+
// Update the originating node's first and last outgoing edges.
|
277
|
+
this.#nodes.unlinkOut(fromNode, edge, this.#edges.prevOut(edge), this.#edges.nextOut(edge));
|
312
278
|
|
279
|
+
// Splice the removed edge out of the linked list of edges in the bucket.
|
280
|
+
this.#edges.unlink(hash, edge);
|
281
|
+
// Splice the removed edge out of the linked list of incoming edges.
|
282
|
+
this.#edges.unlinkIn(edge);
|
283
|
+
// Splice the removed edge out of the linked list of outgoing edges.
|
284
|
+
this.#edges.unlinkOut(edge);
|
285
|
+
// Finally, delete the edge.
|
313
286
|
this.#edges.delete(edge);
|
314
287
|
}
|
315
|
-
|
316
288
|
hasInboundEdges(to) {
|
317
289
|
let node = this.#nodes.head(to);
|
318
|
-
|
319
290
|
while (node !== null) {
|
320
291
|
if (this.#nodes.firstIn(node) !== null) return true;
|
321
292
|
node = this.#nodes.next(node);
|
322
293
|
}
|
323
|
-
|
324
294
|
return false;
|
325
295
|
}
|
326
|
-
|
327
296
|
getInboundEdgesByType(to) {
|
328
297
|
let edges = [];
|
329
298
|
let node = this.#nodes.head(to);
|
330
|
-
|
331
299
|
while (node !== null) {
|
332
300
|
let type = this.#nodes.typeOf(node);
|
333
301
|
let edge = this.#nodes.firstIn(node);
|
334
|
-
|
335
302
|
while (edge !== null) {
|
336
303
|
let from = this.#edges.from(edge);
|
337
304
|
edges.push({
|
@@ -340,21 +307,16 @@ class AdjacencyList {
|
|
340
307
|
});
|
341
308
|
edge = this.#edges.nextIn(edge);
|
342
309
|
}
|
343
|
-
|
344
310
|
node = this.#nodes.next(node);
|
345
311
|
}
|
346
|
-
|
347
312
|
return edges;
|
348
313
|
}
|
349
|
-
|
350
314
|
getOutboundEdgesByType(from) {
|
351
315
|
let edges = [];
|
352
316
|
let node = this.#nodes.head(from);
|
353
|
-
|
354
317
|
while (node !== null) {
|
355
318
|
let type = this.#nodes.typeOf(node);
|
356
319
|
let edge = this.#nodes.firstOut(node);
|
357
|
-
|
358
320
|
while (edge !== null) {
|
359
321
|
let to = this.#edges.to(edge);
|
360
322
|
edges.push({
|
@@ -363,87 +325,68 @@ class AdjacencyList {
|
|
363
325
|
});
|
364
326
|
edge = this.#edges.nextOut(edge);
|
365
327
|
}
|
366
|
-
|
367
328
|
node = this.#nodes.next(node);
|
368
329
|
}
|
369
|
-
|
370
330
|
return edges;
|
371
331
|
}
|
332
|
+
|
372
333
|
/**
|
373
334
|
* Get the list of nodes connected from this node.
|
374
335
|
*/
|
375
|
-
|
376
|
-
|
377
336
|
getNodeIdsConnectedFrom(from, type = 1) {
|
378
337
|
let matches = node => type === _Graph.ALL_EDGE_TYPES || (Array.isArray(type) ? type.includes(this.#nodes.typeOf(node)) : type === this.#nodes.typeOf(node));
|
379
|
-
|
380
338
|
let nodes = [];
|
381
339
|
let seen = new Set();
|
382
340
|
let node = this.#nodes.head(from);
|
383
|
-
|
384
341
|
while (node !== null) {
|
385
342
|
if (matches(node)) {
|
386
343
|
let edge = this.#nodes.firstOut(node);
|
387
|
-
|
388
344
|
while (edge !== null) {
|
389
345
|
let to = this.#edges.to(edge);
|
390
|
-
|
391
346
|
if (!seen.has(to)) {
|
392
347
|
nodes.push(to);
|
393
348
|
seen.add(to);
|
394
349
|
}
|
395
|
-
|
396
350
|
edge = this.#edges.nextOut(edge);
|
397
351
|
}
|
398
352
|
}
|
399
|
-
|
400
353
|
node = this.#nodes.next(node);
|
401
354
|
}
|
402
|
-
|
403
355
|
return nodes;
|
404
356
|
}
|
357
|
+
|
405
358
|
/**
|
406
359
|
* Get the list of nodes connected to this node.
|
407
360
|
*/
|
408
|
-
|
409
|
-
|
410
361
|
getNodeIdsConnectedTo(to, type = 1) {
|
411
362
|
let matches = node => type === _Graph.ALL_EDGE_TYPES || (Array.isArray(type) ? type.includes(this.#nodes.typeOf(node)) : type === this.#nodes.typeOf(node));
|
412
|
-
|
413
363
|
let nodes = [];
|
414
364
|
let seen = new Set();
|
415
365
|
let node = this.#nodes.head(to);
|
416
|
-
|
417
366
|
while (node !== null) {
|
418
367
|
if (matches(node)) {
|
419
368
|
let edge = this.#nodes.firstIn(node);
|
420
|
-
|
421
369
|
while (edge !== null) {
|
422
370
|
let from = this.#edges.from(edge);
|
423
|
-
|
424
371
|
if (!seen.has(from)) {
|
425
372
|
nodes.push(from);
|
426
373
|
seen.add(from);
|
427
374
|
}
|
428
|
-
|
429
375
|
edge = this.#edges.nextIn(edge);
|
430
376
|
}
|
431
377
|
}
|
432
|
-
|
433
378
|
node = this.#nodes.next(node);
|
434
379
|
}
|
435
|
-
|
436
380
|
return nodes;
|
437
381
|
}
|
438
|
-
|
439
382
|
inspect() {
|
440
383
|
return {
|
441
384
|
nodes: this.#nodes.inspect(),
|
442
385
|
edges: this.#edges.inspect()
|
443
386
|
};
|
444
387
|
}
|
445
|
-
|
446
388
|
}
|
389
|
+
|
447
390
|
/**
|
448
391
|
* `SharedTypeMap` is a hashmap of items,
|
449
392
|
* where each item has its own 'type' field.
|
@@ -483,10 +426,7 @@ class AdjacencyList {
|
|
483
426
|
* └────┼─────────┴─────────────────┘ │
|
484
427
|
* └─────────────────────────────────────────────┘
|
485
428
|
*/
|
486
|
-
|
487
|
-
|
488
429
|
exports.default = AdjacencyList;
|
489
|
-
|
490
430
|
class SharedTypeMap {
|
491
431
|
/**
|
492
432
|
* The header for the `SharedTypeMap` comprises 2 4-byte chunks:
|
@@ -502,11 +442,10 @@ class SharedTypeMap {
|
|
502
442
|
*/
|
503
443
|
static HEADER_SIZE = 2;
|
504
444
|
/** The offset from the header where the capacity is stored. */
|
505
|
-
|
506
445
|
static #CAPACITY = 0;
|
507
446
|
/** The offset from the header where the count is stored. */
|
508
|
-
|
509
447
|
static #COUNT = 1;
|
448
|
+
|
510
449
|
/**
|
511
450
|
* Each item in `SharedTypeMap` comprises 2 4-byte chunks:
|
512
451
|
*
|
@@ -519,52 +458,42 @@ class SharedTypeMap {
|
|
519
458
|
* │ NEXT │ TYPE │
|
520
459
|
* └──────┴──────┘
|
521
460
|
*/
|
522
|
-
|
523
461
|
static ITEM_SIZE = 2;
|
524
462
|
/** The offset at which a link to the next item in the same bucket is stored. */
|
525
|
-
|
526
463
|
static #NEXT = 0;
|
527
464
|
/** The offset at which an item's type is stored. */
|
528
|
-
|
529
465
|
static #TYPE = 1;
|
530
|
-
/** The number of items to accommodate per hash bucket. */
|
531
466
|
|
467
|
+
/** The number of items to accommodate per hash bucket. */
|
532
468
|
static BUCKET_SIZE = 2;
|
533
|
-
|
534
469
|
get capacity() {
|
535
470
|
return this.data[SharedTypeMap.#CAPACITY];
|
536
471
|
}
|
537
|
-
|
538
472
|
get count() {
|
539
473
|
return this.data[SharedTypeMap.#COUNT];
|
540
474
|
}
|
541
|
-
|
542
475
|
get load() {
|
543
476
|
return this.getLoad();
|
544
477
|
}
|
545
|
-
|
546
478
|
get length() {
|
547
479
|
return this.getLength();
|
548
480
|
}
|
549
|
-
|
550
481
|
get addressableLimit() {
|
551
482
|
return this.constructor.HEADER_SIZE + this.capacity;
|
552
483
|
}
|
553
|
-
|
554
484
|
get bufferSize() {
|
555
485
|
return `${(this.data.byteLength / 1024 / 1024).toLocaleString(undefined, {
|
556
486
|
minimumFractionDigits: 2,
|
557
487
|
maximumFractionDigits: 2
|
558
488
|
})} mb`;
|
559
489
|
}
|
560
|
-
|
561
490
|
constructor(capacityOrData) {
|
562
491
|
if (typeof capacityOrData === 'number') {
|
563
492
|
let {
|
564
493
|
BYTES_PER_ELEMENT
|
565
494
|
} = Uint32Array;
|
566
|
-
let CAPACITY = SharedTypeMap.#CAPACITY;
|
567
|
-
|
495
|
+
let CAPACITY = SharedTypeMap.#CAPACITY;
|
496
|
+
// $FlowFixMe[incompatible-call]
|
568
497
|
this.data = new Uint32Array(new _sharedBuffer.SharedBuffer(this.getLength(capacityOrData) * BYTES_PER_ELEMENT));
|
569
498
|
this.data[CAPACITY] = capacityOrData;
|
570
499
|
} else {
|
@@ -572,7 +501,6 @@ class SharedTypeMap {
|
|
572
501
|
(0, _assert().default)(this.getLength() === this.data.length, 'Data appears corrupt.');
|
573
502
|
}
|
574
503
|
}
|
575
|
-
|
576
504
|
set(data) {
|
577
505
|
let {
|
578
506
|
HEADER_SIZE,
|
@@ -582,37 +510,35 @@ class SharedTypeMap {
|
|
582
510
|
let COUNT = SharedTypeMap.#COUNT;
|
583
511
|
let CAPACITY = SharedTypeMap.#CAPACITY;
|
584
512
|
let delta = this.capacity - data[CAPACITY];
|
585
|
-
(0, _assert().default)(delta >= 0, 'Cannot copy to a map with smaller capacity.');
|
513
|
+
(0, _assert().default)(delta >= 0, 'Cannot copy to a map with smaller capacity.');
|
586
514
|
|
587
|
-
|
515
|
+
// Copy the header.
|
516
|
+
this.data.set(data.subarray(COUNT, HEADER_SIZE), COUNT);
|
588
517
|
|
518
|
+
// Copy the hash table.
|
589
519
|
let toTable = this.data.subarray(HEADER_SIZE, HEADER_SIZE + this.capacity);
|
590
|
-
toTable.set(data.subarray(HEADER_SIZE, HEADER_SIZE + data[CAPACITY]));
|
591
|
-
|
520
|
+
toTable.set(data.subarray(HEADER_SIZE, HEADER_SIZE + data[CAPACITY]));
|
521
|
+
// Offset first links to account for the change in table capacity.
|
592
522
|
let max = toTable.length;
|
593
|
-
|
594
523
|
for (let i = 0; i < max; i++) {
|
595
524
|
if (toTable[i]) toTable[i] += delta;
|
596
|
-
}
|
597
|
-
|
525
|
+
}
|
598
526
|
|
527
|
+
// Copy the items.
|
599
528
|
let toItems = this.data.subarray(HEADER_SIZE + this.capacity);
|
600
|
-
toItems.set(data.subarray(HEADER_SIZE + data[CAPACITY]));
|
601
|
-
|
529
|
+
toItems.set(data.subarray(HEADER_SIZE + data[CAPACITY]));
|
530
|
+
// Offset next links to account for the change in table capacity.
|
602
531
|
max = toItems.length;
|
603
|
-
|
604
532
|
for (let i = 0; i < max; i += ITEM_SIZE) {
|
605
533
|
if (toItems[i + NEXT]) toItems[i + NEXT] += delta;
|
606
534
|
}
|
607
535
|
}
|
608
|
-
|
609
536
|
getLoad(count = this.count) {
|
610
537
|
let {
|
611
538
|
BUCKET_SIZE
|
612
539
|
} = this.constructor;
|
613
540
|
return count / (this.capacity * BUCKET_SIZE);
|
614
541
|
}
|
615
|
-
|
616
542
|
getLength(capacity = this.capacity) {
|
617
543
|
let {
|
618
544
|
HEADER_SIZE,
|
@@ -621,9 +547,8 @@ class SharedTypeMap {
|
|
621
547
|
} = this.constructor;
|
622
548
|
return capacity + HEADER_SIZE + ITEM_SIZE * BUCKET_SIZE * capacity;
|
623
549
|
}
|
624
|
-
/** Get the next available address in the map. */
|
625
|
-
|
626
550
|
|
551
|
+
/** Get the next available address in the map. */
|
627
552
|
getNextAddress() {
|
628
553
|
let {
|
629
554
|
HEADER_SIZE,
|
@@ -631,27 +556,23 @@ class SharedTypeMap {
|
|
631
556
|
} = this.constructor;
|
632
557
|
return HEADER_SIZE + this.capacity + this.count * ITEM_SIZE;
|
633
558
|
}
|
634
|
-
/** Get the address of the first item with the given hash. */
|
635
|
-
|
636
559
|
|
560
|
+
/** Get the address of the first item with the given hash. */
|
637
561
|
head(hash) {
|
638
562
|
let {
|
639
563
|
HEADER_SIZE
|
640
564
|
} = this.constructor;
|
641
565
|
return this.data[HEADER_SIZE + hash] || null;
|
642
566
|
}
|
643
|
-
/** Get the address of the next item with the same hash as the given item. */
|
644
|
-
|
645
567
|
|
568
|
+
/** Get the address of the next item with the same hash as the given item. */
|
646
569
|
next(item) {
|
647
570
|
let NEXT = SharedTypeMap.#NEXT;
|
648
571
|
return this.data[item + NEXT] || null;
|
649
572
|
}
|
650
|
-
|
651
573
|
typeOf(item) {
|
652
574
|
return this.data[item + SharedTypeMap.#TYPE];
|
653
575
|
}
|
654
|
-
|
655
576
|
link(hash, item, type) {
|
656
577
|
let COUNT = SharedTypeMap.#COUNT;
|
657
578
|
let NEXT = SharedTypeMap.#NEXT;
|
@@ -661,24 +582,19 @@ class SharedTypeMap {
|
|
661
582
|
} = this.constructor;
|
662
583
|
this.data[item + TYPE] = type;
|
663
584
|
let prev = this.head(hash);
|
664
|
-
|
665
585
|
if (prev !== null) {
|
666
586
|
let next = this.next(prev);
|
667
|
-
|
668
587
|
while (next !== null) {
|
669
588
|
prev = next;
|
670
589
|
next = this.next(next);
|
671
590
|
}
|
672
|
-
|
673
591
|
this.data[prev + NEXT] = item;
|
674
592
|
} else {
|
675
593
|
// This is the first item in the bucket!
|
676
594
|
this.data[HEADER_SIZE + hash] = item;
|
677
595
|
}
|
678
|
-
|
679
596
|
this.data[COUNT]++;
|
680
597
|
}
|
681
|
-
|
682
598
|
unlink(hash, item) {
|
683
599
|
let COUNT = SharedTypeMap.#COUNT;
|
684
600
|
let NEXT = SharedTypeMap.#NEXT;
|
@@ -687,18 +603,16 @@ class SharedTypeMap {
|
|
687
603
|
HEADER_SIZE
|
688
604
|
} = this.constructor;
|
689
605
|
this.data[item + TYPE] = 0;
|
690
|
-
let head = this.head(hash);
|
691
|
-
|
606
|
+
let head = this.head(hash);
|
607
|
+
// No bucket to unlink from.
|
692
608
|
if (head === null) return;
|
693
609
|
let next = this.next(item);
|
694
610
|
let prev = null;
|
695
611
|
let candidate = head;
|
696
|
-
|
697
612
|
while (candidate !== null && candidate !== item) {
|
698
613
|
prev = candidate;
|
699
614
|
candidate = this.next(candidate);
|
700
615
|
}
|
701
|
-
|
702
616
|
if (prev !== null && next !== null) {
|
703
617
|
this.data[prev + NEXT] = next;
|
704
618
|
} else if (prev !== null) {
|
@@ -708,18 +622,15 @@ class SharedTypeMap {
|
|
708
622
|
} else {
|
709
623
|
this.data[HEADER_SIZE + hash] = 0;
|
710
624
|
}
|
711
|
-
|
712
625
|
this.data[item + NEXT] = 0;
|
713
626
|
this.data[COUNT]--;
|
714
627
|
}
|
715
|
-
|
716
628
|
forEach(cb) {
|
717
629
|
let max = this.count;
|
718
630
|
let len = this.length;
|
719
631
|
let {
|
720
632
|
ITEM_SIZE
|
721
633
|
} = this.constructor;
|
722
|
-
|
723
634
|
for (let i = this.addressableLimit, count = 0; i < len && count < max; i += ITEM_SIZE) {
|
724
635
|
// Skip items that don't have a type.
|
725
636
|
if (this.typeOf(i)) {
|
@@ -727,20 +638,18 @@ class SharedTypeMap {
|
|
727
638
|
count++;
|
728
639
|
}
|
729
640
|
}
|
730
|
-
}
|
731
|
-
// See https://github.com/facebook/flow/issues/1163#issuecomment-353523840
|
641
|
+
}
|
732
642
|
|
643
|
+
// Trick Flow into believing in `Symbol.iterator`.
|
644
|
+
// See https://github.com/facebook/flow/issues/1163#issuecomment-353523840
|
733
645
|
/*:: @@iterator(): Iterator<TAddress> { return ({}: any); } */
|
734
646
|
// $FlowFixMe[unsupported-syntax]
|
735
|
-
|
736
|
-
|
737
647
|
*[Symbol.iterator]() {
|
738
648
|
let max = this.count;
|
739
649
|
let len = this.length;
|
740
650
|
let {
|
741
651
|
ITEM_SIZE
|
742
652
|
} = this.constructor;
|
743
|
-
|
744
653
|
for (let i = this.addressableLimit, count = 0; i < len && count < max; i += ITEM_SIZE) {
|
745
654
|
if (this.data.subarray(i, i + ITEM_SIZE).some(Boolean)) {
|
746
655
|
yield i;
|
@@ -748,7 +657,6 @@ class SharedTypeMap {
|
|
748
657
|
}
|
749
658
|
}
|
750
659
|
}
|
751
|
-
|
752
660
|
inspect() {
|
753
661
|
const {
|
754
662
|
HEADER_SIZE,
|
@@ -763,17 +671,14 @@ class SharedTypeMap {
|
|
763
671
|
data: this.data.subarray(min, max)
|
764
672
|
};
|
765
673
|
}
|
766
|
-
|
767
674
|
}
|
675
|
+
|
768
676
|
/**
|
769
677
|
* Nodes are stored in a `SharedTypeMap`, keyed on node id plus an edge type.
|
770
678
|
* This means that for any given unique node id, there may be `e` nodes in the
|
771
679
|
* map, where `e` is the number of possible edge types in the graph.
|
772
680
|
*/
|
773
|
-
|
774
|
-
|
775
681
|
exports.SharedTypeMap = SharedTypeMap;
|
776
|
-
|
777
682
|
class NodeTypeMap extends SharedTypeMap {
|
778
683
|
/**
|
779
684
|
* In addition to the header defined by `SharedTypeMap`, the header for
|
@@ -791,8 +696,8 @@ class NodeTypeMap extends SharedTypeMap {
|
|
791
696
|
*/
|
792
697
|
static HEADER_SIZE = 3;
|
793
698
|
/** The offset from the header where the next available node id is stored. */
|
794
|
-
|
795
699
|
static #NEXT_ID = 2;
|
700
|
+
|
796
701
|
/**
|
797
702
|
* In addition to the item fields defined by `SharedTypeMap`,
|
798
703
|
* each node includes another 4 4-byte chunks:
|
@@ -810,46 +715,36 @@ class NodeTypeMap extends SharedTypeMap {
|
|
810
715
|
* │ NEXT │ TYPE │ FIRST_IN │ FIRST_OUT │ LAST_IN │ LAST_OUT │
|
811
716
|
* └──────┴──────┴──────────┴───────────┴─────────┴──────────┘
|
812
717
|
*/
|
813
|
-
|
814
718
|
static ITEM_SIZE = 6;
|
815
719
|
/** The offset at which a node's first incoming edge of this type is stored. */
|
816
|
-
|
817
720
|
static #FIRST_IN = 2;
|
818
721
|
/** The offset at which a node's first outgoing edge of this type is stored. */
|
819
|
-
|
820
722
|
static #FIRST_OUT = 3;
|
821
723
|
/** The offset at which a node's last incoming edge of this type is stored. */
|
822
|
-
|
823
724
|
static #LAST_IN = 4;
|
824
725
|
/** The offset at which a node's last outgoing edge of this type is stored. */
|
825
|
-
|
826
726
|
static #LAST_OUT = 5;
|
827
|
-
/** The smallest functional node map capacity. */
|
828
727
|
|
728
|
+
/** The smallest functional node map capacity. */
|
829
729
|
static MIN_CAPACITY = 2;
|
830
730
|
/** The largest possible node map capacity. */
|
831
|
-
|
832
|
-
|
731
|
+
static MAX_CAPACITY = Math.floor(
|
732
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Invalid_array_length#what_went_wrong
|
833
733
|
(2 ** 31 - 1 - NodeTypeMap.HEADER_SIZE) / NodeTypeMap.ITEM_SIZE / NodeTypeMap.BUCKET_SIZE);
|
834
|
-
|
835
734
|
get nextId() {
|
836
735
|
return (0, _types.toNodeId)(this.data[NodeTypeMap.#NEXT_ID]);
|
837
736
|
}
|
838
|
-
|
839
737
|
set nextId(nextId) {
|
840
738
|
this.data[NodeTypeMap.#NEXT_ID] = (0, _types.fromNodeId)(nextId);
|
841
739
|
}
|
842
|
-
/** Get a unique node id. */
|
843
|
-
|
844
740
|
|
741
|
+
/** Get a unique node id. */
|
845
742
|
getId() {
|
846
743
|
return (0, _types.toNodeId)(this.data[NodeTypeMap.#NEXT_ID]++);
|
847
744
|
}
|
848
|
-
|
849
745
|
getLoad(count = this.count) {
|
850
746
|
return Math.max((0, _types.fromNodeId)(this.nextId) / this.capacity, super.getLoad(count));
|
851
747
|
}
|
852
|
-
|
853
748
|
add(node, type) {
|
854
749
|
let index = (0, _types.fromNodeId)(node);
|
855
750
|
(0, _assert().default)(index >= 0 && index < this.data[NodeTypeMap.#NEXT_ID], `Invalid node id ${String(node)} (${this.data[NodeTypeMap.#NEXT_ID]})`);
|
@@ -857,37 +752,28 @@ class NodeTypeMap extends SharedTypeMap {
|
|
857
752
|
this.link(node, address, type);
|
858
753
|
return address;
|
859
754
|
}
|
860
|
-
|
861
755
|
addressOf(node, type) {
|
862
756
|
let address = this.head(node);
|
863
|
-
|
864
757
|
while (address !== null) {
|
865
758
|
if (this.typeOf(address) === type) {
|
866
759
|
return address;
|
867
760
|
}
|
868
|
-
|
869
761
|
address = this.next(address);
|
870
762
|
}
|
871
|
-
|
872
763
|
return null;
|
873
764
|
}
|
874
|
-
|
875
765
|
firstIn(node) {
|
876
766
|
return this.data[node + NodeTypeMap.#FIRST_IN] || null;
|
877
767
|
}
|
878
|
-
|
879
768
|
firstOut(node) {
|
880
769
|
return this.data[node + NodeTypeMap.#FIRST_OUT] || null;
|
881
770
|
}
|
882
|
-
|
883
771
|
lastIn(node) {
|
884
772
|
return this.data[node + NodeTypeMap.#LAST_IN] || null;
|
885
773
|
}
|
886
|
-
|
887
774
|
lastOut(node) {
|
888
775
|
return this.data[node + NodeTypeMap.#LAST_OUT] || null;
|
889
776
|
}
|
890
|
-
|
891
777
|
linkIn(node, edge) {
|
892
778
|
let first = this.firstIn(node);
|
893
779
|
let last = this.lastIn(node);
|
@@ -895,20 +781,16 @@ class NodeTypeMap extends SharedTypeMap {
|
|
895
781
|
this.data[node + NodeTypeMap.#LAST_IN] = edge;
|
896
782
|
return last;
|
897
783
|
}
|
898
|
-
|
899
784
|
unlinkIn(node, edge, prev, next) {
|
900
785
|
let first = this.firstIn(node);
|
901
786
|
let last = this.lastIn(node);
|
902
|
-
|
903
787
|
if (last === edge) {
|
904
788
|
this.data[node + NodeTypeMap.#LAST_IN] = prev === null ? 0 : prev;
|
905
789
|
}
|
906
|
-
|
907
790
|
if (first === edge) {
|
908
791
|
this.data[node + NodeTypeMap.#FIRST_IN] = next === null ? 0 : next;
|
909
792
|
}
|
910
793
|
}
|
911
|
-
|
912
794
|
linkOut(node, edge) {
|
913
795
|
let first = this.firstOut(node);
|
914
796
|
let last = this.lastOut(node);
|
@@ -916,29 +798,23 @@ class NodeTypeMap extends SharedTypeMap {
|
|
916
798
|
this.data[node + NodeTypeMap.#LAST_OUT] = edge;
|
917
799
|
return last;
|
918
800
|
}
|
919
|
-
|
920
801
|
unlinkOut(node, edge, prev, next) {
|
921
802
|
let first = this.firstOut(node);
|
922
803
|
let last = this.lastOut(node);
|
923
|
-
|
924
804
|
if (last === edge) {
|
925
805
|
this.data[node + NodeTypeMap.#LAST_OUT] = prev === null ? 0 : prev;
|
926
806
|
}
|
927
|
-
|
928
807
|
if (first === edge) {
|
929
808
|
this.data[node + NodeTypeMap.#FIRST_OUT] = next === null ? 0 : next;
|
930
809
|
}
|
931
810
|
}
|
932
|
-
|
933
811
|
}
|
812
|
+
|
934
813
|
/**
|
935
814
|
* Edges are stored in a `SharedTypeMap`,
|
936
815
|
* keyed on the 'from' and 'to' node ids, and the edge type.
|
937
816
|
*/
|
938
|
-
|
939
|
-
|
940
817
|
exports.NodeTypeMap = NodeTypeMap;
|
941
|
-
|
942
818
|
class EdgeTypeMap extends SharedTypeMap {
|
943
819
|
/**
|
944
820
|
* In addition to the header defined by `SharedTypeMap`, the header for
|
@@ -956,8 +832,8 @@ class EdgeTypeMap extends SharedTypeMap {
|
|
956
832
|
*/
|
957
833
|
static HEADER_SIZE = 3;
|
958
834
|
/** The offset from the header where the delete count is stored. */
|
959
|
-
|
960
835
|
static #DELETES = 2;
|
836
|
+
|
961
837
|
/**
|
962
838
|
* In addition to the item fields defined by `SharedTypeMap`,
|
963
839
|
* each edge includes another 6 4-byte chunks:
|
@@ -977,106 +853,83 @@ class EdgeTypeMap extends SharedTypeMap {
|
|
977
853
|
* │ NEXT │ TYPE │ FROM │ TO │ NEXT_IN │ PREV_IN │ NEXT_OUT │ PREV_OUT │
|
978
854
|
* └──────┴──────┴──────┴────┴─────────┴─────────┴──────────┴──────────┘
|
979
855
|
*/
|
980
|
-
|
981
856
|
static ITEM_SIZE = 8;
|
982
857
|
/** The offset at which an edge's 'from' node id is stored. */
|
983
|
-
|
984
858
|
static #FROM = 2;
|
985
859
|
/** The offset at which an edge's 'to' node id is stored. */
|
986
|
-
|
987
860
|
static #TO = 3;
|
988
861
|
/** The offset at which the 'to' node's next incoming edge is stored. */
|
989
|
-
|
990
862
|
static #NEXT_IN = 4;
|
991
863
|
/** The offset at which the 'to' node's previous incoming edge is stored. */
|
992
|
-
|
993
864
|
static #PREV_IN = 5;
|
994
865
|
/** The offset at which the 'from' node's next outgoing edge is stored. */
|
995
|
-
|
996
866
|
static #NEXT_OUT = 6;
|
997
867
|
/** The offset at which the 'from' node's previous outgoing edge is stored. */
|
998
|
-
|
999
868
|
static #PREV_OUT = 7;
|
1000
|
-
/** The smallest functional edge map capacity. */
|
1001
869
|
|
870
|
+
/** The smallest functional edge map capacity. */
|
1002
871
|
static MIN_CAPACITY = 2;
|
1003
872
|
/** The largest possible edge map capacity. */
|
1004
|
-
|
1005
|
-
|
873
|
+
static MAX_CAPACITY = Math.floor(
|
874
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Invalid_array_length#what_went_wrong
|
1006
875
|
(2 ** 31 - 1 - EdgeTypeMap.HEADER_SIZE) / EdgeTypeMap.ITEM_SIZE / EdgeTypeMap.BUCKET_SIZE);
|
1007
876
|
/** The size after which to grow the capacity by the minimum factor. */
|
1008
|
-
|
1009
877
|
static PEAK_CAPACITY = 2 ** 18;
|
1010
|
-
|
1011
878
|
get deletes() {
|
1012
879
|
return this.data[EdgeTypeMap.#DELETES];
|
1013
880
|
}
|
1014
|
-
|
1015
881
|
getNextAddress() {
|
1016
882
|
let {
|
1017
883
|
ITEM_SIZE
|
1018
884
|
} = this.constructor;
|
1019
885
|
return this.addressableLimit + (this.count + this.deletes) * ITEM_SIZE;
|
1020
886
|
}
|
1021
|
-
|
1022
887
|
add(hash, from, to, type) {
|
1023
|
-
(0, _assert().default)(hash >= 0 && hash < this.capacity, `Invalid edge hash ${String(hash)}`);
|
1024
|
-
|
1025
|
-
let edge = this.getNextAddress();
|
1026
|
-
|
888
|
+
(0, _assert().default)(hash >= 0 && hash < this.capacity, `Invalid edge hash ${String(hash)}`);
|
889
|
+
// Use the next available edge address.
|
890
|
+
let edge = this.getNextAddress();
|
891
|
+
// Add our new edge to its hash bucket.
|
1027
892
|
this.link(hash, edge, type);
|
1028
893
|
this.data[edge + EdgeTypeMap.#FROM] = (0, _types.fromNodeId)(from);
|
1029
894
|
this.data[edge + EdgeTypeMap.#TO] = (0, _types.fromNodeId)(to);
|
1030
895
|
return edge;
|
1031
896
|
}
|
1032
|
-
|
1033
897
|
delete(edge) {
|
1034
898
|
this.data[edge + EdgeTypeMap.#FROM] = 0;
|
1035
899
|
this.data[edge + EdgeTypeMap.#TO] = 0;
|
1036
900
|
this.data[EdgeTypeMap.#DELETES]++;
|
1037
901
|
}
|
1038
|
-
|
1039
902
|
addressOf(hash, from, to, type) {
|
1040
903
|
let address = this.head(hash);
|
1041
|
-
|
1042
904
|
while (address !== null) {
|
1043
905
|
if (this.typeOf(address) === type && this.from(address) === from && this.to(address) === to) {
|
1044
906
|
return address;
|
1045
907
|
}
|
1046
|
-
|
1047
908
|
address = this.next(address);
|
1048
909
|
}
|
1049
|
-
|
1050
910
|
return null;
|
1051
911
|
}
|
1052
|
-
|
1053
912
|
from(edge) {
|
1054
913
|
return (0, _types.toNodeId)(this.data[edge + EdgeTypeMap.#FROM]);
|
1055
914
|
}
|
1056
|
-
|
1057
915
|
to(edge) {
|
1058
916
|
return (0, _types.toNodeId)(this.data[edge + EdgeTypeMap.#TO]);
|
1059
917
|
}
|
1060
|
-
|
1061
918
|
nextIn(edge) {
|
1062
919
|
return this.data[edge + EdgeTypeMap.#NEXT_IN] || null;
|
1063
920
|
}
|
1064
|
-
|
1065
921
|
prevIn(edge) {
|
1066
922
|
return this.data[edge + EdgeTypeMap.#PREV_IN] || null;
|
1067
923
|
}
|
1068
|
-
|
1069
924
|
linkIn(edge, next) {
|
1070
925
|
this.data[edge + EdgeTypeMap.#NEXT_IN] = next;
|
1071
926
|
this.data[next + EdgeTypeMap.#PREV_IN] = edge;
|
1072
927
|
}
|
1073
|
-
|
1074
928
|
unlinkIn(edge) {
|
1075
929
|
let next = this.nextIn(edge);
|
1076
930
|
let prev = this.prevIn(edge);
|
1077
931
|
this.data[edge + EdgeTypeMap.#NEXT_IN] = 0;
|
1078
932
|
this.data[edge + EdgeTypeMap.#PREV_IN] = 0;
|
1079
|
-
|
1080
933
|
if (next !== null && prev !== null) {
|
1081
934
|
this.data[prev + EdgeTypeMap.#NEXT_IN] = next;
|
1082
935
|
this.data[next + EdgeTypeMap.#PREV_IN] = prev;
|
@@ -1086,26 +939,21 @@ class EdgeTypeMap extends SharedTypeMap {
|
|
1086
939
|
this.data[prev + EdgeTypeMap.#NEXT_IN] = 0;
|
1087
940
|
}
|
1088
941
|
}
|
1089
|
-
|
1090
942
|
nextOut(edge) {
|
1091
943
|
return this.data[edge + EdgeTypeMap.#NEXT_OUT] || null;
|
1092
944
|
}
|
1093
|
-
|
1094
945
|
prevOut(edge) {
|
1095
946
|
return this.data[edge + EdgeTypeMap.#PREV_OUT] || null;
|
1096
947
|
}
|
1097
|
-
|
1098
948
|
linkOut(edge, next) {
|
1099
949
|
this.data[edge + EdgeTypeMap.#NEXT_OUT] = next;
|
1100
950
|
this.data[next + EdgeTypeMap.#PREV_OUT] = edge;
|
1101
951
|
}
|
1102
|
-
|
1103
952
|
unlinkOut(edge) {
|
1104
953
|
let next = this.nextOut(edge);
|
1105
954
|
let prev = this.prevOut(edge);
|
1106
955
|
this.data[edge + EdgeTypeMap.#NEXT_OUT] = 0;
|
1107
956
|
this.data[edge + EdgeTypeMap.#PREV_OUT] = 0;
|
1108
|
-
|
1109
957
|
if (next !== null && prev !== null) {
|
1110
958
|
this.data[prev + EdgeTypeMap.#NEXT_OUT] = next;
|
1111
959
|
this.data[next + EdgeTypeMap.#PREV_OUT] = prev;
|
@@ -1115,9 +963,8 @@ class EdgeTypeMap extends SharedTypeMap {
|
|
1115
963
|
this.data[prev + EdgeTypeMap.#NEXT_OUT] = 0;
|
1116
964
|
}
|
1117
965
|
}
|
1118
|
-
/** Create a hash of the edge connecting the `from` and `to` nodes. */
|
1119
|
-
|
1120
966
|
|
967
|
+
/** Create a hash of the edge connecting the `from` and `to` nodes. */
|
1121
968
|
hash(from, to, type) {
|
1122
969
|
// Each parameter is hashed by mixing its upper bits into its lower bits to
|
1123
970
|
// increase the likelihood that a change to any bit of the input will vary
|
@@ -1126,33 +973,27 @@ class EdgeTypeMap extends SharedTypeMap {
|
|
1126
973
|
let hash = 17;
|
1127
974
|
hash = hash * 37 + hash32shift(from);
|
1128
975
|
hash = hash * 37 + hash32shift(to);
|
1129
|
-
hash = hash * 37 + hash32shift(type);
|
1130
|
-
|
976
|
+
hash = hash * 37 + hash32shift(type);
|
977
|
+
// Finally, we map the hash to a value modulo the edge capacity.
|
1131
978
|
hash %= this.capacity;
|
1132
979
|
return hash;
|
1133
980
|
}
|
981
|
+
}
|
1134
982
|
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
983
|
+
// From https://gist.github.com/badboy/6267743#32-bit-mix-functions
|
1138
984
|
exports.EdgeTypeMap = EdgeTypeMap;
|
1139
|
-
|
1140
985
|
function hash32shift(key) {
|
1141
986
|
key = ~key + (key << 15); // key = (key << 15) - key - 1;
|
1142
|
-
|
1143
987
|
key = key ^ key >> 12;
|
1144
988
|
key = key + (key << 2);
|
1145
989
|
key = key ^ key >> 4;
|
1146
990
|
key = key * 2057; // key = (key + (key << 3)) + (key << 11);
|
1147
|
-
|
1148
991
|
key = key ^ key >> 16;
|
1149
992
|
return key;
|
1150
993
|
}
|
1151
|
-
|
1152
994
|
function interpolate(x, y, t) {
|
1153
995
|
return x + (y - x) * Math.min(1, Math.max(0, t));
|
1154
996
|
}
|
1155
|
-
|
1156
997
|
function increaseNodeCapacity(nodeCapacity) {
|
1157
998
|
let {
|
1158
999
|
MIN_CAPACITY,
|
@@ -1162,7 +1003,6 @@ function increaseNodeCapacity(nodeCapacity) {
|
|
1162
1003
|
(0, _assert().default)(newCapacity <= MAX_CAPACITY, 'Node capacity overflow!');
|
1163
1004
|
return Math.max(MIN_CAPACITY, newCapacity);
|
1164
1005
|
}
|
1165
|
-
|
1166
1006
|
function getNextEdgeCapacity(capacity, count, load) {
|
1167
1007
|
let {
|
1168
1008
|
MIN_CAPACITY,
|
@@ -1170,12 +1010,12 @@ function getNextEdgeCapacity(capacity, count, load) {
|
|
1170
1010
|
PEAK_CAPACITY
|
1171
1011
|
} = EdgeTypeMap;
|
1172
1012
|
let newCapacity = capacity;
|
1173
|
-
|
1174
1013
|
if (load > LOAD_FACTOR) {
|
1175
1014
|
// This is intended to strike a balance between growing the edge capacity
|
1176
1015
|
// in too small increments, which causes a lot of resizing, and growing
|
1177
1016
|
// the edge capacity in too large increments, which results in a lot of
|
1178
1017
|
// wasted memory.
|
1018
|
+
|
1179
1019
|
let growFactor = interpolate(MAX_GROW_FACTOR, MIN_GROW_FACTOR, capacity / PEAK_CAPACITY);
|
1180
1020
|
newCapacity = Math.round(capacity * growFactor);
|
1181
1021
|
} else if (load < UNLOAD_FACTOR) {
|
@@ -1183,7 +1023,6 @@ function getNextEdgeCapacity(capacity, count, load) {
|
|
1183
1023
|
// but this is only likely to occur when a lot of edges have been removed.
|
1184
1024
|
newCapacity = Math.round(capacity * SHRINK_FACTOR);
|
1185
1025
|
}
|
1186
|
-
|
1187
1026
|
(0, _assert().default)(newCapacity <= MAX_CAPACITY, 'Edge capacity overflow!');
|
1188
1027
|
return Math.max(MIN_CAPACITY, newCapacity);
|
1189
1028
|
}
|