@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,2109 @@
1
+ /**
2
+ * Column implementations for every dtype of design section 5.1, the column factory that wraps raw
3
+ * storage (MutableColumnParts) into a Column, the declaration resolver (ColumnDecl -> ColumnMeta,
4
+ * design sections 5.3 and 5.5), the constructors the table uses for typed arrays, JS arrays and
5
+ * empty declared columns. Bitmap and padding arithmetic comes from bitmap.ts and util/typed-array.ts.
6
+ *
7
+ * Storage follows Apache Arrow's layout without the dependency: a typed values buffer, an optional
8
+ * validity bitmap (u32 words, LSB-first, ceil(rows / 32) words -- the one bitmap layout of the
9
+ * package, decision C13), an optional dictionary, offsets or child, and per-column metadata. No
10
+ * per-row objects exist anywhere. Column contents are frozen by contract unless meta.mutable is true
11
+ * (design section 5.8); the mutable gating throws E_COLUMN_IMMUTABLE.
12
+ */
13
+ import { INVALID_INDEX } from "../constants.js";
14
+ import { GraphFormatError } from "../errors.js";
15
+ import { encodeUtf8Rows } from "../ids/string-store.js";
16
+ import { claimHolder, noteShared } from "../util/shared-buffers.js";
17
+ import { canViewAsPaddedU32, isOverPlainBuffer, paddedU32View, padTo4 } from "../util/typed-array.js";
18
+ import { bitmapClear, bitmapCount, bitmapGet, bitmapSet, bitmapSlice, bitmapWordCount, makeBitmap } from "./bitmap.js";
19
+ import { assertWellFormedString, buildCodeMap, DictionaryBuilder } from "./dictionary.js";
20
+ import { coerceValue, inferValuesDtype } from "./infer.js";
21
+ const ALL_DTYPES = new Set([
22
+ "f32",
23
+ "f64",
24
+ "i32",
25
+ "u32",
26
+ "u8",
27
+ "bool",
28
+ "dict",
29
+ "string",
30
+ "list",
31
+ "json",
32
+ ]);
33
+ const NUMERIC_DTYPES = new Set(["f32", "f64", "i32", "u32", "u8"]);
34
+ const MAX_COMPONENTS = 16;
35
+ /** The deepest nesting a JSON value (a json cell, a default, options or extra) may have. */
36
+ const MAX_JSON_DEPTH = 256;
37
+ const I32_MIN = -2147483648;
38
+ const I32_MAX = 2147483647;
39
+ const U32_MAX = 0xffffffff;
40
+ const U8_MAX = 0xff;
41
+ const decoder = new TextDecoder();
42
+ /** Cached f32 copies of f64 columns for gpuView() (design section 10.4); dropped by markDirty(). */
43
+ const f32Cache = new WeakMap();
44
+ /**
45
+ * Allocate a u8 store from which a zero-copy padded u32 view is constructible (design section 5.7):
46
+ * the backing buffer is a multiple of 4 bytes even when the length is not.
47
+ * @param length - the number of bytes
48
+ * @returns a fresh Uint8Array over a padded buffer
49
+ */
50
+ export function allocU8(length) {
51
+ return new Uint8Array(new ArrayBuffer(padTo4(length)), 0, length);
52
+ }
53
+ /**
54
+ * GPU eligibility of a dtype (design section 10.4).
55
+ * @param dtype - the column dtype
56
+ * @returns "direct" for u32 / i32 / f32 / dict, "packed" for u8 / bool, "convert" for f64, "none" otherwise
57
+ */
58
+ export function gpuEligibility(dtype) {
59
+ switch (dtype) {
60
+ case "f32":
61
+ case "i32":
62
+ case "u32":
63
+ case "dict":
64
+ return "direct";
65
+ case "u8":
66
+ case "bool":
67
+ return "packed";
68
+ case "f64":
69
+ return "convert";
70
+ case "string":
71
+ case "list":
72
+ case "json":
73
+ return "none";
74
+ default: {
75
+ const name = dtype;
76
+ throw new GraphFormatError("E_COLUMN_TYPE", `unknown dtype ${name}`, { dtype: name });
77
+ }
78
+ }
79
+ }
80
+ /**
81
+ * Whether a dtype name is one a list child may have (any dtype but list).
82
+ * @param value - the dtype name
83
+ * @returns true for a scalar dtype
84
+ */
85
+ function isScalarDtype(value) {
86
+ return value !== "list" && ALL_DTYPES.has(value);
87
+ }
88
+ /**
89
+ * Whether a value is a plain object (prototype Object.prototype or null), the only object shape JSON
90
+ * carries.
91
+ * @param value - the value to test
92
+ * @returns true for a plain object
93
+ */
94
+ export function isPlainObject(value) {
95
+ if (typeof value !== "object" || value === null) {
96
+ return false;
97
+ }
98
+ const proto = Object.getPrototypeOf(value);
99
+ return proto === Object.prototype || proto === null;
100
+ }
101
+ /**
102
+ * Reject a metadata value that JSON cannot carry (design section 5.9): only null, booleans, numbers
103
+ * (non-finite and -0 included; the wire tags them), well-formed strings, arrays and plain objects of
104
+ * such values are accepted. E_COLUMN_TYPE with details.field otherwise.
105
+ * @param value - the value to check
106
+ * @param field - the metadata field name for the error details
107
+ * @param depth - the nesting depth of `value` (E_COLUMN_TYPE beyond MAX_JSON_DEPTH)
108
+ */
109
+ export function assertJsonValue(value, field, depth = 0) {
110
+ if (value === null || typeof value === "boolean" || typeof value === "number") {
111
+ return;
112
+ }
113
+ if (typeof value === "string") {
114
+ assertWellFormedString(value, { field });
115
+ return;
116
+ }
117
+ if (depth > MAX_JSON_DEPTH) {
118
+ throw new GraphFormatError("E_COLUMN_TYPE", `${field} is nested deeper than ${MAX_JSON_DEPTH} levels`, {
119
+ field,
120
+ reason: "nesting",
121
+ });
122
+ }
123
+ if (Array.isArray(value)) {
124
+ for (const item of value) {
125
+ assertJsonValue(item, field, depth + 1);
126
+ }
127
+ return;
128
+ }
129
+ if (isPlainObject(value)) {
130
+ for (const key of Object.keys(value)) {
131
+ assertJsonValue(value[key], field, depth + 1);
132
+ }
133
+ return;
134
+ }
135
+ throw new GraphFormatError("E_COLUMN_TYPE", `${field} is not a JSON value`, {
136
+ field,
137
+ found: value === undefined ? "undefined" : typeof value,
138
+ });
139
+ }
140
+ /**
141
+ * A deep copy of a JSON value (already checked by assertJsonValue): arrays and plain objects are
142
+ * copied at every level so a caller's live object never reaches a column's metadata, where a later
143
+ * mutation of the caller's object would change what `value()` returns for unset rows (design
144
+ * section 5.8). The copy is the column's own; like a typed array's contents it is immutable by
145
+ * contract.
146
+ * @param value - the JSON value
147
+ * @returns the copy (primitives are returned as they are)
148
+ */
149
+ function cloneJson(value) {
150
+ if (Array.isArray(value)) {
151
+ return value.map((item) => cloneJson(item));
152
+ }
153
+ if (isPlainObject(value)) {
154
+ const out = {};
155
+ for (const key of Object.keys(value)) {
156
+ out[key] = cloneJson(value[key]);
157
+ }
158
+ return out;
159
+ }
160
+ return value;
161
+ }
162
+ // ============================================================ declaration resolution
163
+ /**
164
+ * Whether a number is exactly representable in a numeric dtype: any number for f32 / f64, an integer
165
+ * in range for i32 / u32 / u8.
166
+ * @param dtype - the numeric dtype
167
+ * @param value - the number
168
+ * @returns true when the dtype stores the value without change
169
+ */
170
+ export function representableNumber(dtype, value) {
171
+ switch (dtype) {
172
+ case "f32":
173
+ case "f64":
174
+ return true;
175
+ case "i32":
176
+ return Number.isInteger(value) && value >= I32_MIN && value <= I32_MAX;
177
+ case "u32":
178
+ return Number.isInteger(value) && value >= 0 && value <= U32_MAX;
179
+ case "u8":
180
+ return Number.isInteger(value) && value >= 0 && value <= U8_MAX;
181
+ default: {
182
+ const name = dtype;
183
+ throw new GraphFormatError("E_COLUMN_TYPE", `unknown dtype ${name}`, { dtype: name });
184
+ }
185
+ }
186
+ }
187
+ /**
188
+ * The single number a declared numeric default reduces to, when it is representable: the number
189
+ * itself for components 1, or the common value of an array of `components` equal numbers.
190
+ * @param dtype - the numeric dtype
191
+ * @param components - the column stride
192
+ * @param value - the declared default
193
+ * @returns the fill number, or null when the default is not representable as one fill value
194
+ */
195
+ function numericDefaultFill(dtype, components, value) {
196
+ if (typeof value === "number") {
197
+ return representableNumber(dtype, value) ? value : null;
198
+ }
199
+ if (components > 1 && Array.isArray(value) && value.length === components) {
200
+ const first = value[0];
201
+ if (typeof first !== "number" || !representableNumber(dtype, first)) {
202
+ return null;
203
+ }
204
+ for (let k = 1; k < components; k++) {
205
+ if (!Object.is(value[k], first)) {
206
+ return null;
207
+ }
208
+ }
209
+ return first;
210
+ }
211
+ return null;
212
+ }
213
+ function typeError(field, message, extra) {
214
+ return new GraphFormatError("E_COLUMN_TYPE", message, { ...extra, field });
215
+ }
216
+ function resolveOrigin(origin) {
217
+ if (origin === undefined) {
218
+ return null;
219
+ }
220
+ if (!isPlainObject(origin)) {
221
+ throw typeError("origin", "origin must be a plain object");
222
+ }
223
+ const pick = (key) => {
224
+ const value = origin[key];
225
+ if (value === undefined || value === null) {
226
+ return null;
227
+ }
228
+ if (typeof value !== "string") {
229
+ throw typeError("origin", `origin.${key} must be a string or null`, { found: typeof value });
230
+ }
231
+ return value;
232
+ };
233
+ return {
234
+ format: pick("format"),
235
+ id: pick("id"),
236
+ title: pick("title"),
237
+ type: pick("type"),
238
+ namespace: pick("namespace"),
239
+ };
240
+ }
241
+ function resolveComponents(field, value, dtype) {
242
+ if (value === undefined) {
243
+ return 1;
244
+ }
245
+ if (!Number.isInteger(value) || value < 1 || value > MAX_COMPONENTS) {
246
+ throw typeError(field, `${field} must be an integer in 1..${MAX_COMPONENTS}, found ${value}`, { found: value });
247
+ }
248
+ if (value > 1 && !NUMERIC_DTYPES.has(dtype)) {
249
+ throw typeError(field, `${field} > 1 is only allowed for f32 / f64 / i32 / u32 / u8, not ${dtype}`, {
250
+ found: value,
251
+ dtype,
252
+ });
253
+ }
254
+ return value;
255
+ }
256
+ /**
257
+ * Resolve the fill value of design section 5.3: the explicit fill when given (type-checked against
258
+ * the dtype), else the declared default when it is representable in the dtype, else the dtype's zero
259
+ * (0 / false / "" ; INVALID_INDEX for a refersTo u32 column so unset references are never in range).
260
+ * @param dtype - the column dtype
261
+ * @param components - the stride
262
+ * @param explicit - the declared fill, if any
263
+ * @param defaultValue - the declared default, if any
264
+ * @param options - the declared options (the initial dictionary of a dict column)
265
+ * @param refersTo - the referenced index space, if any
266
+ * @returns the resolved fill
267
+ */
268
+ function resolveFill(dtype, components, explicit, defaultValue, options, refersTo) {
269
+ switch (dtype) {
270
+ case "f32":
271
+ case "f64":
272
+ case "i32":
273
+ case "u32":
274
+ case "u8": {
275
+ if (explicit !== undefined) {
276
+ if (typeof explicit !== "number") {
277
+ throw typeError("fill", `fill of a ${dtype} column must be a number`, { found: typeof explicit });
278
+ }
279
+ return explicit;
280
+ }
281
+ if (refersTo !== null) {
282
+ return INVALID_INDEX;
283
+ }
284
+ const fromDefault = numericDefaultFill(dtype, components, defaultValue);
285
+ return fromDefault ?? 0;
286
+ }
287
+ case "bool": {
288
+ if (explicit !== undefined) {
289
+ if (typeof explicit !== "boolean") {
290
+ throw typeError("fill", "fill of a bool column must be a boolean", { found: typeof explicit });
291
+ }
292
+ return explicit;
293
+ }
294
+ return typeof defaultValue === "boolean" ? defaultValue : false;
295
+ }
296
+ case "dict": {
297
+ if (explicit !== undefined) {
298
+ if (typeof explicit !== "string") {
299
+ throw typeError("fill", "fill of a dict column must be a string", { found: typeof explicit });
300
+ }
301
+ return explicit;
302
+ }
303
+ if (typeof defaultValue === "string" && (options === null || options.includes(defaultValue))) {
304
+ return defaultValue;
305
+ }
306
+ if (options !== null && options.length > 0 && typeof options[0] === "string") {
307
+ return options[0];
308
+ }
309
+ return "";
310
+ }
311
+ case "string": {
312
+ if (explicit !== undefined) {
313
+ if (typeof explicit !== "string") {
314
+ throw typeError("fill", "fill of a string column must be a string", { found: typeof explicit });
315
+ }
316
+ return explicit;
317
+ }
318
+ return "";
319
+ }
320
+ case "list":
321
+ case "json": {
322
+ // the nominal fill of a list / json column is ""; unset rows physically hold an empty list / undefined
323
+ if (explicit !== undefined && explicit !== "") {
324
+ throw typeError("fill", `a ${dtype} column takes no fill value`, { found: explicit });
325
+ }
326
+ return "";
327
+ }
328
+ default: {
329
+ const name = dtype;
330
+ throw new GraphFormatError("E_COLUMN_TYPE", `unknown dtype ${name}`, { dtype: name });
331
+ }
332
+ }
333
+ }
334
+ /**
335
+ * Resolve a column declaration into the complete metadata of design section 5.5: every field
336
+ * present, null for none, defaults applied, every rule checked (components, list child, refersTo,
337
+ * JSON-ness of default / options / extra, fill typing). E_COLUMN_TYPE with details.field on every
338
+ * violation.
339
+ * @param name - the column name (overrides decl.name)
340
+ * @param domain - the table the column belongs to
341
+ * @param decl - the declaration; dtype is required
342
+ * @returns the resolved metadata, frozen
343
+ */
344
+ export function resolveColumnMeta(name, domain, decl) {
345
+ const { dtype } = decl;
346
+ if (dtype === undefined || !ALL_DTYPES.has(dtype)) {
347
+ throw typeError("dtype", `column "${name}" has no valid dtype`, { name, found: dtype });
348
+ }
349
+ const components = resolveComponents("components", decl.components, dtype);
350
+ let itemDtype = null;
351
+ let itemComponents = null;
352
+ if (dtype === "list") {
353
+ const { itemDtype: declaredItem } = decl;
354
+ if (declaredItem === undefined || !isScalarDtype(declaredItem)) {
355
+ throw typeError("itemDtype", `list column "${name}" needs a scalar itemDtype`, {
356
+ name,
357
+ found: declaredItem,
358
+ });
359
+ }
360
+ itemDtype = declaredItem;
361
+ itemComponents = resolveComponents("itemComponents", decl.itemComponents, declaredItem);
362
+ }
363
+ else if (decl.itemDtype !== undefined || decl.itemComponents !== undefined) {
364
+ throw typeError("itemDtype", `itemDtype / itemComponents are only allowed on list columns, not ${dtype}`, {
365
+ name,
366
+ dtype,
367
+ });
368
+ }
369
+ const refersTo = decl.refersTo ?? null;
370
+ if (refersTo !== null) {
371
+ if (refersTo !== "node" && refersTo !== "edge") {
372
+ throw typeError("refersTo", `refersTo must be "node" or "edge"`, { found: refersTo });
373
+ }
374
+ const indexColumn = (dtype === "u32" && components === 1) || (dtype === "list" && itemDtype === "u32" && itemComponents === 1);
375
+ if (!indexColumn) {
376
+ throw typeError("refersTo", `refersTo requires a u32 column or a list of u32, not ${dtype}`, {
377
+ name,
378
+ dtype,
379
+ });
380
+ }
381
+ }
382
+ const defaultValue = decl.default;
383
+ if (defaultValue !== undefined) {
384
+ assertJsonValue(defaultValue, "default");
385
+ }
386
+ let options = null;
387
+ if (decl.options !== undefined) {
388
+ if (!Array.isArray(decl.options)) {
389
+ throw typeError("options", "options must be an array");
390
+ }
391
+ assertJsonValue(decl.options, "options");
392
+ if (dtype === "dict") {
393
+ for (const option of decl.options) {
394
+ if (typeof option !== "string") {
395
+ throw typeError("options", "options of a dict column must be strings", { found: typeof option });
396
+ }
397
+ }
398
+ }
399
+ options = Object.freeze(cloneJson([...decl.options]));
400
+ }
401
+ const extra = decl.extra ?? {};
402
+ if (!isPlainObject(extra)) {
403
+ throw typeError("extra", "extra must be a plain object");
404
+ }
405
+ assertJsonValue(extra, "extra");
406
+ const role = decl.role ?? null;
407
+ if (role !== null && typeof role !== "string") {
408
+ throw typeError("role", "role must be a string", { found: typeof role });
409
+ }
410
+ const fill = resolveFill(dtype, components, decl.fill, defaultValue, options, refersTo);
411
+ return Object.freeze({
412
+ name,
413
+ domain,
414
+ dtype,
415
+ components,
416
+ itemDtype,
417
+ itemComponents,
418
+ nullable: decl.nullable ?? true,
419
+ mutable: decl.mutable ?? false,
420
+ role,
421
+ refersTo,
422
+ unique: decl.unique ?? false,
423
+ default: cloneJson(defaultValue),
424
+ fill,
425
+ options,
426
+ origin: resolveOrigin(decl.origin),
427
+ dynamic: decl.dynamic ?? false,
428
+ extra: Object.freeze(cloneJson({ ...extra })),
429
+ });
430
+ }
431
+ /**
432
+ * The declaration that reproduces a column's metadata, for patching an existing column's meta
433
+ * (rename, move between tables, set() with a decl patch).
434
+ * @param meta - the metadata to convert
435
+ * @returns an equivalent declaration patch
436
+ */
437
+ export function metaToDecl(meta) {
438
+ return {
439
+ name: meta.name,
440
+ dtype: meta.dtype,
441
+ components: meta.components,
442
+ itemDtype: meta.itemDtype ?? undefined,
443
+ itemComponents: meta.itemComponents ?? undefined,
444
+ nullable: meta.nullable,
445
+ mutable: meta.mutable,
446
+ role: meta.role ?? undefined,
447
+ refersTo: meta.refersTo ?? undefined,
448
+ unique: meta.unique,
449
+ default: meta.default,
450
+ fill: meta.fill,
451
+ options: meta.options ?? undefined,
452
+ origin: meta.origin ?? undefined,
453
+ dynamic: meta.dynamic,
454
+ extra: meta.extra,
455
+ };
456
+ }
457
+ // ============================================================ column base
458
+ /**
459
+ * Members shared by every column implementation (design sections 5.3, 5.7 and 5.8).
460
+ */
461
+ class ColumnImpl {
462
+ constructor(dtype, meta, length, validity, nullCount) {
463
+ this.versionValue = 0;
464
+ this.defaultCache = null;
465
+ this.dtype = dtype;
466
+ this.meta = meta;
467
+ this.length = length;
468
+ this.gpu = gpuEligibility(dtype);
469
+ this.validityWords = validity;
470
+ this.nullCountValue = nullCount;
471
+ this.defaultValue = cloneJson(meta.default);
472
+ }
473
+ get validity() {
474
+ return this.validityWords;
475
+ }
476
+ get nullCount() {
477
+ return this.nullCountValue;
478
+ }
479
+ get version() {
480
+ return this.versionValue;
481
+ }
482
+ get paddedByteLength() {
483
+ return this.byteLength;
484
+ }
485
+ get validityByteLength() {
486
+ return this.validityWords === null ? 0 : this.validityWords.byteLength;
487
+ }
488
+ /**
489
+ * Whether the column's storage was transferred away (design sections 9.1 and 11.3): derived from
490
+ * the array state, since a transferred ArrayBuffer leaves zero-length views behind.
491
+ * @returns true when a typed array the column needs is empty although the column has rows
492
+ */
493
+ get detached() {
494
+ if (this.length === 0) {
495
+ return false;
496
+ }
497
+ const words = this.validityWords;
498
+ return (words !== null && words.length === 0) || this.storageDetached();
499
+ }
500
+ isSet(row) {
501
+ if (!(row >= 0 && row < this.length)) {
502
+ return false;
503
+ }
504
+ const words = this.validityWords;
505
+ if (words === null) {
506
+ return true;
507
+ }
508
+ if (words.length === 0) {
509
+ throw this.detachedError();
510
+ }
511
+ return bitmapGet(words, row);
512
+ }
513
+ value(row) {
514
+ this.assertAttached();
515
+ if (!(Number.isInteger(row) && row >= 0 && row < this.length)) {
516
+ throw new GraphFormatError("E_INDEX_RANGE", `row ${row} is out of range for a column of ${this.length} rows`, {
517
+ row,
518
+ length: this.length,
519
+ column: this.meta.name,
520
+ });
521
+ }
522
+ const words = this.validityWords;
523
+ if (words !== null && !bitmapGet(words, row)) {
524
+ // an unset row reads the column's own copy of the declared default; a structured default is
525
+ // handed out as a fresh copy so no caller can change what later reads see (design section 5.8)
526
+ const { defaultValue } = this;
527
+ return defaultValue === undefined ? undefined : cloneJson(defaultValue);
528
+ }
529
+ return this.readValue(row);
530
+ }
531
+ materializeDefault() {
532
+ this.assertAttached();
533
+ if (this.meta.default === undefined) {
534
+ throw new GraphFormatError("E_NO_DEFAULT", `column "${this.meta.name}" declares no default`, {
535
+ column: this.meta.name,
536
+ });
537
+ }
538
+ if (this.nullCountValue === 0 || this.fillIsDefault()) {
539
+ return this.self();
540
+ }
541
+ this.defaultCache ?? (this.defaultCache = this.withDefaults());
542
+ return this.defaultCache;
543
+ }
544
+ paddedU32View() {
545
+ throw new GraphFormatError("E_GPU_INELIGIBLE", `a ${this.dtype} column has no u32 view`, {
546
+ column: this.meta.name,
547
+ dtype: this.dtype,
548
+ });
549
+ }
550
+ markDirty() {
551
+ this.assertMutable("markDirty");
552
+ this.assertAttached();
553
+ this.versionValue++;
554
+ this.defaultCache = null;
555
+ f32Cache.delete(this);
556
+ const words = this.validityWords;
557
+ if (words !== null) {
558
+ this.nullCountValue = this.length - bitmapCount(words, this.length);
559
+ }
560
+ }
561
+ mutableValidity() {
562
+ this.assertMutable("mutableValidity");
563
+ return this.validityWords;
564
+ }
565
+ setAll() {
566
+ this.assertMutable("setAll");
567
+ this.validityWords = null;
568
+ this.nullCountValue = 0;
569
+ this.versionValue++;
570
+ this.defaultCache = null;
571
+ }
572
+ assertAttached() {
573
+ if (this.detached) {
574
+ throw this.detachedError();
575
+ }
576
+ }
577
+ detachedError() {
578
+ return new GraphFormatError("E_DETACHED", `column "${this.meta.name}" was transferred away`, {
579
+ column: this.meta.name,
580
+ });
581
+ }
582
+ assertMutable(operation) {
583
+ if (!this.meta.mutable) {
584
+ throw new GraphFormatError("E_COLUMN_IMMUTABLE", `${operation}() on immutable column "${this.meta.name}"`, {
585
+ column: this.meta.name,
586
+ operation,
587
+ });
588
+ }
589
+ }
590
+ checkRange(start, end) {
591
+ this.assertAttached();
592
+ if (!(Number.isInteger(start) && Number.isInteger(end) && start >= 0 && start <= end && end <= this.length)) {
593
+ throw new GraphFormatError("E_INDEX_RANGE", `row range [${start}, ${end}) is invalid for ${this.length} rows`, {
594
+ start,
595
+ end,
596
+ length: this.length,
597
+ column: this.meta.name,
598
+ });
599
+ }
600
+ }
601
+ /**
602
+ * The validity bitmap and null count of the row range [start, end); zero-copy at word boundaries.
603
+ * @param start - first row
604
+ * @param end - one past the last row
605
+ * @returns the bitmap over the range (null when every row is set) and its null count
606
+ */
607
+ sliceValidity(start, end) {
608
+ const words = this.validityWords;
609
+ if (words === null) {
610
+ return { validity: null, nullCount: 0 };
611
+ }
612
+ const rows = end - start;
613
+ const validity = start % 32 === 0
614
+ ? shareView(words.subarray(start >>> 5, (start >>> 5) + bitmapWordCount(rows)))
615
+ : bitmapSlice(words, start, end);
616
+ return { validity, nullCount: rows - bitmapCount(validity, rows) };
617
+ }
618
+ cloneValidity() {
619
+ this.assertAttached();
620
+ return this.validityWords === null ? null : this.validityWords.slice();
621
+ }
622
+ }
623
+ /**
624
+ * Record that a zero-copy view aliases a buffer another column still holds (the owner count of
625
+ * design section 9.1): a slice attached to a second snapshot must never let a transfer detach the
626
+ * source, so the shared buffer is copied by `toWire({ transfer: true })` and `transferables()`.
627
+ * @param view - a subarray of a column's storage
628
+ * @returns the view
629
+ */
630
+ function shareView(view) {
631
+ noteShared(view.buffer);
632
+ return view;
633
+ }
634
+ // ============================================================ fixed-width numeric columns
635
+ class FixedColumnImpl extends ColumnImpl {
636
+ constructor(dtype, meta, length, data, validity, nullCount) {
637
+ super(dtype, meta, length, validity, nullCount);
638
+ this.data = data;
639
+ }
640
+ get byteLength() {
641
+ return this.data.byteLength + this.validityByteLength;
642
+ }
643
+ mutableData() {
644
+ this.assertMutable("mutableData");
645
+ this.assertAttached();
646
+ return this.data;
647
+ }
648
+ storageDetached() {
649
+ return this.data.length === 0 && this.length * this.meta.components > 0;
650
+ }
651
+ slice(start, end) {
652
+ this.checkRange(start, end);
653
+ const { components } = this.meta;
654
+ const data = this.sliceData(start * components, end * components);
655
+ const { validity, nullCount } = this.sliceValidity(start, end);
656
+ return this.wrap(this.meta, end - start, data, validity, nullCount);
657
+ }
658
+ clone() {
659
+ this.assertAttached();
660
+ return this.wrap(this.meta, this.length, this.copyData(), this.cloneValidity(), this.nullCountValue);
661
+ }
662
+ readValue(row) {
663
+ const { components } = this.meta;
664
+ if (components === 1) {
665
+ return this.data[row];
666
+ }
667
+ return this.data.subarray(row * components, (row + 1) * components);
668
+ }
669
+ fillIsDefault() {
670
+ const { fill, default: defaultValue, components } = this.meta;
671
+ if (typeof fill !== "number") {
672
+ return false;
673
+ }
674
+ return numericDefaultFill(this.dtype, components, defaultValue) === fill;
675
+ }
676
+ withDefaults() {
677
+ const { components, default: defaultValue } = this.meta;
678
+ const data = this.copyData();
679
+ const words = this.validityWords;
680
+ if (words !== null) {
681
+ for (let row = 0; row < this.length; row++) {
682
+ if (!bitmapGet(words, row)) {
683
+ writeNumeric(this.dtype, data, row * components, components, defaultValue, this.meta.name, row);
684
+ }
685
+ }
686
+ }
687
+ return this.wrap(this.meta, this.length, data, this.cloneValidity(), this.nullCountValue);
688
+ }
689
+ sliceData(start, end) {
690
+ return shareView(this.data.subarray(start, end));
691
+ }
692
+ copyData() {
693
+ return this.data.slice();
694
+ }
695
+ }
696
+ /**
697
+ * Write one row's numeric value (a number, or an array of `components` numbers) into a typed array,
698
+ * rejecting values the dtype cannot represent (a non-integer or out-of-range number for i32 / u32 /
699
+ * u8) with E_COLUMN_TYPE instead of letting the typed array wrap them.
700
+ * @param dtype - the numeric dtype
701
+ * @param data - the destination
702
+ * @param at - the element offset of the row
703
+ * @param components - the stride
704
+ * @param value - the value to write
705
+ * @param column - the column name for error details
706
+ * @param row - the row for error details
707
+ */
708
+ function writeNumeric(dtype, data, at, components, value, column, row) {
709
+ if (typeof value === "number") {
710
+ assertRepresentable(dtype, value, column, row);
711
+ if (components === 1) {
712
+ data[at] = value;
713
+ }
714
+ else {
715
+ data.fill(value, at, at + components);
716
+ }
717
+ return;
718
+ }
719
+ if (components === 1 || !isArrayLikeNumbers(value, components)) {
720
+ throw new GraphFormatError("E_COLUMN_TYPE", `row ${row} of ${dtype} column "${column}" is not ${components} number(s)`, { column, row, components, found: typeof value });
721
+ }
722
+ for (let k = 0; k < components; k++) {
723
+ assertRepresentable(dtype, value[k], column, row);
724
+ }
725
+ for (let k = 0; k < components; k++) {
726
+ data[at + k] = value[k];
727
+ }
728
+ }
729
+ function assertRepresentable(dtype, value, column, row) {
730
+ if (!representableNumber(dtype, value)) {
731
+ throw new GraphFormatError("E_COLUMN_TYPE", `row ${row} of ${dtype} column "${column}": ${value} is not representable`, { column, row, value });
732
+ }
733
+ }
734
+ /**
735
+ * Whether a value is an array-like of exactly `length` numbers (a multi-component cell).
736
+ * @param value - the value
737
+ * @param length - the required length
738
+ * @returns true for a numeric vector of that length
739
+ */
740
+ export function isArrayLikeNumbers(value, length) {
741
+ if (typeof value !== "object" || value === null) {
742
+ return false;
743
+ }
744
+ const arrayLike = value;
745
+ if (arrayLike.length !== length) {
746
+ return false;
747
+ }
748
+ for (let k = 0; k < length; k++) {
749
+ if (typeof arrayLike[k] !== "number") {
750
+ return false;
751
+ }
752
+ }
753
+ return true;
754
+ }
755
+ class F32ColumnImpl extends FixedColumnImpl {
756
+ constructor(meta, length, data, validity, nullCount) {
757
+ super("f32", meta, length, data, validity, nullCount);
758
+ }
759
+ self() {
760
+ return this;
761
+ }
762
+ wrap(meta, length, data, validity, nullCount) {
763
+ return new F32ColumnImpl(meta, length, data, validity, nullCount);
764
+ }
765
+ }
766
+ class F64ColumnImpl extends FixedColumnImpl {
767
+ constructor(meta, length, data, validity, nullCount) {
768
+ super("f64", meta, length, data, validity, nullCount);
769
+ }
770
+ self() {
771
+ return this;
772
+ }
773
+ wrap(meta, length, data, validity, nullCount) {
774
+ return new F64ColumnImpl(meta, length, data, validity, nullCount);
775
+ }
776
+ }
777
+ class I32ColumnImpl extends FixedColumnImpl {
778
+ constructor(meta, length, data, validity, nullCount) {
779
+ super("i32", meta, length, data, validity, nullCount);
780
+ }
781
+ self() {
782
+ return this;
783
+ }
784
+ wrap(meta, length, data, validity, nullCount) {
785
+ return new I32ColumnImpl(meta, length, data, validity, nullCount);
786
+ }
787
+ }
788
+ class U32ColumnImpl extends FixedColumnImpl {
789
+ constructor(meta, length, data, validity, nullCount) {
790
+ super("u32", meta, length, data, validity, nullCount);
791
+ }
792
+ paddedU32View() {
793
+ return this.data;
794
+ }
795
+ self() {
796
+ return this;
797
+ }
798
+ wrap(meta, length, data, validity, nullCount) {
799
+ return new U32ColumnImpl(meta, length, data, validity, nullCount);
800
+ }
801
+ }
802
+ class U8ColumnImpl extends FixedColumnImpl {
803
+ constructor(meta, length, data, validity, nullCount) {
804
+ super("u8", meta, length, data, validity, nullCount);
805
+ }
806
+ get paddedByteLength() {
807
+ return padTo4(this.data.byteLength) + this.validityByteLength;
808
+ }
809
+ paddedU32View() {
810
+ return paddedU32View(this.data);
811
+ }
812
+ self() {
813
+ return this;
814
+ }
815
+ wrap(meta, length, data, validity, nullCount) {
816
+ return new U8ColumnImpl(meta, length, data, validity, nullCount);
817
+ }
818
+ sliceData(start, end) {
819
+ if (start % 4 === 0) {
820
+ return shareView(this.data.subarray(start, end));
821
+ }
822
+ const out = allocU8(end - start);
823
+ out.set(this.data.subarray(start, end));
824
+ return out;
825
+ }
826
+ copyData() {
827
+ const out = allocU8(this.data.length);
828
+ out.set(this.data);
829
+ return out;
830
+ }
831
+ }
832
+ // ============================================================ bool column
833
+ class BoolColumnImpl extends ColumnImpl {
834
+ constructor(meta, length, data, validity, nullCount) {
835
+ super("bool", meta, length, validity, nullCount);
836
+ this.data = data;
837
+ }
838
+ get byteLength() {
839
+ return this.data.byteLength + this.validityByteLength;
840
+ }
841
+ mutableData() {
842
+ this.assertMutable("mutableData");
843
+ this.assertAttached();
844
+ return this.data;
845
+ }
846
+ paddedU32View() {
847
+ this.assertAttached();
848
+ return this.data;
849
+ }
850
+ slice(start, end) {
851
+ this.checkRange(start, end);
852
+ const rows = end - start;
853
+ const data = start % 32 === 0
854
+ ? shareView(this.data.subarray(start >>> 5, (start >>> 5) + bitmapWordCount(rows)))
855
+ : bitmapSlice(this.data, start, end);
856
+ const { validity, nullCount } = this.sliceValidity(start, end);
857
+ return new BoolColumnImpl(this.meta, rows, data, validity, nullCount);
858
+ }
859
+ clone() {
860
+ this.assertAttached();
861
+ return new BoolColumnImpl(this.meta, this.length, this.data.slice(), this.cloneValidity(), this.nullCountValue);
862
+ }
863
+ storageDetached() {
864
+ return this.data.length === 0;
865
+ }
866
+ self() {
867
+ return this;
868
+ }
869
+ readValue(row) {
870
+ return bitmapGet(this.data, row);
871
+ }
872
+ fillIsDefault() {
873
+ return this.meta.fill === this.meta.default;
874
+ }
875
+ withDefaults() {
876
+ const { default: defaultValue, name } = this.meta;
877
+ if (typeof defaultValue !== "boolean") {
878
+ throw new GraphFormatError("E_COLUMN_TYPE", `default of bool column "${name}" is not a boolean`, {
879
+ column: name,
880
+ field: "default",
881
+ });
882
+ }
883
+ const data = this.data.slice();
884
+ const words = this.validityWords;
885
+ if (words !== null) {
886
+ for (let row = 0; row < this.length; row++) {
887
+ if (!bitmapGet(words, row)) {
888
+ if (defaultValue) {
889
+ bitmapSet(data, row);
890
+ }
891
+ else {
892
+ bitmapClear(data, row);
893
+ }
894
+ }
895
+ }
896
+ }
897
+ return new BoolColumnImpl(this.meta, this.length, data, this.cloneValidity(), this.nullCountValue);
898
+ }
899
+ }
900
+ // ============================================================ dict column
901
+ class DictColumnImpl extends ColumnImpl {
902
+ constructor(meta, length, codes, dictionary, validity, nullCount) {
903
+ super("dict", meta, length, validity, nullCount);
904
+ this.codeMap = null;
905
+ this.codes = codes;
906
+ this.dictionary = dictionary;
907
+ }
908
+ get byteLength() {
909
+ return this.codes.byteLength + this.validityByteLength;
910
+ }
911
+ codeOf(value) {
912
+ this.codeMap ?? (this.codeMap = buildCodeMap(this.dictionary));
913
+ const code = this.codeMap.get(value);
914
+ return code === undefined ? INVALID_INDEX : code;
915
+ }
916
+ mutableData() {
917
+ this.assertMutable("mutableData");
918
+ this.assertAttached();
919
+ return this.codes;
920
+ }
921
+ paddedU32View() {
922
+ this.assertAttached();
923
+ return this.codes;
924
+ }
925
+ slice(start, end) {
926
+ this.checkRange(start, end);
927
+ const { validity, nullCount } = this.sliceValidity(start, end);
928
+ return new DictColumnImpl(this.meta, end - start, shareView(this.codes.subarray(start, end)), this.dictionary, validity, nullCount);
929
+ }
930
+ storageDetached() {
931
+ return this.codes.length === 0;
932
+ }
933
+ clone() {
934
+ this.assertAttached();
935
+ return new DictColumnImpl(this.meta, this.length, this.codes.slice(), [...this.dictionary], this.cloneValidity(), this.nullCountValue);
936
+ }
937
+ self() {
938
+ return this;
939
+ }
940
+ readValue(row) {
941
+ return this.dictionary[this.codes[row]];
942
+ }
943
+ fillIsDefault() {
944
+ return this.meta.fill === this.meta.default;
945
+ }
946
+ withDefaults() {
947
+ const { default: defaultValue, name } = this.meta;
948
+ if (typeof defaultValue !== "string") {
949
+ throw new GraphFormatError("E_COLUMN_TYPE", `default of dict column "${name}" is not a string`, {
950
+ column: name,
951
+ field: "default",
952
+ });
953
+ }
954
+ let dict = this.dictionary;
955
+ let code = this.codeOf(defaultValue);
956
+ if (code === INVALID_INDEX) {
957
+ code = dict.length;
958
+ dict = [...dict, defaultValue];
959
+ }
960
+ const codes = this.codes.slice();
961
+ const words = this.validityWords;
962
+ if (words !== null) {
963
+ for (let row = 0; row < this.length; row++) {
964
+ if (!bitmapGet(words, row)) {
965
+ codes[row] = code;
966
+ }
967
+ }
968
+ }
969
+ return new DictColumnImpl(this.meta, this.length, codes, dict, this.cloneValidity(), this.nullCountValue);
970
+ }
971
+ }
972
+ // ============================================================ string column
973
+ /**
974
+ * Encode strings into an Arrow Utf8 store: rows + 1 offsets and the concatenated bytes in a padded
975
+ * u8 store.
976
+ * @param strings - the rows
977
+ * @returns the offsets and bytes
978
+ */
979
+ function encodeStrings(strings) {
980
+ const encoded = encodeUtf8Rows(strings, strings.length);
981
+ const utf8 = allocU8(encoded.utf8.length);
982
+ utf8.set(encoded.utf8);
983
+ return { offsets: encoded.offsets, utf8 };
984
+ }
985
+ class StringColumnImpl extends ColumnImpl {
986
+ constructor(meta, length, offsets, utf8, strings, validity, nullCount) {
987
+ super("string", meta, length, validity, nullCount);
988
+ this.offsetsStore = offsets;
989
+ this.utf8Store = utf8;
990
+ this.strings = strings;
991
+ }
992
+ get offsets() {
993
+ this.store();
994
+ return this.offsetsStore;
995
+ }
996
+ get utf8() {
997
+ this.store();
998
+ return this.utf8Store;
999
+ }
1000
+ get byteLength() {
1001
+ this.store();
1002
+ return this.offsetsStore.byteLength + this.utf8Store.byteLength + this.validityByteLength;
1003
+ }
1004
+ valueAt(row) {
1005
+ const cached = this.strings?.[row];
1006
+ if (cached !== undefined) {
1007
+ return cached;
1008
+ }
1009
+ this.assertAttached();
1010
+ this.store();
1011
+ const offsets = this.offsetsStore;
1012
+ const utf8 = this.utf8Store;
1013
+ const text = decoder.decode(utf8.subarray(offsets[row], offsets[row + 1]));
1014
+ this.strings ?? (this.strings = new Array(this.length));
1015
+ this.strings[row] = text;
1016
+ return text;
1017
+ }
1018
+ decodeAll() {
1019
+ const out = new Array(this.length);
1020
+ for (let row = 0; row < this.length; row++) {
1021
+ out[row] = this.valueAt(row);
1022
+ }
1023
+ return out;
1024
+ }
1025
+ slice(start, end) {
1026
+ this.checkRange(start, end);
1027
+ const rows = end - start;
1028
+ const { validity, nullCount } = this.sliceValidity(start, end);
1029
+ const strings = this.strings === null ? null : this.strings.slice(start, end);
1030
+ if (this.offsetsStore === null || this.utf8Store === null) {
1031
+ return new StringColumnImpl(this.meta, rows, null, null, strings, validity, nullCount);
1032
+ }
1033
+ const base = this.offsetsStore[start];
1034
+ const offsets = new Uint32Array(rows + 1);
1035
+ for (let i = 0; i <= rows; i++) {
1036
+ offsets[i] = this.offsetsStore[start + i] - base;
1037
+ }
1038
+ const utf8 = shareView(this.utf8Store.subarray(base, this.offsetsStore[end]));
1039
+ return new StringColumnImpl(this.meta, rows, offsets, utf8, strings, validity, nullCount);
1040
+ }
1041
+ clone() {
1042
+ this.assertAttached();
1043
+ const strings = this.strings === null ? null : [...this.strings];
1044
+ if (this.offsetsStore === null || this.utf8Store === null) {
1045
+ return new StringColumnImpl(this.meta, this.length, null, null, strings, this.cloneValidity(), this.nullCountValue);
1046
+ }
1047
+ const utf8 = allocU8(this.utf8Store.length);
1048
+ utf8.set(this.utf8Store);
1049
+ return new StringColumnImpl(this.meta, this.length, this.offsetsStore.slice(), utf8, strings, this.cloneValidity(), this.nullCountValue);
1050
+ }
1051
+ /**
1052
+ * Whether the Utf8 store has been materialised (the wire writer asks before encoding).
1053
+ * @returns true when offsets and utf8 exist without being computed
1054
+ */
1055
+ hasStore() {
1056
+ return this.offsetsStore !== null;
1057
+ }
1058
+ /**
1059
+ * The decoded cache as it stands (sparse), for zero-copy re-wrapping.
1060
+ * @returns the cache, or null when no row has been decoded
1061
+ */
1062
+ decodedCache() {
1063
+ return this.strings;
1064
+ }
1065
+ self() {
1066
+ return this;
1067
+ }
1068
+ storageDetached() {
1069
+ // a materialised store is detached when its offsets vanished; undecoded rows then cannot be read
1070
+ return this.offsetsStore !== null && this.offsetsStore.length === 0;
1071
+ }
1072
+ readValue(row) {
1073
+ return this.valueAt(row);
1074
+ }
1075
+ fillIsDefault() {
1076
+ return this.meta.fill === this.meta.default;
1077
+ }
1078
+ withDefaults() {
1079
+ const { default: defaultValue, name } = this.meta;
1080
+ if (typeof defaultValue !== "string") {
1081
+ throw new GraphFormatError("E_COLUMN_TYPE", `default of string column "${name}" is not a string`, {
1082
+ column: name,
1083
+ field: "default",
1084
+ });
1085
+ }
1086
+ const strings = this.decodeAll();
1087
+ const words = this.validityWords;
1088
+ if (words !== null) {
1089
+ for (let row = 0; row < this.length; row++) {
1090
+ if (!bitmapGet(words, row)) {
1091
+ strings[row] = defaultValue;
1092
+ }
1093
+ }
1094
+ }
1095
+ return new StringColumnImpl(this.meta, this.length, null, null, strings, this.cloneValidity(), this.nullCountValue);
1096
+ }
1097
+ /**
1098
+ * Materialise the Utf8 store from the decoded strings on first need (design section 5.7); a
1099
+ * store that was transferred away is E_DETACHED.
1100
+ */
1101
+ store() {
1102
+ if (this.offsetsStore !== null && this.utf8Store !== null) {
1103
+ this.assertAttached();
1104
+ return;
1105
+ }
1106
+ const strings = this.strings ?? [];
1107
+ const dense = new Array(this.length);
1108
+ for (let row = 0; row < this.length; row++) {
1109
+ dense[row] = strings[row] ?? "";
1110
+ }
1111
+ const encoded = encodeStrings(dense);
1112
+ this.offsetsStore = encoded.offsets;
1113
+ this.utf8Store = encoded.utf8;
1114
+ // the column is the first holder of the store it materialises (design section 9.1)
1115
+ claimHolder(encoded.offsets.buffer);
1116
+ claimHolder(encoded.utf8.buffer);
1117
+ }
1118
+ }
1119
+ class ListColumnImpl extends ColumnImpl {
1120
+ constructor(meta, length, offsets, child, validity, nullCount) {
1121
+ super("list", meta, length, validity, nullCount);
1122
+ this.offsets = offsets;
1123
+ this.child = child;
1124
+ }
1125
+ get byteLength() {
1126
+ return this.offsets.byteLength + this.child.byteLength + this.validityByteLength;
1127
+ }
1128
+ sliceOf(row) {
1129
+ this.assertAttached();
1130
+ const start = this.offsets[row];
1131
+ const end = this.offsets[row + 1];
1132
+ const out = new Array(end - start);
1133
+ for (let i = start; i < end; i++) {
1134
+ out[i - start] = this.child.value(i);
1135
+ }
1136
+ return out;
1137
+ }
1138
+ slice(start, end) {
1139
+ this.checkRange(start, end);
1140
+ const rows = end - start;
1141
+ const base = this.offsets[start];
1142
+ const offsets = new Uint32Array(rows + 1);
1143
+ for (let i = 0; i <= rows; i++) {
1144
+ offsets[i] = this.offsets[start + i] - base;
1145
+ }
1146
+ const { validity, nullCount } = this.sliceValidity(start, end);
1147
+ return new ListColumnImpl(this.meta, rows, offsets, this.child.slice(base, this.offsets[end]), validity, nullCount);
1148
+ }
1149
+ clone() {
1150
+ this.assertAttached();
1151
+ return new ListColumnImpl(this.meta, this.length, this.offsets.slice(), this.child.clone(), this.cloneValidity(), this.nullCountValue);
1152
+ }
1153
+ storageDetached() {
1154
+ return this.offsets.length === 0 || isColumnDetached(this.child);
1155
+ }
1156
+ self() {
1157
+ return this;
1158
+ }
1159
+ readValue(row) {
1160
+ return this.sliceOf(row);
1161
+ }
1162
+ fillIsDefault() {
1163
+ return false;
1164
+ }
1165
+ withDefaults() {
1166
+ const { default: defaultValue, name } = this.meta;
1167
+ if (!Array.isArray(defaultValue)) {
1168
+ throw new GraphFormatError("E_COLUMN_TYPE", `default of list column "${name}" is not an array`, {
1169
+ column: name,
1170
+ field: "default",
1171
+ });
1172
+ }
1173
+ const rows = new Array(this.length);
1174
+ const words = this.validityWords;
1175
+ for (let row = 0; row < this.length; row++) {
1176
+ rows[row] = words !== null && !bitmapGet(words, row) ? defaultValue : this.sliceOf(row);
1177
+ }
1178
+ const parts = listPartsFromValues(this.meta, rows);
1179
+ return new ListColumnImpl(this.meta, this.length, parts.offsets, parts.child, this.cloneValidity(), this.nullCountValue);
1180
+ }
1181
+ }
1182
+ // ============================================================ json column
1183
+ class JsonColumnImpl extends ColumnImpl {
1184
+ constructor(meta, length, values, validity, nullCount) {
1185
+ super("json", meta, length, validity, nullCount);
1186
+ this.values = values;
1187
+ }
1188
+ get byteLength() {
1189
+ return this.validityByteLength;
1190
+ }
1191
+ slice(start, end) {
1192
+ this.checkRange(start, end);
1193
+ const { validity, nullCount } = this.sliceValidity(start, end);
1194
+ return new JsonColumnImpl(this.meta, end - start, this.values.slice(start, end), validity, nullCount);
1195
+ }
1196
+ clone() {
1197
+ this.assertAttached();
1198
+ return new JsonColumnImpl(this.meta, this.length, [...this.values], this.cloneValidity(), this.nullCountValue);
1199
+ }
1200
+ storageDetached() {
1201
+ return false;
1202
+ }
1203
+ self() {
1204
+ return this;
1205
+ }
1206
+ readValue(row) {
1207
+ return this.values[row];
1208
+ }
1209
+ fillIsDefault() {
1210
+ return false;
1211
+ }
1212
+ withDefaults() {
1213
+ const { default: defaultValue } = this.meta;
1214
+ const values = [...this.values];
1215
+ const words = this.validityWords;
1216
+ if (words !== null) {
1217
+ for (let row = 0; row < this.length; row++) {
1218
+ if (!bitmapGet(words, row)) {
1219
+ values[row] = defaultValue;
1220
+ }
1221
+ }
1222
+ }
1223
+ return new JsonColumnImpl(this.meta, this.length, values, this.cloneValidity(), this.nullCountValue);
1224
+ }
1225
+ }
1226
+ /**
1227
+ * Whether a value is a Column built by this package's factory (the structural Column type also
1228
+ * admits a foreign object with the right members, which the table refuses with E_COLUMN_TYPE).
1229
+ * @param value - the candidate
1230
+ * @returns true for an instance of a column implementation
1231
+ */
1232
+ export function isPackageColumn(value) {
1233
+ return value instanceof ColumnImpl;
1234
+ }
1235
+ /**
1236
+ * Whether a column's storage was transferred away (design section 9.1): derived from the array
1237
+ * state. Every accessor of such a column throws E_DETACHED; the wire module and the checksum
1238
+ * comparison ask first.
1239
+ * @param column - any column
1240
+ * @returns true when detached
1241
+ */
1242
+ export function isColumnDetached(column) {
1243
+ return column.detached;
1244
+ }
1245
+ // ============================================================ factory
1246
+ function lengthError(meta, what, expected, found) {
1247
+ return new GraphFormatError("E_COLUMN_LENGTH", `column "${meta.name}": ${what} has length ${found}, expected ${expected}`, {
1248
+ column: meta.name,
1249
+ what,
1250
+ expected,
1251
+ found,
1252
+ });
1253
+ }
1254
+ function slotError(meta, message) {
1255
+ return new GraphFormatError("E_COLUMN_TYPE", `column "${meta.name}": ${message}`, { column: meta.name });
1256
+ }
1257
+ /**
1258
+ * Wrap raw column storage into a Column of the dtype named by parts.meta (the column factory of
1259
+ * design section 5.7). Checks the slot use and the length rules of invariant I12 (E_COLUMN_LENGTH /
1260
+ * E_COLUMN_TYPE / E_COLUMN_ALIGNMENT), recomputes nullCount from the validity bitmap, and adopts
1261
+ * every buffer by reference.
1262
+ * @param parts - the storage slots; every slot the dtype does not use must be null
1263
+ * @returns the column
1264
+ */
1265
+ export function createColumn(parts) {
1266
+ const { meta, length } = parts;
1267
+ if (!(Number.isInteger(length) && length >= 0)) {
1268
+ throw new GraphFormatError("E_COLUMN_LENGTH", `column "${meta.name}": invalid row count ${length}`, {
1269
+ column: meta.name,
1270
+ found: length,
1271
+ });
1272
+ }
1273
+ const { validity: given } = parts;
1274
+ let validity = null;
1275
+ if (given !== null) {
1276
+ if (!meta.nullable) {
1277
+ throw slotError(meta, "a non-nullable column cannot carry a validity bitmap");
1278
+ }
1279
+ if (!(given instanceof Uint32Array)) {
1280
+ throw slotError(meta, "validity must be a Uint32Array");
1281
+ }
1282
+ if (given.length !== bitmapWordCount(length)) {
1283
+ throw lengthError(meta, "validity", bitmapWordCount(length), given.length);
1284
+ }
1285
+ validity = given;
1286
+ }
1287
+ const nullCount = validity === null ? 0 : length - bitmapCount(validity, length);
1288
+ const { dtype } = meta;
1289
+ switch (dtype) {
1290
+ case "f32":
1291
+ return new F32ColumnImpl(meta, length, checkNumeric(parts, Float32Array), validity, nullCount);
1292
+ case "f64":
1293
+ return new F64ColumnImpl(meta, length, checkNumeric(parts, Float64Array), validity, nullCount);
1294
+ case "i32":
1295
+ return new I32ColumnImpl(meta, length, checkNumeric(parts, Int32Array), validity, nullCount);
1296
+ case "u32":
1297
+ return new U32ColumnImpl(meta, length, checkNumeric(parts, Uint32Array), validity, nullCount);
1298
+ case "u8": {
1299
+ const data = checkNumeric(parts, Uint8Array);
1300
+ if (!canViewAsPaddedU32(data)) {
1301
+ throw new GraphFormatError("E_COLUMN_ALIGNMENT", `column "${meta.name}": no padded u32 view over the u8 data`, {
1302
+ column: meta.name,
1303
+ byteOffset: data.byteOffset,
1304
+ byteLength: data.byteLength,
1305
+ });
1306
+ }
1307
+ return new U8ColumnImpl(meta, length, data, validity, nullCount);
1308
+ }
1309
+ case "bool": {
1310
+ if (!(parts.data instanceof Uint32Array)) {
1311
+ throw slotError(meta, "bool data must be packed Uint32Array words");
1312
+ }
1313
+ if (parts.data.length !== bitmapWordCount(length)) {
1314
+ throw lengthError(meta, "data", bitmapWordCount(length), parts.data.length);
1315
+ }
1316
+ return new BoolColumnImpl(meta, length, parts.data, validity, nullCount);
1317
+ }
1318
+ case "dict": {
1319
+ if (!(parts.data instanceof Uint32Array)) {
1320
+ throw slotError(meta, "dict codes must be a Uint32Array");
1321
+ }
1322
+ if (parts.data.length !== length) {
1323
+ throw lengthError(meta, "codes", length, parts.data.length);
1324
+ }
1325
+ if (parts.dictionary === null) {
1326
+ throw slotError(meta, "dict column needs a dictionary");
1327
+ }
1328
+ return new DictColumnImpl(meta, length, parts.data, parts.dictionary, validity, nullCount);
1329
+ }
1330
+ case "string": {
1331
+ if (parts.offsets === null || parts.utf8 === null) {
1332
+ const { strings } = parts;
1333
+ if (strings === null || strings.length !== length) {
1334
+ throw lengthError(meta, "strings", length, strings === null ? -1 : strings.length);
1335
+ }
1336
+ for (let row = 0; row < length; row++) {
1337
+ if (typeof strings[row] !== "string") {
1338
+ throw slotError(meta, `row ${row} is not a decoded string and no Utf8 store is present`);
1339
+ }
1340
+ }
1341
+ return new StringColumnImpl(meta, length, null, null, strings, validity, nullCount);
1342
+ }
1343
+ if (parts.offsets.length !== length + 1) {
1344
+ throw lengthError(meta, "offsets", length + 1, parts.offsets.length);
1345
+ }
1346
+ if (parts.offsets[length] !== parts.utf8.length) {
1347
+ throw lengthError(meta, "utf8", parts.offsets[length], parts.utf8.length);
1348
+ }
1349
+ return new StringColumnImpl(meta, length, parts.offsets, parts.utf8, parts.strings, validity, nullCount);
1350
+ }
1351
+ case "list": {
1352
+ if (parts.offsets === null || parts.offsets.length !== length + 1) {
1353
+ throw lengthError(meta, "offsets", length + 1, parts.offsets === null ? -1 : parts.offsets.length);
1354
+ }
1355
+ const { child } = parts;
1356
+ if (child === null || child.dtype === "list") {
1357
+ throw slotError(meta, "list column needs a non-list child column");
1358
+ }
1359
+ if (child.validity !== null || child.meta.nullable) {
1360
+ throw slotError(meta, "list child must be non-nullable");
1361
+ }
1362
+ if (child.dtype !== meta.itemDtype || child.meta.components !== meta.itemComponents) {
1363
+ throw slotError(meta, `list child is ${child.dtype} x${child.meta.components}, declared ${String(meta.itemDtype)} x${String(meta.itemComponents)}`);
1364
+ }
1365
+ if (parts.offsets[length] !== child.length) {
1366
+ throw lengthError(meta, "child", parts.offsets[length], child.length);
1367
+ }
1368
+ return new ListColumnImpl(meta, length, parts.offsets, child, validity, nullCount);
1369
+ }
1370
+ case "json": {
1371
+ if (parts.values === null || parts.values.length !== length) {
1372
+ throw lengthError(meta, "values", length, parts.values === null ? -1 : parts.values.length);
1373
+ }
1374
+ return new JsonColumnImpl(meta, length, parts.values, validity, nullCount);
1375
+ }
1376
+ default: {
1377
+ const name = dtype;
1378
+ throw new GraphFormatError("E_COLUMN_TYPE", `unknown dtype ${name}`, { dtype: name });
1379
+ }
1380
+ }
1381
+ }
1382
+ function checkNumeric(parts, ctor) {
1383
+ const { meta, length } = parts;
1384
+ if (!(parts.data instanceof ctor)) {
1385
+ throw slotError(meta, `${meta.dtype} data must be a ${ctor.name}`);
1386
+ }
1387
+ const expected = length * meta.components;
1388
+ if (parts.data.length !== expected) {
1389
+ throw lengthError(meta, "data", expected, parts.data.length);
1390
+ }
1391
+ return parts.data;
1392
+ }
1393
+ /**
1394
+ * The raw storage of a column, sharing every buffer by reference (the inverse of createColumn): used
1395
+ * to re-wrap a column under new metadata and by the remap helpers.
1396
+ * @param column - the column to unwrap
1397
+ * @returns its storage slots
1398
+ */
1399
+ export function partsOf(column) {
1400
+ const base = {
1401
+ meta: column.meta,
1402
+ length: column.length,
1403
+ data: null,
1404
+ validity: column.validity,
1405
+ nullCount: column.nullCount,
1406
+ dictionary: null,
1407
+ offsets: null,
1408
+ utf8: null,
1409
+ strings: null,
1410
+ child: null,
1411
+ values: null,
1412
+ };
1413
+ const { dtype } = column;
1414
+ switch (dtype) {
1415
+ case "f32":
1416
+ case "f64":
1417
+ case "i32":
1418
+ case "u32":
1419
+ case "u8":
1420
+ case "bool":
1421
+ base.data = column.data;
1422
+ return base;
1423
+ case "dict":
1424
+ base.data = column.codes;
1425
+ base.dictionary = column.dictionary;
1426
+ return base;
1427
+ case "string": {
1428
+ if (column instanceof StringColumnImpl) {
1429
+ base.strings = column.decodedCache();
1430
+ if (column.hasStore()) {
1431
+ base.offsets = column.offsets;
1432
+ base.utf8 = column.utf8;
1433
+ }
1434
+ return base;
1435
+ }
1436
+ base.offsets = column.offsets;
1437
+ base.utf8 = column.utf8;
1438
+ return base;
1439
+ }
1440
+ case "list":
1441
+ base.offsets = column.offsets;
1442
+ base.child = column.child;
1443
+ return base;
1444
+ case "json":
1445
+ base.values = column.values;
1446
+ return base;
1447
+ default: {
1448
+ const name = dtype;
1449
+ throw new GraphFormatError("E_COLUMN_TYPE", `unknown dtype ${name}`, { dtype: name });
1450
+ }
1451
+ }
1452
+ }
1453
+ /**
1454
+ * The distinct backing buffers of a column's materialised typed storage (data or codes, validity,
1455
+ * a materialised Utf8 store, a list's offsets and child), for the owner count of design section
1456
+ * 9.1: a table claims them when it attaches the column, so a buffer viewed from two tables (a
1457
+ * zero-copy slice, a same-realm wire receiver) is recognised as shared. A string store that has not
1458
+ * been materialised is not encoded by asking.
1459
+ * @param column - the column
1460
+ * @returns the buffers, each once
1461
+ */
1462
+ export function columnBuffers(column) {
1463
+ const out = new Set();
1464
+ const add = (view) => {
1465
+ if (view !== null) {
1466
+ out.add(view.buffer);
1467
+ }
1468
+ };
1469
+ add(column.validity);
1470
+ switch (column.dtype) {
1471
+ case "f32":
1472
+ case "f64":
1473
+ case "i32":
1474
+ case "u32":
1475
+ case "u8":
1476
+ case "bool":
1477
+ add(column.data);
1478
+ break;
1479
+ case "dict":
1480
+ add(column.codes);
1481
+ break;
1482
+ case "string":
1483
+ if (!(column instanceof StringColumnImpl) || column.hasStore()) {
1484
+ add(column.offsets);
1485
+ add(column.utf8);
1486
+ }
1487
+ break;
1488
+ case "list":
1489
+ add(column.offsets);
1490
+ for (const buffer of columnBuffers(column.child)) {
1491
+ out.add(buffer);
1492
+ }
1493
+ break;
1494
+ case "json":
1495
+ break;
1496
+ default: {
1497
+ const unknown = column;
1498
+ throw new GraphFormatError("E_COLUMN_TYPE", `unknown dtype ${unknown.dtype}`, {});
1499
+ }
1500
+ }
1501
+ return [...out];
1502
+ }
1503
+ /**
1504
+ * The same storage under different metadata (a rename, a move to another table, a declaration
1505
+ * patch); the buffers are shared, the Column object is new. The dtype, components and list child
1506
+ * shape must be unchanged (E_COLUMN_TYPE otherwise); the fill is kept because the unset rows
1507
+ * physically hold it; a nullable -> non-nullable change requires no unset rows.
1508
+ * @param column - the column to re-wrap
1509
+ * @param meta - the new metadata
1510
+ * @returns a new column sharing the storage
1511
+ */
1512
+ export function rewrapColumn(column, meta) {
1513
+ const old = column.meta;
1514
+ if (meta.dtype !== old.dtype ||
1515
+ meta.components !== old.components ||
1516
+ meta.itemDtype !== old.itemDtype ||
1517
+ meta.itemComponents !== old.itemComponents) {
1518
+ throw new GraphFormatError("E_COLUMN_TYPE", `column "${old.name}" is ${old.dtype} x${old.components}; a patch cannot change its dtype`, {
1519
+ column: old.name,
1520
+ field: "dtype",
1521
+ });
1522
+ }
1523
+ if (meta.fill !== old.fill) {
1524
+ throw new GraphFormatError("E_COLUMN_TYPE", `the fill of existing column "${old.name}" cannot change`, {
1525
+ column: old.name,
1526
+ field: "fill",
1527
+ });
1528
+ }
1529
+ const parts = partsOf(column);
1530
+ if (!meta.nullable && column.validity !== null) {
1531
+ if (column.nullCount > 0) {
1532
+ throw new GraphFormatError("E_COLUMN_TYPE", `column "${old.name}" has ${column.nullCount} unset rows and cannot become non-nullable`, {
1533
+ column: old.name,
1534
+ field: "nullable",
1535
+ nullCount: column.nullCount,
1536
+ });
1537
+ }
1538
+ parts.validity = null;
1539
+ }
1540
+ parts.meta = meta;
1541
+ return createColumn(parts);
1542
+ }
1543
+ /**
1544
+ * Release the cached f32 copy a `gpuView()` of an f64 column keeps (design section 7.2:
1545
+ * `dropCaches()` releases every cached gpuView copy). A no-op for every other dtype.
1546
+ * @param column - the column
1547
+ */
1548
+ export function dropGpuViewCache(column) {
1549
+ f32Cache.delete(column);
1550
+ }
1551
+ /**
1552
+ * The array a GPU binds for a column (design section 10.4): its own data for u32 / i32 / f32, the
1553
+ * padded view for u8, the packed words for bool, the codes for dict, and a cached f32 copy for f64
1554
+ * (dropped by markDirty() and by GraphSnapshot.dropCaches()).
1555
+ * @param column - the column
1556
+ * @returns the bindable array; E_GPU_INELIGIBLE for string / list / json
1557
+ */
1558
+ export function gpuViewOf(column) {
1559
+ const { dtype } = column;
1560
+ switch (dtype) {
1561
+ case "f32":
1562
+ case "i32":
1563
+ case "u32":
1564
+ case "bool":
1565
+ return column.data;
1566
+ case "u8":
1567
+ return column.paddedU32View();
1568
+ case "dict":
1569
+ return column.codes;
1570
+ case "f64": {
1571
+ let cached = f32Cache.get(column);
1572
+ if (cached === undefined) {
1573
+ cached = new Float32Array(column.data);
1574
+ f32Cache.set(column, cached);
1575
+ }
1576
+ return cached;
1577
+ }
1578
+ case "string":
1579
+ case "list":
1580
+ case "json":
1581
+ throw new GraphFormatError("E_GPU_INELIGIBLE", `a ${dtype} column cannot be bound by the GPU`, {
1582
+ column: column.meta.name,
1583
+ dtype,
1584
+ });
1585
+ default: {
1586
+ const name = dtype;
1587
+ throw new GraphFormatError("E_COLUMN_TYPE", `unknown dtype ${name}`, { dtype: name });
1588
+ }
1589
+ }
1590
+ }
1591
+ // ============================================================ construction from declarations and values
1592
+ /**
1593
+ * The physical dict code stored in rows that hold the fill: the code of the fill string, interned
1594
+ * on demand when those rows are SET (a non-nullable column), or 0 when the fill is not a member and
1595
+ * the rows are unset (their code is never read).
1596
+ * @param dictionary - the dictionary
1597
+ * @param fill - the column's fill
1598
+ * @param set - whether the rows holding the fill are set
1599
+ * @returns the code
1600
+ */
1601
+ function fillCode(dictionary, fill, set) {
1602
+ const text = typeof fill === "string" ? fill : "";
1603
+ if (set) {
1604
+ // a SET row holding the fill (a non-nullable column) needs a code that names a member: the
1605
+ // nominal "" is interned on demand rather than borrowing code 0, which may belong to another
1606
+ // value or to nothing
1607
+ return dictionary.intern(text);
1608
+ }
1609
+ // an unset row's code is never read; 0 when the fill is not a member
1610
+ const code = dictionary.codeOf(text);
1611
+ return code === INVALID_INDEX ? 0 : code;
1612
+ }
1613
+ /**
1614
+ * The initial dictionary of a dict column: the declared options in order, then the fill when it is
1615
+ * a string not among them (so the fill code names a member).
1616
+ * @param meta - the column metadata
1617
+ * @returns the seeded dictionary builder
1618
+ */
1619
+ export function seedDictionary(meta) {
1620
+ const dictionary = new DictionaryBuilder(meta.options === null ? undefined : meta.options);
1621
+ if (typeof meta.fill === "string" && meta.fill !== "") {
1622
+ dictionary.intern(meta.fill);
1623
+ }
1624
+ return dictionary;
1625
+ }
1626
+ /**
1627
+ * A MutableColumnParts record with every storage slot null, for producers that fill one slot.
1628
+ * @param meta - the column metadata
1629
+ * @param length - the row count
1630
+ * @param validity - the validity bitmap, or null
1631
+ * @returns the parts
1632
+ */
1633
+ export function emptyParts(meta, length, validity) {
1634
+ return {
1635
+ meta,
1636
+ length,
1637
+ data: null,
1638
+ validity,
1639
+ nullCount: validity === null ? 0 : length,
1640
+ dictionary: null,
1641
+ offsets: null,
1642
+ utf8: null,
1643
+ strings: null,
1644
+ child: null,
1645
+ values: null,
1646
+ };
1647
+ }
1648
+ function childMeta(meta) {
1649
+ if (meta.itemDtype === null || meta.itemComponents === null) {
1650
+ throw new GraphFormatError("E_COLUMN_TYPE", `column "${meta.name}" is not a list`, {
1651
+ column: meta.name,
1652
+ field: "itemDtype",
1653
+ });
1654
+ }
1655
+ return resolveColumnMeta(`${meta.name}.item`, meta.domain, {
1656
+ dtype: meta.itemDtype,
1657
+ components: meta.itemComponents,
1658
+ nullable: false,
1659
+ refersTo: meta.refersTo ?? undefined,
1660
+ });
1661
+ }
1662
+ /**
1663
+ * A column of `length` rows with no value set: every row unset (when nullable) and the data holding
1664
+ * the fill (design section 5.3). This is what a declared column looks like before any value is
1665
+ * written, and what remap uses for rows no source row maps to.
1666
+ * @param domain - the table the column belongs to
1667
+ * @param length - the number of rows
1668
+ * @param decl - the declaration; dtype required
1669
+ * @returns the empty column
1670
+ */
1671
+ export function createEmptyColumn(domain, length, decl) {
1672
+ const name = decl.name ?? "";
1673
+ const meta = resolveColumnMeta(name, domain, decl);
1674
+ return emptyColumnOf(meta, length);
1675
+ }
1676
+ /**
1677
+ * An empty column from resolved metadata; see createEmptyColumn.
1678
+ * @param meta - the resolved metadata
1679
+ * @param length - the number of rows
1680
+ * @returns the empty column
1681
+ */
1682
+ function emptyColumnOf(meta, length) {
1683
+ const validity = meta.nullable ? new Uint32Array(bitmapWordCount(length)) : null;
1684
+ const parts = emptyParts(meta, length, validity);
1685
+ const { dtype, components, fill } = meta;
1686
+ switch (dtype) {
1687
+ case "f32":
1688
+ parts.data = new Float32Array(length * components).fill(fill);
1689
+ break;
1690
+ case "f64":
1691
+ parts.data = new Float64Array(length * components).fill(fill);
1692
+ break;
1693
+ case "i32":
1694
+ parts.data = new Int32Array(length * components).fill(fill);
1695
+ break;
1696
+ case "u32":
1697
+ parts.data = new Uint32Array(length * components).fill(fill);
1698
+ break;
1699
+ case "u8":
1700
+ parts.data = allocU8(length * components).fill(fill);
1701
+ break;
1702
+ case "bool":
1703
+ parts.data = makeBitmap(length, fill === true);
1704
+ break;
1705
+ case "dict": {
1706
+ const dictionary = seedDictionary(meta);
1707
+ parts.data = new Uint32Array(length).fill(fillCode(dictionary, fill, validity === null && length > 0));
1708
+ parts.dictionary = dictionary.values;
1709
+ break;
1710
+ }
1711
+ case "string": {
1712
+ const fillText = typeof fill === "string" ? fill : "";
1713
+ parts.strings = new Array(length).fill(fillText);
1714
+ break;
1715
+ }
1716
+ case "list":
1717
+ parts.offsets = new Uint32Array(length + 1);
1718
+ parts.child = emptyColumnOf(childMeta(meta), 0);
1719
+ break;
1720
+ case "json":
1721
+ parts.values = new Array(length).fill(undefined);
1722
+ break;
1723
+ default: {
1724
+ const dtypeName = dtype;
1725
+ throw new GraphFormatError("E_COLUMN_TYPE", `unknown dtype ${dtypeName}`, { dtype: dtypeName });
1726
+ }
1727
+ }
1728
+ return createColumn(parts);
1729
+ }
1730
+ /**
1731
+ * Whether a JS array entry is an unset cell: undefined for every dtype, null too except for json,
1732
+ * where null is a value (design section 12.1).
1733
+ * @param value - the entry
1734
+ * @param dtype - the column dtype
1735
+ * @returns true when unset
1736
+ */
1737
+ function isUnsetEntry(value, dtype) {
1738
+ return value === undefined || (value === null && dtype !== "json");
1739
+ }
1740
+ /**
1741
+ * Build the offsets and child of a list column from an array of rows (each an array of items).
1742
+ * @param meta - the list column's metadata
1743
+ * @param rows - one array per row (an empty array for unset rows)
1744
+ * @returns the offsets and the wrapped child
1745
+ */
1746
+ function listPartsFromValues(meta, rows) {
1747
+ const offsets = new Uint32Array(rows.length + 1);
1748
+ const items = [];
1749
+ for (let row = 0; row < rows.length; row++) {
1750
+ for (const item of rows[row]) {
1751
+ items.push(item);
1752
+ }
1753
+ offsets[row + 1] = items.length;
1754
+ }
1755
+ const child = columnOfValues(childMeta(meta), items);
1756
+ return { offsets, child };
1757
+ }
1758
+ /**
1759
+ * A column from a JS array of values under resolved metadata (one entry per row; undefined, and null
1760
+ * except for json, is an unset row). Values are coerced per design section 5.1 (a boolean into a
1761
+ * number column becomes 1 / 0, a number or boolean into a string column its canonical text) and
1762
+ * rejected with E_COLUMN_TYPE otherwise.
1763
+ * @param meta - the resolved metadata
1764
+ * @param values - the entries, `length` of them
1765
+ * @returns the column
1766
+ */
1767
+ export function columnOfValues(meta, values) {
1768
+ const { length } = values;
1769
+ const { dtype, components, fill, name } = meta;
1770
+ let validity = null;
1771
+ let nullCount = 0;
1772
+ for (let row = 0; row < length; row++) {
1773
+ if (isUnsetEntry(values[row], dtype)) {
1774
+ if (!meta.nullable) {
1775
+ throw new GraphFormatError("E_COLUMN_TYPE", `row ${row} of non-nullable column "${name}" is unset`, {
1776
+ column: name,
1777
+ row,
1778
+ });
1779
+ }
1780
+ validity ?? (validity = makeBitmap(length, true));
1781
+ bitmapClear(validity, row);
1782
+ nullCount++;
1783
+ }
1784
+ }
1785
+ const parts = emptyParts(meta, length, validity);
1786
+ parts.nullCount = nullCount;
1787
+ const isSet = (row) => validity === null || bitmapGet(validity, row);
1788
+ switch (dtype) {
1789
+ case "f32":
1790
+ case "f64":
1791
+ case "i32":
1792
+ case "u32":
1793
+ case "u8": {
1794
+ const data = allocNumeric(dtype, length * components);
1795
+ if (fill !== 0) {
1796
+ data.fill(fill);
1797
+ }
1798
+ for (let row = 0; row < length; row++) {
1799
+ if (!isSet(row)) {
1800
+ continue;
1801
+ }
1802
+ const raw = values[row];
1803
+ const value = typeof raw === "boolean" ? Number(raw) : raw;
1804
+ writeNumeric(dtype, data, row * components, components, value, name, row);
1805
+ }
1806
+ parts.data = data;
1807
+ break;
1808
+ }
1809
+ case "bool": {
1810
+ const data = makeBitmap(length, fill === true);
1811
+ for (let row = 0; row < length; row++) {
1812
+ if (!isSet(row)) {
1813
+ continue;
1814
+ }
1815
+ const value = coerceValue(values[row], "bool");
1816
+ if (value === true) {
1817
+ bitmapSet(data, row);
1818
+ }
1819
+ else {
1820
+ bitmapClear(data, row);
1821
+ }
1822
+ }
1823
+ parts.data = data;
1824
+ break;
1825
+ }
1826
+ case "dict": {
1827
+ const dictionary = seedDictionary(meta);
1828
+ const codes = new Uint32Array(length);
1829
+ if (validity !== null) {
1830
+ codes.fill(fillCode(dictionary, fill, false));
1831
+ }
1832
+ for (let row = 0; row < length; row++) {
1833
+ if (!isSet(row)) {
1834
+ continue;
1835
+ }
1836
+ const value = coerceValue(values[row], "string");
1837
+ if (typeof value !== "string") {
1838
+ throw new GraphFormatError("E_COLUMN_TYPE", `row ${row} of dict column "${name}" is not a string`, {
1839
+ column: name,
1840
+ row,
1841
+ });
1842
+ }
1843
+ codes[row] = dictionary.intern(value);
1844
+ }
1845
+ parts.data = codes;
1846
+ parts.dictionary = dictionary.values;
1847
+ break;
1848
+ }
1849
+ case "string": {
1850
+ const fillText = typeof fill === "string" ? fill : "";
1851
+ const strings = new Array(length);
1852
+ for (let row = 0; row < length; row++) {
1853
+ if (!isSet(row)) {
1854
+ strings[row] = fillText;
1855
+ continue;
1856
+ }
1857
+ const value = coerceValue(values[row], "string");
1858
+ if (typeof value !== "string") {
1859
+ throw new GraphFormatError("E_COLUMN_TYPE", `row ${row} of string column "${name}" is not a string`, { column: name, row });
1860
+ }
1861
+ assertWellFormedString(value, { column: name, row });
1862
+ strings[row] = value;
1863
+ }
1864
+ parts.strings = strings;
1865
+ break;
1866
+ }
1867
+ case "list": {
1868
+ const rows = new Array(length);
1869
+ for (let row = 0; row < length; row++) {
1870
+ if (!isSet(row)) {
1871
+ rows[row] = [];
1872
+ continue;
1873
+ }
1874
+ const value = values[row];
1875
+ if (!Array.isArray(value)) {
1876
+ throw new GraphFormatError("E_COLUMN_TYPE", `row ${row} of list column "${name}" is not an array`, {
1877
+ column: name,
1878
+ row,
1879
+ });
1880
+ }
1881
+ rows[row] = value;
1882
+ }
1883
+ const listParts = listPartsFromValues(meta, rows);
1884
+ parts.offsets = listParts.offsets;
1885
+ parts.child = listParts.child;
1886
+ break;
1887
+ }
1888
+ case "json": {
1889
+ const out = new Array(length);
1890
+ for (let row = 0; row < length; row++) {
1891
+ if (!isSet(row)) {
1892
+ out[row] = undefined;
1893
+ continue;
1894
+ }
1895
+ assertJsonValue(values[row], `row ${row}`);
1896
+ out[row] = values[row];
1897
+ }
1898
+ parts.values = out;
1899
+ break;
1900
+ }
1901
+ default: {
1902
+ const dtypeName = dtype;
1903
+ throw new GraphFormatError("E_COLUMN_TYPE", `unknown dtype ${dtypeName}`, { dtype: dtypeName });
1904
+ }
1905
+ }
1906
+ return createColumn(parts);
1907
+ }
1908
+ /**
1909
+ * Allocate a numeric buffer of a dtype (a padded store for u8).
1910
+ * @param dtype - the numeric dtype
1911
+ * @param length - the element count
1912
+ * @returns the zeroed array
1913
+ */
1914
+ export function allocNumeric(dtype, length) {
1915
+ switch (dtype) {
1916
+ case "f32":
1917
+ return new Float32Array(length);
1918
+ case "f64":
1919
+ return new Float64Array(length);
1920
+ case "i32":
1921
+ return new Int32Array(length);
1922
+ case "u32":
1923
+ return new Uint32Array(length);
1924
+ case "u8":
1925
+ return allocU8(length);
1926
+ default: {
1927
+ const name = dtype;
1928
+ throw new GraphFormatError("E_COLUMN_TYPE", `unknown dtype ${name}`, { dtype: name });
1929
+ }
1930
+ }
1931
+ }
1932
+ /**
1933
+ * Infer the declaration of a column from a JS array of values when the caller gave no dtype: the
1934
+ * widening rules of design section 5.1 over the entries (json when every entry is unset), and for a
1935
+ * list the same rules over the items.
1936
+ * @param values - the entries
1937
+ * @returns the dtype and, for a list, the item dtype
1938
+ */
1939
+ function inferDeclFromValues(values) {
1940
+ let sawArray = false;
1941
+ let sawOther = false;
1942
+ for (const value of values) {
1943
+ if (value === undefined || value === null) {
1944
+ continue;
1945
+ }
1946
+ if (Array.isArray(value)) {
1947
+ sawArray = true;
1948
+ }
1949
+ else {
1950
+ sawOther = true;
1951
+ }
1952
+ }
1953
+ if (sawArray && !sawOther) {
1954
+ const items = [];
1955
+ for (const value of values) {
1956
+ if (Array.isArray(value)) {
1957
+ for (const item of value) {
1958
+ items.push(item);
1959
+ }
1960
+ }
1961
+ }
1962
+ return { dtype: "list", itemDtype: inferValuesDtype(items) ?? "json" };
1963
+ }
1964
+ return { dtype: inferValuesDtype(values) ?? "json", itemDtype: undefined };
1965
+ }
1966
+ /**
1967
+ * A column from a JS array of values with a declaration patch (the set() path for string / list /
1968
+ * json and for inferred dtypes). The dtype is inferred when the patch has none; nullable defaults to
1969
+ * true.
1970
+ * @param domain - the table the column belongs to
1971
+ * @param length - the table's row count; E_COLUMN_LENGTH when values.length differs
1972
+ * @param name - the column name
1973
+ * @param values - the entries, one per row
1974
+ * @param decl - the declaration patch
1975
+ * @returns the column
1976
+ */
1977
+ export function columnFromValues(domain, length, name, values, decl) {
1978
+ if (values.length !== length) {
1979
+ throw new GraphFormatError("E_COLUMN_LENGTH", `column "${name}": ${values.length} values for ${length} rows`, {
1980
+ column: name,
1981
+ expected: length,
1982
+ found: values.length,
1983
+ });
1984
+ }
1985
+ let patch = { nullable: true, ...decl };
1986
+ if (patch.dtype === undefined) {
1987
+ const inferred = inferDeclFromValues(values);
1988
+ patch = { ...patch, dtype: inferred.dtype };
1989
+ if (inferred.dtype === "list" && patch.itemDtype === undefined) {
1990
+ patch = { ...patch, itemDtype: inferred.itemDtype };
1991
+ }
1992
+ }
1993
+ const meta = resolveColumnMeta(name, domain, patch);
1994
+ return columnOfValues(meta, values);
1995
+ }
1996
+ /**
1997
+ * The dtype a typed array class implies when the caller gave none.
1998
+ * @param data - the array
1999
+ * @returns f32 / f64 / i32 / u32 / u8
2000
+ */
2001
+ export function dtypeOfArray(data) {
2002
+ if (data instanceof Float32Array) {
2003
+ return "f32";
2004
+ }
2005
+ if (data instanceof Float64Array) {
2006
+ return "f64";
2007
+ }
2008
+ if (data instanceof Int32Array) {
2009
+ return "i32";
2010
+ }
2011
+ if (data instanceof Uint32Array) {
2012
+ return "u32";
2013
+ }
2014
+ return "u8";
2015
+ }
2016
+ /**
2017
+ * A column adopting a typed array by reference (design section 5.7): the dtype comes from the array
2018
+ * class unless the patch names one (bool and dict over a Uint32Array); the length must be rowCount *
2019
+ * components (bool: ceil(rowCount / 32)) or E_COLUMN_LENGTH; a u8 array from which no padded u32 view
2020
+ * is constructible is copied, or refused with E_COLUMN_ALIGNMENT under adopt "strict". nullable
2021
+ * defaults to false (every row is set).
2022
+ * @param domain - the table the column belongs to
2023
+ * @param length - the table's row count
2024
+ * @param name - the column name
2025
+ * @param data - the typed array
2026
+ * @param decl - the declaration patch
2027
+ * @param adopt - "copy" (default) or "strict"
2028
+ * @returns the column (`column.data !== data` only when a u8 array was copied)
2029
+ */
2030
+ export function columnFromTypedArray(domain, length, name, data, decl, adopt = "copy") {
2031
+ const arrayDtype = dtypeOfArray(data);
2032
+ const dtype = decl.dtype ?? arrayDtype;
2033
+ const compatible = dtype === arrayDtype || (arrayDtype === "u32" && (dtype === "bool" || dtype === "dict"));
2034
+ if (!compatible) {
2035
+ throw new GraphFormatError("E_COLUMN_TYPE", `column "${name}": a ${arrayDtype} array cannot back a ${dtype} column`, {
2036
+ column: name,
2037
+ field: "dtype",
2038
+ found: arrayDtype,
2039
+ expected: dtype,
2040
+ });
2041
+ }
2042
+ const meta = resolveColumnMeta(name, domain, { nullable: false, ...decl, dtype });
2043
+ const parts = emptyParts(meta, length, null);
2044
+ let expected;
2045
+ switch (dtype) {
2046
+ case "bool":
2047
+ expected = bitmapWordCount(length);
2048
+ break;
2049
+ case "dict":
2050
+ expected = length;
2051
+ break;
2052
+ case "f32":
2053
+ case "f64":
2054
+ case "i32":
2055
+ case "u32":
2056
+ case "u8":
2057
+ expected = length * meta.components;
2058
+ break;
2059
+ default: {
2060
+ const dtypeName = dtype;
2061
+ throw new GraphFormatError("E_COLUMN_TYPE", `column "${name}": a ${dtypeName} column cannot adopt a typed array`, {
2062
+ column: name,
2063
+ field: "dtype",
2064
+ });
2065
+ }
2066
+ }
2067
+ if (data.length !== expected) {
2068
+ throw new GraphFormatError("E_COLUMN_LENGTH", `column "${name}": array length ${data.length}, expected ${expected}`, {
2069
+ column: name,
2070
+ expected,
2071
+ found: data.length,
2072
+ });
2073
+ }
2074
+ if (!isOverPlainBuffer(data)) {
2075
+ // decision D-SAB: no SharedArrayBuffer in v1; a resizable buffer's view can change length (I17)
2076
+ const reason = data.buffer instanceof ArrayBuffer ? "resizable ArrayBuffer" : "SharedArrayBuffer";
2077
+ throw new GraphFormatError("E_UNSUPPORTED", `column "${name}": a view over a ${reason} cannot be adopted`, {
2078
+ column: name,
2079
+ reason,
2080
+ });
2081
+ }
2082
+ let stored = data;
2083
+ if (data instanceof Uint8Array && !canViewAsPaddedU32(data)) {
2084
+ if (adopt === "strict") {
2085
+ throw new GraphFormatError("E_COLUMN_ALIGNMENT", `column "${name}": no zero-copy padded u32 view over the u8 array`, {
2086
+ column: name,
2087
+ byteOffset: data.byteOffset,
2088
+ byteLength: data.byteLength,
2089
+ bufferByteLength: data.buffer.byteLength,
2090
+ });
2091
+ }
2092
+ const copy = allocU8(data.length);
2093
+ copy.set(data);
2094
+ stored = copy;
2095
+ }
2096
+ parts.data = stored;
2097
+ if (dtype === "dict") {
2098
+ const dictionary = seedDictionary(meta);
2099
+ for (let row = 0; row < length; row++) {
2100
+ const code = data[row];
2101
+ if (code >= dictionary.size) {
2102
+ throw new GraphFormatError("E_COLUMN_TYPE", `column "${name}": code ${code} at row ${row} is outside the ${dictionary.size} declared options`, { column: name, row, code });
2103
+ }
2104
+ }
2105
+ parts.dictionary = dictionary.values;
2106
+ }
2107
+ return createColumn(parts);
2108
+ }
2109
+ //# sourceMappingURL=column.js.map