@ferrybig/cgeodata-reader 0.0.0-stage → 0.1.1

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 (78) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +313 -2
  3. package/binary-format.md +2620 -0
  4. package/dist/cache.d.ts +24 -0
  5. package/dist/cache.js +60 -0
  6. package/dist/cellBounds.d.ts +19 -0
  7. package/dist/cellBounds.js +36 -0
  8. package/dist/chunk.d.ts +35 -0
  9. package/dist/chunk.js +277 -0
  10. package/dist/chunkDecoder.d.ts +39 -0
  11. package/dist/chunkDecoder.js +116 -0
  12. package/dist/chunkDecoderWasm.d.ts +2 -0
  13. package/dist/chunkDecoderWasm.js +4 -0
  14. package/dist/cli/buildLocateIndex.d.ts +2 -0
  15. package/dist/cli/buildLocateIndex.js +46 -0
  16. package/dist/cli/buildSearchIndex.d.ts +2 -0
  17. package/dist/cli/buildSearchIndex.js +90 -0
  18. package/dist/cli/chunk.d.ts +2 -0
  19. package/dist/cli/chunk.js +67 -0
  20. package/dist/cli/info.d.ts +2 -0
  21. package/dist/cli/info.js +87 -0
  22. package/dist/cli/list.d.ts +2 -0
  23. package/dist/cli/list.js +71 -0
  24. package/dist/cli/locate.d.ts +2 -0
  25. package/dist/cli/locate.js +64 -0
  26. package/dist/cli/lookup.d.ts +2 -0
  27. package/dist/cli/lookup.js +128 -0
  28. package/dist/cli/search.d.ts +2 -0
  29. package/dist/cli/search.js +119 -0
  30. package/dist/cli/shared.d.ts +50 -0
  31. package/dist/cli/shared.js +140 -0
  32. package/dist/cli/show.d.ts +2 -0
  33. package/dist/cli/show.js +38 -0
  34. package/dist/cli.d.ts +2 -0
  35. package/dist/cli.js +107 -0
  36. package/dist/container.d.ts +17 -0
  37. package/dist/container.js +68 -0
  38. package/dist/directory.d.ts +21 -0
  39. package/dist/directory.js +31 -0
  40. package/dist/errors.d.ts +52 -0
  41. package/dist/errors.js +56 -0
  42. package/dist/format.d.ts +22 -0
  43. package/dist/format.js +23 -0
  44. package/dist/header.d.ts +30 -0
  45. package/dist/header.js +69 -0
  46. package/dist/index.d.ts +20 -0
  47. package/dist/index.js +18 -0
  48. package/dist/indexes.d.ts +11 -0
  49. package/dist/indexes.js +12 -0
  50. package/dist/inflate.d.ts +21 -0
  51. package/dist/inflate.js +61 -0
  52. package/dist/locateIndex.d.ts +142 -0
  53. package/dist/locateIndex.js +561 -0
  54. package/dist/morton.d.ts +28 -0
  55. package/dist/morton.js +51 -0
  56. package/dist/node.d.ts +65 -0
  57. package/dist/node.js +137 -0
  58. package/dist/placeTable.d.ts +69 -0
  59. package/dist/placeTable.js +177 -0
  60. package/dist/rangeChunk.d.ts +46 -0
  61. package/dist/rangeChunk.js +401 -0
  62. package/dist/rangeDecoder.d.ts +17 -0
  63. package/dist/rangeDecoder.js +129 -0
  64. package/dist/rangeDecoderWasm.d.ts +2 -0
  65. package/dist/rangeDecoderWasm.js +4 -0
  66. package/dist/reader.d.ts +534 -0
  67. package/dist/reader.js +1132 -0
  68. package/dist/relevance.d.ts +184 -0
  69. package/dist/relevance.js +266 -0
  70. package/dist/searchIndex.d.ts +148 -0
  71. package/dist/searchIndex.js +786 -0
  72. package/dist/source.d.ts +49 -0
  73. package/dist/source.js +50 -0
  74. package/dist/turns.d.ts +19 -0
  75. package/dist/turns.js +40 -0
  76. package/dist/varint.d.ts +52 -0
  77. package/dist/varint.js +137 -0
  78. package/package.json +76 -5
