@shbernal/ts-xlsx 2.1.0 → 3.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/README.md +4 -1
- package/dist/bytes.d.ts +7 -0
- package/dist/bytes.js +22 -0
- package/dist/core/address.d.ts +60 -2
- package/dist/core/address.js +40 -24
- package/dist/core/autofilter.d.ts +5 -4
- package/dist/core/autofilter.js +34 -35
- package/dist/core/axis-handle.d.ts +10 -0
- package/dist/core/axis-handle.js +17 -0
- package/dist/core/cell.d.ts +25 -0
- package/dist/core/cell.js +9 -3
- package/dist/core/clone.d.ts +29 -0
- package/dist/core/clone.js +24 -0
- package/dist/core/color-resolution.js +2 -4
- package/dist/core/column.d.ts +5 -1
- package/dist/core/column.js +34 -35
- package/dist/core/comment-thread.js +2 -1
- package/dist/core/conditional-formatting-overlay.d.ts +2 -1
- package/dist/core/conditional-formatting-overlay.js +2 -2
- package/dist/core/conditional-formatting.d.ts +14 -6
- package/dist/core/conditional-formatting.js +45 -43
- package/dist/core/containers.d.ts +12 -0
- package/dist/core/containers.js +5 -0
- package/dist/core/data-validation-overlay.d.ts +2 -1
- package/dist/core/data-validation-overlay.js +4 -4
- package/dist/core/data-validation.d.ts +9 -3
- package/dist/core/data-validation.js +21 -18
- package/dist/core/date-format.d.ts +20 -0
- package/dist/core/date-format.js +160 -0
- package/dist/core/date.d.ts +43 -8
- package/dist/core/date.js +18 -2
- package/dist/core/formula.d.ts +26 -0
- package/dist/core/formula.js +20 -4
- package/dist/core/grid-edits.d.ts +2 -3
- package/dist/core/grid-edits.js +69 -70
- package/dist/core/grid-shift.d.ts +58 -9
- package/dist/core/grid-shift.js +26 -4
- package/dist/core/image.d.ts +14 -10
- package/dist/core/image.js +9 -9
- package/dist/core/internal.d.ts +8 -2
- package/dist/core/limits.d.ts +23 -0
- package/dist/core/limits.js +4 -0
- package/dist/core/merge-index.d.ts +31 -0
- package/dist/core/merge-index.js +60 -0
- package/dist/core/merge.d.ts +5 -14
- package/dist/core/merge.js +18 -27
- package/dist/core/page-setup.d.ts +72 -2
- package/dist/core/page-setup.js +34 -8
- package/dist/core/pivot-table.d.ts +1 -1
- package/dist/core/pivot-table.js +67 -53
- package/dist/core/protection.js +2 -11
- package/dist/core/range.js +34 -30
- package/dist/core/row-input.js +2 -2
- package/dist/core/row.d.ts +5 -1
- package/dist/core/row.js +22 -23
- package/dist/core/style.d.ts +134 -75
- package/dist/core/style.js +112 -53
- package/dist/core/table-style.d.ts +1 -1
- package/dist/core/table-style.js +4 -4
- package/dist/core/table.d.ts +28 -10
- package/dist/core/table.js +54 -39
- package/dist/core/theme.js +6 -5
- package/dist/core/used-extent.d.ts +44 -0
- package/dist/core/used-extent.js +102 -0
- package/dist/core/value.d.ts +9 -0
- package/dist/core/value.js +36 -34
- package/dist/core/workbook-media.d.ts +34 -0
- package/dist/core/workbook-media.js +44 -0
- package/dist/core/workbook-styles.d.ts +67 -1
- package/dist/core/workbook-vba.js +3 -2
- package/dist/core/workbook.d.ts +23 -2
- package/dist/core/workbook.js +24 -40
- package/dist/core/worksheet-comments.d.ts +2 -1
- package/dist/core/worksheet-comments.js +20 -13
- package/dist/core/worksheet-merges.d.ts +44 -0
- package/dist/core/worksheet-merges.js +59 -0
- package/dist/core/worksheet-model.d.ts +9 -1
- package/dist/core/worksheet-model.js +24 -9
- package/dist/core/worksheet.d.ts +72 -12
- package/dist/core/worksheet.js +68 -70
- package/dist/customui/ribbon.js +6 -7
- package/dist/entries/core.d.ts +18 -13
- package/dist/entries/core.js +12 -6
- package/dist/entries/xlsx.d.ts +1 -1
- package/dist/errors.d.ts +64 -0
- package/dist/errors.js +22 -0
- package/dist/hex.d.ts +8 -0
- package/dist/hex.js +3 -0
- package/dist/io/csv/delimiter.d.ts +9 -0
- package/dist/io/csv/delimiter.js +6 -0
- package/dist/io/csv/read.js +11 -4
- package/dist/io/csv/write.d.ts +9 -2
- package/dist/io/csv/write.js +9 -38
- package/dist/io/opc/inflate.js +15 -1
- package/dist/io/opc/namespaces.d.ts +16 -0
- package/dist/io/opc/namespaces.js +4 -0
- package/dist/io/opc/part-paths.d.ts +10 -0
- package/dist/io/opc/part-paths.js +16 -1
- package/dist/io/opc/read-opc.d.ts +69 -10
- package/dist/io/opc/read-opc.js +49 -55
- package/dist/io/opc/read-options.d.ts +5 -1
- package/dist/io/opc/rels.d.ts +1 -6
- package/dist/io/opc/rels.js +1 -4
- package/dist/io/opc/sniff-format.d.ts +3 -3
- package/dist/io/opc/sniff-format.js +2 -2
- package/dist/io/style/xf-style.d.ts +48 -2
- package/dist/io/style/xf-style.js +14 -0
- package/dist/io/xlsb/formula.d.ts +2 -2
- package/dist/io/xlsb/formula.js +49 -34
- package/dist/io/xlsb/primitives.js +8 -14
- package/dist/io/xlsb/ptg-functions.d.ts +19 -10
- package/dist/io/xlsb/ptg-functions.js +385 -596
- package/dist/io/xlsb/read-styles.js +50 -83
- package/dist/io/xlsb/read-worksheet.d.ts +20 -1
- package/dist/io/xlsb/read-worksheet.js +73 -89
- package/dist/io/xlsb/read.d.ts +6 -3
- package/dist/io/xlsb/read.js +47 -29
- package/dist/io/xlsb/record-stream.d.ts +30 -0
- package/dist/io/xlsb/record-stream.js +20 -0
- package/dist/io/xlsb/record-types.d.ts +1 -5
- package/dist/io/xlsb/record-types.js +1 -5
- package/dist/io/xlsx/cell-accumulator.d.ts +9 -2
- package/dist/io/xlsx/cell-accumulator.js +32 -9
- package/dist/io/xlsx/cell-style-resolution.d.ts +25 -0
- package/dist/io/xlsx/cell-style-resolution.js +29 -0
- package/dist/io/xlsx/cell-value.d.ts +5 -3
- package/dist/io/xlsx/cell-value.js +13 -15
- package/dist/io/xlsx/color-xml.js +3 -2
- package/dist/io/xlsx/column-budget.d.ts +26 -0
- package/dist/io/xlsx/column-budget.js +22 -0
- package/dist/io/xlsx/comments.d.ts +6 -2
- package/dist/io/xlsx/comments.js +36 -31
- package/dist/io/xlsx/conditional-formatting.d.ts +9 -5
- package/dist/io/xlsx/conditional-formatting.js +51 -46
- package/dist/io/xlsx/data-validation.d.ts +9 -7
- package/dist/io/xlsx/data-validation.js +38 -35
- package/dist/io/xlsx/edit-vba.d.ts +7 -2
- package/dist/io/xlsx/edit-vba.js +15 -29
- package/dist/io/xlsx/font-xml.d.ts +7 -0
- package/dist/io/xlsx/font-xml.js +104 -0
- package/dist/io/xlsx/hyperlinks.d.ts +41 -7
- package/dist/io/xlsx/hyperlinks.js +40 -28
- package/dist/io/xlsx/images.js +24 -25
- package/dist/io/xlsx/namespaces.d.ts +16 -0
- package/dist/io/xlsx/namespaces.js +7 -0
- package/dist/io/xlsx/package-plan.d.ts +23 -3
- package/dist/io/xlsx/package-plan.js +66 -25
- package/dist/io/xlsx/part-names.d.ts +41 -0
- package/dist/io/xlsx/part-names.js +46 -0
- package/dist/io/xlsx/pivot.js +8 -7
- package/dist/io/xlsx/read-parts.d.ts +59 -0
- package/dist/io/xlsx/read-parts.js +252 -0
- package/dist/io/xlsx/read-pivot.js +3 -1
- package/dist/io/xlsx/read-repair.d.ts +26 -0
- package/dist/io/xlsx/read-repair.js +39 -0
- package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +1 -1
- package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +7 -3
- package/dist/io/xlsx/read-rows.d.ts +18 -5
- package/dist/io/xlsx/read-rows.js +50 -37
- package/dist/io/xlsx/read-shared-strings.js +5 -3
- package/dist/io/xlsx/read-styles.d.ts +1 -6
- package/dist/io/xlsx/read-styles.js +65 -109
- package/dist/io/xlsx/read-workbook-xml.d.ts +36 -0
- package/dist/io/xlsx/read-workbook-xml.js +170 -0
- package/dist/io/xlsx/read-worksheet.d.ts +11 -1
- package/dist/io/xlsx/read-worksheet.js +51 -193
- package/dist/io/xlsx/read.d.ts +5 -13
- package/dist/io/xlsx/read.js +111 -436
- package/dist/io/xlsx/rich-text.js +1 -1
- package/dist/io/xlsx/row-position.d.ts +18 -0
- package/dist/io/xlsx/row-position.js +11 -0
- package/dist/io/xlsx/row-xml.d.ts +128 -0
- package/dist/io/xlsx/row-xml.js +203 -0
- package/dist/io/xlsx/shared-formulas.js +2 -2
- package/dist/io/xlsx/sheet-properties.d.ts +10 -1
- package/dist/io/xlsx/sheet-properties.js +166 -37
- package/dist/io/xlsx/style-elements.d.ts +43 -0
- package/dist/io/xlsx/style-elements.js +156 -0
- package/dist/io/xlsx/styles.d.ts +12 -3
- package/dist/io/xlsx/styles.js +5 -204
- package/dist/io/xlsx/tables.js +11 -14
- package/dist/io/xlsx/theme-xml.d.ts +26 -4
- package/dist/io/xlsx/theme-xml.js +107 -69
- package/dist/io/xlsx/threaded-comments.d.ts +8 -10
- package/dist/io/xlsx/threaded-comments.js +3 -1
- package/dist/io/xlsx/workbook-xml.d.ts +54 -4
- package/dist/io/xlsx/workbook-xml.js +84 -57
- package/dist/io/xlsx/worksheet-xml.d.ts +23 -70
- package/dist/io/xlsx/worksheet-xml.js +59 -242
- package/dist/io/xlsx/write-stream.d.ts +71 -16
- package/dist/io/xlsx/write-stream.js +79 -41
- package/dist/io/xlsx/write.d.ts +6 -2
- package/dist/io/xlsx/write.js +217 -153
- package/dist/rel-type.d.ts +16 -0
- package/dist/rel-type.js +6 -0
- package/dist/token-set.d.ts +28 -0
- package/dist/token-set.js +7 -0
- package/dist/vba/bytes.d.ts +14 -5
- package/dist/vba/bytes.js +16 -6
- package/dist/vba/cfb-format.d.ts +32 -0
- package/dist/vba/cfb-format.js +13 -0
- package/dist/vba/cfb-writer.d.ts +9 -0
- package/dist/vba/cfb-writer.js +28 -25
- package/dist/vba/cfb.d.ts +13 -1
- package/dist/vba/cfb.js +73 -57
- package/dist/vba/codepage.js +2 -1
- package/dist/vba/errors.d.ts +7 -0
- package/dist/vba/ms-ovba.js +79 -23
- package/dist/vba/project-editor.js +35 -40
- package/dist/vba/project.d.ts +11 -1
- package/dist/vba/project.js +40 -21
- package/dist/vba/vba-encoding.js +5 -6
- package/dist/xml/xml-attrs.d.ts +37 -0
- package/dist/xml/xml-attrs.js +30 -0
- package/dist/xml/xml-chars.d.ts +40 -0
- package/dist/xml/xml-chars.js +12 -0
- package/dist/xml/xml-namespaces.d.ts +31 -0
- package/dist/xml/xml-namespaces.js +50 -0
- package/dist/xml/xml-read.d.ts +128 -81
- package/dist/xml/xml-read.js +161 -178
- package/dist/xml/xml-scan.d.ts +78 -0
- package/dist/xml/xml-scan.js +180 -0
- package/dist/xml/xml.d.ts +29 -28
- package/dist/xml/xml.js +24 -24
- package/package.json +13 -9
package/dist/vba/cfb-writer.js
CHANGED
|
@@ -1,24 +1,18 @@
|
|
|
1
|
+
import { quoted } from '../errors.js';
|
|
2
|
+
import { DIFSECT, DIR_ENTRY_SIZE, ENDOFCHAIN, FATSECT, FREESECT, MAX_NAME_CHARS, MINI_STREAM_CUTOFF, NOSTREAM, TYPE_ROOT, TYPE_STORAGE, TYPE_STREAM, } from './cfb-format.js';
|
|
1
3
|
import { VbaAuthorError } from './errors.js';
|
|
4
|
+
export function isStream(node) {
|
|
5
|
+
return 'data' in node;
|
|
6
|
+
}
|
|
7
|
+
export function isStorage(node) {
|
|
8
|
+
return !isStream(node);
|
|
9
|
+
}
|
|
2
10
|
const SECTOR = 512;
|
|
3
11
|
const MINI_SECTOR = 64;
|
|
4
|
-
const MINI_CUTOFF = 4096;
|
|
5
|
-
const DIR_ENTRY_SIZE = 128;
|
|
6
12
|
const ENTRIES_PER_DIR_SECTOR = SECTOR / DIR_ENTRY_SIZE;
|
|
7
13
|
const FAT_ENTRIES_PER_SECTOR = SECTOR / 4;
|
|
8
14
|
const DIFAT_HEADER_SLOTS = 109;
|
|
9
|
-
const MAX_NAME_CHARS = 31;
|
|
10
|
-
const FREESECT = 0xffffffff;
|
|
11
|
-
const ENDOFCHAIN = 0xfffffffe;
|
|
12
|
-
const FATSECT = 0xfffffffd;
|
|
13
|
-
const DIFSECT = 0xfffffffc;
|
|
14
|
-
const NOSTREAM = 0xffffffff;
|
|
15
|
-
const TYPE_STORAGE = 1;
|
|
16
|
-
const TYPE_STREAM = 2;
|
|
17
|
-
const TYPE_ROOT = 5;
|
|
18
15
|
const COLOR_BLACK = 1;
|
|
19
|
-
function isStream(node) {
|
|
20
|
-
return 'data' in node;
|
|
21
|
-
}
|
|
22
16
|
export function writeCompoundFile(root) {
|
|
23
17
|
const entries = [];
|
|
24
18
|
const addEntry = (name, type, startSector) => {
|
|
@@ -36,7 +30,8 @@ export function writeCompoundFile(root) {
|
|
|
36
30
|
return entry;
|
|
37
31
|
};
|
|
38
32
|
const rootEntry = addEntry('Root Entry', TYPE_ROOT, ENDOFCHAIN);
|
|
39
|
-
const
|
|
33
|
+
const miniChunks = [];
|
|
34
|
+
let miniLength = 0;
|
|
40
35
|
const miniFat = [];
|
|
41
36
|
const bigStreams = [];
|
|
42
37
|
const addNode = (node, siblings) => {
|
|
@@ -46,15 +41,18 @@ export function writeCompoundFile(root) {
|
|
|
46
41
|
siblings.push(entry);
|
|
47
42
|
if (node.data.length === 0) {
|
|
48
43
|
}
|
|
49
|
-
else if (node.data.length >=
|
|
44
|
+
else if (node.data.length >= MINI_STREAM_CUTOFF) {
|
|
50
45
|
bigStreams.push({ entry, data: node.data, sectors: Math.ceil(node.data.length / SECTOR) });
|
|
51
46
|
}
|
|
52
47
|
else {
|
|
53
|
-
const startMini =
|
|
48
|
+
const startMini = miniLength / MINI_SECTOR;
|
|
54
49
|
const numMini = Math.ceil(node.data.length / MINI_SECTOR);
|
|
55
50
|
for (let k = 0; k < numMini; k++)
|
|
56
51
|
miniFat.push(k < numMini - 1 ? startMini + k + 1 : ENDOFCHAIN);
|
|
57
|
-
|
|
52
|
+
const padded = new Uint8Array(numMini * MINI_SECTOR);
|
|
53
|
+
padded.set(node.data);
|
|
54
|
+
miniChunks.push(padded);
|
|
55
|
+
miniLength += padded.length;
|
|
58
56
|
entry.startSector = startMini;
|
|
59
57
|
}
|
|
60
58
|
}
|
|
@@ -74,7 +72,7 @@ export function writeCompoundFile(root) {
|
|
|
74
72
|
linkChildren(rootEntry, rootKids);
|
|
75
73
|
const dirSectors = Math.ceil(entries.length / ENTRIES_PER_DIR_SECTOR);
|
|
76
74
|
const miniFatSectors = miniFat.length > 0 ? Math.ceil((miniFat.length * 4) / FAT_ENTRIES_PER_SECTOR) : 0;
|
|
77
|
-
const miniStreamSectors = Math.ceil(
|
|
75
|
+
const miniStreamSectors = Math.ceil(miniLength / SECTOR);
|
|
78
76
|
const baseSectors = dirSectors +
|
|
79
77
|
miniFatSectors +
|
|
80
78
|
miniStreamSectors +
|
|
@@ -112,7 +110,7 @@ export function writeCompoundFile(root) {
|
|
|
112
110
|
cursor += difatSectors;
|
|
113
111
|
const totalSectors = cursor;
|
|
114
112
|
rootEntry.startSector = miniStreamStart;
|
|
115
|
-
rootEntry.size =
|
|
113
|
+
rootEntry.size = miniLength;
|
|
116
114
|
const fat = new Array(fatSectors * FAT_ENTRIES_PER_SECTOR).fill(FREESECT);
|
|
117
115
|
const chainRegion = (start, count) => {
|
|
118
116
|
for (let k = 0; k < count; k++)
|
|
@@ -146,8 +144,13 @@ export function writeCompoundFile(root) {
|
|
|
146
144
|
for (let i = 0; i < miniFatSectors * FAT_ENTRIES_PER_SECTOR; i++) {
|
|
147
145
|
dv.setUint32(at(miniFatStart) + i * 4, miniFat[i] ?? FREESECT, true);
|
|
148
146
|
}
|
|
149
|
-
if (
|
|
150
|
-
|
|
147
|
+
if (miniLength > 0) {
|
|
148
|
+
let offset = at(miniStreamStart);
|
|
149
|
+
for (const chunk of miniChunks) {
|
|
150
|
+
buf.set(chunk, offset);
|
|
151
|
+
offset += chunk.length;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
151
154
|
for (const big of bigStreams)
|
|
152
155
|
buf.set(big.data, at(big.entry.startSector));
|
|
153
156
|
for (const [i, sector] of fat.entries())
|
|
@@ -168,10 +171,10 @@ function validateSiblingNames(storageName, siblings) {
|
|
|
168
171
|
if (node.name.length === 0)
|
|
169
172
|
throw new VbaAuthorError('entry name must not be empty');
|
|
170
173
|
if (node.name.length > MAX_NAME_CHARS) {
|
|
171
|
-
throw new VbaAuthorError(`entry name
|
|
174
|
+
throw new VbaAuthorError(`entry name ${quoted(node.name)} exceeds the ${MAX_NAME_CHARS}-character CFB limit`);
|
|
172
175
|
}
|
|
173
176
|
if (seen.has(node.name)) {
|
|
174
|
-
throw new VbaAuthorError(`duplicate entry name
|
|
177
|
+
throw new VbaAuthorError(`duplicate entry name ${quoted(node.name)} under storage ${quoted(storageName)}`);
|
|
175
178
|
}
|
|
176
179
|
seen.add(node.name);
|
|
177
180
|
if (!isStream(node))
|
|
@@ -207,7 +210,7 @@ function writeHeader(dv, p) {
|
|
|
207
210
|
dv.setUint16(32, 6, true);
|
|
208
211
|
dv.setUint32(44, p.fatSectors, true);
|
|
209
212
|
dv.setUint32(48, p.dirStart, true);
|
|
210
|
-
dv.setUint32(56,
|
|
213
|
+
dv.setUint32(56, MINI_STREAM_CUTOFF, true);
|
|
211
214
|
dv.setUint32(60, p.miniFatStart, true);
|
|
212
215
|
dv.setUint32(64, p.miniFatSectors, true);
|
|
213
216
|
dv.setUint32(68, p.difatSectors > 0 ? p.difatStart : ENDOFCHAIN, true);
|
package/dist/vba/cfb.d.ts
CHANGED
|
@@ -4,13 +4,25 @@ export declare class CompoundFile {
|
|
|
4
4
|
constructor(buf: Uint8Array);
|
|
5
5
|
/** List every stream/storage name in the directory (order as stored). */
|
|
6
6
|
names(): string[];
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Read a stream's raw bytes by exact entry name, or `undefined` if absent.
|
|
9
|
+
*
|
|
10
|
+
* @returns bytes that may be a **view onto the caller's buffer** rather than a copy. A stream small
|
|
11
|
+
* enough to fit in one sector is assembled by `concat` from a single chunk, and `concat` hands a
|
|
12
|
+
* lone chunk straight back; anything larger is copied into a fresh buffer. So whether the result
|
|
13
|
+
* aliases the source depends on the stream's size, which is a rule no caller should have to know
|
|
14
|
+
* and none here relies on: nothing mutates what this returns. Stated because it is the sort of
|
|
15
|
+
* thing that is true until someone writes the first mutation, and then true only for large
|
|
16
|
+
* streams. Copy before mutating.
|
|
17
|
+
*/
|
|
8
18
|
readStream(name: string): Uint8Array | undefined;
|
|
9
19
|
/**
|
|
10
20
|
* Reconstruct the container's top-level children as the writer's node shape, recursing into every
|
|
11
21
|
* storage, so a caller can swap one stream and re-emit the whole hierarchy with {@link writeCompoundFile}.
|
|
12
22
|
* Walks the red-black sibling tree each storage navigates (not the linear directory scan), so any part
|
|
13
23
|
* a host reaches is carried through. Cycle- and bounds-guarded like every other chain walk here.
|
|
24
|
+
*
|
|
25
|
+
* @returns nodes whose stream bytes carry {@link readStream}'s aliasing caveat.
|
|
14
26
|
*/
|
|
15
27
|
tree(): CfbNode[];
|
|
16
28
|
}
|
package/dist/vba/cfb.js
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
|
+
import { quoted } from '../errors.js';
|
|
1
2
|
import { concat, decodeUtf16le, readU16, readU32 } from './bytes.js';
|
|
3
|
+
import { DIR_ENTRY_SIZE, MAX_REGULAR_SECTOR, MINI_STREAM_CUTOFF, NOSTREAM, TYPE_EMPTY, TYPE_ROOT, TYPE_STORAGE, TYPE_STREAM, } from './cfb-format.js';
|
|
2
4
|
import { VbaParseError } from './errors.js';
|
|
3
|
-
const MAX_REGULAR_SECTOR = 0xfffffffa;
|
|
4
|
-
const NOSTREAM = 0xffffffff;
|
|
5
|
-
const TYPE_EMPTY = 0;
|
|
6
|
-
const TYPE_STORAGE = 1;
|
|
7
|
-
const TYPE_STREAM = 2;
|
|
8
|
-
const TYPE_ROOT = 5;
|
|
9
5
|
const CFB_SIGNATURE_LO = 0xe011cfd0;
|
|
10
6
|
const CFB_SIGNATURE_HI = 0xe11ab1a1;
|
|
11
|
-
const
|
|
7
|
+
const MAX_TREE_DEPTH = 256;
|
|
12
8
|
export class CompoundFile {
|
|
13
9
|
#buf;
|
|
14
10
|
#sectorSize;
|
|
@@ -39,6 +35,9 @@ export class CompoundFile {
|
|
|
39
35
|
const numFatSectors = readU32(this.#buf, 44);
|
|
40
36
|
const firstDirSector = readU32(this.#buf, 48);
|
|
41
37
|
this.#miniCutoff = readU32(this.#buf, 56);
|
|
38
|
+
if (this.#miniCutoff !== MINI_STREAM_CUTOFF) {
|
|
39
|
+
throw new VbaParseError(`unsupported mini-stream cutoff ${this.#miniCutoff}`);
|
|
40
|
+
}
|
|
42
41
|
const firstMiniFatSector = readU32(this.#buf, 60);
|
|
43
42
|
const firstDifatSector = readU32(this.#buf, 68);
|
|
44
43
|
const numDifatSectors = readU32(this.#buf, 72);
|
|
@@ -69,11 +68,15 @@ export class CompoundFile {
|
|
|
69
68
|
const root = this.#dir[rootIdx];
|
|
70
69
|
return this.#buildSiblings(root.child, new Set([rootIdx]));
|
|
71
70
|
}
|
|
72
|
-
#buildSiblings(firstChild, seen) {
|
|
71
|
+
#buildSiblings(firstChild, seen, depth = 0) {
|
|
72
|
+
if (depth > MAX_TREE_DEPTH)
|
|
73
|
+
throw new VbaParseError('directory tree nests too deep');
|
|
73
74
|
const nodes = [];
|
|
74
|
-
const walk = (idx) => {
|
|
75
|
+
const walk = (idx, siblingDepth) => {
|
|
75
76
|
if (idx >= MAX_REGULAR_SECTOR)
|
|
76
77
|
return;
|
|
78
|
+
if (siblingDepth > MAX_TREE_DEPTH)
|
|
79
|
+
throw new VbaParseError('directory tree nests too deep');
|
|
77
80
|
if (idx >= this.#dir.length)
|
|
78
81
|
throw new VbaParseError('directory sibling index out of range');
|
|
79
82
|
if (seen.has(idx))
|
|
@@ -82,16 +85,16 @@ export class CompoundFile {
|
|
|
82
85
|
const e = this.#dir[idx];
|
|
83
86
|
if (e.type === TYPE_EMPTY)
|
|
84
87
|
throw new VbaParseError('directory tree links an empty entry');
|
|
85
|
-
walk(e.left);
|
|
88
|
+
walk(e.left, siblingDepth + 1);
|
|
86
89
|
if (e.type === TYPE_STORAGE) {
|
|
87
|
-
nodes.push({ name: e.name, children: this.#buildSiblings(e.child, seen) });
|
|
90
|
+
nodes.push({ name: e.name, children: this.#buildSiblings(e.child, seen, depth + 1) });
|
|
88
91
|
}
|
|
89
92
|
else {
|
|
90
93
|
nodes.push({ name: e.name, data: this.#readEntryData(e) });
|
|
91
94
|
}
|
|
92
|
-
walk(e.right);
|
|
95
|
+
walk(e.right, siblingDepth + 1);
|
|
93
96
|
};
|
|
94
|
-
walk(firstChild);
|
|
97
|
+
walk(firstChild, depth);
|
|
95
98
|
return nodes;
|
|
96
99
|
}
|
|
97
100
|
#readEntryData(entry) {
|
|
@@ -100,8 +103,9 @@ export class CompoundFile {
|
|
|
100
103
|
return this.#readViaMiniFat(entry.startSector, entry.size);
|
|
101
104
|
}
|
|
102
105
|
#readDifat(numFatSectors, firstDifat, numDifat) {
|
|
106
|
+
const wanted = Math.min(numFatSectors, this.#maxSector);
|
|
103
107
|
const ids = [];
|
|
104
|
-
for (let i = 0; i < 109 && ids.length <
|
|
108
|
+
for (let i = 0; i < 109 && ids.length < wanted; i++) {
|
|
105
109
|
const v = readU32(this.#buf, 76 + i * 4);
|
|
106
110
|
if (v >= MAX_REGULAR_SECTOR)
|
|
107
111
|
break;
|
|
@@ -110,12 +114,12 @@ export class CompoundFile {
|
|
|
110
114
|
const seen = new Set();
|
|
111
115
|
let sector = firstDifat;
|
|
112
116
|
const perSector = this.#sectorSize / 4 - 1;
|
|
113
|
-
for (let s = 0; s < numDifat && sector < MAX_REGULAR_SECTOR; s++) {
|
|
117
|
+
for (let s = 0; s < numDifat && sector < MAX_REGULAR_SECTOR && ids.length < wanted; s++) {
|
|
114
118
|
if (seen.has(sector))
|
|
115
119
|
throw new VbaParseError('cycle in DIFAT sector chain');
|
|
116
120
|
seen.add(sector);
|
|
117
121
|
const base = this.#dataSectorOffset(sector);
|
|
118
|
-
for (let i = 0; i < perSector; i++) {
|
|
122
|
+
for (let i = 0; i < perSector && ids.length < wanted; i++) {
|
|
119
123
|
const v = readU32(this.#buf, base + i * 4);
|
|
120
124
|
if (v < MAX_REGULAR_SECTOR)
|
|
121
125
|
ids.push(v);
|
|
@@ -129,23 +133,28 @@ export class CompoundFile {
|
|
|
129
133
|
const perSector = this.#sectorSize / 4;
|
|
130
134
|
for (const sid of fatSectorIds) {
|
|
131
135
|
const base = this.#dataSectorOffset(sid);
|
|
132
|
-
for (let i = 0; i < perSector; i++)
|
|
136
|
+
for (let i = 0; i < perSector && fat.length < this.#maxSector; i++) {
|
|
133
137
|
fat.push(readU32(this.#buf, base + i * 4));
|
|
138
|
+
}
|
|
139
|
+
if (fat.length >= this.#maxSector)
|
|
140
|
+
break;
|
|
134
141
|
}
|
|
135
142
|
return fat;
|
|
136
143
|
}
|
|
137
144
|
#readChainValues(firstSector) {
|
|
138
145
|
const values = [];
|
|
139
146
|
const perSector = this.#sectorSize / 4;
|
|
147
|
+
const cap = this.#maxSector * (this.#sectorSize / this.#miniSectorSize);
|
|
140
148
|
const seen = new Set();
|
|
141
149
|
let sector = firstSector;
|
|
142
|
-
while (sector < MAX_REGULAR_SECTOR) {
|
|
150
|
+
while (sector < MAX_REGULAR_SECTOR && values.length < cap) {
|
|
143
151
|
if (seen.has(sector))
|
|
144
152
|
throw new VbaParseError('cycle in mini-FAT sector chain');
|
|
145
153
|
seen.add(sector);
|
|
146
154
|
const base = this.#dataSectorOffset(sector);
|
|
147
|
-
for (let i = 0; i < perSector; i++)
|
|
155
|
+
for (let i = 0; i < perSector && values.length < cap; i++) {
|
|
148
156
|
values.push(readU32(this.#buf, base + i * 4));
|
|
157
|
+
}
|
|
149
158
|
sector = this.#nextInFat(sector);
|
|
150
159
|
}
|
|
151
160
|
return values;
|
|
@@ -183,63 +192,70 @@ export class CompoundFile {
|
|
|
183
192
|
const name = decodeUtf16le(raw.subarray(off, off + Math.max(0, nameLen - 2)));
|
|
184
193
|
const startSector = readU32(raw, off + 116);
|
|
185
194
|
const size = readU32(raw, off + 120);
|
|
195
|
+
if (readU32(raw, off + 124) !== 0) {
|
|
196
|
+
throw new VbaParseError(`directory entry ${quoted(name)} declares a stream larger than 4 GiB`);
|
|
197
|
+
}
|
|
186
198
|
entries.push({ name, type, startSector, size, left, right, child });
|
|
187
199
|
}
|
|
188
200
|
return entries;
|
|
189
201
|
}
|
|
190
202
|
#readChainFull(startSector) {
|
|
191
|
-
|
|
192
|
-
const seen = new Set();
|
|
193
|
-
let sector = startSector;
|
|
194
|
-
while (sector < MAX_REGULAR_SECTOR) {
|
|
195
|
-
if (seen.has(sector))
|
|
196
|
-
throw new VbaParseError('cycle in FAT sector chain');
|
|
197
|
-
seen.add(sector);
|
|
198
|
-
const base = this.#dataSectorOffset(sector);
|
|
199
|
-
chunks.push(this.#buf.subarray(base, base + this.#sectorSize));
|
|
200
|
-
sector = this.#nextInFat(sector);
|
|
201
|
-
}
|
|
202
|
-
return concat(chunks);
|
|
203
|
+
return this.#walkChain(startSector, undefined, this.#fatChain('FAT sector chain'));
|
|
203
204
|
}
|
|
204
205
|
#readViaFat(startSector, size) {
|
|
205
|
-
|
|
206
|
-
const seen = new Set();
|
|
207
|
-
let sector = startSector;
|
|
208
|
-
let remaining = size;
|
|
209
|
-
while (sector < MAX_REGULAR_SECTOR && remaining > 0) {
|
|
210
|
-
if (seen.has(sector))
|
|
211
|
-
throw new VbaParseError('cycle in stream FAT chain');
|
|
212
|
-
seen.add(sector);
|
|
213
|
-
const base = this.#dataSectorOffset(sector);
|
|
214
|
-
const take = Math.min(this.#sectorSize, remaining);
|
|
215
|
-
chunks.push(this.#buf.subarray(base, base + take));
|
|
216
|
-
remaining -= take;
|
|
217
|
-
sector = this.#nextInFat(sector);
|
|
218
|
-
}
|
|
219
|
-
return concat(chunks);
|
|
206
|
+
return this.#walkChain(startSector, size, this.#fatChain('stream FAT chain'));
|
|
220
207
|
}
|
|
221
208
|
#readViaMiniFat(startSector, size) {
|
|
209
|
+
return this.#walkChain(startSector, size, this.#miniFatChain());
|
|
210
|
+
}
|
|
211
|
+
#walkChain(startSector, size, chain) {
|
|
222
212
|
const chunks = [];
|
|
223
213
|
const seen = new Set();
|
|
224
214
|
let sector = startSector;
|
|
225
|
-
let remaining = size;
|
|
215
|
+
let remaining = size ?? Infinity;
|
|
226
216
|
while (sector < MAX_REGULAR_SECTOR && remaining > 0) {
|
|
227
217
|
if (seen.has(sector))
|
|
228
|
-
throw new VbaParseError(
|
|
218
|
+
throw new VbaParseError(`cycle in ${chain.what}`);
|
|
229
219
|
seen.add(sector);
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
if (base + take > this.#miniStream.length) {
|
|
233
|
-
throw new VbaParseError('mini-stream sector runs past end of the mini stream');
|
|
234
|
-
}
|
|
235
|
-
chunks.push(this.#miniStream.subarray(base, base + take));
|
|
220
|
+
const take = Math.min(chain.sectorSize, remaining);
|
|
221
|
+
chunks.push(chain.sliceAt(sector, take));
|
|
236
222
|
remaining -= take;
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
223
|
+
sector = chain.next(sector);
|
|
224
|
+
}
|
|
225
|
+
if (size !== undefined && remaining > 0) {
|
|
226
|
+
throw new VbaParseError(`${chain.what} ends after ${size - remaining} bytes, but ${size} were declared`);
|
|
240
227
|
}
|
|
241
228
|
return concat(chunks);
|
|
242
229
|
}
|
|
230
|
+
#fatChain(what) {
|
|
231
|
+
return {
|
|
232
|
+
what,
|
|
233
|
+
sectorSize: this.#sectorSize,
|
|
234
|
+
sliceAt: (sector, take) => {
|
|
235
|
+
const base = this.#dataSectorOffset(sector);
|
|
236
|
+
return this.#buf.subarray(base, base + take);
|
|
237
|
+
},
|
|
238
|
+
next: (sector) => this.#nextInFat(sector),
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
#miniFatChain() {
|
|
242
|
+
return {
|
|
243
|
+
what: 'stream mini-FAT chain',
|
|
244
|
+
sectorSize: this.#miniSectorSize,
|
|
245
|
+
sliceAt: (sector, take) => {
|
|
246
|
+
const base = sector * this.#miniSectorSize;
|
|
247
|
+
if (base + take > this.#miniStream.length) {
|
|
248
|
+
throw new VbaParseError('mini-stream sector runs past end of the mini stream');
|
|
249
|
+
}
|
|
250
|
+
return this.#miniStream.subarray(base, base + take);
|
|
251
|
+
},
|
|
252
|
+
next: (sector) => {
|
|
253
|
+
if (sector >= this.#miniFat.length)
|
|
254
|
+
throw new VbaParseError('mini-FAT index out of range');
|
|
255
|
+
return this.#miniFat[sector];
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
}
|
|
243
259
|
#nextInFat(sector) {
|
|
244
260
|
if (sector >= this.#fat.length)
|
|
245
261
|
throw new VbaParseError('FAT index out of range');
|
package/dist/vba/codepage.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { hex } from '../hex.js';
|
|
1
2
|
import { VbaAuthorError, VbaParseError } from './errors.js';
|
|
2
3
|
const CODEPAGE_LABEL = new Map([
|
|
3
4
|
[1250, 'windows-1250'],
|
|
@@ -56,7 +57,7 @@ export function encoderForCodePage(codePage) {
|
|
|
56
57
|
const code = text.charCodeAt(i);
|
|
57
58
|
const byte = charToByte.get(code);
|
|
58
59
|
if (byte === undefined) {
|
|
59
|
-
throw new VbaAuthorError(`character U+${code
|
|
60
|
+
throw new VbaAuthorError(`character U+${hex(code, 4)} is not representable in code page ${codePage}`);
|
|
60
61
|
}
|
|
61
62
|
out[i] = byte;
|
|
62
63
|
}
|
package/dist/vba/errors.d.ts
CHANGED
|
@@ -17,6 +17,13 @@ export declare class VbaParseError extends XlsxError {
|
|
|
17
17
|
* [MS-CFB] 31-character limit, a duplicate stream name, or a project so large it would exceed the
|
|
18
18
|
* writer's single-header DIFAT bound. This is a caller-side contract violation, distinct from
|
|
19
19
|
* {@link VbaParseError} (which reports a malformed blob *read* from an untrusted file).
|
|
20
|
+
*
|
|
21
|
+
* It shares `code: 'authoring'` with `AuthoringError` rather than being one, and it is the only
|
|
22
|
+
* subsystem in the tree that got its own authoring class. The reason is symmetry with
|
|
23
|
+
* {@link VbaParseError} and not the VBA subsystem's importance: a caller who catches the parse error
|
|
24
|
+
* to skip a macro project it cannot read is the same caller who catches the author error to skip one
|
|
25
|
+
* it cannot write, and the two halves of that pair have to be nameable the same way. A CSV or table
|
|
26
|
+
* authoring failure has no read-side twin to pair with, which is why those stay `AuthoringError`.
|
|
20
27
|
*/
|
|
21
28
|
export declare class VbaAuthorError extends XlsxError {
|
|
22
29
|
readonly name = "VbaAuthorError";
|
package/dist/vba/ms-ovba.js
CHANGED
|
@@ -3,6 +3,9 @@ import { VbaParseError } from './errors.js';
|
|
|
3
3
|
const MAX_CHUNK_DECOMPRESSED = 4096;
|
|
4
4
|
const CHUNK_SIGNATURE = 0b011 << 12;
|
|
5
5
|
const CHUNK_COMPRESSED_FLAG = 0x8000;
|
|
6
|
+
const MAX_MATCH_CANDIDATES = 64;
|
|
7
|
+
const HASH_BITS = 12;
|
|
8
|
+
const NO_CANDIDATE = -1;
|
|
6
9
|
const DEFAULT_MAX_OUTPUT = 64 * 1024 * 1024;
|
|
7
10
|
export function decompressContainer(buf, start = 0, maxOutput = DEFAULT_MAX_OUTPUT) {
|
|
8
11
|
if (start >= buf.length) {
|
|
@@ -11,7 +14,7 @@ export function decompressContainer(buf, start = 0, maxOutput = DEFAULT_MAX_OUTP
|
|
|
11
14
|
if (buf[start] !== 0x01) {
|
|
12
15
|
throw new VbaParseError(`compressed container must begin with a 0x01 signature byte, found 0x${(buf[start] ?? 0).toString(16)}`);
|
|
13
16
|
}
|
|
14
|
-
const out =
|
|
17
|
+
const out = new ByteSink(maxOutput);
|
|
15
18
|
let pos = start + 1;
|
|
16
19
|
while (pos + 2 <= buf.length) {
|
|
17
20
|
const header = readU16(buf, pos);
|
|
@@ -28,7 +31,6 @@ export function decompressContainer(buf, start = 0, maxOutput = DEFAULT_MAX_OUTP
|
|
|
28
31
|
if (!compressed) {
|
|
29
32
|
for (let i = pos; i < chunkEnd; i++)
|
|
30
33
|
out.push(buf[i]);
|
|
31
|
-
guardOutput(out.length, maxOutput);
|
|
32
34
|
pos = chunkEnd;
|
|
33
35
|
continue;
|
|
34
36
|
}
|
|
@@ -39,7 +41,6 @@ export function decompressContainer(buf, start = 0, maxOutput = DEFAULT_MAX_OUTP
|
|
|
39
41
|
const isCopy = (flagByte >> bit) & 1;
|
|
40
42
|
if (!isCopy) {
|
|
41
43
|
out.push(buf[pos++]);
|
|
42
|
-
guardOutput(out.length, maxOutput);
|
|
43
44
|
continue;
|
|
44
45
|
}
|
|
45
46
|
if (pos + 2 > chunkEnd) {
|
|
@@ -55,30 +56,78 @@ export function decompressContainer(buf, start = 0, maxOutput = DEFAULT_MAX_OUTP
|
|
|
55
56
|
throw new VbaParseError('copy token references before the start of its chunk');
|
|
56
57
|
}
|
|
57
58
|
for (let i = 0; i < length; i++)
|
|
58
|
-
out.push(out
|
|
59
|
-
|
|
59
|
+
out.push(out.at(src + i));
|
|
60
|
+
}
|
|
61
|
+
if (out.length - chunkStart > MAX_CHUNK_DECOMPRESSED) {
|
|
62
|
+
throw new VbaParseError(`chunk decompressed to more than ${MAX_CHUNK_DECOMPRESSED} bytes, which [MS-OVBA] forbids`);
|
|
60
63
|
}
|
|
61
64
|
}
|
|
62
65
|
pos = chunkEnd;
|
|
63
66
|
}
|
|
64
|
-
return
|
|
67
|
+
return out.bytes();
|
|
68
|
+
}
|
|
69
|
+
class ByteSink {
|
|
70
|
+
#buf;
|
|
71
|
+
#length = 0;
|
|
72
|
+
#limit;
|
|
73
|
+
constructor(limit = Infinity) {
|
|
74
|
+
this.#limit = limit;
|
|
75
|
+
this.#buf = new Uint8Array(Math.min(MAX_CHUNK_DECOMPRESSED, limit));
|
|
76
|
+
}
|
|
77
|
+
get length() {
|
|
78
|
+
return this.#length;
|
|
79
|
+
}
|
|
80
|
+
at(index) {
|
|
81
|
+
return this.#buf[index];
|
|
82
|
+
}
|
|
83
|
+
set(index, byte) {
|
|
84
|
+
this.#buf[index] = byte;
|
|
85
|
+
}
|
|
86
|
+
push(byte) {
|
|
87
|
+
if (this.#length >= this.#limit) {
|
|
88
|
+
throw new VbaParseError(`decompressed output exceeds the ${this.#limit}-byte ceiling (possible bomb)`);
|
|
89
|
+
}
|
|
90
|
+
if (this.#length === this.#buf.length) {
|
|
91
|
+
const grown = new Uint8Array(Math.min(Math.max(this.#buf.length * 2, MAX_CHUNK_DECOMPRESSED), this.#limit));
|
|
92
|
+
grown.set(this.#buf);
|
|
93
|
+
this.#buf = grown;
|
|
94
|
+
}
|
|
95
|
+
this.#buf[this.#length++] = byte;
|
|
96
|
+
}
|
|
97
|
+
pushAll(bytes) {
|
|
98
|
+
for (const byte of bytes)
|
|
99
|
+
this.push(byte);
|
|
100
|
+
}
|
|
101
|
+
bytes() {
|
|
102
|
+
return this.#buf.slice(0, this.#length);
|
|
103
|
+
}
|
|
65
104
|
}
|
|
66
105
|
export function compressContainer(data) {
|
|
67
|
-
const out =
|
|
106
|
+
const out = new ByteSink();
|
|
107
|
+
out.push(0x01);
|
|
68
108
|
for (let start = 0; start < data.length; start += MAX_CHUNK_DECOMPRESSED) {
|
|
69
109
|
const chunk = data.subarray(start, Math.min(start + MAX_CHUNK_DECOMPRESSED, data.length));
|
|
70
110
|
const tokens = compressChunk(chunk);
|
|
71
111
|
const compressed = tokens.length < chunk.length;
|
|
72
112
|
const body = compressed ? tokens : chunk;
|
|
73
113
|
const header = (compressed ? CHUNK_COMPRESSED_FLAG : 0) | CHUNK_SIGNATURE | ((body.length - 1) & 0x0fff);
|
|
74
|
-
out.push(header & 0xff
|
|
75
|
-
|
|
76
|
-
|
|
114
|
+
out.push(header & 0xff);
|
|
115
|
+
out.push((header >> 8) & 0xff);
|
|
116
|
+
out.pushAll(body);
|
|
77
117
|
}
|
|
78
|
-
return
|
|
118
|
+
return out.bytes();
|
|
79
119
|
}
|
|
80
120
|
function compressChunk(chunk) {
|
|
81
|
-
const tokens =
|
|
121
|
+
const tokens = new ByteSink();
|
|
122
|
+
const heads = new Int32Array(1 << HASH_BITS).fill(NO_CANDIDATE);
|
|
123
|
+
const prev = new Int32Array(MAX_CHUNK_DECOMPRESSED).fill(NO_CANDIDATE);
|
|
124
|
+
const remember = (at) => {
|
|
125
|
+
if (at + 2 >= chunk.length)
|
|
126
|
+
return;
|
|
127
|
+
const bucket = hash3(chunk, at);
|
|
128
|
+
prev[at] = heads[bucket];
|
|
129
|
+
heads[bucket] = at;
|
|
130
|
+
};
|
|
82
131
|
let pos = 0;
|
|
83
132
|
while (pos < chunk.length) {
|
|
84
133
|
const flagIndex = tokens.length;
|
|
@@ -90,42 +139,49 @@ function compressChunk(chunk) {
|
|
|
90
139
|
const windowStart = Math.max(0, pos - (1 << bitCount));
|
|
91
140
|
let bestLength = 0;
|
|
92
141
|
let bestOffset = 0;
|
|
93
|
-
|
|
142
|
+
let candidate = pos + 2 < chunk.length ? heads[hash3(chunk, pos)] : NO_CANDIDATE;
|
|
143
|
+
for (let tried = 0; candidate >= windowStart && tried < MAX_MATCH_CANDIDATES; tried++) {
|
|
94
144
|
let len = 0;
|
|
95
145
|
while (len < maxLength &&
|
|
96
146
|
pos + len < chunk.length &&
|
|
97
|
-
chunk[
|
|
147
|
+
chunk[candidate + len] === chunk[pos + len]) {
|
|
98
148
|
len++;
|
|
99
149
|
}
|
|
100
150
|
if (len > bestLength) {
|
|
101
151
|
bestLength = len;
|
|
102
|
-
bestOffset = pos -
|
|
152
|
+
bestOffset = pos - candidate;
|
|
103
153
|
if (bestLength === maxLength)
|
|
104
154
|
break;
|
|
105
155
|
}
|
|
156
|
+
candidate = prev[candidate];
|
|
106
157
|
}
|
|
107
158
|
if (bestLength >= 3) {
|
|
108
159
|
const token = ((bestOffset - 1) << (16 - bitCount)) | (bestLength - 3);
|
|
109
|
-
tokens.push(token & 0xff
|
|
160
|
+
tokens.push(token & 0xff);
|
|
161
|
+
tokens.push((token >> 8) & 0xff);
|
|
110
162
|
flags |= 1 << bit;
|
|
163
|
+
for (let i = 0; i < bestLength; i++)
|
|
164
|
+
remember(pos + i);
|
|
111
165
|
pos += bestLength;
|
|
112
166
|
}
|
|
113
167
|
else {
|
|
114
168
|
tokens.push(chunk[pos]);
|
|
169
|
+
remember(pos);
|
|
115
170
|
pos++;
|
|
116
171
|
}
|
|
117
172
|
}
|
|
118
|
-
tokens
|
|
173
|
+
tokens.set(flagIndex, flags);
|
|
119
174
|
}
|
|
120
|
-
return tokens;
|
|
175
|
+
return tokens.bytes();
|
|
121
176
|
}
|
|
122
|
-
function
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
177
|
+
function hash3(chunk, pos) {
|
|
178
|
+
const first = chunk[pos];
|
|
179
|
+
const second = chunk[pos + 1];
|
|
180
|
+
const third = chunk[pos + 2];
|
|
181
|
+
return ((first << 8) ^ (second << 4) ^ third) & ((1 << HASH_BITS) - 1);
|
|
126
182
|
}
|
|
127
183
|
function copyTokenHelp(decompressedSoFar) {
|
|
128
|
-
const bitCount = Math.max(Math.ceil(Math.log2(Math.max(decompressedSoFar, 1))), 4);
|
|
184
|
+
const bitCount = Math.min(Math.max(Math.ceil(Math.log2(Math.max(decompressedSoFar, 1))), 4), Math.log2(MAX_CHUNK_DECOMPRESSED));
|
|
129
185
|
const lengthMask = 0xffff >> bitCount;
|
|
130
186
|
return { lengthMask, bitCount };
|
|
131
187
|
}
|