@felixdrp/gcf-wasm 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/NOTICE +31 -0
- package/README.md +115 -2
- package/dist/cli.js +712 -0
- package/dist/gcf.wasm +0 -0
- package/dist/host.d.ts +330 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +1006 -0
- package/package.json +34 -4
package/dist/cli.js
ADDED
|
@@ -0,0 +1,712 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
|
|
6
|
+
// src/host.ts
|
|
7
|
+
class GcfError extends Error {
|
|
8
|
+
code;
|
|
9
|
+
detail;
|
|
10
|
+
line;
|
|
11
|
+
constructor(code, detail, line) {
|
|
12
|
+
super(line > 0 ? `${code} (line ${line}): ${detail}` : `${code}: ${detail}`);
|
|
13
|
+
this.name = "GcfError";
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.detail = detail;
|
|
16
|
+
this.line = line;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
var utf8 = new TextEncoder;
|
|
20
|
+
var fromUtf8 = new TextDecoder;
|
|
21
|
+
function isPlainGraph(text) {
|
|
22
|
+
if (!text.startsWith("GCF profile=graph"))
|
|
23
|
+
return false;
|
|
24
|
+
const nl = text.indexOf(`
|
|
25
|
+
`);
|
|
26
|
+
const header = nl < 0 ? text : text.slice(0, nl);
|
|
27
|
+
return !/ delta=true(\s|$)/.test(header);
|
|
28
|
+
}
|
|
29
|
+
function toJsonText(value) {
|
|
30
|
+
try {
|
|
31
|
+
const s = JSON.stringify(value);
|
|
32
|
+
if (s !== undefined && !/(?<![\d.eE+-])-?\d{16,}(?![\d.eE])/.test(s))
|
|
33
|
+
return s;
|
|
34
|
+
} catch {}
|
|
35
|
+
const skip = (v) => v === undefined || typeof v === "function" || typeof v === "symbol";
|
|
36
|
+
const walk = (v0, key) => {
|
|
37
|
+
const v = v0 !== null && typeof v0 === "object" && typeof v0.toJSON === "function" ? v0.toJSON(key) : v0;
|
|
38
|
+
if (typeof v === "bigint")
|
|
39
|
+
return v.toString();
|
|
40
|
+
if (typeof v === "number" && Number.isInteger(v) && Math.abs(v) >= 9007199254740992)
|
|
41
|
+
return v.toExponential();
|
|
42
|
+
if (v === null || skip(v) || typeof v === "number" || typeof v === "boolean" || typeof v === "string")
|
|
43
|
+
return skip(v) ? "null" : JSON.stringify(v);
|
|
44
|
+
if (Array.isArray(v)) {
|
|
45
|
+
const items = [];
|
|
46
|
+
for (let i = 0;i < v.length; i++)
|
|
47
|
+
items.push(skip(v[i]) ? "null" : walk(v[i], String(i)));
|
|
48
|
+
return `[${items.join(",")}]`;
|
|
49
|
+
}
|
|
50
|
+
const entries = v instanceof Map ? [...v.entries()] : Object.entries(v);
|
|
51
|
+
return `{${entries.filter(([, x]) => !skip(x)).map(([k, x]) => `${JSON.stringify(String(k))}:${walk(x, String(k))}`).join(",")}}`;
|
|
52
|
+
};
|
|
53
|
+
return walk(value, "");
|
|
54
|
+
}
|
|
55
|
+
function isPlainRecord(v) {
|
|
56
|
+
if (v === null || typeof v !== "object" || Array.isArray(v))
|
|
57
|
+
return false;
|
|
58
|
+
const proto = Object.getPrototypeOf(v);
|
|
59
|
+
return (proto === Object.prototype || proto === null) && v.toJSON === undefined;
|
|
60
|
+
}
|
|
61
|
+
function utf8LengthStrict(s) {
|
|
62
|
+
let n = 0;
|
|
63
|
+
for (let i = 0;i < s.length; i++) {
|
|
64
|
+
const c = s.charCodeAt(i);
|
|
65
|
+
if (c < 128)
|
|
66
|
+
n += 1;
|
|
67
|
+
else if (c < 2048)
|
|
68
|
+
n += 2;
|
|
69
|
+
else if (c >= 55296 && c <= 56319) {
|
|
70
|
+
const d = i + 1 < s.length ? s.charCodeAt(i + 1) : 0;
|
|
71
|
+
if (d < 56320 || d > 57343)
|
|
72
|
+
return -1;
|
|
73
|
+
n += 4;
|
|
74
|
+
i++;
|
|
75
|
+
} else if (c >= 56320 && c <= 57343)
|
|
76
|
+
return -1;
|
|
77
|
+
else
|
|
78
|
+
n += 3;
|
|
79
|
+
}
|
|
80
|
+
return n;
|
|
81
|
+
}
|
|
82
|
+
function parseLargeInts(text, mode) {
|
|
83
|
+
let i = 0;
|
|
84
|
+
const ws = () => {
|
|
85
|
+
while (i < text.length && (text[i] === " " || text[i] === `
|
|
86
|
+
` || text[i] === "\r" || text[i] === "\t"))
|
|
87
|
+
i++;
|
|
88
|
+
};
|
|
89
|
+
const value = () => {
|
|
90
|
+
ws();
|
|
91
|
+
const c = text[i];
|
|
92
|
+
if (c === "{") {
|
|
93
|
+
i++;
|
|
94
|
+
const o = {};
|
|
95
|
+
ws();
|
|
96
|
+
if (text[i] === "}") {
|
|
97
|
+
i++;
|
|
98
|
+
return o;
|
|
99
|
+
}
|
|
100
|
+
for (;; ) {
|
|
101
|
+
ws();
|
|
102
|
+
const k = value();
|
|
103
|
+
ws();
|
|
104
|
+
i++;
|
|
105
|
+
o[k] = value();
|
|
106
|
+
ws();
|
|
107
|
+
if (text[i++] === "}")
|
|
108
|
+
return o;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (c === "[") {
|
|
112
|
+
i++;
|
|
113
|
+
const a = [];
|
|
114
|
+
ws();
|
|
115
|
+
if (text[i] === "]") {
|
|
116
|
+
i++;
|
|
117
|
+
return a;
|
|
118
|
+
}
|
|
119
|
+
for (;; ) {
|
|
120
|
+
a.push(value());
|
|
121
|
+
ws();
|
|
122
|
+
if (text[i++] === "]")
|
|
123
|
+
return a;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (c === '"') {
|
|
127
|
+
const start = i++;
|
|
128
|
+
while (text[i] !== '"')
|
|
129
|
+
i += text[i] === "\\" ? 2 : 1;
|
|
130
|
+
i++;
|
|
131
|
+
return JSON.parse(text.slice(start, i));
|
|
132
|
+
}
|
|
133
|
+
const start = i;
|
|
134
|
+
while (i < text.length && !`,}]
|
|
135
|
+
\r `.includes(text[i]))
|
|
136
|
+
i++;
|
|
137
|
+
const lex = text.slice(start, i);
|
|
138
|
+
if (lex === "true")
|
|
139
|
+
return true;
|
|
140
|
+
if (lex === "false")
|
|
141
|
+
return false;
|
|
142
|
+
if (lex === "null")
|
|
143
|
+
return null;
|
|
144
|
+
if (/^-?\d+$/.test(lex)) {
|
|
145
|
+
const n = Number(lex);
|
|
146
|
+
if (!Number.isSafeInteger(n))
|
|
147
|
+
return mode === "bigint" ? BigInt(lex) : lex;
|
|
148
|
+
return n;
|
|
149
|
+
}
|
|
150
|
+
return Number(lex);
|
|
151
|
+
};
|
|
152
|
+
return value();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
class GcfCodec {
|
|
156
|
+
exports;
|
|
157
|
+
constructor(exports) {
|
|
158
|
+
this.exports = exports;
|
|
159
|
+
}
|
|
160
|
+
static async instantiate(wasm) {
|
|
161
|
+
const instance = wasm instanceof WebAssembly.Module ? await WebAssembly.instantiate(wasm, {}) : (await WebAssembly.instantiate(wasm, {})).instance;
|
|
162
|
+
return new GcfCodec(instance.exports);
|
|
163
|
+
}
|
|
164
|
+
load(input) {
|
|
165
|
+
const x = this.exports;
|
|
166
|
+
if (typeof input !== "string") {
|
|
167
|
+
const ptr = x.gcf_input_alloc(input.length);
|
|
168
|
+
new Uint8Array(x.memory.buffer, ptr, input.length).set(input);
|
|
169
|
+
return input.length;
|
|
170
|
+
}
|
|
171
|
+
const cap = input.length * 3;
|
|
172
|
+
const ptr = x.gcf_input_alloc(cap);
|
|
173
|
+
return utf8.encodeInto(input, new Uint8Array(x.memory.buffer, ptr, cap)).written;
|
|
174
|
+
}
|
|
175
|
+
output(status) {
|
|
176
|
+
const x = this.exports;
|
|
177
|
+
const text = (p, n) => fromUtf8.decode(new Uint8Array(x.memory.buffer, p, n));
|
|
178
|
+
if (status !== 0) {
|
|
179
|
+
throw new GcfError(text(x.gcf_error_name_ptr(), x.gcf_error_name_len()), text(x.gcf_error_message_ptr(), x.gcf_error_message_len()), x.gcf_error_line());
|
|
180
|
+
}
|
|
181
|
+
return text(x.gcf_output_ptr(), x.gcf_output_len());
|
|
182
|
+
}
|
|
183
|
+
outputBytes(status, view) {
|
|
184
|
+
if (status !== 0)
|
|
185
|
+
this.output(status);
|
|
186
|
+
const x = this.exports;
|
|
187
|
+
const bytes = new Uint8Array(x.memory.buffer, x.gcf_output_ptr(), x.gcf_output_len());
|
|
188
|
+
return view ? bytes : bytes.slice();
|
|
189
|
+
}
|
|
190
|
+
run(fn, input) {
|
|
191
|
+
return this.output(fn.call(this.exports, this.load(input)));
|
|
192
|
+
}
|
|
193
|
+
parse(json, mode) {
|
|
194
|
+
if (!this.exports.gcf_output_unsafe_ints() || mode === "number")
|
|
195
|
+
return JSON.parse(json);
|
|
196
|
+
if (mode === "error") {
|
|
197
|
+
const m = /-?\d{16,}/.exec(json);
|
|
198
|
+
throw new GcfError("unsafe_integer", `integer ${m ? m[0] : ""} exceeds the safe-integer range (2^53-1) of a JavaScript number; decode with largeInt 'string' | 'bigint' | 'number', or model the value as a string`, 0);
|
|
199
|
+
}
|
|
200
|
+
return parseLargeInts(json, mode);
|
|
201
|
+
}
|
|
202
|
+
decodeToJson(input) {
|
|
203
|
+
return this.output(this.exports.gcf_decode(this.load(input)));
|
|
204
|
+
}
|
|
205
|
+
decode(input, opts = {}) {
|
|
206
|
+
const mode = opts.largeInt ?? "error";
|
|
207
|
+
if (typeof input === "string" && isPlainGraph(input)) {
|
|
208
|
+
const cols = this.parse(this.output(this.exports.gcf_decode_graph_columns(this.load(input))), mode);
|
|
209
|
+
return GcfCodec.graphFromColumns(cols);
|
|
210
|
+
}
|
|
211
|
+
return this.parse(this.decodeToJson(input), mode);
|
|
212
|
+
}
|
|
213
|
+
decodeLazy(input, opts = {}) {
|
|
214
|
+
const tape = this.outputBytes(this.exports.gcf_decode_tape(this.load(input)), false);
|
|
215
|
+
return new LazyTape(tape.buffer, opts.largeInt ?? "error").root();
|
|
216
|
+
}
|
|
217
|
+
static graphFromColumns(c) {
|
|
218
|
+
const { names, kinds, provs, types, statuses, k, p, s, d, es, et, ty, st } = c;
|
|
219
|
+
const n = k.length;
|
|
220
|
+
const symbols = new Array(n);
|
|
221
|
+
for (let i = 0;i < n; i++) {
|
|
222
|
+
symbols[i] = { qualifiedName: names[i], kind: kinds[k[i]], score: s[i], provenance: provs[p[i]], distance: d[i] };
|
|
223
|
+
}
|
|
224
|
+
const m = es.length;
|
|
225
|
+
const edges = new Array(m);
|
|
226
|
+
for (let j = 0;j < m; j++) {
|
|
227
|
+
edges[j] = { source: names[es[j]], target: names[et[j]], edgeType: types[ty[j]], status: statuses[st[j]] };
|
|
228
|
+
}
|
|
229
|
+
const out = { tool: c.tool, tokenBudget: c.tokenBudget, tokensUsed: c.tokensUsed, packRoot: c.packRoot };
|
|
230
|
+
if (c.unchanged)
|
|
231
|
+
out.unchanged = true;
|
|
232
|
+
if (c.session)
|
|
233
|
+
out.session = true;
|
|
234
|
+
out.symbols = symbols;
|
|
235
|
+
out.edges = edges;
|
|
236
|
+
if (c.references)
|
|
237
|
+
out.references = c.references;
|
|
238
|
+
return out;
|
|
239
|
+
}
|
|
240
|
+
encodeJsonBytes(json, opts = {}) {
|
|
241
|
+
const len = this.load(json);
|
|
242
|
+
const status = opts.profile === "graph" ? this.exports.gcf_encode_graph(len) : this.exports.gcf_encode(len, opts.noFlatten ? 1 : 0);
|
|
243
|
+
return this.outputBytes(status, opts.view ?? false);
|
|
244
|
+
}
|
|
245
|
+
decodeToJsonBytes(gcf, opts = {}) {
|
|
246
|
+
return this.outputBytes(this.exports.gcf_decode(this.load(gcf)), opts.view ?? false);
|
|
247
|
+
}
|
|
248
|
+
encode(value, opts = {}) {
|
|
249
|
+
const x = this.exports;
|
|
250
|
+
if (x.gcf_features && (x.gcf_features() & 2) !== 0) {
|
|
251
|
+
let text;
|
|
252
|
+
try {
|
|
253
|
+
text = JSON.stringify(value);
|
|
254
|
+
} catch {
|
|
255
|
+
text = undefined;
|
|
256
|
+
}
|
|
257
|
+
if (text !== undefined)
|
|
258
|
+
return this.output(x.gcf_encode(this.load(text), (opts.noFlatten ? 1 : 0) | 2));
|
|
259
|
+
}
|
|
260
|
+
return this.encodeJson(toJsonText(value), opts);
|
|
261
|
+
}
|
|
262
|
+
encodeJson(json, opts = {}) {
|
|
263
|
+
return this.output(this.exports.gcf_encode(this.load(json), opts.noFlatten ? 1 : 0));
|
|
264
|
+
}
|
|
265
|
+
encodeGraph(payload) {
|
|
266
|
+
if (this.exports.gcf_encode_graph_cols) {
|
|
267
|
+
const out = this.encodeGraphColumns(payload);
|
|
268
|
+
if (out !== null)
|
|
269
|
+
return out;
|
|
270
|
+
}
|
|
271
|
+
return this.run(this.exports.gcf_encode_graph, toJsonText(payload));
|
|
272
|
+
}
|
|
273
|
+
encodeGraphColumns(p) {
|
|
274
|
+
if (!isPlainRecord(p))
|
|
275
|
+
return null;
|
|
276
|
+
const syms = Array.isArray(p.symbols) && p.symbols.toJSON === undefined ? p.symbols : [];
|
|
277
|
+
const edges = Array.isArray(p.edges) && p.edges.toJSON === undefined ? p.edges : [];
|
|
278
|
+
const ns = syms.length;
|
|
279
|
+
const ne = edges.length;
|
|
280
|
+
const parts = new Array(2 + 3 * ns + 4 * ne);
|
|
281
|
+
const nums = new Float64Array(2 + 2 * ns);
|
|
282
|
+
let k = 0;
|
|
283
|
+
let bail = false;
|
|
284
|
+
const str = (v) => {
|
|
285
|
+
if (typeof v === "string")
|
|
286
|
+
return v;
|
|
287
|
+
if (v !== null && typeof v === "object")
|
|
288
|
+
bail = true;
|
|
289
|
+
return "";
|
|
290
|
+
};
|
|
291
|
+
const num = (v) => {
|
|
292
|
+
if (typeof v === "number")
|
|
293
|
+
return Number.isFinite(v) && v !== 0 ? v : 0;
|
|
294
|
+
if (typeof v === "bigint" || v !== null && typeof v === "object" || typeof v === "string" && utf8LengthStrict(v) < 0)
|
|
295
|
+
bail = true;
|
|
296
|
+
return 0;
|
|
297
|
+
};
|
|
298
|
+
parts[k++] = str(p.tool);
|
|
299
|
+
parts[k++] = str(p.packRoot);
|
|
300
|
+
nums[0] = num(p.tokenBudget);
|
|
301
|
+
nums[1] = num(p.tokensUsed);
|
|
302
|
+
for (let i = 0;i < ns; i++) {
|
|
303
|
+
const s = syms[i];
|
|
304
|
+
if (s !== null && typeof s === "object" && !Array.isArray(s)) {
|
|
305
|
+
if (!isPlainRecord(s))
|
|
306
|
+
return null;
|
|
307
|
+
parts[k++] = str(s.qualifiedName);
|
|
308
|
+
parts[k++] = str(s.kind);
|
|
309
|
+
parts[k++] = str(s.provenance);
|
|
310
|
+
nums[2 + 2 * i] = num(s.score);
|
|
311
|
+
nums[3 + 2 * i] = num(s.distance);
|
|
312
|
+
} else {
|
|
313
|
+
parts[k++] = "";
|
|
314
|
+
parts[k++] = "";
|
|
315
|
+
parts[k++] = "";
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
for (let i = 0;i < ne; i++) {
|
|
319
|
+
const e = edges[i];
|
|
320
|
+
if (e !== null && typeof e === "object" && !Array.isArray(e)) {
|
|
321
|
+
if (!isPlainRecord(e))
|
|
322
|
+
return null;
|
|
323
|
+
parts[k++] = str(e.source);
|
|
324
|
+
parts[k++] = str(e.target);
|
|
325
|
+
parts[k++] = str(e.edgeType);
|
|
326
|
+
parts[k++] = str(e.status);
|
|
327
|
+
} else {
|
|
328
|
+
parts[k++] = "";
|
|
329
|
+
parts[k++] = "";
|
|
330
|
+
parts[k++] = "";
|
|
331
|
+
parts[k++] = "";
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (bail)
|
|
335
|
+
return null;
|
|
336
|
+
const joined = parts.join("");
|
|
337
|
+
const head = 48 + 40 * ns + 32 * ne;
|
|
338
|
+
const cap = head + 3 * joined.length;
|
|
339
|
+
const x = this.exports;
|
|
340
|
+
const ptr = x.gcf_input_alloc(cap);
|
|
341
|
+
const buf = x.memory.buffer;
|
|
342
|
+
const written = utf8.encodeInto(joined, new Uint8Array(buf, ptr + head, cap - head)).written;
|
|
343
|
+
const lens = new Uint32Array(parts.length);
|
|
344
|
+
if (written === joined.length) {
|
|
345
|
+
for (let i = 0;i < parts.length; i++)
|
|
346
|
+
lens[i] = parts[i].length;
|
|
347
|
+
} else {
|
|
348
|
+
for (let i = 0;i < parts.length; i++) {
|
|
349
|
+
const n = utf8LengthStrict(parts[i]);
|
|
350
|
+
if (n < 0)
|
|
351
|
+
return null;
|
|
352
|
+
lens[i] = n;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
const u32 = new Uint32Array(buf, ptr, head >>> 2);
|
|
356
|
+
const f64 = new Float64Array(buf, ptr, head >>> 3);
|
|
357
|
+
u32[0] = 1195787079;
|
|
358
|
+
u32[1] = ns;
|
|
359
|
+
u32[2] = ne;
|
|
360
|
+
u32[3] = head;
|
|
361
|
+
let off = 0;
|
|
362
|
+
let j = 0;
|
|
363
|
+
const put = (at) => {
|
|
364
|
+
u32[at] = off;
|
|
365
|
+
u32[at + 1] = lens[j];
|
|
366
|
+
off += lens[j++];
|
|
367
|
+
};
|
|
368
|
+
put(4);
|
|
369
|
+
put(6);
|
|
370
|
+
f64[4] = nums[0];
|
|
371
|
+
f64[5] = nums[1];
|
|
372
|
+
for (let i = 0;i < ns; i++) {
|
|
373
|
+
f64[6 + 5 * i] = nums[2 + 2 * i];
|
|
374
|
+
f64[7 + 5 * i] = nums[3 + 2 * i];
|
|
375
|
+
const w = 16 + 10 * i;
|
|
376
|
+
put(w);
|
|
377
|
+
put(w + 2);
|
|
378
|
+
put(w + 4);
|
|
379
|
+
}
|
|
380
|
+
const e0 = 12 + 10 * ns;
|
|
381
|
+
for (let i = 0;i < ne; i++) {
|
|
382
|
+
const w = e0 + 8 * i;
|
|
383
|
+
put(w);
|
|
384
|
+
put(w + 2);
|
|
385
|
+
put(w + 4);
|
|
386
|
+
put(w + 6);
|
|
387
|
+
}
|
|
388
|
+
return this.output(x.gcf_encode_graph_cols(head + written));
|
|
389
|
+
}
|
|
390
|
+
encodeGraphDelta(delta) {
|
|
391
|
+
return this.run(this.exports.gcf_encode_graph_delta, toJsonText(delta));
|
|
392
|
+
}
|
|
393
|
+
encodeGrouped(data, keyField, groupField) {
|
|
394
|
+
return this.run(this.exports.gcf_encode_grouped, toJsonText({ data, keyField, groupField }));
|
|
395
|
+
}
|
|
396
|
+
encodeGenericFull(set, tool = "") {
|
|
397
|
+
return this.run(this.exports.gcf_encode_generic_full, toJsonText({ set, tool }));
|
|
398
|
+
}
|
|
399
|
+
encodeGenericDelta(delta) {
|
|
400
|
+
return this.run(this.exports.gcf_encode_generic_delta, toJsonText(delta));
|
|
401
|
+
}
|
|
402
|
+
encodeGenericDiff(base, next, opts = {}) {
|
|
403
|
+
return this.run(this.exports.gcf_generic_diff, toJsonText({ base, next, ...opts }));
|
|
404
|
+
}
|
|
405
|
+
packRoot(input) {
|
|
406
|
+
return this.run(this.exports.gcf_pack_root, toJsonText(input));
|
|
407
|
+
}
|
|
408
|
+
applyGenericDelta(wire, base, expectedNewRoot) {
|
|
409
|
+
return this.run(this.exports.gcf_delta_decode_apply, toJsonText({ wire, base, expectedNewRoot }));
|
|
410
|
+
}
|
|
411
|
+
applyGraphDelta(wire, base) {
|
|
412
|
+
return JSON.parse(this.run(this.exports.gcf_graph_delta_apply, toJsonText({ wire, base })));
|
|
413
|
+
}
|
|
414
|
+
encodeGraphSessionRaw(payload, known, next) {
|
|
415
|
+
return this.run(this.exports.gcf_encode_graph_session, toJsonText({ payload, known, next }));
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
var TAPE_HEADER = 20;
|
|
419
|
+
var textDecoder = new TextDecoder;
|
|
420
|
+
var lazyNodes = new WeakMap;
|
|
421
|
+
|
|
422
|
+
class LazyTape {
|
|
423
|
+
w;
|
|
424
|
+
pool;
|
|
425
|
+
dv;
|
|
426
|
+
rootIndex;
|
|
427
|
+
keyText = new Map;
|
|
428
|
+
keyOffset = new Map;
|
|
429
|
+
proxies = new Map;
|
|
430
|
+
largeInt;
|
|
431
|
+
constructor(buf, largeInt) {
|
|
432
|
+
this.largeInt = largeInt;
|
|
433
|
+
const h = new Uint32Array(buf, 0, 5);
|
|
434
|
+
if (h[0] !== 1413890887 || h[1] !== 1)
|
|
435
|
+
throw new GcfError("invalid_input", "not a GCF tape", 0);
|
|
436
|
+
this.rootIndex = h[2];
|
|
437
|
+
this.w = new Uint32Array(buf, TAPE_HEADER, h[3]);
|
|
438
|
+
this.pool = new Uint8Array(buf, TAPE_HEADER + h[3] * 4, h[4]);
|
|
439
|
+
this.dv = new DataView(buf);
|
|
440
|
+
}
|
|
441
|
+
root() {
|
|
442
|
+
return this.value(this.rootIndex);
|
|
443
|
+
}
|
|
444
|
+
text(off, len) {
|
|
445
|
+
const p = this.pool;
|
|
446
|
+
if (len <= 24) {
|
|
447
|
+
let s = "";
|
|
448
|
+
for (let i = off, end = off + len;i < end; i++) {
|
|
449
|
+
const b = p[i];
|
|
450
|
+
if (b >= 128)
|
|
451
|
+
return textDecoder.decode(p.subarray(off, off + len));
|
|
452
|
+
s += String.fromCharCode(b);
|
|
453
|
+
}
|
|
454
|
+
return s;
|
|
455
|
+
}
|
|
456
|
+
return textDecoder.decode(p.subarray(off, off + len));
|
|
457
|
+
}
|
|
458
|
+
key(off, len) {
|
|
459
|
+
let k = this.keyText.get(off);
|
|
460
|
+
if (k === undefined) {
|
|
461
|
+
k = this.text(off, len);
|
|
462
|
+
this.keyText.set(off, k);
|
|
463
|
+
this.keyOffset.set(k, off);
|
|
464
|
+
}
|
|
465
|
+
return k;
|
|
466
|
+
}
|
|
467
|
+
int64(i) {
|
|
468
|
+
const lo = this.w[i + 1];
|
|
469
|
+
const hi = this.w[i + 2] | 0;
|
|
470
|
+
const n = hi * 4294967296 + lo;
|
|
471
|
+
if (Number.isSafeInteger(n))
|
|
472
|
+
return n;
|
|
473
|
+
const big = BigInt(hi) << 32n | BigInt(lo);
|
|
474
|
+
switch (this.largeInt) {
|
|
475
|
+
case "bigint":
|
|
476
|
+
return big;
|
|
477
|
+
case "string":
|
|
478
|
+
return big.toString();
|
|
479
|
+
case "number":
|
|
480
|
+
return Number(big);
|
|
481
|
+
default:
|
|
482
|
+
throw new GcfError("unsafe_integer", `integer ${big} exceeds the safe-integer range (2^53-1) of a JavaScript number; decode with largeInt 'string' | 'bigint' | 'number'`, 0);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
value(i) {
|
|
486
|
+
const h = this.w[i];
|
|
487
|
+
switch (h & 15) {
|
|
488
|
+
case 0:
|
|
489
|
+
return null;
|
|
490
|
+
case 1:
|
|
491
|
+
return false;
|
|
492
|
+
case 2:
|
|
493
|
+
return true;
|
|
494
|
+
case 3:
|
|
495
|
+
return (h | 0) >> 4;
|
|
496
|
+
case 4:
|
|
497
|
+
return this.int64(i);
|
|
498
|
+
case 5:
|
|
499
|
+
return this.dv.getFloat64(TAPE_HEADER + (i + 1) * 4, true);
|
|
500
|
+
case 6:
|
|
501
|
+
return this.text(this.w[i + 1], h >>> 4);
|
|
502
|
+
default: {
|
|
503
|
+
let p = this.proxies.get(i);
|
|
504
|
+
if (p === undefined) {
|
|
505
|
+
p = (h & 15) === 7 ? this.arrayProxy(i) : this.objectProxy(i);
|
|
506
|
+
this.proxies.set(i, p);
|
|
507
|
+
lazyNodes.set(p, [this, i]);
|
|
508
|
+
}
|
|
509
|
+
return p;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
find(i, name) {
|
|
514
|
+
const n = this.w[i] >>> 4;
|
|
515
|
+
const base = i + 1;
|
|
516
|
+
const w = this.w;
|
|
517
|
+
const off = this.keyOffset.get(name);
|
|
518
|
+
if (off !== undefined) {
|
|
519
|
+
for (let k = 0, j = base;k < n; k++, j += 3)
|
|
520
|
+
if (w[j] === off)
|
|
521
|
+
return w[j + 2];
|
|
522
|
+
return -1;
|
|
523
|
+
}
|
|
524
|
+
for (let k = 0, j = base;k < n; k++, j += 3) {
|
|
525
|
+
if (this.key(w[j], w[j + 1]) === name)
|
|
526
|
+
return w[j + 2];
|
|
527
|
+
}
|
|
528
|
+
return -1;
|
|
529
|
+
}
|
|
530
|
+
keys(i) {
|
|
531
|
+
const n = this.w[i] >>> 4;
|
|
532
|
+
const out = new Array(n);
|
|
533
|
+
for (let k = 0, j = i + 1;k < n; k++, j += 3)
|
|
534
|
+
out[k] = this.key(this.w[j], this.w[j + 1]);
|
|
535
|
+
return out;
|
|
536
|
+
}
|
|
537
|
+
objectProxy(i) {
|
|
538
|
+
const tape = this;
|
|
539
|
+
const readOnly = () => false;
|
|
540
|
+
return new Proxy({}, {
|
|
541
|
+
get(t, prop, receiver) {
|
|
542
|
+
if (typeof prop === "string") {
|
|
543
|
+
const c = tape.find(i, prop);
|
|
544
|
+
if (c >= 0)
|
|
545
|
+
return tape.value(c);
|
|
546
|
+
}
|
|
547
|
+
return Reflect.get(t, prop, receiver);
|
|
548
|
+
},
|
|
549
|
+
has(t, prop) {
|
|
550
|
+
return typeof prop === "string" && tape.find(i, prop) >= 0 || Reflect.has(t, prop);
|
|
551
|
+
},
|
|
552
|
+
ownKeys() {
|
|
553
|
+
return tape.keys(i);
|
|
554
|
+
},
|
|
555
|
+
getOwnPropertyDescriptor(_t, prop) {
|
|
556
|
+
if (typeof prop !== "string")
|
|
557
|
+
return;
|
|
558
|
+
const c = tape.find(i, prop);
|
|
559
|
+
return c < 0 ? undefined : { value: tape.value(c), writable: false, enumerable: true, configurable: true };
|
|
560
|
+
},
|
|
561
|
+
set: readOnly,
|
|
562
|
+
defineProperty: readOnly,
|
|
563
|
+
deleteProperty: readOnly
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
arrayProxy(i) {
|
|
567
|
+
const tape = this;
|
|
568
|
+
const n = this.w[i] >>> 4;
|
|
569
|
+
const index = (prop) => {
|
|
570
|
+
if (typeof prop !== "string" || prop.length === 0)
|
|
571
|
+
return -1;
|
|
572
|
+
const c = prop.charCodeAt(0);
|
|
573
|
+
if (c < 48 || c > 57)
|
|
574
|
+
return -1;
|
|
575
|
+
const k = Number(prop);
|
|
576
|
+
return Number.isInteger(k) && k < n && String(k) === prop ? k : -1;
|
|
577
|
+
};
|
|
578
|
+
const readOnly = () => false;
|
|
579
|
+
return new Proxy(new Array(n), {
|
|
580
|
+
get(t, prop, receiver) {
|
|
581
|
+
const k = index(prop);
|
|
582
|
+
if (k >= 0)
|
|
583
|
+
return tape.value(tape.w[i + 1 + k]);
|
|
584
|
+
return Reflect.get(t, prop, receiver);
|
|
585
|
+
},
|
|
586
|
+
has(t, prop) {
|
|
587
|
+
return index(prop) >= 0 || Reflect.has(t, prop);
|
|
588
|
+
},
|
|
589
|
+
ownKeys() {
|
|
590
|
+
const keys = new Array(n + 1);
|
|
591
|
+
for (let k = 0;k < n; k++)
|
|
592
|
+
keys[k] = String(k);
|
|
593
|
+
keys[n] = "length";
|
|
594
|
+
return keys;
|
|
595
|
+
},
|
|
596
|
+
getOwnPropertyDescriptor(t, prop) {
|
|
597
|
+
const k = index(prop);
|
|
598
|
+
if (k >= 0)
|
|
599
|
+
return { value: tape.value(tape.w[i + 1 + k]), writable: false, enumerable: true, configurable: true };
|
|
600
|
+
return Reflect.getOwnPropertyDescriptor(t, prop);
|
|
601
|
+
},
|
|
602
|
+
set: readOnly,
|
|
603
|
+
defineProperty: readOnly,
|
|
604
|
+
deleteProperty: readOnly
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
plain(i) {
|
|
608
|
+
const h = this.w[i];
|
|
609
|
+
const tag = h & 15;
|
|
610
|
+
if (tag === 7) {
|
|
611
|
+
const n = h >>> 4;
|
|
612
|
+
const out = new Array(n);
|
|
613
|
+
for (let k = 0;k < n; k++)
|
|
614
|
+
out[k] = this.plain(this.w[i + 1 + k]);
|
|
615
|
+
return out;
|
|
616
|
+
}
|
|
617
|
+
if (tag === 8) {
|
|
618
|
+
const n = h >>> 4;
|
|
619
|
+
const out = {};
|
|
620
|
+
for (let k = 0, j = i + 1;k < n; k++, j += 3)
|
|
621
|
+
out[this.key(this.w[j], this.w[j + 1])] = this.plain(this.w[j + 2]);
|
|
622
|
+
return out;
|
|
623
|
+
}
|
|
624
|
+
return this.value(i);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
var I64_MIN = -9223372036854775808n;
|
|
628
|
+
|
|
629
|
+
// src/cli.ts
|
|
630
|
+
var USAGE = `usage:
|
|
631
|
+
gcf encode [file] [--graph] [--no-flatten] JSON -> GCF (stdin if no file)
|
|
632
|
+
gcf decode [file] [--large-int=error|string|bigint|number]
|
|
633
|
+
gcf stats [file] [--graph] JSON vs GCF size and estimated tokens`;
|
|
634
|
+
function main(argv, gcf) {
|
|
635
|
+
const [command, ...rest] = argv;
|
|
636
|
+
if (command === "--help" || command === "-h") {
|
|
637
|
+
console.log(USAGE);
|
|
638
|
+
return 0;
|
|
639
|
+
}
|
|
640
|
+
if (command !== "encode" && command !== "decode" && command !== "stats") {
|
|
641
|
+
console.error(USAGE);
|
|
642
|
+
return command ? 1 : 0;
|
|
643
|
+
}
|
|
644
|
+
const flags = new Set(rest.filter((a) => a.startsWith("--")));
|
|
645
|
+
const file = rest.find((a) => !a.startsWith("--"));
|
|
646
|
+
const input = readFileSync(file && file !== "-" ? file : 0, "utf8");
|
|
647
|
+
const isGraphPayload = (json) => {
|
|
648
|
+
if (flags.has("--graph"))
|
|
649
|
+
return true;
|
|
650
|
+
try {
|
|
651
|
+
const v = JSON.parse(json);
|
|
652
|
+
return v !== null && typeof v === "object" && Array.isArray(v.symbols) && Array.isArray(v.edges ?? []);
|
|
653
|
+
} catch {
|
|
654
|
+
return false;
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
const encode = (json) => isGraphPayload(json) ? gcf.encodeGraph(JSON.parse(json)) : gcf.encodeJson(json, { noFlatten: flags.has("--no-flatten") });
|
|
658
|
+
try {
|
|
659
|
+
if (command === "encode") {
|
|
660
|
+
process.stdout.write(encode(input));
|
|
661
|
+
} else if (command === "decode") {
|
|
662
|
+
const mode = [...flags].find((f) => f.startsWith("--large-int="))?.split("=")[1];
|
|
663
|
+
const value = gcf.decode(input, { largeInt: mode ?? "string" });
|
|
664
|
+
console.log(JSON.stringify(value, (_k, v) => typeof v === "bigint" ? v.toString() : v, 2));
|
|
665
|
+
} else {
|
|
666
|
+
const out = encode(input);
|
|
667
|
+
const jsonTokens = Math.round(input.trim().length / 4);
|
|
668
|
+
const gcfTokens = Math.round(out.trim().length / 4);
|
|
669
|
+
const bar = (n) => "█".repeat(Math.max(1, Math.round(30 * n / Math.max(jsonTokens, 1)))).padEnd(30, "░");
|
|
670
|
+
console.log(`JSON ${bar(jsonTokens)} ${input.length} chars ~${jsonTokens} tokens`);
|
|
671
|
+
console.log(`GCF ${bar(gcfTokens)} ${out.length} chars ~${gcfTokens} tokens`);
|
|
672
|
+
console.log(`
|
|
673
|
+
~${Math.round(100 * (1 - gcfTokens / Math.max(jsonTokens, 1)))}% fewer tokens (estimate: 4 chars/token)`);
|
|
674
|
+
}
|
|
675
|
+
return 0;
|
|
676
|
+
} catch (e) {
|
|
677
|
+
if (e instanceof GcfError) {
|
|
678
|
+
console.error(`gcf: ${e.message}`);
|
|
679
|
+
return 2;
|
|
680
|
+
}
|
|
681
|
+
throw e;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
// src/index.ts
|
|
685
|
+
var shared;
|
|
686
|
+
async function wasmBytes(source) {
|
|
687
|
+
if (source instanceof URL || typeof source === "string") {
|
|
688
|
+
const url = source instanceof URL ? source : new URL(source, import.meta.url);
|
|
689
|
+
if (url.protocol === "file:") {
|
|
690
|
+
const { readFile } = await import("node:fs/promises");
|
|
691
|
+
return readFile(url);
|
|
692
|
+
}
|
|
693
|
+
source = fetch(url);
|
|
694
|
+
}
|
|
695
|
+
const res = await source;
|
|
696
|
+
if (!res.ok)
|
|
697
|
+
throw new Error(`failed to load gcf.wasm: HTTP ${res.status}`);
|
|
698
|
+
return res.arrayBuffer();
|
|
699
|
+
}
|
|
700
|
+
function init(source) {
|
|
701
|
+
if (source === undefined) {
|
|
702
|
+
shared ??= wasmBytes(new URL("./gcf.wasm", import.meta.url)).then((b) => GcfCodec.instantiate(b));
|
|
703
|
+
return shared;
|
|
704
|
+
}
|
|
705
|
+
if (source instanceof WebAssembly.Module || source instanceof ArrayBuffer || ArrayBuffer.isView(source)) {
|
|
706
|
+
return GcfCodec.instantiate(source);
|
|
707
|
+
}
|
|
708
|
+
return wasmBytes(source).then((b) => GcfCodec.instantiate(b));
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// src/cli-npm.ts
|
|
712
|
+
process.exitCode = main(process.argv.slice(2), await init());
|
package/dist/gcf.wasm
ADDED
|
Binary file
|