links-notation 0.22.0 → 0.23.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/dist/index.js CHANGED
@@ -2980,16 +2980,609 @@ class FormatOptions {
2980
2980
  // src/FormatConfig.js
2981
2981
  class FormatConfig extends FormatOptions {
2982
2982
  }
2983
+ // src/Binary.js
2984
+ var U64 = (1n << 64n) - 1n;
2985
+ var WIDTHS = [1, 2, 4, 8];
2986
+ function requireValue(condition, message) {
2987
+ if (!condition)
2988
+ throw new RangeError(message);
2989
+ }
2990
+ function uint64(value) {
2991
+ requireValue(typeof value === "bigint" || Number.isSafeInteger(value) && value >= 0, "expected uint64");
2992
+ const result = BigInt(value);
2993
+ requireValue(result >= 0n && result <= U64, "uint64 overflow");
2994
+ return result;
2995
+ }
2996
+
2997
+ class External {
2998
+ constructor(value) {
2999
+ this.value = uint64(value);
3000
+ }
3001
+ }
3002
+
3003
+ class ArityRange {
3004
+ constructor(min = 2, max = 2) {
3005
+ this.min = uint64(min);
3006
+ this.max = max === null ? null : uint64(max);
3007
+ requireValue(this.min > 0n && this.min <= U64 >> 4n && (this.max === null || this.max >= this.min), "invalid arity");
3008
+ }
3009
+ contains(length) {
3010
+ return BigInt(length) >= this.min && (this.max === null || BigInt(length) <= this.max);
3011
+ }
3012
+ get fixed() {
3013
+ return this.min === this.max;
3014
+ }
3015
+ static parse(text) {
3016
+ requireValue(/^[0-9]+(?:\.\.[0-9]*)?$/.test(text), "invalid arity");
3017
+ const parts = text.split("..");
3018
+ return new ArityRange(BigInt(parts[0]), parts.at(-1) === "" ? null : BigInt(parts.at(-1)));
3019
+ }
3020
+ }
3021
+
3022
+ class DecodeLimits {
3023
+ constructor(values = {}) {
3024
+ Object.assign(this, {
3025
+ maxLinks: 2 ** 22,
3026
+ maxReferences: 2 ** 24,
3027
+ maxNodes: 2 ** 22,
3028
+ maxStringBytes: 64 * 2 ** 20,
3029
+ maxDepth: 64
3030
+ }, values);
3031
+ for (const key of Object.keys(this)) {
3032
+ requireValue([
3033
+ "maxLinks",
3034
+ "maxReferences",
3035
+ "maxNodes",
3036
+ "maxStringBytes",
3037
+ "maxDepth"
3038
+ ].includes(key), "unknown limit");
3039
+ requireValue(Number.isSafeInteger(this[key]) && this[key] >= 0, "invalid limit");
3040
+ }
3041
+ }
3042
+ static unlimited() {
3043
+ return new DecodeLimits({
3044
+ maxLinks: Number.MAX_SAFE_INTEGER,
3045
+ maxReferences: Number.MAX_SAFE_INTEGER,
3046
+ maxNodes: Number.MAX_SAFE_INTEGER,
3047
+ maxStringBytes: Number.MAX_SAFE_INTEGER,
3048
+ maxDepth: Number.MAX_SAFE_INTEGER
3049
+ });
3050
+ }
3051
+ }
3052
+
3053
+ class BinaryLinoOptions {
3054
+ constructor(externalReferences = false, arity = new ArityRange, packedWidths = false) {
3055
+ Object.assign(this, { externalReferences, arity, packedWidths });
3056
+ }
3057
+ static ofPacket(packet) {
3058
+ let min = 2, max = 2;
3059
+ for (const [, link] of packet.links()) {
3060
+ min = Math.min(min, link.length);
3061
+ max = Math.max(max, link.length);
3062
+ }
3063
+ return new BinaryLinoOptions(packet.externalReferences, new ArityRange(min, max), new Set(packet.sections.map((s) => s.width)).size > 1);
3064
+ }
3065
+ }
3066
+
3067
+ class Section {
3068
+ constructor(gap, arity, width, links) {
3069
+ Object.assign(this, { gap: uint64(gap), arity, width, links });
3070
+ }
3071
+ }
3072
+ function referenceWidth(reference, external) {
3073
+ const value = reference instanceof External ? reference.value : uint64(reference);
3074
+ requireValue(!(reference instanceof External) || external, "external references disabled");
3075
+ for (const width of WIDTHS)
3076
+ if (value < 1n << BigInt(width * 8 - Number(external)))
3077
+ return width;
3078
+ throw new RangeError("reference exceeds capacity");
3079
+ }
3080
+ function leb(out, input) {
3081
+ let value = uint64(input);
3082
+ while (value >= 128n) {
3083
+ out.push(Number(value & 127n) | 128);
3084
+ value >>= 7n;
3085
+ }
3086
+ out.push(Number(value));
3087
+ }
3088
+
3089
+ class PacketReader {
3090
+ constructor(bytes, offset = 0) {
3091
+ requireValue(bytes instanceof Uint8Array && Number.isSafeInteger(offset) && offset >= 0 && offset <= bytes.length, "invalid byte cursor");
3092
+ this.bytes = bytes;
3093
+ this.offset = offset;
3094
+ }
3095
+ raw(width) {
3096
+ requireValue(width <= this.bytes.length - this.offset, "unexpected end of packet");
3097
+ let result = 0n;
3098
+ for (let i = 0;i < width; i++)
3099
+ result |= BigInt(this.bytes[this.offset++]) << BigInt(i * 8);
3100
+ return result;
3101
+ }
3102
+ leb() {
3103
+ let value = 0n;
3104
+ for (let shift = 0n;shift < 64n; shift += 7n) {
3105
+ const byte = this.raw(1);
3106
+ requireValue(shift !== 63n || (byte & 127n) <= 1n, "LEB128 overflow");
3107
+ value |= (byte & 127n) << shift;
3108
+ if (byte < 128n)
3109
+ return value;
3110
+ }
3111
+ throw new RangeError("LEB128 overflow");
3112
+ }
3113
+ read(limits = new DecodeLimits) {
3114
+ if (this.offset === this.bytes.length)
3115
+ return null;
3116
+ const header = Number(this.raw(1));
3117
+ requireValue((header & 240) === 16, "unsupported binary version");
3118
+ const sections = [], counts = [];
3119
+ if (header & 2) {
3120
+ requireValue((header & 12) === 0, "explicit header width bits set");
3121
+ const sectionCount = this.leb();
3122
+ requireValue(sectionCount <= BigInt(limits.maxLinks), "too many sections");
3123
+ let address = 1n, total = 0n;
3124
+ for (let i = 0n;i < sectionCount; i++) {
3125
+ const shape = this.leb(), gap = shape & 4n ? this.leb() : 0n;
3126
+ const min = shape >> 4n, extra = shape & 8n ? this.leb() : null;
3127
+ const arity = new ArityRange(min, extra === null ? min : extra === 0n ? null : uint64(min + extra));
3128
+ const count = this.leb();
3129
+ address = uint64(address + gap + count);
3130
+ total += count;
3131
+ requireValue(total <= BigInt(limits.maxLinks), "too many links");
3132
+ sections.push(new Section(gap, arity, WIDTHS[Number(shape & 3n)], []));
3133
+ counts.push(Number(count));
3134
+ }
3135
+ } else {
3136
+ const count = this.leb();
3137
+ uint64(6n + count);
3138
+ requireValue(count <= BigInt(limits.maxLinks), "too many links");
3139
+ if (count) {
3140
+ sections.push(new Section(5, new ArityRange, WIDTHS[header >> 2 & 3], []));
3141
+ counts.push(Number(count));
3142
+ }
3143
+ }
3144
+ let references = 0n;
3145
+ for (let i = 0;i < sections.length; i++) {
3146
+ const section = sections[i];
3147
+ for (let j = 0;j < counts[i]; j++) {
3148
+ const length = uint64(section.arity.min + (section.arity.fixed ? 0n : this.leb()));
3149
+ requireValue(section.arity.contains(length), "link outside arity");
3150
+ references += length;
3151
+ requireValue(references <= BigInt(limits.maxReferences), "too many references");
3152
+ const link = [], top = 1n << BigInt(section.width * 8 - 1);
3153
+ for (let k = 0n;k < length; k++) {
3154
+ const raw = this.raw(section.width);
3155
+ link.push(header & 1 && raw >= top ? new External(raw === top ? 0n : (top << 1n) - raw) : raw);
3156
+ }
3157
+ section.links.push(link);
3158
+ }
3159
+ }
3160
+ return new LinksPacket(Boolean(header & 1), sections);
3161
+ }
3162
+ }
3163
+
3164
+ class LinksPacket {
3165
+ constructor(externalReferences = false, sections = []) {
3166
+ Object.assign(this, { externalReferences, sections });
3167
+ }
3168
+ links() {
3169
+ const result = [];
3170
+ let address = 1n;
3171
+ for (const section of this.sections) {
3172
+ address = uint64(address + uint64(section.gap));
3173
+ for (const link of section.links) {
3174
+ result.push([
3175
+ address,
3176
+ link.map((r) => r instanceof External ? r : uint64(r))
3177
+ ]);
3178
+ address = uint64(address + 1n);
3179
+ }
3180
+ }
3181
+ return result;
3182
+ }
3183
+ validate(limits = new DecodeLimits) {
3184
+ requireValue(this.sections.length <= limits.maxLinks && this.links().length <= limits.maxLinks, "too many links or sections");
3185
+ let references = 0;
3186
+ for (const s of this.sections) {
3187
+ requireValue(WIDTHS.includes(s.width), "invalid width");
3188
+ new ArityRange(s.arity.min, s.arity.max);
3189
+ for (const link of s.links) {
3190
+ requireValue(s.arity.contains(link.length), "link outside arity");
3191
+ references += link.length;
3192
+ requireValue(references <= limits.maxReferences, "too many references");
3193
+ for (const r of link)
3194
+ requireValue(referenceWidth(r, this.externalReferences) <= s.width, "reference outside width");
3195
+ }
3196
+ }
3197
+ return this;
3198
+ }
3199
+ toBytes(limits = new DecodeLimits) {
3200
+ this.validate(limits);
3201
+ const s = this.sections[0];
3202
+ const compact = !s || this.sections.length === 1 && s.gap === 5n && s.arity.min === 2n && s.arity.max === 2n && s.links.length > 0;
3203
+ const out = [16 | Number(this.externalReferences)];
3204
+ if (compact) {
3205
+ out[0] |= WIDTHS.indexOf(s?.width ?? 1) << 2;
3206
+ leb(out, s?.links.length ?? 0);
3207
+ } else {
3208
+ out[0] |= 2;
3209
+ leb(out, this.sections.length);
3210
+ for (const s of this.sections) {
3211
+ leb(out, s.arity.min << 4n | BigInt(WIDTHS.indexOf(s.width) | (s.gap ? 4 : 0) | (s.arity.fixed ? 0 : 8)));
3212
+ if (s.gap)
3213
+ leb(out, s.gap);
3214
+ if (!s.arity.fixed)
3215
+ leb(out, s.arity.max === null ? 0n : s.arity.max - s.arity.min);
3216
+ leb(out, s.links.length);
3217
+ }
3218
+ }
3219
+ for (const s of this.sections)
3220
+ for (const link of s.links) {
3221
+ if (!s.arity.fixed)
3222
+ leb(out, BigInt(link.length) - s.arity.min);
3223
+ for (const r of link) {
3224
+ const bits = BigInt(s.width * 8);
3225
+ let raw = r instanceof External ? r.value === 0n ? 1n << bits - 1n : (1n << bits) - r.value : uint64(r);
3226
+ for (let i = 0;i < s.width; i++) {
3227
+ out.push(Number(raw & 255n));
3228
+ raw >>= 8n;
3229
+ }
3230
+ }
3231
+ }
3232
+ return Uint8Array.from(out);
3233
+ }
3234
+ static fromBytes(bytes, limits = new DecodeLimits) {
3235
+ const reader = new PacketReader(bytes), packet = reader.read(limits);
3236
+ requireValue(packet !== null, "empty input");
3237
+ requireValue(reader.offset === bytes.length, "trailing bytes");
3238
+ return packet;
3239
+ }
3240
+ static parseLinks(text) {
3241
+ return text.split(";").filter((s) => s.trim()).map((entry) => {
3242
+ const [a, refs] = entry.split(":");
3243
+ return [
3244
+ uint64(BigInt(a.trim())),
3245
+ refs.trim().split(/\s+/).filter(Boolean).map((r) => r.startsWith("#") ? new External(BigInt(r.slice(1))) : uint64(BigInt(r)))
3246
+ ];
3247
+ });
3248
+ }
3249
+ static pack(externalReferences, input, packedWidths = false) {
3250
+ const links = input.map(([a, r]) => [uint64(a), r]);
3251
+ let previous = 0n;
3252
+ const needs = links.map(([a, refs]) => {
3253
+ requireValue(a > previous && a < U64 && refs.length > 0, "invalid address order or empty link");
3254
+ previous = a;
3255
+ return refs.reduce((need, r) => Math.max(need, referenceWidth(r, externalReferences)), 1);
3256
+ });
3257
+ function layout(packed) {
3258
+ if (!links.length)
3259
+ return [];
3260
+ const widest = needs.reduce((a, b) => Math.max(a, b), 1), opens = [], bests = [];
3261
+ let costs = Array(8).fill(Infinity);
3262
+ const cheapest = (c) => c.reduce((best, v, s) => v < c[best] ? s : best, 0);
3263
+ for (let i = 0;i < links.length; i++) {
3264
+ const best = cheapest(costs), before = i ? costs[best] : 0, next = Array(8).fill(Infinity);
3265
+ let mask = 0;
3266
+ bests.push(best);
3267
+ for (let state = 0;state < 8; state++) {
3268
+ const width = WIDTHS[state >> 1], variable = state & 1;
3269
+ if (!packed && width !== widest || width < needs[i])
3270
+ continue;
3271
+ const opening = before + 2 + variable;
3272
+ const continuing = i && links[i - 1][0] + 1n === links[i][0] && (variable || links[i - 1][1].length === links[i][1].length) ? costs[state] : Infinity;
3273
+ if (continuing <= opening)
3274
+ next[state] = continuing + links[i][1].length * width + variable;
3275
+ else {
3276
+ next[state] = opening + links[i][1].length * width + variable;
3277
+ mask |= 1 << state;
3278
+ }
3279
+ }
3280
+ opens.push(mask);
3281
+ costs = next;
3282
+ }
3283
+ const result = [];
3284
+ let end = links.length, state = cheapest(costs);
3285
+ for (let i = links.length - 1;i >= 0; i--)
3286
+ if (opens[i] & 1 << state) {
3287
+ result.push([end - i, WIDTHS[state >> 1]]);
3288
+ end = i;
3289
+ state = bests[i];
3290
+ }
3291
+ return result.reverse();
3292
+ }
3293
+ function packet(plan) {
3294
+ const sections = [];
3295
+ let index = 0, address = 1n;
3296
+ for (const [count, width] of plan) {
3297
+ const members = links.slice(index, index + count), start = members[0][0];
3298
+ let min = Infinity, max = 0;
3299
+ for (const [, r] of members) {
3300
+ min = Math.min(min, r.length);
3301
+ max = Math.max(max, r.length);
3302
+ }
3303
+ sections.push(new Section(start - address, new ArityRange(min, max), width, members.map(([, r]) => r)));
3304
+ index += count;
3305
+ address = start + BigInt(count);
3306
+ }
3307
+ return new LinksPacket(externalReferences, sections);
3308
+ }
3309
+ const uniform = packet(layout(false));
3310
+ if (!packedWidths)
3311
+ return uniform;
3312
+ const packed = packet(layout(true));
3313
+ return packed.toBytes(DecodeLimits.unlimited()).length < uniform.toBytes(DecodeLimits.unlimited()).length ? packed : uniform;
3314
+ }
3315
+ }
3316
+
3317
+ class Encoder {
3318
+ constructor(options) {
3319
+ this.options = options;
3320
+ this.doublets = [];
3321
+ this.tuples = [];
3322
+ this.created = new Map;
3323
+ this.powers = [["i", 1n]];
3324
+ }
3325
+ link(items) {
3326
+ const key = items.map(([k, v]) => `${k}${v}`).join(",");
3327
+ if (!this.created.has(key)) {
3328
+ const doublet = items.length === 2 && items.every(([k]) => k !== "t"), target = doublet ? this.doublets : this.tuples;
3329
+ const node = [doublet ? "d" : "t", BigInt(target.length)];
3330
+ target.push(items);
3331
+ this.created.set(key, node);
3332
+ }
3333
+ return this.created.get(key);
3334
+ }
3335
+ chain(items) {
3336
+ let tail = ["i", 0n];
3337
+ for (let i = items.length - 1;i >= 0; i--)
3338
+ tail = this.link([items[i], tail]);
3339
+ return tail;
3340
+ }
3341
+ typed(marker, elements) {
3342
+ const items = [["i", BigInt(marker)], ...elements];
3343
+ return items.length !== 2 && this.options.arity.contains(items.length) ? this.link(items) : this.link([items[0], this.chain(elements)]);
3344
+ }
3345
+ list(elements) {
3346
+ if (!elements.length)
3347
+ return ["i", 0n];
3348
+ return elements.length === 2 || this.options.arity.contains(elements.length) ? this.link(elements) : this.typed(4, elements);
3349
+ }
3350
+ unary(value) {
3351
+ const powers = [];
3352
+ for (let bit = 63;bit >= 0; bit--)
3353
+ if (value & 1n << BigInt(bit)) {
3354
+ while (this.powers.length <= bit) {
3355
+ const p = this.powers.at(-1);
3356
+ this.powers.push(this.link([p, p]));
3357
+ }
3358
+ powers.push(this.powers[bit]);
3359
+ }
3360
+ let sum = powers.pop() ?? ["i", 0n];
3361
+ while (powers.length)
3362
+ sum = this.link([powers.pop(), sum]);
3363
+ return sum;
3364
+ }
3365
+ scalar(value) {
3366
+ return this.options.externalReferences && value < 1n << 63n ? ["e", value] : this.unary(value);
3367
+ }
3368
+ reference(text) {
3369
+ if (/^(0|[1-9][0-9]*)$/.test(text) && text.length <= 20 && BigInt(text) <= U64) {
3370
+ const value = BigInt(text);
3371
+ return this.options.externalReferences && value < 1n << 63n ? ["e", value] : this.link([["i", 2n], this.unary(value)]);
3372
+ }
3373
+ return this.typed(3, Array.from(text, (c) => this.scalar(BigInt(c.codePointAt(0)))));
3374
+ }
3375
+ encode(node) {
3376
+ if (node.id !== null && !node.values.length)
3377
+ return this.reference(node.id);
3378
+ if (node.id !== null) {
3379
+ const id = this.reference(node.id);
3380
+ return this.typed(5, [id, ...node.values.map((v) => this.encode(v))]);
3381
+ }
3382
+ return this.list(node.values.map((v) => this.encode(v)));
3383
+ }
3384
+ finish() {
3385
+ const resolve = ([k, v]) => k === "e" ? new External(v) : k === "i" ? v : 6n + v + (k === "t" ? BigInt(this.doublets.length) : 0n);
3386
+ return LinksPacket.pack(this.options.externalReferences, this.doublets.concat(this.tuples).map((items, i) => [6n + BigInt(i), items.map(resolve)]), this.options.packedWidths);
3387
+ }
3388
+ }
3389
+
3390
+ class Decoder {
3391
+ constructor(packet, limits) {
3392
+ packet.validate(limits);
3393
+ this.limits = limits;
3394
+ this.links = [];
3395
+ this.unary = [];
3396
+ this.nodes = limits.maxNodes;
3397
+ this.strings = limits.maxStringBytes;
3398
+ for (const [address, refs] of packet.links()) {
3399
+ requireValue(address === 6n + BigInt(this.links.length) && refs.every((r) => r instanceof External || r < address), "document addresses must be contiguous and references backward");
3400
+ const value = (r) => r instanceof External ? null : r <= 1n ? r : r >= 6n ? this.unary[Number(r - 6n)] : null;
3401
+ const a = refs.length === 2 ? value(refs[0]) : null, b = refs.length === 2 ? value(refs[1]) : null;
3402
+ this.unary.push(a !== null && b !== null && a + b <= U64 ? a + b : null);
3403
+ this.links.push(refs);
3404
+ }
3405
+ }
3406
+ chain(tail) {
3407
+ const elements = [];
3408
+ while (tail !== 0n) {
3409
+ requireValue(typeof tail === "bigint" && tail >= 6n, "broken chain");
3410
+ const items = this.links[Number(tail - 6n)];
3411
+ requireValue(items.length === 2 && elements.length < this.limits.maxNodes, "broken or excessive chain");
3412
+ elements.push(items[0]);
3413
+ tail = items[1];
3414
+ }
3415
+ return elements;
3416
+ }
3417
+ number(r) {
3418
+ const value = r instanceof External ? r.value : r <= 1n ? r : r >= 6n ? this.unary[Number(r - 6n)] : null;
3419
+ requireValue(value !== null, "expected unary number");
3420
+ return value;
3421
+ }
3422
+ text(text) {
3423
+ this.strings -= new globalThis.TextEncoder().encode(text).length;
3424
+ requireValue(this.strings >= 0, "string budget exceeded");
3425
+ return new Link(text);
3426
+ }
3427
+ decode(r, depth) {
3428
+ requireValue(depth < this.limits.maxDepth && --this.nodes >= 0, "node or depth budget exceeded");
3429
+ if (r instanceof External)
3430
+ return this.text(String(r.value));
3431
+ if (r === 0n)
3432
+ return new Link;
3433
+ requireValue(r >= 6n, "standalone marker");
3434
+ const items = this.links[Number(r - 6n)];
3435
+ let elements;
3436
+ if (typeof items[0] === "bigint" && items[0] >= 1n && items[0] <= 5n) {
3437
+ const marker = Number(items[0]);
3438
+ elements = items.length !== 2 || marker === 2 ? items.slice(1) : this.chain(items[1]);
3439
+ if (marker === 2) {
3440
+ requireValue(elements.length === 1, "number needs one value");
3441
+ return this.text(String(this.number(elements[0])));
3442
+ }
3443
+ if (marker === 3) {
3444
+ const chars = [];
3445
+ let byteCount = 0;
3446
+ for (const e of elements) {
3447
+ const p = this.number(e);
3448
+ requireValue(p <= 0x10ffffn && !(p >= 0xd800n && p <= 0xdfffn), "invalid Unicode scalar");
3449
+ const size = p <= 0x7fn ? 1 : p <= 0x7ffn ? 2 : p <= 0xffffn ? 3 : 4;
3450
+ requireValue(size <= this.strings - byteCount, "string budget exceeded");
3451
+ byteCount += size;
3452
+ chars.push(String.fromCodePoint(Number(p)));
3453
+ }
3454
+ return this.text(chars.join(""));
3455
+ }
3456
+ if (marker === 5) {
3457
+ requireValue(elements.length > 0, "identified needs id");
3458
+ const id = this.decode(elements[0], depth);
3459
+ requireValue(id.id !== null && !id.values.length, "id must be reference");
3460
+ return new Link(id.id, elements.slice(1).map((e) => this.decode(e, depth + 1)));
3461
+ }
3462
+ requireValue(marker === 4, "invalid typed marker");
3463
+ } else
3464
+ elements = items;
3465
+ return new Link(null, elements.map((e) => this.decode(e, depth + 1)));
3466
+ }
3467
+ document() {
3468
+ if (!this.links.length)
3469
+ return [];
3470
+ const root = this.links.at(-1);
3471
+ let elements;
3472
+ if (root.length === 2 && root[0] === 4n)
3473
+ elements = this.chain(root[1]);
3474
+ else {
3475
+ requireValue(!(typeof root[0] === "bigint" && root[0] >= 1n && root[0] <= 5n), "root must be list");
3476
+ elements = root;
3477
+ }
3478
+ return elements.map((e) => this.decode(e, 0));
3479
+ }
3480
+ }
3481
+ function canonical(node) {
3482
+ if (node.id === null && node.values.length === 1 && node.values[0].id !== null && !node.values[0].values.length)
3483
+ return node.values[0];
3484
+ return new Link(node.id, node.values.map(canonical));
3485
+ }
3486
+ function formatBinaryReference(text) {
3487
+ if (text && !text.startsWith("#") && !/[\p{White_Space}\u001c-\u001f\ufeff():"'`]/u.test(text))
3488
+ return text;
3489
+ let choice = null;
3490
+ for (const quote of ["'", '"', "`"])
3491
+ if (!text.startsWith(quote)) {
3492
+ let longest = 0, run = 0;
3493
+ for (const c of text) {
3494
+ run = c === quote ? run + 1 : 0;
3495
+ longest = Math.max(longest, run);
3496
+ }
3497
+ const count = longest + 1 | 1;
3498
+ if (choice === null || count < choice.count)
3499
+ choice = { quote, count };
3500
+ }
3501
+ const delimiter = choice.quote.repeat(choice.count);
3502
+ return delimiter + text + delimiter;
3503
+ }
3504
+ function formatBinaryDocument(document) {
3505
+ function nested(node, top = false) {
3506
+ if (node.id !== null && !node.values.length)
3507
+ return formatBinaryReference(node.id);
3508
+ const values = node.values.map((v) => nested(v)).join(" ");
3509
+ if (node.id !== null)
3510
+ return `(${formatBinaryReference(node.id)}: ${values})`;
3511
+ if (node.values.length === 1 && node.values[0].id !== null && !node.values[0].values.length)
3512
+ return `((${values}))`;
3513
+ return top && node.values.length >= 2 ? values : `(${values})`;
3514
+ }
3515
+ return document.map((n) => nested(n, true)).join(`
3516
+ `);
3517
+ }
3518
+
3519
+ class BinaryLinoCodec {
3520
+ constructor(options = new BinaryLinoOptions, limits = new DecodeLimits) {
3521
+ Object.assign(this, { options, limits });
3522
+ }
3523
+ encodePacket(document) {
3524
+ requireValue(this.options.arity.contains(2), "arity must contain 2");
3525
+ const pending = document.map((n) => [n, 0]);
3526
+ let nodes = 0, strings = 0;
3527
+ while (pending.length) {
3528
+ const [n, depth] = pending.pop();
3529
+ requireValue(n instanceof Link && depth < this.limits.maxDepth, "invalid model or excessive depth");
3530
+ nodes += 1 + Number(n.id !== null && n.values.length > 0);
3531
+ if (n.id !== null) {
3532
+ requireValue(typeof n.id === "string" && n.id.isWellFormed(), "invalid Unicode string");
3533
+ strings += new globalThis.TextEncoder().encode(n.id).length;
3534
+ }
3535
+ requireValue(nodes <= this.limits.maxNodes && strings <= this.limits.maxStringBytes, "model budget exceeded");
3536
+ for (const v of n.values)
3537
+ pending.push([v, depth + 1]);
3538
+ }
3539
+ const encoder = new Encoder(this.options);
3540
+ if (document.length)
3541
+ encoder.list(document.map((n) => encoder.encode(n)));
3542
+ return encoder.finish().validate(this.limits);
3543
+ }
3544
+ encode(document) {
3545
+ return this.encodePacket(document).toBytes(this.limits);
3546
+ }
3547
+ decodePacket(packet) {
3548
+ return new Decoder(packet, this.limits).document();
3549
+ }
3550
+ decode(bytes) {
3551
+ return this.decodePacket(LinksPacket.fromBytes(bytes, this.limits));
3552
+ }
3553
+ parseDocument(text, parser = new Parser) {
3554
+ return parser.parse(text).map(canonical);
3555
+ }
3556
+ formatDocument(document) {
3557
+ return formatBinaryDocument(document);
3558
+ }
3559
+ encodeText(text, parser) {
3560
+ return this.encode(this.parseDocument(text, parser));
3561
+ }
3562
+ decodeText(bytes) {
3563
+ return this.formatDocument(this.decode(bytes));
3564
+ }
3565
+ }
2983
3566
  export {
3567
+ ArityRange,
3568
+ BinaryLinoCodec,
3569
+ BinaryLinoOptions,
2984
3570
  DEFAULT_MAX_DEPTH,
3571
+ DecodeLimits,
3572
+ External,
2985
3573
  FormatConfig,
2986
3574
  FormatOptions,
2987
3575
  Link,
2988
3576
  LinksGroup,
3577
+ LinksPacket,
3578
+ PacketReader,
2989
3579
  ParseError,
2990
3580
  Parser,
3581
+ Section,
2991
3582
  StreamParseError,
2992
3583
  StreamParser,
3584
+ formatBinaryDocument,
3585
+ formatBinaryReference,
2993
3586
  formatLinks,
2994
3587
  stripComments
2995
3588
  };
package/index.d.ts CHANGED
@@ -470,3 +470,94 @@ export function formatLinks(
470
470
  links: Link[],
471
471
  lessParentheses?: boolean | FormatOptions | FormatConfig
472
472
  ): string;
473
+
474
+ /** Binary links notation v1: bigint preserves unsigned 64-bit references. */
475
+ export class External {
476
+ value: bigint;
477
+ constructor(value: bigint | number);
478
+ }
479
+ export class ArityRange {
480
+ min: bigint;
481
+ max: bigint | null;
482
+ constructor(min?: bigint | number, max?: bigint | number | null);
483
+ contains(length: bigint | number): boolean;
484
+ readonly fixed: boolean;
485
+ static parse(text: string): ArityRange;
486
+ }
487
+ export interface BinaryLimits {
488
+ maxLinks: number;
489
+ maxReferences: number;
490
+ maxNodes: number;
491
+ maxStringBytes: number;
492
+ maxDepth: number;
493
+ }
494
+ export class DecodeLimits implements BinaryLimits {
495
+ maxLinks: number;
496
+ maxReferences: number;
497
+ maxNodes: number;
498
+ maxStringBytes: number;
499
+ maxDepth: number;
500
+ constructor(values?: Partial<BinaryLimits>);
501
+ static unlimited(): DecodeLimits;
502
+ }
503
+ export class BinaryLinoOptions {
504
+ externalReferences: boolean;
505
+ arity: ArityRange;
506
+ packedWidths: boolean;
507
+ constructor(
508
+ externalReferences?: boolean,
509
+ arity?: ArityRange,
510
+ packedWidths?: boolean
511
+ );
512
+ static ofPacket(packet: LinksPacket): BinaryLinoOptions;
513
+ }
514
+ export type PacketReference = bigint | number | External;
515
+ export type AddressedLink = [bigint | number, PacketReference[]];
516
+ export class Section {
517
+ gap: bigint;
518
+ arity: ArityRange;
519
+ width: number;
520
+ links: PacketReference[][];
521
+ constructor(
522
+ gap: bigint | number,
523
+ arity: ArityRange,
524
+ width: number,
525
+ links: PacketReference[][]
526
+ );
527
+ }
528
+ export class PacketReader {
529
+ bytes: Uint8Array;
530
+ offset: number;
531
+ constructor(bytes: Uint8Array, offset?: number);
532
+ read(limits?: DecodeLimits): LinksPacket | null;
533
+ }
534
+ export class LinksPacket {
535
+ externalReferences: boolean;
536
+ sections: Section[];
537
+ constructor(externalReferences?: boolean, sections?: Section[]);
538
+ links(): [bigint, (bigint | External)[]][];
539
+ validate(limits?: DecodeLimits): this;
540
+ toBytes(limits?: DecodeLimits): Uint8Array;
541
+ static fromBytes(bytes: Uint8Array, limits?: DecodeLimits): LinksPacket;
542
+ static parseLinks(text: string): [bigint, (bigint | External)[]][];
543
+ static pack(
544
+ externalReferences: boolean,
545
+ links: AddressedLink[],
546
+ packedWidths?: boolean
547
+ ): LinksPacket;
548
+ }
549
+ export class BinaryLinoCodec {
550
+ options: BinaryLinoOptions;
551
+ limits: DecodeLimits;
552
+ constructor(options?: BinaryLinoOptions, limits?: DecodeLimits);
553
+ encodePacket(document: Link[]): LinksPacket;
554
+ encode(document: Link[]): Uint8Array;
555
+ decodePacket(packet: LinksPacket): Link[];
556
+ decode(bytes: Uint8Array): Link[];
557
+ parseDocument(text: string, parser?: Parser): Link[];
558
+ formatDocument(document: Link[]): string;
559
+ encodeText(text: string, parser?: Parser): Uint8Array;
560
+ decodeText(bytes: Uint8Array): string;
561
+ }
562
+ export function formatBinaryDocument(document: Link[]): string;
563
+ export function formatBinaryReference(reference: string): string;