@atlaspack/graph 3.2.1-canary.3354
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/LICENSE +201 -0
- package/lib/AdjacencyList.js +1415 -0
- package/lib/BitSet.js +80 -0
- package/lib/ContentGraph.js +80 -0
- package/lib/Graph.js +521 -0
- package/lib/index.js +54 -0
- package/lib/shared-buffer.js +28 -0
- package/lib/types.js +14 -0
- package/package.json +23 -0
- package/src/AdjacencyList.js +1655 -0
- package/src/BitSet.js +98 -0
- package/src/ContentGraph.js +96 -0
- package/src/Graph.js +740 -0
- package/src/index.js +9 -0
- package/src/shared-buffer.js +23 -0
- package/src/types.js +18 -0
- package/test/AdjacencyList.test.js +322 -0
- package/test/BitSet.test.js +110 -0
- package/test/ContentGraph.test.js +42 -0
- package/test/Graph.test.js +559 -0
- package/test/integration/adjacency-list-shared-array.js +20 -0
package/src/BitSet.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// @flow strict-local
|
|
2
|
+
|
|
3
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32#implementing_count_leading_ones_and_beyond
|
|
4
|
+
function ctz32(n: number): number {
|
|
5
|
+
if (n === 0) {
|
|
6
|
+
return 32;
|
|
7
|
+
}
|
|
8
|
+
let reversed = n & -n;
|
|
9
|
+
return 31 - Math.clz32(reversed);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class BitSet {
|
|
13
|
+
bits: Uint32Array;
|
|
14
|
+
|
|
15
|
+
constructor(maxBits: number) {
|
|
16
|
+
this.bits = new Uint32Array(Math.ceil(maxBits / 32));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
clone(): BitSet {
|
|
20
|
+
let res = new BitSet(this.capacity);
|
|
21
|
+
res.bits.set(this.bits);
|
|
22
|
+
return res;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static union(a: BitSet, b: BitSet): BitSet {
|
|
26
|
+
let res = a.clone();
|
|
27
|
+
res.union(b);
|
|
28
|
+
return res;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get capacity(): number {
|
|
32
|
+
return this.bits.length * 32;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
add(bit: number) {
|
|
36
|
+
let i = bit >>> 5;
|
|
37
|
+
let b = bit & 31;
|
|
38
|
+
this.bits[i] |= 1 << b;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
delete(bit: number) {
|
|
42
|
+
let i = bit >>> 5;
|
|
43
|
+
let b = bit & 31;
|
|
44
|
+
this.bits[i] &= ~(1 << b);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
has(bit: number): boolean {
|
|
48
|
+
let i = bit >>> 5;
|
|
49
|
+
let b = bit & 31;
|
|
50
|
+
return Boolean(this.bits[i] & (1 << b));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
empty(): boolean {
|
|
54
|
+
for (let k = 0; k < this.bits.length; k++) {
|
|
55
|
+
if (this.bits[k] !== 0) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
clear() {
|
|
64
|
+
this.bits.fill(0);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
intersect(other: BitSet) {
|
|
68
|
+
for (let i = 0; i < this.bits.length; i++) {
|
|
69
|
+
this.bits[i] &= other.bits[i];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
union(other: BitSet) {
|
|
74
|
+
for (let i = 0; i < this.bits.length; i++) {
|
|
75
|
+
this.bits[i] |= other.bits[i];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
remove(other: BitSet) {
|
|
80
|
+
for (let i = 0; i < this.bits.length; i++) {
|
|
81
|
+
this.bits[i] &= ~other.bits[i];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
forEach(fn: (bit: number) => void) {
|
|
86
|
+
// https://lemire.me/blog/2018/02/21/iterating-over-set-bits-quickly/
|
|
87
|
+
let bits = this.bits;
|
|
88
|
+
for (let k = 0; k < bits.length; k++) {
|
|
89
|
+
let v = bits[k];
|
|
90
|
+
while (v !== 0) {
|
|
91
|
+
let t = (v & -v) >>> 0;
|
|
92
|
+
// $FlowFixMe
|
|
93
|
+
fn((k << 5) + ctz32(v));
|
|
94
|
+
v ^= t;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// @flow strict-local
|
|
2
|
+
import type {ContentKey, NodeId} from './types';
|
|
3
|
+
|
|
4
|
+
import Graph, {type SerializedGraph, type GraphOpts} from './Graph';
|
|
5
|
+
import nullthrows from 'nullthrows';
|
|
6
|
+
|
|
7
|
+
export type ContentGraphOpts<TNode, TEdgeType: number = 1> = {|
|
|
8
|
+
...GraphOpts<TNode, TEdgeType>,
|
|
9
|
+
_contentKeyToNodeId: Map<ContentKey, NodeId>,
|
|
10
|
+
_nodeIdToContentKey: Map<NodeId, ContentKey>,
|
|
11
|
+
|};
|
|
12
|
+
export type SerializedContentGraph<TNode, TEdgeType: number = 1> = {|
|
|
13
|
+
...SerializedGraph<TNode, TEdgeType>,
|
|
14
|
+
_contentKeyToNodeId: Map<ContentKey, NodeId>,
|
|
15
|
+
|};
|
|
16
|
+
|
|
17
|
+
export default class ContentGraph<TNode, TEdgeType: number = 1> extends Graph<
|
|
18
|
+
TNode,
|
|
19
|
+
TEdgeType,
|
|
20
|
+
> {
|
|
21
|
+
_contentKeyToNodeId: Map<ContentKey, NodeId>;
|
|
22
|
+
_nodeIdToContentKey: Map<NodeId, ContentKey>;
|
|
23
|
+
|
|
24
|
+
constructor(opts: ?ContentGraphOpts<TNode, TEdgeType>) {
|
|
25
|
+
if (opts) {
|
|
26
|
+
let {_contentKeyToNodeId, _nodeIdToContentKey, ...rest} = opts;
|
|
27
|
+
super(rest);
|
|
28
|
+
this._contentKeyToNodeId = _contentKeyToNodeId;
|
|
29
|
+
this._nodeIdToContentKey = _nodeIdToContentKey;
|
|
30
|
+
} else {
|
|
31
|
+
super();
|
|
32
|
+
this._contentKeyToNodeId = new Map();
|
|
33
|
+
this._nodeIdToContentKey = new Map();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// $FlowFixMe[prop-missing]
|
|
38
|
+
static deserialize(
|
|
39
|
+
opts: ContentGraphOpts<TNode, TEdgeType>,
|
|
40
|
+
): ContentGraph<TNode, TEdgeType> {
|
|
41
|
+
return new ContentGraph(opts);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// $FlowFixMe[prop-missing]
|
|
45
|
+
serialize(): SerializedContentGraph<TNode, TEdgeType> {
|
|
46
|
+
// $FlowFixMe[prop-missing]
|
|
47
|
+
return {
|
|
48
|
+
...super.serialize(),
|
|
49
|
+
_contentKeyToNodeId: this._contentKeyToNodeId,
|
|
50
|
+
_nodeIdToContentKey: this._nodeIdToContentKey,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
addNodeByContentKey(contentKey: ContentKey, node: TNode): NodeId {
|
|
55
|
+
if (this.hasContentKey(contentKey)) {
|
|
56
|
+
throw new Error('Graph already has content key ' + contentKey);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let nodeId = super.addNode(node);
|
|
60
|
+
this._contentKeyToNodeId.set(contentKey, nodeId);
|
|
61
|
+
this._nodeIdToContentKey.set(nodeId, contentKey);
|
|
62
|
+
return nodeId;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
addNodeByContentKeyIfNeeded(contentKey: ContentKey, node: TNode): NodeId {
|
|
66
|
+
return this.hasContentKey(contentKey)
|
|
67
|
+
? this.getNodeIdByContentKey(contentKey)
|
|
68
|
+
: this.addNodeByContentKey(contentKey, node);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
getNodeByContentKey(contentKey: ContentKey): ?TNode {
|
|
72
|
+
let nodeId = this._contentKeyToNodeId.get(contentKey);
|
|
73
|
+
if (nodeId != null) {
|
|
74
|
+
return super.getNode(nodeId);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
getNodeIdByContentKey(contentKey: ContentKey): NodeId {
|
|
79
|
+
return nullthrows(
|
|
80
|
+
this._contentKeyToNodeId.get(contentKey),
|
|
81
|
+
`Expected content key ${contentKey} to exist`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
hasContentKey(contentKey: ContentKey): boolean {
|
|
86
|
+
return this._contentKeyToNodeId.has(contentKey);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
removeNode(nodeId: NodeId): void {
|
|
90
|
+
this._assertHasNodeId(nodeId);
|
|
91
|
+
let contentKey = nullthrows(this._nodeIdToContentKey.get(nodeId));
|
|
92
|
+
this._contentKeyToNodeId.delete(contentKey);
|
|
93
|
+
this._nodeIdToContentKey.delete(nodeId);
|
|
94
|
+
super.removeNode(nodeId);
|
|
95
|
+
}
|
|
96
|
+
}
|