Skip to content

Commit 4ca68bd

Browse files
committed
Fix issue #38: Use runtime's native DecompressionStream to inflate zip streams.
1 parent 5bf583c commit 4ca68bd

File tree

4 files changed

+48
-15
lines changed

4 files changed

+48
-15
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,16 @@ All notable changes to this project will be documented in this file.
77
### Added
88

99
- archive: Support DEFLATE in Zipper where JS implementations support it in CompressionStream.
10+
[Issue #40](https://github.com/codedread/bitjs/issues/40)
11+
- archive: Support DEFLATE in Unzipper where JS implementations support it in DecompressionStream.
12+
[Issue #38](https://github.com/codedread/bitjs/issues/38)
1013
- file: Added detection of GZIP files.
1114
- io: Added a skip() method to BitStream to match ByteStream.
1215

16+
### Fixed
17+
18+
- Fixed a benign JS error in the Web Worker wrapper
19+
1320
## [1.2.1] - 2024-01-19
1421

1522
### Added

README.md

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,13 @@ A set of dependency-free JavaScript modules to handle binary data in JS (using
88
[Typed Arrays](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray)).
99
Includes:
1010

11-
* bitjs/archive: Unarchiving files (unzip, unrar, untar) in JavaScript,
12-
implemented as Web Workers where supported, and allowing progressive
13-
unarchiving while streaming.
14-
* bitjs/codecs: Get the codec info of media containers in a ISO RFC6381
15-
MIME type string
11+
* bitjs/archive: Decompressing files (unzip, unrar, untar) in JavaScript, implemented as Web
12+
Workers where supported, and allowing progressive unarchiving while streaming.
13+
* bitjs/codecs: Get the codec info of media containers in a ISO RFC6381 MIME type string.
1614
* bitjs/file: Detect the type of file from its binary signature.
1715
* bitjs/image: Parsing GIF, JPEG, PNG. Conversion of WebP to PNG or JPEG.
18-
* bitjs/io: Low-level classes for interpreting binary data (BitStream
19-
ByteStream). For example, reading or peeking at N bits at a time.
16+
* bitjs/io: Low-level classes for interpreting binary data (BitStream, ByteStream). For example,
17+
reading or peeking at N bits at a time.
2018

2119
## Installation
2220

archive/decompress.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,3 +316,22 @@ export function getUnarchiver(ab, options = {}) {
316316
}
317317
return unarchiver;
318318
}
319+
320+
// import * as fs from 'node:fs';
321+
// async function main() {
322+
// const nodeBuf = fs.readFileSync(`./action-1.cbz`);
323+
// const ab = nodeBuf.buffer.slice(nodeBuf.byteOffset, nodeBuf.byteOffset + nodeBuf.length);
324+
// const then = Date.now();
325+
// const zipper = new Unzipper(ab, {debug: true});
326+
// zipper.addEventListener('extract', evt => {
327+
// const f = evt.unarchivedFile;
328+
// fs.writeFileSync(f.filename, Buffer.from(f.fileData));
329+
// });
330+
// zipper.addEventListener('finish', evt => {
331+
// console.dir(evt);
332+
// console.log(`Took ${(Date.now() - then)}ms`);
333+
// });
334+
// await zipper.start();
335+
// }
336+
337+
// main();

archive/unzip.js

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ class ZipLocalFile {
178178
}
179179

180180
// determine what kind of compressed data we have and decompress
181-
unzip() {
181+
async unzip() {
182182
if (!this.fileData) {
183183
err('unzip() called on a file with out compressed file data');
184184
}
@@ -196,7 +196,7 @@ class ZipLocalFile {
196196
if (logToConsole) {
197197
info(`ZIP v2.0, DEFLATE: ${this.filename} (${this.compressedSize} bytes)`);
198198
}
199-
this.fileData = inflate(this.fileData, this.uncompressedSize);
199+
this.fileData = await inflate(this.fileData, this.uncompressedSize);
200200
}
201201
else {
202202
err(`UNSUPPORTED VERSION/FORMAT: ZIP v${this.version}, ` +
@@ -483,9 +483,18 @@ function inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer) {
483483
* Compression method 8. Deflate: http://tools.ietf.org/html/rfc1951
484484
* @param {Uint8Array} compressedData A Uint8Array of the compressed file data.
485485
* @param {number} numDecompressedBytes
486-
* @returns {Uint8Array} The decompressed array.
486+
* @returns {Promise<Uint8Array>} The decompressed array.
487487
*/
488-
function inflate(compressedData, numDecompressedBytes) {
488+
async function inflate(compressedData, numDecompressedBytes) {
489+
// Try to use native implementation of DEFLATE if it exists.
490+
try {
491+
const blob = new Blob([compressedData.buffer]);
492+
const decompressedStream = blob.stream().pipeThrough(new DecompressionStream('deflate-raw'));
493+
return new Uint8Array(await new Response(decompressedStream).arrayBuffer());
494+
} catch (err) {
495+
// Fall through to non-native implementation of DEFLATE.
496+
}
497+
489498
// Bit stream representing the compressed data.
490499
/** @type {BitStream} */
491500
const bstream = new BitStream(compressedData.buffer,
@@ -596,7 +605,7 @@ function inflate(compressedData, numDecompressedBytes) {
596605
return buffer.data;
597606
}
598607

599-
function archiveUnzip() {
608+
async function archiveUnzip() {
600609
let bstream = bytestream.tee();
601610

602611
// loop until we don't see any more local files or we find a data descriptor.
@@ -619,7 +628,7 @@ function archiveUnzip() {
619628
currentBytesUnarchivedInFile = 0;
620629

621630
// Actually do the unzipping.
622-
oneLocalFile.unzip();
631+
await oneLocalFile.unzip();
623632

624633
if (oneLocalFile.fileData != null) {
625634
hostPort.postMessage({ type: 'extract', unarchivedFile: oneLocalFile }, [oneLocalFile.fileData.buffer]);
@@ -724,7 +733,7 @@ function archiveUnzip() {
724733

725734
// event.data.file has the first ArrayBuffer.
726735
// event.data.bytes has all subsequent ArrayBuffers.
727-
const onmessage = function (event) {
736+
const onmessage = async function (event) {
728737
const bytes = event.data.file || event.data.bytes;
729738
logToConsole = !!event.data.logToConsole;
730739

@@ -755,7 +764,7 @@ const onmessage = function (event) {
755764
if (unarchiveState === UnarchiveState.UNARCHIVING ||
756765
unarchiveState === UnarchiveState.WAITING) {
757766
try {
758-
archiveUnzip();
767+
await archiveUnzip();
759768
} catch (e) {
760769
if (typeof e === 'string' && e.startsWith('Error! Overflowed')) {
761770
// Overrun the buffer.

0 commit comments

Comments
 (0)