@graphty/graph-format 0.0.0 → 0.2.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/LICENSE +21 -0
- package/README.md +216 -43
- package/dist/graph-format.d.ts +1 -0
- package/dist/graph-format.d.ts.map +1 -0
- package/dist/graph-format.js +15798 -0
- package/dist/graph-format.js.map +1 -0
- package/dist/src/builder/arena.d.ts +85 -0
- package/dist/src/builder/arena.d.ts.map +1 -0
- package/dist/src/builder/arena.js +154 -0
- package/dist/src/builder/arena.js.map +1 -0
- package/dist/src/builder/compact.d.ts +515 -0
- package/dist/src/builder/compact.d.ts.map +1 -0
- package/dist/src/builder/compact.js +1553 -0
- package/dist/src/builder/compact.js.map +1 -0
- package/dist/src/builder/counting-sort.d.ts +96 -0
- package/dist/src/builder/counting-sort.d.ts.map +1 -0
- package/dist/src/builder/counting-sort.js +430 -0
- package/dist/src/builder/counting-sort.js.map +1 -0
- package/dist/src/builder/freeze.d.ts +72 -0
- package/dist/src/builder/freeze.d.ts.map +1 -0
- package/dist/src/builder/freeze.js +548 -0
- package/dist/src/builder/freeze.js.map +1 -0
- package/dist/src/builder/graph-builder.d.ts +602 -0
- package/dist/src/builder/graph-builder.d.ts.map +1 -0
- package/dist/src/builder/graph-builder.js +1940 -0
- package/dist/src/builder/graph-builder.js.map +1 -0
- package/dist/src/builder/options.d.ts +16 -0
- package/dist/src/builder/options.d.ts.map +1 -0
- package/dist/src/builder/options.js +21 -0
- package/dist/src/builder/options.js.map +1 -0
- package/dist/src/columns/bitmap.d.ts +96 -0
- package/dist/src/columns/bitmap.d.ts.map +1 -0
- package/dist/src/columns/bitmap.js +184 -0
- package/dist/src/columns/bitmap.js.map +1 -0
- package/dist/src/columns/column.d.ts +220 -0
- package/dist/src/columns/column.d.ts.map +1 -0
- package/dist/src/columns/column.js +2109 -0
- package/dist/src/columns/column.js.map +1 -0
- package/dist/src/columns/dictionary.d.ts +60 -0
- package/dist/src/columns/dictionary.d.ts.map +1 -0
- package/dist/src/columns/dictionary.js +102 -0
- package/dist/src/columns/dictionary.js.map +1 -0
- package/dist/src/columns/growable.d.ts +198 -0
- package/dist/src/columns/growable.d.ts.map +1 -0
- package/dist/src/columns/growable.js +296 -0
- package/dist/src/columns/growable.js.map +1 -0
- package/dist/src/columns/infer.d.ts +121 -0
- package/dist/src/columns/infer.d.ts.map +1 -0
- package/dist/src/columns/infer.js +268 -0
- package/dist/src/columns/infer.js.map +1 -0
- package/dist/src/columns/remap.d.ts +143 -0
- package/dist/src/columns/remap.d.ts.map +1 -0
- package/dist/src/columns/remap.js +588 -0
- package/dist/src/columns/remap.js.map +1 -0
- package/dist/src/columns/table.d.ts +184 -0
- package/dist/src/columns/table.d.ts.map +1 -0
- package/dist/src/columns/table.js +485 -0
- package/dist/src/columns/table.js.map +1 -0
- package/dist/src/constants.d.ts +34 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +79 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/errors.d.ts +76 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/errors.js +32 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/ids/edge-id-index.d.ts +77 -0
- package/dist/src/ids/edge-id-index.d.ts.map +1 -0
- package/dist/src/ids/edge-id-index.js +155 -0
- package/dist/src/ids/edge-id-index.js.map +1 -0
- package/dist/src/ids/node-id-map.d.ts +280 -0
- package/dist/src/ids/node-id-map.d.ts.map +1 -0
- package/dist/src/ids/node-id-map.js +1139 -0
- package/dist/src/ids/node-id-map.js.map +1 -0
- package/dist/src/ids/string-store.d.ts +219 -0
- package/dist/src/ids/string-store.d.ts.map +1 -0
- package/dist/src/ids/string-store.js +526 -0
- package/dist/src/ids/string-store.js.map +1 -0
- package/dist/src/index.d.ts +30 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +29 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/populate/from-csr.d.ts +61 -0
- package/dist/src/populate/from-csr.d.ts.map +1 -0
- package/dist/src/populate/from-csr.js +706 -0
- package/dist/src/populate/from-csr.js.map +1 -0
- package/dist/src/populate/from-edge-arrays.d.ts +51 -0
- package/dist/src/populate/from-edge-arrays.d.ts.map +1 -0
- package/dist/src/populate/from-edge-arrays.js +217 -0
- package/dist/src/populate/from-edge-arrays.js.map +1 -0
- package/dist/src/populate/from-records.d.ts +51 -0
- package/dist/src/populate/from-records.d.ts.map +1 -0
- package/dist/src/populate/from-records.js +447 -0
- package/dist/src/populate/from-records.js.map +1 -0
- package/dist/src/snapshot/derived.d.ts +211 -0
- package/dist/src/snapshot/derived.d.ts.map +1 -0
- package/dist/src/snapshot/derived.js +1254 -0
- package/dist/src/snapshot/derived.js.map +1 -0
- package/dist/src/snapshot/graph-meta.d.ts +23 -0
- package/dist/src/snapshot/graph-meta.d.ts.map +1 -0
- package/dist/src/snapshot/graph-meta.js +159 -0
- package/dist/src/snapshot/graph-meta.js.map +1 -0
- package/dist/src/snapshot/graph-snapshot.d.ts +509 -0
- package/dist/src/snapshot/graph-snapshot.d.ts.map +1 -0
- package/dist/src/snapshot/graph-snapshot.js +1247 -0
- package/dist/src/snapshot/graph-snapshot.js.map +1 -0
- package/dist/src/snapshot/hash.d.ts +90 -0
- package/dist/src/snapshot/hash.d.ts.map +1 -0
- package/dist/src/snapshot/hash.js +228 -0
- package/dist/src/snapshot/hash.js.map +1 -0
- package/dist/src/snapshot/queries.d.ts +74 -0
- package/dist/src/snapshot/queries.d.ts.map +1 -0
- package/dist/src/snapshot/queries.js +131 -0
- package/dist/src/snapshot/queries.js.map +1 -0
- package/dist/src/snapshot/validate.d.ts +192 -0
- package/dist/src/snapshot/validate.d.ts.map +1 -0
- package/dist/src/snapshot/validate.js +990 -0
- package/dist/src/snapshot/validate.js.map +1 -0
- package/dist/src/snapshot/views.d.ts +274 -0
- package/dist/src/snapshot/views.d.ts.map +1 -0
- package/dist/src/snapshot/views.js +856 -0
- package/dist/src/snapshot/views.js.map +1 -0
- package/dist/src/types/builder.d.ts +456 -0
- package/dist/src/types/builder.d.ts.map +1 -0
- package/dist/src/types/builder.js +11 -0
- package/dist/src/types/builder.js.map +1 -0
- package/dist/src/types/columns.d.ts +615 -0
- package/dist/src/types/columns.d.ts.map +1 -0
- package/dist/src/types/columns.js +14 -0
- package/dist/src/types/columns.js.map +1 -0
- package/dist/src/types/index.d.ts +14 -0
- package/dist/src/types/index.d.ts.map +1 -0
- package/dist/src/types/index.js +11 -0
- package/dist/src/types/index.js.map +1 -0
- package/dist/src/types/internal.d.ts +172 -0
- package/dist/src/types/internal.d.ts.map +1 -0
- package/dist/src/types/internal.js +13 -0
- package/dist/src/types/internal.js.map +1 -0
- package/dist/src/types/snapshot.d.ts +801 -0
- package/dist/src/types/snapshot.d.ts.map +1 -0
- package/dist/src/types/snapshot.js +14 -0
- package/dist/src/types/snapshot.js.map +1 -0
- package/dist/src/types/wire.d.ts +196 -0
- package/dist/src/types/wire.d.ts.map +1 -0
- package/dist/src/types/wire.js +8 -0
- package/dist/src/types/wire.js.map +1 -0
- package/dist/src/util/mask.d.ts +55 -0
- package/dist/src/util/mask.d.ts.map +1 -0
- package/dist/src/util/mask.js +71 -0
- package/dist/src/util/mask.js.map +1 -0
- package/dist/src/util/options.d.ts +22 -0
- package/dist/src/util/options.d.ts.map +1 -0
- package/dist/src/util/options.js +49 -0
- package/dist/src/util/options.js.map +1 -0
- package/dist/src/util/shared-buffers.d.ts +42 -0
- package/dist/src/util/shared-buffers.d.ts.map +1 -0
- package/dist/src/util/shared-buffers.js +52 -0
- package/dist/src/util/shared-buffers.js.map +1 -0
- package/dist/src/util/typed-array.d.ts +205 -0
- package/dist/src/util/typed-array.d.ts.map +1 -0
- package/dist/src/util/typed-array.js +289 -0
- package/dist/src/util/typed-array.js.map +1 -0
- package/dist/src/wire/bytes.d.ts +69 -0
- package/dist/src/wire/bytes.d.ts.map +1 -0
- package/dist/src/wire/bytes.js +361 -0
- package/dist/src/wire/bytes.js.map +1 -0
- package/dist/src/wire/carried-views.d.ts +111 -0
- package/dist/src/wire/carried-views.d.ts.map +1 -0
- package/dist/src/wire/carried-views.js +286 -0
- package/dist/src/wire/carried-views.js.map +1 -0
- package/dist/src/wire/from-wire.d.ts +144 -0
- package/dist/src/wire/from-wire.d.ts.map +1 -0
- package/dist/src/wire/from-wire.js +1425 -0
- package/dist/src/wire/from-wire.js.map +1 -0
- package/dist/src/wire/to-wire.d.ts +171 -0
- package/dist/src/wire/to-wire.d.ts.map +1 -0
- package/dist/src/wire/to-wire.js +652 -0
- package/dist/src/wire/to-wire.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/package.json +72 -7
- package/src/builder/arena.ts +192 -0
- package/src/builder/compact.ts +1811 -0
- package/src/builder/counting-sort.ts +506 -0
- package/src/builder/freeze.ts +689 -0
- package/src/builder/graph-builder.ts +2264 -0
- package/src/builder/options.ts +29 -0
- package/src/columns/bitmap.ts +196 -0
- package/src/columns/column.ts +2574 -0
- package/src/columns/dictionary.ts +114 -0
- package/src/columns/growable.ts +350 -0
- package/src/columns/infer.ts +285 -0
- package/src/columns/remap.ts +663 -0
- package/src/columns/table.ts +568 -0
- package/src/constants.ts +89 -0
- package/src/errors.ts +115 -0
- package/src/ids/edge-id-index.ts +179 -0
- package/src/ids/node-id-map.ts +1340 -0
- package/src/ids/string-store.ts +589 -0
- package/src/index.ts +139 -0
- package/src/lib-resizable-array-buffer.d.ts +34 -0
- package/src/populate/from-csr.ts +802 -0
- package/src/populate/from-edge-arrays.ts +257 -0
- package/src/populate/from-records.ts +536 -0
- package/src/snapshot/derived.ts +1486 -0
- package/src/snapshot/graph-meta.ts +167 -0
- package/src/snapshot/graph-snapshot.ts +1503 -0
- package/src/snapshot/hash.ts +241 -0
- package/src/snapshot/queries.ts +136 -0
- package/src/snapshot/validate.ts +1108 -0
- package/src/snapshot/views.ts +943 -0
- package/src/types/builder.ts +480 -0
- package/src/types/columns.ts +727 -0
- package/src/types/index.ts +14 -0
- package/src/types/internal.ts +195 -0
- package/src/types/snapshot.ts +886 -0
- package/src/types/wire.ts +207 -0
- package/src/util/mask.ts +89 -0
- package/src/util/options.ts +57 -0
- package/src/util/shared-buffers.ts +56 -0
- package/src/util/typed-array.ts +371 -0
- package/src/wire/bytes.ts +440 -0
- package/src/wire/carried-views.ts +327 -0
- package/src/wire/from-wire.ts +1690 -0
- package/src/wire/to-wire.ts +755 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dictionary encoding for `dict` columns (design section 5.4): a string dictionary in first-seen (or
|
|
3
|
+
* declared) order, dense codes `0..size - 1`, a reverse map built eagerly by the builder and lazily
|
|
4
|
+
* by a snapshot, and the re-interning pass that merges one dictionary into another (`GraphBuilder.from`
|
|
5
|
+
* / `addGraph`, O(rows)). The lone-surrogate check every string value must pass (design section 11.3)
|
|
6
|
+
* is `assertWellFormedString`, over the one `hasLoneSurrogate` of the string store.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { INVALID_INDEX } from "../constants.js";
|
|
10
|
+
import { GraphFormatError } from "../errors.js";
|
|
11
|
+
import { detachString, hasLoneSurrogate } from "../ids/string-store.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Reject a string value that contains a lone surrogate with E_COLUMN_TYPE (details.reason "lone
|
|
15
|
+
* surrogate"); every string that enters a column or a dictionary passes through here.
|
|
16
|
+
* @param value - the string to check
|
|
17
|
+
* @param details - extra details (column, row, field) to attach to the error
|
|
18
|
+
*/
|
|
19
|
+
export function assertWellFormedString(value: string, details?: Readonly<Record<string, unknown>>): void {
|
|
20
|
+
if (hasLoneSurrogate(value)) {
|
|
21
|
+
throw new GraphFormatError("E_COLUMN_TYPE", "string value contains a lone surrogate", {
|
|
22
|
+
...details,
|
|
23
|
+
reason: "lone surrogate",
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Build the reverse map of a dictionary (value -> code). Used by a snapshot's DictColumn on the
|
|
30
|
+
* first codeOf() call; the builder keeps one live in its DictionaryBuilder instead.
|
|
31
|
+
* @param dictionary - the dictionary in code order
|
|
32
|
+
* @returns a map from value to code
|
|
33
|
+
*/
|
|
34
|
+
export function buildCodeMap(dictionary: readonly string[]): Map<string, number> {
|
|
35
|
+
const map = new Map<string, number>();
|
|
36
|
+
for (let i = 0; i < dictionary.length; i++) {
|
|
37
|
+
const value = dictionary[i];
|
|
38
|
+
if (!map.has(value)) {
|
|
39
|
+
map.set(value, i);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return map;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* An interning dictionary: values are appended in first-seen order and every value has exactly one
|
|
47
|
+
* dense code. The builder keeps one per dict column while staging and keeps interning into it across
|
|
48
|
+
* freezes; a frozen column takes a COPY of the values array (invariant I17, design section 5.4).
|
|
49
|
+
*/
|
|
50
|
+
export class DictionaryBuilder {
|
|
51
|
+
/** The dictionary in code order; copied by the frozen column. */
|
|
52
|
+
readonly values: string[];
|
|
53
|
+
|
|
54
|
+
private readonly codes: Map<string, number>;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Create a dictionary, optionally seeded with declared options (GEXF `<options>`) in order.
|
|
58
|
+
* @param initial - the initial members in code order; duplicates keep their first code
|
|
59
|
+
*/
|
|
60
|
+
constructor(initial?: readonly string[]) {
|
|
61
|
+
this.values = [];
|
|
62
|
+
this.codes = new Map();
|
|
63
|
+
if (initial !== undefined) {
|
|
64
|
+
for (const value of initial) {
|
|
65
|
+
this.intern(value);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Number of distinct values interned so far.
|
|
72
|
+
* @returns the dictionary size
|
|
73
|
+
*/
|
|
74
|
+
get size(): number {
|
|
75
|
+
return this.values.length;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The code of a value, appending it when unseen.
|
|
80
|
+
* @param value - the string to intern; E_COLUMN_TYPE when it holds a lone surrogate
|
|
81
|
+
* @returns the dense code of the value
|
|
82
|
+
*/
|
|
83
|
+
intern(value: string): number {
|
|
84
|
+
const existing = this.codes.get(value);
|
|
85
|
+
if (existing !== undefined) {
|
|
86
|
+
return existing;
|
|
87
|
+
}
|
|
88
|
+
assertWellFormedString(value);
|
|
89
|
+
const code = this.values.length;
|
|
90
|
+
const owned = detachString(value);
|
|
91
|
+
this.values.push(owned);
|
|
92
|
+
this.codes.set(owned, code);
|
|
93
|
+
return code;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* The code of a value without interning it.
|
|
98
|
+
* @param value - the string to look up
|
|
99
|
+
* @returns its code, or INVALID_INDEX when absent
|
|
100
|
+
*/
|
|
101
|
+
codeOf(value: string): number {
|
|
102
|
+
const code = this.codes.get(value);
|
|
103
|
+
return code === undefined ? INVALID_INDEX : code;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Whether a value has been interned.
|
|
108
|
+
* @param value - the string to test
|
|
109
|
+
* @returns true when present
|
|
110
|
+
*/
|
|
111
|
+
has(value: string): boolean {
|
|
112
|
+
return this.codes.has(value);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Growable typed staging for the builder (design section 6.2): structure-of-arrays typed arrays that
|
|
3
|
+
* grow by doubling (capacity rounded up to 16 elements, backing buffers a multiple of 64 bytes) into
|
|
4
|
+
* a resizable ArrayBuffer when the engine has one and by allocate-and-copy otherwise. The edge arrays
|
|
5
|
+
* (src, dst, weight), the incidence lists, the per-declaration column data and the alive / weightSet /
|
|
6
|
+
* validity bitmaps of the builder are all instances of the two classes here.
|
|
7
|
+
*
|
|
8
|
+
* Nothing a snapshot holds may alias staging (invariant I18): view() hands out a zero-copy window for
|
|
9
|
+
* the freeze passes to read, and trim() produces the exact-length copy over a fresh fixed-length
|
|
10
|
+
* buffer that a snapshot may keep.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { type TypedArrayData, type U32 } from "../types/index.js";
|
|
14
|
+
import {
|
|
15
|
+
allocateStagingBuffer,
|
|
16
|
+
growTypedArray,
|
|
17
|
+
HAS_RESIZABLE_ARRAY_BUFFER,
|
|
18
|
+
roundUp,
|
|
19
|
+
STAGING_ELEMENT_GRANULE,
|
|
20
|
+
stagingByteLength,
|
|
21
|
+
type TypedArrayCtor,
|
|
22
|
+
} from "../util/typed-array.js";
|
|
23
|
+
import { bitmapClearTrailing, bitmapCount, bitmapGet, bitmapSet, bitmapWordCount, bitmapWrite } from "./bitmap.js";
|
|
24
|
+
|
|
25
|
+
/** Every bit set: the fill word of a range of whole words. */
|
|
26
|
+
const ALL_ONES = 0xffffffff;
|
|
27
|
+
|
|
28
|
+
/** Construction options shared by GrowableTypedArray and GrowableBitmap. */
|
|
29
|
+
interface GrowableOptions {
|
|
30
|
+
/** Initial capacity in elements (bits for a GrowableBitmap); defaults to 0, so the first push allocates. */
|
|
31
|
+
readonly capacity?: number | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Whether backing buffers are resizable ArrayBuffers; defaults to the engine's HAS_RESIZABLE_ARRAY_BUFFER. Tests pass
|
|
34
|
+
* false to exercise the allocate-and-copy path on an engine that has the feature.
|
|
35
|
+
*/
|
|
36
|
+
readonly resizable?: boolean | undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* A typed array that grows by doubling (design section 6.2). `length` is the number of elements in
|
|
41
|
+
* use; `capacity` the allocated element count. Elements between length and capacity are unspecified
|
|
42
|
+
* until written. Growth may replace the backing view object, so hot loops that cache `array` re-read
|
|
43
|
+
* it after any call that can grow (push, pushAll, resize, ensureCapacity).
|
|
44
|
+
*/
|
|
45
|
+
export class GrowableTypedArray<T extends TypedArrayData> {
|
|
46
|
+
private readonly ctor: TypedArrayCtor<T>;
|
|
47
|
+
private readonly resizable: boolean;
|
|
48
|
+
private backing: T;
|
|
49
|
+
private used = 0;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Create an empty growable array.
|
|
53
|
+
* @param ctor - the typed-array class of the elements (Uint32Array, Float32Array, ...)
|
|
54
|
+
* @param options - initial capacity and buffer kind
|
|
55
|
+
*/
|
|
56
|
+
constructor(ctor: TypedArrayCtor<T>, options?: GrowableOptions) {
|
|
57
|
+
this.ctor = ctor;
|
|
58
|
+
this.resizable = options?.resizable ?? HAS_RESIZABLE_ARRAY_BUFFER;
|
|
59
|
+
this.backing = this.allocate(options?.capacity ?? 0);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The number of elements in use.
|
|
64
|
+
* @returns the length
|
|
65
|
+
*/
|
|
66
|
+
get length(): number {
|
|
67
|
+
return this.used;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The allocated element count; growth happens when length would exceed it.
|
|
72
|
+
* @returns the capacity
|
|
73
|
+
*/
|
|
74
|
+
get capacity(): number {
|
|
75
|
+
return this.backing.length;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The backing view over the whole capacity, for hot loops that index it directly. Its identity
|
|
80
|
+
* changes whenever the array grows.
|
|
81
|
+
* @returns the backing typed array
|
|
82
|
+
*/
|
|
83
|
+
get array(): T {
|
|
84
|
+
return this.backing;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Append one element.
|
|
89
|
+
* @param value - the element
|
|
90
|
+
* @returns the index it was written at (the old length)
|
|
91
|
+
*/
|
|
92
|
+
push(value: number): number {
|
|
93
|
+
const index = this.used;
|
|
94
|
+
if (index >= this.backing.length) {
|
|
95
|
+
this.backing = growTypedArray(this.backing, this.ctor, index + 1, this.resizable);
|
|
96
|
+
}
|
|
97
|
+
this.backing[index] = value;
|
|
98
|
+
this.used = index + 1;
|
|
99
|
+
return index;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Append many elements at once (bulk addEdges input).
|
|
104
|
+
* @param values - the elements, in order
|
|
105
|
+
* @returns the index the first one was written at (the old length)
|
|
106
|
+
*/
|
|
107
|
+
pushAll(values: ArrayLike<number>): number {
|
|
108
|
+
const index = this.used;
|
|
109
|
+
const end = index + values.length;
|
|
110
|
+
this.ensureCapacity(end);
|
|
111
|
+
this.backing.set(values, index);
|
|
112
|
+
this.used = end;
|
|
113
|
+
return index;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Read element i.
|
|
118
|
+
* @param i - the index, below length
|
|
119
|
+
* @returns the element
|
|
120
|
+
*/
|
|
121
|
+
get(i: number): number {
|
|
122
|
+
return this.backing[i];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Write element i.
|
|
127
|
+
* @param i - the index, below length
|
|
128
|
+
* @param value - the element
|
|
129
|
+
*/
|
|
130
|
+
set(i: number, value: number): void {
|
|
131
|
+
this.backing[i] = value;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Make sure at least `n` elements fit without a further reallocation.
|
|
136
|
+
* @param n - the capacity to guarantee
|
|
137
|
+
*/
|
|
138
|
+
ensureCapacity(n: number): void {
|
|
139
|
+
if (n > this.backing.length) {
|
|
140
|
+
this.backing = growTypedArray(this.backing, this.ctor, n, this.resizable);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Set the length: growing fills the new elements with `fill` (the incidence lists grow with
|
|
146
|
+
* INVALID_INDEX), shrinking drops the tail without releasing capacity.
|
|
147
|
+
* @param length - the new length
|
|
148
|
+
* @param fill - the value written into elements [old length, length); defaults to 0
|
|
149
|
+
*/
|
|
150
|
+
resize(length: number, fill = 0): void {
|
|
151
|
+
if (length > this.used) {
|
|
152
|
+
this.ensureCapacity(length);
|
|
153
|
+
this.backing.fill(fill, this.used, length);
|
|
154
|
+
}
|
|
155
|
+
this.used = length;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* A zero-copy window over the elements in use. It aliases staging, so it is for the freeze passes
|
|
160
|
+
* to read from and never for a snapshot to keep (invariant I18); it is invalidated by growth.
|
|
161
|
+
* @returns a view of exactly `length` elements
|
|
162
|
+
*/
|
|
163
|
+
view(): T {
|
|
164
|
+
return new this.ctor(this.backing.buffer, 0, this.used);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* An exact-length copy over a fresh fixed-length ArrayBuffer: what a snapshot may keep, and what a
|
|
169
|
+
* compaction writes back through.
|
|
170
|
+
* @returns a new typed array of `length` elements
|
|
171
|
+
*/
|
|
172
|
+
trim(): T {
|
|
173
|
+
const out = new this.ctor(new ArrayBuffer(this.used * this.ctor.BYTES_PER_ELEMENT), 0, this.used);
|
|
174
|
+
out.set(this.view());
|
|
175
|
+
return out;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Drop every element but keep the capacity (a builder reused after a merging freeze).
|
|
180
|
+
*/
|
|
181
|
+
clear(): void {
|
|
182
|
+
this.used = 0;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Drop the elements and the backing storage (freeze({ release: true }) and dispose(), design
|
|
187
|
+
* section 6.1). The array is usable again afterwards; the next push allocates.
|
|
188
|
+
*/
|
|
189
|
+
release(): void {
|
|
190
|
+
this.used = 0;
|
|
191
|
+
this.backing = this.allocate(0);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Allocate a backing view of at least `capacity` elements over a fresh staging buffer.
|
|
196
|
+
* @param capacity - the element count wanted; 0 allocates an empty view over an empty buffer
|
|
197
|
+
* @returns the view
|
|
198
|
+
*/
|
|
199
|
+
private allocate(capacity: number): T {
|
|
200
|
+
const bytesPerElement = this.ctor.BYTES_PER_ELEMENT;
|
|
201
|
+
const byteLength =
|
|
202
|
+
capacity === 0 ? 0 : stagingByteLength(roundUp(capacity, STAGING_ELEMENT_GRANULE), bytesPerElement);
|
|
203
|
+
return new this.ctor(allocateStagingBuffer(byteLength, this.resizable), 0, byteLength / bytesPerElement);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* A growable packed bitmap (design section 6.2: nodeAlive, edgeAlive, weightSet and the validity of
|
|
209
|
+
* every staging column), in the one LSB-first u32 layout of design section 5.3. `length` is in bits;
|
|
210
|
+
* every bit at or above it is kept clear so view() and trim() are exact bitmaps over `length` bits.
|
|
211
|
+
*/
|
|
212
|
+
export class GrowableBitmap {
|
|
213
|
+
private readonly words: GrowableTypedArray<U32>;
|
|
214
|
+
private bits = 0;
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Create an empty bitmap.
|
|
218
|
+
* @param options - initial capacity in bits and buffer kind
|
|
219
|
+
*/
|
|
220
|
+
constructor(options?: GrowableOptions) {
|
|
221
|
+
this.words = new GrowableTypedArray<U32>(Uint32Array, {
|
|
222
|
+
capacity: bitmapWordCount(options?.capacity ?? 0),
|
|
223
|
+
resizable: options?.resizable,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* The number of bits in use.
|
|
229
|
+
* @returns the length in bits
|
|
230
|
+
*/
|
|
231
|
+
get length(): number {
|
|
232
|
+
return this.bits;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Append one bit.
|
|
237
|
+
* @param value - the bit
|
|
238
|
+
* @returns the index it was written at (the old length)
|
|
239
|
+
*/
|
|
240
|
+
push(value: boolean): number {
|
|
241
|
+
const index = this.bits;
|
|
242
|
+
if (index % 32 === 0) {
|
|
243
|
+
this.words.push(0);
|
|
244
|
+
}
|
|
245
|
+
if (value) {
|
|
246
|
+
bitmapWrite(this.words.array, index, true);
|
|
247
|
+
}
|
|
248
|
+
this.bits = index + 1;
|
|
249
|
+
return index;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Read bit i.
|
|
254
|
+
* @param i - the bit index, below length
|
|
255
|
+
* @returns true when set
|
|
256
|
+
*/
|
|
257
|
+
get(i: number): boolean {
|
|
258
|
+
return bitmapGet(this.words.array, i);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Write bit i.
|
|
263
|
+
* @param i - the bit index, below length
|
|
264
|
+
* @param value - the bit
|
|
265
|
+
*/
|
|
266
|
+
set(i: number, value: boolean): void {
|
|
267
|
+
bitmapWrite(this.words.array, i, value);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* The number of set bits below length.
|
|
272
|
+
* @returns the count
|
|
273
|
+
*/
|
|
274
|
+
count(): number {
|
|
275
|
+
return bitmapCount(this.words.array, this.bits);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Set the length in bits: growing writes `fill` into the new bits, shrinking clears the dropped
|
|
280
|
+
* bits so the trailing-bits-clear convention holds.
|
|
281
|
+
* @param length - the new length in bits
|
|
282
|
+
* @param fill - the value of bits [old length, length); defaults to false
|
|
283
|
+
*/
|
|
284
|
+
resize(length: number, fill = false): void {
|
|
285
|
+
const oldBits = this.bits;
|
|
286
|
+
this.words.resize(bitmapWordCount(length), 0);
|
|
287
|
+
if (length < oldBits) {
|
|
288
|
+
bitmapClearTrailing(this.words.array, length);
|
|
289
|
+
} else if (fill && length > oldBits) {
|
|
290
|
+
this.setRange(oldBits, length);
|
|
291
|
+
}
|
|
292
|
+
this.bits = length;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Set every bit in [from, to): the ragged head and tail bit by bit, the whole words in between
|
|
297
|
+
* with one fill.
|
|
298
|
+
* @param from - the first bit to set
|
|
299
|
+
* @param to - one past the last bit to set
|
|
300
|
+
*/
|
|
301
|
+
private setRange(from: number, to: number): void {
|
|
302
|
+
const { array } = this.words;
|
|
303
|
+
let i = from;
|
|
304
|
+
while (i < to && i % 32 !== 0) {
|
|
305
|
+
bitmapSet(array, i++);
|
|
306
|
+
}
|
|
307
|
+
const fullEnd = to - (to % 32);
|
|
308
|
+
if (i < fullEnd) {
|
|
309
|
+
array.fill(ALL_ONES, i / 32, fullEnd / 32);
|
|
310
|
+
i = fullEnd;
|
|
311
|
+
}
|
|
312
|
+
while (i < to) {
|
|
313
|
+
bitmapSet(array, i++);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* A zero-copy window over the ceil(length / 32) words in use; aliases staging (invariant I18) and
|
|
319
|
+
* is invalidated by growth.
|
|
320
|
+
* @returns the words
|
|
321
|
+
*/
|
|
322
|
+
view(): U32 {
|
|
323
|
+
return this.words.view();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* An exact copy of the words in use over a fresh fixed-length ArrayBuffer: a validity bitmap or a
|
|
328
|
+
* bool column's data a snapshot may keep.
|
|
329
|
+
* @returns a new U32 of ceil(length / 32) words
|
|
330
|
+
*/
|
|
331
|
+
trim(): U32 {
|
|
332
|
+
return this.words.trim();
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Drop every bit but keep the capacity.
|
|
337
|
+
*/
|
|
338
|
+
clear(): void {
|
|
339
|
+
this.bits = 0;
|
|
340
|
+
this.words.clear();
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Drop the bits and the backing storage.
|
|
345
|
+
*/
|
|
346
|
+
release(): void {
|
|
347
|
+
this.bits = 0;
|
|
348
|
+
this.words.release();
|
|
349
|
+
}
|
|
350
|
+
}
|