@@ -0,0 +1,401 @@
1
+ import { CGeoDataFormatError } from "./errors.js";
2
+ import { decodeRangeCodedWasm } from "./rangeDecoder.js";
3
+ /** Probabilities are 12-bit: the chance of a 0, out of 4096. */
4
+ const PROB_BITS = 12;
5
+ const PROB_INIT = 2048;
6
+ /** How fast a probability adapts: it moves 1/16 of the way each time. */
7
+ const ADAPT_SHIFT = 4;
8
+ const TOP = 2 ** 24;
9
+ /** Context table sizes (Section 8.7.3). */
10
+ export const SPLIT_CONTEXTS = 16 * 16;
11
+ export const VALUE_CONTEXTS = 17 * 8 * 4;
12
+ export const RANK_LENGTH_CONTEXTS = 4 * 32;
13
+ export const RANK_BIT_CONTEXTS = 32 * 32;
14
+ /** A version 2 chunk's palette_count is below this, so a palette index
15
+ * and a depth fit in one 32-bit integer together. */
16
+ export const MAX_PALETTE_COUNT = 2 ** 26;
17
+ /** At most this many records per byte of coded stream: every leaf takes at
18
+ * least one binary decision, which costs at least log2(4096 / 4081) bits,
19
+ * so 2048 records per byte can't be reached by any stream. Checked before
20
+ * allocating for `record_count`. */
21
+ export const MAX_RECORDS_PER_BYTE = 2048;
22
+ /** Section 8.7.2's neighbour category: 0 when there is no neighbour, else
23
+ * how much deeper than `d` its record is (1: not deeper, 2: one level, 3:
24
+ * more). `tag` is the neighbour's `index << 5 | depth`, or -1. */
25
+ function category(tag, d) {
26
+ if (tag < 0)
27
+ return 0;
28
+ const e = tag & 31;
29
+ return e <= d ? 1 : e === d + 1 ? 2 : 3;
30
+ }
31
+ class RangeDecoder {
32
+ range = 0xffffffff;
33
+ code = 0;
34
+ pos;
35
+ buf;
36
+ end;
37
+ constructor(buf, start, end) {
38
+ this.buf = buf;
39
+ this.end = end;
40
+ if (end - start < 5) {
41
+ throw new CGeoDataFormatError("chunk range-coded stream is shorter than 5 bytes");
42
+ }
43
+ if (buf[start] !== 0) {
44
+ throw new CGeoDataFormatError("chunk range-coded stream does not start with a 0 byte");
45
+ }
46
+ let code = 0;
47
+ for (let i = 1; i <= 4; i++)
48
+ code = code * 256 + buf[start + i];
49
+ if (code === 0xffffffff) {
50
+ throw new CGeoDataFormatError("chunk range-coded stream starts with code 0xffffffff");
51
+ }
52
+ this.code = code;
53
+ this.pos = start + 5;
54
+ }
55
+ bit(probs, i) {
56
+ const p = probs[i];
57
+ const bound = (this.range >>> PROB_BITS) * p;
58
+ let b;
59
+ if (this.code < bound) {
60
+ this.range = bound;
61
+ probs[i] = p + (((1 << PROB_BITS) - p) >> ADAPT_SHIFT);
62
+ b = 0;
63
+ }
64
+ else {
65
+ this.code -= bound;
66
+ this.range -= bound;
67
+ probs[i] = p - (p >> ADAPT_SHIFT);
68
+ b = 1;
69
+ }
70
+ while (this.range < TOP) {
71
+ if (this.pos >= this.end) {
72
+ throw new CGeoDataFormatError("chunk range-coded stream runs past end of block");
73
+ }
74
+ this.range *= 256;
75
+ this.code = this.code * 256 + this.buf[this.pos++];
76
+ }
77
+ return b;
78
+ }
79
+ }
80
+ /** The narrowest typed array for palette indices up to `paletteCount`
81
+ * (Section 8.3's S, with 3 bytes rounded up to 4). */
82
+ export function paletteIndexArray(paletteCount, n) {
83
+ if (paletteCount <= 255)
84
+ return new Uint8Array(n);
85
+ if (paletteCount <= 65535)
86
+ return new Uint16Array(n);
87
+ return new Uint32Array(n);
88
+ }
89
+ /**
90
+ * Decodes the records of a version 2 chunk from `buf[offset..]`, the
91
+ * range-coded stream that follows `record_count` and runs to the end of
92
+ * the block. Throws a `CGeoDataFormatError` for anything Section 8.7
93
+ * rejects.
94
+ */
95
+ export function decodeRangeCodedRecords(buf, offset, paletteCount, recordCount, maxDepth) {
96
+ if (paletteCount >= MAX_PALETTE_COUNT) {
97
+ throw new CGeoDataFormatError(`chunk palette_count ${paletteCount} is 2^26 or more, past what a version 2 chunk allows`);
98
+ }
99
+ checkRecordCount(recordCount, maxDepth, buf.length - offset);
100
+ const fast = decodeRangeCodedWasm(buf, offset, paletteCount, recordCount, maxDepth);
101
+ if (fast)
102
+ return fast;
103
+ const rc = new RangeDecoder(buf, offset, buf.length);
104
+ const D = maxDepth;
105
+ const N = 2 ** D;
106
+ const split = new Uint16Array(SPLIT_CONTEXTS).fill(PROB_INIT);
107
+ const eqW = new Uint16Array(VALUE_CONTEXTS).fill(PROB_INIT);
108
+ const eqS = new Uint16Array(VALUE_CONTEXTS).fill(PROB_INIT);
109
+ const rankLength = new Uint16Array(RANK_LENGTH_CONTEXTS).fill(PROB_INIT);
110
+ const rankBits = new Uint16Array(RANK_BIT_CONTEXTS).fill(PROB_INIT);
111
+ // row[y] / col[x]: the last record decoded in row y / column x, as
112
+ // `index << 5 | depth`; in Morton order that is the cell west of /
113
+ // south of the next one to be decoded there.
114
+ const row = new Int32Array(N).fill(-1);
115
+ const col = new Int32Array(N).fill(-1);
116
+ // Move-to-front list of palette indices 0..P.
117
+ const mtf = new Int32Array(paletteCount + 1);
118
+ for (let k = 0; k <= paletteCount; k++)
119
+ mtf[k] = k;
120
+ const codes = new Uint32Array(recordCount);
121
+ const depths = new Uint8Array(recordCount);
122
+ const paletteIndices = paletteIndexArray(paletteCount, recordCount);
123
+ // Pending nodes, popped in pre-order: at most 3 per level plus one.
124
+ const stackD = new Int32Array(4 * (D + 1));
125
+ const stackX = new Int32Array(4 * (D + 1));
126
+ const stackY = new Int32Array(4 * (D + 1));
127
+ let sp = 1;
128
+ let j = 0;
129
+ let prev = -1;
130
+ let code = 0;
131
+ while (j < recordCount) {
132
+ if (sp === 0) {
133
+ throw new CGeoDataFormatError(`chunk record_count ${recordCount} is more than the ${j} records its quadtree has`);
134
+ }
135
+ sp--;
136
+ const d = stackD[sp];
137
+ const x = stackX[sp];
138
+ const y = stackY[sp];
139
+ const w = x > 0 ? row[y] : -1;
140
+ const s = y > 0 ? col[x] : -1;
141
+ const wc = category(w, d);
142
+ if (d < D && rc.bit(split, d * 16 + wc * 4 + category(s, d)) === 1) {
143
+ const h = 2 ** (D - d - 1);
144
+ const d1 = d + 1;
145
+ // Pushed in reverse, so the south-west child pops first.
146
+ stackD[sp] = d1;
147
+ stackX[sp] = x + h;
148
+ stackY[sp] = y + h;
149
+ sp++;
150
+ stackD[sp] = d1;
151
+ stackX[sp] = x;
152
+ stackY[sp] = y + h;
153
+ sp++;
154
+ stackD[sp] = d1;
155
+ stackX[sp] = x + h;
156
+ stackY[sp] = y;
157
+ sp++;
158
+ stackD[sp] = d1;
159
+ stackX[sp] = x;
160
+ stackY[sp] = y;
161
+ sp++;
162
+ continue;
163
+ }
164
+ const wv = w < 0 ? -1 : w >>> 5;
165
+ const sv = s < 0 ? -1 : s >>> 5;
166
+ const c = (d * 8 +
167
+ (wv === sv ? 4 : 0) +
168
+ (prev === wv ? 2 : 0) +
169
+ (prev === sv ? 1 : 0)) *
170
+ 4 +
171
+ wc;
172
+ let v = -1;
173
+ let p = 0;
174
+ if (wv >= 0 && rc.bit(eqW, c) === 1)
175
+ v = wv;
176
+ else if (sv >= 0 && sv !== wv && rc.bit(eqS, c) === 1)
177
+ v = sv;
178
+ if (v < 0) {
179
+ const cc = (wv >= 0 ? 32 : 0) + (sv >= 0 ? 64 : 0);
180
+ let nb = 0;
181
+ while (rc.bit(rankLength, cc + nb) === 1) {
182
+ if (++nb === 32) {
183
+ throw new CGeoDataFormatError(`chunk record ${j}'s palette rank is 2^32 or more`);
184
+ }
185
+ }
186
+ let value = 1;
187
+ for (let k = nb - 1; k >= 0; k--) {
188
+ value = value * 2 + rc.bit(rankBits, nb * 32 + k);
189
+ }
190
+ let rank = value - 1;
191
+ for (;; p++) {
192
+ if (p > paletteCount) {
193
+ throw new CGeoDataFormatError(`chunk record ${j}'s palette rank ${value - 1} is past the palette`);
194
+ }
195
+ const t = mtf[p];
196
+ if (t === wv || t === sv)
197
+ continue;
198
+ if (rank === 0)
199
+ break;
200
+ rank--;
201
+ }
202
+ v = mtf[p];
203
+ }
204
+ else {
205
+ while (mtf[p] !== v)
206
+ p++;
207
+ }
208
+ for (let k = p; k > 0; k--)
209
+ mtf[k] = mtf[k - 1];
210
+ mtf[0] = v;
211
+ const size = 2 ** (D - d);
212
+ const tag = v * 32 + d;
213
+ row.fill(tag, y, y + size);
214
+ col.fill(tag, x, x + size);
215
+ codes[j] = code;
216
+ depths[j] = d;
217
+ paletteIndices[j] = v;
218
+ code += size * size;
219
+ prev = v;
220
+ j++;
221
+ }
222
+ if (rc.pos !== rc.end) {
223
+ throw new CGeoDataFormatError(`chunk block has ${rc.end - rc.pos} trailing bytes after its range-coded stream`);
224
+ }
225
+ return { codes, depths, paletteIndices };
226
+ }
227
+ /** Section 8.7.5's bounds on `record_count`, checked before allocating
228
+ * for it: at most one record per cell, and at most `MAX_RECORDS_PER_BYTE`
229
+ * per byte of stream. */
230
+ export function checkRecordCount(recordCount, maxDepth, streamBytes) {
231
+ if (recordCount > 4 ** maxDepth) {
232
+ throw new CGeoDataFormatError(`chunk record_count ${recordCount} is more than the 4^${maxDepth} cells of a chunk`);
233
+ }
234
+ if (recordCount > MAX_RECORDS_PER_BYTE * Math.max(streamBytes, 0)) {
235
+ throw new CGeoDataFormatError(`chunk record_count ${recordCount} is more than a ${streamBytes}-byte stream can code`);
236
+ }
237
+ }
238
+ class RangeEncoder {
239
+ low = 0;
240
+ range = 0xffffffff;
241
+ cache = 0;
242
+ cacheSize = 1;
243
+ out = [];
244
+ bit(probs, i, b) {
245
+ const p = probs[i];
246
+ const bound = (this.range >>> PROB_BITS) * p;
247
+ if (b === 0) {
248
+ this.range = bound;
249
+ probs[i] = p + (((1 << PROB_BITS) - p) >> ADAPT_SHIFT);
250
+ }
251
+ else {
252
+ this.low += bound;
253
+ this.range -= bound;
254
+ probs[i] = p - (p >> ADAPT_SHIFT);
255
+ }
256
+ while (this.range < TOP) {
257
+ this.range *= 256;
258
+ this.shiftLow();
259
+ }
260
+ }
261
+ /** Emits the top byte of `low`, holding back runs of 0xff until a
262
+ * carry out of them is ruled out (LZMA's encoder). */
263
+ shiftLow() {
264
+ if (this.low < 0xff000000 || this.low >= 0x100000000) {
265
+ const carry = this.low >= 0x100000000 ? 1 : 0;
266
+ let c = this.cache;
267
+ do {
268
+ this.out.push((c + carry) & 0xff);
269
+ c = 0xff;
270
+ } while (--this.cacheSize !== 0);
271
+ this.cache = Math.floor(this.low / 0x1000000) & 0xff;
272
+ }
273
+ this.cacheSize++;
274
+ this.low = (this.low % 0x1000000) * 256;
275
+ }
276
+ finish() {
277
+ for (let i = 0; i < 5; i++)
278
+ this.shiftLow();
279
+ return Uint8Array.from(this.out);
280
+ }
281
+ }
282
+ /**
283
+ * Encodes records that tile a chunk from code 0 (Section 8.4) as Section
284
+ * 8.7's range-coded stream: `depths[j]` and `paletteIndices[j]` (0..P)
285
+ * per record, in Morton order. The inverse of `decodeRangeCodedRecords`.
286
+ * Throws a RangeError for records that don't tile the chunk.
287
+ */
288
+ export function encodeRangeCodedRecords(depths, paletteIndices, paletteCount, maxDepth) {
289
+ if (paletteCount >= MAX_PALETTE_COUNT) {
290
+ throw new RangeError(`palette_count ${paletteCount} is 2^26 or more, past what a version 2 chunk allows`);
291
+ }
292
+ const rc = new RangeEncoder();
293
+ const D = maxDepth;
294
+ const N = 2 ** D;
295
+ const recordCount = depths.length;
296
+ const split = new Uint16Array(SPLIT_CONTEXTS).fill(PROB_INIT);
297
+ const eqW = new Uint16Array(VALUE_CONTEXTS).fill(PROB_INIT);
298
+ const eqS = new Uint16Array(VALUE_CONTEXTS).fill(PROB_INIT);
299
+ const rankLength = new Uint16Array(RANK_LENGTH_CONTEXTS).fill(PROB_INIT);
300
+ const rankBits = new Uint16Array(RANK_BIT_CONTEXTS).fill(PROB_INIT);
301
+ const row = new Int32Array(N).fill(-1);
302
+ const col = new Int32Array(N).fill(-1);
303
+ const mtf = new Int32Array(paletteCount + 1);
304
+ for (let k = 0; k <= paletteCount; k++)
305
+ mtf[k] = k;
306
+ const stackD = new Int32Array(4 * (D + 1));
307
+ const stackX = new Int32Array(4 * (D + 1));
308
+ const stackY = new Int32Array(4 * (D + 1));
309
+ let sp = 1;
310
+ let j = 0;
311
+ let prev = -1;
312
+ while (j < recordCount) {
313
+ if (sp === 0)
314
+ throw new RangeError("records run past the end of the chunk");
315
+ sp--;
316
+ const d = stackD[sp];
317
+ const x = stackX[sp];
318
+ const y = stackY[sp];
319
+ const depth = depths[j];
320
+ if (depth < d || depth > D) {
321
+ throw new RangeError(`record ${j} at depth ${depth} doesn't tile the chunk`);
322
+ }
323
+ const w = x > 0 ? row[y] : -1;
324
+ const s = y > 0 ? col[x] : -1;
325
+ const wc = category(w, d);
326
+ if (d < D) {
327
+ const splits = depth > d ? 1 : 0;
328
+ rc.bit(split, d * 16 + wc * 4 + category(s, d), splits);
329
+ if (splits) {
330
+ const h = 2 ** (D - d - 1);
331
+ const d1 = d + 1;
332
+ stackD[sp] = d1;
333
+ stackX[sp] = x + h;
334
+ stackY[sp] = y + h;
335
+ sp++;
336
+ stackD[sp] = d1;
337
+ stackX[sp] = x;
338
+ stackY[sp] = y + h;
339
+ sp++;
340
+ stackD[sp] = d1;
341
+ stackX[sp] = x + h;
342
+ stackY[sp] = y;
343
+ sp++;
344
+ stackD[sp] = d1;
345
+ stackX[sp] = x;
346
+ stackY[sp] = y;
347
+ sp++;
348
+ continue;
349
+ }
350
+ }
351
+ const v = paletteIndices[j];
352
+ if (!(v >= 0 && v <= paletteCount)) {
353
+ throw new RangeError(`record ${j}'s palette index ${v} is past P (${paletteCount})`);
354
+ }
355
+ const wv = w < 0 ? -1 : w >>> 5;
356
+ const sv = s < 0 ? -1 : s >>> 5;
357
+ const c = (d * 8 +
358
+ (wv === sv ? 4 : 0) +
359
+ (prev === wv ? 2 : 0) +
360
+ (prev === sv ? 1 : 0)) *
361
+ 4 +
362
+ wc;
363
+ let coded = false;
364
+ if (wv >= 0) {
365
+ rc.bit(eqW, c, v === wv ? 1 : 0);
366
+ coded = v === wv;
367
+ }
368
+ if (!coded && sv >= 0 && sv !== wv) {
369
+ rc.bit(eqS, c, v === sv ? 1 : 0);
370
+ coded = v === sv;
371
+ }
372
+ let p = 0;
373
+ while (mtf[p] !== v)
374
+ p++;
375
+ if (!coded) {
376
+ let rank = 0;
377
+ for (let k = 0; k < p; k++)
378
+ if (mtf[k] !== wv && mtf[k] !== sv)
379
+ rank++;
380
+ const value = rank + 1;
381
+ const nb = 31 - Math.clz32(value);
382
+ const cc = (wv >= 0 ? 32 : 0) + (sv >= 0 ? 64 : 0);
383
+ for (let k = 0; k < nb; k++)
384
+ rc.bit(rankLength, cc + k, 1);
385
+ rc.bit(rankLength, cc + nb, 0);
386
+ for (let k = nb - 1; k >= 0; k--)
387
+ rc.bit(rankBits, nb * 32 + k, (value >>> k) & 1);
388
+ }
389
+ for (let k = p; k > 0; k--)
390
+ mtf[k] = mtf[k - 1];
391
+ mtf[0] = v;
392
+ const size = 2 ** (D - d);
393
+ const tag = v * 32 + d;
394
+ row.fill(tag, y, y + size);
395
+ col.fill(tag, x, x + size);
396
+ prev = v;
397
+ j++;
398
+ }
399
+ return rc.finish();
400
+ }
401
+ //# sourceMappingURL=rangeChunk.js.map
@@ -0,0 +1,17 @@
1
+ import type { RangeCodedRecords } from "./rangeChunk.ts";
2
+ /** Turns the fast path off (or back on), for tests and benchmarks that
3
+ * compare it against the JavaScript decoder. */
4
+ export declare function useWasmRangeDecoder(on: boolean): void;
5
+ /** How many bytes of WebAssembly memory the fast path holds right now:
6
+ * 0 while idle. */
7
+ export declare function wasmRangeDecoderBytes(): number;
8
+ /** Whether this platform runs the fast path. */
9
+ export declare function wasmRangeDecoderAvailable(): boolean;
10
+ /**
11
+ * The records of the range-coded stream `buf[offset..]` (Section 8.7), or
12
+ * undefined when the fast path declined it or isn't available, and the
13
+ * caller should decode it in JavaScript. The caller has checked Section
14
+ * 8.7.5's bounds on `recordCount`, that `maxDepth` is at most 16 and
15
+ * `paletteCount` below 2^26.
16
+ */
17
+ export declare function decodeRangeCodedWasm(buf: Uint8Array, offset: number, paletteCount: number, recordCount: number, maxDepth: number): RangeCodedRecords | undefined;
@@ -0,0 +1,129 @@
1
+ import { RANGE_DECODER_WASM } from "./rangeDecoderWasm.js";
2
+ /**
3
+ * `decodeRangeCodedRecords`'s fast path (wasm/rangeDecoder.wat), about
4
+ * twice as fast as the JavaScript decoder in ./rangeChunk.ts. It only
5
+ * accepts or declines a stream: a declined one -- malformed, or valid
6
+ * but out of its reach -- is decoded again in JavaScript, which throws
7
+ * the error or returns the records. Platforms without WebAssembly (or
8
+ * that forbid compiling it, such as a page whose CSP lacks
9
+ * `'wasm-unsafe-eval'`) always take the JavaScript path.
10
+ *
11
+ * Memory: one instance, grown to the largest chunk decoded so far and
12
+ * dropped once no decode has used it for `IDLE_RELEASE_MS`, as
13
+ * ./chunkDecoder.ts does for version 0 chunks.
14
+ */
15
+ /** How long the instance outlives its last decode. */
16
+ const IDLE_RELEASE_MS = 1000;
17
+ const PAGE_BYTES = 65536;
18
+ /** Probabilities and node stack (wasm/rangeDecoder.wat's `scratch`). */
19
+ const FIXED_SCRATCH_BYTES = 4992 + 816;
20
+ /** The compiled module; null once compiling failed. */
21
+ let compiled;
22
+ let instance;
23
+ let releaseTimer;
24
+ let enabled = true;
25
+ /** Turns the fast path off (or back on), for tests and benchmarks that
26
+ * compare it against the JavaScript decoder. */
27
+ export function useWasmRangeDecoder(on) {
28
+ enabled = on;
29
+ if (!on)
30
+ release();
31
+ }
32
+ /** How many bytes of WebAssembly memory the fast path holds right now:
33
+ * 0 while idle. */
34
+ export function wasmRangeDecoderBytes() {
35
+ return instance?.memory.buffer.byteLength ?? 0;
36
+ }
37
+ /** Whether this platform runs the fast path. */
38
+ export function wasmRangeDecoderAvailable() {
39
+ return enabled && getInstance() !== undefined;
40
+ }
41
+ function getInstance() {
42
+ if (instance)
43
+ return instance;
44
+ if (compiled === undefined) {
45
+ try {
46
+ // Throws a ReferenceError where there's no WebAssembly at all.
47
+ compiled = new WebAssembly.Module(Uint8Array.from(atob(RANGE_DECODER_WASM), (c) => c.charCodeAt(0)));
48
+ }
49
+ catch {
50
+ compiled = null;
51
+ }
52
+ }
53
+ if (compiled === null)
54
+ return undefined;
55
+ const exports = new WebAssembly.Instance(compiled).exports;
56
+ instance = {
57
+ memory: exports.memory,
58
+ decode: exports.decode,
59
+ };
60
+ return instance;
61
+ }
62
+ function release() {
63
+ instance = undefined;
64
+ if (releaseTimer !== undefined)
65
+ clearTimeout(releaseTimer);
66
+ releaseTimer = undefined;
67
+ }
68
+ function releaseWhenIdle() {
69
+ if (releaseTimer !== undefined)
70
+ clearTimeout(releaseTimer);
71
+ releaseTimer = setTimeout(release, IDLE_RELEASE_MS);
72
+ // Node.js: don't keep the process alive just to free this.
73
+ releaseTimer.unref?.();
74
+ }
75
+ /**
76
+ * The records of the range-coded stream `buf[offset..]` (Section 8.7), or
77
+ * undefined when the fast path declined it or isn't available, and the
78
+ * caller should decode it in JavaScript. The caller has checked Section
79
+ * 8.7.5's bounds on `recordCount`, that `maxDepth` is at most 16 and
80
+ * `paletteCount` below 2^26.
81
+ */
82
+ export function decodeRangeCodedWasm(buf, offset, paletteCount, recordCount, maxDepth) {
83
+ if (!enabled)
84
+ return undefined;
85
+ const decoder = getInstance();
86
+ if (!decoder)
87
+ return undefined;
88
+ const width = paletteCount <= 255 ? 1 : paletteCount <= 65535 ? 2 : 4;
89
+ const streamBytes = buf.length - offset;
90
+ // Slack past the stream: a stream that runs past its end reads a few
91
+ // bytes there before the fast path declines it.
92
+ const out = Math.ceil((streamBytes + 64) / 16) * 16;
93
+ const indices = out + 4 * recordCount;
94
+ const depthsAt = indices + width * recordCount;
95
+ const scratch = Math.ceil((depthsAt + recordCount) / 16) * 16;
96
+ const needed = scratch + FIXED_SCRATCH_BYTES + 4 * (2 * 2 ** maxDepth + paletteCount + 1);
97
+ const { memory } = decoder;
98
+ if (memory.buffer.byteLength < needed) {
99
+ try {
100
+ memory.grow(Math.ceil((needed - memory.buffer.byteLength) / PAGE_BYTES));
101
+ }
102
+ catch {
103
+ return undefined; // out of memory: leave it to the JavaScript path
104
+ }
105
+ }
106
+ new Uint8Array(memory.buffer, 0, streamBytes).set(buf.subarray(offset));
107
+ let ok;
108
+ try {
109
+ ok = decoder.decode(streamBytes, recordCount, paletteCount, maxDepth, width, out, scratch);
110
+ }
111
+ catch {
112
+ // A stream that runs far past its end can read past the memory.
113
+ ok = 0;
114
+ }
115
+ releaseWhenIdle();
116
+ if (!ok)
117
+ return undefined;
118
+ const { buffer } = memory;
119
+ return {
120
+ codes: new Uint32Array(buffer, out, recordCount).slice(),
121
+ depths: new Uint8Array(buffer, depthsAt, recordCount).slice(),
122
+ paletteIndices: width === 1
123
+ ? new Uint8Array(buffer, indices, recordCount).slice()
124
+ : width === 2
125
+ ? new Uint16Array(buffer, indices, recordCount).slice()
126
+ : new Uint32Array(buffer, indices, recordCount).slice(),
127
+ };
128
+ }
129
+ //# sourceMappingURL=rangeDecoder.js.map
@@ -0,0 +1,2 @@
1
+ /** rangeDecoder.wat's compiled module (1381 bytes), base64. */
2
+ export declare const RANGE_DECODER_WASM = "AGFzbQEAAAABFwNgAX8Bf2ACf38Bf2AHf39/f39/fwF/AwQDAAECBQMBAAEGFQR/AUEAC38BQQALfwFBAAt/AUEACwcTAgZtZW1vcnkCAAZkZWNvZGUAAgqKCgOBAQEDfyAALwEAIQEjAEEMdiABbCECIwEgAkkEQCACJAAgACABQYAgIAFrQQR2ajsBAAUjASACayQBIwAgAmskACAAIAEgAUEEdms7AQBBASEDCwJAA0AjAEGAgIAITw0BIwFBCHQjAi0AAHIkASMCQQFqJAIjAEEIdCQADAALCyADCzEBAX8gAEEASARAQQAPCyAAQR9xIQIgAiABTQRAQQEPCyACIAFBAWpGBEBBAg8LQQML0ggBJH9BAC0AAARAQQAPC0EBLQAAQRh0QQItAABBEHRyQQMtAABBCHRBBC0AAHJyJAFBfyQAQQUkAiAAJAMgBiEHIAZBgARqIQggBkHADGohCSAGQYAVaiEKIAZBgBdqIQsgBkGAJ2ohDCAMQbAGaiENIA1BBCADdGohDiAOQQQgA3RqIQ8gBSABQQJ0aiERIBEgASAEbGohEEEAIRIDQCAGIBJqQYAQOwEAIBJBAmohEiASQYAnSQ0ACyANQf8BQQggA3T8CwBBACESA0AgDyASQQJ0aiASNgIAIBJBAWohEiASIAJNDQALIAxCADcDACAMQQA2AghBASETQX8hFQJAA0AgFCABTw0BIBNFBEBBAA8LIBNBAWshEyAMIBNBDGxqIRsgGygCACEXIBsoAgQhGCAbKAIIIRkgGAR/IA0gGUECdGooAgAFQX8LIRwgGQR/IA4gGEECdGooAgAFQX8LIR0gHCAXEAEhHiAXIANJBEAgByAXQQR0IB5BAnRqIB0gFxABakEBdGoQAARAQQEgAyAXa0EBa3QhGiAXQQFqIRcgGyAXNgIAIBsgGCAaajYCBCAbIBkgGmo2AgggGyAXNgIMIBsgGDYCECAbIBkgGmo2AhQgGyAXNgIYIBsgGCAaajYCHCAbIBk2AiAgGyAXNgIkIBsgGDYCKCAbIBk2AiwgE0EEaiETDAILC0F/IBxBBXYgHEEASBshH0F/IB1BBXYgHUEASBshICAXQQN0IB8gIEZBAnRqIBUgH0ZBAXRqIBUgIEZqQQJ0IB5qISFBfyEiIB9BAE4EQCAIICFBAXRqEAAEQCAfISILCyAiQQBIICBBAE4gICAfR3FxBEAgCSAhQQF0ahAABEAgICEiCwtBACEjICJBAEgEQEEgQQAgH0EAThtBwABBACAgQQBOG2ohJEEAISUCQANAIAogJCAlakEBdGoQAEUNASAlQQFqISUgJUEgRgRAQQAPCwwACwtBASEmICUhEgJAA0AgEkUNASASQQFrIRIgJkEBdCALICVBBXQgEmpBAXRqEABqISYMAAsLICZBAWshJwJAA0AgIyACSwRAQQAPCyAPICNBAnRqKAIAISggKCAfRyAoICBHcQRAICdFDQIgJ0EBayEnCyAjQQFqISMMAAsLICghIgUDQCAPICNBAnRqKAIAICJHBEAgI0EBaiEjDAELCwsgIwRAIA9BBGogDyAjQQJ0/AoAACAPICI2AgALQQEgAyAXa3QhKSAiQQV0IBdyISpBACESA0AgDSAZIBJqQQJ0aiAqNgIAIA4gGCASakECdGogKjYCACASQQFqIRIgEiApSQ0ACyAFIBRBAnRqIBY2AgAgECAUaiAXOgAAIARBAUYEQCARIBRqICI6AAAFIARBAkYEQCARIBRBAXRqICI7AQAFIBEgFEECdGogIjYCAAsLIBYgKSApbGohFiAiIRUgFEEBaiEUDAALCyMCIwNGCw==";
@@ -0,0 +1,4 @@
1
+ // Generated by `npm run build:wasm -w reader` from wasm/rangeDecoder.wat; do not edit.
2
+ /** rangeDecoder.wat's compiled module (1381 bytes), base64. */
3
+ export const RANGE_DECODER_WASM = "AGFzbQEAAAABFwNgAX8Bf2ACf38Bf2AHf39/f39/fwF/AwQDAAECBQMBAAEGFQR/AUEAC38BQQALfwFBAAt/AUEACwcTAgZtZW1vcnkCAAZkZWNvZGUAAgqKCgOBAQEDfyAALwEAIQEjAEEMdiABbCECIwEgAkkEQCACJAAgACABQYAgIAFrQQR2ajsBAAUjASACayQBIwAgAmskACAAIAEgAUEEdms7AQBBASEDCwJAA0AjAEGAgIAITw0BIwFBCHQjAi0AAHIkASMCQQFqJAIjAEEIdCQADAALCyADCzEBAX8gAEEASARAQQAPCyAAQR9xIQIgAiABTQRAQQEPCyACIAFBAWpGBEBBAg8LQQML0ggBJH9BAC0AAARAQQAPC0EBLQAAQRh0QQItAABBEHRyQQMtAABBCHRBBC0AAHJyJAFBfyQAQQUkAiAAJAMgBiEHIAZBgARqIQggBkHADGohCSAGQYAVaiEKIAZBgBdqIQsgBkGAJ2ohDCAMQbAGaiENIA1BBCADdGohDiAOQQQgA3RqIQ8gBSABQQJ0aiERIBEgASAEbGohEEEAIRIDQCAGIBJqQYAQOwEAIBJBAmohEiASQYAnSQ0ACyANQf8BQQggA3T8CwBBACESA0AgDyASQQJ0aiASNgIAIBJBAWohEiASIAJNDQALIAxCADcDACAMQQA2AghBASETQX8hFQJAA0AgFCABTw0BIBNFBEBBAA8LIBNBAWshEyAMIBNBDGxqIRsgGygCACEXIBsoAgQhGCAbKAIIIRkgGAR/IA0gGUECdGooAgAFQX8LIRwgGQR/IA4gGEECdGooAgAFQX8LIR0gHCAXEAEhHiAXIANJBEAgByAXQQR0IB5BAnRqIB0gFxABakEBdGoQAARAQQEgAyAXa0EBa3QhGiAXQQFqIRcgGyAXNgIAIBsgGCAaajYCBCAbIBkgGmo2AgggGyAXNgIMIBsgGDYCECAbIBkgGmo2AhQgGyAXNgIYIBsgGCAaajYCHCAbIBk2AiAgGyAXNgIkIBsgGDYCKCAbIBk2AiwgE0EEaiETDAILC0F/IBxBBXYgHEEASBshH0F/IB1BBXYgHUEASBshICAXQQN0IB8gIEZBAnRqIBUgH0ZBAXRqIBUgIEZqQQJ0IB5qISFBfyEiIB9BAE4EQCAIICFBAXRqEAAEQCAfISILCyAiQQBIICBBAE4gICAfR3FxBEAgCSAhQQF0ahAABEAgICEiCwtBACEjICJBAEgEQEEgQQAgH0EAThtBwABBACAgQQBOG2ohJEEAISUCQANAIAogJCAlakEBdGoQAEUNASAlQQFqISUgJUEgRgRAQQAPCwwACwtBASEmICUhEgJAA0AgEkUNASASQQFrIRIgJkEBdCALICVBBXQgEmpBAXRqEABqISYMAAsLICZBAWshJwJAA0AgIyACSwRAQQAPCyAPICNBAnRqKAIAISggKCAfRyAoICBHcQRAICdFDQIgJ0EBayEnCyAjQQFqISMMAAsLICghIgUDQCAPICNBAnRqKAIAICJHBEAgI0EBaiEjDAELCwsgIwRAIA9BBGogDyAjQQJ0/AoAACAPICI2AgALQQEgAyAXa3QhKSAiQQV0IBdyISpBACESA0AgDSAZIBJqQQJ0aiAqNgIAIA4gGCASakECdGogKjYCACASQQFqIRIgEiApSQ0ACyAFIBRBAnRqIBY2AgAgECAUaiAXOgAAIARBAUYEQCARIBRqICI6AAAFIARBAkYEQCARIBRBAXRqICI7AQAFIBEgFEECdGogIjYCAAsLIBYgKSApbGohFiAiIRUgFEEBaiEUDAALCyMCIwNGCw==";
4
+ //# sourceMappingURL=rangeDecoderWasm.js.map