@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.
Files changed (224) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +216 -43
  3. package/dist/graph-format.d.ts +1 -0
  4. package/dist/graph-format.d.ts.map +1 -0
  5. package/dist/graph-format.js +15798 -0
  6. package/dist/graph-format.js.map +1 -0
  7. package/dist/src/builder/arena.d.ts +85 -0
  8. package/dist/src/builder/arena.d.ts.map +1 -0
  9. package/dist/src/builder/arena.js +154 -0
  10. package/dist/src/builder/arena.js.map +1 -0
  11. package/dist/src/builder/compact.d.ts +515 -0
  12. package/dist/src/builder/compact.d.ts.map +1 -0
  13. package/dist/src/builder/compact.js +1553 -0
  14. package/dist/src/builder/compact.js.map +1 -0
  15. package/dist/src/builder/counting-sort.d.ts +96 -0
  16. package/dist/src/builder/counting-sort.d.ts.map +1 -0
  17. package/dist/src/builder/counting-sort.js +430 -0
  18. package/dist/src/builder/counting-sort.js.map +1 -0
  19. package/dist/src/builder/freeze.d.ts +72 -0
  20. package/dist/src/builder/freeze.d.ts.map +1 -0
  21. package/dist/src/builder/freeze.js +548 -0
  22. package/dist/src/builder/freeze.js.map +1 -0
  23. package/dist/src/builder/graph-builder.d.ts +602 -0
  24. package/dist/src/builder/graph-builder.d.ts.map +1 -0
  25. package/dist/src/builder/graph-builder.js +1940 -0
  26. package/dist/src/builder/graph-builder.js.map +1 -0
  27. package/dist/src/builder/options.d.ts +16 -0
  28. package/dist/src/builder/options.d.ts.map +1 -0
  29. package/dist/src/builder/options.js +21 -0
  30. package/dist/src/builder/options.js.map +1 -0
  31. package/dist/src/columns/bitmap.d.ts +96 -0
  32. package/dist/src/columns/bitmap.d.ts.map +1 -0
  33. package/dist/src/columns/bitmap.js +184 -0
  34. package/dist/src/columns/bitmap.js.map +1 -0
  35. package/dist/src/columns/column.d.ts +220 -0
  36. package/dist/src/columns/column.d.ts.map +1 -0
  37. package/dist/src/columns/column.js +2109 -0
  38. package/dist/src/columns/column.js.map +1 -0
  39. package/dist/src/columns/dictionary.d.ts +60 -0
  40. package/dist/src/columns/dictionary.d.ts.map +1 -0
  41. package/dist/src/columns/dictionary.js +102 -0
  42. package/dist/src/columns/dictionary.js.map +1 -0
  43. package/dist/src/columns/growable.d.ts +198 -0
  44. package/dist/src/columns/growable.d.ts.map +1 -0
  45. package/dist/src/columns/growable.js +296 -0
  46. package/dist/src/columns/growable.js.map +1 -0
  47. package/dist/src/columns/infer.d.ts +121 -0
  48. package/dist/src/columns/infer.d.ts.map +1 -0
  49. package/dist/src/columns/infer.js +268 -0
  50. package/dist/src/columns/infer.js.map +1 -0
  51. package/dist/src/columns/remap.d.ts +143 -0
  52. package/dist/src/columns/remap.d.ts.map +1 -0
  53. package/dist/src/columns/remap.js +588 -0
  54. package/dist/src/columns/remap.js.map +1 -0
  55. package/dist/src/columns/table.d.ts +184 -0
  56. package/dist/src/columns/table.d.ts.map +1 -0
  57. package/dist/src/columns/table.js +485 -0
  58. package/dist/src/columns/table.js.map +1 -0
  59. package/dist/src/constants.d.ts +34 -0
  60. package/dist/src/constants.d.ts.map +1 -0
  61. package/dist/src/constants.js +79 -0
  62. package/dist/src/constants.js.map +1 -0
  63. package/dist/src/errors.d.ts +76 -0
  64. package/dist/src/errors.d.ts.map +1 -0
  65. package/dist/src/errors.js +32 -0
  66. package/dist/src/errors.js.map +1 -0
  67. package/dist/src/ids/edge-id-index.d.ts +77 -0
  68. package/dist/src/ids/edge-id-index.d.ts.map +1 -0
  69. package/dist/src/ids/edge-id-index.js +155 -0
  70. package/dist/src/ids/edge-id-index.js.map +1 -0
  71. package/dist/src/ids/node-id-map.d.ts +280 -0
  72. package/dist/src/ids/node-id-map.d.ts.map +1 -0
  73. package/dist/src/ids/node-id-map.js +1139 -0
  74. package/dist/src/ids/node-id-map.js.map +1 -0
  75. package/dist/src/ids/string-store.d.ts +219 -0
  76. package/dist/src/ids/string-store.d.ts.map +1 -0
  77. package/dist/src/ids/string-store.js +526 -0
  78. package/dist/src/ids/string-store.js.map +1 -0
  79. package/dist/src/index.d.ts +30 -0
  80. package/dist/src/index.d.ts.map +1 -0
  81. package/dist/src/index.js +29 -0
  82. package/dist/src/index.js.map +1 -0
  83. package/dist/src/populate/from-csr.d.ts +61 -0
  84. package/dist/src/populate/from-csr.d.ts.map +1 -0
  85. package/dist/src/populate/from-csr.js +706 -0
  86. package/dist/src/populate/from-csr.js.map +1 -0
  87. package/dist/src/populate/from-edge-arrays.d.ts +51 -0
  88. package/dist/src/populate/from-edge-arrays.d.ts.map +1 -0
  89. package/dist/src/populate/from-edge-arrays.js +217 -0
  90. package/dist/src/populate/from-edge-arrays.js.map +1 -0
  91. package/dist/src/populate/from-records.d.ts +51 -0
  92. package/dist/src/populate/from-records.d.ts.map +1 -0
  93. package/dist/src/populate/from-records.js +447 -0
  94. package/dist/src/populate/from-records.js.map +1 -0
  95. package/dist/src/snapshot/derived.d.ts +211 -0
  96. package/dist/src/snapshot/derived.d.ts.map +1 -0
  97. package/dist/src/snapshot/derived.js +1254 -0
  98. package/dist/src/snapshot/derived.js.map +1 -0
  99. package/dist/src/snapshot/graph-meta.d.ts +23 -0
  100. package/dist/src/snapshot/graph-meta.d.ts.map +1 -0
  101. package/dist/src/snapshot/graph-meta.js +159 -0
  102. package/dist/src/snapshot/graph-meta.js.map +1 -0
  103. package/dist/src/snapshot/graph-snapshot.d.ts +509 -0
  104. package/dist/src/snapshot/graph-snapshot.d.ts.map +1 -0
  105. package/dist/src/snapshot/graph-snapshot.js +1247 -0
  106. package/dist/src/snapshot/graph-snapshot.js.map +1 -0
  107. package/dist/src/snapshot/hash.d.ts +90 -0
  108. package/dist/src/snapshot/hash.d.ts.map +1 -0
  109. package/dist/src/snapshot/hash.js +228 -0
  110. package/dist/src/snapshot/hash.js.map +1 -0
  111. package/dist/src/snapshot/queries.d.ts +74 -0
  112. package/dist/src/snapshot/queries.d.ts.map +1 -0
  113. package/dist/src/snapshot/queries.js +131 -0
  114. package/dist/src/snapshot/queries.js.map +1 -0
  115. package/dist/src/snapshot/validate.d.ts +192 -0
  116. package/dist/src/snapshot/validate.d.ts.map +1 -0
  117. package/dist/src/snapshot/validate.js +990 -0
  118. package/dist/src/snapshot/validate.js.map +1 -0
  119. package/dist/src/snapshot/views.d.ts +274 -0
  120. package/dist/src/snapshot/views.d.ts.map +1 -0
  121. package/dist/src/snapshot/views.js +856 -0
  122. package/dist/src/snapshot/views.js.map +1 -0
  123. package/dist/src/types/builder.d.ts +456 -0
  124. package/dist/src/types/builder.d.ts.map +1 -0
  125. package/dist/src/types/builder.js +11 -0
  126. package/dist/src/types/builder.js.map +1 -0
  127. package/dist/src/types/columns.d.ts +615 -0
  128. package/dist/src/types/columns.d.ts.map +1 -0
  129. package/dist/src/types/columns.js +14 -0
  130. package/dist/src/types/columns.js.map +1 -0
  131. package/dist/src/types/index.d.ts +14 -0
  132. package/dist/src/types/index.d.ts.map +1 -0
  133. package/dist/src/types/index.js +11 -0
  134. package/dist/src/types/index.js.map +1 -0
  135. package/dist/src/types/internal.d.ts +172 -0
  136. package/dist/src/types/internal.d.ts.map +1 -0
  137. package/dist/src/types/internal.js +13 -0
  138. package/dist/src/types/internal.js.map +1 -0
  139. package/dist/src/types/snapshot.d.ts +801 -0
  140. package/dist/src/types/snapshot.d.ts.map +1 -0
  141. package/dist/src/types/snapshot.js +14 -0
  142. package/dist/src/types/snapshot.js.map +1 -0
  143. package/dist/src/types/wire.d.ts +196 -0
  144. package/dist/src/types/wire.d.ts.map +1 -0
  145. package/dist/src/types/wire.js +8 -0
  146. package/dist/src/types/wire.js.map +1 -0
  147. package/dist/src/util/mask.d.ts +55 -0
  148. package/dist/src/util/mask.d.ts.map +1 -0
  149. package/dist/src/util/mask.js +71 -0
  150. package/dist/src/util/mask.js.map +1 -0
  151. package/dist/src/util/options.d.ts +22 -0
  152. package/dist/src/util/options.d.ts.map +1 -0
  153. package/dist/src/util/options.js +49 -0
  154. package/dist/src/util/options.js.map +1 -0
  155. package/dist/src/util/shared-buffers.d.ts +42 -0
  156. package/dist/src/util/shared-buffers.d.ts.map +1 -0
  157. package/dist/src/util/shared-buffers.js +52 -0
  158. package/dist/src/util/shared-buffers.js.map +1 -0
  159. package/dist/src/util/typed-array.d.ts +205 -0
  160. package/dist/src/util/typed-array.d.ts.map +1 -0
  161. package/dist/src/util/typed-array.js +289 -0
  162. package/dist/src/util/typed-array.js.map +1 -0
  163. package/dist/src/wire/bytes.d.ts +69 -0
  164. package/dist/src/wire/bytes.d.ts.map +1 -0
  165. package/dist/src/wire/bytes.js +361 -0
  166. package/dist/src/wire/bytes.js.map +1 -0
  167. package/dist/src/wire/carried-views.d.ts +111 -0
  168. package/dist/src/wire/carried-views.d.ts.map +1 -0
  169. package/dist/src/wire/carried-views.js +286 -0
  170. package/dist/src/wire/carried-views.js.map +1 -0
  171. package/dist/src/wire/from-wire.d.ts +144 -0
  172. package/dist/src/wire/from-wire.d.ts.map +1 -0
  173. package/dist/src/wire/from-wire.js +1425 -0
  174. package/dist/src/wire/from-wire.js.map +1 -0
  175. package/dist/src/wire/to-wire.d.ts +171 -0
  176. package/dist/src/wire/to-wire.d.ts.map +1 -0
  177. package/dist/src/wire/to-wire.js +652 -0
  178. package/dist/src/wire/to-wire.js.map +1 -0
  179. package/dist/tsconfig.build.tsbuildinfo +1 -0
  180. package/package.json +72 -7
  181. package/src/builder/arena.ts +192 -0
  182. package/src/builder/compact.ts +1811 -0
  183. package/src/builder/counting-sort.ts +506 -0
  184. package/src/builder/freeze.ts +689 -0
  185. package/src/builder/graph-builder.ts +2264 -0
  186. package/src/builder/options.ts +29 -0
  187. package/src/columns/bitmap.ts +196 -0
  188. package/src/columns/column.ts +2574 -0
  189. package/src/columns/dictionary.ts +114 -0
  190. package/src/columns/growable.ts +350 -0
  191. package/src/columns/infer.ts +285 -0
  192. package/src/columns/remap.ts +663 -0
  193. package/src/columns/table.ts +568 -0
  194. package/src/constants.ts +89 -0
  195. package/src/errors.ts +115 -0
  196. package/src/ids/edge-id-index.ts +179 -0
  197. package/src/ids/node-id-map.ts +1340 -0
  198. package/src/ids/string-store.ts +589 -0
  199. package/src/index.ts +139 -0
  200. package/src/lib-resizable-array-buffer.d.ts +34 -0
  201. package/src/populate/from-csr.ts +802 -0
  202. package/src/populate/from-edge-arrays.ts +257 -0
  203. package/src/populate/from-records.ts +536 -0
  204. package/src/snapshot/derived.ts +1486 -0
  205. package/src/snapshot/graph-meta.ts +167 -0
  206. package/src/snapshot/graph-snapshot.ts +1503 -0
  207. package/src/snapshot/hash.ts +241 -0
  208. package/src/snapshot/queries.ts +136 -0
  209. package/src/snapshot/validate.ts +1108 -0
  210. package/src/snapshot/views.ts +943 -0
  211. package/src/types/builder.ts +480 -0
  212. package/src/types/columns.ts +727 -0
  213. package/src/types/index.ts +14 -0
  214. package/src/types/internal.ts +195 -0
  215. package/src/types/snapshot.ts +886 -0
  216. package/src/types/wire.ts +207 -0
  217. package/src/util/mask.ts +89 -0
  218. package/src/util/options.ts +57 -0
  219. package/src/util/shared-buffers.ts +56 -0
  220. package/src/util/typed-array.ts +371 -0
  221. package/src/wire/bytes.ts +440 -0
  222. package/src/wire/carried-views.ts +327 -0
  223. package/src/wire/from-wire.ts +1690 -0
  224. 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
+ }