diff --git a/.c8rc b/.c8rc deleted file mode 100644 index c1becbc..0000000 --- a/.c8rc +++ /dev/null @@ -1,23 +0,0 @@ -{ - "check-coverage": true, - "all": true, - "include": [ - "archive/*.js", - "codecs/*.js", - "image/parsers/*.js", - "file/*.js", - "io/*.js" - ], - "exclude": [ - "archive/archive.js", - "archive/webworker-wrapper.js", - "io/*-worker.js" - ], - "reporter": [ - "html", - "lcov", - "text", - "text-summary" - ], - "report-dir": "coverage" -} diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml deleted file mode 100644 index decf3c2..0000000 --- a/.github/workflows/node.js.yml +++ /dev/null @@ -1,30 +0,0 @@ -# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions - -name: Node.js CI - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - build: - - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [19.x, 20.x, 21.x] - # See NodeJS release schedule at https://nodejs.org/en/about/previous-releases. - - steps: - - uses: actions/checkout@v4 - - name: Use NodeJS ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - - run: npm ci - - run: npm run build --if-present - - run: npm test diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 3077f7a..0000000 --- a/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.DS_Store -coverage -node_modules diff --git a/Archive.md b/Archive.md new file mode 100644 index 0000000..1433e38 --- /dev/null +++ b/Archive.md @@ -0,0 +1,46 @@ +# Introduction # + +The plan is to migrate decode.js, unzip.js, unrar.js and untar.js from kthoom into BitJS. The code is a little messy right now and not very easily extractable in library form, so the following is a design proposal for the API. + +# Details # + +This code will live in the namespace: **bitjs.archive** + +## UnarchiveEvent ## + +The first thing to note is that decode.js currently depends on being in its own worker thread. Even though this is probably the preferred means of using the unarchiving code, I'd really like the code to not depend upon this (i.e. it should be possible to untar in the main window JS context). + +The right way to handle this is to define events (**bitjs.archive.UnarchiveEvent**) that the unarchiving code sends out to listeners. Some UnarchiveEvent types: + + * Start Event + * Progress Event - sends progress update information + * Extract Event - sends the extracted file + * Finish Event + * Error Event + +Client code would then listen for unarchiving events and act accordingly. For example, the event listeners in a [web worker](http://dev.w3.org/html5/workers/) could postMessage(). + +## UnarchivedFile ## + +We'll define a **bits.archive.UnarchivedFile** object that has the following two properites: + + * filename - returns the full filename + * fileData - returns a TypedArray representing the file's binary contents + +These objects will be returned in Extracted File Events to the client code's listener. + +## Unarchiver ## + +We will define a base abstract class called **bitjs.archive.Unarchiver**: + + * the constructor accepts a [Typed Array](http://www.khronos.org/registry/typedarray/specs/latest/) + * an **addEventListener()** method + * a **removeEventListener()** method + * a **run()** method, which will start the unarchive process and return immediately + * an abstract **run()** method which must be implemented by a subclass + +## Unzipper, Unrarrer, Untarrer ## + +We will have three concrete subclasses of Unarchiver, one for each supported file format: zip, rar, tar that will each live in separate files. This helps save on JS load time/memory, since the web application only needs to include the files it needs. + +**NOTE**: kthoom's file sniffing will necessarily have to be done first (using a bitjs.io stream, naturally) to determine which Unarchiver subclass to create and kick off. \ No newline at end of file diff --git a/BitStream.md b/BitStream.md new file mode 100644 index 0000000..2a04b8f --- /dev/null +++ b/BitStream.md @@ -0,0 +1,7 @@ +# Introduction # + +TODO: Add content here. + +# Details # + +TODO: Add content here. \ No newline at end of file diff --git a/ByteBuffer.md b/ByteBuffer.md new file mode 100644 index 0000000..2a04b8f --- /dev/null +++ b/ByteBuffer.md @@ -0,0 +1,7 @@ +# Introduction # + +TODO: Add content here. + +# Details # + +TODO: Add content here. \ No newline at end of file diff --git a/ByteStream.md b/ByteStream.md new file mode 100644 index 0000000..2a04b8f --- /dev/null +++ b/ByteStream.md @@ -0,0 +1,7 @@ +# Introduction # + +TODO: Add content here. + +# Details # + +TODO: Add content here. \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index af74450..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,145 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -## [1.2.6] - 2026-03-18 - -### Added - -- codecs: Add support for ffprobe's "extradata" - -## [1.2.5] - 2025-10-05 - -### Fixed - -- archive: [Issue #52](https://github.com/codedread/bitjs/issues/47) Fixed a race condition in Zipper. - -## [1.2.4] - 2024-12-08 - -### Fixed - -- Fixed import error in unrar/rarvm. - -## [1.2.3] - 2024-02-04 - -### Added - -- archive: Support semantic methods for subscribing to unarchive events (onExtract), - [Issue #47](https://github.com/codedread/bitjs/issues/47). -- archive: Added Gunzipper to decompress gzip files. Only supported on runtimes that supported - DecompressionStream('gzip') for now. [Issue #48](https://github.com/codedread/bitjs/issues/48). -- io: Added a getData() method to ByteBuffer to retrieve a copy of the bytes that have been written. - -### Changed - -- archive: Unrarrer throws an explicit error when encountering RAR5 files. -- io: ByteBuffer.insertXXX() now throws an error if trying to write past the end of the buffer. - -## [1.2.2] - 2024-01-26 - -### Added - -- archive: Support DEFLATE in Zipper where JS implementations support it in CompressionStream. - [Issue #40](https://github.com/codedread/bitjs/issues/40) -- archive: Support DEFLATE in Unzipper where JS implementations support it in DecompressionStream. - [Issue #38](https://github.com/codedread/bitjs/issues/38) -- file: Added detection of GZIP files. -- io: Added a skip() method to BitStream to match ByteStream. - -### Fixed - -- Fixed a benign JS error in the Web Worker wrapper - -## [1.2.1] - 2024-01-19 - -### Added - -- image: Added PNG event-based parser (all critical and most ancillary chunks). - -### Changed - -- io: Fix ByteStream bug where skip(0) did not return the ByteStream. - -### Removed - -- image: Removed all custom parser Events and just use CustomEvent. - -## [1.2.0] - 2024-01-15 - -### Added - -- image: Added GIF and JPEG event-based parsers. -- io: Added a skip() method to ByteStream. - -## [1.1.7] - 2023-12-16 - -### Changed - -- archive: Enable unarchiving/archiving in NodeJS. -- Update unit test coverage and documentation. - -## [1.1.6] - 2023-10-25 - -### Changed - -- codecs: Special handling for mp3 streams inside mp4 containers. -- codecs: Handle ffprobe level -99 in mp4 files. - -## [1.1.5] - 2023-10-22 - -### Changed - -- codecs: Add support for HE-AAC profile in mp4a. - -## [1.1.4] - 2023-10-19 - -### Changed - -- codecs: Add support for DTS audio codec and AV1 video codec. -- codecs: Update how Matroska video/audio files are detected (video/x-matroska). - [Issue #43](https://github.com/codedread/bitjs/issues/43) -- untar: Fix long path/filenames in 'ustar' format. [Issue #42](https://github.com/codedread/bitjs/issues/43) - -## [1.1.3] - 2023-10-15 - -### Changed - -- codecs: Add support for WAV files to getShortMIMEString() and getFullMIMEString(). -- codecs: Fix support for AVI files in getFullMIMEString(). - -## [1.1.2] - 2023-09-30 - -### Changed - -- codecs: Handle m4a files as audio/mp4. - -## [1.1.1] - 2023-06-21 - -### Changed - -- Fix missing RarVM import in unrar.js. - -## [1.1.0] - 2023-05-28 - -### Added - -- Starter thinking around a Media API. - -### Changed - -- Change console.warn to a console.error when importing archive.js. - -### Removed - -- Removed build step for bitjs.io now that all browsers (Firefox 114+) support ES Module Workers. - -## [1.0.11] - 2023-02-15 - -### Added - -- Add sniffer support for the ICO format. -- Add unit test coverage via c8. - -### Fixed - -- Fixes for the audio/flac codec type. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 391c1e7..0000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,128 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, religion, or sexual identity -and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the - overall community - -Examples of unacceptable behavior include: - -* The use of sexualized language or imagery, and sexual attention or - advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email - address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -codedread at gmail dot com. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series -of actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or -permanent ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within -the community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.0, available at -https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. - -Community Impact Guidelines were inspired by [Mozilla's code of conduct -enforcement ladder](https://github.com/mozilla/diversity). - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations. diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 64656ff..0000000 --- a/LICENSE +++ /dev/null @@ -1,8 +0,0 @@ -The MIT License (MIT) -Copyright (c) 2011 Google Inc, antimatter15 - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/ProjectHome.md b/ProjectHome.md new file mode 100644 index 0000000..85b7e70 --- /dev/null +++ b/ProjectHome.md @@ -0,0 +1,29 @@ +## Introduction ## + +A set of tools to handle binary data in JS (using [Typed Arrays](http://www.khronos.org/registry/typedarray/specs/latest/)). + +## Example Usage ## + +### bitjs.io ### + +This namespace includes stream objects for reading and writing binary data at the bit and byte level: BitStream, ByteStream. + +``` +var bstream = new bitjs.io.BitStream(someArrayBuffer, true, offset, length); +var crc = bstream.readBits(12); // read in 12 bits as CRC, advancing the pointer +var flagbits = bstream.peekBits(6); // look ahead at next 6 bits, but do not advance the pointer +``` + +### bitjs.archive ### + +This namespace includes objects for unarchiving binary data in popular archive formats (zip, rar, tar) providing unzip, unrar and untar capabilities via JavaScript in the browser. The unarchive code depends on browser support of [Web Workers](http://dev.w3.org/html5/workers/). See the [design doc](http://code.google.com/p/bitjs/wiki/Archive). + +``` +function updateProgressBar(e) { ... update UI element ... } +function displayZipContents(e) { ... display contents of the extracted zip file ... } + +var unzipper = new bitjs.archive.Unzipper(zipFileArrayBuffer); +unzipper.addEventListener("progress", updateProgressBar); +unzipper.addEventListener("finish", displayZipContents); +unzipper.start(); +``` \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index e4bb0b8..0000000 --- a/README.md +++ /dev/null @@ -1,189 +0,0 @@ -[![Node.js CI](https://github.com/codedread/bitjs/actions/workflows/node.js.yml/badge.svg)](https://github.com/codedread/bitjs/actions/workflows/node.js.yml) - -# bitjs: Binary Tools for JavaScript - -## Introduction - -A set of dependency-free JavaScript modules to work with binary data in JS (using -[Typed Arrays](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray)). -Includes: - - * bitjs/archive: Decompressing files (unzip, unrar, untar, gunzip) in JavaScript, implemented as - Web Workers where supported, and allowing progressive unarchiving while streaming. - * bitjs/codecs: Get the codec info of media containers in a ISO RFC6381 MIME type string. - * bitjs/file: Detect the type of file from its binary signature. - * bitjs/image: Parsing GIF, JPEG, PNG. Conversion of WebP to PNG or JPEG. - * bitjs/io: Low-level classes for interpreting binary data (BitStream, ByteStream). For example, - reading or peeking at N bits at a time. - -## Installation - -Install it using your favourite package manager, the package is registered under `@codedread/bitjs`. -```bash -npm install @codedread/bitjs -``` -or -```bash -yarn add @codedread/bitjs -``` - -### CommonJS/ESM in Node - -This module is an ES Module. If your project uses CommonJS modules, it's a little trickier to use. -One example of this is if a TypeScript project compiles to CommonJS, it will try to turn imports -into require() statements, which will break. The fix for this (unfortunately) is to update your -tsconfig.json: - -```json - "moduleResolution": "Node16", -``` - -and use a Dynamic Import: - -```javascript -const { getFullMIMEString } = await import('@codedread/bitjs'); -``` - -## Packages - -### bitjs.archive - -This package includes objects for decompressing and compressing binary data in popular archive -formats (zip, rar, tar, gzip). Here is a simple example of unrar: - -#### Decompressing - -```javascript -import { Unrarrer } from './bitjs/archive/decompress.js'; -const unrar = new Unrarrer(rarFileArrayBuffer); -unrar.addEventListener('extract', (e) => { - const {filename, fileData} = e.unarchivedFile; - console.log(`Extracted ${filename} (${fileData.byteLength} bytes)`); - // Do something with fileData... -}); -unrar.addEventListener('finish', () => console.log('Done')); -unrar.start(); -``` - -More details and examples are located on [the API page](./docs/bitjs.archive.md). - -### bitjs.codecs - -This package includes code for dealing with media files (audio/video). It is useful for deriving -ISO RFC6381 MIME type strings, including the codec information. Currently supports a limited subset -of MP4 and WEBM. - -How to use: - * First, install ffprobe (ffmpeg) on your system. - * Then: -```javascript - -import { getFullMIMEString } from 'bitjs/codecs/codecs.js'; -/** - * @typedef {import('bitjs/codecs/codecs.js').ProbeInfo} ProbeInfo - */ - -const cmd = 'ffprobe -show_format -show_streams -print_format json -v quiet foo.mp4'; -exec(cmd, (error, stdout) => { - /** @type {ProbeInfo} */ - const info = JSON.parse(stdout); - // 'video/mp4; codecs="avc1.4D4028, mp4a.40.2"' - const contentType = getFullMIMEString(info); - ... -}); -``` - -### bitjs.file - -This package includes code for dealing with files. It includes a sniffer which detects the type of -file, given an ArrayBuffer. - -```javascript -import { findMimeType } from './bitjs/file/sniffer.js'; -const mimeType = findMimeType(someArrayBuffer); -``` - -### bitjs.image - -This package includes code for dealing with image files. It includes low-level, event-based -parsers for GIF, JPEG, and PNG images. - -It also includes a module for converting WebP images into -alternative raster graphics formats (PNG/JPG), though this latter module is deprecated, now that -WebP images are well-supported in all browsers. - -#### GIF Parser -```javascript -import { GifParser } from './bitjs/image/parsers/gif.js' - -const parser = new GifParser(someArrayBuffer); -parser.onApplicationExtension(evt => { - const appId = evt.detail.applicationIdentifier; - const appAuthCode = new TextDecoder().decode(evt.detail.applicationAuthenticationCode); - if (appId === 'XMP Data' && appAuthCode === 'XMP') { - /** @type {Uint8Array} */ - const appData = evt.detail.applicationData; - // Do something with appData (parse the XMP). - } -}); -parser.start(); -``` - -#### JPEG Parser -```javascript -import { JpegParser } from './bitjs/image/parsers/jpeg.js' -import { ExifTagNumber } from './bitjs/image/parsers/exif.js'; - -const parser = new JpegParser(someArrayBuffer) - .onApp1Exif(evt => console.log(evt.detail.get(ExifTagNumber.IMAGE_DESCRIPTION).stringValue)); -await parser.start(); -``` - -#### PNG Parser -```javascript -import { PngParser } from './bitjs/image/parsers/png.js' -import { ExifTagNumber } from './bitjs/image/parsers/exif.js'; - -const parser = new PngParser(someArrayBuffer); - .onExifProfile(evt => console.log(evt.detail.get(ExifTagNumber.IMAGE_DESCRIPTION).stringValue)) - .onTextualData(evt => console.dir(evt.detail)); -await parser.start(); -``` - -#### WebP Converter -```javascript -import { convertWebPtoPNG, convertWebPtoJPG } from './bitjs/image/webp-shim/webp-shim.js'; -// convertWebPtoPNG() takes in an ArrayBuffer containing the bytes of a WebP -// image and returns a Promise that resolves with an ArrayBuffer containing the -// bytes of an equivalent PNG image. -convertWebPtoPNG(webpBuffer).then(pngBuf => { - const pngUrl = URL.createObjectURL(new Blob([pngBuf], {type: 'image/png'})); - someImgElement.setAttribute(src, pngUrl); -}); -``` - -### bitjs.io - -This package includes stream objects for reading and writing binary data at the bit and byte level: -BitStream, ByteStream. - -```javascript -import { BitStream } from './bitjs/io/bitstream.js'; -const bstream = new BitStream(someArrayBuffer, true /** most-significant-bit-to-least */ ); -const crc = bstream.readBits(12); // Read in 12 bits as CRC. Advance pointer. -const flagbits = bstream.peekBits(6); // Look ahead at next 6 bits. Do not advance pointer. -``` - -More details and examples are located on [the API page](./docs/bitjs.io.md). - -## Reference - -* [UnRar](http://codedread.github.io/bitjs/docs/unrar.html): A work-in-progress description of the -RAR file format. - -## History - -This project grew out of another project of mine, [kthoom](https://github.com/codedread/kthoom) (a -comic book reader implemented in the browser). This repository was automatically exported from -[my original repository on GoogleCode](https://code.google.com/p/bitjs) and has undergone -considerable changes and improvements since then. diff --git a/archive/archive.js b/archive/archive.js deleted file mode 100644 index 7cfa3c7..0000000 --- a/archive/archive.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * archive.js - * - * Provides base functionality for unarchiving. - * DEPRECATED: Use decompress.js instead. - * - * Licensed under the MIT License - * - * Copyright(c) 2011 Google Inc. - */ - -// TODO(2.0): When up-revving to a major new version, remove this module. - -import { UnarchiveAppendEvent, UnarchiveErrorEvent, UnarchiveEvent, UnarchiveEventType, - UnarchiveExtractEvent, UnarchiveFinishEvent, UnarchiveInfoEvent, - UnarchiveProgressEvent, UnarchiveStartEvent } from './events.js'; -import { Unarchiver, Unzipper, Unrarrer, Untarrer, getUnarchiver } from './decompress.js'; - -export { - UnarchiveAppendEvent, - UnarchiveErrorEvent, - UnarchiveEvent, - UnarchiveEventType, - UnarchiveExtractEvent, - UnarchiveFinishEvent, - UnarchiveInfoEvent, - UnarchiveProgressEvent, - UnarchiveStartEvent, - Unarchiver, - Unzipper, Unrarrer, Untarrer, getUnarchiver -} - -console.error(`bitjs: Stop importing archive.js, this module will be removed. Import decompress.js instead.`); diff --git a/archive/common.js b/archive/common.js deleted file mode 100644 index b589041..0000000 --- a/archive/common.js +++ /dev/null @@ -1,78 +0,0 @@ -/** - * common.js - * - * Provides common definitions or functionality needed by multiple modules. - * - * Licensed under the MIT License - * - * Copyright(c) 2023 Google Inc. - */ - -/** - * @typedef FileInfo An object that is sent to the implementation representing a file to compress. - * @property {string} fileName The name of the file. TODO: Includes the path? - * @property {number} lastModTime The number of ms since the Unix epoch (1970-01-01 at midnight). - * @property {Uint8Array} fileData The bytes of the file. - */ - -/** - * @typedef Implementation - * @property {MessagePort} hostPort The port the host uses to communicate with the implementation. - * @property {Function} disconnectFn A function to call when the port has been disconnected. - */ - -/** - * Connects a host to a compress/decompress implementation via MessagePorts. The implementation must - * have an exported connect() function that accepts a MessagePort. If the runtime support Workers - * (e.g. web browsers, deno), imports the implementation inside a Web Worker. Otherwise, it - * dynamically imports the implementation inside the current JS context (node, bun). - * @param {string} implFilename The compressor/decompressor implementation filename relative to this - * path (e.g. './unzip.js'). - * @param {Function} disconnectFn A function to run when the port is disconnected. - * @returns {Promise} The Promise resolves to the Implementation, which includes the - * MessagePort connected to the implementation that the host should use. - */ -export async function getConnectedPort(implFilename) { - const messageChannel = new MessageChannel(); - const hostPort = messageChannel.port1; - const implPort = messageChannel.port2; - - if (typeof Worker === 'undefined') { - const implModule = await import(`${implFilename}`); - await implModule.connect(implPort); - return { - hostPort, - disconnectFn: () => implModule.disconnect(), - }; - } - - return new Promise((resolve, reject) => { - const workerScriptPath = new URL(`./webworker-wrapper.js`, import.meta.url).href; - const worker = new Worker(workerScriptPath, { type: 'module' }); - worker.postMessage({ implSrc: implFilename }, [implPort]); - resolve({ - hostPort, - disconnectFn: () => worker.postMessage({ disconnect: true }), - }); - }); -} - -// Zip-specific things. - -export const LOCAL_FILE_HEADER_SIG = 0x04034b50; -export const CENTRAL_FILE_HEADER_SIG = 0x02014b50; -export const END_OF_CENTRAL_DIR_SIG = 0x06054b50; -export const CRC32_MAGIC_NUMBER = 0xedb88320; -export const ARCHIVE_EXTRA_DATA_SIG = 0x08064b50; -export const DIGITAL_SIGNATURE_SIG = 0x05054b50; -export const END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50; -export const DATA_DESCRIPTOR_SIG = 0x08074b50; - -/** - * @readonly - * @enum {number} - */ -export const ZipCompressionMethod = { - STORE: 0, // Default. - DEFLATE: 8, // As per http://tools.ietf.org/html/rfc1951. -}; diff --git a/archive/compress.js b/archive/compress.js deleted file mode 100644 index 42ba89d..0000000 --- a/archive/compress.js +++ /dev/null @@ -1,259 +0,0 @@ -/** - * compress.js - * - * Provides base functionality for compressing. - * - * Licensed under the MIT License - * - * Copyright(c) 2023 Google Inc. - */ - -import { ZipCompressionMethod, getConnectedPort } from './common.js'; - -// TODO(2.0): Remove this comment. -// NOTE: THIS IS A WORK-IN-PROGRESS! THE API IS NOT FROZEN! USE AT YOUR OWN RISK! - -/** - * @typedef FileInfo An object that is sent to the implementation to represent a file to zip. - * @property {string} fileName The name of the file. TODO: Includes the path? - * @property {number} lastModTime The number of ms since the Unix epoch (1970-01-01 at midnight). - * @property {Uint8Array} fileData The bytes of the file. - */ - -/** The number of milliseconds to periodically send any pending files to the Worker. */ -const FLUSH_TIMER_MS = 50; - -/** - * Data elements are packed into bytes in order of increasing bit number within the byte, - * i.e., starting with the least-significant bit of the byte. - * Data elements other than Huffman codes are packed starting with the least-significant bit of the - * data element. - * Huffman codes are packed starting with the most-significant bit of the code. - */ - -/** - * @typedef CompressorOptions - * @property {ZipCompressionMethod} zipCompressionMethod - */ - -/** - * @readonly - * @enum {string} - */ -export const CompressStatus = { - NOT_STARTED: 'not_started', - READY: 'ready', - WORKING: 'working', - COMPLETE: 'complete', - ERROR: 'error', -}; - -// TODO: Extend EventTarget and introduce subscribe methods (onProgress, onInsert, onFinish, etc). - -/** - * A thing that zips files. - * NOTE: THIS IS A WORK-IN-PROGRESS! THE API IS NOT FROZEN! USE AT YOUR OWN RISK! - * TODO(2.0): Add semantic onXXX methods for an event-driven API. - */ -export class Zipper { - /** - * @type {Uint8Array} - * @private - */ - byteArray = new Uint8Array(0); - - /** - * The overall state of the Zipper. - * @type {CompressStatus} - * @private - */ - compressStatus_ = CompressStatus.NOT_STARTED; - // Naming of this property preserved for compatibility with 1.2.4-. - get compressState() { return this.compressStatus_; } - - /** - * The client-side port that sends messages to, and receives messages from the - * decompressor implementation. - * @type {MessagePort} - * @private - */ - port_; - - /** - * A function to call to disconnect the implementation from the host. - * @type {Function} - * @private - */ - disconnectFn_; - - /** - * A timer that periodically flushes pending files to the Worker. Set upon start() and stopped - * upon the last file being compressed by the Worker. - * @type {Number} - * @private - */ - flushTimer_ = 0; - - /** - * Whether the last files have been added by the client. - * @type {boolean} - * @private - */ - lastFilesReceived_ = false; - - /** - * The pending files to be sent to the Worker. - * @type {FileInfo[]} - * @private - */ - pendingFilesToSend_ = []; - - /** - * @param {CompressorOptions} options - */ - constructor(options) { - /** - * @type {CompressorOptions} - * @private - */ - this.zipOptions = options; - this.zipCompressionMethod = options.zipCompressionMethod || ZipCompressionMethod.STORE; - if (!Object.values(ZipCompressionMethod).includes(this.zipCompressionMethod)) { - throw `Compression method ${this.zipCompressionMethod} not supported`; - } - - if (this.zipCompressionMethod === ZipCompressionMethod.DEFLATE) { - // As per https://developer.mozilla.org/en-US/docs/Web/API/CompressionStream, NodeJS only - // supports deflate-raw from 21.2.0+ (Nov 2023). https://nodejs.org/en/blog/release/v21.2.0. - try { - new CompressionStream('deflate-raw'); - } catch (err) { - throw `CompressionStream with deflate-raw not supported by JS runtime: ${err}`; - } - } - } - - /** - * Must only be called on a Zipper that has been started. See start(). - * @param {FileInfo[]} files - * @param {boolean} isLastFile - */ - appendFiles(files, isLastFile = false) { - if (this.compressStatus_ === CompressStatus.NOT_STARTED) { - throw `appendFiles() called, but Zipper not started.`; - } - if (this.lastFilesReceived_) throw `appendFiles() called, but last file already received.`; - - this.lastFilesReceived_ = isLastFile; - this.pendingFilesToSend_.push(...files); - } - - /** - * Send in a set of files to be compressed. Set isLastFile to true if no more files are to be - * added in the future. The return Promise will not resolve until isLastFile is set to true either - * in this method or in an appendFiles() call. - * @param {FileInfo[]} files - * @param {boolean} isLastFile - * @returns {Promise} A Promise that will resolve once the final file has been sent. - * The Promise resolves to an array of bytes of the entire zipped archive. - */ - async start(files = [], isLastFile = false) { - if (this.compressStatus_ !== CompressStatus.NOT_STARTED) { - throw `start() called, but Zipper already started.`; - } - - // We optimize for the case where isLastFile=true in a start() call by posting to the Worker - // immediately upon async resolving below. Otherwise, we push these files into the pending set - // and rely on the flush timer to send them into the Worker. - if (!isLastFile) { - this.pendingFilesToSend_.push(...files); - this.flushTimer_ = setInterval(() => this.flushAnyPendingFiles_(), FLUSH_TIMER_MS); - } - this.compressStatus_ = CompressStatus.READY; - this.lastFilesReceived_ = isLastFile; - - // After this point, the function goes async, so appendFiles() may run before anything else in - // this function. - const impl = await getConnectedPort('./zip.js'); - this.port_ = impl.hostPort; - this.disconnectFn_ = impl.disconnectFn; - return new Promise((resolve, reject) => { - this.port_.onerror = (evt) => { - console.log('Impl error: message = ' + evt.message); - reject(evt.message); - }; - - this.port_.onmessage = (evt) => { - if (typeof evt.data == 'string') { - // Just log any strings the implementation pumps our way. - console.log(evt.data); - } else { - switch (evt.data.type) { - // Message sent back upon the first message the Worker receives, which may or may not - // have sent any files for compression, e.g. start([]). - case 'start': - this.compressStatus_ = CompressStatus.WORKING; - break; - // Message sent back when the last file has been compressed by the Worker. - case 'finish': - if (this.flushTimer_) { - clearInterval(this.flushTimer_); - this.flushTimer_ = 0; - } - this.compressStatus_ = CompressStatus.COMPLETE; - this.port_.close(); - this.disconnectFn_(); - this.port_ = null; - this.disconnectFn_ = null; - resolve(this.byteArray); - break; - // Message sent back when the Worker has written some bytes to the zip file. - case 'compress': - this.addBytes_(evt.data.bytes); - break; - } - } - }; - - // See note above about optimizing for the start(files, true) case. - if (isLastFile) { - this.port_.postMessage({ files, isLastFile, compressionMethod: this.zipCompressionMethod }); - } - }); - } - - /** - * Updates the internal byte array with new bytes (by allocating a new array and copying). - * @param {Uint8Array} newBytes - * @private - */ - addBytes_(newBytes) { - const oldArray = this.byteArray; - this.byteArray = new Uint8Array(oldArray.byteLength + newBytes.byteLength); - this.byteArray.set(oldArray); - this.byteArray.set(newBytes, oldArray.byteLength); - } - - /** - * Called internally by the async machinery to send any pending files to the Worker. This method - * sends at most one message to the Worker. - * @private - */ - flushAnyPendingFiles_() { - if (this.compressStatus_ === CompressStatus.NOT_STARTED) { - throw `flushAppendFiles_() called but Zipper not started.`; - } - // If the port is not initialized or we have no pending files, just return immediately and - // try again on the next flush. - if (!this.port_ || this.pendingFilesToSend_.length === 0) return; - - // Send all files to the worker. If we have received the last file, then let the Worker know. - this.port_.postMessage({ - files: this.pendingFilesToSend_, - isLastFile: this.lastFilesReceived_, - compressionMethod: this.zipCompressionMethod, - }); - // Release the memory from the browser's main thread. - this.pendingFilesToSend_ = []; - } -} diff --git a/archive/decompress.js b/archive/decompress.js deleted file mode 100644 index 3d7dcfb..0000000 --- a/archive/decompress.js +++ /dev/null @@ -1,398 +0,0 @@ -/** - * decompress.js - * - * Provides base functionality for unarchiving/decompression. - * - * Licensed under the MIT License - * - * Copyright(c) 2021 Google Inc. - */ - -import { UnarchiveAppendEvent, UnarchiveErrorEvent, UnarchiveEvent, UnarchiveEventType, - UnarchiveExtractEvent, UnarchiveFinishEvent, UnarchiveInfoEvent, - UnarchiveProgressEvent, UnarchiveStartEvent } from './events.js'; -import { getConnectedPort } from './common.js'; -import { findMimeType } from '../file/sniffer.js'; - -// Exported as a convenience (and also because this module used to contain these). -// TODO(2.0): Remove this export, since they have moved to events.js? -export { - UnarchiveAppendEvent, - UnarchiveErrorEvent, - UnarchiveEvent, - UnarchiveEventType, - UnarchiveExtractEvent, - UnarchiveFinishEvent, - UnarchiveInfoEvent, - UnarchiveProgressEvent, - UnarchiveStartEvent, -} - -/** - * All extracted files returned by an Unarchiver will implement - * the following interface: - * TODO: Move this interface into common.js? - */ - -/** - * @typedef UnarchivedFile - * @property {string} filename - * @property {Uint8Array} fileData - */ - -/** - * @typedef UnarchiverOptions - * @property {boolean=} debug Set to true for verbose unarchiver logging. - */ - -/** - * Base class for all Unarchivers. - */ -export class Unarchiver extends EventTarget { - /** - * The client-side port that sends messages to, and receives messages from, the - * decompressor implementation. - * @type {MessagePort} - * @private - */ - port_; - - /** - * A function to call to disconnect the implementation from the host. - * @type {Function} - * @private - */ - disconnectFn_; - - /** - * @param {ArrayBuffer} arrayBuffer The Array Buffer. Note that this ArrayBuffer must not be - * referenced once it is sent to the Unarchiver, since it is marked as Transferable and sent - * to the decompress implementation. - * @param {UnarchiverOptions|string} options An optional object of options, or a string - * representing where the BitJS files are located. The string version of this argument is - * deprecated. - */ - constructor(arrayBuffer, options = {}) { - super(); - - // TODO(2.0): Remove this. - if (typeof options === 'string') { - console.warn(`Deprecated: Don't send a raw string to Unarchiver()`); - console.warn(` send UnarchiverOptions instead.`); - options = { }; - } - - /** - * The ArrayBuffer object. - * @type {ArrayBuffer} - * @protected - */ - this.ab = arrayBuffer; - - /** - * @orivate - * @type {boolean} - */ - this.debugMode_ = !!(options.debug); - } - - /** - * Overridden so that the type hints for eventType are specific. Prefer onExtract(), etc. - * @param {'progress'|'extract'|'finish'} eventType - * @param {EventListenerOrEventListenerObject} listener - * @override - */ - addEventListener(eventType, listener) { - super.addEventListener(eventType, listener); - } - - /** - * Type-safe way to subscribe to an UnarchiveExtractEvent. - * @param {function(UnarchiveExtractEvent)} listener - * @returns {Unarchiver} for chaining. - */ - onExtract(listener) { - super.addEventListener(UnarchiveEventType.EXTRACT, listener); - return this; - } - - /** - * Type-safe way to subscribe to an UnarchiveFinishEvent. - * @param {function(UnarchiveFinishEvent)} listener - * @returns {Unarchiver} for chaining. - */ - onFinish(listener) { - super.addEventListener(UnarchiveEventType.FINISH, listener); - return this; - } - - /** - * Type-safe way to subscribe to an UnarchiveProgressEvent. - * @param {function(UnarchiveProgressEvent)} listener - * @returns {Unarchiver} for chaining. - */ - onProgress(listener) { - super.addEventListener(UnarchiveEventType.PROGRESS, listener); - return this; - } - - /** - * This method must be overridden by the subclass to return the script filename. - * @returns {string} The MIME type of the archive. - * @protected. - */ - getMIMEType() { - throw 'Subclasses of Unarchiver must overload getMIMEType()'; - } - - /** - * This method must be overridden by the subclass to return the script filename. - * @returns {string} The script filename. - * @protected. - */ - getScriptFileName() { - throw 'Subclasses of Unarchiver must overload getScriptFileName()'; - } - - /** - * Create an UnarchiveEvent out of the object sent back from the implementation. - * @param {Object} obj - * @returns {UnarchiveEvent} - * @private - */ - createUnarchiveEvent_(obj) { - switch (obj.type) { - case UnarchiveEventType.START: - return new UnarchiveStartEvent(); - case UnarchiveEventType.PROGRESS: - return new UnarchiveProgressEvent( - obj.currentFilename, - obj.currentFileNumber, - obj.currentBytesUnarchivedInFile, - obj.currentBytesUnarchived, - obj.totalUncompressedBytesInArchive, - obj.totalFilesInArchive, - obj.totalCompressedBytesRead); - case UnarchiveEventType.EXTRACT: - return new UnarchiveExtractEvent(obj.unarchivedFile); - case UnarchiveEventType.FINISH: - return new UnarchiveFinishEvent(obj.metadata); - case UnarchiveEventType.INFO: - return new UnarchiveInfoEvent(obj.msg); - case UnarchiveEventType.ERROR: - return new UnarchiveErrorEvent(obj.msg); - } - } - - /** - * Receive an event and pass it to the listener functions. - * @param {Object} obj - * @returns {boolean} Returns true if the decompression is finished. - * @private - */ - handlePortEvent_(obj) { - const type = obj.type; - if (type && Object.values(UnarchiveEventType).includes(type)) { - const evt = this.createUnarchiveEvent_(obj); - this.dispatchEvent(evt); - if (evt.type == UnarchiveEventType.FINISH) { - this.stop(); - return true; - } - } else { - console.log(`Unknown object received from port: ${obj}`); - } - return false; - } - - /** - * Starts the unarchive by connecting the ports and sending the first ArrayBuffer. - * @returns {Promise} A Promise that resolves when the decompression is complete. While the - * decompression is proceeding, you can send more bytes of the archive to the decompressor - * using the update() method. - */ - async start() { - const impl = await getConnectedPort(this.getScriptFileName()); - this.port_ = impl.hostPort; - this.disconnectFn_ = impl.disconnectFn; - return new Promise((resolve, reject) => { - this.port_.onerror = (evt) => { - console.log('Impl error: message = ' + evt.message); - reject(evt); - }; - - this.port_.onmessage = (evt) => { - if (typeof evt.data == 'string') { - // Just log any strings the implementation pumps our way. - console.log(evt.data); - } else { - if (this.handlePortEvent_(evt.data)) { - resolve(); - } - } - }; - - const ab = this.ab; - this.port_.postMessage({ - file: ab, - logToConsole: this.debugMode_, - }, [ab]); - this.ab = null; - }); - } - - // TODO(bitjs): Test whether ArrayBuffers must be transferred... - /** - * Adds more bytes to the unarchiver. - * @param {ArrayBuffer} ab The ArrayBuffer with more bytes in it. If opt_transferable is - * set to true, this ArrayBuffer must not be referenced after calling update(), since it - * is marked as Transferable and sent to the implementation. - * @param {boolean=} opt_transferable Optional boolean whether to mark this ArrayBuffer - * as a Tranferable object, which means it can no longer be referenced outside of - * the implementation context. - */ - update(ab, opt_transferable = false) { - const numBytes = ab.byteLength; - if (this.port_) { - // Send the ArrayBuffer over, and mark it as a Transferable object if necessary. - if (opt_transferable) { - this.port_.postMessage({ bytes: ab }, [ab]); - } else { - this.port_.postMessage({ bytes: ab }); - } - } - - this.dispatchEvent(new UnarchiveAppendEvent(numBytes)); - } - - /** - * Closes the port to the decompressor implementation and terminates it. - */ - stop() { - if (this.port_) { - this.port_.close(); - this.disconnectFn_(); - this.port_ = null; - this.disconnectFn_ = null; - } - } -} - -// Thin wrappers of decompressors for clients who want to construct a specific -// unarchiver themselves rather than use getUnarchiver(). -export class Unzipper extends Unarchiver { - /** - * @param {ArrayBuffer} ab - * @param {UnarchiverOptions} options - */ - constructor(ab, options = {}) { - super(ab, options); - } - - getMIMEType() { return 'application/zip'; } - getScriptFileName() { return './unzip.js'; } -} - -export class Unrarrer extends Unarchiver { - /** - * @param {ArrayBuffer} ab - * @param {UnarchiverOptions} options - */ - constructor(ab, options = {}) { - super(ab, options); - } - - getMIMEType() { return 'application/x-rar-compressed'; } - getScriptFileName() { return './unrar.js'; } -} - -export class Untarrer extends Unarchiver { - /** - * @param {ArrayBuffer} ab - * @param {UnarchiverOptions} options - */ - constructor(ab, options = {}) { - super(ab, options); - } - - getMIMEType() { return 'application/x-tar'; } - getScriptFileName() { return './untar.js'; }; -} - -/** - * IMPORTANT NOTES for Gunzipper: - * 1) A Gunzipper will only ever emit one EXTRACT event, because a gzipped file only ever contains - * a single file. - * 2) If the gzipped file does not include the original filename as a FNAME block, then the - * UnarchivedFile in the UnarchiveExtractEvent will not include a filename. It will be up to the - * client to re-assemble the filename (if needed). - * 3) update() is not supported on a Gunzipper, since the current implementation relies on runtime - * support for DecompressionStream('gzip') which can throw hard-to-detect errors reading only - * only part of a file. - * 4) PROGRESS events are not yet supported in Gunzipper. - */ -export class Gunzipper extends Unarchiver { - /** - * @param {ArrayBuffer} ab - * @param {UnarchiverOptions} options - */ - constructor(ab, options = {}) { - super(ab, options); - } - - getMIMEType() { return 'application/gzip'; } - getScriptFileName() { return './gunzip.js'; } -} - -// TODO(2.0): When up-revving to a major new version, remove the string type for options. - -/** - * Factory method that creates an unarchiver based on the byte signature found - * in the ArrayBuffer. - * @param {ArrayBuffer} ab The ArrayBuffer to unarchive. Note that this ArrayBuffer - * must not be referenced after calling this method, as the ArrayBuffer may be - * transferred to a different JS context once start() is called. - * @param {UnarchiverOptions|string} options An optional object of options, or a - * string representing where the path to the unarchiver script files. The latter - * is now deprecated (use UnarchiverOptions). - * @returns {Unarchiver} - */ -export function getUnarchiver(ab, options = {}) { - if (ab.byteLength < 10) { - return null; - } - - let unarchiver = null; - const mimeType = findMimeType(ab); - - if (mimeType === 'application/x-rar-compressed') { // Rar! - unarchiver = new Unrarrer(ab, options); - } else if (mimeType === 'application/zip') { // PK (Zip) - unarchiver = new Unzipper(ab, options); - } else if (mimeType === 'application/gzip') { // GZIP - unarchiver = new Gunzipper(ab, options); - } else { // Try with tar - unarchiver = new Untarrer(ab, options); - } - return unarchiver; -} - -// import * as fs from 'node:fs'; -// async function main() { -// const nodeBuf = fs.readFileSync(`./tests/archive-testfiles/archive-rar-store.rar`); -// const ab = nodeBuf.buffer.slice(nodeBuf.byteOffset, nodeBuf.byteOffset + nodeBuf.length); -// const then = Date.now(); -// const unarchiver = getUnarchiver(ab, {debug: true}) -// unarchiver.addEventListener('extract', evt => { -// console.dir(evt); -// const f = evt.unarchivedFile; -// fs.writeFileSync(f.filename, Buffer.from(f.fileData)); -// }); -// unarchiver.addEventListener('finish', evt => { -// console.dir(evt); -// console.log(`Took ${(Date.now() - then)}ms`); -// }); -// await unarchiver.start(); -// } - -// main(); diff --git a/archive/events.js b/archive/events.js deleted file mode 100644 index b7b582b..0000000 --- a/archive/events.js +++ /dev/null @@ -1,146 +0,0 @@ -/** - * events.js - * - * Licensed under the MIT License - * - * Copyright(c) 2023 Google Inc. - */ - -// TODO(2.0): Consider deprecating the Event subclasses here and: -// 1) Make @typedef structures in jsdoc for all the payloads -// 2) Use CustomEvent for payload event propagation -// 3) Add semantic methods to the archivers (onExtract, onProgress) like the image parsers. -// 4) Move everything into common.js ? - -/** - * The UnarchiveEvent types. - */ -export const UnarchiveEventType = { - START: 'start', - APPEND: 'append', - PROGRESS: 'progress', - EXTRACT: 'extract', - FINISH: 'finish', - INFO: 'info', - ERROR: 'error' -}; - -// TODO: Use CustomEvent and a @template and remove these boilerplate events. - -/** An unarchive event. */ -export class UnarchiveEvent extends Event { - /** - * @param {string} type The event type. - */ - constructor(type) { - super(type); - } -} - -/** Updates all Unarchiver listeners that an append has occurred. */ -export class UnarchiveAppendEvent extends UnarchiveEvent { - /** - * @param {number} numBytes The number of bytes appended. - */ - constructor(numBytes) { - super(UnarchiveEventType.APPEND); - - /** - * The number of appended bytes. - * @type {number} - */ - this.numBytes = numBytes; - } -} - -/** Useful for passing info up to the client (for debugging). */ -export class UnarchiveInfoEvent extends UnarchiveEvent { - /** - * @param {string} msg The info message. - */ - constructor(msg) { - super(UnarchiveEventType.INFO); - - /** - * The information message. - * @type {string} - */ - this.msg = msg; - } -} - -/** An unrecoverable error has occured. */ -export class UnarchiveErrorEvent extends UnarchiveEvent { - /** - * @param {string} msg The error message. - */ - constructor(msg) { - super(UnarchiveEventType.ERROR); - - /** - * The information message. - * @type {string} - */ - this.msg = msg; - } -} - -/** Start event. */ -export class UnarchiveStartEvent extends UnarchiveEvent { - constructor() { - super(UnarchiveEventType.START); - } -} - -/** Finish event. */ -export class UnarchiveFinishEvent extends UnarchiveEvent { - /** - * @param {Object} metadata A collection of metadata about the archive file. - */ - constructor(metadata = {}) { - super(UnarchiveEventType.FINISH); - this.metadata = metadata; - } -} - -// TODO(bitjs): Fully document these. They are confusing. -/** Progress event. */ -export class UnarchiveProgressEvent extends UnarchiveEvent { - /** - * @param {string} currentFilename - * @param {number} currentFileNumber - * @param {number} currentBytesUnarchivedInFile - * @param {number} currentBytesUnarchived - * @param {number} totalUncompressedBytesInArchive - * @param {number} totalFilesInArchive - * @param {number} totalCompressedBytesRead - */ - constructor(currentFilename, currentFileNumber, currentBytesUnarchivedInFile, - currentBytesUnarchived, totalUncompressedBytesInArchive, totalFilesInArchive, - totalCompressedBytesRead) { - super(UnarchiveEventType.PROGRESS); - - this.currentFilename = currentFilename; - this.currentFileNumber = currentFileNumber; - this.currentBytesUnarchivedInFile = currentBytesUnarchivedInFile; - this.totalFilesInArchive = totalFilesInArchive; - this.currentBytesUnarchived = currentBytesUnarchived; - this.totalUncompressedBytesInArchive = totalUncompressedBytesInArchive; - this.totalCompressedBytesRead = totalCompressedBytesRead; - } -} - -/** Extract event. */ -export class UnarchiveExtractEvent extends UnarchiveEvent { - /** - * @param {UnarchivedFile} unarchivedFile - */ - constructor(unarchivedFile) { - super(UnarchiveEventType.EXTRACT); - - /** - * @type {UnarchivedFile} - */ - this.unarchivedFile = unarchivedFile; - } -} diff --git a/archive/gunzip.js b/archive/gunzip.js deleted file mode 100644 index 2f32631..0000000 --- a/archive/gunzip.js +++ /dev/null @@ -1,125 +0,0 @@ -/** - * gunzip.js - * - * Licensed under the MIT License - * - * Copyright(c) 2024 Google Inc. - * - * Reference Documentation: - * - * https://www.ietf.org/rfc/rfc1952.txt - */ - -import { BitStream } from '../io/bitstream.js'; -import { ByteStream } from '../io/bytestream.js'; - -/** @type {MessagePort} */ -let hostPort; - -/** @type {ByteStream} */ -let bstream = null; -// undefined unless a FNAME block is present. -let filename; - -const err = str => hostPort.postMessage({ type: 'error', msg: str }); - -async function gunzip() { - const sig = bstream.readBytes(2); - if (sig[0] !== 0x1F || sig[1] !== 0x8B) { - const errMsg = `First two bytes not 0x1F, 0x8B: ${sig[0].toString(16)} ${sig[1].toString(16)}`; - err(errMsg); - return; - } - const compressionMethod = bstream.readNumber(1); - if (compressionMethod !== 8) { - const errMsg = `Compression method ${compressionMethod} not supported`; - err(errMsg); - return; - } - - // Parse the GZIP header to see if we can find a filename (FNAME block). - const flags = new BitStream(bstream.readBytes(1).buffer); - flags.skip(1); // skip FTEXT bit - const fhcrc = flags.readBits(1); - const fextra = flags.readBits(1); - const fname = flags.readBits(1); - const fcomment = flags.readBits(1); - - bstream.skip(4); // MTIME - bstream.skip(1); // XFL - bstream.skip(1); // OS - - if (fextra) { - const xlen = bstream.readNumber(2); - bstream.skip(xlen); - } - - if (fname) { - // Find the null-terminator byte. - let numBytes = 0; - const findNull = bstream.tee(); - while (findNull.readNumber(1) !== 0) numBytes++; - filename = bstream.readString(numBytes); - } - - if (fcomment) { - // Find the null-terminator byte. - let numBytes = 0; - const findNull = bstream.tee(); - while (findNull.readNumber(1) !== 0) numBytes++; - bstream.skip(numBytes); // COMMENT - } - - if (fhcrc) { - bstream.readNumber(2); // CRC16 - } - - // Now try to use native implementation of INFLATE, if supported by the runtime. - const blob = new Blob([bstream.bytes.buffer]); - const decompressedStream = blob.stream().pipeThrough(new DecompressionStream('gzip')); - const fileData = new Uint8Array(await new Response(decompressedStream).arrayBuffer()); - const unarchivedFile = { filename, fileData }; - hostPort.postMessage({ type: 'extract', unarchivedFile }, [fileData.buffer]); - - // TODO: Supported chunked decompression? - // TODO: Fall through to non-native implementation via inflate() ? - - hostPort.postMessage({ type: 'finish', metadata: {} }); -} - -// event.data.file has the first ArrayBuffer. -const onmessage = async function (event) { - const bytes = event.data.file; - - if (!bstream) { - bstream = new ByteStream(bytes); - bstream.setLittleEndian(true); - } else { - throw `Gunzipper does not calling update() with more bytes. Send the whole file with start().` - } - - await gunzip(); -}; - -/** - * Connect the host to the gunzip implementation with the given MessagePort. - * @param {MessagePort} port - */ -export function connect(port) { - if (hostPort) { - throw `connect(): hostPort already connected in gunzip.js`; - } - - hostPort = port; - port.onmessage = onmessage; -} - -export function disconnect() { - if (!hostPort) { - throw `disconnect(): hostPort was not connected in gunzip.js`; - } - - hostPort = null; - bstream = null; - filename = undefined; -} diff --git a/archive/inflate.js b/archive/inflate.js deleted file mode 100644 index c0f0ab3..0000000 --- a/archive/inflate.js +++ /dev/null @@ -1,410 +0,0 @@ -/** - * inflate.js - * - * Licensed under the MIT License - * - * Copyright(c) 2024 Google Inc. - * - * Implementation of INFLATE. Uses DecompressionStream, if the runtime supports it, otherwise uses - * an implementation purely in JS. - * - * Reference Documentation: - * - * DEFLATE format: http://tools.ietf.org/html/rfc1951 - */ - -import { BitStream } from '../io/bitstream.js'; -import { ByteBuffer } from '../io/bytebuffer.js'; - -/** - * @typedef SymbolLengthPair - * @property {number} length - * @property {number} symbol - */ - -/** - * Returns a table of Huffman codes. Each entry's key is its code and its value is a JavaScript - * object containing {length: 6, symbol: X}. - * @param {number[]} bitLengths An array representing the bit lengths of the codes, in order. - * See section 3.2.2 of https://datatracker.ietf.org/doc/html/rfc1951. - * @returns {Map} - */ -function getHuffmanCodes(bitLengths) { - // ensure bitLengths is an array containing at least one element - if (typeof bitLengths != typeof [] || bitLengths.length < 1) { - err('Error! getHuffmanCodes() called with an invalid array'); - return null; - } - - // Reference: http://tools.ietf.org/html/rfc1951#page-8 - const numLengths = bitLengths.length; - const bl_count = []; - let MAX_BITS = 1; - - // Step 1: count up how many codes of each length we have - for (let i = 0; i < numLengths; ++i) { - const length = bitLengths[i]; - // test to ensure each bit length is a positive, non-zero number - if (typeof length != typeof 1 || length < 0) { - err(`bitLengths contained an invalid number in getHuffmanCodes(): ${length} of type ${typeof length}`); - return null; - } - // increment the appropriate bitlength count - if (bl_count[length] == undefined) bl_count[length] = 0; - // a length of zero means this symbol is not participating in the huffman coding - if (length > 0) bl_count[length]++; - if (length > MAX_BITS) MAX_BITS = length; - } - - // Step 2: Find the numerical value of the smallest code for each code length - const next_code = []; - let code = 0; - for (let bits = 1; bits <= MAX_BITS; ++bits) { - const length = bits - 1; - // ensure undefined lengths are zero - if (bl_count[length] == undefined) bl_count[length] = 0; - code = (code + bl_count[bits - 1]) << 1; - next_code[bits] = code; - } - - // Step 3: Assign numerical values to all codes - /** @type Map */ - const table = new Map(); - for (let n = 0; n < numLengths; ++n) { - const len = bitLengths[n]; - if (len != 0) { - table.set(next_code[len], { length: len, symbol: n }); - next_code[len]++; - } - } - - return table; -} - -/* - The Huffman codes for the two alphabets are fixed, and are not - represented explicitly in the data. The Huffman code lengths - for the literal/length alphabet are: - - Lit Value Bits Codes - --------- ---- ----- - 0 - 143 8 00110000 through - 10111111 - 144 - 255 9 110010000 through - 111111111 - 256 - 279 7 0000000 through - 0010111 - 280 - 287 8 11000000 through - 11000111 -*/ -// fixed Huffman codes go from 7-9 bits, so we need an array whose index can hold up to 9 bits -let fixedHCtoLiteral = null; -let fixedHCtoDistance = null; -/** @returns {Map} */ -function getFixedLiteralTable() { - // create once - if (!fixedHCtoLiteral) { - const bitlengths = new Array(288); - for (let i = 0; i <= 143; ++i) bitlengths[i] = 8; - for (let i = 144; i <= 255; ++i) bitlengths[i] = 9; - for (let i = 256; i <= 279; ++i) bitlengths[i] = 7; - for (let i = 280; i <= 287; ++i) bitlengths[i] = 8; - - // get huffman code table - fixedHCtoLiteral = getHuffmanCodes(bitlengths); - } - return fixedHCtoLiteral; -} - -/** @returns {Map} */ -function getFixedDistanceTable() { - // create once - if (!fixedHCtoDistance) { - const bitlengths = new Array(32); - for (let i = 0; i < 32; ++i) { bitlengths[i] = 5; } - - // get huffman code table - fixedHCtoDistance = getHuffmanCodes(bitlengths); - } - return fixedHCtoDistance; -} - -/** - * Extract one bit at a time until we find a matching Huffman Code - * then return that symbol. - * @param {BitStream} bstream - * @param {Map} hcTable - * @returns {number} - */ -function decodeSymbol(bstream, hcTable) { - let code = 0; - let len = 0; - - // loop until we match - for (; ;) { - // read in next bit - const bit = bstream.readBits(1); - code = (code << 1) | bit; - ++len; - - // check against Huffman Code table and break if found - if (hcTable.has(code) && hcTable.get(code).length == len) { - break; - } - if (len > hcTable.length) { - err(`Bit stream out of sync, didn't find a Huffman Code, length was ${len} ` + - `and table only max code length of ${hcTable.length}`); - break; - } - } - return hcTable.get(code).symbol; -} - - -const CodeLengthCodeOrder = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; - -/* - Extra Extra Extra -Code Bits Length(s) Code Bits Lengths Code Bits Length(s) ----- ---- ------ ---- ---- ------- ---- ---- ------- - 257 0 3 267 1 15,16 277 4 67-82 - 258 0 4 268 1 17,18 278 4 83-98 - 259 0 5 269 2 19-22 279 4 99-114 - 260 0 6 270 2 23-26 280 4 115-130 - 261 0 7 271 2 27-30 281 5 131-162 - 262 0 8 272 2 31-34 282 5 163-194 - 263 0 9 273 3 35-42 283 5 195-226 - 264 0 10 274 3 43-50 284 5 227-257 - 265 1 11,12 275 3 51-58 285 0 258 - 266 1 13,14 276 3 59-66 -*/ -const LengthLookupTable = [ - [0, 3], [0, 4], [0, 5], [0, 6], - [0, 7], [0, 8], [0, 9], [0, 10], - [1, 11], [1, 13], [1, 15], [1, 17], - [2, 19], [2, 23], [2, 27], [2, 31], - [3, 35], [3, 43], [3, 51], [3, 59], - [4, 67], [4, 83], [4, 99], [4, 115], - [5, 131], [5, 163], [5, 195], [5, 227], - [0, 258] -]; - -/* - Extra Extra Extra - Code Bits Dist Code Bits Dist Code Bits Distance - ---- ---- ---- ---- ---- ------ ---- ---- -------- - 0 0 1 10 4 33-48 20 9 1025-1536 - 1 0 2 11 4 49-64 21 9 1537-2048 - 2 0 3 12 5 65-96 22 10 2049-3072 - 3 0 4 13 5 97-128 23 10 3073-4096 - 4 1 5,6 14 6 129-192 24 11 4097-6144 - 5 1 7,8 15 6 193-256 25 11 6145-8192 - 6 2 9-12 16 7 257-384 26 12 8193-12288 - 7 2 13-16 17 7 385-512 27 12 12289-16384 - 8 3 17-24 18 8 513-768 28 13 16385-24576 - 9 3 25-32 19 8 769-1024 29 13 24577-32768 -*/ -const DistLookupTable = [ - [0, 1], [0, 2], [0, 3], [0, 4], - [1, 5], [1, 7], - [2, 9], [2, 13], - [3, 17], [3, 25], - [4, 33], [4, 49], - [5, 65], [5, 97], - [6, 129], [6, 193], - [7, 257], [7, 385], - [8, 513], [8, 769], - [9, 1025], [9, 1537], - [10, 2049], [10, 3073], - [11, 4097], [11, 6145], - [12, 8193], [12, 12289], - [13, 16385], [13, 24577] -]; - -/** - * @param {BitStream} bstream - * @param {Map} hcLiteralTable - * @param {Map} hcDistanceTable - * @param {ByteBuffer} buffer - * @returns - */ -function inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer) { - /* - loop (until end of block code recognized) - decode literal/length value from input stream - if value < 256 - copy value (literal byte) to output stream - otherwise - if value = end of block (256) - break from loop - otherwise (value = 257..285) - decode distance from input stream - - move backwards distance bytes in the output - stream, and copy length bytes from this - position to the output stream. - */ - let blockSize = 0; - for (; ;) { - const symbol = decodeSymbol(bstream, hcLiteralTable); - if (symbol < 256) { - // copy literal byte to output - buffer.insertByte(symbol); - blockSize++; - } else { - // end of block reached - if (symbol == 256) { - break; - } else { - const lengthLookup = LengthLookupTable[symbol - 257]; - let length = lengthLookup[1] + bstream.readBits(lengthLookup[0]); - const distLookup = DistLookupTable[decodeSymbol(bstream, hcDistanceTable)]; - let distance = distLookup[1] + bstream.readBits(distLookup[0]); - - // now apply length and distance appropriately and copy to output - - // TODO: check that backward distance < data.length? - - // http://tools.ietf.org/html/rfc1951#page-11 - // "Note also that the referenced string may overlap the current - // position; for example, if the last 2 bytes decoded have values - // X and Y, a string reference with - // adds X,Y,X,Y,X to the output stream." - // - // loop for each character - let ch = buffer.ptr - distance; - blockSize += length; - if (length > distance) { - const data = buffer.data; - while (length--) { - buffer.insertByte(data[ch++]); - } - } else { - buffer.insertBytes(buffer.data.subarray(ch, ch + length)) - } - } // length-distance pair - } // length-distance pair or end-of-block - } // loop until we reach end of block - return blockSize; -} - -/** - * Compression method 8. Deflate: http://tools.ietf.org/html/rfc1951 - * @param {Uint8Array} compressedData A Uint8Array of the compressed file data. - * @param {number} numDecompressedBytes - * @returns {Promise} The decompressed array. - */ -export async function inflate(compressedData, numDecompressedBytes) { - // Try to use native implementation of DEFLATE if it exists. - try { - const blob = new Blob([compressedData.buffer]); - const decompressedStream = blob.stream().pipeThrough(new DecompressionStream('deflate-raw')); - return new Uint8Array(await new Response(decompressedStream).arrayBuffer()); - } catch (err) { - // Fall through to non-native implementation of DEFLATE. - } - - // Bit stream representing the compressed data. - /** @type {BitStream} */ - const bstream = new BitStream(compressedData.buffer, - false /* mtl */, - compressedData.byteOffset, - compressedData.byteLength); - /** @type {ByteBuffer} */ - const buffer = new ByteBuffer(numDecompressedBytes); - let blockSize = 0; - - // block format: http://tools.ietf.org/html/rfc1951#page-9 - let bFinal = 0; - do { - bFinal = bstream.readBits(1); - let bType = bstream.readBits(2); - blockSize = 0; - // no compression - if (bType == 0) { - // skip remaining bits in this byte - while (bstream.bitPtr != 0) bstream.readBits(1); - const len = bstream.readBits(16); - const nlen = bstream.readBits(16); - // TODO: check if nlen is the ones-complement of len? - if (len > 0) buffer.insertBytes(bstream.readBytes(len)); - blockSize = len; - } - // fixed Huffman codes - else if (bType == 1) { - blockSize = inflateBlockData(bstream, getFixedLiteralTable(), getFixedDistanceTable(), buffer); - } - // dynamic Huffman codes - else if (bType == 2) { - const numLiteralLengthCodes = bstream.readBits(5) + 257; - const numDistanceCodes = bstream.readBits(5) + 1; - const numCodeLengthCodes = bstream.readBits(4) + 4; - - // populate the array of code length codes (first de-compaction) - const codeLengthsCodeLengths = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - for (let i = 0; i < numCodeLengthCodes; ++i) { - codeLengthsCodeLengths[CodeLengthCodeOrder[i]] = bstream.readBits(3); - } - - // get the Huffman Codes for the code lengths - const codeLengthsCodes = getHuffmanCodes(codeLengthsCodeLengths); - - // now follow this mapping - /* - 0 - 15: Represent code lengths of 0 - 15 - 16: Copy the previous code length 3 - 6 times. - The next 2 bits indicate repeat length - (0 = 3, ... , 3 = 6) - Example: Codes 8, 16 (+2 bits 11), - 16 (+2 bits 10) will expand to - 12 code lengths of 8 (1 + 6 + 5) - 17: Repeat a code length of 0 for 3 - 10 times. - (3 bits of length) - 18: Repeat a code length of 0 for 11 - 138 times - (7 bits of length) - */ - // to generate the true code lengths of the Huffman Codes for the literal - // and distance tables together - const literalCodeLengths = []; - let prevCodeLength = 0; - const maxCodeLengths = numLiteralLengthCodes + numDistanceCodes; - while (literalCodeLengths.length < maxCodeLengths) { - const symbol = decodeSymbol(bstream, codeLengthsCodes); - if (symbol <= 15) { - literalCodeLengths.push(symbol); - prevCodeLength = symbol; - } else if (symbol === 16) { - let repeat = bstream.readBits(2) + 3; - while (repeat--) { - literalCodeLengths.push(prevCodeLength); - } - } else if (symbol === 17) { - let repeat = bstream.readBits(3) + 3; - while (repeat--) { - literalCodeLengths.push(0); - } - } else if (symbol == 18) { - let repeat = bstream.readBits(7) + 11; - while (repeat--) { - literalCodeLengths.push(0); - } - } - } - - // now split the distance code lengths out of the literal code array - const distanceCodeLengths = literalCodeLengths.splice(numLiteralLengthCodes, numDistanceCodes); - - // now generate the true Huffman Code tables using these code lengths - const hcLiteralTable = getHuffmanCodes(literalCodeLengths); - const hcDistanceTable = getHuffmanCodes(distanceCodeLengths); - blockSize = inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer); - } else { // error - err('Error! Encountered deflate block of type 3'); - return null; - } - } while (bFinal != 1); - // we are done reading blocks if the bFinal bit was set for this block - - // return the buffer data bytes - return buffer.data; -} diff --git a/archive/rarvm.js b/archive/rarvm.js deleted file mode 100644 index da0782f..0000000 --- a/archive/rarvm.js +++ /dev/null @@ -1,1011 +0,0 @@ -/** - * rarvm.js - * - * Licensed under the MIT License - * - * Copyright(c) 2017 Google Inc. - */ - -import { BitStream } from '../io/bitstream.js'; - -/** - * CRC Implementation. - */ -const CRCTab = new Array(256).fill(0); - -// Helper functions between signed and unsigned integers. - -/** - * -1 becomes 0xffffffff - */ -function fromSigned32ToUnsigned32(val) { - return (val < 0) ? (val += 0x100000000) : val; -} - -/** - * 0xffffffff becomes -1 - */ -function fromUnsigned32ToSigned32(val) { - return (val >= 0x80000000) ? (val -= 0x100000000) : val; -} - -/** - * -1 becomes 0xff - */ -function fromSigned8ToUnsigned8(val) { - return (val < 0) ? (val += 0x100) : val; -} - -/** - * 0xff becomes -1 - */ -function fromUnsigned8ToSigned8(val) { - return (val >= 0x80) ? (val -= 0x100) : val; -} - -function InitCRC() { - for (let i = 0; i < 256; ++i) { - let c = i; - for (let j = 0; j < 8; ++j) { - // Read http://stackoverflow.com/questions/6798111/bitwise-operations-on-32-bit-unsigned-ints - // for the bitwise operator issue (JS interprets operands as 32-bit signed - // integers and we need to deal with unsigned ones here). - c = ((c & 1) ? ((c >>> 1) ^ 0xEDB88320) : (c >>> 1)) >>> 0; - } - CRCTab[i] = c; - } -} - -/** - * @param {number} startCRC - * @param {Uint8Array} arr - * @returns {number} - */ -function CRC(startCRC, arr) { - if (CRCTab[1] == 0) { - InitCRC(); - } - -/* -#if defined(LITTLE_ENDIAN) && defined(PRESENT_INT32) && defined(ALLOW_NOT_ALIGNED_INT) - while (Size>0 && ((long)Data & 7)) - { - StartCRC=CRCTab[(byte)(StartCRC^Data[0])]^(StartCRC>>8); - Size--; - Data++; - } - while (Size>=8) - { - StartCRC^=*(uint32 *)Data; - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - StartCRC^=*(uint32 *)(Data+4); - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - Data+=8; - Size-=8; - } -#endif -*/ - - for (let i = 0; i < arr.length; ++i) { - const byte = ((startCRC ^ arr[i]) >>> 0) & 0xff; - startCRC = (CRCTab[byte] ^ (startCRC >>> 8)) >>> 0; - } - - return startCRC; -} - -// ============================================================================================== // - - -/** - * RarVM Implementation. - */ -export const VM_MEMSIZE = 0x40000; -export const VM_MEMMASK = (VM_MEMSIZE - 1); -export const VM_GLOBALMEMADDR = 0x3C000; -export const VM_GLOBALMEMSIZE = 0x2000; -export const VM_FIXEDGLOBALSIZE = 64; -export const MAXWINSIZE = 0x400000; -export const MAXWINMASK = (MAXWINSIZE - 1); - -/** - */ -const VM_Commands = { - VM_MOV: 0, - VM_CMP: 1, - VM_ADD: 2, - VM_SUB: 3, - VM_JZ: 4, - VM_JNZ: 5, - VM_INC: 6, - VM_DEC: 7, - VM_JMP: 8, - VM_XOR: 9, - VM_AND: 10, - VM_OR: 11, - VM_TEST: 12, - VM_JS: 13, - VM_JNS: 14, - VM_JB: 15, - VM_JBE: 16, - VM_JA: 17, - VM_JAE: 18, - VM_PUSH: 19, - VM_POP: 20, - VM_CALL: 21, - VM_RET: 22, - VM_NOT: 23, - VM_SHL: 24, - VM_SHR: 25, - VM_SAR: 26, - VM_NEG: 27, - VM_PUSHA: 28, - VM_POPA: 29, - VM_PUSHF: 30, - VM_POPF: 31, - VM_MOVZX: 32, - VM_MOVSX: 33, - VM_XCHG: 34, - VM_MUL: 35, - VM_DIV: 36, - VM_ADC: 37, - VM_SBB: 38, - VM_PRINT: 39, - -/* -#ifdef VM_OPTIMIZE - VM_MOVB, VM_MOVD, VM_CMPB, VM_CMPD, - - VM_ADDB, VM_ADDD, VM_SUBB, VM_SUBD, VM_INCB, VM_INCD, VM_DECB, VM_DECD, - VM_NEGB, VM_NEGD, -#endif -*/ - - // TODO: This enum value would be much larger if VM_OPTIMIZE. - VM_STANDARD: 40, -}; - -/** - */ -const VM_StandardFilters = { - VMSF_NONE: 0, - VMSF_E8: 1, - VMSF_E8E9: 2, - VMSF_ITANIUM: 3, - VMSF_RGB: 4, - VMSF_AUDIO: 5, - VMSF_DELTA: 6, - VMSF_UPCASE: 7, -}; - -/** - */ -const VM_Flags = { - VM_FC: 1, - VM_FZ: 2, - VM_FS: 0x80000000, -}; - -/** - */ -const VM_OpType = { - VM_OPREG: 0, - VM_OPINT: 1, - VM_OPREGMEM: 2, - VM_OPNONE: 3, -}; - -/** - * Finds the key that maps to a given value in an object. This function is useful in debugging - * variables that use the above enums. - * @param {Object} obj - * @param {number} val - * @returns {string} The key/enum value as a string. - */ -function findKeyForValue(obj, val) { - for (let key in obj) { - if (obj[key] === val) { - return key; - } - } - return null; -} - -function getDebugString(obj, val) { - let s = 'Unknown.'; - if (obj === VM_Commands) { - s = 'VM_Commands.'; - } else if (obj === VM_StandardFilters) { - s = 'VM_StandardFilters.'; - } else if (obj === VM_Flags) { - s = 'VM_OpType.'; - } else if (obj === VM_OpType) { - s = 'VM_OpType.'; - } - - return s + findKeyForValue(obj, val); -} - -/** - */ -class VM_PreparedOperand { - constructor() { - /** @type {VM_OpType} */ - this.Type; - - /** @type {number} */ - this.Data = 0; - - /** @type {number} */ - this.Base = 0; - - // TODO: In C++ this is a uint* - /** @type {Array} */ - this.Addr = null; - }; - - /** @returns {string} */ - toString() { - if (this.Type === null) { - return 'Error: Type was null in VM_PreparedOperand'; - } - return '{ ' - + 'Type: ' + getDebugString(VM_OpType, this.Type) - + ', Data: ' + this.Data - + ', Base: ' + this.Base - + ' }'; - } -} - -/** - */ -class VM_PreparedCommand { - constructor() { - /** @type {VM_Commands} */ - this.OpCode; - - /** @type {boolean} */ - this.ByteMode = false; - - /** @type {VM_PreparedOperand} */ - this.Op1 = new VM_PreparedOperand(); - - /** @type {VM_PreparedOperand} */ - this.Op2 = new VM_PreparedOperand(); - } - - /** @returns {string} */ - toString(indent) { - if (this.OpCode === null) { - return 'Error: OpCode was null in VM_PreparedCommand'; - } - indent = indent || ''; - return indent + '{\n' - + indent + ' OpCode: ' + getDebugString(VM_Commands, this.OpCode) + ',\n' - + indent + ' ByteMode: ' + this.ByteMode + ',\n' - + indent + ' Op1: ' + this.Op1.toString() + ',\n' - + indent + ' Op2: ' + this.Op2.toString() + ',\n' - + indent + '}'; - } -} - -/** - */ -class VM_PreparedProgram { - constructor() { - /** @type {Array} */ - this.Cmd = []; - - /** @type {Array} */ - this.AltCmd = null; - - /** @type {Uint8Array} */ - this.GlobalData = new Uint8Array(); - - /** @type {Uint8Array} */ - this.StaticData = new Uint8Array(); // static data contained in DB operators - - /** @type {Uint32Array} */ - this.InitR = new Uint32Array(7); - - /** - * A pointer to bytes that have been filtered by a program. - * @type {Uint8Array} - */ - this.FilteredData = null; - } - - /** @returns {string} */ - toString() { - let s = '{\n Cmd: [\n'; - for (let i = 0; i < this.Cmd.length; ++i) { - s += this.Cmd[i].toString(' ') + ',\n'; - } - s += '],\n'; - // TODO: Dump GlobalData, StaticData, InitR? - s += ' }\n'; - return s; - } -} - -/** - */ -export class UnpackFilter { - constructor() { - /** @type {number} */ - this.BlockStart = 0; - - /** @type {number} */ - this.BlockLength = 0; - - /** @type {number} */ - this.ExecCount = 0; - - /** @type {boolean} */ - this.NextWindow = false; - - // position of parent filter in Filters array used as prototype for filter - // in PrgStack array. Not defined for filters in Filters array. - /** @type {number} */ - this.ParentFilter = null; - - /** @type {VM_PreparedProgram} */ - this.Prg = new VM_PreparedProgram(); - } -} - -const VMCF_OP0 = 0; -const VMCF_OP1 = 1; -const VMCF_OP2 = 2; -const VMCF_OPMASK = 3; -const VMCF_BYTEMODE = 4; -const VMCF_JUMP = 8; -const VMCF_PROC = 16; -const VMCF_USEFLAGS = 32; -const VMCF_CHFLAGS = 64; - -const VM_CmdFlags = [ - /* VM_MOV */ VMCF_OP2 | VMCF_BYTEMODE , - /* VM_CMP */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_ADD */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_SUB */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_JZ */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_JNZ */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_INC */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_DEC */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_JMP */ VMCF_OP1 | VMCF_JUMP , - /* VM_XOR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_AND */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_OR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_TEST */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_JS */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_JNS */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_JB */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_JBE */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_JA */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_JAE */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_PUSH */ VMCF_OP1 , - /* VM_POP */ VMCF_OP1 , - /* VM_CALL */ VMCF_OP1 | VMCF_PROC , - /* VM_RET */ VMCF_OP0 | VMCF_PROC , - /* VM_NOT */ VMCF_OP1 | VMCF_BYTEMODE , - /* VM_SHL */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_SHR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_SAR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_NEG */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_PUSHA */ VMCF_OP0 , - /* VM_POPA */ VMCF_OP0 , - /* VM_PUSHF */ VMCF_OP0 | VMCF_USEFLAGS , - /* VM_POPF */ VMCF_OP0 | VMCF_CHFLAGS , - /* VM_MOVZX */ VMCF_OP2 , - /* VM_MOVSX */ VMCF_OP2 , - /* VM_XCHG */ VMCF_OP2 | VMCF_BYTEMODE , - /* VM_MUL */ VMCF_OP2 | VMCF_BYTEMODE , - /* VM_DIV */ VMCF_OP2 | VMCF_BYTEMODE , - /* VM_ADC */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS , - /* VM_SBB */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS , - /* VM_PRINT */ VMCF_OP0 , -]; - - -/** - */ -class StandardFilterSignature { - /** - * @param {number} length - * @param {number} crc - * @param {VM_StandardFilters} type - */ - constructor(length, crc, type) { - /** @type {number} */ - this.Length = length; - - /** @type {number} */ - this.CRC = crc; - - /** @type {VM_StandardFilters} */ - this.Type = type; - } -} - -/** - * @type {Array} - */ -const StdList = [ - new StandardFilterSignature(53, 0xad576887, VM_StandardFilters.VMSF_E8), - new StandardFilterSignature(57, 0x3cd7e57e, VM_StandardFilters.VMSF_E8E9), - new StandardFilterSignature(120, 0x3769893f, VM_StandardFilters.VMSF_ITANIUM), - new StandardFilterSignature(29, 0x0e06077d, VM_StandardFilters.VMSF_DELTA), - new StandardFilterSignature(149, 0x1c2c5dc8, VM_StandardFilters.VMSF_RGB), - new StandardFilterSignature(216, 0xbc85e701, VM_StandardFilters.VMSF_AUDIO), - new StandardFilterSignature(40, 0x46b9c560, VM_StandardFilters.VMSF_UPCASE), -]; - -/** - * @constructor - */ -export class RarVM { - constructor() { - /** @private {Uint8Array} */ - this.mem_ = null; - - /** @private {Uint32Array} */ - this.R_ = new Uint32Array(8); - - /** @private {number} */ - this.flags_ = 0; - } - - /** - * Initializes the memory of the VM. - */ - init() { - if (!this.mem_) { - this.mem_ = new Uint8Array(VM_MEMSIZE); - } - } - - /** - * @param {Uint8Array} code - * @returns {VM_StandardFilters} - */ - isStandardFilter(code) { - const codeCRC = (CRC(0xffffffff, code, code.length) ^ 0xffffffff) >>> 0; - for (let i = 0; i < StdList.length; ++i) { - if (StdList[i].CRC == codeCRC && StdList[i].Length == code.length) - return StdList[i].Type; - } - - return VM_StandardFilters.VMSF_NONE; - } - - /** - * @param {VM_PreparedOperand} op - * @param {boolean} byteMode - * @param {BitStream} bstream A rtl bit stream. - */ - decodeArg(op, byteMode, bstream) { - const data = bstream.peekBits(16); - if (data & 0x8000) { - op.Type = VM_OpType.VM_OPREG; // Operand is register (R[0]..R[7]) - bstream.readBits(1); // 1 flag bit and... - op.Data = bstream.readBits(3); // ... 3 register number bits - op.Addr = [this.R_[op.Data]] // TODO &R[Op.Data] // Register address - } else { - if ((data & 0xc000) == 0) { - op.Type = VM_OpType.VM_OPINT; // Operand is integer - bstream.readBits(2); // 2 flag bits - if (byteMode) { - op.Data = bstream.readBits(8); // Byte integer. - } else { - op.Data = RarVM.readData(bstream); // 32 bit integer. - } - } else { - // Operand is data addressed by register data, base address or both. - op.Type = VM_OpType.VM_OPREGMEM; - if ((data & 0x2000) == 0) { - bstream.readBits(3); // 3 flag bits - // Base address is zero, just use the address from register. - op.Data = bstream.readBits(3); // (Data>>10)&7 - op.Addr = [this.R_[op.Data]]; // TODO &R[op.Data] - op.Base = 0; - } else { - bstream.readBits(4); // 4 flag bits - if ((data & 0x1000) == 0) { - // Use both register and base address. - op.Data = bstream.readBits(3); - op.Addr = [this.R_[op.Data]]; // TODO &R[op.Data] - } else { - // Use base address only. Access memory by fixed address. - op.Data = 0; - } - op.Base = RarVM.readData(bstream); // Read base address. - } - } - } - } - - /** - * @param {VM_PreparedProgram} prg - */ - execute(prg) { - this.R_.set(prg.InitR); - - const globalSize = Math.min(prg.GlobalData.length, VM_GLOBALMEMSIZE); - if (globalSize) { - this.mem_.set(prg.GlobalData.subarray(0, globalSize), VM_GLOBALMEMADDR); - } - - const staticSize = Math.min(prg.StaticData.length, VM_GLOBALMEMSIZE - globalSize); - if (staticSize) { - this.mem_.set(prg.StaticData.subarray(0, staticSize), VM_GLOBALMEMADDR + globalSize); - } - - this.R_[7] = VM_MEMSIZE; - this.flags_ = 0; - - const preparedCodes = prg.AltCmd ? prg.AltCmd : prg.Cmd; - if (prg.Cmd.length > 0 && !this.executeCode(preparedCodes)) { - // Invalid VM program. Let's replace it with 'return' command. - preparedCode.OpCode = VM_Commands.VM_RET; - } - - const dataView = new DataView(this.mem_.buffer, VM_GLOBALMEMADDR); - let newBlockPos = dataView.getUint32(0x20, true /* little endian */) & VM_MEMMASK; - const newBlockSize = dataView.getUint32(0x1c, true /* little endian */) & VM_MEMMASK; - if (newBlockPos + newBlockSize >= VM_MEMSIZE) { - newBlockPos = newBlockSize = 0; - } - prg.FilteredData = this.mem_.subarray(newBlockPos, newBlockPos + newBlockSize); - - prg.GlobalData = new Uint8Array(0); - - const dataSize = Math.min(dataView.getUint32(0x30), (VM_GLOBALMEMSIZE - VM_FIXEDGLOBALSIZE)); - if (dataSize != 0) { - const len = dataSize + VM_FIXEDGLOBALSIZE; - prg.GlobalData = new Uint8Array(len); - prg.GlobalData.set(mem.subarray(VM_GLOBALMEMADDR, VM_GLOBALMEMADDR + len)); - } - } - - /** - * @param {Array} preparedCodes - * @returns {boolean} - */ - executeCode(preparedCodes) { - let codeIndex = 0; - let cmd = preparedCodes[codeIndex]; - // TODO: Why is this an infinite loop instead of just returning - // when a VM_RET is hit? - while (1) { - switch (cmd.OpCode) { - case VM_Commands.VM_RET: - if (this.R_[7] >= VM_MEMSIZE) { - return true; - } - //SET_IP(GET_VALUE(false,(uint *)&Mem[R[7] & VM_MEMMASK])); - this.R_[7] += 4; - continue; - - case VM_Commands.VM_STANDARD: - this.executeStandardFilter(cmd.Op1.Data); - break; - - default: - console.error('RarVM OpCode not supported: ' + getDebugString(VM_Commands, cmd.OpCode)); - break; - } // switch (cmd.OpCode) - codeIndex++; - cmd = preparedCodes[codeIndex]; - } - } - - /** - * @param {number} filterType - */ - executeStandardFilter(filterType) { - switch (filterType) { - case VM_StandardFilters.VMSF_RGB: { - const dataSize = this.R_[4]; - const width = this.R_[0] - 3; - const posR = this.R_[1]; - const Channels = 3; - let srcOffset = 0; - let destOffset = dataSize; - - // byte *SrcData=Mem,*DestData=SrcData+DataSize; - // SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize); - const dataView = new DataView(this.mem_.buffer, VM_GLOBALMEMADDR /* offset */); - dataView.setUint32(0x20 /* byte offset */, - dataSize /* value */, - true /* little endian */); - - if (dataSize >= (VM_GLOBALMEMADDR / 2) || posR < 0) { - break; - } - - for (let curChannel = 0; curChannel < Channels; ++curChannel) { - let prevByte=0; - - for (let i = curChannel; i < dataSize; i += Channels) { - let predicted; - const upperPos = i - width; - if (upperPos >= 3) { - const upperByte = this.mem_[destOffset + upperPos]; - const upperLeftByte = this.mem_[destOffset + upperPos - 3]; - predicted = prevByte + upperByte - upperLeftByte; - - const pa = Math.abs(predicted - prevByte); - const pb = Math.abs(predicted - upperByte); - const pc = Math.abs(predicted - upperLeftByte); - if (pa <= pb && pa <= pc) { - predicted = prevByte; - } else if (pb <= pc) { - predicted = upperByte; - } else { - predicted = upperLeftByte; - } - } else { - predicted = prevByte; - } - //DestData[I]=PrevByte=(byte)(Predicted-*(SrcData++)); - prevByte = (predicted - this.mem_[srcOffset++]) & 0xff; - this.mem_[destOffset + i] = prevByte; - } - } - for (let i = posR, border = dataSize - 2; i < border; i += 3) { - const g = this.mem_[destOffset + i + 1]; - this.mem_[destOffset + i] += g; - this.mem_[destOffset + i + 2] += g; - } - - break; - } - - // The C++ version of this standard filter uses an odd mixture of - // signed and unsigned integers, bytes and various casts. Careful! - case VM_StandardFilters.VMSF_AUDIO: { - const dataSize = this.R_[4]; - const channels = this.R_[0]; - let srcOffset = 0; - let destOffset = dataSize; - - //SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize); - const dataView = new DataView(this.mem_.buffer, VM_GLOBALMEMADDR); - dataView.setUint32(0x20 /* byte offset */, - dataSize /* value */, - true /* little endian */); - - if (dataSize >= VM_GLOBALMEMADDR / 2) { - break; - } - - for (let curChannel = 0; curChannel < channels; ++curChannel) { - let prevByte = 0; // uint - let prevDelta = 0; // uint - let dif = [0, 0, 0, 0, 0, 0, 0]; - let d1 = 0, d2 = 0, d3; // ints - let k1 = 0, k2 = 0, k3 = 0; // ints - - for (var i = curChannel, byteCount = 0; - i < dataSize; - i += channels, ++byteCount) { - d3 = d2; - d2 = fromUnsigned32ToSigned32(prevDelta - d1); - d1 = fromUnsigned32ToSigned32(prevDelta); - - let predicted = fromSigned32ToUnsigned32(8*prevByte + k1*d1 + k2*d2 + k3*d3); // uint - predicted = (predicted >>> 3) & 0xff; - - let curByte = this.mem_[srcOffset++]; // uint - - // Predicted-=CurByte; - predicted = fromSigned32ToUnsigned32(predicted - curByte); - this.mem_[destOffset + i] = (predicted & 0xff); - - // PrevDelta=(signed char)(Predicted-PrevByte); - // where Predicted, PrevByte, PrevDelta are all unsigned int (32) - // casting this subtraction to a (signed char) is kind of invalid - // but it does the following: - // - do the subtraction - // - get the bottom 8 bits of the result - // - if it was >= 0x80, then the value is negative (subtract 0x100) - // - if the value is now negative, add 0x100000000 to make unsigned - // - // Example: - // predicted = 101 - // prevByte = 4294967158 - // (predicted - prevByte) = -4294967057 - // take lower 8 bits: 1110 1111 = 239 - // since > 127, subtract 256 = -17 - // since < 0, add 0x100000000 = 4294967279 - prevDelta = fromSigned32ToUnsigned32( - fromUnsigned8ToSigned8((predicted - prevByte) & 0xff)); - prevByte = predicted; - - // int D=((signed char)CurByte)<<3; - let curByteAsSignedChar = fromUnsigned8ToSigned8(curByte); // signed char - let d = (curByteAsSignedChar << 3); - - dif[0] += Math.abs(d); - dif[1] += Math.abs(d-d1); - dif[2] += Math.abs(d+d1); - dif[3] += Math.abs(d-d2); - dif[4] += Math.abs(d+d2); - dif[5] += Math.abs(d-d3); - dif[6] += Math.abs(d+d3); - - if ((byteCount & 0x1f) == 0) { - let minDif = dif[0], numMinDif = 0; - dif[0] = 0; - for (let j = 1; j < 7; ++j) { - if (dif[j] < minDif) { - minDif = dif[j]; - numMinDif = j; - } - dif[j] = 0; - } - switch (numMinDif) { - case 1: if (k1>=-16) k1--; break; - case 2: if (k1 < 16) k1++; break; - case 3: if (k2>=-16) k2--; break; - case 4: if (k2 < 16) k2++; break; - case 5: if (k3>=-16) k3--; break; - case 6: if (k3 < 16) k3++; break; - } - } - } - } - - break; - } - - case VM_StandardFilters.VMSF_DELTA: { - const dataSize = this.R_[4]; - const channels = this.R_[0]; - let srcPos = 0; - const border = dataSize * 2; - - //SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize); - const dataView = new DataView(this.mem_.buffer, VM_GLOBALMEMADDR); - dataView.setUint32(0x20 /* byte offset */, - dataSize /* value */, - true /* little endian */); - - if (dataSize >= VM_GLOBALMEMADDR / 2) { - break; - } - - // Bytes from same channels are grouped to continual data blocks, - // so we need to place them back to their interleaving positions. - for (let curChannel = 0; curChannel < channels; ++curChannel) { - let prevByte = 0; - for (let destPos = dataSize + curChannel; destPos < border; destPos += channels) { - prevByte = (prevByte - this.mem_[srcPos++]) & 0xff; - this.mem_[destPos] = prevByte; - } - } - - break; - } - - default: - console.error('RarVM Standard Filter not supported: ' + getDebugString(VM_StandardFilters, filterType)); - break; - } - } - - /** - * @param {Uint8Array} code - * @param {VM_PreparedProgram} prg - */ - prepare(code, prg) { - let codeSize = code.length; - - //InitBitInput(); - //memcpy(InBuf,Code,Min(CodeSize,BitInput::MAX_SIZE)); - const bstream = new BitStream(code.buffer, true /* rtl */); - - // Calculate the single byte XOR checksum to check validity of VM code. - let xorSum = 0; - for (let i = 1; i < codeSize; ++i) { - xorSum ^= code[i]; - } - - bstream.readBits(8); - - prg.Cmd = []; // TODO: Is this right? I don't see it being done in rarvm.cpp. - - // VM code is valid if equal. - if (xorSum == code[0]) { - const filterType = this.isStandardFilter(code); - if (filterType != VM_StandardFilters.VMSF_NONE) { - // VM code is found among standard filters. - const curCmd = new VM_PreparedCommand(); - prg.Cmd.push(curCmd); - - curCmd.OpCode = VM_Commands.VM_STANDARD; - curCmd.Op1.Data = filterType; - // TODO: Addr=&CurCmd->Op1.Data - curCmd.Op1.Addr = [curCmd.Op1.Data]; - curCmd.Op2.Addr = [null]; // &CurCmd->Op2.Data; - curCmd.Op1.Type = VM_OpType.VM_OPNONE; - curCmd.Op2.Type = VM_OpType.VM_OPNONE; - codeSize = 0; - } - - const dataFlag = bstream.readBits(1); - - // Read static data contained in DB operators. This data cannot be - // changed, it is a part of VM code, not a filter parameter. - - if (dataFlag & 0x8000) { - const dataSize = RarVM.readData(bstream) + 1; - // TODO: This accesses the byte pointer of the bstream directly. Is that ok? - for (let i = 0; i < bstream.bytePtr < codeSize && i < dataSize; ++i) { - // Append a byte to the program's static data. - const newStaticData = new Uint8Array(prg.StaticData.length + 1); - newStaticData.set(prg.StaticData); - newStaticData[newStaticData.length - 1] = bstream.readBits(8); - prg.StaticData = newStaticData; - } - } - - while (bstream.bytePtr < codeSize) { - const curCmd = new VM_PreparedCommand(); - prg.Cmd.push(curCmd); // Prg->Cmd.Add(1) - const flag = bstream.peekBits(1); - if (!flag) { // (Data&0x8000)==0 - curCmd.OpCode = bstream.readBits(4); - } else { - curCmd.OpCode = (bstream.readBits(6) - 24); - } - - if (VM_CmdFlags[curCmd.OpCode] & VMCF_BYTEMODE) { - curCmd.ByteMode = (bstream.readBits(1) != 0); - } else { - curCmd.ByteMode = 0; - } - curCmd.Op1.Type = VM_OpType.VM_OPNONE; - curCmd.Op2.Type = VM_OpType.VM_OPNONE; - const opNum = (VM_CmdFlags[curCmd.OpCode] & VMCF_OPMASK); - curCmd.Op1.Addr = null; - curCmd.Op2.Addr = null; - if (opNum > 0) { - this.decodeArg(curCmd.Op1, curCmd.ByteMode, bstream); // reading the first operand - if (opNum == 2) { - this.decodeArg(curCmd.Op2, curCmd.ByteMode, bstream); // reading the second operand - } else { - if (curCmd.Op1.Type == VM_OpType.VM_OPINT && (VM_CmdFlags[curCmd.OpCode] & (VMCF_JUMP|VMCF_PROC))) { - // Calculating jump distance. - let distance = curCmd.Op1.Data; - if (distance >= 256) { - distance -= 256; - } else { - if (distance >= 136) { - distance -= 264; - } else { - if (distance >= 16) { - distance -= 8; - } else { - if (distance >= 8) { - distance -= 16; - } - } - } - distance += prg.Cmd.length; - } - curCmd.Op1.Data = distance; - } - } - } // if (OpNum>0) - } // while ((uint)InAddrOp1.Data - curCmd.Op1.Addr = [curCmd.Op1.Data]; - curCmd.Op2.Addr = [curCmd.Op2.Data]; - curCmd.Op1.Type = VM_OpType.VM_OPNONE; - curCmd.Op2.Type = VM_OpType.VM_OPNONE; - - // If operand 'Addr' field has not been set by DecodeArg calls above, - // let's set it to point to operand 'Data' field. It is necessary for - // VM_OPINT type operands (usual integers) or maybe if something was - // not set properly for other operands. 'Addr' field is required - // for quicker addressing of operand data. - for (let i = 0; i < prg.Cmd.length; ++i) { - const cmd = prg.Cmd[i]; - if (cmd.Op1.Addr == null) { - cmd.Op1.Addr = [cmd.Op1.Data]; - } - if (cmd.Op2.Addr == null) { - cmd.Op2.Addr = [cmd.Op2.Data]; - } - } - - /* - #ifdef VM_OPTIMIZE - if (CodeSize!=0) - Optimize(Prg); - #endif - */ - } - - /** - * @param {Uint8Array} arr The byte array to set a value in. - * @param {number} value The unsigned 32-bit value to set. - * @param {number} offset Offset into arr to start setting the value, defaults to 0. - */ - setLowEndianValue(arr, value, offset) { - const i = offset || 0; - arr[i] = value & 0xff; - arr[i + 1] = (value >>> 8) & 0xff; - arr[i + 2] = (value >>> 16) & 0xff; - arr[i + 3] = (value >>> 24) & 0xff; - } - - /** - * Sets a number of bytes of the VM memory at the given position from a - * source buffer of bytes. - * @param {number} pos The position in the VM memory to start writing to. - * @param {Uint8Array} buffer The source buffer of bytes. - * @param {number} dataSize The number of bytes to set. - */ - setMemory(pos, buffer, dataSize) { - if (pos < VM_MEMSIZE) { - const numBytes = Math.min(dataSize, VM_MEMSIZE - pos); - for (let i = 0; i < numBytes; ++i) { - this.mem_[pos + i] = buffer[i]; - } - } - } - - /** - * Static function that reads in the next set of bits for the VM - * (might return 4, 8, 16 or 32 bits). - * @param {BitStream} bstream A RTL bit stream. - * @returns {number} The value of the bits read. - */ - static readData(bstream) { - // Read in the first 2 bits. - const flags = bstream.readBits(2); - switch (flags) { // Data&0xc000 - // Return the next 4 bits. - case 0: - return bstream.readBits(4); // (Data>>10)&0xf - - case 1: // 0x4000 - // 0x3c00 => 0011 1100 0000 0000 - if (bstream.peekBits(4) == 0) { // (Data&0x3c00)==0 - // Skip the 4 zero bits. - bstream.readBits(4); - // Read in the next 8 and pad with 1s to 32 bits. - return (0xffffff00 | bstream.readBits(8)) >>> 0; // ((Data>>2)&0xff) - } - - // Else, read in the next 8. - return bstream.readBits(8); - - // Read in the next 16. - case 2: // 0x8000 - const val = bstream.getBits(); - bstream.readBits(16); - return val; //bstream.readBits(16); - - // case 3 - default: - return (bstream.readBits(16) << 16) | bstream.readBits(16); - } - } -} - -// ============================================================================================== // diff --git a/archive/unrar.js b/archive/unrar.js deleted file mode 100644 index 8a75c59..0000000 --- a/archive/unrar.js +++ /dev/null @@ -1,1497 +0,0 @@ -/** - * unrar.js - * - * Licensed under the MIT License - * - * Copyright(c) 2011 Google Inc. - * Copyright(c) 2011 antimatter15 - */ - -// TODO: Rewrite the RarLocalHeader parsing to use a ByteStream instead -// of a BitStream so that it throws properly when not enough bytes are -// present. - -import { BitStream } from '../io/bitstream.js'; -import { ByteStream } from '../io/bytestream.js'; -import { ByteBuffer } from '../io/bytebuffer.js'; -import { RarVM, UnpackFilter, VM_GLOBALMEMADDR, VM_GLOBALMEMSIZE, - VM_FIXEDGLOBALSIZE, MAXWINMASK } from './rarvm.js'; - -const UnarchiveState = { - NOT_STARTED: 0, - UNARCHIVING: 1, - WAITING: 2, - FINISHED: 3, -}; - -/** @type {MessagePort} */ -let hostPort; - -// State - consider putting these into a class. -let unarchiveState = UnarchiveState.NOT_STARTED; -/** @type {ByteStream} */ -let bytestream = null; -let allLocalFiles = null; -let logToConsole = false; - -// Progress variables. -let currentFilename = ''; -let currentFileNumber = 0; -let currentBytesUnarchivedInFile = 0; -let currentBytesUnarchived = 0; -let totalUncompressedBytesInArchive = 0; -let totalFilesInArchive = 0; - -// Helper functions. -const info = function (str) { - hostPort.postMessage({ type: 'info', msg: str }); -}; -const err = function (str) { - hostPort.postMessage({ type: 'error', msg: str }); -}; -const postProgress = function () { - hostPort.postMessage({ - type: 'progress', - currentFilename, - currentFileNumber, - currentBytesUnarchivedInFile, - currentBytesUnarchived, - totalUncompressedBytesInArchive, - totalFilesInArchive, - totalCompressedBytesRead: bytestream.getNumBytesRead(), - }); -}; - -// shows a byte value as its hex representation -const nibble = '0123456789ABCDEF'; -const byteValueToHexString = function (num) { - return nibble[num >> 4] + nibble[num & 0xF]; -}; -const twoByteValueToHexString = function (num) { - return nibble[(num >> 12) & 0xF] + nibble[(num >> 8) & 0xF] + nibble[(num >> 4) & 0xF] + nibble[num & 0xF]; -}; - - -// Volume Types -const MARK_HEAD = 0x72; -const MAIN_HEAD = 0x73; -const FILE_HEAD = 0x74; -const COMM_HEAD = 0x75; -const AV_HEAD = 0x76; -const SUB_HEAD = 0x77; -const PROTECT_HEAD = 0x78; -const SIGN_HEAD = 0x79; -const NEWSUB_HEAD = 0x7a; -const ENDARC_HEAD = 0x7b; - -// ============================================================================================== // - -/** - */ -class RarVolumeHeader { - /** - * @param {ByteStream} bstream - */ - constructor(bstream) { - let headBytesRead = 0; - - // byte 1,2 - this.crc = bstream.readNumber(2); - - // byte 3 - this.headType = bstream.readNumber(1); - - // Get flags - // bytes 4,5 - this.flags = {}; - this.flags.value = bstream.readNumber(2); - const flagsValue = this.flags.value; - - switch (this.headType) { - case MAIN_HEAD: - this.flags.MHD_VOLUME = !!(flagsValue & 0x01); - this.flags.MHD_COMMENT = !!(flagsValue & 0x02); - this.flags.MHD_LOCK = !!(flagsValue & 0x04); - this.flags.MHD_SOLID = !!(flagsValue & 0x08); - this.flags.MHD_PACK_COMMENT = !!(flagsValue & 0x10); - this.flags.MHD_NEWNUMBERING = this.flags.MHD_PACK_COMMENT; - this.flags.MHD_AV = !!(flagsValue & 0x20); - this.flags.MHD_PROTECT = !!(flagsValue & 0x40); - this.flags.MHD_PASSWORD = !!(flagsValue & 0x80); - this.flags.MHD_FIRSTVOLUME = !!(flagsValue & 0x100); - this.flags.MHD_ENCRYPTVER = !!(flagsValue & 0x200); - //bstream.readBits(6); // unused - break; - case FILE_HEAD: - this.flags.LHD_SPLIT_BEFORE = !!(flagsValue & 0x01); - this.flags.LHD_SPLIT_AFTER = !!(flagsValue & 0x02); - this.flags.LHD_PASSWORD = !!(flagsValue & 0x04); - this.flags.LHD_COMMENT = !!(flagsValue & 0x08); - this.flags.LHD_SOLID = !!(flagsValue & 0x10); - // 3 bits unused - this.flags.LHD_LARGE = !!(flagsValue & 0x100); - this.flags.LHD_UNICODE = !!(flagsValue & 0x200); - this.flags.LHD_SALT = !!(flagsValue & 0x400); - this.flags.LHD_VERSION = !!(flagsValue & 0x800); - this.flags.LHD_EXTTIME = !!(flagsValue & 0x1000); - this.flags.LHD_EXTFLAGS = !!(flagsValue & 0x2000); - // 2 bits unused - //info(' LHD_SPLIT_BEFORE = ' + this.flags.LHD_SPLIT_BEFORE); - break; - default: - break; - } - - // byte 6,7 - this.headSize = bstream.readNumber(2); - headBytesRead += 7; - - switch (this.headType) { - case MAIN_HEAD: - this.highPosAv = bstream.readNumber(2); - this.posAv = bstream.readNumber(4); - headBytesRead += 6; - if (this.flags.MHD_ENCRYPTVER) { - this.encryptVer = bstream.readNumber(1); - headBytesRead += 1; - } - //info('Found MAIN_HEAD with highPosAv=' + this.highPosAv + ', posAv=' + this.posAv); - break; - case FILE_HEAD: - this.packSize = bstream.readNumber(4); - this.unpackedSize = bstream.readNumber(4); - this.hostOS = bstream.readNumber(1); - this.fileCRC = bstream.readNumber(4); - this.fileTime = bstream.readNumber(4); - this.unpVer = bstream.readNumber(1); - this.method = bstream.readNumber(1); - this.nameSize = bstream.readNumber(2); - this.fileAttr = bstream.readNumber(4); - headBytesRead += 25; - - if (this.flags.LHD_LARGE) { - //info('Warning: Reading in LHD_LARGE 64-bit size values'); - this.HighPackSize = bstream.readNumber(4); - this.HighUnpSize = bstream.readNumber(4); - headBytesRead += 8; - } else { - this.HighPackSize = 0; - this.HighUnpSize = 0; - if (this.unpackedSize == 0xffffffff) { - this.HighUnpSize = 0x7fffffff - this.unpackedSize = 0xffffffff; - } - } - this.fullPackSize = 0; - this.fullUnpackSize = 0; - this.fullPackSize |= this.HighPackSize; - this.fullPackSize <<= 32; - this.fullPackSize |= this.packSize; - - // read in filename - - // TODO: Use readString? - this.filename = bstream.readBytes(this.nameSize); - headBytesRead += this.nameSize; - let _s = ''; - for (let _i = 0; _i < this.filename.length; _i++) { - _s += String.fromCharCode(this.filename[_i]); - } - - this.filename = _s; - - if (this.flags.LHD_SALT) { - //info('Warning: Reading in 64-bit salt value'); - this.salt = bstream.readBytes(8); // 8 bytes - headBytesRead += 8; - } - - if (this.flags.LHD_EXTTIME) { - // 16-bit flags - const extTimeFlags = bstream.readNumber(2); - headBytesRead += 2; - - // this is adapted straight out of arcread.cpp, Archive::ReadHeader() - for (let I = 0; I < 4; ++I) { - const rmode = extTimeFlags >> ((3 - I) * 4); - if ((rmode & 8) == 0) { - continue; - } - if (I != 0) { - bstream.readBytes(2); - headBytesRead += 2; - } - const count = (rmode & 3); - for (let J = 0; J < count; ++J) { - bstream.readNumber(1); - headBytesRead += 1; - } - } - } - - if (this.flags.LHD_COMMENT) { - //info('Found a LHD_COMMENT'); - } - - if (headBytesRead < this.headSize) { - bstream.readBytes(this.headSize - headBytesRead); - } - - break; - case ENDARC_HEAD: - break; - default: - if (logToConsole) { - info('Found a header of type 0x' + byteValueToHexString(this.headType)); - } - // skip the rest of the header bytes (for now) - bstream.readBytes(this.headSize - 7); - break; - } - } - - dump() { - info(' crc=' + this.crc); - info(' headType=' + this.headType); - info(' flags=' + twoByteValueToHexString(this.flags.value)); - info(' headSize=' + this.headSize); - if (this.headType == FILE_HEAD) { - info('Found FILE_HEAD with packSize=' + this.packSize + ', unpackedSize= ' + - this.unpackedSize + ', hostOS=' + this.hostOS + ', unpVer=' + this.unpVer + ', method=' + - this.method + ', filename=' + this.filename); - } - } -} - -const BLOCK_LZ = 0; -const BLOCK_PPM = 1; - -const rLDecode = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224]; -const rLBits = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5]; -const rDBitLengthCounts = [4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 14, 0, 12]; -const rSDDecode = [0, 4, 8, 16, 32, 64, 128, 192]; -const rSDBits = [2, 2, 3, 4, 5, 6, 6, 6]; - -const rDDecode = [0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, - 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, - 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, 65536, 98304, - 131072, 196608, 262144, 327680, 393216, 458752, 524288, 589824, - 655360, 720896, 786432, 851968, 917504, 983040]; - -const rDBits = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, - 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, - 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16]; - -const rLOW_DIST_REP_COUNT = 16; - -const rNC = 299; -const rDC = 60; -const rLDC = 17; -const rRC = 28; -const rBC = 20; -const rHUFF_TABLE_SIZE = (rNC + rDC + rRC + rLDC); - -const UnpOldTable = new Array(rHUFF_TABLE_SIZE); - -const BD = { //bitdecode - DecodeLen: new Array(16), - DecodePos: new Array(16), - DecodeNum: new Array(rBC) -}; -const LD = { //litdecode - DecodeLen: new Array(16), - DecodePos: new Array(16), - DecodeNum: new Array(rNC) -}; -const DD = { //distdecode - DecodeLen: new Array(16), - DecodePos: new Array(16), - DecodeNum: new Array(rDC) -}; -const LDD = { //low dist decode - DecodeLen: new Array(16), - DecodePos: new Array(16), - DecodeNum: new Array(rLDC) -}; -const RD = { //rep decode - DecodeLen: new Array(16), - DecodePos: new Array(16), - DecodeNum: new Array(rRC) -}; - -/** - * @type {Array} - */ -const rOldBuffers = []; - -/** - * The current buffer we are unpacking to. - * @type {ByteBuffer} - */ -let rBuffer; - -/** - * The buffer of the final bytes after filtering (only used in Unpack29). - * @type {ByteBuffer} - */ -let wBuffer; - - -/** - * In unpack.cpp, UnpPtr keeps track of what bytes have been unpacked - * into the Window buffer and WrPtr keeps track of what bytes have been - * actually written to disk after the unpacking and optional filtering - * has been done. - * - * In our case, rBuffer is the buffer for the unpacked bytes and wBuffer is - * the final output bytes. - */ - - -/** - * Read in Huffman tables for RAR - * @param {BitStream} bstream - */ -function RarReadTables(bstream) { - const BitLength = new Array(rBC); - const Table = new Array(rHUFF_TABLE_SIZE); - - // before we start anything we need to get byte-aligned - bstream.readBits((8 - bstream.bitPtr) & 0x7); - - if (bstream.readBits(1)) { - info('Error! PPM not implemented yet'); - return; - } - - if (!bstream.readBits(1)) { //discard old table - for (let i = UnpOldTable.length; i--;) { - UnpOldTable[i] = 0; - } - } - - // read in bit lengths - for (let I = 0; I < rBC; ++I) { - const Length = bstream.readBits(4); - if (Length == 15) { - let ZeroCount = bstream.readBits(4); - if (ZeroCount == 0) { - BitLength[I] = 15; - } else { - ZeroCount += 2; - while (ZeroCount-- > 0 && I < rBC) { - BitLength[I++] = 0; - } - --I; - } - } else { - BitLength[I] = Length; - } - } - - // now all 20 bit lengths are obtained, we construct the Huffman Table: - - RarMakeDecodeTables(BitLength, 0, BD, rBC); - - const TableSize = rHUFF_TABLE_SIZE; - for (let i = 0; i < TableSize;) { - const num = RarDecodeNumber(bstream, BD); - if (num < 16) { - Table[i] = (num + UnpOldTable[i]) & 0xf; - i++; - } else if (num < 18) { - let N = (num == 16) ? (bstream.readBits(3) + 3) : (bstream.readBits(7) + 11); - - while (N-- > 0 && i < TableSize) { - Table[i] = Table[i - 1]; - i++; - } - } else { - let N = (num == 18) ? (bstream.readBits(3) + 3) : (bstream.readBits(7) + 11); - - while (N-- > 0 && i < TableSize) { - Table[i++] = 0; - } - } - } - - RarMakeDecodeTables(Table, 0, LD, rNC); - RarMakeDecodeTables(Table, rNC, DD, rDC); - RarMakeDecodeTables(Table, rNC + rDC, LDD, rLDC); - RarMakeDecodeTables(Table, rNC + rDC + rLDC, RD, rRC); - - for (let i = UnpOldTable.length; i--;) { - UnpOldTable[i] = Table[i]; - } - return true; -} - - -function RarDecodeNumber(bstream, dec) { - const DecodeLen = dec.DecodeLen; - const DecodePos = dec.DecodePos; - const DecodeNum = dec.DecodeNum; - const bitField = bstream.getBits() & 0xfffe; - //some sort of rolled out binary search - const bits = ((bitField < DecodeLen[8]) ? - ((bitField < DecodeLen[4]) ? - ((bitField < DecodeLen[2]) ? - ((bitField < DecodeLen[1]) ? 1 : 2) - : ((bitField < DecodeLen[3]) ? 3 : 4)) - : (bitField < DecodeLen[6]) ? - ((bitField < DecodeLen[5]) ? 5 : 6) - : ((bitField < DecodeLen[7]) ? 7 : 8)) - : ((bitField < DecodeLen[12]) ? - ((bitField < DecodeLen[10]) ? - ((bitField < DecodeLen[9]) ? 9 : 10) - : ((bitField < DecodeLen[11]) ? 11 : 12)) - : (bitField < DecodeLen[14]) ? - ((bitField < DecodeLen[13]) ? 13 : 14) - : 15)); - bstream.readBits(bits); - const N = DecodePos[bits] + ((bitField - DecodeLen[bits - 1]) >>> (16 - bits)); - - return DecodeNum[N]; -} - - -function RarMakeDecodeTables(BitLength, offset, dec, size) { - const DecodeLen = dec.DecodeLen; - const DecodePos = dec.DecodePos; - const DecodeNum = dec.DecodeNum; - const LenCount = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - const TmpPos = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - let N = 0; - let M = 0; - - for (let i = DecodeNum.length; i--;) { - DecodeNum[i] = 0; - } - for (let i = 0; i < size; i++) { - LenCount[BitLength[i + offset] & 0xF]++; - } - LenCount[0] = 0; - TmpPos[0] = 0; - DecodePos[0] = 0; - DecodeLen[0] = 0; - - for (let I = 1; I < 16; ++I) { - N = 2 * (N + LenCount[I]); - M = (N << (15 - I)); - if (M > 0xFFFF) { - M = 0xFFFF; - } - DecodeLen[I] = M; - DecodePos[I] = DecodePos[I - 1] + LenCount[I - 1]; - TmpPos[I] = DecodePos[I]; - } - for (let I = 0; I < size; ++I) { - if (BitLength[I + offset] != 0) { - DecodeNum[TmpPos[BitLength[offset + I] & 0xF]++] = I; - } - } - -} - -// TODO: implement -/** - * @param {BitStream} bstream - * @param {boolean} Solid - */ -function Unpack15(bstream, Solid) { - info('ERROR! RAR 1.5 compression not supported'); -} - -/** - * Unpacks the bit stream into rBuffer using the Unpack20 algorithm. - * @param {BitStream} bstream - * @param {boolean} Solid - */ -function Unpack20(bstream, Solid) { - const destUnpSize = rBuffer.data.length; - let oldDistPtr = 0; - let Bits; - - if (!Solid) { - RarReadTables20(bstream); - } - while (destUnpSize > rBuffer.ptr) { - let num = RarDecodeNumber(bstream, LD); - if (num < 256) { - rBuffer.insertByte(num); - continue; - } - if (num > 269) { - let Length = rLDecode[num -= 270] + 3; - if ((Bits = rLBits[num]) > 0) { - Length += bstream.readBits(Bits); - } - let DistNumber = RarDecodeNumber(bstream, DD); - let Distance = rDDecode[DistNumber] + 1; - if ((Bits = rDBits[DistNumber]) > 0) { - Distance += bstream.readBits(Bits); - } - if (Distance >= 0x2000) { - Length++; - if (Distance >= 0x40000) { - Length++; - } - } - lastLength = Length; - lastDist = rOldDist[oldDistPtr++ & 3] = Distance; - RarCopyString(Length, Distance); - continue; - } - if (num == 269) { - RarReadTables20(bstream); - RarUpdateProgress(); - continue; - } - if (num == 256) { - lastDist = rOldDist[oldDistPtr++ & 3] = lastDist; - RarCopyString(lastLength, lastDist); - continue; - } - if (num < 261) { - const Distance = rOldDist[(oldDistPtr - (num - 256)) & 3]; - const LengthNumber = RarDecodeNumber(bstream, RD); - let Length = rLDecode[LengthNumber] + 2; - if ((Bits = rLBits[LengthNumber]) > 0) { - Length += bstream.readBits(Bits); - } - if (Distance >= 0x101) { - Length++; - if (Distance >= 0x2000) { - Length++ - if (Distance >= 0x40000) { - Length++; - } - } - } - lastLength = Length; - lastDist = rOldDist[oldDistPtr++ & 3] = Distance; - RarCopyString(Length, Distance); - continue; - } - if (num < 270) { - let Distance = rSDDecode[num -= 261] + 1; - if ((Bits = rSDBits[num]) > 0) { - Distance += bstream.readBits(Bits); - } - lastLength = 2; - lastDist = rOldDist[oldDistPtr++ & 3] = Distance; - RarCopyString(2, Distance); - continue; - } - - } - RarUpdateProgress(); -} - -function RarUpdateProgress() { - const change = rBuffer.ptr - currentBytesUnarchivedInFile; - currentBytesUnarchivedInFile = rBuffer.ptr; - currentBytesUnarchived += change; - postProgress(); -} - -const rNC20 = 298; -const rDC20 = 48; -const rRC20 = 28; -const rBC20 = 19; -const rMC20 = 257; - -const UnpOldTable20 = new Array(rMC20 * 4); - -// TODO: This function should return a boolean value, see unpack20.cpp. -function RarReadTables20(bstream) { - const BitLength = new Array(rBC20); - const Table = new Array(rMC20 * 4); - let TableSize; - let N; - let I; - const AudioBlock = bstream.readBits(1); - if (!bstream.readBits(1)) { - for (let i = UnpOldTable20.length; i--;) { - UnpOldTable20[i] = 0; - } - } - TableSize = rNC20 + rDC20 + rRC20; - for (I = 0; I < rBC20; I++) { - BitLength[I] = bstream.readBits(4); - } - RarMakeDecodeTables(BitLength, 0, BD, rBC20); - I = 0; - while (I < TableSize) { - const num = RarDecodeNumber(bstream, BD); - if (num < 16) { - Table[I] = num + UnpOldTable20[I] & 0xf; - I++; - } else if (num == 16) { - N = bstream.readBits(2) + 3; - while (N-- > 0 && I < TableSize) { - Table[I] = Table[I - 1]; - I++; - } - } else { - if (num == 17) { - N = bstream.readBits(3) + 3; - } else { - N = bstream.readBits(7) + 11; - } - while (N-- > 0 && I < TableSize) { - Table[I++] = 0; - } - } - } - RarMakeDecodeTables(Table, 0, LD, rNC20); - RarMakeDecodeTables(Table, rNC20, DD, rDC20); - RarMakeDecodeTables(Table, rNC20 + rDC20, RD, rRC20); - for (let i = UnpOldTable20.length; i--;) { - UnpOldTable20[i] = Table[i]; - } -} - -let lowDistRepCount = 0; -let prevLowDist = 0; - -let rOldDist = [0, 0, 0, 0]; -let lastDist; -let lastLength; - -// ============================================================================================== // - -// Unpack code specific to RarVM -const VM = new RarVM(); - -/** - * Filters code, one entry per filter. - * @type {Array} - */ -let Filters = []; - -/** - * Filters stack, several entrances of same filter are possible. - * @type {Array} - */ -let PrgStack = []; - -/** - * Lengths of preceding blocks, one length per filter. Used to reduce - * size required to write block length if lengths are repeating. - * @type {Array} - */ -let OldFilterLengths = []; - -let LastFilter = 0; - -function InitFilters() { - OldFilterLengths = []; - LastFilter = 0; - Filters = []; - PrgStack = []; -} - - -/** - * @param {number} firstByte The first byte (flags). - * @param {Uint8Array} vmCode An array of bytes. - */ -function RarAddVMCode(firstByte, vmCode) { - VM.init(); - const bstream = new BitStream(vmCode.buffer, true /* rtl */); - - let filtPos; - if (firstByte & 0x80) { - filtPos = RarVM.readData(bstream); - if (filtPos == 0) { - InitFilters(); - } else { - filtPos--; - } - } else { - filtPos = LastFilter; - } - - if (filtPos > Filters.length || filtPos > OldFilterLengths.length) { - return false; - } - - LastFilter = filtPos; - const newFilter = (filtPos == Filters.length); - - // new filter for PrgStack - const stackFilter = new UnpackFilter(); - let filter = null; - // new filter code, never used before since VM reset - if (newFilter) { - // too many different filters, corrupt archive - if (filtPos > 1024) { - return false; - } - - filter = new UnpackFilter(); - Filters.push(filter); - stackFilter.ParentFilter = (Filters.length - 1); - OldFilterLengths.push(0); // OldFilterLengths.Add(1) - filter.ExecCount = 0; - } else { // filter was used in the past - filter = Filters[filtPos]; - stackFilter.ParentFilter = filtPos; - filter.ExecCount++; - } - - let emptyCount = 0; - for (let i = 0; i < PrgStack.length; ++i) { - PrgStack[i - emptyCount] = PrgStack[i]; - - if (PrgStack[i] == null) { - emptyCount++; - } - if (emptyCount > 0) { - PrgStack[i] = null; - } - } - - if (emptyCount == 0) { - PrgStack.push(null); //PrgStack.Add(1); - emptyCount = 1; - } - - const stackPos = PrgStack.length - emptyCount; - PrgStack[stackPos] = stackFilter; - stackFilter.ExecCount = filter.ExecCount; - - let blockStart = RarVM.readData(bstream); - if (firstByte & 0x40) { - blockStart += 258; - } - stackFilter.BlockStart = (blockStart + rBuffer.ptr) & MAXWINMASK; - - if (firstByte & 0x20) { - stackFilter.BlockLength = RarVM.readData(bstream); - } else { - stackFilter.BlockLength = filtPos < OldFilterLengths.length - ? OldFilterLengths[filtPos] - : 0; - } - stackFilter.NextWindow = (wBuffer.ptr != rBuffer.ptr) && - (((wBuffer.ptr - rBuffer.ptr) & MAXWINMASK) <= blockStart); - - OldFilterLengths[filtPos] = stackFilter.BlockLength; - - for (let i = 0; i < 7; ++i) { - stackFilter.Prg.InitR[i] = 0; - } - stackFilter.Prg.InitR[3] = VM_GLOBALMEMADDR; - stackFilter.Prg.InitR[4] = stackFilter.BlockLength; - stackFilter.Prg.InitR[5] = stackFilter.ExecCount; - - // set registers to optional parameters if any - if (firstByte & 0x10) { - const initMask = bstream.readBits(7); - for (let i = 0; i < 7; ++i) { - if (initMask & (1 << i)) { - stackFilter.Prg.InitR[i] = RarVM.readData(bstream); - } - } - } - - if (newFilter) { - const vmCodeSize = RarVM.readData(bstream); - if (vmCodeSize >= 0x10000 || vmCodeSize == 0) { - return false; - } - const vmCode = new Uint8Array(vmCodeSize); - for (let i = 0; i < vmCodeSize; ++i) { - //if (Inp.Overflow(3)) - // return(false); - vmCode[i] = bstream.readBits(8); - } - VM.prepare(vmCode, filter.Prg); - } - stackFilter.Prg.Cmd = filter.Prg.Cmd; - stackFilter.Prg.AltCmd = filter.Prg.Cmd; - - const staticDataSize = filter.Prg.StaticData.length; - if (staticDataSize > 0 && staticDataSize < VM_GLOBALMEMSIZE) { - // read statically defined data contained in DB commands - for (let i = 0; i < staticDataSize; ++i) { - stackFilter.Prg.StaticData[i] = filter.Prg.StaticData[i]; - } - } - - if (stackFilter.Prg.GlobalData.length < VM_FIXEDGLOBALSIZE) { - stackFilter.Prg.GlobalData = new Uint8Array(VM_FIXEDGLOBALSIZE); - } - - const globalData = stackFilter.Prg.GlobalData; - for (let i = 0; i < 7; ++i) { - VM.setLowEndianValue(globalData, stackFilter.Prg.InitR[i], i * 4); - } - - VM.setLowEndianValue(globalData, stackFilter.BlockLength, 0x1c); - VM.setLowEndianValue(globalData, 0, 0x20); - VM.setLowEndianValue(globalData, stackFilter.ExecCount, 0x2c); - for (let i = 0; i < 16; ++i) { - globalData[0x30 + i] = 0; - } - - // put data block passed as parameter if any - if (firstByte & 8) { - //if (Inp.Overflow(3)) - // return(false); - const dataSize = RarVM.readData(bstream); - if (dataSize > (VM_GLOBALMEMSIZE - VM_FIXEDGLOBALSIZE)) { - return false; - } - - const curSize = stackFilter.Prg.GlobalData.length; - if (curSize < dataSize + VM_FIXEDGLOBALSIZE) { - // Resize global data and update the stackFilter and local variable. - const numBytesToAdd = dataSize + VM_FIXEDGLOBALSIZE - curSize; - const newGlobalData = new Uint8Array(globalData.length + numBytesToAdd); - newGlobalData.set(globalData); - - stackFilter.Prg.GlobalData = newGlobalData; - globalData = newGlobalData; - } - //byte *GlobalData=&StackFilter->Prg.GlobalData[VM_FIXEDGLOBALSIZE]; - for (let i = 0; i < dataSize; ++i) { - //if (Inp.Overflow(3)) - // return(false); - globalData[VM_FIXEDGLOBALSIZE + i] = bstream.readBits(8); - } - } - - return true; -} - - -/** - * @param {!BitStream} bstream - */ -function RarReadVMCode(bstream) { - const firstByte = bstream.readBits(8); - let length = (firstByte & 7) + 1; - if (length == 7) { - length = bstream.readBits(8) + 7; - } else if (length == 8) { - length = bstream.readBits(16); - } - - // Read all bytes of VM code into an array. - const vmCode = new Uint8Array(length); - for (let i = 0; i < length; i++) { - // Do something here with checking readbuf. - vmCode[i] = bstream.readBits(8); - } - return RarAddVMCode(firstByte, vmCode); -} - -/** - * Unpacks the bit stream into rBuffer using the Unpack29 algorithm. - * @param {BitStream} bstream - * @param {boolean} Solid - */ -function Unpack29(bstream, Solid) { - // lazy initialize rDDecode and rDBits - - const DDecode = new Array(rDC); - const DBits = new Array(rDC); - - let Dist = 0; - let BitLength = 0; - let Slot = 0; - - for (let I = 0; I < rDBitLengthCounts.length; I++, BitLength++) { - for (let J = 0; J < rDBitLengthCounts[I]; J++, Slot++, Dist += (1 << BitLength)) { - DDecode[Slot] = Dist; - DBits[Slot] = BitLength; - } - } - - let Bits; - //tablesRead = false; - - rOldDist = [0, 0, 0, 0] - - lastDist = 0; - lastLength = 0; - - for (let i = UnpOldTable.length; i--;) { - UnpOldTable[i] = 0; - } - - // read in Huffman tables - RarReadTables(bstream); - - while (true) { - let num = RarDecodeNumber(bstream, LD); - - if (num < 256) { - rBuffer.insertByte(num); - continue; - } - if (num >= 271) { - let Length = rLDecode[num -= 271] + 3; - if ((Bits = rLBits[num]) > 0) { - Length += bstream.readBits(Bits); - } - const DistNumber = RarDecodeNumber(bstream, DD); - let Distance = DDecode[DistNumber] + 1; - if ((Bits = DBits[DistNumber]) > 0) { - if (DistNumber > 9) { - if (Bits > 4) { - Distance += ((bstream.getBits() >>> (20 - Bits)) << 4); - bstream.readBits(Bits - 4); - //todo: check this - } - if (lowDistRepCount > 0) { - lowDistRepCount--; - Distance += prevLowDist; - } else { - const LowDist = RarDecodeNumber(bstream, LDD); - if (LowDist == 16) { - lowDistRepCount = rLOW_DIST_REP_COUNT - 1; - Distance += prevLowDist; - } else { - Distance += LowDist; - prevLowDist = LowDist; - } - } - } else { - Distance += bstream.readBits(Bits); - } - } - if (Distance >= 0x2000) { - Length++; - if (Distance >= 0x40000) { - Length++; - } - } - RarInsertOldDist(Distance); - RarInsertLastMatch(Length, Distance); - RarCopyString(Length, Distance); - continue; - } - if (num == 256) { - if (!RarReadEndOfBlock(bstream)) { - break; - } - continue; - } - if (num == 257) { - if (!RarReadVMCode(bstream)) { - break; - } - continue; - } - if (num == 258) { - if (lastLength != 0) { - RarCopyString(lastLength, lastDist); - } - continue; - } - if (num < 263) { - const DistNum = num - 259; - const Distance = rOldDist[DistNum]; - - for (let I = DistNum; I > 0; I--) { - rOldDist[I] = rOldDist[I - 1]; - } - rOldDist[0] = Distance; - - const LengthNumber = RarDecodeNumber(bstream, RD); - let Length = rLDecode[LengthNumber] + 2; - if ((Bits = rLBits[LengthNumber]) > 0) { - Length += bstream.readBits(Bits); - } - RarInsertLastMatch(Length, Distance); - RarCopyString(Length, Distance); - continue; - } - if (num < 272) { - let Distance = rSDDecode[num -= 263] + 1; - if ((Bits = rSDBits[num]) > 0) { - Distance += bstream.readBits(Bits); - } - RarInsertOldDist(Distance); - RarInsertLastMatch(2, Distance); - RarCopyString(2, Distance); - continue; - } - } // while (true) - RarUpdateProgress(); - RarWriteBuf(); -} - -/** - * Does stuff to the current byte buffer (rBuffer) based on - * the filters loaded into the RarVM and writes out to wBuffer. - */ -function RarWriteBuf() { - let writeSize = (rBuffer.ptr & MAXWINMASK); - - for (let i = 0; i < PrgStack.length; ++i) { - const flt = PrgStack[i]; - if (flt == null) { - continue; - } - - if (flt.NextWindow) { - flt.NextWindow = false; - continue; - } - - const blockStart = flt.BlockStart; - const blockLength = flt.BlockLength; - - // WrittenBorder = wBuffer.ptr - if (((blockStart - wBuffer.ptr) & MAXWINMASK) < writeSize) { - if (wBuffer.ptr != blockStart) { - // Copy blockStart bytes from rBuffer into wBuffer. - RarWriteArea(wBuffer.ptr, blockStart); - writeSize = (rBuffer.ptr - wBuffer.ptr) & MAXWINMASK; - } - if (blockLength <= writeSize) { - const blockEnd = (blockStart + blockLength) & MAXWINMASK; - if (blockStart < blockEnd || blockEnd == 0) { - VM.setMemory(0, rBuffer.data.subarray(blockStart, blockStart + blockLength), blockLength); - } else { - const firstPartLength = MAXWINSIZE - blockStart; - VM.setMemory(0, rBuffer.data.subarray(blockStart, blockStart + firstPartLength), firstPartLength); - VM.setMemory(firstPartLength, rBuffer.data, blockEnd); - } - - const parentPrg = Filters[flt.ParentFilter].Prg; - const prg = flt.Prg; - - if (parentPrg.GlobalData.length > VM_FIXEDGLOBALSIZE) { - // Copy global data from previous script execution if any. - prg.GlobalData = new Uint8Array(parentPrg.GlobalData); - } - - RarExecuteCode(prg); - - if (prg.GlobalData.length > VM_FIXEDGLOBALSIZE) { - // Save global data for next script execution. - const globalDataLen = prg.GlobalData.length; - if (parentPrg.GlobalData.length < globalDataLen) { - parentPrg.GlobalData = new Uint8Array(globalDataLen); - } - parentPrg.GlobalData.set( - this.mem_.subarray(VM_FIXEDGLOBALSIZE, VM_FIXEDGLOBALSIZE + globalDataLen), - VM_FIXEDGLOBALSIZE); - } else { - parentPrg.GlobalData = new Uint8Array(0); - } - - let filteredData = prg.FilteredData; - - PrgStack[i] = null; - while (i + 1 < PrgStack.length) { - const nextFilter = PrgStack[i + 1]; - if (nextFilter == null || nextFilter.BlockStart != blockStart || - nextFilter.BlockLength != filteredData.length || nextFilter.NextWindow) { - break; - } - - // Apply several filters to same data block. - - VM.setMemory(0, filteredData, filteredData.length); - - const innerParentPrg = Filters[nextFilter.ParentFilter].Prg; - const nextPrg = nextFilter.Prg; - - const globalDataLen = innerParentPrg.GlobalData.length; - if (globalDataLen > VM_FIXEDGLOBALSIZE) { - // Copy global data from previous script execution if any. - nextPrg.GlobalData = new Uint8Array(globalDataLen); - nextPrg.GlobalData.set(innerParentPrg.GlobalData.subarray(VM_FIXEDGLOBALSIZE, VM_FIXEDGLOBALSIZE + globalDataLen), VM_FIXEDGLOBALSIZE); - } - - RarExecuteCode(nextPrg); - - if (nextPrg.GlobalData.length > VM_GLOBALMEMSIZE) { - // Save global data for next script execution. - const globalDataLen = nextPrg.GlobalData.length; - if (innerParentPrg.GlobalData.length < globalDataLen) { - innerParentPrg.GlobalData = new Uint8Array(globalDataLen); - } - innerParentPrg.GlobalData.set( - this.mem_.subarray(VM_FIXEDGLOBALSIZE, VM_FIXEDGLOBALSIZE + globalDataLen), - VM_FIXEDGLOBALSIZE); - } else { - innerParentPrg.GlobalData = new Uint8Array(0); - } - - filteredData = nextPrg.FilteredData; - i++; - PrgStack[i] = null; - } // while (i + 1 < PrgStack.length) - - for (let j = 0; j < filteredData.length; ++j) { - wBuffer.insertByte(filteredData[j]); - } - writeSize = (rBuffer.ptr - wBuffer.ptr) & MAXWINMASK; - } // if (blockLength <= writeSize) - else { - for (let j = i; j < PrgStack.length; ++j) { - const theFlt = PrgStack[j]; - if (theFlt != null && theFlt.NextWindow) { - theFlt.NextWindow = false; - } - } - return; - } - } // if (((blockStart - wBuffer.ptr) & MAXWINMASK) < writeSize) - } // for (let i = 0; i < PrgStack.length; ++i) - - // Write any remaining bytes from rBuffer to wBuffer; - RarWriteArea(wBuffer.ptr, rBuffer.ptr); - - // Now that the filtered buffer has been written, swap it back to rBuffer. - rBuffer = wBuffer; -} - -/** - * Copy bytes from rBuffer to wBuffer. - * @param {number} startPtr The starting point to copy from rBuffer. - * @param {number} endPtr The ending point to copy from rBuffer. - */ -function RarWriteArea(startPtr, endPtr) { - if (endPtr < startPtr) { - console.error('endPtr < startPtr, endPtr=' + endPtr + ', startPtr=' + startPtr); - // RarWriteData(startPtr, -(int)StartPtr & MAXWINMASK); - // RarWriteData(0, endPtr); - return; - } else if (startPtr < endPtr) { - RarWriteData(startPtr, endPtr - startPtr); - } -} - -/** - * Writes bytes into wBuffer from rBuffer. - * @param {number} offset The starting point to copy bytes from rBuffer. - * @param {number} numBytes The number of bytes to copy. - */ -function RarWriteData(offset, numBytes) { - if (wBuffer.ptr >= rBuffer.data.length) { - return; - } - const leftToWrite = rBuffer.data.length - wBuffer.ptr; - if (numBytes > leftToWrite) { - numBytes = leftToWrite; - } - for (let i = 0; i < numBytes; ++i) { - wBuffer.insertByte(rBuffer.data[offset + i]); - } -} - -/** - * @param {VM_PreparedProgram} prg - */ -function RarExecuteCode(prg) { - if (prg.GlobalData.length > 0) { - const writtenFileSize = wBuffer.ptr; - prg.InitR[6] = writtenFileSize; - VM.setLowEndianValue(prg.GlobalData, writtenFileSize, 0x24); - VM.setLowEndianValue(prg.GlobalData, (writtenFileSize >>> 32) >> 0, 0x28); - VM.execute(prg); - } -} - -function RarReadEndOfBlock(bstream) { - RarUpdateProgress(); - - let NewTable = false; - let NewFile = false; - if (bstream.readBits(1)) { - NewTable = true; - } else { - NewFile = true; - NewTable = !!bstream.readBits(1); - } - //tablesRead = !NewTable; - return !(NewFile || NewTable && !RarReadTables(bstream)); -} - -function RarInsertLastMatch(length, distance) { - lastDist = distance; - lastLength = length; -} - -function RarInsertOldDist(distance) { - rOldDist.splice(3, 1); - rOldDist.splice(0, 0, distance); -} - -/** - * Copies len bytes from distance bytes ago in the buffer to the end of the - * current byte buffer. - * @param {number} length How many bytes to copy. - * @param {number} distance How far back in the buffer from the current write - * pointer to start copying from. - */ -function RarCopyString(len, distance) { - let srcPtr = rBuffer.ptr - distance; - // If we need to go back to previous buffers, then seek back. - if (srcPtr < 0) { - let l = rOldBuffers.length; - while (srcPtr < 0) { - srcPtr = rOldBuffers[--l].data.length + srcPtr; - } - // TODO: lets hope that it never needs to read across buffer boundaries - while (len--) { - rBuffer.insertByte(rOldBuffers[l].data[srcPtr++]); - } - } - if (len > distance) { - while (len--) { - rBuffer.insertByte(rBuffer.data[srcPtr++]); - } - } else { - rBuffer.insertBytes(rBuffer.data.subarray(srcPtr, srcPtr + len)); - } -} - -/** - * @param {RarLocalFile} v - */ -function unpack(v) { - // TODO: implement what happens when unpVer is < 15 - const Ver = v.header.unpVer <= 15 ? 15 : v.header.unpVer; - const Solid = v.header.flags.LHD_SOLID; - const bstream = new BitStream(v.fileData.buffer, true /* rtl */, v.fileData.byteOffset, v.fileData.byteLength); - - rBuffer = new ByteBuffer(v.header.unpackedSize); - - if (logToConsole) { - info('Unpacking ' + v.filename + ' RAR v' + Ver); - } - - switch (Ver) { - case 15: // rar 1.5 compression - Unpack15(bstream, Solid); - break; - case 20: // rar 2.x compression - case 26: // files larger than 2GB - Unpack20(bstream, Solid); - break; - case 29: // rar 3.x compression - case 36: // alternative hash - wBuffer = new ByteBuffer(rBuffer.data.length); - Unpack29(bstream, Solid); - break; - } // switch(method) - - rOldBuffers.push(rBuffer); - // TODO: clear these old buffers when there's over 4MB of history - return rBuffer.data; -} - -/** - */ -class RarLocalFile { - /** - * @param {ByteStream} bstream - */ - constructor(bstream) { - this.header = new RarVolumeHeader(bstream); - this.filename = this.header.filename; - - if (this.header.headType != FILE_HEAD && this.header.headType != ENDARC_HEAD) { - this.isValid = false; - info('Error! RAR Volume did not include a FILE_HEAD header '); - } - else { - // read in the compressed data - /** @type {Uint8Array} */ - this.fileData = null; - if (this.header.packSize > 0) { - this.fileData = bstream.readBytes(this.header.packSize); - this.isValid = true; - } - } - } - - unrar() { - if (!this.header.flags.LHD_SPLIT_BEFORE) { - // unstore file - if (this.header.method == 0x30) { - if (logToConsole) { - info('Unstore ' + this.filename); - } - this.isValid = true; - - currentBytesUnarchivedInFile += this.fileData.length; - currentBytesUnarchived += this.fileData.length; - - // Create a new buffer and copy it over. - const len = this.header.packSize; - const newBuffer = new ByteBuffer(len); - newBuffer.insertBytes(this.fileData); - this.fileData = newBuffer.data; - } else { - this.isValid = true; - this.fileData = unpack(this); - } - } - } -} - -// Reads in the volume and main header. -function unrar_start() { - let bstream = bytestream.tee(); - const header = new RarVolumeHeader(bstream); - if (header.crc == 0x6152 && header.headType == 0x72 && header.flags.value == 0x1A21) { - if (header.headSize == 7) { - if (logToConsole) { - info('Found RAR signature'); - } - - const mhead = new RarVolumeHeader(bstream); - if (mhead.headType != MAIN_HEAD) { - info('Error! RAR did not include a MAIN_HEAD header'); - } else { - bytestream = bstream.tee(); - } - } else if (header.headSize === 0x107) { - throw 'Error! RAR5 files not supported yet. See https://github.com/codedread/bitjs/issues/25'; - } - } -} - -function unrar() { - let bstream = bytestream.tee(); - - let localFile = null; - do { - localFile = new RarLocalFile(bstream); - if (logToConsole) { - info('RAR localFile isValid=' + localFile.isValid + ', volume packSize=' + localFile.header.packSize); - localFile.header.dump(); - } - - if (localFile && localFile.isValid && localFile.header.packSize > 0) { - bytestream = bstream.tee(); - totalUncompressedBytesInArchive += localFile.header.unpackedSize; - allLocalFiles.push(localFile); - - currentFilename = localFile.header.filename; - currentBytesUnarchivedInFile = 0; - localFile.unrar(); - - if (localFile.isValid) { - hostPort.postMessage({ type: 'extract', unarchivedFile: localFile }, [localFile.fileData.buffer]); - postProgress(); - } - } else if (localFile.header.packSize == 0 && localFile.header.unpackedSize == 0) { - // Skip this file. - localFile.isValid = true; - } - } while (localFile.isValid && bstream.getNumBytesLeft() > 0); - - totalFilesInArchive = allLocalFiles.length; - - postProgress(); - - bytestream = bstream.tee(); -}; - -// event.data.file has the first ArrayBuffer. -// event.data.bytes has all subsequent ArrayBuffers. -const onmessage = function (event) { - const bytes = event.data.file || event.data.bytes; - logToConsole = !!event.data.logToConsole; - - // This is the very first time we have been called. Initialize the bytestream. - if (!bytestream) { - bytestream = new ByteStream(bytes); - - currentFilename = ''; - currentFileNumber = 0; - currentBytesUnarchivedInFile = 0; - currentBytesUnarchived = 0; - totalUncompressedBytesInArchive = 0; - totalFilesInArchive = 0; - allLocalFiles = []; - hostPort.postMessage({ type: 'start' }); - } else { - bytestream.push(bytes); - } - - if (unarchiveState === UnarchiveState.NOT_STARTED) { - try { - unrar_start(); - unarchiveState = UnarchiveState.UNARCHIVING; - } catch (e) { - if (typeof e === 'string' && e.startsWith('Error! Overflowed')) { - if (logToConsole) { - console.dir(e); - } - // Overrun the buffer. - unarchiveState = UnarchiveState.WAITING; - postProgress(); - } else { - console.error('Found an error while unrarring'); - console.dir(e); - throw e; - } - } - } - - if (unarchiveState === UnarchiveState.UNARCHIVING || - unarchiveState === UnarchiveState.WAITING) { - try { - unrar(); - unarchiveState = UnarchiveState.FINISHED; - hostPort.postMessage({ type: 'finish', metadata: {} }); - } catch (e) { - if (typeof e === 'string' && e.startsWith('Error! Overflowed')) { - if (logToConsole) { - console.dir(e); - } - // Overrun the buffer. - unarchiveState = UnarchiveState.WAITING; - } else { - console.error('Found an error while unrarring'); - console.dir(e); - throw e; - } - } - } -}; - -/** - * Connect the host to the unrar implementation with the given MessagePort. - * @param {MessagePort} port - */ -export function connect(port) { - if (hostPort) { - throw `hostPort already connected in unrar.js`; - } - hostPort = port; - port.onmessage = onmessage; -} - -export function disconnect() { - if (!hostPort) { - throw `hostPort was not connected in unzip.js`; - } - - hostPort = null; - - unarchiveState = UnarchiveState.NOT_STARTED; - bytestream = null; - allLocalFiles = null; - logToConsole = false; - - currentFilename = ''; - currentFileNumber = 0; - currentBytesUnarchivedInFile = 0; - currentBytesUnarchived = 0; - totalUncompressedBytesInArchive = 0; - totalFilesInArchive = 0; -} diff --git a/archive/untar.js b/archive/untar.js deleted file mode 100644 index 51adf64..0000000 --- a/archive/untar.js +++ /dev/null @@ -1,249 +0,0 @@ -/** - * untar.js - * - * Licensed under the MIT License - * - * Copyright(c) 2011 Google Inc. - * - * Reference Documentation: - * - * TAR format: http://www.gnu.org/software/automake/manual/tar/Standard.html - */ - -import { ByteStream } from '../io/bytestream.js'; - -const UnarchiveState = { - NOT_STARTED: 0, - UNARCHIVING: 1, - WAITING: 2, - FINISHED: 3, -}; - -/** @type {MessagePort} */ -let hostPort; - -// State - consider putting these into a class. -let unarchiveState = UnarchiveState.NOT_STARTED; -/** @type {ByteStream} */ -let bytestream = null; -let allLocalFiles = null; -let logToConsole = false; - -// Progress variables. -let currentFilename = ''; -let currentFileNumber = 0; -let currentBytesUnarchivedInFile = 0; -let currentBytesUnarchived = 0; -let totalUncompressedBytesInArchive = 0; -let totalFilesInArchive = 0; - -// Helper functions. -const info = function (str) { - hostPort.postMessage({ type: 'info', msg: str }); -}; -const err = function (str) { - hostPort.postMessage({ type: 'error', msg: str }); -}; -const postProgress = function () { - hostPort.postMessage({ - type: 'progress', - currentFilename, - currentFileNumber, - currentBytesUnarchivedInFile, - currentBytesUnarchived, - totalUncompressedBytesInArchive, - totalFilesInArchive, - totalCompressedBytesRead: bytestream.getNumBytesRead(), - }); -}; - -// Removes all characters from the first zero-byte in the string onwards. -const readCleanString = function (bstr, numBytes) { - const str = bstr.readString(numBytes); - const zIndex = str.indexOf(String.fromCharCode(0)); - return zIndex != -1 ? str.substr(0, zIndex) : str; -}; - -class TarLocalFile { - // takes a ByteStream and parses out the local file information - constructor(bstream) { - this.isValid = false; - - let bytesRead = 0; - - // Read in the header block - this.name = readCleanString(bstream, 100); - this.mode = readCleanString(bstream, 8); - this.uid = readCleanString(bstream, 8); - this.gid = readCleanString(bstream, 8); - this.size = parseInt(readCleanString(bstream, 12), 8); - this.mtime = readCleanString(bstream, 12); - this.chksum = readCleanString(bstream, 8); - this.typeflag = readCleanString(bstream, 1); - this.linkname = readCleanString(bstream, 100); - this.maybeMagic = readCleanString(bstream, 6); - - if (this.maybeMagic == 'ustar') { - this.version = readCleanString(bstream, 2); - this.uname = readCleanString(bstream, 32); - this.gname = readCleanString(bstream, 32); - this.devmajor = readCleanString(bstream, 8); - this.devminor = readCleanString(bstream, 8); - this.prefix = readCleanString(bstream, 155); - - // From https://linux.die.net/man/1/ustar: - // "The name field (100 chars) an inserted slash ('/') and the prefix field (155 chars) - // produce the pathname of the file. When recreating the original filename, name and prefix - // are concatenated, using a slash character in the middle. If a pathname does not fit in the - // space provided or may not be split at a slash character so that the parts will fit into - // 100 + 155 chars, the file may not be archived. Linknames longer than 100 chars may not be - // archived too." - if (this.prefix.length) { - this.name = `${this.prefix}/${this.name}`; - } - bstream.readBytes(12); // 512 - 500 - } else { - bstream.readBytes(255); // 512 - 257 - } - - bytesRead += 512; - - // Done header, now rest of blocks are the file contents. - this.filename = this.name; - /** @type {Uint8Array} */ - this.fileData = null; - - info(`Untarring file '${this.filename}'`); - info(` size = ${this.size}`); - info(` typeflag = ${this.typeflag}`); - - // A regular file. - if (this.typeflag == 0) { - info(' This is a regular file.'); - const sizeInBytes = parseInt(this.size); - this.fileData = new Uint8Array(bstream.readBytes(sizeInBytes)); - bytesRead += sizeInBytes; - if (this.name.length > 0 && this.size > 0 && this.fileData && this.fileData.buffer) { - this.isValid = true; - } - - // Round up to 512-byte blocks. - const remaining = 512 - bytesRead % 512; - if (remaining > 0 && remaining < 512) { - bstream.readBytes(remaining); - } - } else if (this.typeflag == 5) { - info(' This is a directory.') - } - } -} - -const untar = function () { - let bstream = bytestream.tee(); - - // While we don't encounter an empty block, keep making TarLocalFiles. - while (bstream.peekNumber(4) != 0) { - const oneLocalFile = new TarLocalFile(bstream); - if (oneLocalFile && oneLocalFile.isValid) { - // If we make it to this point and haven't thrown an error, we have successfully - // read in the data for a local file, so we can update the actual bytestream. - bytestream = bstream.tee(); - - allLocalFiles.push(oneLocalFile); - totalUncompressedBytesInArchive += oneLocalFile.size; - - // update progress - currentFilename = oneLocalFile.filename; - currentFileNumber = totalFilesInArchive++; - currentBytesUnarchivedInFile = oneLocalFile.size; - currentBytesUnarchived += oneLocalFile.size; - hostPort.postMessage({ type: 'extract', unarchivedFile: oneLocalFile }, [oneLocalFile.fileData.buffer]); - postProgress(); - } - } - totalFilesInArchive = allLocalFiles.length; - - postProgress(); - - bytestream = bstream.tee(); -}; - -// event.data.file has the first ArrayBuffer. -// event.data.bytes has all subsequent ArrayBuffers. -const onmessage = function (event) { - const bytes = event.data.file || event.data.bytes; - logToConsole = !!event.data.logToConsole; - - // This is the very first time we have been called. Initialize the bytestream. - if (!bytestream) { - bytestream = new ByteStream(bytes); - } else { - bytestream.push(bytes); - } - - if (unarchiveState === UnarchiveState.NOT_STARTED) { - currentFilename = ''; - currentFileNumber = 0; - currentBytesUnarchivedInFile = 0; - currentBytesUnarchived = 0; - totalUncompressedBytesInArchive = 0; - totalFilesInArchive = 0; - allLocalFiles = []; - - hostPort.postMessage({ type: 'start' }); - - unarchiveState = UnarchiveState.UNARCHIVING; - - postProgress(); - } - - if (unarchiveState === UnarchiveState.UNARCHIVING || - unarchiveState === UnarchiveState.WAITING) { - try { - untar(); - unarchiveState = UnarchiveState.FINISHED; - hostPort.postMessage({ type: 'finish', metadata: {} }); - } catch (e) { - if (typeof e === 'string' && e.startsWith('Error! Overflowed')) { - // Overrun the buffer. - unarchiveState = UnarchiveState.WAITING; - } else { - console.error('Found an error while untarring'); - console.dir(e); - throw e; - } - } - } -}; - -/** - * Connect the host to the untar implementation with the given MessagePort. - * @param {MessagePort} port - */ -export function connect(port) { - if (hostPort) { - throw `hostPort already connected in untar.js`; - } - hostPort = port; - port.onmessage = onmessage; -} - -export function disconnect() { - if (!hostPort) { - throw `hostPort was not connected in unzip.js`; - } - - hostPort = null; - - unarchiveState = UnarchiveState.NOT_STARTED; - bytestream = null; - allLocalFiles = null; - logToConsole = false; - - currentFilename = ''; - currentFileNumber = 0; - currentBytesUnarchivedInFile = 0; - currentBytesUnarchived = 0; - totalUncompressedBytesInArchive = 0; - totalFilesInArchive = 0; -} diff --git a/archive/unzip.js b/archive/unzip.js deleted file mode 100644 index 215fe23..0000000 --- a/archive/unzip.js +++ /dev/null @@ -1,414 +0,0 @@ -/** - * unzip.js - * - * Licensed under the MIT License - * - * Copyright(c) 2011 Google Inc. - * Copyright(c) 2011 antimatter15 - * - * Reference Documentation: - * - * ZIP format: http://www.pkware.com/documents/casestudies/APPNOTE.TXT - * DEFLATE format: http://tools.ietf.org/html/rfc1951 - */ - -import { ByteStream } from '../io/bytestream.js'; -import { ARCHIVE_EXTRA_DATA_SIG, CENTRAL_FILE_HEADER_SIG, CRC32_MAGIC_NUMBER, - DATA_DESCRIPTOR_SIG, DIGITAL_SIGNATURE_SIG, END_OF_CENTRAL_DIR_SIG, - LOCAL_FILE_HEADER_SIG } from './common.js'; -import { inflate } from './inflate.js'; - -const UnarchiveState = { - NOT_STARTED: 0, - UNARCHIVING: 1, - WAITING: 2, - FINISHED: 3, -}; - -/** @type {MessagePort} */ -let hostPort; - -// State - consider putting these into a class. -let unarchiveState = UnarchiveState.NOT_STARTED; -/** @type {ByteStream} */ -let bytestream = null; -let allLocalFiles = null; -let logToConsole = false; - -// Progress variables. -let currentFilename = ''; -let currentFileNumber = 0; -let currentBytesUnarchivedInFile = 0; -let currentBytesUnarchived = 0; -let totalUncompressedBytesInArchive = 0; -let totalFilesInArchive = 0; - -// Helper functions. -const info = function (str) { - hostPort.postMessage({ type: 'info', msg: str }); -}; -const err = function (str) { - hostPort.postMessage({ type: 'error', msg: str }); -}; -const postProgress = function () { - hostPort.postMessage({ - type: 'progress', - currentFilename, - currentFileNumber, - currentBytesUnarchivedInFile, - currentBytesUnarchived, - totalUncompressedBytesInArchive, - totalFilesInArchive, - totalCompressedBytesRead: bytestream.getNumBytesRead(), - }); -}; - -// mask for getting the Nth bit (zero-based) -const BIT = [0x01, 0x02, 0x04, 0x08, - 0x10, 0x20, 0x40, 0x80, - 0x100, 0x200, 0x400, 0x800, - 0x1000, 0x2000, 0x4000, 0x8000]; - -class ZipLocalFile { - /** @param {ByteStream} bstream */ - constructor(bstream) { - if (typeof bstream != typeof {} || !bstream.readNumber || typeof bstream.readNumber != typeof function () { }) { - return null; - } - - bstream.readNumber(4); // swallow signature - this.version = bstream.readNumber(2); - this.generalPurpose = bstream.readNumber(2); - this.compressionMethod = bstream.readNumber(2); - this.lastModFileTime = bstream.readNumber(2); - this.lastModFileDate = bstream.readNumber(2); - this.crc32 = bstream.readNumber(4); - this.compressedSize = bstream.readNumber(4); - this.uncompressedSize = bstream.readNumber(4); - this.fileNameLength = bstream.readNumber(2); - this.extraFieldLength = bstream.readNumber(2); - - this.filename = null; - if (this.fileNameLength > 0) { - this.filename = bstream.readString(this.fileNameLength); - } - - this.extraField = null; - if (this.extraFieldLength > 0) { - this.extraField = bstream.readString(this.extraFieldLength); - } - - // Data descriptor is present if this bit is set, compressed size should be zero. - this.hasDataDescriptor = ((this.generalPurpose & BIT[3]) !== 0); - if (this.hasDataDescriptor && - (this.crc32 !== 0 || this.compressedSize !== 0 || this.uncompressedSize !== 0)) { - err('Zip local file with a data descriptor and non-zero crc/compressedSize/uncompressedSize'); - } - - // Read in the compressed data if we have no data descriptor. - /** @type {Uint8Array} */ - this.fileData = null; - let descriptorSize = 0; - if (this.hasDataDescriptor) { - // Hold on to a reference to the bstream, since that is where the compressed file data begins. - let savedBstream = bstream.tee(); - - // Seek ahead one byte at a time, looking for the next local file header signature or the end - // of all local files. - let foundDataDescriptor = false; - let numBytesSeeked = 0; - while (!foundDataDescriptor) { - while (bstream.peekNumber(4) !== LOCAL_FILE_HEADER_SIG && - bstream.peekNumber(4) !== ARCHIVE_EXTRA_DATA_SIG && - bstream.peekNumber(4) !== CENTRAL_FILE_HEADER_SIG) { - numBytesSeeked++; - bstream.readBytes(1); - } - - // Copy all the read bytes into a buffer and examine the last 16 bytes to see if they are the - // data descriptor. - let bufferedByteArr = savedBstream.peekBytes(numBytesSeeked); - const descriptorStream = new ByteStream(bufferedByteArr.buffer, numBytesSeeked - 16, 16); - const maybeDescriptorSig = descriptorStream.readNumber(4); - const maybeCrc32 = descriptorStream.readNumber(4); - const maybeCompressedSize = descriptorStream.readNumber(4); - const maybeUncompressedSize = descriptorStream.readNumber(4); - - // From the PKZIP App Note: "The signature value 0x08074b50 is also used by some ZIP - // implementations as a marker for the Data Descriptor record". - if (maybeDescriptorSig === DATA_DESCRIPTOR_SIG) { - if (maybeCompressedSize === (numBytesSeeked - 16)) { - foundDataDescriptor = true; - descriptorSize = 16; - } - } else if (maybeCompressedSize === (numBytesSeeked - 12)) { - foundDataDescriptor = true; - descriptorSize = 12; - } - - if (foundDataDescriptor) { - this.crc32 = maybeCrc32; - this.compressedSize = maybeCompressedSize; - this.uncompressedSize = maybeUncompressedSize; - } - } - bstream = savedBstream; - } - - this.fileData = new Uint8Array(bstream.readBytes(this.compressedSize)); - bstream.readBytes(descriptorSize); - - // Now that we have all the bytes for this file, we can print out some information. - if (logToConsole) { - info('Zip Local File Header:'); - info(` version=${this.version}`); - info(` general purpose=${this.generalPurpose}`); - info(` compression method=${this.compressionMethod}`); - info(` last mod file time=${this.lastModFileTime}`); - info(` last mod file date=${this.lastModFileDate}`); - info(` crc32=${this.crc32}`); - info(` compressed size=${this.compressedSize}`); - info(` uncompressed size=${this.uncompressedSize}`); - info(` file name length=${this.fileNameLength}`); - info(` extra field length=${this.extraFieldLength}`); - info(` filename = '${this.filename}'`); - info(` hasDataDescriptor = ${this.hasDataDescriptor}`); - } - } - - // determine what kind of compressed data we have and decompress - async unzip() { - if (!this.fileData) { - err('unzip() called on a file with out compressed file data'); - } - - // Zip Version 1.0, no compression (store only) - if (this.compressionMethod == 0) { - if (logToConsole) { - info(`ZIP v${this.version}, store only: ${this.filename} (${this.compressedSize} bytes)`); - } - currentBytesUnarchivedInFile = this.compressedSize; - currentBytesUnarchived += this.compressedSize; - } - // version == 20, compression method == 8 (DEFLATE) - else if (this.compressionMethod == 8) { - if (logToConsole) { - info(`ZIP v2.0, DEFLATE: ${this.filename} (${this.compressedSize} bytes)`); - } - this.fileData = await inflate(this.fileData, this.uncompressedSize); - } - else { - err(`UNSUPPORTED VERSION/FORMAT: ZIP v${this.version}, ` + - `compression method=${this.compressionMethod}: ` + - `${this.filename} (${this.compressedSize} bytes)`); - this.fileData = null; - } - } -} - -async function archiveUnzip() { - let bstream = bytestream.tee(); - - // loop until we don't see any more local files or we find a data descriptor. - while (bstream.peekNumber(4) == LOCAL_FILE_HEADER_SIG) { - // Note that this could throw an error if the bstream overflows, which is caught in the - // message handler. - const oneLocalFile = new ZipLocalFile(bstream); - // this should strip out directories/folders - if (oneLocalFile && oneLocalFile.uncompressedSize > 0 && oneLocalFile.fileData) { - // If we make it to this point and haven't thrown an error, we have successfully - // read in the data for a local file, so we can update the actual bytestream. - bytestream = bstream.tee(); - - allLocalFiles.push(oneLocalFile); - totalUncompressedBytesInArchive += oneLocalFile.uncompressedSize; - - // update progress - currentFilename = oneLocalFile.filename; - currentFileNumber = allLocalFiles.length - 1; - currentBytesUnarchivedInFile = 0; - - // Actually do the unzipping. - await oneLocalFile.unzip(); - - if (oneLocalFile.fileData != null) { - hostPort.postMessage({ type: 'extract', unarchivedFile: oneLocalFile }, [oneLocalFile.fileData.buffer]); - postProgress(); - } - } - } - totalFilesInArchive = allLocalFiles.length; - - // archive extra data record - if (bstream.peekNumber(4) == ARCHIVE_EXTRA_DATA_SIG) { - if (logToConsole) { - info(' Found an Archive Extra Data Signature'); - } - - // skipping this record for now - bstream.readNumber(4); - const archiveExtraFieldLength = bstream.readNumber(4); - bstream.readString(archiveExtraFieldLength); - } - - // central directory structure - // TODO: handle the rest of the structures (Zip64 stuff) - if (bstream.peekNumber(4) == CENTRAL_FILE_HEADER_SIG) { - if (logToConsole) { - info(' Found a Central File Header'); - } - - // read all file headers - while (bstream.peekNumber(4) == CENTRAL_FILE_HEADER_SIG) { - bstream.readNumber(4); // signature - const cdfh = { - versionMadeBy: bstream.readNumber(2), - versionNeededToExtract: bstream.readNumber(2), - generalPurposeBitFlag: bstream.readNumber(2), - compressionMethod: bstream.readNumber(2), - lastModFileTime: bstream.readNumber(2), - lastModFileDate: bstream.readNumber(2), - crc32: bstream.readNumber(4), - compressedSize: bstream.readNumber(4), - uncompressedSize: bstream.readNumber(4), - fileNameLength: bstream.readNumber(2), - extraFieldLength: bstream.readNumber(2), - fileCommentLength: bstream.readNumber(2), - diskNumberStart: bstream.readNumber(2), - internalFileAttributes: bstream.readNumber(2), - externalFileAttributes: bstream.readNumber(4), - relativeOffset: bstream.readNumber(4), - }; - cdfh.fileName = bstream.readString(cdfh.fileNameLength); - cdfh.extraField = bstream.readString(cdfh.extraFieldLength); - cdfh.fileComment = bstream.readString(cdfh.fileCommentLength); - if (logToConsole) { - console.log('Central Directory File Header:'); - for (const field in cdfh) { - console.log(` ${field} = ${cdfh[field]}`); - } - } - } - } - - // digital signature - if (bstream.peekNumber(4) == DIGITAL_SIGNATURE_SIG) { - if (logToConsole) { - info(' Found a Digital Signature'); - } - - bstream.readNumber(4); - const sizeOfSignature = bstream.readNumber(2); - bstream.readString(sizeOfSignature); // digital signature data - } - - let metadata = {}; - if (bstream.peekNumber(4) == END_OF_CENTRAL_DIR_SIG) { - bstream.readNumber(4); // signature - const eocds = { - numberOfThisDisk: bstream.readNumber(2), - diskWhereCentralDirectoryStarts: bstream.readNumber(2), - numberOfCentralDirectoryRecordsOnThisDisk: bstream.readNumber(2), - totalNumberOfCentralDirectoryRecords: bstream.readNumber(2), - sizeOfCentralDirectory: bstream.readNumber(4), - offsetOfStartOfCentralDirectory: bstream.readNumber(4), - commentLength: bstream.readNumber(2), - }; - eocds.comment = bstream.readString(eocds.commentLength); - if (logToConsole) { - console.log('End of Central Dir Signature:'); - for (const field in eocds) { - console.log(` ${field} = ${eocds[field]}`); - } - } - metadata.comment = eocds.comment; - } - - postProgress(); - - bytestream = bstream.tee(); - - unarchiveState = UnarchiveState.FINISHED; - hostPort.postMessage({ type: 'finish', metadata }); -} - -// event.data.file has the first ArrayBuffer. -// event.data.bytes has all subsequent ArrayBuffers. -const onmessage = async function (event) { - const bytes = event.data.file || event.data.bytes; - logToConsole = !!event.data.logToConsole; - - // This is the very first time we have been called. Initialize the bytestream. - if (!bytestream) { - bytestream = new ByteStream(bytes); - } else { - bytestream.push(bytes); - } - - if (unarchiveState === UnarchiveState.NOT_STARTED) { - currentFilename = ''; - currentFileNumber = 0; - currentBytesUnarchivedInFile = 0; - currentBytesUnarchived = 0; - totalUncompressedBytesInArchive = 0; - totalFilesInArchive = 0; - currentBytesUnarchived = 0; - allLocalFiles = []; - - hostPort.postMessage({ type: 'start' }); - - unarchiveState = UnarchiveState.UNARCHIVING; - - postProgress(); - } - - if (unarchiveState === UnarchiveState.UNARCHIVING || - unarchiveState === UnarchiveState.WAITING) { - try { - await archiveUnzip(); - } catch (e) { - if (typeof e === 'string' && e.startsWith('Error! Overflowed')) { - // Overrun the buffer. - unarchiveState = UnarchiveState.WAITING; - } else { - console.error('Found an error while unzipping'); - console.dir(e); - throw e; - } - } - } -}; - -/** - * Connect the host to the unzip implementation with the given MessagePort. - * @param {MessagePort} port - */ -export function connect(port) { - if (hostPort) { - throw `hostPort already connected in unzip.js`; - } - - hostPort = port; - port.onmessage = onmessage; -} - -export function disconnect() { - if (!hostPort) { - throw `hostPort was not connected in unzip.js`; - } - - hostPort = null; - - unarchiveState = UnarchiveState.NOT_STARTED; - bytestream = null; - allLocalFiles = null; - logToConsole = false; - - // Progress variables. - currentFilename = ''; - currentFileNumber = 0; - currentBytesUnarchivedInFile = 0; - currentBytesUnarchived = 0; - totalUncompressedBytesInArchive = 0; - totalFilesInArchive = 0; -} diff --git a/archive/webworker-wrapper.js b/archive/webworker-wrapper.js deleted file mode 100644 index db6ca2b..0000000 --- a/archive/webworker-wrapper.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * webworker-wrapper.js - * - * Licensed under the MIT License - * - * Copyright(c) 2023 Google Inc. - */ - -/** - * A WebWorker wrapper for a decompress/compress implementation. Upon creation and being sent its - * first message, it dynamically imports the decompressor / compressor implementation and connects - * the message port. All other communication takes place over the MessageChannel. - */ - -/** @type {MessagePort} */ -let implPort; - -let module; - -onmessage = async (evt) => { - if (evt.data.implSrc) { - module = await import(evt.data.implSrc); - module.connect(evt.ports[0]); - } else if (evt.data.disconnect) { - module.disconnect(); - } -}; diff --git a/archive/zip.js b/archive/zip.js deleted file mode 100644 index cbcc46c..0000000 --- a/archive/zip.js +++ /dev/null @@ -1,310 +0,0 @@ -/** - * zip.js - * - * Licensed under the MIT License - * - * Copyright(c) 2021 Google Inc. - * - * Reference Documentation: - * - * ZIP format: http://www.pkware.com/documents/casestudies/APPNOTE.TXT - * DEFLATE format: http://tools.ietf.org/html/rfc1951 - */ - -import { ByteBuffer } from '../io/bytebuffer.js'; -import { CENTRAL_FILE_HEADER_SIG, CRC32_MAGIC_NUMBER, END_OF_CENTRAL_DIR_SIG, - LOCAL_FILE_HEADER_SIG, ZipCompressionMethod } from './common.js'; - -/** @typedef {import('./common.js').FileInfo} FileInfo */ - -/** @type {MessagePort} */ -let hostPort; - -/** - * The client sends a set of CompressFilesMessage to the MessagePort containing files to archive in - * order. The client sets isLastFile to true to indicate to the implementation when the last file - * has been sent to be compressed. - * - * The impl posts an event to the port indicating compression has started: { type: 'start' }. - * As each file compresses, bytes are sent back in order: { type: 'compress', bytes: Uint8Array }. - * After the last file compresses, we indicate finish by: { type 'finish' } - * - * The client should append the bytes to a single buffer in the order they were received. - */ - -// TODO(bitjs): Figure out where this typedef should live. -/** - * @typedef CompressFilesMessage A message the client sends to the implementation. - * @property {FileInfo[]} files A set of files to add to the zip file. - * @property {boolean} isLastFile Indicates this is the last set of files to add to the zip file. - * @property {ZipCompressionMethod=} compressionMethod The compression method to use. Ignored except - * for the first message sent. - */ - -// TODO: Support options that can let client choose levels of compression/performance. - -/** - * @typedef CentralDirectoryFileHeaderInfo An object to be used to construct the central directory. - * @property {string} fileName - * @property {number} compressionMethod (2 bytes) - * @property {number} lastModFileTime (2 bytes) - * @property {number} lastModFileDate (2 bytes) - * @property {number} crc32 (4 bytes) - * @property {number} compressedSize (4 bytes) - * @property {number} uncompressedSize (4 bytes) - * @property {number} byteOffset (4 bytes) - */ - -/** @type {ZipCompressionMethod} */ -let compressionMethod = ZipCompressionMethod.STORE; - -/** @type {FileInfo[]} */ -let filesCompressed = []; - -/** @type {CentralDirectoryFileHeaderInfo[]} */ -let centralDirectoryInfos = []; - -/** @type {number} */ -let numBytesWritten = 0; - -const CompressorState = { - NOT_STARTED: 0, - COMPRESSING: 1, - WAITING: 2, - FINISHED: 3, -}; -let state = CompressorState.NOT_STARTED; -const crc32Table = createCRC32Table(); - -/** Helper functions. */ - -/** - * Logic taken from https://github.com/nodeca/pako/blob/master/lib/zlib/crc32.js - * @returns {Uint8Array} - */ -function createCRC32Table() { - const table = new Uint32Array(256); - for (let n = 0; n < 256; n++) { - let c = n; - for (let k = 0; k < 8; k++) { - c = ((c & 1) ? (CRC32_MAGIC_NUMBER ^ (c >>> 1)) : (c >>> 1)); - } - table[n] = c; - } - - return table; -}; - -/** - * Logic taken from https://github.com/nodeca/pako/blob/master/lib/zlib/crc32.js - * @param {number} crc - * @param {Uint8Array} bytes - * @returns {number} - */ -function calculateCRC32(crc, bytes) { - const len = bytes.byteLength; - crc ^= -1; - for (const byte of bytes) { - crc = (crc >>> 8) ^ crc32Table[(crc ^ byte) & 0xFF]; - } - crc ^= -1; - if (crc < 0) { - crc += 0x100000000; - } - return crc; -} - -/** - * Logic taken from https://github.com/thejoshwolfe/yazl. - * @param {number} lastModTime The number of ms since the Unix epoch (1970-01-01 at midnight). - * @returns {number} - */ -function dateToDosDate(jsDate) { - let date = 0; - date |= jsDate.getDate() & 0x1f; // 1-31 - date |= ((jsDate.getMonth() + 1) & 0xf) << 5; // 0-11, 1-12 - date |= ((jsDate.getFullYear() - 1980) & 0x7f) << 9; // 0-128, 1980-2108 - return date; -} - -/** - * Logic taken from https://github.com/thejoshwolfe/yazl. - * @param {number} lastModTime The number of ms since the Unix epoch (1970-01-01 at midnight). - * @returns {number} - */ -function dateToDosTime(jsDate) { - let time = 0; - time |= Math.floor(jsDate.getSeconds() / 2); // 0-59, 0-29 (lose odd numbers) - time |= (jsDate.getMinutes() & 0x3f) << 5; // 0-59 - time |= (jsDate.getHours() & 0x1f) << 11; // 0-23 - return time; -} - -/** - * @param {FileInfo} file - * @returns {Promise} - */ -async function zipOneFile(file) { - /** @type {Uint8Array} */ - let compressedBytes; - if (compressionMethod === ZipCompressionMethod.STORE) { - compressedBytes = file.fileData; - } else if (compressionMethod === ZipCompressionMethod.DEFLATE) { - const blob = new Blob([file.fileData.buffer]); - const compressedStream = blob.stream().pipeThrough(new CompressionStream('deflate-raw')); - compressedBytes = new Uint8Array(await new Response(compressedStream).arrayBuffer()); - } - - // Zip Local File Header has 30 bytes and then the filename and extrafields. - const fileHeaderSize = 30 + file.fileName.length; - - /** @type {ByteBuffer} */ - const buffer = new ByteBuffer(fileHeaderSize + compressedBytes.byteLength); - - buffer.writeNumber(LOCAL_FILE_HEADER_SIG, 4); // Magic number. - buffer.writeNumber(0x0A, 2); // Version. - buffer.writeNumber(0, 2); // General Purpose Flags. - buffer.writeNumber(compressionMethod, 2); // Compression Method. - - const jsDate = new Date(file.lastModTime); - - /** @type {CentralDirectoryFileHeaderInfo} */ - const centralDirectoryInfo = { - compressionMethod, - lastModFileTime: dateToDosTime(jsDate), - lastModFileDate: dateToDosDate(jsDate), - crc32: calculateCRC32(0, file.fileData), - compressedSize: compressedBytes.byteLength, - uncompressedSize: file.fileData.byteLength, - fileName: file.fileName, - byteOffset: numBytesWritten, - }; - centralDirectoryInfos.push(centralDirectoryInfo); - - buffer.writeNumber(centralDirectoryInfo.lastModFileTime, 2); // Last Mod File Time. - buffer.writeNumber(centralDirectoryInfo.lastModFileDate, 2); // Last Mod Date. - buffer.writeNumber(centralDirectoryInfo.crc32, 4); // crc32. - buffer.writeNumber(centralDirectoryInfo.compressedSize, 4); // Compressed size. - buffer.writeNumber(centralDirectoryInfo.uncompressedSize, 4); // Uncompressed size. - buffer.writeNumber(centralDirectoryInfo.fileName.length, 2); // Filename length. - buffer.writeNumber(0, 2); // Extra field length. - buffer.writeASCIIString(centralDirectoryInfo.fileName); // Filename. Assumes ASCII. - buffer.insertBytes(compressedBytes); - - return buffer; -} - -/** - * @returns {ByteBuffer} - */ -function writeCentralFileDirectory() { - // Each central directory file header is 46 bytes + the filename. - let cdsLength = filesCompressed.map(f => f.fileName.length + 46).reduce((a, c) => a + c); - // 22 extra bytes for the end-of-central-dir header. - const buffer = new ByteBuffer(cdsLength + 22); - - for (const cdInfo of centralDirectoryInfos) { - buffer.writeNumber(CENTRAL_FILE_HEADER_SIG, 4); // Magic number. - buffer.writeNumber(0, 2); // Version made by. // 0x31e - buffer.writeNumber(0, 2); // Version needed to extract (minimum). // 0x14 - buffer.writeNumber(0, 2); // General purpose bit flag - buffer.writeNumber(compressionMethod, 2); // Compression method. - buffer.writeNumber(cdInfo.lastModFileTime, 2); // Last Mod File Time. - buffer.writeNumber(cdInfo.lastModFileDate, 2); // Last Mod Date. - buffer.writeNumber(cdInfo.crc32, 4); // crc32. - buffer.writeNumber(cdInfo.compressedSize, 4); // Compressed size. - buffer.writeNumber(cdInfo.uncompressedSize, 4); // Uncompressed size. - buffer.writeNumber(cdInfo.fileName.length, 2); // File name length. - buffer.writeNumber(0, 2); // Extra field length. - buffer.writeNumber(0, 2); // Comment length. - buffer.writeNumber(0, 2); // Disk number where file starts. - buffer.writeNumber(0, 2); // Internal file attributes. - buffer.writeNumber(0, 4); // External file attributes. - buffer.writeNumber(cdInfo.byteOffset, 4); // Relative offset of local file header. - buffer.writeASCIIString(cdInfo.fileName); // File name. - } - - // 22 more bytes. - buffer.writeNumber(END_OF_CENTRAL_DIR_SIG, 4); // Magic number. - buffer.writeNumber(0, 2); // Number of this disk. - buffer.writeNumber(0, 2); // Disk where central directory starts. - buffer.writeNumber(filesCompressed.length, 2); // Number of central directory records on this disk. - buffer.writeNumber(filesCompressed.length, 2); // Total number of central directory records. - buffer.writeNumber(cdsLength, 4); // Size of central directory. - buffer.writeNumber(numBytesWritten, 4); // Offset of start of central directory. - buffer.writeNumber(0, 2); // Comment length. - - return buffer; -} - -/** - * @param {{data: CompressFilesMessage}} evt The event for the implementation to process. It is an - * error to send any more events after a previous event had isLastFile is set to true. - */ -const onmessage = async function(evt) { - if (state === CompressorState.FINISHED) { - throw `The zip implementation was sent a message after last file received.`; - } - - if (state === CompressorState.NOT_STARTED) { - hostPort.postMessage({ type: 'start' }); - } - - state = CompressorState.COMPRESSING; - - if (filesCompressed.length === 0 && evt.data.compressionMethod !== undefined) { - if (!Object.values(ZipCompressionMethod).includes(evt.data.compressionMethod)) { - throw `Do not support compression method ${evt.data.compressionMethod}`; - } - - compressionMethod = evt.data.compressionMethod; - } - - const msg = evt.data; - const filesToCompress = msg.files; - while (filesToCompress.length > 0) { - const fileInfo = filesToCompress.shift(); - const fileBuffer = await zipOneFile(fileInfo); - filesCompressed.push(fileInfo); - numBytesWritten += fileBuffer.data.byteLength; - hostPort.postMessage({ type: 'compress', bytes: fileBuffer.data }, [ fileBuffer.data.buffer ]); - } - - if (evt.data.isLastFile) { - const centralBuffer = writeCentralFileDirectory(); - numBytesWritten += centralBuffer.data.byteLength; - hostPort.postMessage({ type: 'compress', bytes: centralBuffer.data }, - [ centralBuffer.data.buffer ]); - - state = CompressorState.FINISHED; - hostPort.postMessage({ type: 'finish' }); - } else { - state = CompressorState.WAITING; - } -}; - - -/** - * Connect the host to the zip implementation with the given MessagePort. - * @param {MessagePort} port - */ -export function connect(port) { - if (hostPort) { - throw `hostPort already connected in zip.js`; - } - hostPort = port; - port.onmessage = onmessage; -} - -export function disconnect() { - if (!hostPort) { - throw `hostPort was not connected in zip.js`; - } - - hostPort = null; - - filesCompressed = []; - centralDirectoryInfos = []; - numBytesWritten = 0; - state = CompressorState.NOT_STARTED; -} diff --git a/build/Dockerfile b/build/Dockerfile deleted file mode 100644 index 1501a9a..0000000 --- a/build/Dockerfile +++ /dev/null @@ -1,21 +0,0 @@ -FROM ubuntu:18.04 - -RUN apt-get update && apt-get install -y \ - cmake \ - git-core \ - python \ - wget - -RUN git clone https://github.com/webmproject/libwebp - -RUN git clone https://github.com/emscripten-core/emsdk.git && \ - cd emsdk && \ - ./emsdk install latest && \ - ./emsdk activate latest - -RUN git clone https://github.com/nothings/stb.git - -# TODO: Figure out how to invoke emsdk/emsdk_env.sh and have it affect PATH. -ENV PATH="$PATH:/emsdk:/emsdk/node/12.9.1_64bit/bin:/emsdk/upstream/emscripten" - -COPY . . diff --git a/build/Makefile b/build/Makefile deleted file mode 100644 index 224db92..0000000 --- a/build/Makefile +++ /dev/null @@ -1,9 +0,0 @@ - -all: bitjs-image-webp-shim - -clean: - $(MAKE) -C image/webp-shim clean - -# Make webp-shim/ -bitjs-image-webp-shim: - $(MAKE) -C image/webp-shim diff --git a/build/README.md b/build/README.md deleted file mode 100644 index 1f96a6a..0000000 --- a/build/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Build - -This folder contains files needed to build various pieces of the library. You only need to worry -about this if you intend on patching / modifying the library in some way. It is only needed for the -WebP Shim parts of bitjs. - -## Prerequisites - * Install Docker - * git clone this repository - -## Build Instructions - -Assuming you have cloned the repository in /path/to/bitjs: - * cd /path/to/bitjs/build/ - * Build the Docker image: `docker build -f Dockerfile -t wps/0.1 .` - * Run the Docker image: `docker run -it --mount type=bind,source=/path/to/bitjs/,target=/out wps/0.1` - * Build the library: `make` - * Exit the Docker image: `exit` - -Various library files will be output to /path/to/bitjs/. For example, the -/path/to/bitjs/image/webp-shim/ directory will now contain update webp-shim-module.js and -webp-shim-module.wasm files. - -# TODO(2.0): Remove this. diff --git a/build/image/webp-shim/Makefile b/build/image/webp-shim/Makefile deleted file mode 100644 index 95b1f56..0000000 --- a/build/image/webp-shim/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -OUT_PATH=/out/image/webp-shim -OUT_NAME=webp-shim-module -OUT_TARGET=${OUT_PATH}/${OUT_NAME}.js - -LIBWEBP_PATH=/libwebp/src -LIBWEBP_SRC=$(shell find ${LIBWEBP_PATH}/ -name '*.c') - -SHIM_PATH=src -SHIM_SRC=${SHIM_PATH}/webp.c - -debug: ${OUT_TARGET} -clean: - rm -rf ${OUT_PATH}/${OUT_NAME}.* - -# Debug: -# -O0 -# Optimizations: -# -Os --llvm-lto 1 -${OUT_TARGET}: ${SHIM_SRC} Makefile - emcc -Os --llvm-lto 1 \ - -s WASM=1 \ - -s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap"]' \ - -s ALLOW_MEMORY_GROWTH=1 \ - -I /stb \ - -I /libwebp \ - ${LIBWEBP_SRC} \ - ${SHIM_SRC} \ - -o ${OUT_TARGET} diff --git a/build/image/webp-shim/samples/norway.webp b/build/image/webp-shim/samples/norway.webp deleted file mode 100644 index 0da983e..0000000 Binary files a/build/image/webp-shim/samples/norway.webp and /dev/null differ diff --git a/build/image/webp-shim/src/webp.c b/build/image/webp-shim/src/webp.c deleted file mode 100644 index 3b3925e..0000000 --- a/build/image/webp-shim/src/webp.c +++ /dev/null @@ -1,110 +0,0 @@ -/** - * webp.c - * - * Provides functionality for decoding a WebP image and converting it into - * alternative raster graphics formats (PNG/JPG). - * - * Licensed under the MIT License - * - * Copyright(c) 2020 Google Inc. - */ - -// TODO(2.0): Remove this. It seems unnecessary given WebP is universally supported now. - -#include -#include -#include "emscripten.h" -#include "src/webp/decode.h" - -#define STBI_WRITE_NO_STDIO -#define STB_IMAGE_WRITE_IMPLEMENTATION -#include "stb_image_write.h" -#include "stretchy_buffer.h" - -// Method to create buffers to transfer data into wasm. -EMSCRIPTEN_KEEPALIVE uint8_t* create_buffer(int size) { return malloc(size * sizeof(uint8_t)); } -EMSCRIPTEN_KEEPALIVE void destroy_buffer(uint8_t* p) { free(p); } - -// ImageBuffer is a struct representing information about the image data. -// TODO: Use embind to make this a C++ class? -typedef struct { - size_t byte_count; - uint8_t* bytes; // A stretchy buffer. -} ImageBuffer; - -// Methods to get ImageBuffer fields. -EMSCRIPTEN_KEEPALIVE uint8_t* get_image_bytes_from_handle(ImageBuffer* p) { return p->bytes; } -EMSCRIPTEN_KEEPALIVE size_t get_num_bytes_from_handle(ImageBuffer* p) { return p->byte_count; } -EMSCRIPTEN_KEEPALIVE void release_image_handle(ImageBuffer* image_buffer) { - if (image_buffer) { - sb_free(image_buffer->bytes); - free(image_buffer); - } -} - -ImageBuffer* createImageBuffer() { - ImageBuffer* image_buffer = (ImageBuffer*) malloc(sizeof(ImageBuffer)); - image_buffer->byte_count = 0; - image_buffer->bytes = NULL; - return image_buffer; -} - -void write_image_to_mem(void* context, void* data, int size) { - ImageBuffer* image_buffer = (ImageBuffer*) context; - - // If the buffer is not big enough for the new data, grow it. - int size_of_buffer = sb_count(image_buffer->bytes); - if (size_of_buffer < image_buffer->byte_count + size) { - sb_add(image_buffer->bytes, size < 128 ? 128 : size); - } - - uint8_t* start_ptr = image_buffer->bytes + image_buffer->byte_count; - memcpy(start_ptr, data, size); - image_buffer->byte_count += size; -} - -uint8_t* decode_webp_to_rgba(uint8_t* webp_ptr, size_t size, int* width, int* height) { - if (!webp_ptr) { - printf("decode_webp_to_rgba() called with NULL webp_ptr"); - } - if (!WebPGetInfo(webp_ptr, size, width, height)) { - fprintf(stderr, "WebPGetInfo() returned an error\n"); - return NULL; - } - uint8_t* rgba_ptr = WebPDecodeRGBA(webp_ptr, size, width, height); - return rgba_ptr; -} - -EMSCRIPTEN_KEEPALIVE ImageBuffer* get_png_handle_from_webp(uint8_t* webp_ptr, size_t size) { - int width, height; - uint8_t* rgba_ptr = decode_webp_to_rgba(webp_ptr, size, &width, &height); - if (!rgba_ptr) { - return NULL; - } - - ImageBuffer* image_buffer = createImageBuffer(); - int result = stbi_write_png_to_func(write_image_to_mem, image_buffer, width, height, 4, - rgba_ptr, width * 4); - WebPFree(rgba_ptr); - if (!result) { - return NULL; - } - return image_buffer; -} - -EMSCRIPTEN_KEEPALIVE ImageBuffer* get_jpg_handle_from_webp(uint8_t* webp_ptr, size_t size) { - int width, height; - uint8_t* rgba_ptr = decode_webp_to_rgba(webp_ptr, size, &width, &height); - if (!rgba_ptr) { - return NULL; - } - - ImageBuffer* image_buffer = createImageBuffer(); - int result = stbi_write_jpg_to_func(write_image_to_mem, image_buffer, width, height, 4, - rgba_ptr, 100); - WebPFree(rgba_ptr); - if (!result) { - return NULL; - } - return image_buffer; -} diff --git a/build/image/webp-shim/test/harness.html b/build/image/webp-shim/test/harness.html deleted file mode 100644 index 84f1b28..0000000 --- a/build/image/webp-shim/test/harness.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/build/image/webp-shim/test/harness.js b/build/image/webp-shim/test/harness.js deleted file mode 100644 index 7eb7493..0000000 --- a/build/image/webp-shim/test/harness.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * harness.js - * - * Example of how to use the webp-shim functions. - * - * Licensed under the MIT License - * - * Copyright(c) 2020 Google Inc. - */ -import {convertWebPtoPNG, convertWebPtoJPG} from '../../../image/webp-shim/webp-shim.js'; - -new Promise((resolve, reject) => { - const xhr = new XMLHttpRequest(); - xhr.open('GET', '../samples/norway.webp', true); - xhr.responseType = 'arraybuffer' - xhr.onload = evt => resolve(evt.target.response); - xhr.onerror = err => reject(err); - xhr.send(null); -}).then(webpBuffer => { - convertWebPtoPNG(webpBuffer).then(pngArr => { - document.getElementById('png').setAttribute('src', - URL.createObjectURL(new Blob([pngArr], {type: 'image/png'}))); - }); - - convertWebPtoJPG(webpBuffer).then(jpgArr => { - document.getElementById('jpg').setAttribute('src', - URL.createObjectURL(new Blob([jpgArr], {type: 'image/jpeg'}))); - }); -}); diff --git a/codecs/codecs.js b/codecs/codecs.js deleted file mode 100644 index e9eabea..0000000 --- a/codecs/codecs.js +++ /dev/null @@ -1,337 +0,0 @@ -/* - * codecs.js - * - * Licensed under the MIT License - * - * Copyright(c) 2022 Google Inc. - */ - -/** - * This module helps interpret ffprobe -print_format json output. - * Its coverage is pretty sparse right now, so send me pull requests! - */ - -/** - * @typedef ProbeStream ffprobe -show_streams -print_format json. Only the fields we care about. - * @property {number} index - * @property {string} codec_name - * @property {string} codec_long_name - * @property {string} profile - * @property {string} codec_type Either 'audio' or 'video'. - * @property {string} codec_tag_string - * @property {string} id - * @property {number?} level - * @property {number?} width - * @property {number?} height - * @property {string} r_frame_rate Like "60000/1001" - */ - -/** - * @typedef ProbeFormat Only the fields we care about from the following command: - * ffprobe -show_format -show_streams -v quiet -print_format json -i file.mp4 - * @property {string} filename - * @property {string} format_name - * @property {string} duration Number of seconds, as a string like "473.506367". - * @property {string} size Number of bytes, as a string. - * @property {string} bit_rate Bit rate, as a string. - */ - -/** - * @typedef ProbeInfo ffprobe -show_format -show_streams -print_format json - * @property {ProbeStream[]} streams - * @property {ProbeFormat} format - */ - -/** - * Maps the ffprobe format.format_name string to a short MIME type. - * @type {Object} - */ -const FORMAT_NAME_TO_SHORT_TYPE = { - 'avi': 'video/x-msvideo', - 'flac': 'audio/flac', - 'mp3': 'audio/mpeg', - 'wav': 'audio/wav', -} - -// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers#webm says that only -// the following codecs are supported for webm: -// - video: AV1, VP8, VP9 -// - audio: Opus, Vorbis -const WEBM_AUDIO_CODECS = [ 'opus', 'vorbis' ]; -const WEBM_VIDEO_CODECS = [ 'av1', 'vp8', 'vp9' ]; - -/** - * TODO: Reconcile this with file/sniffer.js findMimeType() which does signature matching. - * @param {ProbeInfo} info - * @returns {string} - */ -export function getShortMIMEString(info) { - if (!info) throw `Invalid ProbeInfo`; - if (!info.streams || info.streams.length === 0) throw `No streams in ProbeInfo`; - - const formatName = info?.format?.format_name; - if (formatName && Object.keys(FORMAT_NAME_TO_SHORT_TYPE).includes(formatName)) { - return FORMAT_NAME_TO_SHORT_TYPE[formatName]; - } - - // M4A files are specifically audio/mp4. - if (info?.format?.filename?.toLowerCase().endsWith('.m4a')) { - return 'audio/mp4'; - } - - // Otherwise, any file with at least 1 video stream is considered video/. - // Otherwise, any file with at least 1 audio stream is considered audio/. - const type = info.streams.some(s => s.codec_type === 'video') ? - 'video' : - info.streams.some(s => s.codec_type === 'audio') ? 'audio' : undefined; - if (!type) { - throw `Cannot handle media file type (no video/audio streams for ${info.format.format_name}). ` + - `Please file a bug https://github.com/codedread/bitjs/issues/new`; - } - - /** @type {string} */ - let subType; - switch (formatName) { - case 'mpeg': - subType = 'mpeg'; - break; - case 'mov,mp4,m4a,3gp,3g2,mj2': - subType = 'mp4'; - break; - case 'ogg': - subType = 'ogg'; - break; - case 'matroska,webm': - let isWebM = true; - for (const stream of info.streams) { - if ( (stream.codec_type === 'audio' && !WEBM_AUDIO_CODECS.includes(stream.codec_name)) - || (stream.codec_type === 'video' && !WEBM_VIDEO_CODECS.includes(stream.codec_name))) { - isWebM = false; - break; - } - } - subType = isWebM ? 'webm' : 'x-matroska'; - break; - default: - throw `Cannot handle format ${formatName} yet. ` + - `Please file a bug https://github.com/codedread/bitjs/issues/new`; - } - - return `${type}/${subType}`; -} - -/** - * Accepts the ffprobe JSON output and returns an ISO MIME string with parameters (RFC6381), such - * as 'video/mp4; codecs="avc1.4D4028, mp4a.40.2"'. This string should be suitable to be used on - * the server as the Content-Type header of a media stream which can subsequently be used on the - * client as the type value of a SourceBuffer object `mediaSource.addSourceBuffer(contentType)`. - * NOTE: For now, this method fails hard (throws an error) when it encounters a format/codec it - * does not recognize. Please file a bug or send a PR. - * @param {ProbeInfo} info - * @returns {string} - */ -export function getFullMIMEString(info) { - /** A string like 'video/mp4' */ - let contentType = `${getShortMIMEString(info)}`; - if (Object.values(FORMAT_NAME_TO_SHORT_TYPE).includes(contentType)) { - return contentType; - } - - let codecFrags = new Set(); - - for (const stream of info.streams) { - if (stream.codec_type === 'audio') { - // MP3 can sometimes have codec_tag_string=mp4a, so we check for it first. - if (stream.codec_name === 'mp3') { - codecFrags.add('mp3'); - continue; - } - - switch (stream.codec_tag_string) { - case 'mp4a': codecFrags.add(getMP4ACodecString(stream)); break; - default: - switch (stream.codec_name) { - case 'aac': codecFrags.add(getMP4ACodecString(stream)); break; - // I'm going off of what Chromium calls this one, with the dash. - case 'ac3': codecFrags.add('ac-3'); break; - case 'dts': codecFrags.add('dts'); break; - case 'flac': codecFrags.add('flac'); break; - case 'opus': codecFrags.add('opus'); break; - case 'vorbis': codecFrags.add('vorbis'); break; - default: - throw `Could not handle audio codec_name ${stream.codec_name}, ` + - `codec_tag_string ${stream.codec_tag_string} for file ${info.format.filename} yet. ` + - `Please file a bug https://github.com/codedread/bitjs/issues/new`; - } - break; - } - } - else if (stream.codec_type === 'video') { - switch (stream.codec_tag_string) { - case 'avc1': codecFrags.add(getAVC1CodecString(stream)); break; - case 'vp09': codecFrags.add(getVP09CodecString(stream)); break; - // We don't handle these as video streams with codecs, so skip them. - case 'png': continue; - default: - switch (stream.codec_name) { - case 'av1': codecFrags.add('av1'); break; - case 'h264': codecFrags.add(getAVC1CodecString(stream)); break; - // Skip mjpeg as a video stream for the codecs string. - case 'mjpeg': break; - case 'mpeg2video': codecFrags.add('mpeg2video'); break; - case 'vp8': codecFrags.add('vp8'); break; - case 'vp9': codecFrags.add(getVP09CodecString(stream)); break; - default: - throw `Could not handle video codec_name ${stream.codec_name}, ` + - `codec_tag_string ${stream.codec_tag_string} for file ${info.format.filename} yet. ` + - `Please file a bug https://github.com/codedread/bitjs/issues/new`; - - } - } - } - } - - if (codecFrags.size === 0) return contentType; - return contentType + '; codecs="' + Array.from(codecFrags).join(',') + '"'; -} - -// TODO: Consider whether any of these should be exported. - -function getCleanHex(extradata) { - // 1. Split into lines - // 2. Extract only the middle hex section (after ':' and before ' ') - // 3. Remove all spaces - return extradata.split('\n') - .map(line => line.split(':')[1]?.split(' ')[0]) - .filter(Boolean) - .join('') - .replace(/\s/g, ''); -} - -/** - * AVC1 is the same thing as H264. - * https://developer.mozilla.org/en-US/docs/Web/Media/Formats/codecs_parameter#iso_base_media_file_format_mp4_quicktime_and_3gp - * @param {ProbeStream} stream - * @returns {string} - */ -function getAVC1CodecString(stream) { - if (stream.extradata && stream.codec_tag_string === 'avc1') { - const hex = getCleanHex(stream.extradata); // results in "014d4028..." - - // Bytes 1, 2, and 3 are the Profile, Constraints, and Level. - // In the hex string, these are characters at index 2 through 8. - const codecPart = hex.substring(2, 8).toUpperCase(); - return `avc1.${codecPart}`; - } - - if (!stream.profile) throw `No profile found in AVC1 stream`; - - let frag = 'avc1'; - - // Add PP and CC hex digits. - switch (stream.profile) { - case 'Constrained Baseline': - frag += '.4240'; - break; - case 'Baseline': - frag += '.4200'; - break; - case 'Extended': - frag += '.5800'; - break; - case 'Main': - frag += '.4D00'; - break; - case 'High': - frag += '.6400'; - break; - default: - throw `Cannot handle AVC1 stream with profile ${stream.profile} yet. ` + - `Please file a bug https://github.com/codedread/bitjs/issues/new`; - } - - // Add LL hex digits. - const levelAsHex = Number(stream.level).toString(16).toUpperCase().padStart(2, '0'); - if (levelAsHex.length !== 2) { - throw `Cannot handle AVC1 level ${stream.level} yet. ` + - `Please file a bug https://github.com/codedread/bitjs/issues/new`; - } - frag += levelAsHex; - - return frag; -} - -/** - * https://developer.mozilla.org/en-US/docs/Web/Media/Formats/codecs_parameter#webm - * @param {ProbeStream} stream - * @returns {string} - */ -function getVP09CodecString(stream) { - // TODO: Consider just returning 'vp9' here instead since I have so much guesswork. - // https://developer.mozilla.org/en-US/docs/Web/Media/Formats/codecs_parameter#webm - - // The ISO format is cccc.PP.LL.DD - let frag = 'vp09'; - - // Add PP hex digits. - switch (stream.profile) { - case 'Profile 0': - frag += '.00'; - break; - case 'Profile 1': - frag += '.01'; - break; - case 'Profile 2': - frag += '.02'; - break; - case 'Profile 3': - frag += '.03'; - break; - default: - throw `Cannot handle VP09 stream with profile ${stream.profile} yet. ` + - `Please file a bug https://github.com/codedread/bitjs/issues/new`; - } - - // Add LL hex digits. - // If ffprobe is spitting out -99 as level... it means unknown, so we will guess level=1. - if (stream.level === -99) { frag += `.10`; } - else { - const levelAsHex = Number(stream.level).toString(16).toUpperCase().padStart(2, '0'); - if (levelAsHex.length !== 2) { - throw `Cannot handle VP09 level ${stream.level} yet. ` + - `Please file a bug https://github.com/codedread/bitjs/issues/new`; - } - frag += `.${levelAsHex}`; - } - - // Add DD hex digits. - // TODO: This is just a guess at DD (10-bit color depth), need to try and extract this info - // from ffprobe JSON output instead. - frag += '.10'; - - return frag; -} - -/** - * MP4A is the same as AAC. - * https://developer.mozilla.org/en-US/docs/Web/Media/Formats/codecs_parameter#mp4a - * @param {ProbeStream} stream - * @returns {string} - */ -function getMP4ACodecString(stream) { - let frag = 'mp4a.40'; - // https://dashif.org/codecs/audio/ - switch (stream.profile) { - case 'LC': - frag += '.2'; - break; - case 'HE-AAC': - frag += '.5'; - break; - // TODO: more! - default: - throw `Cannot handle AAC stream with profile ${stream.profile} yet. ` + - `Please file a bug https://github.com/codedread/bitjs/issues/new`; - } - return frag; -} diff --git a/docs/bitjs.archive.md b/docs/bitjs.archive.md deleted file mode 100644 index 22a9b01..0000000 --- a/docs/bitjs.archive.md +++ /dev/null @@ -1,198 +0,0 @@ -# bitjs.archive - -This package includes objects for unarchiving binary data in popular archive formats (zip, rar, -tar, gzip) providing unzip, unrar, untar, gunzip capabilities via JavaScript in the browser or -various JavaScript runtimes (node, deno, bun). - -A compressor that creates Zip files is also present. - -The decompression / compression happens inside a Web Worker, if the runtime supports it (browsers, -deno). The library uses native decompression, if supported by the browser -(via [DecompressionStream](https://developer.mozilla.org/en-US/docs/Web/API/DecompressionStream/DecompressionStream)), -and falls back to JavaScript implementations otherwise. - -The API is event-based, you will want to subscribe to some of these events: - * 'progress': Periodic updates on the progress (bytes processed). - * 'extract': Sent whenever a single file in the archive was fully decompressed. - * 'finish': Sent when decompression/compression is complete. - -## Decompressing - -### Simple Example of unzip - -Here is a simple example of unzipping a file. It is assumed the zip file exists as an -[`ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer), -which you can get via -[`XHR`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest_API/Sending_and_Receiving_Binary_Data), -from a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob/arrayBuffer), -[`Fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Response/arrayBuffer), -[`FileReader`](https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsArrayBuffer), -etc. - -```javascript - import { Unzipper } from './bitjs/archive/decompress.js'; - const unzipper = new Unzipper(zipFileArrayBuffer); - unzipper.onExtract(evt => { - const {filename, fileData} = evt.unarchivedFile; - console.log(`unzipped ${filename} (${fileData.byteLength} bytes)`); - // Do something with fileData... - }); - unzipper.addEventListener('finish', () => console.log(`Finished!`)); - unzipper.start(); -``` - -`start()` is an async method that resolves a `Promise` when the decompression is complete, so you can -`await` on it, if you need to. - -### Progressive unzipping - -The unarchivers also support progressively decoding while streaming the file, if you are receiving -the zipped file from a slow place (a Cloud API, for instance). Send the first `ArrayBuffer` in the -constructor, and send subsequent `ArrayBuffers` using the `update()` method. - -```javascript - import { Unzipper } from './bitjs/archive/decompress.js'; - const unzipper = new Unzipper(anArrayBufferWithStartingBytes); - unzipper.addEventListener('extract', () => {...}); - unzipper.addEventListener('finish', () => {...}); - unzipper.start(); - ... - // after some time - unzipper.update(anArrayBufferWithMoreBytes); - ... - // after some more time - unzipper.update(anArrayBufferWithYetMoreBytes); -``` - -### getUnarchiver() - -If you don't want to bother with figuring out if you have a zip, rar, tar, or gz file, you can use -the convenience method `getUnarchiver()`, which sniffs the bytes for you and creates the appropriate -unarchiver. - -```javascript - import { getUnarchiver } from './bitjs/archive/decompress.js'; - const unarchiver = getUnarchiver(anArrayBuffer); - unarchiver.onExtract(evt => {...}); - // etc... - unarchiver.start(); -``` - -### Non-Browser JavaScript Runtime Examples - -The API works in other JavaScript runtimes too (Node, Deno, Bun). - -#### NodeJS - -```javascript - import * as fs from 'fs'; - import { getUnarchiver } from './archive/decompress.js'; - - const nodeBuf = fs.readFileSync('comic.cbz'); - // NOTE: Small files may not have a zero byte offset in Node, so we slice(). - // See https://nodejs.org/api/buffer.html#bufbyteoffset. - const ab = nodeBuf.buffer.slice(nodeBuf.byteOffset, nodeBuf.byteOffset + nodeBuf.length); - const unarchiver = getUnarchiver(ab); - unarchiver.addEventListener('progress', () => process.stdout.write('.')); - unarchiver.addEventListener('extract', (evt) => { - const {filename, fileData} = evt.unarchivedFile; - console.log(`${filename} (${fileData.byteLength} bytes)`); - }); - unarchiver.addEventListener('finish', () => console.log(`Done!`)); - unarchiver.start(); -``` - -#### Deno - -```typescript - import { UnarchiveExtractEvent } from './archive/events.js'; - import { getUnarchiver} from './archive/decompress.js'; - - const print = (s: string) => Deno.writeAll(Deno.stdout, new TextEncoder().encode(s)); - - async function go() { - const arr: Uint8Array = await Deno.readFile('example.zip'); - const unarchiver = getUnarchiver(arr.buffer); - unarchiver.addEventListener('extract', (evt) => { - const {filename, fileData} = (evt as UnarchiveExtractEvent).unarchivedFile; - print(`\n${filename} (${fileData.byteLength} bytes)\n`); - // Do something with fileData... - }); - unarchiver.addEventListener('finish', () => { console.log(`Done!`); Deno.exit(); }); - unarchiver.addEventListener('progress', (evt) => print('.')); - unarchiver.start(); - } - - await go(); -``` - -## Compressing - -The Zipper only supports creating zip files without compression (store only) for now. The interface -is pretty straightforward and there is no event-based / streaming API. - -```javascript - import { Zipper } from './bitjs/archive/compress.js'; - const zipper = new Zipper(); - const now = Date.now(); - // Create a zip file with files foo.jpg and bar.txt. - const zippedArrayBuffer = await zipper.start( - [ - { - fileName: 'foo.jpg', - lastModTime: now, - fileData: fooArrayBuffer, - }, - { - fileName: 'bar.txt', - lastModTime: now, - fileData: barArrayBuffer, - } - ], - true /* isLastFile */); -``` - -If you don't want to have all files in memory at once, you can zip progressively: - -```javascript - import { Zipper } from './bitjs/archive/compress.js'; - const zipper = new Zipper(); - const zipPromise = zipper.start(); - // ... some time later - zipper.appendFiles([file1]); - // ... more time later - zipper.appendFiles([file2]); - // ... now we add the last file - zipper.appendFiles([file3], true /* isLastFile */); - - const zippedArrayBuffer = await zipPromise; - ... -``` - -## Implementation Details - -All you generally need to worry about is calling getUnarchiver(), listen for events, and then `start()`. However, if you are interested in how it works under the covers, read on... - -The implementations are written in pure JavaScript and communicate with the host software (the thing that wants to do the unzipping) via a MessageChannel. The host and implementation each own a MessagePort and pass messages to each other through it. In a web browser, the implementation is invoked as a Web Worker to save the main UI thread from getting the CPU spins. - -```mermaid -sequenceDiagram - participant Host Code - participant Port1 - box Any JavaScript Context (could be a Web Worker) - participant Port2 - participant unrar.js - end - Host Code->>Port1: postMessage(rar bytes) - Port1-->>Port2: (MessageChannel) - Port2->>unrar.js: onmessage(rar bytes) - Note right of unrar.js: unrar the thing - - unrar.js->>Port2: postMessage(an extracted file) - Port2-->>Port1: (MessageChannel) - Port1->>Host Code: onmessage(an extracted file) - - unrar.js->>Port2: postMessage(2nd extracted file) - Port2-->>Port1: (MessageChannel) - Port1->>Host Code: onmessage(2nd extracted file) -``` diff --git a/docs/bitjs.io.md b/docs/bitjs.io.md deleted file mode 100644 index 401cd42..0000000 --- a/docs/bitjs.io.md +++ /dev/null @@ -1,88 +0,0 @@ -# bitjs.io - -This package includes stream objects for reading and writing binary data at the bit and byte level: -BitStream, ByteStream. - -Streams are given an ArrayBuffer of bytes and keeps track of where in the stream you are. As you -read through the stream, the pointer is advanced through the buffer. If you need to peek at a number -without advancing the pointer, use the `peek` methods. - -## BitStream - -A bit stream is a way to read a variable number of bits from a series of bytes. This is useful for -parsing certain protocols (for example pkzip or rar algorithm). Note that the order of reading -bits can go from least-to-most significant bit, or the reverse. - -### Least-to-Most Direction - -![BitStream reading from least-to-most significant bit](bitstream-ltm.png) - -```javascript -const bstream = new BitStream(ab, false /* mtl */); -bstream.readBits(6); // (blue) 0b001011 = 11 -bstream.readBits(5); // (red) 0b11001 = 25 -bstream.readBits(8); // (green) 0b10000010 = 130 -``` - -### Most-to-Least Direction - -![BitStream reading from most-to-least significant bit](bitstream-mtl.png) - -```javascript -const bstream = new BitStream(ab, true /* mtl */); -bstream.readBits(6); // (blue) 0b010010 = 18 -bstream.readBits(5); // (red) 0b11000 = 24 -bstream.readBits(8); // (green) 0b10110100 = 180 -``` - -## ByteStream - -A ByteStream is a convenient way to read numbers and ASCII strings from a set of bytes. For example, -interpreting 2 bytes in the stream as a number is done by calling `someByteStream.readNumber(2)`. By -default, the byte stream is considered Little Endian, but can be changed at any point using -`someByteStream.setBigEndian()` and toggled back with `someByteStream.setLittleEndian()`. - -If you need to peek at bytes without advancing the pointer, use the `peek` methods. - -By default, numbers are unsigned, but `peekSignedNumber(n)` and `readSignedNumber(n)` exist for -signed numbers. - -```javascript - const byteStream = new ByteStream(someArrayBuffer); - byteStream.setBigEndian(); - byteStream.skip(2); // skip two bytes. - // Interpret next 2 bytes as the string length. - const strLen = byteStream.readNumber(2); - // Read in bytes as an ASCII string. - const someString = byteStream.readString(strLen); - // Interpret next byte as an int8 (0xFF would be -1). - const someVal = byteStream.readSignedNumber(1); - ... -``` - -### Appending to the Stream - -If you get more bytes (for example, from an asynchronous process), you can add them to the end of -the byte stream by using `someByteStream.push(nextBytesAsAnArrayBuffer)`. - -### Forking / Teeing the stream. - -If you have a need to seek ahead to a different section of the stream of bytes, and want to later -return to where you left off, you should use `tee()` method to make a copy of the ByteStream. This -will let you seek to the appropriate spot to grab some bytes using the teed stream, while you can -pick up where you left off with the original stream. - -```javascript - const origStream = new ByteStream(someArrayBuffer); - const strLen = origStream.readNumber(4); // Bytes 0-3. - const strOffset = origStream.readNumber(4); // Bytes 4-7. - - const teedStream = origStream.tee(); - const description = teedStream.skip(strOffset).readString(strLen); - - const someOtherVal = origStream.readNumber(4); // Bytes 8-11 -``` - -Note that the teed stream is not "connected" to the original stream. If you push new bytes to the -original stream, the teed stream does not see them. If you find this behavior unexpected, please -file a bug. diff --git a/docs/bitstream-ltm.png b/docs/bitstream-ltm.png deleted file mode 100644 index 7ed03be..0000000 Binary files a/docs/bitstream-ltm.png and /dev/null differ diff --git a/docs/bitstream-mtl.png b/docs/bitstream-mtl.png deleted file mode 100644 index 56b0349..0000000 Binary files a/docs/bitstream-mtl.png and /dev/null differ diff --git a/docs/unrar.html b/docs/unrar.html deleted file mode 100644 index d59b1ba..0000000 --- a/docs/unrar.html +++ /dev/null @@ -1,599 +0,0 @@ - - - - Documentation of the RAR format - - - -
-

The RAR Format

-
- -
-
-

1 Introduction

-
- -

This document, a work-in-progress, describes the RAR format. It serves a similar role that the ZIP App Note does for the ZIP format.

- -

NOTE 1: This documentation MUST NOT be used to create RAR-compatible archive programs like WinRAR. It is only for the purposes of writing decompression software (i.e. unrar) in various languages. It was reverse-engineered from the UnRAR source located at this page with Eugene Roshal's permission.

- -

NOTE 2: This documentation will initially focus on what I believe is Version 3 of the RAR format.

- -
-
-

1.1 License

-
- -

The author of this document is Jeff Schiller <codedread@gmail.com>. It is licensed under the CC-BY-3.0 License.

- -
- -
- -
-
-

2 General Format of a .RAR File

-
- -

Overall .RAR file format:

- -
-signature               7 bytes    (0x52 0x61 0x72 0x21 0x1A 0x07 0x00)
-[1st volume header]
-...
-[2nd volume header]
-...
-...
-[nth volume header]
-...
-
- -

In general, a modern single-volume RAR file has a MAIN_HEAD structure followed by multiple FILE_HEAD structures.

- -
-

2.1 Volume Header Format

-
- -

The Base Header Block is:

- -
-header_crc              2 bytes
-header_type             1 byte
-header_flags            2 bytes
-header_size             2 bytes
-
- -

The header_size indicates how many total bytes the header requires. The header_type field determines how the remaining bytes should be interpreted.

- -
-

2.1.1 Header Type

-
- -

The header type is 8 bits (1 byte) and can have the following values:

- - - - - - - - - - - - - -
ValueType
0x72MARK_HEAD
0x73MAIN_HEAD
0x74FILE_HEAD
0x75COMM_HEAD
0x76AV_HEAD
0x77SUB_HEAD
0x78PROTECT_HEAD
0x79SIGN_HEAD
0x7aNEWSUB_HEAD
0x7bENDARC_HEAD
- -
-
2.1.1.1 MARK_HEAD
-
- -

TBD

- -
-
2.1.1.2 MAIN_HEAD
-
- -

The remaining bytes in the volume header for MAIN_HEAD are:

- -
-HighPosAv               2 bytes
-PosAV                   4 bytes
-EncryptVer              1 byte (only present if MHD_ENCRYPTVER is set) 
-
- -
-
2.1.1.3 FILE_HEAD
-
- -

The remaining bytes in the FILE_HEAD structure are:

- -
-PackSize                4 bytes
-UnpSize                 4 bytes
-HostOS                  1 byte
-FileCRC                 4 bytes
-FileTime (mtime)        4 bytes (MS-DOS date/time format)
-UnpVer                  1 byte
-Method                  1 byte
-NameSize                2 bytes
-FileAttr                4 bytes
-HighPackSize            4 bytes (only present if LHD_LARGE is set)
-HighUnpSize             4 bytes (only present if LHD_LARGE is set)
-FileName                (NameSize) bytes
-Salt                    8 bytes (only present if LHD_SALT is set)
-ExtTime Structure       See Description (only present if LHD_EXTTIME is set)
-Packed Data             (Total Packed Size) bytes
-
- -

If the LHD_LARGE flag is set, then the archive is large and 64-bits are needed to represent the packed and unpacked size. HighPackSize is used as the upper 32-bits and PackSize is used as the lower 32-bits for the packed size in bytes. HighUnpSize is used as the upper 32-bits and UnpSize is used as the lower 32-bits for the unpacked size in bytes.

- -
-
ExtTime Structure
-
- -

This structure has 4 sections representing additional time data.

-

The first 16 bits contain a set of flags, 4 bits for each section.

- -

Each flag contains:

-
    -
  • Bit 0: Whether this section is present or not
  • -
  • Bit 2: Signals that the MS-DOS date/time should have its seconds increased by 1
  • -
      -
    • MS-DOS date/time format can only store even numbers of seconds. This bit counters this limitation.
    • -
    -
  • Bit 3 and 4: The amount of bytes to be read as the remainder, between 0 and 3
  • -
- -

Each section contains:

- -
    -
  • 4 bytes containing an MS-DOS date/time (except for mtime, which is part of FILE_HEAD)
  • -
  • 0 to 3 bytes containing fractions of seconds in 100ns increments
  • -
      -
    • They form a 24-bit integer, sorted from least significant to most significant bits
    • -
    • If fewer than 3 bytes are present, the remaining least significant bits are to be padded with zeroes in order to complete 24-bits
    • -
    -
- -

Follow this pseudo-code to read in the ExtTime Structure.

- -
-const one_second = 10000000; // 1 second in 100ns intervals => 1E7
-var extTimeFlags = readBits(16)
-
-mtime:
-mtime_rmode = extTimeFlags >> 12
-if ((mtime_rmode & 8)==0) goto ctime
-mtime_count = mtime_rmode & 0x3
-mtime_remainder = 0
-for (var i = 0; i < mtime_count; i++) {
-    mtime_remainder = (readBits(8) << 16) | (mtime_remainder >> 8)
-}
-if ((mtime_rmode & 4)!=0) mtime_remainder += one_second
-
-ctime:
-ctime_rmode = extTimeFlags >> 8
-if ((ctime_rmode & 8)==0) goto atime
-ctime = readBits(32)
-ctime_count = ctime_rmode & 0x3
-ctime_remainder = 0
-for (var i = 0; i < ctime_count; i++) {
-    ctime_remainder = (readBits(8) << 16) | (ctime_remainder >> 8)
-}
-if ((ctime_rmode & 4)!=0) ctime_remainder += one_second
-
-atime:
-atime_rmode = extTimeFlags >> 4
-if ((atime_rmode & 8)==0) goto arctime
-atime = readBits(32)
-atime_count = atime_rmode & 0x3
-atime_remainder = 0
-for (var i = 0; i < atime_count; i++) {
-    atime_remainder = (readBits(8) << 16) | (atime_remainder >> 8)
-}
-if ((atime_rmode & 4)!=0) atime_remainder += one_second
-
-arctime:
-arctime_rmode = extTimeFlags
-if ((arctime_rmode & 8)==0) goto done_exttime
-arctime = readBits(32)
-arctime_count = arctime_rmode & 0x3
-arctime_remainder = 0
-for (var i = 0; i < arctime_count; i++) {
-    arctime_remainder = (readBits(8) << 16) | (arctime_remainder >> 8)
-}
-if ((arctime_rmode & 4)!=0) arctime_remainder += one_second
-
-done_exttime
-
- - -
-
2.1.1.4 COMM_HEAD
-
- -

TBD

- -
-
2.1.1.5 AV_HEAD
-
- -

TBD

- -
-
2.1.1.6 SUB_HEAD
-
- -

TBD

- -
-
2.1.1.7 PROTECT_HEAD
-
- -

TBD

- -
-
2.1.1.8 SIGN_HEAD
-
- -

TBD

- -
-
2.1.1.9 NEWSUB_HEAD
-
- -

TBD

- -
-
2.1.1.10 ENDARC_HEAD
-
- -

TBD

- -
-

2.1.2 Header Flags

-
- -

The header flags are 16 bits (2 bytes). Depending on the type of Volume Header, the flags are interpreted differently.

- -

The Main Header Flags are:

- - - - - - - - - - - - - -
ValueFlag
0x0001MHD_VOLUME
0x0002MHD_COMMENT
0x0004MHD_LOCK
0x0008MHD_SOLID
0x0010MHD_PACK_COMMENT or MHD_NEWNUMBERING
0x0020MHD_AV
0x0040MHD_PROTECT
0x0080MHD_PASSWORD
0x0100MHD_FIRSTVOLUME
0x0200MHD_ENCRYPTVER
- -

The File Header Flags are:

- - - - - - - - - - - - - - -
ValueFlag
0x0001LHD_SPLIT_BEFORE
0x0002LHD_SPLIT_AFTER
0x0004LHD_PASSWORD
0x0008LHD_COMMENT
0x0010LHD_SOLID
0x0100LHD_LARGE
0x0200LHD_UNICODE
0x0400LHD_SALT
0x0800LHD_VERSION
0x1000LHD_EXTTIME
0x2000LHD_EXTFLAGS
- -
-
2.1.2.1 MHD_VOLUME
-
- -

Value 0x0001. TBD

- -
-
2.1.2.2 MHD_COMMENT
-
- -

Value 0x0002. TBD

- -
-
2.1.2.3 MHD_LOCK
-
- -

Value 0x0004. TBD

- -
-
2.1.2.4 MHD_SOLID
-
- -

Value 0x0008. TBD

- -
-
2.1.2.5 MHD_PACK_COMMENT
-
- -

Value 0x0010. TBD

- -
-
2.1.2.6 MHD_AV
-
- -

Value 0x0020. TBD

- -
-
2.1.2.7 MHD_PROTECT
-
- -

Value 0x0040. TBD

- -
-
2.1.2.8 MHD_PASSWORD
-
- -

Value 0x0080. TBD

- -
-
2.1.2.9 MHD_FIRSTVOLUME
-
- -

Value 0x0100. TBD

- -
-
2.1.2.10 MHD_ENCRYPTVER
-
- -

Value 0x0200. Indicates whether encryption is present in the archive volume.

- -
-
2.1.2.11 LHD_SPLIT_BEFORE
-
- -

Value 0x0001. TBD

- -
-
2.1.2.12 LHD_SPLIT_AFTER
-
- -

Value 0x0002. TBD

- -
-
2.1.2.13 LHD_PASSWORD
-
- -

Value 0x0004. TBD

- -
-
2.1.2.14 LHD_COMMENT
-
- -

Value 0x0008. TBD

- -
-
2.1.2.15 LHD_SOLID
-
- -

Value 0x0010. TBD

- -
-
2.1.2.16 LHD_LARGE
-
- -

Value 0x0100. Indicates if the archive is large. In this case, 64 bits are used to describe the packed and unpacked size.

- -
-
2.1.2.17 LHD_UNICODE
-
- -

Value 0x0200. Indicates if the filename is Unicode.

- -
-
2.1.2.18 LHD_SALT
-
- -

Value 0x0400. Indicates if the 64-bit salt value is present.

- -
-
2.1.2.19 LHD_VERSION
-
- -

Value 0x0800. TBD

- -
-
2.1.2.20 LHD_EXTTIME
-
- -

Value 0x1000. The ExtTime Structure is present in the FILE_HEAD header.

- -
-
2.1.2.21 LHD_EXTFLAGS
-
- -

Value 0x2000. TBD

-
- -
-
-

3 Unpacking

-
- -

Once the header information and packed bytes have been extracted, the packed bytes must then be unpacked. RAR uses a variety of algorithms for this. Chief among these are Lempel-Ziv compression and Prediction by Partial Matching. The details of the unpacking are specified in the following subsections based on the values of UnpVer and Method as decoded in the FILE_HEAD structure.

- -

If Method is 0x30 (decimal 48), then the packed bytes are the unpacked bytes and no decompression/unpacking is necessary (i.e. the file was not compressed).

- -

Otherwise:

- - - - - - - -
UnpVer Value (decimal)Algorithm To Use
15Unpack15
20Unpack20
26
29Unpack29
36
- -
-

3.1 Unpack15

-
- -

TBD

- -
-

3.2 Unpack20

-
- -

TBD

- -
-

3.3 Unpack29

-
- -

The structure of packed data consists of N number of blocks. If the first bit of a block is set, then process the block as a PPM block. Otherwise, this is an LZ block.

- -
-

3.3.1 LZ Block

-
- -

The format of a LZ block is:

- -
-isPPM                   1 bit
-keepOldTable            1 bit
-huffmanCodeTable        (variable size)
-
- -
-
3.3.1.1 Huffman Code Tables
-
- -

The Huffman Encoding tables consist of a series of bit lengths. For a more thorough treatment of the concepts of Huffman Encoding, see the Deflate spec. The RAR format uses a set of twenty bit lengths to construct Huffman Codes. The Huffman Encoding tables in RAR files consist of at most twenty entries of the format:

- -
-BitLength               4 bits
-ZeroCount               4 bits (only present if BitLength is 15)
-
- -

If BitLength is 15, then the next 4 bits are read as ZeroCount. If the ZeroCount is 0, then the bit length is 15, otherwise (ZeroCount+2) is the number of consecutive zero bit lengths that are in the table. For instance, if the following 4-bit numbers are present:

- - - - - - - - - - - - - - -
0x8indicates a bit-length of 8
0x4indicates a bit-length of 4
0x4indicates a bit-length of 4
0x2indicates a bit-length of 2
0xFthese two 4-bit numbers specify a bit-length of 15
0x0
0xFthese two 4-bit numbers specify a run of 5 zeros
0x3
0x9indicates a bit-length of 9
0x3indicates a bit-length of 3
0xFthese two 4-bit numbers specify a run of 8 zeros
0x6
- -

This example describes a Huffman Encoding Bit Length table of:

- - - - - - - - - - - - - -
CodeBit LengthCodeBitLength
18119
24123
34130
42140
515150
60160
70170
80180
90190
100200
- -

Once the twenty bit lengths are obtained, the Huffman Encoding table is constructed by using the following algorithm:

- -
-1) Count the number of codes for each code length.  Let
-   LenCount[N] be the number of codes of length N, where N = {1..16}.
-             
-2) Find the decode length and positions:
-
-        N = 0
-        TmpPos[0] = 0
-        DecodePos[0] = 0
-        DecodeLen[0] = 0
-        for (I = 1; I < 16; I++) 
-        {
-            N = 2 * (N+LenCount[I])
-            M = N << (15-I)
-            if (M > 0xFFFF) M = 0xFFFF
-            
-            DecodeLen[I] = (unsigned int)M
-            TmpPos[I] = DecodePos[I] = DecodePos[I-1] + LenCount[I-1]
-        }
-
-3) Assign numerical values to all codes:
-
-        for (I = 0; I < TableSize; I++)
-        {
-            if (BitLength[I] != 0)
-                DecodeNum[ TmpPos[BitLength[I] & 0xF]++ ] = I
-        }
-
-
-
- -
-
-

Appendix A. Credits

-
- -
    -
  • Eugene Roshal <roshal@rarlab.com> - creator and owner of the RAR format
  • -
  • Jeff Schiller <codedread@gmail.com> - creator of this document as part of the bitjs project
  • -
  • Andre Brait <andrebrait@gmail.com> - for documenting the ExtTime structure
  • -
-
- - - diff --git a/file/sniffer.js b/file/sniffer.js deleted file mode 100644 index 0d9bf13..0000000 --- a/file/sniffer.js +++ /dev/null @@ -1,157 +0,0 @@ -/** - * File Sniffer. - * Makes an attempt to resolve a byte stream into a MIME type. - * - * Licensed under the MIT License - * - * Copyright(c) 2020 Google Inc. - */ - -// https://mimesniff.spec.whatwg.org/ is a good resource. -// Easy targets for reverse-engineering: -// - https://github.com/h2non/filetype -// - https://github.com/gabriel-vasile/mimetype (particularly internal/magic/ftyp.go) - -// NOTE: Because the ICO format also starts with a couple zero bytes, this tree will rely on the -// File Type box never going beyond 255 bytes in length which, seems unlikely according to -// https://dev.to/alfg/a-quick-dive-into-mp4-57fo. -// 'ISO-BMFF': [[0x00, 0x00, 0x00, '??', 0x66, 0x74, 0x79, 0x70]], // box_length, then 'ftyp' -// 'MATROSKA': [[0x1A, 0x45, 0xDF, 0xA3]] - -// A subset of "modern" formats from https://en.wikipedia.org/wiki/List_of_file_signatures. -// Mapping of MIME type to magic numbers. Each file type can have multiple signatures. -// '??' is used as a placeholder value. -const fileSignatures = { - // Document formats. - 'application/pdf': [[0x25, 0x50, 0x44, 0x46, 0x2d]], // '%PDF-' - - // Archive formats: - 'application/gzip': [[0x1F, 0x8B, 0x08]], - 'application/x-tar': [ // 'ustar' - [0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x30, 0x30], - [0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00], - ], - 'application/x-7z-compressed': [[0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C]], // '7z' - 'application/x-bzip2': [[0x42, 0x5A, 0x68]], // 'BZh' - 'application/x-rar-compressed': [[0x52, 0x61, 0x72, 0x21, 0x1A, 0x07]], // 'Rar!' - 'application/zip': [ // 'PK' - [0x50, 0x4B, 0x03, 0x04], - [0x50, 0x4B, 0x05, 0x06], - [0x50, 0x4B, 0x07, 0x08], - ], - - // Image formats. - 'image/bmp': [[0x42, 0x4D]], // 'BM' - 'image/gif': [[0x47, 0x49, 0x46, 0x38]], // 'GIF8' - 'image/jpeg': [[0xFF, 0xD8, 0xFF]], - 'image/png': [[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]], - 'image/webp': [[0x52, 0x49, 0x46, 0x46, '??', '??', '??', '??', 0x57, 0x45, 0x42, 0x50]], // 'RIFF....WEBP' - 'image/x-icon': [[0x00, 0x00, 0x01, 0x00]], - - // Audio/Video formats. - 'application/ogg': [[0x4F, 0x67, 0x67, 0x53]], // 'OggS' - 'audio/flac': [[0x66, 0x4C, 0x61, 0x43]], // 'fLaC' - 'audio/mpeg': [ - [0xFF, 0xFB], - [0xFF, 0xF3], - [0xFF, 0xF2], - [0x49, 0x44, 0x33], // 'ID3' - ], - 'audio/wav': [[0x52, 0x49, 0x46, 0x46, '??', '??', '??', '??', 0x57, 0x41, 0x56, 0x45]], // 'RIFF....WAVE' - 'video/avi': [[0x52, 0x49, 0x46, 0x46, '??', '??', '??', '??', 0x41, 0x56, 0x49, 0x20]], // 'RIFF....AVI ' - - // Miscellaneous. - 'font/woff': [[0x77, 0x4F, 0x46, 0x46]], // 'wOFF' - 'font/woff2': [[0x77, 0x4F, 0x46, 0x32]], // 'wOF2' -}; - -/** - * Represents a single byte in the magic number tree. If this node terminates a known MIME type - * (see magic numbers above), then the mimeType field will be set. - */ -class Node { - /** @type {string} */ - mimeType; - - /** @type {Object} */ - children = {}; - - /** @param {number} value The byte that this Node points at. */ - constructor(value) { - /** @type {number} */ - this.value = value; - } -} - -/** Top-level node in the byte tree. */ -let root = null; -/** The maximum depth of the byte tree. */ -let maxDepth = 0; - -/** - * This function initializes the byte tree. It is lazily called upon findMimeType(), but if you care - * about when the tree initializes (like in startup, etc), you can call it yourself here. - */ -export function initialize() { - root = new Node(); - - // Construct the tree, erroring if overlapping mime types are found. - for (const mimeType in fileSignatures) { - for (const signature of fileSignatures[mimeType]) { - let curNode = root; - let depth = 0; - for (const byte of signature) { - if (curNode.children[byte] === undefined) { - if (byte === '??' && !curNode.children['??'] && Object.keys(curNode.children).length > 0) { - // Reset the byte tree, it is bogus. - root = null; - throw 'Cannot add a placeholder child to a node that has non-placeholder children'; - } else if (byte !== '??' && curNode.children['??']) { - // Reset the byte tree, it is bogus. - root = null; - throw 'Cannot add a non-placeholder child to a node that has a placeholder child'; - } - curNode.children[byte] = new Node(byte); - } - depth++; - curNode = curNode.children[byte]; - } // for each byte - - if (maxDepth < depth) { - maxDepth = depth; - } - - if (curNode.mimeType) { - throw `Magic number collision: ${curNode.mimeType} overlaps with ${mimeType}`; - } else if (Object.keys(curNode.children).length > 0) { - throw `${mimeType} magic number is not unique, it collides with other mime types`; - } - curNode.mimeType = mimeType; - } // for each signature - } -} - -/** - * Finds the likely MIME type represented by the ArrayBuffer. - * @param {ArrayBuffer} ab - * @returns {string} The MIME type of the buffer, or undefined. - */ -export function findMimeType(ab) { - if (!root) { - initialize(); - } - - const arr = new Uint8Array(ab); - let curNode = root; - let mimeType; - // Step through bytes, updating curNode as it walks down the byte tree. - for (const byte of arr) { - // If we found the mimeType or it is unknown, break the loop. - if (!curNode || (mimeType = curNode.mimeType)) { - break; - } - // Move into the next byte's node (if it exists) or the placeholder node (if it exists). - curNode = curNode.children[byte] || curNode.children['??']; - } - return mimeType; -} diff --git a/image/parsers/README.md b/image/parsers/README.md deleted file mode 100644 index 17b0168..0000000 --- a/image/parsers/README.md +++ /dev/null @@ -1,6 +0,0 @@ -General-purpose, event-based parsers for digital images. - -Currently supports GIF, JPEG, and PNG. - -Some nice implementations of Exif parsing for HEIF, TIFF here: -https://github.com/MikeKovarik/exifr/tree/master/src/file-parsers \ No newline at end of file diff --git a/image/parsers/exif.js b/image/parsers/exif.js deleted file mode 100644 index e09d182..0000000 --- a/image/parsers/exif.js +++ /dev/null @@ -1,316 +0,0 @@ -/* - * exif.js - * - * Parse EXIF. - * - * Licensed under the MIT License - * - * Copyright(c) 2024 Google Inc. - */ - -import { ByteStream } from '../../io/bytestream.js'; - -/** @enum {number} */ -export const ExifTagNumber = { - // Tags used by IFD0. - IMAGE_DESCRIPTION: 0x010e, - MAKE: 0x010f, - MODEL: 0x0110, - ORIENTATION: 0x0112, - X_RESOLUTION: 0x011a, - Y_RESOLUTION: 0x011b, - RESOLUTION_UNIT: 0x0128, - SOFTWARE: 0x0131, - DATE_TIME: 0x0132, - WHITE_POINT: 0x013e, - PRIMARY_CHROMATICITIES: 0x013f, - Y_CB_CR_COEFFICIENTS: 0x0211, - Y_CB_CR_POSITIONING: 0x0213, - REFERENCE_BLACK_WHITE: 0x0214, - COPYRIGHT: 0x8298, - EXIF_OFFSET: 0x8769, - - // Tags used by Exif SubIFD. - EXPOSURE_TIME: 0x829a, - F_NUMBER: 0x829d, - EXPOSURE_PROGRAM: 0x8822, - ISO_SPEED_RATINGS: 0x8827, - EXIF_VERSION: 0x9000, - DATE_TIME_ORIGINAL: 0x9003, - DATE_TIME_DIGITIZED: 0x9004, - COMPONENT_CONFIGURATION: 0x9101, - COMPRESSED_BITS_PER_PIXEL: 0x9102, - SHUTTER_SPEED_VALUE: 0x9201, - APERTURE_VALUE: 0x9202, - BRIGHTNESS_VALUE: 0x9203, - EXPOSURE_BIAS_VALUE: 0x9204, - MAX_APERTURE_VALUE: 0x9205, - SUBJECT_DISTANCE: 0x9206, - METERING_MODE: 0x9207, - LIGHT_SOURCE: 0x9208, - FLASH: 0x9209, - FOCAL_LENGTH: 0x920a, - MAKER_NOTE: 0x927c, - USER_COMMENT: 0x9286, - FLASH_PIX_VERSION: 0xa000, - COLOR_SPACE: 0xa001, - EXIF_IMAGE_WIDTH: 0xa002, - EXIF_IMAGE_HEIGHT: 0xa003, - RELATED_SOUND_FILE: 0xa004, - EXIF_INTEROPERABILITY_OFFSET: 0xa005, - FOCAL_PLANE_X_RESOLUTION: 0xa20e, - FOCAL_PLANE_Y_RESOLUTION: 0x20f, - FOCAL_PLANE_RESOLUTION_UNIT: 0xa210, - SENSING_METHOD: 0xa217, - FILE_SOURCE: 0xa300, - SCENE_TYPE: 0xa301, - - // Tags used by IFD1. - IMAGE_WIDTH: 0x0100, - IMAGE_LENGTH: 0x0101, - BITS_PER_SAMPLE: 0x0102, - COMPRESSION: 0x0103, - PHOTOMETRIC_INTERPRETATION: 0x0106, - STRIP_OFFSETS: 0x0111, - SAMPLES_PER_PIXEL: 0x0115, - ROWS_PER_STRIP: 0x0116, - STRIP_BYTE_COUNTS: 0x0117, - // X_RESOLUTION, Y_RESOLUTION - PLANAR_CONFIGURATION: 0x011c, - // RESOLUTION_UNIT - JPEG_IF_OFFSET: 0x0201, - JPEG_IF_BYTE_COUNT: 0x0202, - // Y_CB_CR_COEFFICIENTS - Y_CB_CR_SUB_SAMPLING: 0x0212, - // Y_CB_CR_POSITIONING, REFERENCE_BLACK_WHITE -}; - -/** @enum {number} */ -export const ExifDataFormat = { - UNSIGNED_BYTE: 1, - ASCII_STRING: 2, - UNSIGNED_SHORT: 3, - UNSIGNED_LONG: 4, - UNSIGNED_RATIONAL: 5, - SIGNED_BYTE: 6, - UNDEFINED: 7, - SIGNED_SHORT: 8, - SIGNED_LONG: 9, - SIGNED_RATIONAL: 10, - SINGLE_FLOAT: 11, - DOUBLE_FLOAT: 12, -}; - -/** - * @typedef ExifValue - * @property {ExifTagNumber} tagNumber The numerical value of the tag. - * @property {string=} tagName A string representing the tag number. - * @property {ExifDataFormat} dataFormat The data format. - * @property {number=} numericalValue Populated for SIGNED/UNSIGNED BYTE/SHORT/LONG/FLOAT. - * @property {string=} stringValue Populated only for ASCII_STRING. - * @property {number=} numeratorValue Populated only for SIGNED/UNSIGNED RATIONAL. - * @property {number=} denominatorValue Populated only for SIGNED/UNSIGNED RATIONAL. - * @property {number=} numComponents Populated only for UNDEFINED data format. - * @property {number=} offsetValue Populated only for UNDEFINED data format. - */ - -/** - * @param {number} tagNumber - * @param {string} type - * @param {number} len - * @param {number} dataVal - */ -function warnBadLength(tagNumber, type, len, dataVal) { - const hexTag = tagNumber.toString(16); - console.warn(`Tag 0x${hexTag} is ${type} with len=${len} and data=${dataVal}`); -} - -/** - * @param {ByteStream} stream - * @param {ByteStream} lookAheadStream - * @param {boolean} debug - * @returns {ExifValue} - */ -export function getExifValue(stream, lookAheadStream, DEBUG = false) { - const tagNumber = stream.readNumber(2); - let tagName = findNameWithValue(ExifTagNumber, tagNumber); - if (!tagName) { - tagName = `UNKNOWN (0x${tagNumber.toString(16)})`; - } - - let dataFormat = stream.readNumber(2); - - // Handle bad types for special tags. - if (tagNumber === ExifTagNumber.EXIF_OFFSET) { - dataFormat = ExifDataFormat.UNSIGNED_LONG; - } - - const dataFormatName = findNameWithValue(ExifDataFormat, dataFormat); - if (!dataFormatName) throw `Invalid data format: ${dataFormat}`; - - /** @type {ExifValue} */ - const exifValue = { - tagNumber, - tagName, - dataFormat, - }; - - let len = stream.readNumber(4); - switch (dataFormat) { - case ExifDataFormat.UNSIGNED_BYTE: - if (len !== 1 && DEBUG) { - warnBadLength(tagNumber, dataFormatName, len, stream.peekNumber(4)); - } - exifValue.numericalValue = stream.readNumber(1); - stream.skip(3); - break; - case ExifDataFormat.ASCII_STRING: - if (len <= 4) { - exifValue.stringValue = stream.readString(4); - } else { - const strOffset = stream.readNumber(4); - exifValue.stringValue = lookAheadStream.tee().skip(strOffset).readString(len - 1); - } - break; - case ExifDataFormat.UNSIGNED_SHORT: - if (len !== 1 && DEBUG) { - warnBadLength(tagNumber, dataFormatName, len, stream.peekNumber(4)); - } - exifValue.numericalValue = stream.readNumber(2); - stream.skip(2); - break; - case ExifDataFormat.UNSIGNED_LONG: - if (len !== 1 && DEBUG) { - warnBadLength(tagNumber, dataFormatName, len, stream.peekNumber(4)); - } - exifValue.numericalValue = stream.readNumber(4); - break; - case ExifDataFormat.UNSIGNED_RATIONAL: - if (len !== 1 && DEBUG) { - warnBadLength(tagNumber, dataFormatName, len, stream.peekNumber(4)); - } - - const uratStream = lookAheadStream.tee().skip(stream.readNumber(4)); - exifValue.numeratorValue = uratStream.readNumber(4); - exifValue.denominatorValue = uratStream.readNumber(4); - break; - case ExifDataFormat.SIGNED_BYTE: - if (len !== 1 && DEBUG) { - warnBadLength(tagNumber, dataFormatName, len, stream.peekSignedNumber(4)); - } - exifValue.numericalValue = stream.readSignedNumber(1); - stream.skip(3); - break; - case ExifDataFormat.UNDEFINED: - exifValue.numComponents = len; - exifValue.offsetValue = stream.readNumber(4); - break; - case ExifDataFormat.SIGNED_SHORT: - if (len !== 1 && DEBUG) { - warnBadLength(tagNumber, dataFormatName, len, stream.peekSignedNumber(4)); - } - exifValue.numericalValue = stream.readSignedNumber(2); - stream.skip(2); - break; - case ExifDataFormat.SIGNED_LONG: - if (len !== 1) { - warnBadLength(tagNumber, dataFormatName, len, stream.peekSignedNumber(4)); - } - exifValue.numericalValue = stream.readSignedNumber(4); - break; - case ExifDataFormat.SIGNED_RATIONAL: - if (len !== 1 && DEBUG) { - warnBadLength(tagNumber, dataFormatName, len, stream.peekNumber(4)); - } - - const ratStream = lookAheadStream.tee().skip(stream.readNumber(4)); - exifValue.numeratorValue = ratStream.readSignedNumber(4); - exifValue.denominatorValue = ratStream.readSignedNumber(4); - break; - default: - throw `Bad data format: ${dataFormat}`; - } - return exifValue; -} - -/** - * Reads an Image File Directory from stream, populating the map. - * @param {ByteStream} stream The stream to extract the Exif value descriptors. - * @param {ByteStream} lookAheadStream The lookahead stream if the offset is used. - * @param {Map v === valToFind); - return entry ? entry[0] : null; -} diff --git a/image/parsers/gif.js b/image/parsers/gif.js deleted file mode 100644 index 6283e60..0000000 --- a/image/parsers/gif.js +++ /dev/null @@ -1,485 +0,0 @@ -/* - * gif.js - * - * An event-based parser for GIF images. - * - * Licensed under the MIT License - * - * Copyright(c) 2023 Google Inc. - */ - -import { BitStream } from '../../io/bitstream.js'; -import { ByteStream } from '../../io/bytestream.js'; -import { createEvent } from './parsers.js'; - -// https://www.w3.org/Graphics/GIF/spec-gif89a.txt - -export const GifParseEventType = { - APPLICATION_EXTENSION: 'application_extension', - COMMENT_EXTENSION: 'comment_extension', - GRAPHIC_CONTROL_EXTENSION: 'graphic_control_extension', - HEADER: 'header', - LOGICAL_SCREEN: 'logical_screen', - PLAIN_TEXT_EXTENSION: 'plain_text_extension', - TABLE_BASED_IMAGE: 'table_based_image', - TRAILER: 'trailer', -}; - -/** - * @typedef GifHeader - * @property {string} version - */ - -/** - * @typedef GifColor - * @property {number} red - * @property {number} green - * @property {number} blue - */ - -/** - * @typedef GifLogicalScreen - * @property {number} logicalScreenWidth - * @property {number} logicalScreenHeight - * @property {boolean} globalColorTableFlag - * @property {number} colorResolution - * @property {boolean} sortFlag - * @property {number} globalColorTableSize - * @property {number} backgroundColorIndex - * @property {number} pixelAspectRatio - * @property {GifColor[]=} globalColorTable Only if globalColorTableFlag is true. - */ - -/** - * @typedef GifTableBasedImage - * @property {number} imageLeftPosition - * @property {number} imageTopPosition - * @property {number} imageWidth - * @property {number} imageHeight - * @property {boolean} localColorTableFlag - * @property {boolean} interlaceFlag - * @property {boolean} sortFlag - * @property {number} localColorTableSize - * @property {GifColor[]=} localColorTable Only if localColorTableFlag is true. - * @property {number} lzwMinimumCodeSize - * @property {Uint8Array} imageData - */ - -/** - * @typedef GifGraphicControlExtension - * @property {number} disposalMethod - * @property {boolean} userInputFlag - * @property {boolean} transparentColorFlag - * @property {number} delayTime - * @property {number} transparentColorIndex - */ - -/** - * @typedef GifCommentExtension - * @property {string} comment - */ - -/** - * @typedef GifPlainTextExtension - * @property {number} textGridLeftPosition - * @property {number} textGridTopPosition - * @property {number} textGridWidth - * @property {number} textGridHeight - * @property {number} characterCellWidth - * @property {number} characterCellHeight - * @property {number} textForegroundColorIndex - * @property {number} textBackgroundColorIndex - * @property {string} plainText - */ - -/** - * @typedef GifApplicationExtension - * @property {string} applicationIdentifier - * @property {Uint8Array} applicationAuthenticationCode - * @property {Uint8Array} applicationData - */ - -/** - * The Grammar. - * - * ::= Header * Trailer - * ::= Logical Screen Descriptor [Global Color Table] - * ::= | - * - * ::= [Graphic Control Extension] - * ::= | - * Plain Text Extension - * ::= Image Descriptor [Local Color Table] Image Data - * ::= Application Extension | - * Comment Extension - */ - -export class GifParser extends EventTarget { - /** - * @type {ByteStream} - * @private - */ - bstream; - - /** - * @type {string} - * @private - */ - version; - - /** @param {ArrayBuffer} ab */ - constructor(ab) { - super(); - this.bstream = new ByteStream(ab); - // The entire GIF structure is Little-Endian (which is actually ByteStream's default). - this.bstream.setLittleEndian(); - } - - /** - * Type-safe way to bind a listener for a GifApplicationExtension. - * @param {function(CustomEvent): void} listener - * @returns {GifParser} for chaining - */ - onApplicationExtension(listener) { - super.addEventListener(GifParseEventType.APPLICATION_EXTENSION, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a GifCommentExtension. - * @param {function(CustomEvent): void} listener - * @returns {GifParser} for chaining - */ - onCommentExtension(listener) { - super.addEventListener(GifParseEventType.COMMENT_EXTENSION, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a GifGraphicControlExtension. - * @param {function(CustomEvent): void} listener - * @returns {GifParser} for chaining - */ - onGraphicControlExtension(listener) { - super.addEventListener(GifParseEventType.GRAPHIC_CONTROL_EXTENSION, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a GifHeader. - * @param {function(CustomEvent): void} listener - * @returns {GifParser} for chaining - */ - onHeader(listener) { - super.addEventListener(GifParseEventType.HEADER, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a GifLogicalScreen. - * @param {function(CustomEvent): void} listener - * @returns {GifParser} for chaining - */ - onLogicalScreen(listener) { - super.addEventListener(GifParseEventType.LOGICAL_SCREEN, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a GifPlainTextExtension. - * @param {function(CustomEvent): void} listener - * @returns {GifParser} for chaining - */ - onPlainTextExtension(listener) { - super.addEventListener(GifParseEventType.PLAIN_TEXT_EXTENSION, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a GifTableBasedImage. - * @param {function(CustomEvent): void} listener - * @returns {GifParser} for chaining - */ - onTableBasedImage(listener) { - super.addEventListener(GifParseEventType.TABLE_BASED_IMAGE, listener); - return this; - } - - /** - * Type-safe way to bind a listener for the GifTrailer. - * @param {function(CustomEvent): void} listener - * @returns {GifParser} for chaining - */ - onTrailer(listener) { - super.addEventListener(GifParseEventType.TRAILER, listener); - return this; - } - - /** - * @returns {Promise} A Promise that resolves when the parsing is complete. - */ - async start() { - // Header. - const gif = this.bstream.readString(3); // "GIF" - if (gif !== "GIF") throw `Not GIF: ${gif}`; - - const version = this.version = this.bstream.readString(3); // "87a" or "89a" - if (!["87a", "89a"].includes(version)) throw `Bad version: ${version}`; - - this.dispatchEvent(createEvent(GifParseEventType.HEADER, {version})); - - // Logical Screen Descriptor. - const logicalScreenWidth = this.bstream.readNumber(2); - const logicalScreenHeight = this.bstream.readNumber(2); - - const bitstream = new BitStream(this.bstream.readBytes(1).buffer, true); - const globalColorTableFlag = !!bitstream.readBits(1); - const colorResolution = bitstream.readBits(3) + 1; - const sortFlag = !!bitstream.readBits(1); // sortFlag - const globalColorTableSize = 2 ** (bitstream.readBits(3) + 1); - const backgroundColorIndex = this.bstream.readNumber(1); - const pixelAspectRatio = this.bstream.readNumber(1); - - // Global Color Table - let globalColorTable = undefined; - if (globalColorTableFlag) { - globalColorTable = []; - // Series of R,G,B. - for (let c = 0; c < globalColorTableSize; ++c) { - globalColorTable.push(/** @type {GifColor} */ ({ - red: this.bstream.readNumber(1), - green: this.bstream.readNumber(1), - blue: this.bstream.readNumber(1), - })); - } - } - this.dispatchEvent(createEvent(GifParseEventType.LOGICAL_SCREEN, - /** @type {GifLogicalScreen} */ ({ - logicalScreenWidth, - logicalScreenHeight, - globalColorTableFlag, - colorResolution, - sortFlag, - globalColorTableSize, - backgroundColorIndex, - pixelAspectRatio, - globalColorTable, - }) - )); - - while (this.readGraphicBlock()) { - // Read a graphic block - } - } - - /** - * @private - * @returns {boolean} True if this was not the last block. - */ - readGraphicBlock() { - let nextByte = this.bstream.readNumber(1); - - // Image Descriptor. - if (nextByte === 0x2C) { - const imageLeftPosition = this.bstream.readNumber(2); - const imageTopPosition = this.bstream.readNumber(2); - const imageWidth = this.bstream.readNumber(2); - const imageHeight = this.bstream.readNumber(2); - - const bitstream = new BitStream(this.bstream.readBytes(1).buffer, true); - const localColorTableFlag = !!bitstream.readBits(1); - const interlaceFlag = !!bitstream.readBits(1); - const sortFlag = !!bitstream.readBits(1); - bitstream.readBits(2); // reserved - const localColorTableSize = 2 ** (bitstream.readBits(3) + 1); - - let localColorTable = undefined; - if (localColorTableFlag) { - // this.bstream.readBytes(3 * localColorTableSize); - localColorTable = []; - // Series of R,G,B. - for (let c = 0; c < localColorTableSize; ++c) { - localColorTable.push(/** @type {GifColor} */ ({ - red: this.bstream.readNumber(1), - green: this.bstream.readNumber(1), - blue: this.bstream.readNumber(1), - })); - } - } - - // Table-Based Image. - const lzwMinimumCodeSize = this.bstream.readNumber(1); - const bytesArr = []; - let bytes; - let totalNumBytes = 0; - while ((bytes = this.readSubBlock())) { - totalNumBytes += bytes.byteLength; - bytesArr.push(bytes); - } - - const imageData = new Uint8Array(totalNumBytes); - let ptr = 0; - for (const arr of bytesArr) { - imageData.set(arr, ptr); - ptr += arr.byteLength; - } - - this.dispatchEvent(createEvent(GifParseEventType.TABLE_BASED_IMAGE, - /** @type {GifTableBasedImage} */ ({ - imageLeftPosition, - imageTopPosition, - imageWidth, - imageHeight, - localColorTableFlag, - interlaceFlag, - sortFlag, - localColorTableSize, - localColorTable, - lzwMinimumCodeSize, - imageData, - }) - )); - - return true; - } - // Extensions. - else if (nextByte === 0x21) { - if (this.version !== '89a') { - throw `Found Extension Introducer (0x21) but was not GIF 89a: ${this.version}`; - } - - const label = this.bstream.readNumber(1); - - // Graphic Control Extension. - if (label === 0xF9) { - const blockSize = this.bstream.readNumber(1); - if (blockSize !== 4) throw `GCE: Block size of ${blockSize}`; - - // Packed Fields. - const bitstream = new BitStream(this.bstream.readBytes(1).buffer, true); - bitstream.readBits(3); // Reserved - const disposalMethod = bitstream.readBits(3); - const userInputFlag = !!bitstream.readBits(1); - const transparentColorFlag = !!bitstream.readBits(1); - - const delayTime = this.bstream.readNumber(2); - const transparentColorIndex = this.bstream.readNumber(1); - const blockTerminator = this.bstream.readNumber(1); - if (blockTerminator !== 0) throw `GCE: Block terminator of ${blockTerminator}`; - - this.dispatchEvent(createEvent(GifParseEventType.GRAPHIC_CONTROL_EXTENSION, - /** @type {GifGraphicControlExtension} */ ({ - disposalMethod, - userInputFlag, - transparentColorFlag, - delayTime, - transparentColorIndex, - }) - )); - return true; - } - - // Comment Extension. - else if (label === 0xFE) { - let bytes; - let comment = ''; - while ((bytes = this.readSubBlock())) { - comment += new TextDecoder().decode(bytes); - } - this.dispatchEvent(createEvent(GifParseEventType.COMMENT_EXTENSION, comment)); - return true; - } - - // Plain Text Extension. - else if (label === 0x01) { - const blockSize = this.bstream.readNumber(1); - if (blockSize !== 12) throw `PTE: Block size of ${blockSize}`; - - const textGridLeftPosition = this.bstream.readNumber(2); - const textGridTopPosition = this.bstream.readNumber(2); - const textGridWidth = this.bstream.readNumber(2); - const textGridHeight = this.bstream.readNumber(2); - const characterCellWidth = this.bstream.readNumber(1); - const characterCellHeight = this.bstream.readNumber(1); - const textForegroundColorIndex = this.bstream.readNumber(1); - const textBackgroundColorIndex = this.bstream.readNumber(1); - let bytes; - let plainText = '' - while ((bytes = this.readSubBlock())) { - plainText += new TextDecoder().decode(bytes); - } - - this.dispatchEvent(createEvent(GifParseEventType.PLAIN_TEXT_EXTENSION, - /** @type {GifPlainTextExtension} */ ({ - textGridLeftPosition, - textGridTopPosition, - textGridWidth, - textGridHeight, - characterCellWidth, - characterCellHeight, - textForegroundColorIndex, - textBackgroundColorIndex, - plainText, - }) - )); - - return true; - } - - // Application Extension. - else if (label === 0xFF) { - const blockSize = this.bstream.readNumber(1); - if (blockSize !== 11) throw `AE: Block size of ${blockSize}`; - - const applicationIdentifier = this.bstream.readString(8); - const applicationAuthenticationCode = this.bstream.readBytes(3); - const bytesArr = []; - let bytes; - let totalNumBytes = 0; - while ((bytes = this.readSubBlock())) { - totalNumBytes += bytes.byteLength; - bytesArr.push(bytes); - } - - const applicationData = new Uint8Array(totalNumBytes); - let ptr = 0; - for (const arr of bytesArr) { - applicationData.set(arr, ptr); - ptr += arr.byteLength; - } - - this.dispatchEvent(createEvent(GifParseEventType.APPLICATION_EXTENSION, - /** {@type GifApplicationExtension} */ ({ - applicationIdentifier, - applicationAuthenticationCode, - applicationData, - }) - )); - - return true; - } - - else { - throw `Unrecognized extension label=0x${label.toString(16)}`; - } - } - else if (nextByte === 0x3B) { - this.dispatchEvent(createEvent(GifParseEventType.TRAILER)); - // Read the trailer. - return false; - } - else { - throw `Unknown marker: 0x${nextByte.toString(16)}`; - } - } - - /** - * @private - * @returns {Uint8Array} Data from the sub-block, or null if this was the last, zero-length block. - */ - readSubBlock() { - let subBlockSize = this.bstream.readNumber(1); - if (subBlockSize === 0) return null; - return this.bstream.readBytes(subBlockSize); - } -} diff --git a/image/parsers/jpeg.js b/image/parsers/jpeg.js deleted file mode 100644 index d3e62e3..0000000 --- a/image/parsers/jpeg.js +++ /dev/null @@ -1,490 +0,0 @@ -/* - * jpeg.js - * - * An event-based parser for JPEG images. - * - * Licensed under the MIT License - * - * Copyright(c) 2024 Google Inc. - */ - -import { ByteStream } from '../../io/bytestream.js'; -import { getExifProfile } from './exif.js'; -import { createEvent } from './parsers.js'; - -/** @typedef {import('./exif.js').ExifValue} ExifValue */ - -// https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format -// https://www.media.mit.edu/pia/Research/deepview/exif.html -// https://mykb.cipindanci.com/archive/SuperKB/1294/JPEG%20File%20Layout%20and%20Format.htm -// https://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf - -let DEBUG = false; - -/** @enum {string} */ -export const JpegParseEventType = { - APP0_MARKER: 'app0_marker', - APP0_EXTENSION: 'app0_extension', - APP1_EXIF: 'app1_exif', - DEFINE_QUANTIZATION_TABLE: 'define_quantization_table', - DEFINE_HUFFMAN_TABLE: 'define_huffman_table', - START_OF_FRAME: 'start_of_frame', - START_OF_SCAN: 'start_of_scan', -} - -/** @enum {number} */ -export const JpegSegmentType = { - SOF0: 0xC0, - SOF1: 0xC1, - SOF2: 0xC2, - DHT: 0xC4, - SOI: 0xD8, - EOI: 0xD9, - SOS: 0xDA, - DQT: 0xDB, - APP0: 0xE0, - APP1: 0xE1, -}; - -/** - * @param {Uint8Array} bytes An array of bytes of size 2. - * @returns {JpegSegmentType} Returns the second byte in bytes. - */ -function getJpegMarker(bytes) { - if (bytes.byteLength < 2) throw `Bad bytes length: ${bytes.byteLength}`; - if (bytes[0] !== 0xFF) throw `Bad marker, first byte=0x${bytes[0].toString(16)}`; - return bytes[1]; -} - -/** @enum {number} */ -export const JpegDensityUnits = { - NO_UNITS: 0, - PIXELS_PER_INCH: 1, - PIXELS_PER_CM: 2, -}; - -/** - * @typedef JpegApp0Marker - * @property {string} jfifVersion Like '1.02'. - * @property {JpegDensityUnits} densityUnits - * @property {number} xDensity - * @property {number} yDensity - * @property {number} xThumbnail - * @property {number} yThumbnail - * @property {Uint8Array} thumbnailData RGB data. Size is 3 x thumbnailWidth x thumbnailHeight. - */ - -/** @enum {number} */ -export const JpegExtensionThumbnailFormat = { - JPEG: 0x10, - ONE_BYTE_PER_PIXEL_PALETTIZED: 0x11, - THREE_BYTES_PER_PIXEL_RGB: 0x13, -}; - -/** - * @typedef JpegApp0Extension - * @property {JpegExtensionThumbnailFormat} thumbnailFormat - * @property {Uint8Array} thumbnailData Raw thumbnail data - */ - -/** @typedef {Map} JpegExifProfile */ - -/** - * @typedef JpegDefineQuantizationTable - * @property {number} tableNumber Table/component number. - * @property {number} precision (0=byte, 1=word). - * @property {number[]} tableValues 64 numbers representing the quantization table. - */ - -/** @enum {number} */ -export const JpegHuffmanTableType = { - DC: 0, - AC: 1, -}; - -/** - * @typedef JpegDefineHuffmanTable - * @property {number} tableNumber Table/component number (0-3). - * @property {JpegHuffmanTableType} tableType Either DC or AC. - * @property {number[]} numberOfSymbols A 16-byte array specifying the # of symbols of each length. - * @property {number[]} symbols - */ - -/** @enum {number} */ -export const JpegDctType = { - BASELINE: 0, - EXTENDED_SEQUENTIAL: 1, - PROGRESSIVE: 2, -}; - -/** @enum {number} */ -export const JpegComponentType = { - Y: 1, - CB: 2, - CR: 3, - I: 4, - Q: 5, -}; - -/** - * @typedef JpegComponentDetail - * @property {JpegComponentType} componentId - * @property {number} verticalSamplingFactor - * @property {number} horizontalSamplingFactor - * @property {number} quantizationTableNumber - */ - -/** - * @typedef JpegStartOfFrame - * @property {JpegDctType} dctType - * @property {number} dataPrecision - * @property {number} imageHeight - * @property {number} imageWidth - * @property {number} numberOfComponents Usually 1, 3, or 4. - * @property {JpegComponentDetail[]} componentDetails - */ - -/** - * @typedef JpegStartOfScan - * @property {number} componentsInScan - * @property {number} componentSelectorY - * @property {number} huffmanTableSelectorY - * @property {number} componentSelectorCb - * @property {number} huffmanTableSelectorCb - * @property {number} componentSelectorCr - * @property {number} huffmanTableSelectorCr - * @property {number} scanStartPositionInBlock - * @property {number} scanEndPositionInBlock - * @property {number} successiveApproximationBitPosition - * @property {Uint8Array} rawImageData - */ - -export class JpegParser extends EventTarget { - /** - * @type {ByteStream} - * @private - */ - bstream; - - /** - * @type {boolean} - * @private - */ - hasApp0MarkerSegment = false; - - /** @param {ArrayBuffer} ab */ - constructor(ab) { - super(); - this.bstream = new ByteStream(ab); - } - - /** - * Type-safe way to bind a listener for a JpegApp0Marker. - * @param {function(CustomEvent): void} listener - * @returns {JpegParser} for chaining - */ - onApp0Marker(listener) { - super.addEventListener(JpegParseEventType.APP0_MARKER, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a JpegApp0Extension. - * @param {function(CustomEvent): void} listener - * @returns {JpegParser} for chaining - */ - onApp0Extension(listener) { - super.addEventListener(JpegParseEventType.APP0_EXTENSION, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a JpegExifProfile. - * @param {function(CustomEvent): void} listener - * @returns {JpegParser} for chaining - */ - onApp1Exif(listener) { - super.addEventListener(JpegParseEventType.APP1_EXIF, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a JpegDefineQuantizationTable. - * @param {function(CustomEvent): void} listener - * @returns {JpegParser} for chaining - */ - onDefineQuantizationTable(listener) { - super.addEventListener(JpegParseEventType.DEFINE_QUANTIZATION_TABLE, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a JpegDefineHuffmanTable. - * @param {function(CustomEvent): void} listener - * @returns {JpegParser} for chaining - */ - onDefineHuffmanTable(listener) { - super.addEventListener(JpegParseEventType.DEFINE_HUFFMAN_TABLE, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a JpegStartOfFrame. - * @param {function(CustomEvent): void} listener - * @returns {JpegParser} for chaining - */ - onStartOfFrame(listener) { - super.addEventListener(JpegParseEventType.START_OF_FRAME, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a JpegStartOfScan. - * @param {function(CustomEvent): void} listener - * @returns {JpegParser} for chaining - */ - onStartOfScan(listener) { - super.addEventListener(JpegParseEventType.START_OF_SCAN, listener); - return this; - } - - /** @returns {Promise} A Promise that resolves when the parsing is complete. */ - async start() { - const segmentType = getJpegMarker(this.bstream.readBytes(2)); - if (segmentType !== JpegSegmentType.SOI) throw `Did not start with a SOI`; - - let jpegMarker; - do { - jpegMarker = getJpegMarker(this.bstream.readBytes(2)); - - if (jpegMarker === JpegSegmentType.APP0) { - this.bstream.setBigEndian(); - const length = this.bstream.readNumber(2); - const skipAheadStream = this.bstream.tee().skip(length - 2); - - const identifier = this.bstream.readString(4); - if (identifier === 'JFIF') { - if (this.hasApp0MarkerSegment) throw `JFIF found after JFIF`; - if (this.bstream.readNumber(1) !== 0) throw 'No null byte terminator for JFIF'; - - this.hasApp0MarkerSegment = true; - const majorVer = `${this.bstream.readNumber(1)}.`; - const minorVer = `${this.bstream.readNumber(1)}`.padStart(2, '0'); - const densityUnits = this.bstream.readNumber(1); - const xDensity = this.bstream.readNumber(2); - const yDensity = this.bstream.readNumber(2); - const xThumbnail = this.bstream.readNumber(1); - const yThumbnail = this.bstream.readNumber(1); - - /** @type {JpegApp0Marker} */ - let app0MarkerSegment = { - jfifVersion: `${majorVer}${minorVer}`, - densityUnits, - xDensity, - yDensity, - xThumbnail, - yThumbnail, - thumbnailData: this.bstream.readBytes(3 * xThumbnail * yThumbnail), - }; - this.dispatchEvent(createEvent(JpegParseEventType.APP0_MARKER, app0MarkerSegment)); - } - else if (identifier === 'JFXX') { - if (!this.hasApp0MarkerSegment) throw `JFXX found without JFIF`; - if (this.bstream.readNumber(1) !== 0) throw 'No null byte terminator for JFXX'; - - const thumbnailFormat = this.bstream.readNumber(1); - if (!Object.values(JpegExtensionThumbnailFormat).includes(thumbnailFormat)) { - throw `Bad Extension Thumbnail Format: ${thumbnailFormat}`; - } - - // The JFXX segment has length (2), 'JFXX' (4), null byte (1), thumbnail format (1) - const thumbnailData = this.bstream.readBytes(length - 8); - - /** @type {JpegApp0Extension} */ - let app0ExtensionSegment = { - thumbnailFormat, - thumbnailData, - }; - this.dispatchEvent(createEvent(JpegParseEventType.APP0_EXTENSION, app0ExtensionSegment)); - } - else { - throw `Bad APP0 identifier: ${identifier}`; - } - - this.bstream = skipAheadStream; - } // End of APP0 - else if (jpegMarker === JpegSegmentType.APP1) { - this.bstream.setBigEndian(); - const length = this.bstream.readNumber(2); - const skipAheadStream = this.bstream.tee().skip(length - 2); - - const identifier = this.bstream.readString(4); - if (identifier !== 'Exif') { - // TODO: Handle XMP. - // console.log(identifier + this.bstream.readString(length - 2 - 4)); - this.bstream = skipAheadStream; - continue; - } - if (this.bstream.readNumber(2) !== 0) throw `No null byte termination`; - - const exifValueMap = getExifProfile(this.bstream); - this.dispatchEvent(createEvent(JpegParseEventType.APP1_EXIF, exifValueMap)); - - this.bstream = skipAheadStream; - } // End of APP1 - else if (jpegMarker === JpegSegmentType.DQT) { - this.bstream.setBigEndian(); - const length = this.bstream.readNumber(2); - - const dqtLength = length - 2; - let ptr = 0; - while (ptr < dqtLength) { - // https://gist.github.com/FranckFreiburger/d8e7445245221c5cf38e69a88f22eeeb#file-getjpegquality-js-L76 - const firstByte = this.bstream.readNumber(1); - // Lower 4 bits are the component index. - const tableNumber = (firstByte & 0xF); - // Upper 4 bits are the precision (0=byte, 1=word). - const precision = ((firstByte & 0xF0) >> 4); - if (precision !== 0 && precision !== 1) throw `Weird value for DQT precision: ${precision}`; - - const valSize = precision === 0 ? 1 : 2; - const tableValues = new Array(64); - for (let v = 0; v < 64; ++v) { - tableValues[v] = this.bstream.readNumber(valSize); - } - - /** @type {JpegDefineQuantizationTable} */ - const table = { - tableNumber, - precision, - tableValues, - }; - this.dispatchEvent(createEvent(JpegParseEventType.DEFINE_QUANTIZATION_TABLE, table)); - - ptr += (1 + valSize * 64); - } - } // End of DQT - else if (jpegMarker === JpegSegmentType.DHT) { - this.bstream.setBigEndian(); - const length = this.bstream.readNumber(2); - let ptr = 2; - - while (ptr < length) { - const firstByte = this.bstream.readNumber(1); - const tableNumber = (firstByte & 0xF); - const tableType = ((firstByte & 0xF0) >> 4); - if (tableNumber > 3) throw `Weird DHT table number = ${tableNumber}`; - if (tableType !== 0 && tableType !== 1) throw `Weird DHT table type = ${tableType}`; - - const numberOfSymbols = Array.from(this.bstream.readBytes(16)); - let numCodes = 0; - for (let symbolLength = 1; symbolLength <= 16; ++symbolLength) { - const numSymbolsAtLength = numberOfSymbols[symbolLength - 1]; - numCodes += numSymbolsAtLength; - } - if (numCodes > 256) throw `Bad # of DHT codes: ${numCodes}`; - - const symbols = Array.from(this.bstream.readBytes(numCodes)); - - /** @type {JpegDefineHuffmanTable} */ - const table = { - tableNumber, - tableType, - numberOfSymbols, - symbols, - }; - this.dispatchEvent(createEvent(JpegParseEventType.DEFINE_HUFFMAN_TABLE, table)); - - ptr += (1 + 16 + numCodes); - } - if (ptr !== length) throw `Bad DHT ptr: ${ptr}!`; - } // End of DHT - else if (jpegMarker === JpegSegmentType.SOF0 - || jpegMarker === JpegSegmentType.SOF1 - || jpegMarker === JpegSegmentType.SOF2) { - this.bstream.setBigEndian(); - const length = this.bstream.readNumber(2); - - const dctType = (jpegMarker - JpegSegmentType.SOF0); - if (![0, 1, 2].includes(dctType)) throw `Weird DCT type: ${dctType}`; - - const dataPrecision = this.bstream.readNumber(1); - const imageHeight = this.bstream.readNumber(2); - const imageWidth = this.bstream.readNumber(2); - const numberOfComponents = this.bstream.readNumber(1); - const componentDetails = []; - for (let c = 0; c < numberOfComponents; ++c) { - const componentId = this.bstream.readNumber(1); - const nextByte = this.bstream.readNumber(1); - const verticalSamplingFactor = (nextByte & 0xF); - const horizontalSamplingFactor = ((nextByte & 0xF0) >> 4); - const quantizationTableNumber = this.bstream.readNumber(1); - - componentDetails.push({ - componentId, - verticalSamplingFactor, - horizontalSamplingFactor, - quantizationTableNumber, - }); - } - - /** @type {JpegStartOfFrame} */ - const sof = { - dctType, - dataPrecision, - imageHeight, - imageWidth, - numberOfComponents, - componentDetails, - }; - - this.dispatchEvent(createEvent(JpegParseEventType.START_OF_FRAME, sof)); - } // End of SOF0, SOF1, SOF2 - else if (jpegMarker === JpegSegmentType.SOS) { - this.bstream.setBigEndian(); - const length = this.bstream.readNumber(2); - // console.log(`Inside SOS with length = ${length}`); - if (length !== 12) throw `Bad length in SOS header: ${length}`; - - /** @type {JpegStartOfScan} */ - const sos = { - componentsInScan: this.bstream.readNumber(1), - componentSelectorY: this.bstream.readNumber(1), - huffmanTableSelectorY: this.bstream.readNumber(1), - componentSelectorCb: this.bstream.readNumber(1), - huffmanTableSelectorCb: this.bstream.readNumber(1), - componentSelectorCr: this.bstream.readNumber(1), - huffmanTableSelectorCr: this.bstream.readNumber(1), - scanStartPositionInBlock: this.bstream.readNumber(1), - scanEndPositionInBlock: this.bstream.readNumber(1), - successiveApproximationBitPosition: this.bstream.readNumber(1), - }; - - const rawImageDataStream = this.bstream.tee(); - let numBytes = 0; - // Immediately after SOS header is the compressed image data until the EOI marker is seen. - // Seek until we find the EOI marker. - while (true) { - if (this.bstream.readNumber(1) === 0xFF && - this.bstream.peekNumber(1) === JpegSegmentType.EOI) { - jpegMarker = this.bstream.readNumber(1); - break; - } else { - numBytes++; - } - } - - // NOTE: The below will have the null bytes after every 0xFF value. - sos.rawImageData = rawImageDataStream.readBytes(numBytes); - - this.dispatchEvent(createEvent(JpegParseEventType.START_OF_SCAN, sos)); - } // End of SOS - else { - this.bstream.setBigEndian(); - const length = this.bstream.peekNumber(2); - if (DEBUG) console.log(`Unsupported JPEG marker 0xff${jpegMarker.toString(16)} with length ${length}`); - this.bstream.skip(length); - } - } while (jpegMarker !== JpegSegmentType.EOI); - } -} diff --git a/image/parsers/parsers.js b/image/parsers/parsers.js deleted file mode 100644 index 44ff3b2..0000000 --- a/image/parsers/parsers.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * parsers.js - * - * Common functionality for all image parsers. - * - * Licensed under the MIT License - * - * Copyright(c) 2024 Google Inc. - */ - -/** - * Creates a new event of the given type with the specified data. - * @template T - * @param {string} type The event type. - * @param {T} data The event data. - * @returns {CustomEvent} The new event. - */ -export function createEvent(type, data) { - return new CustomEvent(type, { detail: data }); -} diff --git a/image/parsers/png.js b/image/parsers/png.js deleted file mode 100644 index 3de45c4..0000000 --- a/image/parsers/png.js +++ /dev/null @@ -1,731 +0,0 @@ -/* - * png.js - * - * An event-based parser for PNG images. - * - * Licensed under the MIT License - * - * Copyright(c) 2024 Google Inc. - */ - -import { ByteStream } from '../../io/bytestream.js'; -import { getExifProfile } from './exif.js'; -import { createEvent } from './parsers.js'; - -/** @typedef {import('./exif.js').ExifValue} ExifValue */ - -// https://www.w3.org/TR/png-3/ -// https://en.wikipedia.org/wiki/PNG#File_format - -let DEBUG = false; - -const SIG = new Uint8Array([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]); - -/** @enum {string} */ -export const PngParseEventType = { - // Critical chunks. - IDAT: 'image_data', - IHDR: 'image_header', - PLTE: 'palette', - - // Ancillary chunks. - bKGD: 'background_color', - cHRM: 'chromaticities_white_point', - eXIf: 'exif_profile', - gAMA: 'image_gamma', - hIST: 'histogram', - iTXt: 'intl_text_data', - pHYs: 'physical_pixel_dims', - sBIT: 'significant_bits', - sPLT: 'suggested_palette', - tEXt: 'textual_data', - tIME: 'last_mod_time', - tRNS: 'transparency', - zTXt: 'compressed_textual_data', -}; - -/** @enum {number} */ -export const PngColorType = { - GREYSCALE: 0, - TRUE_COLOR: 2, - INDEXED_COLOR: 3, - GREYSCALE_WITH_ALPHA: 4, - TRUE_COLOR_WITH_ALPHA: 6, -}; - -/** @enum {number} */ -export const PngInterlaceMethod = { - NO_INTERLACE: 0, - ADAM7_INTERLACE: 1, -} - -/** - * @typedef PngImageHeader - * @property {number} width - * @property {number} height - * @property {number} bitDepth - * @property {PngColorType} colorType - * @property {number} compressionMethod - * @property {number} filterMethod - * @property {number} interlaceMethod - */ - -/** - * @typedef PngSignificantBits - * @property {number=} significant_greyscale Populated for color types 0, 4. - * @property {number=} significant_red Populated for color types 2, 3, 6. - * @property {number=} significant_green Populated for color types 2, 3, 6. - * @property {number=} significant_blue Populated for color types 2, 3, 6. - * @property {number=} significant_alpha Populated for color types 4, 6. - */ - -/** - * @typedef PngChromaticities - * @property {number} whitePointX - * @property {number} whitePointY - * @property {number} redX - * @property {number} redY - * @property {number} greenX - * @property {number} greenY - * @property {number} blueX - * @property {number} blueY - */ - -/** - * @typedef PngColor - * @property {number} red - * @property {number} green - * @property {number} blue - */ - -/** - * @typedef PngPalette - * @property {PngColor[]} entries - */ - -/** - * @typedef PngTransparency - * @property {number=} greySampleValue Populated for color type 0. - * @property {number=} redSampleValue Populated for color type 2. - * @property {number=} blueSampleValue Populated for color type 2. - * @property {number=} greenSampleValue Populated for color type 2. - * @property {number[]=} alphaPalette Populated for color type 3. - */ - -/** - * @typedef PngImageData - * @property {Uint8Array} rawImageData - */ - -/** - * @typedef PngTextualData - * @property {string} keyword - * @property {string=} textString - */ - -/** - * @typedef PngCompressedTextualData - * @property {string} keyword - * @property {number} compressionMethod Only value supported is 0 for deflate compression. - * @property {Uint8Array=} compressedText - */ - -/** - * @typedef PngIntlTextualData - * @property {string} keyword - * @property {number} compressionFlag 0 for uncompressed, 1 for compressed. - * @property {number} compressionMethod 0 means zlib defalt when compressionFlag is 1. - * @property {string=} languageTag - * @property {string=} translatedKeyword - * @property {Uint8Array} text The raw UTF-8 text (may be compressed). - */ - -/** - * @typedef PngBackgroundColor - * @property {number=} greyscale Only for color types 0 and 4. - * @property {number=} red Only for color types 2 and 6. - * @property {number=} green Only for color types 2 and 6. - * @property {number=} blue Only for color types 2 and 6. - * @property {number=} paletteIndex Only for color type 3. - */ - -/** - * @typedef PngLastModTime - * @property {number} year Four-digit year. - * @property {number} month One-based. Value from 1-12. - * @property {number} day One-based. Value from 1-31. - * @property {number} hour Zero-based. Value from 0-23. - * @property {number} minute Zero-based. Value from 0-59. - * @property {number} second Zero-based. Value from 0-60 to allow for leap-seconds. - */ - -export const PngUnitSpecifier = { - UNKNOWN: 0, - METRE: 1, -}; - -/** - * @typedef PngPhysicalPixelDimensions - * @property {number} pixelPerUnitX - * @property {number} pixelPerUnitY - * @property {PngUnitSpecifier} unitSpecifier - */ - -/** @typedef {Map} PngExifProfile */ - -/** - * @typedef PngHistogram - * @property {number[]} frequencies The # of frequencies matches the # of palette entries. - */ - -/** - * @typedef PngSuggestedPaletteEntry - * @property {number} red - * @property {number} green - * @property {number} blue - * @property {number} alpha - * @property {number} frequency - */ - -/** - * @typedef PngSuggestedPalette - * @property {string} paletteName - * @property {number} sampleDepth Either 8 or 16. - * @property {PngSuggestedPaletteEntry[]} entries - */ - -/** - * @typedef PngChunk Internal use only. - * @property {number} length - * @property {string} chunkType - * @property {ByteStream} chunkStream Do not read more than length! - * @property {number} crc - */ - -export class PngParser extends EventTarget { - /** - * @type {ByteStream} - * @private - */ - bstream; - - /** - * @type {PngColorType} - * @private - */ - colorType; - - /** - * @type {PngPalette} - * @private - */ - palette; - - /** @param {ArrayBuffer} ab */ - constructor(ab) { - super(); - this.bstream = new ByteStream(ab); - this.bstream.setBigEndian(); - } - - /** - * Type-safe way to bind a listener for a PngBackgroundColor. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onBackgroundColor(listener) { - super.addEventListener(PngParseEventType.bKGD, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a PngChromaticities. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onChromaticities(listener) { - super.addEventListener(PngParseEventType.cHRM, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a PngCompressedTextualData. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onCompressedTextualData(listener) { - super.addEventListener(PngParseEventType.zTXt, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a PngExifProfile. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onExifProfile(listener) { - super.addEventListener(PngParseEventType.eXIf, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a PngImageGamma. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onGamma(listener) { - super.addEventListener(PngParseEventType.gAMA, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a PngHistogram. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onHistogram(listener) { - super.addEventListener(PngParseEventType.hIST, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a PngImageData. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onImageData(listener) { - super.addEventListener(PngParseEventType.IDAT, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a PngImageHeader. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onImageHeader(listener) { - super.addEventListener(PngParseEventType.IHDR, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a PngIntlTextualData. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onIntlTextualData(listener) { - super.addEventListener(PngParseEventType.iTXt, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a PngLastModTime. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onLastModTime(listener) { - super.addEventListener(PngParseEventType.tIME, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a PngPalette. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onPalette(listener) { - super.addEventListener(PngParseEventType.PLTE, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a PngPhysicalPixelDimensions. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onPhysicalPixelDimensions(listener) { - super.addEventListener(PngParseEventType.pHYs, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a PngSignificantBits. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onSignificantBits(listener) { - super.addEventListener(PngParseEventType.sBIT, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a PngSuggestedPalette. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onSuggestedPalette(listener) { - super.addEventListener(PngParseEventType.sPLT, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a PngTextualData. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onTextualData(listener) { - super.addEventListener(PngParseEventType.tEXt, listener); - return this; - } - - /** - * Type-safe way to bind a listener for a PngTransparency. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onTransparency(listener) { - super.addEventListener(PngParseEventType.tRNS, listener); - return this; - } - - /** @returns {Promise} A Promise that resolves when the parsing is complete. */ - async start() { - const sigLength = SIG.byteLength; - const sig = this.bstream.readBytes(sigLength); - for (let sb = 0; sb < sigLength; ++sb) { - if (sig[sb] !== SIG[sb]) throw `Bad PNG signature: ${sig}`; - } - - /** @type {PngChunk} */ - let chunk; - do { - const length = this.bstream.readNumber(4); - chunk = { - length, - chunkType: this.bstream.readString(4), - chunkStream: this.bstream.tee(), - crc: this.bstream.skip(length).readNumber(4), - }; - - const chStream = chunk.chunkStream; - switch (chunk.chunkType) { - // https://www.w3.org/TR/png-3/#11IHDR - case 'IHDR': - if (this.colorType) throw `Found multiple IHDR chunks`; - /** @type {PngImageHeader} */ - const header = { - width: chStream.readNumber(4), - height: chStream.readNumber(4), - bitDepth: chStream.readNumber(1), - colorType: chStream.readNumber(1), - compressionMethod: chStream.readNumber(1), - filterMethod: chStream.readNumber(1), - interlaceMethod: chStream.readNumber(1), - }; - if (!Object.values(PngColorType).includes(header.colorType)) { - throw `Bad PNG color type: ${header.colorType}`; - } - if (header.compressionMethod !== 0) { - throw `Bad PNG compression method: ${header.compressionMethod}`; - } - if (header.filterMethod !== 0) { - throw `Bad PNG filter method: ${header.filterMethod}`; - } - if (!Object.values(PngInterlaceMethod).includes(header.interlaceMethod)) { - throw `Bad PNG interlace method: ${header.interlaceMethod}`; - } - - this.colorType = header.colorType; - - this.dispatchEvent(createEvent(PngParseEventType.IHDR, header)); - break; - - // https://www.w3.org/TR/png-3/#11gAMA - case 'gAMA': - if (length !== 4) throw `Bad length for gAMA: ${length}`; - this.dispatchEvent(createEvent(PngParseEventType.gAMA, chStream.readNumber(4))); - break; - - // https://www.w3.org/TR/png-3/#11bKGD - case 'bKGD': - if (this.colorType === undefined) throw `bKGD before IHDR`; - if (this.colorType === PngColorType.INDEXED_COLOR && !this.palette) throw `bKGD before PLTE`; - /** @type {PngBackgroundColor} */ - const bkgdColor = {}; - - if (this.colorType === PngColorType.GREYSCALE || - this.colorType === PngColorType.GREYSCALE_WITH_ALPHA) { - bkgdColor.greyscale = chStream.readNumber(2); - } else if (this.colorType === PngColorType.TRUE_COLOR || - this.colorType === PngColorType.TRUE_COLOR_WITH_ALPHA) { - bkgdColor.red = chStream.readNumber(2); - bkgdColor.green = chStream.readNumber(2); - bkgdColor.blue = chStream.readNumber(2); - } else if (this.colorType === PngColorType.INDEXED_COLOR) { - bkgdColor.paletteIndex = chStream.readNumber(1); - } - - this.dispatchEvent(createEvent(PngParseEventType.bKGD, bkgdColor)); - break; - - // https://www.w3.org/TR/png-3/#11sBIT - case 'sBIT': - if (this.colorType === undefined) throw `sBIT before IHDR`; - /** @type {PngSignificantBits} */ - const sigBits = {}; - - const sbitBadLengthErr = `Weird sBIT length for color type ${this.colorType}: ${length}`; - if (this.colorType === PngColorType.GREYSCALE) { - if (length !== 1) throw sbitBadLengthErr; - sigBits.significant_greyscale = chStream.readNumber(1); - } else if (this.colorType === PngColorType.TRUE_COLOR || - this.colorType === PngColorType.INDEXED_COLOR) { - if (length !== 3) throw sbitBadLengthErr; - sigBits.significant_red = chStream.readNumber(1); - sigBits.significant_green = chStream.readNumber(1); - sigBits.significant_blue = chStream.readNumber(1); - } else if (this.colorType === PngColorType.GREYSCALE_WITH_ALPHA) { - if (length !== 2) throw sbitBadLengthErr; - sigBits.significant_greyscale = chStream.readNumber(1); - sigBits.significant_alpha = chStream.readNumber(1); - } else if (this.colorType === PngColorType.TRUE_COLOR_WITH_ALPHA) { - if (length !== 4) throw sbitBadLengthErr; - sigBits.significant_red = chStream.readNumber(1); - sigBits.significant_green = chStream.readNumber(1); - sigBits.significant_blue = chStream.readNumber(1); - sigBits.significant_alpha = chStream.readNumber(1); - } - - this.dispatchEvent(createEvent(PngParseEventType.sBIT, sigBits)); - break; - - // https://www.w3.org/TR/png-3/#11cHRM - case 'cHRM': - if (length !== 32) throw `Weird length for cHRM chunk: ${length}`; - - /** @type {PngChromaticities} */ - const chromaticities = { - whitePointX: chStream.readNumber(4), - whitePointY: chStream.readNumber(4), - redX: chStream.readNumber(4), - redY: chStream.readNumber(4), - greenX: chStream.readNumber(4), - greenY: chStream.readNumber(4), - blueX: chStream.readNumber(4), - blueY: chStream.readNumber(4), - }; - this.dispatchEvent(createEvent(PngParseEventType.cHRM, chromaticities)); - break; - - // https://www.w3.org/TR/png-3/#11PLTE - case 'PLTE': - if (this.colorType === undefined) throw `PLTE before IHDR`; - if (this.colorType === PngColorType.GREYSCALE || - this.colorType === PngColorType.GREYSCALE_WITH_ALPHA) throw `PLTE with greyscale`; - if (length % 3 !== 0) throw `PLTE length was not divisible by 3`; - - /** @type {PngColor[]} */ - const paletteEntries = []; - for (let p = 0; p < length / 3; ++p) { - paletteEntries.push({ - red: chStream.readNumber(1), - green: chStream.readNumber(1), - blue: chStream.readNumber(1), - }); - } - - /** @type {PngPalette} */ - this.palette = { - entries: paletteEntries, - }; - - this.dispatchEvent(createEvent(PngParseEventType.PLTE, this.palette)); - break; - - // https://www.w3.org/TR/png-3/#11pHYs - case 'pHYs': - /** @type {physicalPixelDimensions} */ - const pixelDims = { - pixelPerUnitX: chStream.readNumber(4), - pixelPerUnitY: chStream.readNumber(4), - unitSpecifier: chStream.readNumber(1), - }; - if (!Object.values(PngUnitSpecifier).includes(pixelDims.unitSpecifier)) { - throw `Bad pHYs unit specifier: ${pixelDims.unitSpecifier}`; - } - - this.dispatchEvent(createEvent(PngParseEventType.pHYs, pixelDims)); - break; - - // https://www.w3.org/TR/png-3/#11tEXt - case 'tEXt': - const byteArr = chStream.peekBytes(length); - const nullIndex = byteArr.indexOf(0); - /** @type {PngTextualData} */ - const textualData = { - keyword: chStream.readString(nullIndex), - textString: chStream.skip(1).readString(length - nullIndex - 1), - }; - this.dispatchEvent(createEvent(PngParseEventType.tEXt, textualData)); - break; - - // https://www.w3.org/TR/png-3/#11tIME - case 'tIME': - /** @type {PngLastModTime} */ - const lastModTime = { - year: chStream.readNumber(2), - month: chStream.readNumber(1), - day: chStream.readNumber(1), - hour: chStream.readNumber(1), - minute: chStream.readNumber(1), - second: chStream.readNumber(1), - }; - this.dispatchEvent(createEvent(PngParseEventType.tIME, lastModTime)); - break; - - // https://www.w3.org/TR/png-3/#11tRNS - case 'tRNS': - if (this.colorType === undefined) throw `tRNS before IHDR`; - if (this.colorType === PngColorType.GREYSCALE_WITH_ALPHA || - this.colorType === PngColorType.TRUE_COLOR_WITH_ALPHA) { - throw `tRNS with color type ${this.colorType}`; - } - - /** @type {PngTransparency} */ - const transparency = {}; - - const trnsBadLengthErr = `Weird sBIT length for color type ${this.colorType}: ${length}`; - if (this.colorType === PngColorType.GREYSCALE) { - if (length !== 2) throw trnsBadLengthErr; - transparency.greySampleValue = chStream.readNumber(2); - } else if (this.colorType === PngColorType.TRUE_COLOR) { - if (length !== 6) throw trnsBadLengthErr; - // Oddly the order is RBG instead of RGB :-/ - transparency.redSampleValue = chStream.readNumber(2); - transparency.blueSampleValue = chStream.readNumber(2); - transparency.greenSampleValue = chStream.readNumber(2); - } else if (this.colorType === PngColorType.INDEXED_COLOR) { - if (!this.palette) throw `tRNS before PLTE`; - if (length > this.palette.entries.length) throw `More tRNS entries than palette`; - - transparency.alphaPalette = []; - for (let a = 0; a < length; ++a) { - transparency.alphaPalette.push(chStream.readNumber(1)); - } - } - - this.dispatchEvent(createEvent(PngParseEventType.tRNS, transparency)); - break; - - // https://www.w3.org/TR/png-3/#11zTXt - case 'zTXt': - const compressedByteArr = chStream.peekBytes(length); - const compressedNullIndex = compressedByteArr.indexOf(0); - - /** @type {PngCompressedTextualData} */ - const compressedTextualData = { - keyword: chStream.readString(compressedNullIndex), - compressionMethod: chStream.skip(1).readNumber(1), - compressedText: chStream.readBytes(length - compressedNullIndex - 2), - }; - this.dispatchEvent(createEvent(PngParseEventType.zTXt, compressedTextualData)); - break; - - // https://www.w3.org/TR/png-3/#11iTXt - case 'iTXt': - const intlByteArr = chStream.peekBytes(length); - const intlNull0 = intlByteArr.indexOf(0); - const intlNull1 = intlByteArr.indexOf(0, intlNull0 + 1); - const intlNull2 = intlByteArr.indexOf(0, intlNull1 + 1); - if (intlNull0 === -1) throw `iTXt: Did not have one null`; - if (intlNull1 === -1) throw `iTXt: Did not have two nulls`; - if (intlNull2 === -1) throw `iTXt: Did not have three nulls`; - - /** @type {PngIntlTextualData} */ - const intlTextData = { - keyword: chStream.readString(intlNull0), - compressionFlag: chStream.skip(1).readNumber(1), - compressionMethod: chStream.readNumber(1), - languageTag: (intlNull1 - intlNull0 > 1) ? chStream.readString(intlNull1 - intlNull0 - 1) : undefined, - translatedKeyword: (intlNull2 - intlNull1 > 1) ? chStream.skip(1).readString(intlNull2 - intlNull1 - 1) : undefined, - text: chStream.skip(1).readBytes(length - intlNull2 - 1), - }; - - this.dispatchEvent(createEvent(PngParseEventType.iTXt, intlTextData)); - break; - - // https://www.w3.org/TR/png-3/#eXIf - case 'eXIf': - const exifValueMap = getExifProfile(chStream); - this.dispatchEvent(createEvent(PngParseEventType.eXIf, exifValueMap)); - break; - - // https://www.w3.org/TR/png-3/#11hIST - case 'hIST': - if (!this.palette) throw `hIST before PLTE`; - if (length !== this.palette.entries.length * 2) throw `Bad # of hIST frequencies: ${length / 2}`; - - /** @type {PngHistogram} */ - const hist = { frequencies: [] }; - for (let f = 0; f < this.palette.entries.length; ++f) { - hist.frequencies.push(chStream.readNumber(2)); - } - - this.dispatchEvent(createEvent(PngParseEventType.hIST, hist)); - break; - - // https://www.w3.org/TR/png-3/#11sPLT - case 'sPLT': - const spByteArr = chStream.peekBytes(length); - const spNameNullIndex = spByteArr.indexOf(0); - - /** @type {PngSuggestedPalette} */ - const sPalette = { - paletteName: chStream.readString(spNameNullIndex), - sampleDepth: chStream.skip(1).readNumber(1), - entries: [], - }; - - const sampleDepth = sPalette.sampleDepth; - if (![8, 16].includes(sampleDepth)) throw `Invalid sPLT sample depth: ${sampleDepth}`; - - const remainingByteLength = length - spNameNullIndex - 1 - 1; - const compByteLength = sPalette.sampleDepth === 8 ? 1 : 2; - const entryByteLength = 4 * compByteLength + 2; - if (remainingByteLength % entryByteLength !== 0) { - throw `Invalid # of bytes left in sPLT: ${remainingByteLength}`; - } - - const numEntries = remainingByteLength / entryByteLength; - for (let e = 0; e < numEntries; ++e) { - sPalette.entries.push({ - red: chStream.readNumber(compByteLength), - green: chStream.readNumber(compByteLength), - blue: chStream.readNumber(compByteLength), - alpha: chStream.readNumber(compByteLength), - frequency: chStream.readNumber(2), - }); - } - - this.dispatchEvent(createEvent(PngParseEventType.sPLT, sPalette)); - break; - - // https://www.w3.org/TR/png-3/#11IDAT - case 'IDAT': - /** @type {PngImageData} */ - const data = { - rawImageData: chStream.readBytes(chunk.length), - }; - this.dispatchEvent(createEvent(PngParseEventType.IDAT, data)); - break; - - case 'IEND': - break; - - default: - if (DEBUG) console.log(`Found an unhandled chunk: ${chunk.chunkType}`); - break; - } - } while (chunk.chunkType !== 'IEND'); - } -} diff --git a/image/webp-shim/webp-shim-module.js b/image/webp-shim/webp-shim-module.js deleted file mode 100644 index aa82b5b..0000000 --- a/image/webp-shim/webp-shim-module.js +++ /dev/null @@ -1 +0,0 @@ -var Module=typeof Module!=="undefined"?Module:{};var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);if(typeof module!=="undefined"){module["exports"]=Module}process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",abort);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){return read(f)}}readBinary=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(document.currentScript){scriptDirectory=document.currentScript.src}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime;if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(typeof WebAssembly!=="object"){err("no native wasm support detected")}var wasmMemory;var wasmTable=new WebAssembly.Table({"initial":163,"maximum":163+0,"element":"anyfunc"});var ABORT=false;var EXITSTATUS=0;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}function getCFunc(ident){var func=Module["_"+ident];assert(func,"Cannot call unknown function "+ident+", make sure it is exported");return func}function ccall(ident,returnType,argTypes,args,opts){var toC={"string":function(str){var ret=0;if(str!==null&&str!==undefined&&str!==0){var len=(str.length<<2)+1;ret=stackAlloc(len);stringToUTF8(str,ret,len)}return ret},"array":function(arr){var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string")return UTF8ToString(ret);if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i=endIdx))++endPtr;if(endPtr-idx>16&&u8Array.subarray&&UTF8Decoder){return UTF8Decoder.decode(u8Array.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,outU8Array,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;outU8Array[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;outU8Array[outIdx++]=192|u>>6;outU8Array[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;outU8Array[outIdx++]=224|u>>12;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;outU8Array[outIdx++]=240|u>>18;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}}outU8Array[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function writeArrayToMemory(array,buffer){HEAP8.set(array,buffer)}var WASM_PAGE_SIZE=65536;function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var DYNAMIC_BASE=5269056,DYNAMICTOP_PTR=26016;var INITIAL_INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;if(Module["wasmMemory"]){wasmMemory=Module["wasmMemory"]}else{wasmMemory=new WebAssembly.Memory({"initial":INITIAL_INITIAL_MEMORY/WASM_PAGE_SIZE})}if(wasmMemory){buffer=wasmMemory.buffer}INITIAL_INITIAL_MEMORY=buffer.byteLength;updateGlobalBufferAndViews(buffer);HEAP32[DYNAMICTOP_PTR>>2]=DYNAMIC_BASE;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback();continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){Module["dynCall_v"](func)}else{Module["dynCall_vi"](func,callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";out(what);err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";throw new WebAssembly.RuntimeError(what)}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return String.prototype.startsWith?filename.startsWith(dataURIPrefix):filename.indexOf(dataURIPrefix)===0}var wasmBinaryFile="webp-shim-module.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary()})}return new Promise(function(resolve,reject){resolve(getBinary())})}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&typeof fetch==="function"){fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync();return{}}__ATINIT__.push({func:function(){___wasm_call_ctors()}});function ___assert_fail(condition,filename,line,func){abort("Assertion failed: "+UTF8ToString(condition)+", at: "+[filename?UTF8ToString(filename):"unknown filename",line,func?UTF8ToString(func):"unknown function"])}function _emscripten_get_heap_size(){return HEAPU8.length}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){var oldSize=_emscripten_get_heap_size();var PAGE_MULTIPLE=65536;var maxHeapSize=2147483648-PAGE_MULTIPLE;if(requestedSize>maxHeapSize){return false}var minHeapSize=16777216;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(minHeapSize,requestedSize,overGrownHeapSize),PAGE_MULTIPLE));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}var PATH={splitPath:function(filename){var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:function(parts,allowAboveRoot){var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:function(path){var isAbsolute=path.charAt(0)==="/",trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:function(path){var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:function(path){if(path==="/")return"/";var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},extname:function(path){return PATH.splitPath(path)[3]},join:function(){var paths=Array.prototype.slice.call(arguments,0);return PATH.normalize(paths.join("/"))},join2:function(l,r){return PATH.normalize(l+"/"+r)}};var SYSCALLS={mappings:{},buffers:[null,[],[]],printChar:function(stream,curr){var buffer=SYSCALLS.buffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer,0));buffer.length=0}else{buffer.push(curr)}},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},get64:function(low,high){return low}};function _fd_close(fd){return 0}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){}function _fd_write(fd,iov,iovcnt,pnum){var num=0;for(var i=0;i>2];var len=HEAP32[iov+(i*8+4)>>2];for(var j=0;j>2]=num;return 0}var asmLibraryArg={"a":___assert_fail,"d":_emscripten_memcpy_big,"e":_emscripten_resize_heap,"f":_fd_close,"c":_fd_seek,"b":_fd_write,"memory":wasmMemory,"table":wasmTable};var asm=createWasm();Module["asm"]=asm;var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["g"]).apply(null,arguments)};var _create_buffer=Module["_create_buffer"]=function(){return(_create_buffer=Module["_create_buffer"]=Module["asm"]["h"]).apply(null,arguments)};var _destroy_buffer=Module["_destroy_buffer"]=function(){return(_destroy_buffer=Module["_destroy_buffer"]=Module["asm"]["i"]).apply(null,arguments)};var _get_image_bytes_from_handle=Module["_get_image_bytes_from_handle"]=function(){return(_get_image_bytes_from_handle=Module["_get_image_bytes_from_handle"]=Module["asm"]["j"]).apply(null,arguments)};var _get_num_bytes_from_handle=Module["_get_num_bytes_from_handle"]=function(){return(_get_num_bytes_from_handle=Module["_get_num_bytes_from_handle"]=Module["asm"]["k"]).apply(null,arguments)};var _release_image_handle=Module["_release_image_handle"]=function(){return(_release_image_handle=Module["_release_image_handle"]=Module["asm"]["l"]).apply(null,arguments)};var _get_png_handle_from_webp=Module["_get_png_handle_from_webp"]=function(){return(_get_png_handle_from_webp=Module["_get_png_handle_from_webp"]=Module["asm"]["m"]).apply(null,arguments)};var _get_jpg_handle_from_webp=Module["_get_jpg_handle_from_webp"]=function(){return(_get_jpg_handle_from_webp=Module["_get_jpg_handle_from_webp"]=Module["asm"]["n"]).apply(null,arguments)};var stackSave=Module["stackSave"]=function(){return(stackSave=Module["stackSave"]=Module["asm"]["o"]).apply(null,arguments)};var stackAlloc=Module["stackAlloc"]=function(){return(stackAlloc=Module["stackAlloc"]=Module["asm"]["p"]).apply(null,arguments)};var stackRestore=Module["stackRestore"]=function(){return(stackRestore=Module["stackRestore"]=Module["asm"]["q"]).apply(null,arguments)};var dynCall_vi=Module["dynCall_vi"]=function(){return(dynCall_vi=Module["dynCall_vi"]=Module["asm"]["r"]).apply(null,arguments)};Module["asm"]=asm;Module["cwrap"]=cwrap;var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0)return;function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}noExitRuntime=true;run(); diff --git a/image/webp-shim/webp-shim-module.wasm b/image/webp-shim/webp-shim-module.wasm deleted file mode 100644 index 087d92d..0000000 Binary files a/image/webp-shim/webp-shim-module.wasm and /dev/null differ diff --git a/image/webp-shim/webp-shim.js b/image/webp-shim/webp-shim.js deleted file mode 100644 index 0a3122a..0000000 --- a/image/webp-shim/webp-shim.js +++ /dev/null @@ -1,107 +0,0 @@ -/** - * webp-shim.js - * - * Licensed under the MIT License - * - * Copyright(c) 2020 Google Inc. - */ - -// TODO(2.0): Remove this. It seems unnecessary given WebP is universally supported now. - -const url = import.meta.url; -if (!url.endsWith('/webp-shim.js')) { - throw 'webp-shim must be loaded as webp-shim.js'; -} -const thisModulePath = url.substring(0, url.indexOf('/webp-shim.js')); - -let loadingPromise = undefined; -let api = undefined; - -/** - * @returns {Promise} Returns the API object. - */ -function loadWebPShimApi() { - if (api) { return Promise.resolve(api); } - else if (loadingPromise) { return loadingPromise; } - return loadingPromise = new Promise((resolve, reject) => { - const scriptEl = document.createElement('script'); - scriptEl.onload = () => { - Module.print = str => console.log(`${Date.now()}: ${str}`); - Module.printErr = str => console.error(`${Date.now()}: ${str}`); - Module.onRuntimeInitialized = () => { - api = { - createWASMBuffer: Module.cwrap('create_buffer', 'number', ['number', 'number']), - destroyWASMBuffer: Module.cwrap('destroy_buffer', '', ['number']), - getJPGHandle: Module.cwrap('get_jpg_handle_from_webp', 'number', ['number', 'number']), - getPNGHandle: Module.cwrap('get_png_handle_from_webp', 'number', ['number', 'number']), - getImageBytesFromHandle: Module.cwrap('get_image_bytes_from_handle', 'number', ['number']), - getNumBytesFromHandle: Module.cwrap('get_num_bytes_from_handle', 'number', ['number']), - module: Module, - releaseImageHandle: Module.cwrap('release_image_handle', '', ['number']), - }; - resolve(api); - }; - }; - scriptEl.onerror = err => reject(err); - scriptEl.src = `${thisModulePath}/webp-shim-module.js`; - document.body.appendChild(scriptEl); - }); -} - -/** - * @param {ArrayBuffer|Uint8Array} webpBuffer The byte array containing the WebP image bytes. - * @returns {Promise} A Promise resolving to a byte array containing the PNG bytes. - */ -export function convertWebPtoPNG(webpBuffer) { - return loadWebPShimApi().then((api) => { - // Create a buffer of the WebP bytes that we can send into WASM-land. - const webpArray = new Uint8Array(webpBuffer); - const size = webpArray.byteLength; - const webpWASMBuffer = api.createWASMBuffer(size); - api.module.HEAPU8.set(webpArray, webpWASMBuffer); - - // Convert to PNG. - const pngHandle = api.getPNGHandle(webpWASMBuffer, size); - if (!pngHandle) { - api.destroyWASMBuffer(webpWASMBuffer); - return null; - } - const numPNGBytes = api.getNumBytesFromHandle(pngHandle); - const pngBufPtr = api.getImageBytesFromHandle(pngHandle); - let pngBuffer = api.module.HEAPU8.slice(pngBufPtr, pngBufPtr + numPNGBytes - 1); - - // Cleanup. - api.releaseImageHandle(pngHandle); - api.destroyWASMBuffer(webpWASMBuffer); - return pngBuffer; - }); -} - -/** - * @param {ArrayBuffer|Uint8Array} webpBuffer The byte array containing the WebP image bytes. - * @returns {Promise} A Promise resolving to a byte array containing the JPG bytes. - */ -export function convertWebPtoJPG(webpBuffer) { - return loadWebPShimApi().then((api) => { - // Create a buffer of the WebP bytes that we can send into WASM-land. - const webpArray = new Uint8Array(webpBuffer); - const size = webpArray.byteLength; - const webpWASMBuffer = api.createWASMBuffer(size); - api.module.HEAPU8.set(webpArray, webpWASMBuffer); - - // Convert to JPG. - const jpgHandle = api.getJPGHandle(webpWASMBuffer, size); - if (!jpgHandle) { - api.destroyWASMBuffer(webpWASMBuffer); - return null; - } - const numJPGBytes = api.getNumBytesFromHandle(jpgHandle); - const jpgBufPtr = api.getImageBytesFromHandle(jpgHandle); - const jpgBuffer = api.module.HEAPU8.slice(jpgBufPtr, jpgBufPtr + numJPGBytes - 1); - - // Cleanup. - api.releaseImageHandle(jpgHandle); - api.destroyWASMBuffer(webpWASMBuffer); - return jpgBuffer; - }); -} diff --git a/index.js b/index.js deleted file mode 100644 index 37fff75..0000000 --- a/index.js +++ /dev/null @@ -1,65 +0,0 @@ -/** - * index.js - * - * Licensed under the MIT License - * - * Copyright(c) 2020 Google Inc. - */ - -/** @typedef {import('./codecs/codecs.js').ProbeStream} ProbeStream */ -/** @typedef {import('./codecs/codecs.js').ProbeFormat} ProbeFormat */ -/** @typedef {import('./codecs/codecs.js').ProbeInfo} ProbeInfo */ - -/** @typedef {import('./image/parsers/gif.js').GifApplicationExtension} GifApplicationExtension */ -/** @typedef {import('./image/parsers/gif.js').GifColor} GifColor */ -/** @typedef {import('./image/parsers/gif.js').GifCommentExtension} GifCommentExtension */ -/** @typedef {import('./image/parsers/gif.js').GifGraphicControlExtension} GifGraphicControlExtension */ -/** @typedef {import('./image/parsers/gif.js').GifHeader} GifHeader */ -/** @typedef {import('./image/parsers/gif.js').GifLogicalScreen} GifLogicalScreen */ -/** @typedef {import('./image/parsers/gif.js').GifPlainTextExtension} GifPlainTextExtension */ -/** @typedef {import('./image/parsers/gif.js').GifTableBasedImage} GifTableBasedImage */ - -/** @typedef {import('./image/parsers/jpeg.js').JpegApp0Extension} JpegApp0Extension */ -/** @typedef {import('./image/parsers/jpeg.js').JpegApp0Marker} JpegApp0Marker */ -/** @typedef {import('./image/parsers/jpeg.js').JpegComponentDetail} JpegComponentDetail */ -/** @typedef {import('./image/parsers/jpeg.js').JpegDefineHuffmanTable} JpegDefineHuffmanTable */ -/** @typedef {import('./image/parsers/jpeg.js').JpegDefineQuantizationTable} JpegDefineQuantizationTable */ -/** @typedef {import('./image/parsers/jpeg.js').JpegStartOfFrame} JpegStartOfFrame */ -/** @typedef {import('./image/parsers/jpeg.js').JpegStartOfScan} JpegStartOfScan */ - -/** @typedef {import('./image/parsers/png.js').PngBackgroundColor} PngBackgroundColor */ -/** @typedef {import('./image/parsers/png.js').PngChromaticities} PngChromaticies */ -/** @typedef {import('./image/parsers/png.js').PngColor} PngColor */ -/** @typedef {import('./image/parsers/png.js').PngCompressedTextualData} PngCompressedTextualData */ -/** @typedef {import('./image/parsers/png.js').PngHistogram} PngHistogram */ -/** @typedef {import('./image/parsers/png.js').PngImageData} PngImageData */ -/** @typedef {import('./image/parsers/png.js').PngImageGamma} PngImageGamma */ -/** @typedef {import('./image/parsers/png.js').PngImageHeader} PngImageHeader */ -/** @typedef {import('./image/parsers/png.js').PngIntlTextualData} PngIntlTextualData */ -/** @typedef {import('./image/parsers/png.js').PngLastModTime} PngLastModTime */ -/** @typedef {import('./image/parsers/png.js').PngPalette} PngPalette */ -/** @typedef {import('./image/parsers/png.js').PngPhysicalPixelDimensions} PngPhysicalPixelDimensions */ -/** @typedef {import('./image/parsers/png.js').PngSignificantBits} PngSignificantBits */ -/** @typedef {import('./image/parsers/png.js').PngSuggestedPalette} PngSuggestedPalette */ -/** @typedef {import('./image/parsers/png.js').PngSuggestedPaletteEntry} PngSuggestedPaletteEntry */ -/** @typedef {import('./image/parsers/png.js').PngTextualData} PngTextualData */ -/** @typedef {import('./image/parsers/png.js').PngTransparency} PngTransparency */ - -export { - UnarchiveEvent, UnarchiveEventType, UnarchiveInfoEvent, UnarchiveErrorEvent, - UnarchiveStartEvent, UnarchiveFinishEvent, UnarchiveProgressEvent, UnarchiveExtractEvent, - Unarchiver, Unzipper, Unrarrer, Untarrer, getUnarchiver -} from './archive/decompress.js'; -export { getFullMIMEString, getShortMIMEString } from './codecs/codecs.js'; -export { findMimeType } from './file/sniffer.js'; -export { GifParseEventType, GifParser } from './image/parsers/gif.js'; -export { JpegComponentType, JpegDctType, JpegDensityUnits, JpegExtensionThumbnailFormat, - JpegHuffmanTableType, JpegParseEventType, JpegParser, - JpegSegmentType } from './image/parsers/jpeg.js'; -export { PngColorType, PngInterlaceMethod, PngParseEventType, PngParser, - PngUnitSpecifier } from './image/parsers/png.js'; -export { convertWebPtoPNG, convertWebPtoJPG } from './image/webp-shim/webp-shim.js'; -export { BitBuffer } from './io/bitbuffer.js'; -export { BitStream } from './io/bitstream.js'; -export { ByteBuffer } from './io/bytebuffer.js'; -export { ByteStream } from './io/bytestream.js'; diff --git a/io/bitbuffer.js b/io/bitbuffer.js deleted file mode 100644 index d94f64b..0000000 --- a/io/bitbuffer.js +++ /dev/null @@ -1,199 +0,0 @@ -/* - * bitbuffer.js - * - * Provides writer for bits. - * - * Licensed under the MIT License - * - * Copyright(c) 2023 Google Inc. - * Copyright(c) 2011 antimatter15 - */ - -const BITMASK = [ - 0, - 0b00000001, - 0b00000011, - 0b00000111, - 0b00001111, - 0b00011111, - 0b00111111, - 0b01111111, - 0b11111111, -] - -/** - * A write-only Bit buffer which uses a Uint8Array as a backing store. - */ -export class BitBuffer { - /** - * @param {number} numBytes The number of bytes to allocate. - * @param {boolean} mtl The bit-packing mode. True means pack bits from most-significant (7) to - * least-significant (0). Defaults false: least-significant (0) to most-significant (8). - */ - constructor(numBytes, mtl = false) { - if (typeof numBytes != typeof 1 || numBytes <= 0) { - throw "Error! BitBuffer initialized with '" + numBytes + "'"; - } - - /** - * @type {Uint8Array} - * @public - */ - this.data = new Uint8Array(numBytes); - - /** - * Whether we pack bits from most-significant-bit to least. Defaults false (least-to-most - * significant bit packing). - * @type {boolean} - * @private - */ - this.mtl = mtl; - - /** - * The current byte we are filling with bits. - * @type {number} - * @public - */ - this.bytePtr = 0; - - /** - * Points at the bit within the current byte where the next bit will go. This number ranges - * from 0 to 7 and the direction of packing is indicated by the mtl property. - * @type {number} - * @public - */ - this.bitPtr = this.mtl ? 7 : 0; - } - - // TODO: Be consistent with naming across classes (big-endian and little-endian). - - /** @returns {boolean} */ - getPackingDirection() { - return this.mtl; - } - - /** - * Sets the bit-packing direction. Default (false) is least-significant-bit (0) to - * most-significant (7). Changing the bit-packing direction when the bit pointer is in the - * middle of a byte will fill the rest of that byte with 0s using the current bit-packing - * direction and then set the bit pointer to the appropriate bit of the next byte. If there - * are no more bytes left in this buffer, it will throw an error. - */ - setPackingDirection(mtl = false) { - if (this.mtl !== mtl) { - if (this.mtl && this.bitPtr !== 7) { - this.bytePtr++; - if (this.bytePtr >= this.data.byteLength) { - throw `No more bytes left when switching packing direction`; - } - this.bitPtr = 0; - } else if (!this.mtl && this.bitPtr !== 0) { - this.bytePtr++; - if (this.bytePtr >= this.data.byteLength) { - throw `No more bytes left when switching packing direction`; - } - this.bitPtr = 7; - } - } - - this.mtl = mtl; - } - - /** - * writeBits(3, 6) is the same as writeBits(0b000011, 6). - * Will throw an error (without writing) if this would over-flow the buffer. - * @param {number} val The bits to pack into the buffer. Negative values are not allowed. - * @param {number} numBits Must be positive, non-zero and less or equal to than 53, since - * JavaScript can only support 53-bit integers. - */ - writeBits(val, numBits) { - if (val < 0 || typeof val !== typeof 1) { - throw `Trying to write an invalid value into the BitBuffer: ${val}`; - } - if (numBits < 0 || numBits > 53) { - throw `Trying to write ${numBits} bits into the BitBuffer`; - } - - const totalBitsInBuffer = this.data.byteLength * 8; - const writtenBits = this.bytePtr * 8 + this.bitPtr; - const bitsLeftInBuffer = totalBitsInBuffer - writtenBits; - if (numBits > bitsLeftInBuffer) { - throw `Trying to write ${numBits} into the BitBuffer that only has ${bitsLeftInBuffer}`; - } - - // Least-to-most-significant bit packing method (LTM). - if (!this.mtl) { - let numBitsLeftToWrite = numBits; - while (numBitsLeftToWrite > 0) { - /** The number of bits available to fill in this byte. */ - const bitCapacityInThisByte = 8 - this.bitPtr; - /** The number of bits of val we will write into this byte. */ - const numBitsToWriteIntoThisByte = Math.min(numBitsLeftToWrite, bitCapacityInThisByte); - /** The number of bits that fit in subsequent bytes. */ - const numExcessBits = numBitsLeftToWrite - numBitsToWriteIntoThisByte; - if (numExcessBits < 0) { - throw `Error in LTM bit packing, # of excess bits is negative`; - } - /** The actual bits that need to be written into this byte. Starts at LSB. */ - let actualBitsToWrite = (val & BITMASK[numBitsToWriteIntoThisByte]); - // Only adjust and write bits if any are set to 1. - if (actualBitsToWrite > 0) { - actualBitsToWrite <<= this.bitPtr; - // Now write into the buffer. - this.data[this.bytePtr] |= actualBitsToWrite; - } - // Update the bit/byte pointers and remaining bits to write. - this.bitPtr += numBitsToWriteIntoThisByte; - if (this.bitPtr > 7) { - if (this.bitPtr !== 8) { - throw `Error in LTM bit packing. Tried to write more bits than it should have.`; - } - this.bytePtr++; - this.bitPtr = 0; - } - // Remove bits that have been written from LSB end. - val >>= numBitsToWriteIntoThisByte; - numBitsLeftToWrite -= numBitsToWriteIntoThisByte; - } - } - // Most-to-least-significant bit packing method (MTL). - else { - let numBitsLeftToWrite = numBits; - while (numBitsLeftToWrite > 0) { - /** The number of bits available to fill in this byte. */ - const bitCapacityInThisByte = this.bitPtr + 1; - /** The number of bits of val we will write into this byte. */ - const numBitsToWriteIntoThisByte = Math.min(numBitsLeftToWrite, bitCapacityInThisByte); - /** The number of bits that fit in subsequent bytes. */ - const numExcessBits = numBitsLeftToWrite - numBitsToWriteIntoThisByte; - if (numExcessBits < 0) { - throw `Error in MTL bit packing, # of excess bits is negative`; - } - /** The actual bits that need to be written into this byte. Starts at MSB. */ - let actualBitsToWrite = ((val >> numExcessBits) & BITMASK[numBitsToWriteIntoThisByte]); - // Only adjust and write bits if any are set to 1. - if (actualBitsToWrite > 0) { - // If the number of bits left to write do not fill up this byte, we need to shift these - // bits to the left so they are written into the proper place in the buffer. - if (numBitsLeftToWrite < bitCapacityInThisByte) { - actualBitsToWrite <<= (bitCapacityInThisByte - numBitsLeftToWrite); - } - // Now write into the buffer. - this.data[this.bytePtr] |= actualBitsToWrite; - } - // Update the bit/byte pointers and remaining bits to write - this.bitPtr -= numBitsToWriteIntoThisByte; - if (this.bitPtr < 0) { - if (this.bitPtr !== -1) { - throw `Error in MTL bit packing. Tried to write more bits than it should have.`; - } - this.bytePtr++; - this.bitPtr = 7; - } - // Remove bits that have been written from MSB end. - val -= (actualBitsToWrite << numExcessBits); - numBitsLeftToWrite -= numBitsToWriteIntoThisByte; - } - } - } -} diff --git a/io/bitstream.js b/io/bitstream.js deleted file mode 100644 index 22906fd..0000000 --- a/io/bitstream.js +++ /dev/null @@ -1,338 +0,0 @@ -/* - * bitstream.js - * - * A pull stream for binary bits. - * - * Licensed under the MIT License - * - * Copyright(c) 2023 Google Inc. - * Copyright(c) 2011 antimatter15 - */ - -// mask for getting N number of bits (0-8) -const BITMASK = [0, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF]; - -/** - * This object allows you to peek and consume bits and bytes out of a stream. - * Note that this stream is optimized, and thus, will *NOT* throw an error if - * the end of the stream is reached. Only use this in scenarios where you - * already have all the bits you need. - * - * Bit reading always proceeds from the first byte in the buffer, to the - * second byte, and so on. The MTL flag controls which bit is considered - * first *inside* the byte. The default is least-to-most direction. - * - * An Example for how Most-To-Least vs Least-to-Most mode works: - * - * If you have an ArrayBuffer with the following two Uint8s: - * 185 (0b10111001) and 66 (0b01000010) - * and you perform a series of readBits: 2 bits, then 3, then 5, then 6. - * - * A BitStream in "mtl" mode will yield the following: - * - readBits(2) => 2 ('10') - * - readBits(3) => 7 ('111') - * - readBits(5) => 5 ('00101') - * - readBits(6) => 2 ('000010') - * - * A BitStream in "ltm" mode will yield the following: - * - readBits(2) => 1 ('01') - * - readBits(3) => 6 ('110') - * - readBits(5) => 21 ('10101') - * - readBits(6) => 16 ('010000') - */ -export class BitStream { - /** - * @param {ArrayBuffer} ab An ArrayBuffer object. - * @param {boolean} mtl Whether the stream reads bits from the byte starting with the - * most-significant-bit (bit 7) to least-significant (bit 0). False means the direction is - * from least-significant-bit (bit 0) to most-significant (bit 7). - * @param {Number} opt_offset The offset into the ArrayBuffer - * @param {Number} opt_length The length of this BitStream - */ - constructor(ab, mtl, opt_offset, opt_length) { - if (!(ab instanceof ArrayBuffer)) { - throw 'Error! BitStream constructed with an invalid ArrayBuffer object'; - } - - const offset = opt_offset || 0; - const length = opt_length || ab.byteLength; - - /** - * The bytes in the stream. - * @type {Uint8Array} - * @private - */ - this.bytes = new Uint8Array(ab, offset, length); - - /** - * The byte in the stream that we are currently on. - * @type {Number} - * @private - */ - this.bytePtr = 0; - - /** - * The bit in the current byte that we will read next (can have values 0 through 7). - * @type {Number} - * @private - */ - this.bitPtr = 0; // tracks which bit we are on (can have values 0 through 7) - - /** - * An ever-increasing number. - * @type {Number} - * @private - */ - this.bitsRead_ = 0; - - this.peekBits = mtl ? this.peekBits_mtl : this.peekBits_ltm; - } - - /** - * Returns how many bits have been read in the stream since the beginning of time. - * @returns {number} - */ - getNumBitsRead() { - return this.bitsRead_; - } - - /** - * Returns how many bits are currently in the stream left to be read. - * @returns {number} - */ - getNumBitsLeft() { - const bitsLeftInByte = 8 - this.bitPtr; - return (this.bytes.byteLength - this.bytePtr - 1) * 8 + bitsLeftInByte; - } - - /** - * byte0 byte1 byte2 byte3 - * 7......0 | 7......0 | 7......0 | 7......0 - * - * The bit pointer starts at least-significant bit (0) of byte0 and moves left until it reaches - * bit7 of byte0, then jumps to bit0 of byte1, etc. - * @param {number} n The number of bits to peek, must be a positive integer. - * @param {boolean=} movePointers Whether to move the pointer, defaults false. - * @returns {number} The peeked bits, as an unsigned number. - */ - peekBits_ltm(n, opt_movePointers) { - const NUM = parseInt(n, 10); - let num = NUM; - - // TODO: Handle this consistently between ByteStream and BitStream. ByteStream throws an error. - if (n !== num || num <= 0) { - return 0; - } - - const movePointers = opt_movePointers || false; - let bytes = this.bytes; - let bytePtr = this.bytePtr; - let bitPtr = this.bitPtr; - let result = 0; - let bitsIn = 0; - - // keep going until we have no more bits left to peek at - while (num > 0) { - // We overflowed the stream, so just return what we got. - if (bytePtr >= bytes.length) { - break; - } - - const numBitsLeftInThisByte = (8 - bitPtr); - if (num >= numBitsLeftInThisByte) { - const mask = (BITMASK[numBitsLeftInThisByte] << bitPtr); - result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); - - bytePtr++; - bitPtr = 0; - bitsIn += numBitsLeftInThisByte; - num -= numBitsLeftInThisByte; - } else { - const mask = (BITMASK[num] << bitPtr); - result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); - - bitPtr += num; - break; - } - } - - if (movePointers) { - this.bitPtr = bitPtr; - this.bytePtr = bytePtr; - this.bitsRead_ += NUM; - } - - return result; - } - - /** - * byte0 byte1 byte2 byte3 - * 7......0 | 7......0 | 7......0 | 7......0 - * - * The bit pointer starts at bit7 of byte0 and moves right until it reaches - * bit0 of byte0, then goes to bit7 of byte1, etc. - * @param {number} n The number of bits to peek. Must be a positive integer. - * @param {boolean=} movePointers Whether to move the pointer, defaults false. - * @returns {number} The peeked bits, as an unsigned number. - */ - peekBits_mtl(n, opt_movePointers) { - const NUM = parseInt(n, 10); - let num = NUM; - - // TODO: Handle this consistently between ByteStream and BitStream. ByteStream throws an error. - if (n !== num || num <= 0) { - return 0; - } - - const movePointers = opt_movePointers || false; - let bytes = this.bytes; - let bytePtr = this.bytePtr; - let bitPtr = this.bitPtr; - let result = 0; - - // keep going until we have no more bits left to peek at - while (num > 0) { - // We overflowed the stream, so just return the bits we got. - if (bytePtr >= bytes.length) { - break; - } - - const numBitsLeftInThisByte = (8 - bitPtr); - if (num >= numBitsLeftInThisByte) { - result <<= numBitsLeftInThisByte; - result |= (BITMASK[numBitsLeftInThisByte] & bytes[bytePtr]); - bytePtr++; - bitPtr = 0; - num -= numBitsLeftInThisByte; - } else { - result <<= num; - const numBits = 8 - num - bitPtr; - result |= ((bytes[bytePtr] & (BITMASK[num] << numBits)) >> numBits); - - bitPtr += num; - break; - } - } - - if (movePointers) { - this.bitPtr = bitPtr; - this.bytePtr = bytePtr; - this.bitsRead_ += NUM; - } - - return result; - } - - /** - * Peek at 16 bits from current position in the buffer. - * Bit at (bytePtr,bitPtr) has the highest position in returning data. - * Taken from getbits.hpp in unrar. - * TODO: Move this out of BitStream and into unrar. - * @returns {number} - */ - getBits() { - return (((((this.bytes[this.bytePtr] & 0xff) << 16) + - ((this.bytes[this.bytePtr + 1] & 0xff) << 8) + - ((this.bytes[this.bytePtr + 2] & 0xff))) >>> (8 - this.bitPtr)) & 0xffff); - } - - /** - * Reads n bits out of the stream, consuming them (moving the bit pointer). - * @param {number} n The number of bits to read. Must be a positive integer. - * @returns {number} The read bits, as an unsigned number. - */ - readBits(n) { - return this.peekBits(n, true); - } - - /** - * This returns n bytes as a sub-array, advancing the pointer if movePointers - * is true. Only use this for uncompressed blocks as this throws away remaining - * bits in the current byte. - * @param {number} n The number of bytes to peek. Must be a positive integer. - * @param {boolean=} movePointers Whether to move the pointer, defaults false. - * @returns {Uint8Array} The subarray. - */ - peekBytes(n, opt_movePointers) { - const num = parseInt(n, 10); - if (n !== num || num < 0) { - throw 'Error! Called peekBytes() with a non-positive integer: ' + n; - } else if (num === 0) { - return new Uint8Array(); - } - - // Flush bits until we are byte-aligned. - // from http://tools.ietf.org/html/rfc1951#page-11 - // "Any bits of input up to the next byte boundary are ignored." - while (this.bitPtr != 0) { - this.readBits(1); - } - - const numBytesLeft = this.getNumBitsLeft() / 8; - if (num > numBytesLeft) { - throw 'Error! Overflowed the bit stream! n=' + num + ', bytePtr=' + this.bytePtr + - ', bytes.length=' + this.bytes.length + ', bitPtr=' + this.bitPtr; - } - - const movePointers = opt_movePointers || false; - const result = new Uint8Array(num); - let bytes = this.bytes; - let ptr = this.bytePtr; - let bytesLeftToCopy = num; - while (bytesLeftToCopy > 0) { - const bytesLeftInStream = bytes.length - ptr; - const sourceLength = Math.min(bytesLeftToCopy, bytesLeftInStream); - - result.set(bytes.subarray(ptr, ptr + sourceLength), num - bytesLeftToCopy); - - ptr += sourceLength; - // Overflowed the stream, just return what we got. - if (ptr >= bytes.length) { - break; - } - - bytesLeftToCopy -= sourceLength; - } - - if (movePointers) { - this.bytePtr += num; - this.bitsRead_ += (num * 8); - } - - return result; - } - - /** - * @param {number} n The number of bytes to read. - * @returns {Uint8Array} The subarray. - */ - readBytes(n) { - return this.peekBytes(n, true); - } - - /** - * Skips n bits in the stream. Will throw an error if n is < 0 or greater than the number of - * bits left in the stream. - * @param {number} n The number of bits to skip. Must be a positive integer. - * @returns {BitStream} Returns this BitStream for chaining. - */ - skip(n) { - const num = parseInt(n, 10); - if (n !== num || num < 0) throw `Error! Called skip(${n})`; - else if (num === 0) return this; - - const totalBitsLeft = this.getNumBitsLeft(); - if (n > totalBitsLeft) { - throw `Error! Overflowed the bit stream for skip(${n}), ptrs=${this.bytePtr}/${this.bitPtr}`; - } - - this.bitsRead_ += num; - this.bitPtr += num; - if (this.bitPtr >= 8) { - this.bytePtr += Math.floor(this.bitPtr / 8); - this.bitPtr %= 8; - } - - return this; - } -} diff --git a/io/bytebuffer.js b/io/bytebuffer.js deleted file mode 100644 index e2a0078..0000000 --- a/io/bytebuffer.js +++ /dev/null @@ -1,143 +0,0 @@ -/* - * bytebuffer.js - * - * Provides a writer for bytes. - * - * Licensed under the MIT License - * - * Copyright(c) 2023 Google Inc. - * Copyright(c) 2011 antimatter15 - */ - -// TODO: Allow big-endian and little-endian, with consistent naming. - -/** - * A write-only Byte buffer which uses a Uint8 Typed Array as a backing store. - */ -export class ByteBuffer { - /** - * @param {number} numBytes The number of bytes to allocate. - */ - constructor(numBytes) { - if (typeof numBytes != typeof 1 || numBytes <= 0) { - throw "Error! ByteBuffer initialized with '" + numBytes + "'"; - } - - /** - * @type {Uint8Array} - * @public - */ - this.data = new Uint8Array(numBytes); - - /** - * Points to the byte that will next be written. - * @type {number} - * @public - */ - this.ptr = 0; - } - - /** - * Returns an exact copy of all the data that has been written to the ByteBuffer. - * @returns {Uint8Array} - */ - getData() { - const dataCopy = new Uint8Array(this.ptr); - dataCopy.set(this.data.subarray(0, this.ptr)); - return dataCopy; - } - - /** - * @param {number} b The byte to insert. - */ - insertByte(b) { - if (this.ptr + 1 > this.data.byteLength) { - throw `Cannot insert a byte, the buffer is full.`; - } - - // TODO: throw if byte is invalid? - this.data[this.ptr++] = b; - } - - /** - * @param {Array.|Uint8Array|Int8Array} bytes The bytes to insert. - */ - insertBytes(bytes) { - if (this.ptr + bytes.length > this.data.byteLength) { - throw `Cannot insert ${bytes.length} bytes, the buffer is full.`; - } - - // TODO: throw if bytes is invalid? - this.data.set(bytes, this.ptr); - this.ptr += bytes.length; - } - - /** - * Writes an unsigned number into the next n bytes. If the number is too large - * to fit into n bytes or is negative, an error is thrown. - * @param {number} num The unsigned number to write. - * @param {number} numBytes The number of bytes to write the number into. - */ - writeNumber(num, numBytes) { - if (numBytes < 1 || !numBytes) { - throw 'Trying to write into too few bytes: ' + numBytes; - } - if (num < 0) { - throw 'Trying to write a negative number (' + num + - ') as an unsigned number to an ArrayBuffer'; - } - if (num > (Math.pow(2, numBytes * 8) - 1)) { - throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes'; - } - - // Roll 8-bits at a time into an array of bytes. - const bytes = []; - while (numBytes-- > 0) { - const eightBits = num & 255; - bytes.push(eightBits); - num >>= 8; - } - - this.insertBytes(bytes); - } - - /** - * Writes a signed number into the next n bytes. If the number is too large - * to fit into n bytes, an error is thrown. - * @param {number} num The signed number to write. - * @param {number} numBytes The number of bytes to write the number into. - */ - writeSignedNumber(num, numBytes) { - if (numBytes < 1) { - throw 'Trying to write into too few bytes: ' + numBytes; - } - - const HALF = Math.pow(2, (numBytes * 8) - 1); - if (num >= HALF || num < -HALF) { - throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes'; - } - - // Roll 8-bits at a time into an array of bytes. - const bytes = []; - while (numBytes-- > 0) { - const eightBits = num & 255; - bytes.push(eightBits); - num >>= 8; - } - - this.insertBytes(bytes); - } - - /** - * @param {string} str The ASCII string to write. - */ - writeASCIIString(str) { - for (let i = 0; i < str.length; ++i) { - const curByte = str.charCodeAt(i); - if (curByte < 0 || curByte > 127) { - throw 'Trying to write a non-ASCII string!'; - } - this.insertByte(curByte); - } - } -} diff --git a/io/bytestream.js b/io/bytestream.js deleted file mode 100644 index 97d716a..0000000 --- a/io/bytestream.js +++ /dev/null @@ -1,365 +0,0 @@ -/* - * bytestream.js - * - * A pull stream for bytes. - * - * Licensed under the MIT License - * - * Copyright(c) 2023 Google Inc. - * Copyright(c) 2011 antimatter15 - */ - -/** - * This object allows you to peek and consume bytes as numbers and strings out - * of a stream. More bytes can be pushed into the back of the stream via the - * push() method. - * By default, the stream is Little Endian (that is the least significant byte - * is first). To change to Big Endian, use setBigEndian(). - */ -export class ByteStream { - /** - * @param {ArrayBuffer} ab The ArrayBuffer object. - * @param {number=} opt_offset The offset into the ArrayBuffer - * @param {number=} opt_length The length of this ByteStream - */ - constructor(ab, opt_offset, opt_length) { - if (!(ab instanceof ArrayBuffer)) { - throw 'Error! ByteStream constructed with an invalid ArrayBuffer object'; - } - - const offset = opt_offset || 0; - const length = opt_length || ab.byteLength; - - /** - * The current page of bytes in the stream. - * @type {Uint8Array} - * @private - */ - this.bytes = new Uint8Array(ab, offset, length); - - /** - * The next pages of bytes in the stream. - * @type {Array} - * @private - */ - this.pages_ = []; - - /** - * The byte in the current page that we will read next. - * @type {Number} - * @private - */ - this.ptr = 0; - - /** - * An ever-increasing number. - * @type {Number} - * @private - */ - this.bytesRead_ = 0; - - /** - * Whether the stream is little-endian (true) or big-endian (false). - * @type {boolean} - * @private - */ - this.littleEndian_ = true; - } - - /** @returns {boolean} Whether the stream is little-endian (least significant byte is first). */ - isLittleEndian() { - return this.littleEndian_; - } - - /** - * Big-Endian means the most significant byte is first. it is sometimes called Motorola-style. - * @param {boolean=} val The value to set. If not present, the stream is set to big-endian. - */ - setBigEndian(val = true) { - this.littleEndian_ = !val; - } - - /** - * Little-Endian means the least significant byte is first. is sometimes called Intel-style. - * @param {boolean=} val The value to set. If not present, the stream is set to little-endian. - */ - setLittleEndian(val = true) { - this.littleEndian_ = val; - } - - /** - * Returns how many bytes have been consumed (read or skipped) since the beginning of time. - * @returns {number} - */ - getNumBytesRead() { - return this.bytesRead_; - } - - /** - * Returns how many bytes are currently in the stream left to be read. - * @returns {number} - */ - getNumBytesLeft() { - const bytesInCurrentPage = (this.bytes.byteLength - this.ptr); - return this.pages_.reduce((acc, arr) => acc + arr.length, bytesInCurrentPage); - } - - /** - * Move the pointer ahead n bytes. If the pointer is at the end of the current array - * of bytes and we have another page of bytes, point at the new page. This is a private - * method, no validation is done. - * @param {number} n Number of bytes to increment. - * @private - */ - movePointer_(n) { - this.ptr += n; - this.bytesRead_ += n; - while (this.ptr >= this.bytes.length && this.pages_.length > 0) { - this.ptr -= this.bytes.length; - this.bytes = this.pages_.shift(); - } - } - - /** - * Peeks at the next n bytes as an unsigned number but does not advance the - * pointer. - * @param {number} n The number of bytes to peek at. Must be a positive integer. - * @returns {number} The n bytes interpreted as an unsigned number. - */ - peekNumber(n) { - const num = parseInt(n, 10); - if (n !== num || num < 0) { - throw 'Error! Called peekNumber() with a non-positive integer'; - } else if (num === 0) { - return 0; - } - - if (n > 4) { - throw 'Error! Called peekNumber(' + n + - ') but this method can only reliably read numbers up to 4 bytes long'; - } - - if (this.getNumBytesLeft() < num) { - throw 'Error! Overflowed the byte stream while peekNumber()! n=' + num + - ', ptr=' + this.ptr + ', bytes.length=' + this.getNumBytesLeft(); - } - - let result = 0; - let curPage = this.bytes; - let pageIndex = 0; - let ptr = this.ptr; - for (let i = 0; i < num; ++i) { - const exp = (this.littleEndian_ ? i : (num - 1 - i)) * 8; - result |= (curPage[ptr++] << exp); - - if (ptr >= curPage.length) { - curPage = this.pages_[pageIndex++]; - ptr = 0; - } - } - - return result; - } - - - /** - * Returns the next n bytes as an unsigned number (or -1 on error) - * and advances the stream pointer n bytes. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {number} The n bytes interpreted as an unsigned number. - */ - readNumber(n) { - const num = this.peekNumber(n); - this.movePointer_(n); - return num; - } - - - /** - * Returns the next n bytes as a signed number but does not advance the - * pointer. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {number} The bytes interpreted as a signed number. - */ - peekSignedNumber(n) { - let num = this.peekNumber(n); - const HALF = Math.pow(2, (n * 8) - 1); - const FULL = HALF * 2; - - if (num >= HALF) num -= FULL; - - return num; - } - - - /** - * Returns the next n bytes as a signed number and advances the stream pointer. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {number} The bytes interpreted as a signed number. - */ - readSignedNumber(n) { - const num = this.peekSignedNumber(n); - this.movePointer_(n); - return num; - } - - - /** - * This returns n bytes as a sub-array, advancing the pointer if movePointers - * is true. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @param {boolean} movePointers Whether to move the pointers. - * @returns {Uint8Array} The subarray. - */ - peekBytes(n, movePointers) { - const num = parseInt(n, 10); - if (n !== num || num < 0) { - throw 'Error! Called peekBytes() with a non-positive integer'; - } else if (num === 0) { - return new Uint8Array(); - } - - const totalBytesLeft = this.getNumBytesLeft(); - if (num > totalBytesLeft) { - throw 'Error! Overflowed the byte stream during peekBytes! n=' + num + - ', ptr=' + this.ptr + ', bytes.length=' + this.getNumBytesLeft(); - } - - const result = new Uint8Array(num); - let curPage = this.bytes; - let ptr = this.ptr; - let bytesLeftToCopy = num; - let pageIndex = 0; - while (bytesLeftToCopy > 0) { - const bytesLeftInPage = curPage.length - ptr; - const sourceLength = Math.min(bytesLeftToCopy, bytesLeftInPage); - - result.set(curPage.subarray(ptr, ptr + sourceLength), num - bytesLeftToCopy); - - ptr += sourceLength; - if (ptr >= curPage.length) { - curPage = this.pages_[pageIndex++]; - ptr = 0; - } - - bytesLeftToCopy -= sourceLength; - } - - if (movePointers) { - this.movePointer_(num); - } - - return result; - } - - /** - * Reads the next n bytes as a sub-array. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {Uint8Array} The subarray. - */ - readBytes(n) { - return this.peekBytes(n, true); - } - - /** - * Peeks at the next n bytes as an ASCII string but does not advance the pointer. - * @param {number} n The number of bytes to peek at. Must be a positive integer. - * @returns {string} The next n bytes as a string. - */ - peekString(n) { - const num = parseInt(n, 10); - if (n !== num || num < 0) { - throw 'Error! Called peekString() with a non-positive integer'; - } else if (num === 0) { - return ''; - } - - const totalBytesLeft = this.getNumBytesLeft(); - if (num > totalBytesLeft) { - throw 'Error! Overflowed the byte stream while peekString()! n=' + num + - ', ptr=' + this.ptr + ', bytes.length=' + this.getNumBytesLeft(); - } - - let result = new Array(num); - let curPage = this.bytes; - let pageIndex = 0; - let ptr = this.ptr; - for (let i = 0; i < num; ++i) { - result[i] = String.fromCharCode(curPage[ptr++]); - if (ptr >= curPage.length) { - curPage = this.pages_[pageIndex++]; - ptr = 0; - } - } - - return result.join(''); - } - - /** - * Returns the next n bytes as an ASCII string and advances the stream pointer - * n bytes. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {string} The next n bytes as a string. - */ - readString(n) { - const strToReturn = this.peekString(n); - this.movePointer_(n); - return strToReturn; - } - - /** - * Skips n bytes in the stream. - * @param {number} n The number of bytes to skip. Must be a positive integer. - * @returns {ByteStream} Returns this ByteStream for chaining. - */ - skip(n) { - const num = parseInt(n, 10); - if (n !== num || num < 0) { - throw 'Error! Called skip() with a non-positive integer'; - } else if (num === 0) { - return this; - } - - const totalBytesLeft = this.getNumBytesLeft(); - if (num > totalBytesLeft) { - throw 'Error! Overflowed the byte stream while skip()! n=' + num + - ', ptr=' + this.ptr + ', bytes.length=' + this.getNumBytesLeft(); - } - - this.movePointer_(n); - return this; - } - - /** - * Feeds more bytes into the back of the stream. - * @param {ArrayBuffer} ab - */ - push(ab) { - if (!(ab instanceof ArrayBuffer)) { - throw 'Error! ByteStream.push() called with an invalid ArrayBuffer object'; - } - - this.pages_.push(new Uint8Array(ab)); - // If the pointer is at the end of the current page of bytes, this will advance - // to the next page. - this.movePointer_(0); - } - - /** - * Creates a new ByteStream from this ByteStream that can be read / peeked. - * Note that the teed stream is a disconnected copy. If you push more bytes to the original - * stream, the copy does not get them. - * TODO: Assess whether the above causes more bugs than it avoids. (It would feel weird to me if - * the teed stream shared some state with the original stream.) - * @returns {ByteStream} A clone of this ByteStream. - */ - tee() { - const clone = new ByteStream(this.bytes.buffer); - clone.bytes = this.bytes; - clone.ptr = this.ptr; - clone.pages_ = this.pages_.slice(); - clone.bytesRead_ = this.bytesRead_; - clone.littleEndian_ = this.littleEndian_; - return clone; - } -} diff --git a/media/README.md b/media/README.md deleted file mode 100644 index 300ac90..0000000 --- a/media/README.md +++ /dev/null @@ -1 +0,0 @@ -Dealing with digital media files (audio, video, images). Mostly a thought-experiment for now. diff --git a/media/media.js b/media/media.js deleted file mode 100644 index ef7c27d..0000000 --- a/media/media.js +++ /dev/null @@ -1,35 +0,0 @@ -console.warn(`This is not even an alpha-level API. Do not use.`); - -// A Container Format is a file that embeds multiple data streams into a single file. -// Examples: -// - the ZIP family (ZIP, JAR, CBZ, EPUB, ODF, OOXML) -// - the ISO-BMFF family (MP4, HEVC, HEIC, AVIF, MOV/QT, etc) -// - the Matroska family (MKV, WebM) -// - the RIFF family (WAV, AVI, WebP) -// - the OGG family (OGV, OPUS) - -// The ZIP container needs special processing to determine what files are present inside it :( -// The ISO-BMFF container needs special processing because of its "compatible brands" array :( -// The Matroska container needs special processing because the sub-type can appear anywhere :( -// The OGG container needs special processing to determine what kind of streams are present :( - -/** - * @readonly - * @enum {number} - */ -export const ContainerType = { - UNKNOWN: 0, - ZIP: 1, - ISOBMFF: 100, - MATROSKA: 101, - RIFF: 102, - OGG: 103, -}; - -/** - * @param {ArrayBuffer} ab - * @returns {ContainerType} - */ -export function getContainerType(ab) { - return ContainerType.UNKNOWN; -} diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index ba830be..0000000 --- a/package-lock.json +++ /dev/null @@ -1,2417 +0,0 @@ -{ - "name": "@codedread/bitjs", - "version": "1.2.6", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "@codedread/bitjs", - "version": "1.2.6", - "license": "MIT", - "devDependencies": { - "c8": "^7.14.0", - "chai": "^4.3.4", - "mocha": "^10.1.0", - "typescript": "^4.8.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "node_modules/c8": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/c8/-/c8-7.14.0.tgz", - "integrity": "sha512-i04rtkkcNcCf7zsQcSv/T9EbUn4RXQ6mropeMcjFOsQXQ0iGLAr/xT6TImQg4+U9hmNpN9XdvPkjUL1IzbgxJw==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@istanbuljs/schema": "^0.1.3", - "find-up": "^5.0.0", - "foreground-child": "^2.0.0", - "istanbul-lib-coverage": "^3.2.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-reports": "^3.1.4", - "rimraf": "^3.0.2", - "test-exclude": "^6.0.0", - "v8-to-istanbul": "^9.0.0", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9" - }, - "bin": { - "c8": "bin/c8.js" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/chai": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", - "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", - "dev": true, - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", - "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha": { - "version": "10.8.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", - "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-colors": "^4.1.3", - "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", - "debug": "^4.3.5", - "diff": "^5.2.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^8.1.0", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", - "ms": "^2.1.3", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/mocha/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/v8-to-istanbul": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", - "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, - "dependencies": { - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "requires": { - "fill-range": "^7.1.1" - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "c8": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/c8/-/c8-7.14.0.tgz", - "integrity": "sha512-i04rtkkcNcCf7zsQcSv/T9EbUn4RXQ6mropeMcjFOsQXQ0iGLAr/xT6TImQg4+U9hmNpN9XdvPkjUL1IzbgxJw==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@istanbuljs/schema": "^0.1.3", - "find-up": "^5.0.0", - "foreground-child": "^2.0.0", - "istanbul-lib-coverage": "^3.2.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-reports": "^3.1.4", - "rimraf": "^3.0.2", - "test-exclude": "^6.0.0", - "v8-to-istanbul": "^9.0.0", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9" - } - }, - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - }, - "chai": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", - "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", - "dev": true, - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "requires": { - "ms": "^2.1.3" - } - }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, - "diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, - "foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "minimatch": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", - "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - }, - "mocha": { - "version": "10.8.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", - "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.3", - "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", - "debug": "^4.3.5", - "diff": "^5.2.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^8.1.0", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", - "ms": "^2.1.3", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", - "yargs-unparser": "^2.0.0" - }, - "dependencies": { - "glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - } - } - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", - "dev": true - }, - "v8-to-istanbul": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", - "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - }, - "yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - } - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index b3086aa..0000000 --- a/package.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "@codedread/bitjs", - "version": "1.2.6", - "description": "Binary Tools for JavaScript", - "homepage": "https://github.com/codedread/bitjs", - "author": "Jeff Schiller", - "license": "MIT", - "keywords": [ "binary", "javascript", "archive", "file", "image", - "pkzip", "zip", "rar", "tar", "gzip", - "unzip", "unrar", "untar", "gunzip", - "gif", "jpeg", "png", "webp", - "codecs", "mp4", "avc", "webm" - ], - "main": "./index.js", - "type": "module", - "types": "types/index.d.ts", - "exports": "./index.js", - "repository": { - "type": "git", - "url": "git+https://github.com/codedread/bitjs.git" - }, - "bugs": { - "url": "https://github.com/codedread/bitjs/issues" - }, - "devDependencies": { - "c8": "^7.14.0", - "chai": "^4.3.4", - "mocha": "^10.1.0", - "typescript": "^4.8.0" - }, - "directories": { - "doc": "docs", - "test": "tests" - }, - "scripts": { - "build-webpshim": "cd build; make", - "coverage": "c8 npm run test", - "test": "./node_modules/.bin/mocha tests/*.spec.js", - "update-types": "tsc" - } -} diff --git a/tests/archive-compress.spec.js b/tests/archive-compress.spec.js deleted file mode 100644 index ae2e3a9..0000000 --- a/tests/archive-compress.spec.js +++ /dev/null @@ -1,130 +0,0 @@ -import * as fs from 'node:fs'; -import 'mocha'; -import { expect } from 'chai'; -import { getUnarchiver } from '../archive/decompress.js'; -import { CompressStatus, Zipper } from '../archive/compress.js'; -import { ZipCompressionMethod } from '../archive/common.js'; - -/** - * @typedef {import('./archive/compress.js').FileInfo} FileInfo - */ - -const PATH = `tests/archive-testfiles/`; - -const INPUT_FILENAMES = [ - 'sample-1.txt', - 'sample-2.csv', - 'sample-3.json', -]; - -describe('bitjs.archive.compress', () => { - /** @type {Map} */ - let inputFileInfos = new Map(); - let decompressedFileSize = 0; - - for (const fileName of INPUT_FILENAMES) { - const fullFilename = `${PATH}${fileName}`; - const fd = fs.openSync(fullFilename, 'r'); - const lastModTime = fs.fstatSync(fd).mtimeMs; - const nodeBuf = fs.readFileSync(fullFilename); - const fileData = new Uint8Array( - nodeBuf.buffer.slice(nodeBuf.byteOffset, nodeBuf.byteOffset + nodeBuf.length)); - inputFileInfos.set(fileName, {fileName, lastModTime, fileData}); - decompressedFileSize += fileData.byteLength; - fs.closeSync(fd); - } - - it('zipper throws for invalid compression method', async () => { - expect(() => new Zipper({zipCompressionMethod: 42})).throws(); - }); - - it('zipper works for STORE', (done) => { - let extractCalled = false; - const files = new Map(inputFileInfos); - const zipper = new Zipper({zipCompressionMethod: ZipCompressionMethod.STORE}); - zipper.start(Array.from(files.values()), true).then(byteArray => { - expect(zipper.compressState).equals(CompressStatus.COMPLETE); - expect(byteArray.byteLength > decompressedFileSize).equals(true); - - const unarchiver = getUnarchiver(byteArray.buffer); - unarchiver.addEventListener('extract', evt => { - extractCalled = true; - const {filename, fileData} = evt.unarchivedFile; - expect(files.has(filename)).equals(true); - const inputFile = files.get(filename).fileData; - expect(inputFile.byteLength).equals(fileData.byteLength); - for (let b = 0; b < inputFile.byteLength; ++b) { - expect(inputFile[b]).equals(fileData[b]); - } - }); - unarchiver.addEventListener('finish', evt => done()); - unarchiver.start(); - expect(extractCalled).equals(true); - }); - }); - - try { - new CompressionStream('deflate-raw'); - - it('zipper works for DEFLATE, where deflate-raw is supported', async () => { - const files = new Map(inputFileInfos); - const zipper = new Zipper({zipCompressionMethod: ZipCompressionMethod.DEFLATE}); - const byteArray = await zipper.start(Array.from(files.values()), true); - - expect(zipper.compressState).equals(CompressStatus.COMPLETE); - expect(byteArray.byteLength < decompressedFileSize).equals(true); - - const unarchiver = getUnarchiver(byteArray.buffer); - unarchiver.addEventListener('extract', evt => { - const {filename, fileData} = evt.unarchivedFile; - expect(files.has(filename)).equals(true); - const inputFile = files.get(filename).fileData; - expect(inputFile.byteLength).equals(fileData.byteLength); - for (let b = 0; b < inputFile.byteLength; ++b) { - expect(inputFile[b]).equals(fileData[b]); - } - }); - await unarchiver.start(); - }); - } catch (err) { - // Do nothing. This runtime did not support DEFLATE. (Node < 21.2.0) - } - - it('zipper.start([file1]) and appendFiles(otherFiles, true) works', (done) => { - let extractCalled = false; - const files = new Map(inputFileInfos); - const zipper = new Zipper({zipCompressionMethod: ZipCompressionMethod.STORE}); - const fileArr = Array.from(files.values()); - zipper.start([fileArr.shift()]).then(byteArray => { - expect(zipper.compressState).equals(CompressStatus.COMPLETE); - expect(byteArray.byteLength > decompressedFileSize).equals(true); - - const unarchiver = getUnarchiver(byteArray.buffer); - unarchiver.addEventListener('extract', evt => { - extractCalled = true; - const {filename, fileData} = evt.unarchivedFile; - expect(files.has(filename)).equals(true); - const inputFile = files.get(filename).fileData; - expect(inputFile.byteLength).equals(fileData.byteLength); - for (let b = 0; b < inputFile.byteLength; ++b) { - expect(inputFile[b]).equals(fileData[b]); - } - }); - unarchiver.addEventListener('finish', evt => { - done(); - }); - unarchiver.start(); - expect(extractCalled).equals(true); - }); - zipper.appendFiles(fileArr, true); - }); - - it('appendFiles() throws an error if after last file', (done) => { - const files = new Map(inputFileInfos); - const zipper = new Zipper({zipCompressionMethod: ZipCompressionMethod.STORE}); - zipper.start(Array.from(files.values()), true); - expect(() => zipper.appendFiles(Array.from(files.values()), true)).throws(); - done(); - }); - -}); diff --git a/tests/archive-decompress.spec.js b/tests/archive-decompress.spec.js deleted file mode 100644 index f09045b..0000000 --- a/tests/archive-decompress.spec.js +++ /dev/null @@ -1,96 +0,0 @@ -import * as fs from 'node:fs'; -import 'mocha'; -import { expect } from 'chai'; - -import { Gunzipper, Unarchiver, getUnarchiver } from '../archive/decompress.js'; - -const PATH = `tests/archive-testfiles/`; - -const INPUT_FILES = [ - 'sample-1.txt', - 'sample-2.csv', - 'sample-3.json', -]; - -const ARCHIVE_FILES = [ - // rar a -m0 -ma4 archive-rar-store.rar sample* - 'archive-rar-store.rar', - // rar a -m3 -ma4 archive-rar-default.rar sample* - 'archive-rar-default.rar', - // rar a -m5 -ma4 archive-rar-smaller.rar sample* - 'archive-rar-smaller.rar', - 'archive-tar.tar', - 'archive-zip-store.zip', - 'archive-zip-faster.zip', - 'archive-zip-smaller.zip', -]; - -describe('bitjs.archive.decompress', () => { - /** @type {Map} */ - let inputArrayBuffers = new Map(); - - for (const inputFile of INPUT_FILES) { - const nodeBuf = fs.readFileSync(`${PATH}${inputFile}`); - const ab = nodeBuf.buffer.slice(nodeBuf.byteOffset, nodeBuf.byteOffset + nodeBuf.length); - inputArrayBuffers.set(inputFile, ab); - } - - for (const outFile of ARCHIVE_FILES) { - it(outFile, async () => { - const bufs = new Map(inputArrayBuffers); - const nodeBuf = fs.readFileSync(`${PATH}${outFile}`); - const ab = nodeBuf.buffer.slice(nodeBuf.byteOffset, nodeBuf.byteOffset + nodeBuf.length); - let unarchiver = getUnarchiver(ab); - expect(unarchiver instanceof Unarchiver).equals(true); - let extractEvtFiredForAddEventListener = false; - let extractEvtFiredForOnExtract = false; - - unarchiver.addEventListener('extract', evt => { - extractEvtFiredForAddEventListener = true; - const {filename, fileData} = evt.unarchivedFile; - expect(bufs.has(filename)).equals(true); - const ab = bufs.get(filename); - expect(fileData.byteLength).equals(ab.byteLength); - for (let b = 0; b < fileData.byteLength; ++b) { - expect(fileData[b] === ab[b]); - } - // Remove the value from the map so that it is only used once. - bufs.delete(filename); - }); - unarchiver.onExtract(evt => { - extractEvtFiredForOnExtract = true; - expect(evt.unarchivedFile.filename.length > 0).equals(true); - }) - - await unarchiver.start(); - expect(extractEvtFiredForAddEventListener).equals(true); - expect(extractEvtFiredForOnExtract).equals(true); - }); - } - - describe('gunzip', () => { - it('can gunzip a file', async () => { - const bufs = new Map(inputArrayBuffers); - const nodeBuf = fs.readFileSync(`${PATH}sample-1-slowest.txt.gz`); - const ab = nodeBuf.buffer.slice(nodeBuf.byteOffset, nodeBuf.byteOffset + nodeBuf.length); - let gunzipper = getUnarchiver(ab, {debug: true}); - expect(gunzipper instanceof Gunzipper).equals(true); - let extractEvtFiredForOnExtract = false; - - gunzipper.onExtract(evt => { - extractEvtFiredForOnExtract = true; - const {filename, fileData} = evt.unarchivedFile; - expect(filename).equals('sample-1.txt'); - - const ab = bufs.get('sample-1.txt'); - expect(fileData.byteLength).equals(ab.byteLength); - for (let b = 0; b < fileData.byteLength; ++b) { - expect(fileData[b] === ab[b]); - } - }); - - await gunzipper.start(); - expect(extractEvtFiredForOnExtract).equals(true); - }); - }); -}); diff --git a/tests/archive-testfiles/archive-rar-default.rar b/tests/archive-testfiles/archive-rar-default.rar deleted file mode 100644 index 896e60a..0000000 Binary files a/tests/archive-testfiles/archive-rar-default.rar and /dev/null differ diff --git a/tests/archive-testfiles/archive-rar-smaller.rar b/tests/archive-testfiles/archive-rar-smaller.rar deleted file mode 100644 index 291fc98..0000000 Binary files a/tests/archive-testfiles/archive-rar-smaller.rar and /dev/null differ diff --git a/tests/archive-testfiles/archive-rar-store.rar b/tests/archive-testfiles/archive-rar-store.rar deleted file mode 100644 index 8df29de..0000000 Binary files a/tests/archive-testfiles/archive-rar-store.rar and /dev/null differ diff --git a/tests/archive-testfiles/archive-tar.tar b/tests/archive-testfiles/archive-tar.tar deleted file mode 100644 index 4333926..0000000 Binary files a/tests/archive-testfiles/archive-tar.tar and /dev/null differ diff --git a/tests/archive-testfiles/archive-zip-faster.zip b/tests/archive-testfiles/archive-zip-faster.zip deleted file mode 100644 index 6aa2010..0000000 Binary files a/tests/archive-testfiles/archive-zip-faster.zip and /dev/null differ diff --git a/tests/archive-testfiles/archive-zip-smaller.zip b/tests/archive-testfiles/archive-zip-smaller.zip deleted file mode 100644 index e2b656f..0000000 Binary files a/tests/archive-testfiles/archive-zip-smaller.zip and /dev/null differ diff --git a/tests/archive-testfiles/archive-zip-store.zip b/tests/archive-testfiles/archive-zip-store.zip deleted file mode 100644 index 84c1e2f..0000000 Binary files a/tests/archive-testfiles/archive-zip-store.zip and /dev/null differ diff --git a/tests/archive-testfiles/sample-1-slowest.txt.gz b/tests/archive-testfiles/sample-1-slowest.txt.gz deleted file mode 100644 index f284e4f..0000000 Binary files a/tests/archive-testfiles/sample-1-slowest.txt.gz and /dev/null differ diff --git a/tests/archive-testfiles/sample-1.txt b/tests/archive-testfiles/sample-1.txt deleted file mode 100644 index b7bac92..0000000 --- a/tests/archive-testfiles/sample-1.txt +++ /dev/null @@ -1,20 +0,0 @@ -This is a sample text file. This text file is large enough to make creating -zip files more interesting, since compression has a chance to work on a larger -sample file that has duplicate words in it. - -This is a sample text file. This text file is large enough to make creating -zip files more interesting, since compression has a chance to work on a larger -sample file that has duplicate words in it. - -This is a sample text file. This text file is large enough to make creating -zip files more interesting, since compression has a chance to work on a larger -sample file that has duplicate words in it. - -This is a sample text file. This text file is large enough to make creating -zip files more interesting, since compression has a chance to work on a larger -sample file that has duplicate words in it. - -This is a sample text file. This text file is large enough to make creating -zip files more interesting, since compression has a chance to work on a larger -sample file that has duplicate words in it. - diff --git a/tests/archive-testfiles/sample-2.csv b/tests/archive-testfiles/sample-2.csv deleted file mode 100644 index dd1a5c9..0000000 --- a/tests/archive-testfiles/sample-2.csv +++ /dev/null @@ -1,4 +0,0 @@ -filetype,extension -"text file","txt" -"JSON file","json" -"CSV file","csv" diff --git a/tests/archive-testfiles/sample-3.json b/tests/archive-testfiles/sample-3.json deleted file mode 100644 index 2259b5e..0000000 --- a/tests/archive-testfiles/sample-3.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "file formats": ["csv", "json", "txt"], - "tv shows": { - "it's": ["monty", "python's", "flying", "circus"] - } -} diff --git a/tests/codecs.spec.js b/tests/codecs.spec.js deleted file mode 100644 index 5e350c8..0000000 --- a/tests/codecs.spec.js +++ /dev/null @@ -1,631 +0,0 @@ -/* - * codecs.spec.js - * - * Licensed under the MIT License - * - * Copyright(c) 2022 Google Inc. - */ - -import 'mocha'; -import { expect } from 'chai'; -import { getFullMIMEString, getShortMIMEString } from '../codecs/codecs.js'; - -/** - * @typedef {import('../codecs/codecs.js').ProbeStream} ProbeStream - */ -/** - * @typedef {import('../codecs/codecs.js').ProbeFormat} ProbeFormat - */ -/** - * @typedef {import('../codecs/codecs.js').ProbeInfo} ProbeInfo - */ - -describe('codecs test suite', () => { - - describe('getShortMIMEString()', () => { - it('throws when unknown', () => { - expect(() => getShortMIMEString()).to.throw(); - expect(() => getShortMIMEString(null)).to.throw(); - expect(() => getShortMIMEString({})).to.throw(); - expect(() => getShortMIMEString({ - streams: [], - })).to.throw(); - expect(() => getShortMIMEString({ - format: { format_name: 'mp4' }, - streams: [], - })).to.throw(); - expect(() => getShortMIMEString({ - format: { format_name: 'invalid-video-format' }, - streams: [ { codec_type: 'video' } ], - })).to.throw(); - }); - - it('detects MP3', () => { - expect(getShortMIMEString({ - format: { format_name: 'mp3' }, - streams: [ { codec_type: 'audio'}, { codec_type: 'video' } ], - })).equals('audio/mpeg'); - }); - - it('detects FLAC', () => { - expect(getShortMIMEString({ - format: { format_name: 'flac', format_tag_string: 'fLaC' }, - streams: [ { codec_type: 'audio'}, { codec_type: 'video' } ], - })).equals('audio/flac'); - }); - - it('detects AVI video', () => { - expect(getShortMIMEString({ - format: { format_name: 'avi' }, - streams: [ { codec_type: 'video' } ], - })).equals('video/x-msvideo'); - }); - - it('detects MPEG video', () => { - expect(getShortMIMEString({ - format: { format_name: 'mpeg' }, - streams: [ { codec_type: 'video' } ], - })).equals('video/mpeg'); - }); - - it('detects MPEG audio', () => { - expect(getShortMIMEString({ - format: { format_name: 'mpeg' }, - streams: [ { codec_type: 'audio' } ], - })).equals('audio/mpeg'); - }); - - it('detects M4A audio', () => { - expect(getShortMIMEString({ - format: { filename: 'sample.m4a', format_name: 'mov,mp4,m4a,3gp,3g2,mj2' }, - streams: [ { codec_type: 'video', }, { codec_type: 'audio' } ], - })).equals('audio/mp4'); - }); - - it('detects MP4 video', () => { - expect(getShortMIMEString({ - format: { format_name: 'mov,mp4,m4a,3gp,3g2,mj2' }, - streams: [ { codec_type: 'video' } ], - })).equals('video/mp4'); - }); - - it('detects MP4 audio', () => { - expect(getShortMIMEString({ - format: { format_name: 'mov,mp4,m4a,3gp,3g2,mj2' }, - streams: [ { codec_type: 'audio' } ], - })).equals('audio/mp4'); - }); - - it('detects OGG video', () => { - expect(getShortMIMEString({ - format: { format_name: 'ogg' }, - streams: [ { codec_type: 'video' } ], - })).equals('video/ogg'); - }); - - it('detects OGG audio', () => { - expect(getShortMIMEString({ - format: { format_name: 'ogg' }, - streams: [ { codec_type: 'audio' } ], - })).equals('audio/ogg'); - }); - - it('detects WEBM video', () => { - expect(getShortMIMEString({ - format: { format_name: 'matroska,webm' }, - streams: [ { codec_type: 'video', codec_name: 'vp8' } ], - })).equals('video/webm'); - expect(getShortMIMEString({ - format: { format_name: 'matroska,webm' }, - streams: [ { codec_type: 'video', codec_name: 'vp9' } ], - })).equals('video/webm'); - expect(getShortMIMEString({ - format: { format_name: 'matroska,webm' }, - streams: [ { codec_type: 'video', codec_name: 'av1' } ], - })).equals('video/webm'); - }); - - it('detects WEBM audio', () => { - expect(getShortMIMEString({ - format: { format_name: 'matroska,webm' }, - streams: [ { codec_type: 'audio', codec_name: 'opus' } ], - })).equals('audio/webm'); - expect(getShortMIMEString({ - format: { format_name: 'matroska,webm' }, - streams: [ { codec_type: 'audio', codec_name: 'vorbis' } ], - })).equals('audio/webm'); - }); - - it('detects Matroska Video', () => { - expect(getShortMIMEString({ - format: { format_name: 'matroska,webm' }, - streams: [ { codec_type: 'video', codec_name: 'h264' } ], - })).equals('video/x-matroska'); - - expect(getShortMIMEString({ - format: { format_name: 'matroska,webm' }, - streams: [ - { codec_type: 'audio', codec_name: 'aac' }, - { codec_type: 'video', codec_name: 'vp9' }, - ], - })).equals('video/x-matroska'); - - expect(getShortMIMEString({ - format: { format_name: 'matroska,webm' }, - streams: [ - { codec_type: 'audio', codec_name: 'vorbis' }, - { codec_type: 'video', codec_name: 'h264' }, - ], - })).equals('video/x-matroska'); - }); - - it('detects Matroska audio', () => { - expect(getShortMIMEString({ - format: { format_name: 'matroska,webm' }, - streams: [ { codec_type: 'audio', codec_name: 'aac' } ], - })).equals('audio/x-matroska'); - expect(getShortMIMEString({ - format: { format_name: 'matroska,webm' }, - streams: [ { codec_type: 'audio', codec_name: 'dts' } ], - })).equals('audio/x-matroska'); - }); - }); - - describe('getFullMIMEString()', () => { - it('throws when unknown', () => { - expect(() => getFullMIMEString()).to.throw(); - expect(() => getFullMIMEString(null)).to.throw(); - expect(() => getFullMIMEString({})).to.throw(); - expect(() => getFullMIMEString({ - streams: [], - })).to.throw(); - expect(() => getFullMIMEString({ - format: { format_name: 'invalid-video-format' }, - streams: [ { codec_type: 'video' } ], - })).to.throw(); - }); - - describe('AVC1 / H264', () => { - /** @type {ProbeInfo} */ - let info; - - beforeEach(() => { - info = { - format: { format_name: 'mov,mp4,m4a,3gp,3g2,mj2' }, - streams: [{ - codec_type: 'video', - codec_tag_string: 'avc1', - }], - }; - }); - - describe('Profile tests', () => { - beforeEach(() => { - info.streams[0].level = 20; - }); - - it('detects Constrained Baseline Profile', () => { - info.streams[0].profile = 'Constrained Baseline'; - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.satisfy(s => s.startsWith('video/mp4; codecs="avc1.4240')); - }); - - it('detects Baseline Profile', () => { - info.streams[0].profile = 'Baseline'; - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.satisfy(s => s.startsWith('video/mp4; codecs="avc1.4200')); - }); - - it('detects Extended Profile', () => { - info.streams[0].profile = 'Extended'; - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.satisfy(s => s.startsWith('video/mp4; codecs="avc1.5800')); - }); - - it('detects Main Profile', () => { - info.streams[0].profile = 'Main'; - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.satisfy(s => s.startsWith('video/mp4; codecs="avc1.4D00')); - }); - - it('detects High Profile', () => { - info.streams[0].profile = 'High'; - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.satisfy(s => s.startsWith('video/mp4; codecs="avc1.6400')); - }); - }); - - describe('Level tests', () => { - beforeEach(() => { - info.streams[0].profile = 'Main'; - }); - - it('detects 2-digit hex level', () => { - info.streams[0].level = 21; // 0x15 - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.satisfy(s => s.startsWith('video/mp4; codecs="avc1.')) - .and.satisfy(s => s.endsWith('15"')); - }); - - it('detects 1-digit hex level', () => { - info.streams[0].level = 10; // 0x0A - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.satisfy(s => s.startsWith('video/mp4; codecs="avc1.')) - .and.satisfy(s => s.endsWith('0A"')); - }); - }); - - it('Handles codec_name=h264, but no codec_tag_string', () => { - info.streams[0].codec_name = 'h264'; - info.streams[0].profile = 'Main'; - info.streams[0].codec_tag_string = '[0][0][0][0]'; - info.streams[0].level = 20; - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.satisfy(s => s.startsWith('video/mp4; codecs="avc1.4D00')); - }); - - describe('extradata tests', () => { - it('prefers extradata for codec string', () => { - info.streams[0].extradata = '00000000: 014d 4028 ffe1 001a 674d 4028 9a66 0140 .M@(...gM@(.f.@'; - expect(getFullMIMEString(info)) - .equals('video/mp4; codecs="avc1.4D4028"'); - }); - - it('handles multiline extradata', () => { - info.streams[0].extradata = - '00000000: 0164 0029 ffe1 001b 6764 0029 ac2b 4028 .d.)....gd.).+@(\n' + - '00000010: 2f82 1801 1000 0003 0010 0000 0303 20f1 /............. .'; - expect(getFullMIMEString(info)) - .equals('video/mp4; codecs="avc1.640029"'); - }); - - it('does not use extradata if codec_tag_string is not avc1', () => { - info.streams[0].codec_tag_string = 'not-avc1'; - info.streams[0].codec_name = 'h264'; - info.streams[0].profile = 'Main'; - info.streams[0].level = 20; - info.streams[0].extradata = '00000000: 014d 4028 ffe1 001a 674d 4028 9a66 0140 .M@(...gM@(.f.@'; - expect(getFullMIMEString(info)) - .equals('video/mp4; codecs="avc1.4D0014"'); - }); - }); - }); - }); - - describe('WebM' ,() => { - describe('AV1', () => { - /** @type {ProbeInfo} */ - let info; - - beforeEach(() => { - info = { - format: { format_name: 'matroska,webm' }, - streams: [{ codec_type: 'video', codec_name: 'av1' }], - }; - }); - - it('outputs MIME string', () => { - expect(getShortMIMEString(info)).equals('video/webm'); - expect(getFullMIMEString(info)).equals('video/webm; codecs="av1"') - }); - }); - - describe('VP8', () => { - /** @type {ProbeInfo} */ - let info; - - beforeEach(() => { - info = { - format: { format_name: 'matroska,webm' }, - streams: [{ codec_type: 'video', codec_name: 'vp8' }], - }; - }); - - it('outputs MIME string', () => { - expect(getShortMIMEString(info)).equals('video/webm'); - expect(getFullMIMEString(info)).equals('video/webm; codecs="vp8"') - }); - }); - - describe('VP09 / VP9', () => { - /** @type {ProbeInfo} */ - let info; - - beforeEach(() => { - info = { - format: { format_name: 'matroska,webm' }, - streams: [{ codec_type: 'video', codec_name: 'vp9', codec_tag_string: 'vp09' }], - }; - }); - - describe('Profile tests', () => { - beforeEach(() => { - info.streams[0].level = 20; - }); - - it('detects Profile 0', () => { - info.streams[0].profile = 'Profile 0'; - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.satisfy(s => s.startsWith('video/webm; codecs="vp09.00.')); - }); - }); - - describe('Level tests', () => { - beforeEach(() => { - info.streams[0].profile = 'Profile 0'; - }); - - it('detects 2-digit hex level', () => { - info.streams[0].level = 21; // 0x15 - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.satisfy(s => s.startsWith('video/webm; codecs="vp09.')) - .and.satisfy(s => { - const matches = s.match(/vp09\.[0-9]{2}\.([0-9A-F]{2})\.[0-9A-F]{2}/); - return matches && matches.length === 2 && matches[1] === '15'; - }); - }); - - it('detects level = -99 as level 1', () => { - info.streams[0].level = -99; // I believe this is the "unknown" value from ffprobe. - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.equals('video/webm; codecs="vp09.00.10.10"'); - }); - }); - - it('detects codec_name=vp9 but no codec_tag_string', () => { - info.streams[0].codec_name = 'vp9'; - info.streams[0].codec_tag_string = '[0][0][0][0]'; - info.streams[0].profile = 'Profile 0'; - info.streams[0].level = 21; // 0x15 - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.satisfy(s => s.startsWith('video/webm; codecs="vp09.00.15')); - }); - }); - - describe('Vorbis', () => { - /** @type {ProbeInfo} */ - let info; - - beforeEach(() => { - info = { - format: { format_name: 'matroska,webm' }, - streams: [{ codec_type: 'audio', codec_name: 'vorbis' }], - }; - }); - - it('detects vorbis', () => { - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.equals('audio/webm; codecs="vorbis"'); - }); - }); - - describe('Opus', () => { - /** @type {ProbeInfo} */ - let info; - - beforeEach(() => { - info = { - format: { format_name: 'matroska,webm' }, - streams: [{ - codec_type: 'audio', - codec_name: 'opus', - }], - }; - }); - - it('detects opus', () => { - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.equals('audio/webm; codecs="opus"'); - }); - }); - }); - - describe('Matroska', () => { - describe('MPEG2 codec', () => { - /** @type {ProbeInfo} */ - let info; - - beforeEach(() => { - info = { - format: { format_name: 'matroska,webm' }, - streams: [{ codec_type: 'video', codec_name: 'mpeg2video' }], - }; - }); - - it('outputs full MIME string', () => { - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.equals('video/x-matroska; codecs="mpeg2video"'); - }); - }); - - describe('AC-3', () => { - /** @type {ProbeInfo} */ - let info; - - beforeEach(() => { - info = { - format: { format_name: 'matroska,webm' }, - streams: [{ codec_type: 'audio', codec_name: 'ac3' }], - }; - }); - - it('detects AC-3', () => { - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.equals('audio/x-matroska; codecs="ac-3"'); - }); - }); - - describe('DTS', () => { - /** @type {ProbeInfo} */ - let info; - - beforeEach(() => { - info = { - format: { format_name: 'matroska,webm' }, - streams: [{ codec_type: 'audio', codec_name: 'dts' }], - }; - }); - - it('outputs full MIME string', () => { - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.equals('audio/x-matroska; codecs="dts"'); - }); - }); - }); - - describe('MP4A / AAC', () => { - /** @type {ProbeInfo} */ - let baseInfo = { - format: { format_name: 'mov,mp4,m4a,3gp,3g2,mj2' }, - streams: [{ - codec_type: 'audio', - codec_tag_string: 'mp4a', - }], - }; - - it('throws when unknown', () => { - expect(() => getFullMIMEString(baseInfo)).to.throw(); - }); - - describe('Profile tests', () => { - it('recognizes AAC-LC', () => { - const info = structuredClone(baseInfo); - info.streams[0].profile = 'LC'; - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.equals('audio/mp4; codecs="mp4a.40.2"'); - }); - }); - - it('recognizes codec_name=HE-AAC', () => { - const info = structuredClone(baseInfo); - info.streams[0].profile = 'HE-AAC'; - info.streams[0].codec_name = 'aac'; - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.equals('audio/mp4; codecs="mp4a.40.5"'); - }); - - it('handles codec_name=aac but no codec_tag_string', () => { - const info = structuredClone(baseInfo); - info.streams[0].profile = 'LC'; - info.streams[0].codec_tag_string = '[0][0][0][0]'; - info.streams[0].codec_name = 'aac'; - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.equals('audio/mp4; codecs="mp4a.40.2"'); - }); - - it('handles m4a file with MJPEG stream', () => { - const info = structuredClone(baseInfo); - info.format.filename = 'sample.m4a'; - info.streams[0].profile = 'LC'; - info.streams.push({codec_name: 'mjpeg', codec_type: 'video', profile: 'Baseline'}); - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.equals('audio/mp4; codecs="mp4a.40.2"'); - }) - }); - - describe('MP4 / FLAC', () => { - /** @type {ProbeInfo} */ - let info; - - beforeEach(() => { - info = { - format: { format_name: 'mov,mp4,m4a,3gp,3g2,mj2' }, - streams: [{ - codec_type: 'audio', - codec_tag_string: 'mp4a', - }], - }; - }); - - it('audio/mp4 handles fLaC', () => { - info.streams[0].codec_tag_string = 'fLaC'; - info.streams[0].codec_name = 'flac'; - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.equals('audio/mp4; codecs="flac"'); - }); - - it('video/mp4 handles fLaC', () => { - const vInfo = structuredClone(info); - vInfo.streams[0].codec_tag_string = 'fLaC'; - vInfo.streams[0].codec_name = 'flac'; - vInfo.streams.push({ - codec_type: 'video', - codec_name: 'mjpeg', - }); - expect(getFullMIMEString(vInfo)) - .to.be.a('string') - .and.equals('video/mp4; codecs="flac"'); - - }); - }); - - describe('AVI', () => { - it('detects AVI', () => { - /** @type {ProbeInfo} */ - let info = { - format: { format_name: 'avi' }, - streams: [ - { codec_type: 'video', codec_name: 'mpeg4', codec_tag_string: 'XVID' }, - { codec_type: 'audio', codec_name: 'mp3' }, - { codec_type: 'audio', codec_name: 'mp3' }, - ], - }; - expect(getShortMIMEString(info)) - .to.be.a('string') - .and.equals('video/x-msvideo'); - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.equals('video/x-msvideo'); - }); - }); - - describe('MP3', () => { - it('detects MP3', () => { - /** @type {ProbeInfo} */ - let info = { - format: { format_name: 'mov,mp4,m4a,3gp,3g2,mj2' }, - streams: [ { codec_type: 'audio', codec_name: 'mp3', codec_tag_string: 'mp4a' } ], - }; - expect(getShortMIMEString(info)).equals('audio/mp4'); - expect(getFullMIMEString(info)).equals('audio/mp4; codecs="mp3"'); - }); - }); - - describe('WAV', () => { - it('detects WAV', () => { - /** @type {ProbeInfo} */ - let info = { - format: { format_name: 'wav' }, - streams: [ { codec_type: 'audio', codec_name: 'pcm_s16le' } ], - }; - expect(getShortMIMEString(info)) - .to.be.a('string') - .and.equals('audio/wav'); - expect(getFullMIMEString(info)) - .to.be.a('string') - .and.equals('audio/wav'); - }); - }); -}); diff --git a/tests/file-sniffer.spec.js b/tests/file-sniffer.spec.js deleted file mode 100644 index 6041e00..0000000 --- a/tests/file-sniffer.spec.js +++ /dev/null @@ -1,47 +0,0 @@ -/* - * file-sniffer.spec.js - * - * Licensed under the MIT License - * - * Copyright(c) 2021 Google Inc. - */ - -import { findMimeType } from '../file/sniffer.js'; -import 'mocha'; -import { expect } from 'chai'; - -function sniffTest(mimeType, sig) { - const array = new Uint8Array(16); - array.set(sig); - expect(findMimeType(array.buffer)).equals(mimeType); -} - -describe('bitjs.file.sniffer', () => { - it('handles unknown', () => { sniffTest(undefined, [0x01, 0x99, 0xde, 0xad])}); - it('BMP', () => { sniffTest('image/bmp', [0x42, 0x4D, 0x46]); }); - it('GIF', () => { sniffTest('image/gif', [0x47, 0x49, 0x46, 0x38, 0x20]); }); - it('JPG', () => { sniffTest('image/jpeg', [0xFF, 0xD8, 0xFF, 0x23]); }); - it('PNG', () => { sniffTest('image/png', [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0xFF]); }); - it('WebP', () => { sniffTest('image/webp', [0x52, 0x49, 0x46, 0x46, 0x01, 0x02, 0x03, 0x04, 0x57, 0x45, 0x42, 0x50, 0x81]); }); - it('ICO', () => { sniffTest('image/x-icon', [0x00, 0x00, 0x01, 0x00, 0x69])}); - it('GZIP', () => { sniffTest('application/gzip', [0x1F, 0x8B, 0x08])}); - it('ZIP', () => { sniffTest('application/zip', [0x50, 0x4B, 0x03, 0x04, 0x20]); }); - it('ZIP_empty', () => { sniffTest('application/zip', [0x50, 0x4B, 0x05, 0x06, 0x20]); }); - it('ZIP_spanned', () => { sniffTest('application/zip', [0x50, 0x4B, 0x07, 0x08, 0x20]); }); - it('7Z()', () => { sniffTest('application/x-7z-compressed', [0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C, 0x2B]); }); - it('RAR()', () => { sniffTest('application/x-rar-compressed', [0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x23]); }); - it('BZ2()', () => { sniffTest('application/x-bzip2', [0x42, 0x5A, 0x68, 0x19]); }); - it('PDF()', () => { sniffTest('application/pdf', [0x25, 0x50, 0x44, 0x46, 0x2d, 0x55]); }); - it('MP3_1()', () => { sniffTest('audio/mpeg', [0xFF, 0xFB]); }); - it('MP3_2()', () => { sniffTest('audio/mpeg', [0xFF, 0xF3]); }); - it('MP3_3()', () => { sniffTest('audio/mpeg', [0xFF, 0xF2]); }); - it('MP3_4()', () => { sniffTest('audio/mpeg', [0x49, 0x44, 0x33]); }); - it('OGG', () => { sniffTest('application/ogg', [0x4F, 0x67, 0x67, 0x53]); }); - it('TAR_1', () => { sniffTest('application/x-tar', [0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x30, 0x30]); }); - it('TAR_2', () => { sniffTest('application/x-tar', [0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00]); }); - it('FLAC', () => { sniffTest('audio/flac', [0x66, 0x4C, 0x61, 0x43]); }); - it('WAV', () => { sniffTest('audio/wav', [0x52, 0x49, 0x46, 0x46, 0x01, 0x02, 0x03, 0x04, 0x57, 0x41, 0x56, 0x45, 0x81]); }); - it('AVI', () => { sniffTest('video/avi', [0x52, 0x49, 0x46, 0x46, 0x01, 0x02, 0x03, 0x04, 0x41, 0x56, 0x49, 0x20, 0x81]); }); - it('WOFF', () => { sniffTest('font/woff', [0x77, 0x4F, 0x46, 0x46, 0x80]); }); - it('WOFF2', () => { sniffTest('font/woff2', [0x77, 0x4F, 0x46, 0x32, 0x80]); }); -}); diff --git a/tests/image-parsers-gif.spec.js b/tests/image-parsers-gif.spec.js deleted file mode 100644 index fc61a9f..0000000 --- a/tests/image-parsers-gif.spec.js +++ /dev/null @@ -1,56 +0,0 @@ -import * as fs from 'node:fs'; -import 'mocha'; -import { expect } from 'chai'; -import { GifParser } from '../image/parsers/gif.js'; - -const COMMENT_GIF = `tests/image-testfiles/comment.gif`; -const XMP_GIF = 'tests/image-testfiles/xmp.gif'; - -describe('bitjs.image.parsers.GifParser', () => { - it('parses GIF with Comment Extension', async () => { - const nodeBuf = fs.readFileSync(COMMENT_GIF); - const ab = nodeBuf.buffer.slice(nodeBuf.byteOffset, nodeBuf.byteOffset + nodeBuf.length); - - const parser = new GifParser(ab); - let trailerFound = false; - let comment; - parser.onLogicalScreen(evt => { - const {logicalScreenWidth, logicalScreenHeight} = evt.detail; - expect(logicalScreenWidth).equals(32); - expect(logicalScreenHeight).equals(52); - }); - parser.onTableBasedImage(evt => { - const {imageWidth, imageHeight} = evt.detail; - expect(imageWidth).equals(32); - expect(imageHeight).equals(52); - }); - parser.onCommentExtension(evt => comment = evt.detail); - parser.onTrailer(evt => trailerFound = true); - - await parser.start(); - - expect(trailerFound).equals(true); - expect(comment).equals('JEFF!'); - }); - - it('parses GIF with Application Extension', async () => { - const nodeBuf = fs.readFileSync(XMP_GIF); - const ab = nodeBuf.buffer.slice(nodeBuf.byteOffset, nodeBuf.byteOffset + nodeBuf.length); - - const parser = new GifParser(ab); - let appId; - let appAuthCode; - let hasAppData = false; - parser.onApplicationExtension(evt => { - appId = evt.detail.applicationIdentifier - appAuthCode = new TextDecoder().decode( - evt.detail.applicationAuthenticationCode); - hasAppData = evt.detail.applicationData.byteLength > 0; - }); - - await parser.start(); - - expect(appId).equals("XMP Data"); - expect(appAuthCode).equals('XMP'); - }); -}); diff --git a/tests/image-parsers-jpeg.spec.js b/tests/image-parsers-jpeg.spec.js deleted file mode 100644 index 66b05ec..0000000 --- a/tests/image-parsers-jpeg.spec.js +++ /dev/null @@ -1,39 +0,0 @@ -import * as fs from 'node:fs'; -import 'mocha'; -import { expect } from 'chai'; -import { JpegParser } from '../image/parsers/jpeg.js'; -import { ExifDataFormat, ExifTagNumber } from '../image/parsers/exif.js'; - -/** @typedef {import('../image/parsers/jpeg.js').JpegStartOfFrame} JpegStartOfFrame */ -/** @typedef {import('../image/parsers/exif.js').ExifValue} ExifValue */ - -const FILE_LONG_DESC = 'tests/image-testfiles/long_description.jpg' - -describe('bitjs.image.parsers.JpegParser', () => { - it('extracts Exif and SOF', async () => { - const nodeBuf = fs.readFileSync(FILE_LONG_DESC); - const ab = nodeBuf.buffer.slice(nodeBuf.byteOffset, nodeBuf.byteOffset + nodeBuf.length); - - /** @type {Map { exif = evt.detail }) - .onStartOfFrame(evt => { sof = evt.detail }); - await parser.start(); - - const descVal = exif.get(ExifTagNumber.IMAGE_DESCRIPTION); - expect(descVal.dataFormat).equals(ExifDataFormat.ASCII_STRING); - - const LONG_DESC = 'Operation Mountain Viper put the soldiers of A Company, 2nd Battalion 22nd ' - + 'Infantry Division, 10th Mountain in the Afghanistan province of Daychopan to search for ' - + 'Taliban and or weapon caches that could be used against U.S. and allied forces. Soldiers ' - + 'quickly walk to the ramp of the CH-47 Chinook cargo helicopter that will return them to ' - + 'Kandahar Army Air Field. (U.S. Army photo by Staff Sgt. Kyle Davis) (Released)'; - expect(descVal.stringValue).equals(LONG_DESC); - expect(exif.get(ExifTagNumber.EXIF_IMAGE_HEIGHT).numericalValue).equals(sof.imageHeight); - expect(exif.get(ExifTagNumber.EXIF_IMAGE_WIDTH).numericalValue).equals(sof.imageWidth); - }); -}); diff --git a/tests/image-parsers-png.spec.js b/tests/image-parsers-png.spec.js deleted file mode 100644 index c197446..0000000 --- a/tests/image-parsers-png.spec.js +++ /dev/null @@ -1,321 +0,0 @@ -import * as fs from 'node:fs'; -import 'mocha'; -import { expect } from 'chai'; -import { PngColorType, PngInterlaceMethod, PngUnitSpecifier, PngParser } from '../image/parsers/png.js'; -import { ExifDataFormat, ExifTagNumber } from '../image/parsers/exif.js'; - -/** @typedef {import('../image/parsers/exif.js').ExifValue} ExifValue */ - -/** @typedef {import('../image/parsers/png.js').PngBackgroundColor} PngBackgroundColor */ -/** @typedef {import('../image/parsers/png.js').PngChromaticities} PngChromaticies */ -/** @typedef {import('../image/parsers/png.js').PngCompressedTextualData} PngCompressedTextualData */ -/** @typedef {import('../image/parsers/png.js').PngHistogram} PngHistogram */ -/** @typedef {import('../image/parsers/png.js').PngImageData} PngImageData */ -/** @typedef {import('../image/parsers/png.js').PngImageGamma} PngImageGamma */ -/** @typedef {import('../image/parsers/png.js').PngImageHeader} PngImageHeader */ -/** @typedef {import('../image/parsers/png.js').PngIntlTextualData} PngIntlTextualData */ -/** @typedef {import('../image/parsers/png.js').PngLastModTime} PngLastModTime */ -/** @typedef {import('../image/parsers/png.js').PngPalette} PngPalette */ -/** @typedef {import('../image/parsers/png.js').PngPhysicalPixelDimensions} PngPhysicalPixelDimensions */ -/** @typedef {import('../image/parsers/png.js').PngSignificantBits} PngSignificantBits */ -/** @typedef {import('../image/parsers/png.js').PngSuggestedPalette} PngSuggestedPalette */ -/** @typedef {import('../image/parsers/png.js').PngTextualData} PngTextualData */ -/** @typedef {import('../image/parsers/png.js').PngTransparency} PngTransparency */ - -function getPngParser(fileName) { - const nodeBuf = fs.readFileSync(fileName); - const ab = nodeBuf.buffer.slice(nodeBuf.byteOffset, nodeBuf.byteOffset + nodeBuf.length); - return new PngParser(ab); -} - -describe('bitjs.image.parsers.PngParser', () => { - describe('IHDR', () => { - it('extracts IHDR', async () => { - /** @type {PngImageHeader} */ - let header; - - await getPngParser('tests/image-testfiles/PngSuite.png') - .onImageHeader(evt => { header = evt.detail }) - .start(); - - expect(header.width).equals(256); - expect(header.height).equals(256); - expect(header.bitDepth).equals(8); - expect(header.colorType).equals(PngColorType.TRUE_COLOR); - expect(header.compressionMethod).equals(0); - expect(header.filterMethod).equals(0); - expect(header.interlaceMethod).equals(PngInterlaceMethod.NO_INTERLACE); - }); - - it('throws on corrupt signature', async () => { - /** @type {PngImageHeader} */ - let header; - - try { - await getPngParser('tests/image-testfiles/xs1n0g01.png') - .onImageHeader(evt => { header = evt.detail }) - .start(); - throw new Error(`PngParser did not throw an error for corrupt PNG signature`); - } catch (err) { - expect(err.startsWith('Bad PNG signature')).equals(true); - } - }); - }); - - it('extracts gAMA', async () => { - /** @type {number} */ - let gamma; - await getPngParser('tests/image-testfiles/g05n3p04.png') - .onGamma(evt => gamma = evt.detail) - .start(); - expect(gamma).equals(55000); - }); - - it('extracts sBIT', async () => { - /** @type {PngSignificantBits} */ - let sBits; - await getPngParser('tests/image-testfiles/cs3n2c16.png') - .onSignificantBits(evt => sBits = evt.detail) - .start(); - expect(sBits.significant_red).equals(13); - expect(sBits.significant_green).equals(13); - expect(sBits.significant_blue).equals(13); - expect(sBits.significant_greyscale).equals(undefined); - expect(sBits.significant_alpha).equals(undefined); - }); - - it('extracts cHRM', async () => { - /** @type {PngChromaticies} */ - let chromaticities; - await getPngParser('tests/image-testfiles/ccwn2c08.png') - .onChromaticities(evt => chromaticities = evt.detail) - .start(); - expect(chromaticities.whitePointX).equals(31270); - expect(chromaticities.whitePointY).equals(32900); - expect(chromaticities.redX).equals(64000); - expect(chromaticities.redY).equals(33000); - expect(chromaticities.greenX).equals(30000); - expect(chromaticities.greenY).equals(60000); - expect(chromaticities.blueX).equals(15000); - expect(chromaticities.blueY).equals(6000); - }); - - it('extracts PLTE', async () => { - /** @type {PngPalette} */ - let palette; - await getPngParser('tests/image-testfiles/tbbn3p08.png') - .onPalette(evt => palette = evt.detail) - .start(); - expect(palette.entries.length).equals(246); - const entry = palette.entries[1]; - expect(entry.red).equals(128); - expect(entry.green).equals(86); - expect(entry.blue).equals(86); - }); - - describe('tRNS', () => { - it('extracts alpha palette', async () => { - /** @type {PngTransparency} */ - let transparency; - await getPngParser('tests/image-testfiles/tbbn3p08.png') - .onTransparency(evt => transparency = evt.detail) - .start(); - - expect(transparency.alphaPalette.length).equals(1); - expect(transparency.alphaPalette[0]).equals(0); - }); - - it('extracts 8-bit RGB transparency', async () => { - /** @type {PngTransparency} */ - let transparency; - await getPngParser('tests/image-testfiles/tbrn2c08.png') - .onTransparency(evt => transparency = evt.detail) - .start(); - - expect(transparency.redSampleValue).equals(255); - expect(transparency.blueSampleValue).equals(255); - expect(transparency.greenSampleValue).equals(255); - }); - - it('extracts 16-bit RGB transparency', async () => { - /** @type {PngTransparency} */ - let transparency; - await getPngParser('tests/image-testfiles/tbgn2c16.png') - .onTransparency(evt => transparency = evt.detail) - .start(); - - expect(transparency.redSampleValue).equals(65535); - expect(transparency.blueSampleValue).equals(65535); - expect(transparency.greenSampleValue).equals(65535); - }); - }); - - it('extracts IDAT', async () => { - /** @type {PngImageData} */ - let data; - - await getPngParser('tests/image-testfiles/PngSuite.png') - .onImageData(evt => { data = evt.detail }) - .start(); - - expect(data.rawImageData.byteLength).equals(2205); - expect(data.rawImageData[0]).equals(120); - }); - - it('extracts tEXt', async () => { - /** @type {PngTextualData[]} */ - let textualDataArr = []; - - await getPngParser('tests/image-testfiles/ctzn0g04.png') - .onTextualData(evt => { textualDataArr.push(evt.detail) }) - .start(); - - expect(textualDataArr.length).equals(2); - expect(textualDataArr[0].keyword).equals('Title'); - expect(textualDataArr[0].textString).equals('PngSuite'); - expect(textualDataArr[1].keyword).equals('Author'); - expect(textualDataArr[1].textString).equals('Willem A.J. van Schaik\n(willem@schaik.com)'); - }); - - it('extracts zTXt', async () => { - /** @type {PngCompressedTextualData} */ - let data; - - await getPngParser('tests/image-testfiles/ctzn0g04.png') - .onCompressedTextualData(evt => { data = evt.detail }) - .start(); - - expect(data.keyword).equals('Disclaimer'); - expect(data.compressionMethod).equals(0); - expect(data.compressedText.byteLength).equals(17); - - const blob = new Blob([data.compressedText.buffer]); - const decompressedStream = blob.stream().pipeThrough(new DecompressionStream('deflate')); - const decompressedText = await new Response(decompressedStream).text(); - expect(decompressedText).equals('Freeware.'); - }); - - it('extracts iTXt', async () => { - /** @type {PngIntlTextualData[]} */ - let data = []; - - await getPngParser('tests/image-testfiles/ctjn0g04.png') - .onIntlTextualData(evt => { data.push(evt.detail) }) - .start(); - - expect(data.length).equals(6); - expect(data[1].keyword).equals('Author'); - expect(data[1].compressionFlag).equals(0) - expect(data[5].keyword).equals('Disclaimer'); - // TODO: Test this better! - }); - - describe('bKGD', () => { - it('greyscale', async () => { - /** @type {PngBackgroundColor} */ - let bc; - await getPngParser('tests/image-testfiles/bggn4a16.png') - .onBackgroundColor(evt => { bc = evt.detail }) - .start(); - expect(bc.greyscale).equals(43908); - }); - - it('rgb', async () => { - /** @type {PngBackgroundColor} */ - let bc; - await getPngParser('tests/image-testfiles/tbrn2c08.png') - .onBackgroundColor(evt => { bc = evt.detail }) - .start(); - expect(bc.red).equals(255); - expect(bc.green).equals(0); - expect(bc.blue).equals(0); - }); - - it('paletteIndex', async () => { - /** @type {PngBackgroundColor} */ - let bc; - await getPngParser('tests/image-testfiles/tbbn3p08.png') - .onBackgroundColor(evt => { bc = evt.detail }) - .start(); - expect(bc.paletteIndex).equals(245); - }); - }); - - it('extracts tIME', async () => { - /** @type {PngLastModTime} */ - let lastModTime; - await getPngParser('tests/image-testfiles/cm9n0g04.png') - .onLastModTime(evt => { lastModTime = evt.detail }) - .start(); - expect(lastModTime.year).equals(1999); - expect(lastModTime.month).equals(12); - expect(lastModTime.day).equals(31); - expect(lastModTime.hour).equals(23); - expect(lastModTime.minute).equals(59); - expect(lastModTime.second).equals(59); - }); - - it('extracts pHYs', async () => { - /** @type {PngPhysicalPixelDimensions} */ - let pixelDims; - await getPngParser('tests/image-testfiles/cdun2c08.png') - .onPhysicalPixelDimensions(evt => { pixelDims = evt.detail }) - .start(); - expect(pixelDims.pixelPerUnitX).equals(1000); - expect(pixelDims.pixelPerUnitY).equals(1000); - expect(pixelDims.unitSpecifier).equals(PngUnitSpecifier.METRE); - }); - - it('extracts eXIf', async () => { - /** @type {PngPhysicalPixelDimensions} */ - let exif; - await getPngParser('tests/image-testfiles/exif2c08.png') - .onExifProfile(evt => { exif = evt.detail }) - .start(); - - const descVal = exif.get(ExifTagNumber.COPYRIGHT); - expect(descVal.dataFormat).equals(ExifDataFormat.ASCII_STRING); - expect(descVal.stringValue).equals('2017 Willem van Schaik'); - }); - - it('extracts hIST', async () => { - /** @type {PngPalette} */ - let palette; - /** @type {PngHistogram} */ - let hist; - await getPngParser('tests/image-testfiles/ch1n3p04.png') - .onHistogram(evt => { hist = evt.detail }) - .onPalette(evt => { palette = evt.detail }) - .start(); - - expect(hist.frequencies.length).equals(palette.entries.length); - expect(hist.frequencies[0]).equals(64); - expect(hist.frequencies[1]).equals(112); - }); - - it('extracts sPLT', async () => { - /** @type {PngSuggestedPalette} */ - let sPalette; - await getPngParser('tests/image-testfiles/ps1n0g08.png') - .onSuggestedPalette(evt => { sPalette = evt.detail }) - .start(); - - expect(sPalette.entries.length).equals(216); - expect(sPalette.paletteName).equals('six-cube'); - expect(sPalette.sampleDepth).equals(8); - - const entry0 = sPalette.entries[0]; - expect(entry0.red).equals(0); - expect(entry0.green).equals(0); - expect(entry0.blue).equals(0); - expect(entry0.alpha).equals(255); - expect(entry0.frequency).equals(0); - - const entry1 = sPalette.entries[1]; - expect(entry1.red).equals(0); - expect(entry1.green).equals(0); - expect(entry1.blue).equals(51); - expect(entry1.alpha).equals(255); - expect(entry1.frequency).equals(0); - }); -}); diff --git a/tests/image-testfiles/PngSuite.png b/tests/image-testfiles/PngSuite.png deleted file mode 100644 index 205460d..0000000 Binary files a/tests/image-testfiles/PngSuite.png and /dev/null differ diff --git a/tests/image-testfiles/bggn4a16.png b/tests/image-testfiles/bggn4a16.png deleted file mode 100644 index 13fd85b..0000000 Binary files a/tests/image-testfiles/bggn4a16.png and /dev/null differ diff --git a/tests/image-testfiles/ccwn2c08.png b/tests/image-testfiles/ccwn2c08.png deleted file mode 100644 index 47c2481..0000000 Binary files a/tests/image-testfiles/ccwn2c08.png and /dev/null differ diff --git a/tests/image-testfiles/cdun2c08.png b/tests/image-testfiles/cdun2c08.png deleted file mode 100644 index 846033b..0000000 Binary files a/tests/image-testfiles/cdun2c08.png and /dev/null differ diff --git a/tests/image-testfiles/ch1n3p04.png b/tests/image-testfiles/ch1n3p04.png deleted file mode 100644 index 17cd12d..0000000 Binary files a/tests/image-testfiles/ch1n3p04.png and /dev/null differ diff --git a/tests/image-testfiles/cm9n0g04.png b/tests/image-testfiles/cm9n0g04.png deleted file mode 100644 index dd70911..0000000 Binary files a/tests/image-testfiles/cm9n0g04.png and /dev/null differ diff --git a/tests/image-testfiles/comment.gif b/tests/image-testfiles/comment.gif deleted file mode 100644 index c7ea4d0..0000000 Binary files a/tests/image-testfiles/comment.gif and /dev/null differ diff --git a/tests/image-testfiles/cs3n2c16.png b/tests/image-testfiles/cs3n2c16.png deleted file mode 100644 index bf5fd20..0000000 Binary files a/tests/image-testfiles/cs3n2c16.png and /dev/null differ diff --git a/tests/image-testfiles/ctjn0g04.png b/tests/image-testfiles/ctjn0g04.png deleted file mode 100644 index a77b8d2..0000000 Binary files a/tests/image-testfiles/ctjn0g04.png and /dev/null differ diff --git a/tests/image-testfiles/ctzn0g04.png b/tests/image-testfiles/ctzn0g04.png deleted file mode 100644 index b4401c9..0000000 Binary files a/tests/image-testfiles/ctzn0g04.png and /dev/null differ diff --git a/tests/image-testfiles/exif2c08.png b/tests/image-testfiles/exif2c08.png deleted file mode 100644 index 56eb734..0000000 Binary files a/tests/image-testfiles/exif2c08.png and /dev/null differ diff --git a/tests/image-testfiles/g05n3p04.png b/tests/image-testfiles/g05n3p04.png deleted file mode 100644 index 9619930..0000000 Binary files a/tests/image-testfiles/g05n3p04.png and /dev/null differ diff --git a/tests/image-testfiles/long_description.jpg b/tests/image-testfiles/long_description.jpg deleted file mode 100644 index c5dfe67..0000000 Binary files a/tests/image-testfiles/long_description.jpg and /dev/null differ diff --git a/tests/image-testfiles/ps1n0g08.png b/tests/image-testfiles/ps1n0g08.png deleted file mode 100644 index 99625fa..0000000 Binary files a/tests/image-testfiles/ps1n0g08.png and /dev/null differ diff --git a/tests/image-testfiles/tbbn3p08.png b/tests/image-testfiles/tbbn3p08.png deleted file mode 100644 index 0ede357..0000000 Binary files a/tests/image-testfiles/tbbn3p08.png and /dev/null differ diff --git a/tests/image-testfiles/tbgn2c16.png b/tests/image-testfiles/tbgn2c16.png deleted file mode 100644 index 85cec39..0000000 Binary files a/tests/image-testfiles/tbgn2c16.png and /dev/null differ diff --git a/tests/image-testfiles/tbrn2c08.png b/tests/image-testfiles/tbrn2c08.png deleted file mode 100644 index 5cca0d6..0000000 Binary files a/tests/image-testfiles/tbrn2c08.png and /dev/null differ diff --git a/tests/image-testfiles/xmp.gif b/tests/image-testfiles/xmp.gif deleted file mode 100644 index b6d8411..0000000 Binary files a/tests/image-testfiles/xmp.gif and /dev/null differ diff --git a/tests/image-testfiles/xs1n0g01.png b/tests/image-testfiles/xs1n0g01.png deleted file mode 100644 index 1817c51..0000000 Binary files a/tests/image-testfiles/xs1n0g01.png and /dev/null differ diff --git a/tests/io-bitbuffer.spec.js b/tests/io-bitbuffer.spec.js deleted file mode 100644 index 295e24f..0000000 --- a/tests/io-bitbuffer.spec.js +++ /dev/null @@ -1,146 +0,0 @@ -/* - * io-bitbuffer.spec.js - * - * Licensed under the MIT License - * - * Copyright(c) 2021 Google Inc. - */ - -import { BitBuffer } from '../io/bitbuffer.js'; -import 'mocha'; -import { expect } from 'chai'; - -describe('bitjs.io.BitBuffer', () => { - /** @type {BitBuffer} */ - let buffer; - - it('throws when invalid numBytes', () => { - expect(() => new BitBuffer()).throws(); - }); - - describe('least-to-most-significant bit-packing', () => { - beforeEach(() => { - buffer = new BitBuffer(4); - }); - - it('bit/byte pointers initialized properly', () => { - expect(buffer.getPackingDirection()).equals(false); - expect(buffer.bytePtr).equals(0); - expect(buffer.bitPtr).equals(0); - }); - - it('throws when writing invalid values', () => { - expect(() => buffer.writeBits(-3, 2)).throws(); - expect(() => buffer.writeBits(3, -2)).throws(); - expect(() => buffer.writeBits(0, 54)).throws(); - }); - - it('throws when writing too many bits into the buffer', () => { - buffer.writeBits(0, 31); // thirty-one zeroes. - expect(() => buffer.writeBits(1, 2)).throws(); - }); - - it('write multiple bits', () => { - buffer.writeBits(0b01011, 5); // Should result in: 0b00001011. - expect(buffer.bytePtr).equals(0); - expect(buffer.bitPtr).equals(0 + 5); - expect(Array.from(buffer.data)).to.have.ordered.members([1 + 2 + 8, 0, 0, 0]); - }); - - it('write multiple bits across byte boundary', () => { - buffer.writeBits(0b01011, 5); - buffer.writeBits(0b11101, 5); // Should result in: 0b10101011 and 0b00000011. - expect(buffer.bytePtr).equals(Math.floor((5 + 5) / 8)); - expect(buffer.bitPtr).equals((5 + 5) % 8); - expect(Array.from(buffer.data)).to.have.ordered.members( - [1 + 2 + 8 + 32 + 128, 1 + 2, 0, 0]); - }); - - it('write multiple bytes to buffer', () => { - buffer.writeBits(0, 1); - buffer.writeBits(0x1ffff, 17); // Should result in 0b111111110, 0b11111111, 0b00000011. - expect(buffer.bytePtr).equals(2); - expect(buffer.bitPtr).equals(2); - expect(Array.from(buffer.data)).to.have.ordered.members( - [0xfe, 0xff, 0x03, 0x00]); - }); - - it('properly changes bit-packing direction', () => { - buffer.writeBits(3, 2); - expect(buffer.data[0]).equals(3); - expect(buffer.bytePtr).equals(0); - expect(buffer.bitPtr).equals(2); - - buffer.setPackingDirection(true /** most to least significant */); - expect(buffer.bytePtr).equals(1); - expect(buffer.bitPtr).equals(7); - - buffer.writeBits(7, 3); - expect(buffer.data[0]).equals(3); - expect(buffer.data[1]).equals(224); - }); - - it('throws when switching packing direction and no more bytes left', () => { - buffer.writeBits(0, 25); - expect(() => buffer.setPackingDirection(true)).throws(); - }); - }); - - describe('most-to-least-significant bit-packing', () => { - beforeEach(() => { - buffer = new BitBuffer(4, true); - }); - - it('bit/byte pointers initialized properly', () => { - expect(buffer.getPackingDirection()).equals(true); - expect(buffer.bytePtr).equals(0); - expect(buffer.bitPtr).equals(7); - }) - - it('write multiple bits', () => { - buffer.writeBits(0b01011, 5); // Should result in: 0b01011000 - expect(buffer.bytePtr).equals(0); - expect(buffer.bitPtr).equals(7 - 5); - expect(Array.from(buffer.data)).to.have.ordered.members( - [64+16+8, 0, 0, 0]); - }); - - it('write multiple bits across byte boundary', () => { - buffer.writeBits(0b01011, 5); - buffer.writeBits(0b11101, 5); // Should result in: 0b01011111 and 0b01000000. - expect(buffer.bytePtr).equals(Math.floor((5 + 5) / 8)); - expect(buffer.bitPtr).equals(7 - ((5 + 5) % 8)); - expect(Array.from(buffer.data)).to.have.ordered.members( - [64+16+8+4+2+1, 64, 0, 0]); - }); - - it('write multiple bytes to buffer', () => { - buffer.writeBits(0, 1); - buffer.writeBits(0x1ffff, 17); // Should result in 0b011111111, 0b11111111, 0b11000000. - expect(buffer.bytePtr).equals(2); - expect(buffer.bitPtr).equals(5); - expect(Array.from(buffer.data)).to.have.ordered.members( - [0x7f, 0xff, 0xc0, 0x00]); - }); - - it('properly changes bit-packing direction', () => { - buffer.writeBits(3, 2); - expect(buffer.bytePtr).equals(0); - expect(buffer.bitPtr).equals(5); - expect(buffer.data[0]).equals(192); - - buffer.setPackingDirection(false /** least to most significant */); - expect(buffer.bytePtr).equals(1); - expect(buffer.bitPtr).equals(0); - - buffer.writeBits(7, 3); - expect(buffer.data[0]).equals(192); - expect(buffer.data[1]).equals(7); - }); - - it('throws when switching packing direction and no more bytes left', () => { - buffer.writeBits(0, 25); - expect(() => buffer.setPackingDirection(false)).throws(); - }); - }); -}); diff --git a/tests/io-bitstream.spec.js b/tests/io-bitstream.spec.js deleted file mode 100644 index c38a377..0000000 --- a/tests/io-bitstream.spec.js +++ /dev/null @@ -1,170 +0,0 @@ -/* - * archive-test.js - * - * Licensed under the MIT License - * - * Copyright(c) 2021 Google Inc. - */ - -import { BitStream } from '../io/bitstream.js'; -import 'mocha'; -import { expect } from 'chai'; - -describe('bitjs.io.BitStream', () => { - let array; - beforeEach(() => { - array = new Uint8Array(4); - for (let i = 0; i < 4; ++i) { - array[i] = Number('0b01100101'); - } - }); - - it('throws an error without an ArrayBuffer', () => { - expect(() => new BitStream()).throws(); - }); - - describe('Most-to-Least', () => { - it('peek() and read()', () => { - const stream = new BitStream(array.buffer, true /** mtl */); - - expect(stream.peekBits(0)).equals(0); - expect(stream.peekBits(-1)).equals(0); - expect(stream.bytePtr).equals(0); - expect(stream.bitPtr).equals(0); - - // 0110 - expect(stream.readBits(4)).equals(0b0110); - expect(stream.getNumBitsRead()).equals(4); - - // 0101 011 - expect(stream.readBits(7)).equals(0b0101011); - // 00101 01100101 01 - expect(stream.readBits(15)).equals(0b001010110010101); - // 10010 - expect(stream.readBits(5)).equals(0b10010); - - // Ensure the last bit is read, even if we flow past the end of the stream. - expect(stream.readBits(2)).equals(1); - - expect(stream.getNumBitsRead()).equals(33); - }); - - it('skip() works correctly', () => { - const stream = new BitStream(array.buffer, true /** mtl */); - - expect(stream.skip(0)).equals(stream); - expect(stream.getNumBitsRead()).equals(0); - expect(stream.skip(3)).equals(stream); - expect(stream.getNumBitsRead()).equals(3); - expect(stream.readBits(3)).equals(0b001); - expect(stream.getNumBitsRead()).equals(6); - }); - - it('skip() works over byte boundary', () => { - const stream = new BitStream(array.buffer, true /** mtl */); - expect(stream.readBits(5)).equals(0b01100); - stream.skip(5); - expect(stream.getNumBitsRead()).equals(10); - expect(stream.readBits(5)).equals(0b10010); - }); - - it('skip() throws errors if overflowed', () => { - const stream = new BitStream(array.buffer, true /** mtl */); - expect(() => stream.skip(-1)).throws(); - stream.readBits(30); - expect(() => stream.skip(3)).throws(); - }); - }); - - describe('Least-to-Most', () => { - it('peek() and read()', () => { - /** @type {BitStream} */ - const stream = new BitStream(array.buffer, false /** mtl */); - - expect(stream.peekBits(0)).equals(0); - expect(stream.peekBits(-1)).equals(0); - expect(stream.bytePtr).equals(0); - expect(stream.bitPtr).equals(0); - - // 0101 - expect(stream.peekBits(4)).equals(0b0101); - expect(stream.readBits(4)).equals(0b0101); - // 101 0110 - expect(stream.readBits(7)).equals(0b1010110); - // 01 01100101 01100 - expect(stream.readBits(15)).equals(0b010110010101100); - // 11001 - expect(stream.readBits(5)).equals(0b11001); - - // Only 1 bit left in the buffer, make sure it reads in, even if we over-read. - expect(stream.readBits(2)).equals(0); - }); - - it('skip() works correctly', () => { - const stream = new BitStream(array.buffer, false /** mtl */); - - expect(stream.skip(0)).equals(stream); - expect(stream.getNumBitsRead()).equals(0); - expect(stream.skip(3)).equals(stream); - expect(stream.getNumBitsRead()).equals(3); - expect(stream.readBits(3)).equals(0b100); - expect(stream.getNumBitsRead()).equals(6); - }); - - it('skip() works over byte boundary', () => { - const stream = new BitStream(array.buffer, false /** mtl */); - expect(stream.readBits(5)).equals(0b00101); - stream.skip(5); - expect(stream.getNumBitsRead()).equals(10); - expect(stream.readBits(5)).equals(0b11001); - }); - - it('skip() throws errors if overflowed', () => { - const stream = new BitStream(array.buffer, false /** mtl */); - expect(() => stream.skip(-1)).throws(); - stream.readBits(30); - expect(() => stream.skip(3)).throws(); - }); - }); - - describe('bytes', () => { - it('peekBytes() and readBytes()', () => { - array[1] = Number('0b01010110'); - const stream = new BitStream(array.buffer); - - let twoBytes = stream.peekBytes(2); - expect(twoBytes instanceof Uint8Array).true; - expect(twoBytes.byteLength).equals(2); - expect(twoBytes[0]).equals(Number('0b01100101')); - expect(twoBytes[1]).equals(Number('0b01010110')); - - twoBytes = stream.readBytes(2); - expect(twoBytes instanceof Uint8Array).true; - expect(twoBytes.byteLength).equals(2); - expect(twoBytes[0]).equals(Number('0b01100101')); - expect(twoBytes[1]).equals(Number('0b01010110')); - - expect(() => stream.readBytes(3)).throws(); - }); - - it('peekBytes(0) returns an empty array', () => { - const stream = new BitStream(array.buffer); - const arr = stream.peekBytes(0); - expect(arr.byteLength).equals(0); - }); - - it('peekBytes() should ignore bits until byte-aligned', () => { - array[1] = Number('0b01010110'); - const stream = new BitStream(array.buffer); - stream.skip(3); - const bytes = stream.readBytes(2); - expect(bytes[0]).equals(0b01010110); - expect(bytes[1]).equals(0b01100101); - }) - - it('throws an error with weird values of peekBytes()', () => { - const stream = new BitStream(array.buffer); - expect(() => stream.peekBytes(-1)).throws(); - }); - }); -}); diff --git a/tests/io-bytebuffer.spec.js b/tests/io-bytebuffer.spec.js deleted file mode 100644 index cee2379..0000000 --- a/tests/io-bytebuffer.spec.js +++ /dev/null @@ -1,106 +0,0 @@ -/* - * archive-test.js - * - * Licensed under the MIT License - * - * Copyright(c) 2021 Google Inc. - */ - -import { ByteBuffer } from '../io/bytebuffer.js'; -import 'mocha'; -import { expect } from 'chai'; - -describe('bitjs.io.ByteBuffer', () => { - /** @type {ByteBuffer} */ - let buffer; - - beforeEach(() => { - buffer = new ByteBuffer(4); - }); - - describe('getData()', () => { - it('returns an empty array when nothing has been written', () => { - expect(buffer.getData().byteLength).equals(0); - }); - - it('is sized correctly', () => { - buffer.insertByte(42); - buffer.insertByte(81); - const data = buffer.getData(); - expect(data.byteLength).equals(2); - expect(data[0]).equals(42); - expect(data[1]).equals(81); - }); - }); - - it('throws when initialized incorrectly', () => { - expect(() => new ByteBuffer()).throws(); - }); - - describe('Buffer overflow', () => { - it('insertByte() throws when buffer exceeded', () => { - buffer.insertBytes([0, 2, 4, 6]); - expect(() => buffer.insertByte(1)).throws(); - }); - it('insertBytes() throws when buffer exceeded', () => { - expect(() => buffer.insertBytes([0, 2, 4, 6, 8])).throws(); - }); - }); - - it('insertByte()', () => { - buffer.insertByte(192); - expect(buffer.ptr).equals(1); - expect(buffer.data[0]).equals(192); - }); - - it('writeNumber() with a single unsigned byte', () => { - buffer.writeNumber(192, 1); - expect(buffer.ptr).equals(1); - expect(buffer.data[0]).equals(192); - }); - - it('writeNumber() with a single negative number', () => { - buffer.writeSignedNumber(-120, 1); - expect(buffer.ptr).equals(1); - expect(buffer.data[0]).equals(-120 & 0xff); - }); - - it('Write_MultiByteNumber', () => { - buffer.writeNumber(1234, 4); - expect(buffer.ptr).equals(4); - }); - - it('Write_MultiByteNegativeNumber', () => { - buffer.writeSignedNumber(-1234, 4); - expect(buffer.ptr).equals(4); - }); - - it('WriteOverflowUnsigned', () => { - expect(() => buffer.writeNumber(256, 1)).throws(); - }); - - it('WriteOverflowSignedPositive', () => { - expect(() => buffer.writeSignedNumber(128, 1)).throws(); - }); - - it('WriteOverflowSignedNegative', () => { - expect(() => buffer.writeSignedNumber(-129, 1)).throws(); - }); - - it('throws when trying to write invalid # of bytes', () => { - expect(() => buffer.writeNumber(3, -1)).throws(); - expect(() => buffer.writeNumber(-3, 1)).throws(); - expect(() => buffer.writeSignedNumber(-3, -1)).throws(); - }); - - it('writes an ASCII string', () => { - buffer.writeASCIIString('hi'); - expect(buffer.ptr).equals(2); - expect(buffer.data[0]).equals('h'.charCodeAt(0)); - expect(buffer.data[1]).equals('i'.charCodeAt(0)); - }); - - it('throws in a non-ASCII string', () => { - expect(() => buffer.writeASCIIString('Björk')).throws('Trying to write a non-ASCII string'); - }); -}); \ No newline at end of file diff --git a/tests/io-bytestream.spec.js b/tests/io-bytestream.spec.js deleted file mode 100644 index 740e771..0000000 --- a/tests/io-bytestream.spec.js +++ /dev/null @@ -1,290 +0,0 @@ -/* - * archive-test.js - * - * Licensed under the MIT License - * - * Copyright(c) 2021 Google Inc. - */ - -import { ByteStream } from '../io/bytestream.js'; -import 'mocha'; -import { expect } from 'chai'; - -describe('bitjs.io.ByteStream', () => { - - let array; - beforeEach(() => { - array = new Uint8Array(4); - }); - - it('throws an error without an ArrayBuffer', () => { - expect(() => new ByteStream()).throws(); - expect(() => new ByteStream(array.buffer).push()).throws(); - }); - - it('getNumBytesRead() works', () => { - const stream = new ByteStream(array.buffer); - expect(stream.getNumBytesRead()).equals(0); - stream.readBytes(1); - expect(stream.getNumBytesRead()).equals(1); - stream.readBytes(2); - expect(stream.getNumBytesRead()).equals(3); - }); - - it('throws when peeking a weird numbers of bytes', () => { - array[0] = 255; - const stream = new ByteStream(array.buffer); - expect(stream.peekNumber(0)).equals(0); - expect(() => stream.peekNumber(-2)).throws(); - expect(() => stream.peekNumber(5)).throws(); - - expect(stream.peekBytes(0).length).equals(0); - expect(() => stream.peekBytes(-1)).throws(); - - expect(stream.peekString(0)).equals(''); - expect(() => stream.peekString(-1)).throws(); - expect(() => stream.peekString(5)).throws(); - }); - - it('PeekAndRead_SingleByte', () => { - array[0] = 192; - const stream = new ByteStream(array.buffer); - expect(stream.peekNumber(1)).equals(192); - expect(stream.readNumber(1)).equals(192); - }); - - it('PeekAndRead_MultiByteNumber', () => { - array[0] = (1234 & 0xff); - array[1] = ((1234 >> 8) & 0xff); - const stream = new ByteStream(array.buffer); - expect(stream.peekNumber(4)).equals(1234); - expect(stream.readNumber(4)).equals(1234); - expect(() => stream.readNumber(1)).to.throw(); - }); - - it('PeekAndRead_MultiByteNumber_BigEndian', () => { - array[3] = (1234 & 0xff); - array[2] = ((1234 >> 8) & 0xff); - const stream = new ByteStream(array.buffer); - stream.setBigEndian(); - expect(stream.peekNumber(4)).equals(1234); - expect(stream.readNumber(4)).equals(1234); - expect(() => stream.readNumber(1)).to.throw(); - }); - - it('PeekAndRead_MultiByteNumber_MultiEndian', () => { - array[1] = 1; - array[3] = 1; - // Stream now has 0, 1, 0, 1. - const stream = new ByteStream(array.buffer); - stream.setBigEndian(); - expect(stream.peekNumber(2)).equals(1); - stream.setBigEndian(false); - expect(stream.peekNumber(2)).equals(256); - stream.setBigEndian(true); - expect(stream.peekNumber(2)).equals(1); - - stream.skip(2); - - stream.setLittleEndian(); - expect(stream.peekNumber(2)).equals(256); - stream.setLittleEndian(false); - expect(stream.peekNumber(2)).equals(1); - stream.setLittleEndian(true); - expect(stream.peekNumber(2)).equals(256); - - stream.skip(2); - - expect(() => stream.readNumber(1)).to.throw(); - }); - - it('PeekAndRead_SingleByteSignedNumber', () => { - array[0] = -120; - const stream = new ByteStream(array.buffer); - expect(stream.peekSignedNumber(1)).equals(-120); - expect(stream.readSignedNumber(1)).equals(-120); - }); - - it('PeekAndRead_SingleByteNegativeNumber', () => { - array[0] = -128; - const stream = new ByteStream(array.buffer); - expect(stream.peekSignedNumber(1)).equals(-128); - expect(stream.readSignedNumber(1)).equals(-128); - }); - - it('PeekAndRead_MultiByteSignedNumber', () => { - array[0] = (1234 & 0xff); - array[1] = ((1234 >> 8) & 0xff); - const stream = new ByteStream(array.buffer); - expect(stream.peekSignedNumber(2)).equals(1234); - expect(stream.peekSignedNumber(2)).equals(1234); - }); - - it('PeekAndRead_MultiByteNegativeNumber', () => { - array[0] = (-1234 & 0xff); - array[1] = ((-1234 >> 8) & 0xff); - const stream = new ByteStream(array.buffer); - expect(stream.peekSignedNumber(2)).equals(-1234); - expect(stream.peekSignedNumber(2)).equals(-1234); - }); - - it('ByteStreamReadBytesPastEnd', () => { - const stream = new ByteStream(array.buffer); - expect(() => stream.readBytes(5)).to.throw(); - }); - - it('ReadStringPastEnd', () => { - const stream = new ByteStream(array.buffer); - expect(() => stream.readString(5)).to.throw(); - }); - - it('PushThenReadNumber', () => { - array = new Uint8Array(1); - array[0] = (1234 & 0xff); - const stream = new ByteStream(array.buffer); - - const anotherArray = new Uint8Array(1); - anotherArray[0] = ((1234 >> 8) & 0xff); - stream.push(anotherArray.buffer); - - expect(stream.readNumber(2)).equals(1234); - }); - - it('ReadBytesThenPushThenReadByte', () => { - for (let i = 0; i < 4; ++i) array[i] = i; - const stream = new ByteStream(array.buffer); - - const bytes = stream.readBytes(4); - expect(() => stream.readBytes(1)).to.throw(); - - const anotherArray = new Uint8Array(1); - anotherArray[0] = 4; - stream.push(anotherArray.buffer); - - expect(stream.readNumber(1), 'Could not read in byte after pushing').equals(4); - }); - - it('PushThenReadBytesAcrossOnePage', () => { - for (let i = 0; i < 4; ++i) array[i] = i; - const stream = new ByteStream(array.buffer); - - const anotherArray = new Uint8Array(1); - anotherArray[0] = 4; - stream.push(anotherArray.buffer); - - const bytes = stream.readBytes(5); - expect(bytes.length).equals(5); - for (let i = 0; i < 5; ++i) { - expect(bytes[i]).equals(i); - } - }); - - it('PushThenReadBytesAcrossMultiplePages', () => { - for (let i = 0; i < 4; ++i) array[i] = i; - const stream = new ByteStream(array.buffer); - - const anotherArray = new Uint8Array(4); - for (let i = 0; i < 4; ++i) anotherArray[i] = i + 4; - - const yetAnotherArray = new Uint8Array(4); - for (let i = 0; i < 4; ++i) yetAnotherArray[i] = i + 8; - - stream.push(anotherArray.buffer); - stream.push(yetAnotherArray.buffer); - - const bytes = stream.readBytes(12); - expect(bytes.length).equals(12); - for (let i = 0; i < 12; ++i) { - expect(bytes[i]).equals(i); - } - }); - - it('PushThenReadStringAcrossMultiplePages', () => { - for (let i = 0; i < 4; ++i) array[i] = 65 + i; - const stream = new ByteStream(array.buffer); - - const anotherArray = new Uint8Array(4); - for (let i = 0; i < 4; ++i) anotherArray[i] = 69 + i; - - const yetAnotherArray = new Uint8Array(4); - for (let i = 0; i < 4; ++i) yetAnotherArray[i] = 73 + i; - - stream.push(anotherArray.buffer); - stream.push(yetAnotherArray.buffer); - - const str = stream.readString(12); - expect(str).equals('ABCDEFGHIJKL'); - - const str2 = stream.readString(0); - expect(str2).equals(''); - }); - - describe('skip()', () => { - /** @type {ByteStream} */ - let stream; - - beforeEach(() => { - for (let i = 0; i < 4; ++i) array[i] = i; - stream = new ByteStream(array.buffer); - }); - - it('skips bytes', () => { - stream.skip(2); - expect(stream.getNumBytesRead()).equals(2); - expect(stream.getNumBytesLeft()).equals(2); - expect(stream.readNumber(1)).equals(2); - expect(stream.readNumber(1)).equals(3); - }); - - it('returns itself', () => { - const retVal = stream.skip(2); - expect(stream === retVal).equals(true); - }); - - it('skip(0) has no effect', () => { - const retVal = stream.skip(0); - expect(stream.getNumBytesRead()).equals(0); - expect(stream.getNumBytesLeft()).equals(4); - expect(stream.readNumber(1)).equals(0); - expect(retVal === stream).equals(true); - }); - - it('skip() with negative throws an error', () => { - expect(() => stream.skip(-1)).throws(); - expect(stream.getNumBytesLeft()).equals(4); - }); - - it('skip() past the end throws an error', () => { - expect(() => stream.skip(4)).does.not.throw(); - expect(stream.getNumBytesLeft()).equals(0); - expect(() => stream.skip(1)).throws(); - }); - - it('skip() works correct across pages of bytes', () => { - const extraArr = new Uint8Array(4); - for (let i = 0; i < 4; ++i) extraArr[i] = i + 4; - stream.push(extraArr.buffer); - expect(stream.readNumber(1)).equals(0); - stream.skip(5); - expect(stream.readNumber(1)).equals(6); - }); - }); - - it('Tee', () => { - for (let i = 0; i < 4; ++i) array[i] = 65 + i; - const stream = new ByteStream(array.buffer); - // Set non-default endianness. - stream.setBigEndian(true); - - const anotherArray = new Uint8Array(4); - for (let i = 0; i < 4; ++i) anotherArray[i] = 69 + i; - stream.push(anotherArray.buffer); - - const teed = stream.tee(); - expect(teed !== stream).equals(true); - teed.readBytes(5); - expect(stream.getNumBytesLeft()).equals(8); - expect(teed.getNumBytesLeft()).equals(3); - expect(teed.isLittleEndian()).equals(stream.isLittleEndian()); - }); -}); diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 8a871a8..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - // Change this to match your project - "include": ["index.js"], - "exclude": [], - "compilerOptions": { - // Tells TypeScript to read JS files, as - // normally they are ignored as source files - "allowJs": true, - // Generate d.ts files - "declaration": true, - // This compiler run should - // only output d.ts files - "emitDeclarationOnly": true, - // Types should go into this directory. - // Removing this would place the .d.ts files - // next to the .js files - "outDir": "types", - // go to js file when using IDE functions like - // "Go to Definition" in VSCode - "declarationMap": true - } -} diff --git a/types/archive/archive.d.ts b/types/archive/archive.d.ts deleted file mode 100644 index 10d7710..0000000 --- a/types/archive/archive.d.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Factory method that creates an unarchiver based on the byte signature found - * in the arrayBuffer. - * @param {ArrayBuffer} ab The ArrayBuffer to unarchive. Note that this ArrayBuffer - * must not be referenced after calling this method, as the ArrayBuffer is marked - * as Transferable and sent to a Worker thread once start() is called. - * @param {Object|string} options An optional object of options, or a string - * representing where the path to the unarchiver script files. - * @returns {Unarchiver} - */ -export function getUnarchiver(ab: ArrayBuffer, options?: any | string): Unarchiver; -export class Unzipper extends UnzipperInternal { - constructor(ab: any, options: any); -} -export class Unrarrer extends UnrarrerInternal { - constructor(ab: any, options: any); -} -export class Untarrer extends UntarrerInternal { - constructor(ab: any, options: any); -} -export type UnarchivedFile = { - filename: string; - fileData: Uint8Array; -}; -import { Unarchiver } from "./decompress-internal.js"; -import { UnarchiveAppendEvent } from "./decompress-internal.js"; -import { UnarchiveErrorEvent } from "./decompress-internal.js"; -import { UnarchiveEvent } from "./decompress-internal.js"; -import { UnarchiveEventType } from "./decompress-internal.js"; -import { UnarchiveExtractEvent } from "./decompress-internal.js"; -import { UnarchiveFinishEvent } from "./decompress-internal.js"; -import { UnarchiveInfoEvent } from "./decompress-internal.js"; -import { UnarchiveProgressEvent } from "./decompress-internal.js"; -import { UnarchiveStartEvent } from "./decompress-internal.js"; -import { UnzipperInternal } from "./decompress-internal.js"; -import { UnrarrerInternal } from "./decompress-internal.js"; -import { UntarrerInternal } from "./decompress-internal.js"; -export { UnarchiveAppendEvent, UnarchiveErrorEvent, UnarchiveEvent, UnarchiveEventType, UnarchiveExtractEvent, UnarchiveFinishEvent, UnarchiveInfoEvent, UnarchiveProgressEvent, UnarchiveStartEvent, Unarchiver }; -//# sourceMappingURL=archive.d.ts.map \ No newline at end of file diff --git a/types/archive/archive.d.ts.map b/types/archive/archive.d.ts.map deleted file mode 100644 index 278860a..0000000 --- a/types/archive/archive.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../../archive/archive.js"],"names":[],"mappings":"AA4EA;;;;;;;;;GASG;AACH,kCAPW,WAAW,YAGX,MAAO,MAAM,GAEX,UAAU,CAKtB;AAzBD;IACE,mCAAwE;CACzE;AAED;IACE,mCAAwE;CACzE;AAED;IACE,mCAAwE;CACzE;;cArCa,MAAM;cACN,UAAU"} \ No newline at end of file diff --git a/types/archive/common.d.ts b/types/archive/common.d.ts deleted file mode 100644 index 531b4f7..0000000 --- a/types/archive/common.d.ts +++ /dev/null @@ -1,73 +0,0 @@ -/** - * common.js - * - * Provides common definitions or functionality needed by multiple modules. - * - * Licensed under the MIT License - * - * Copyright(c) 2023 Google Inc. - */ -/** - * @typedef FileInfo An object that is sent to the implementation representing a file to compress. - * @property {string} fileName The name of the file. TODO: Includes the path? - * @property {number} lastModTime The number of ms since the Unix epoch (1970-01-01 at midnight). - * @property {Uint8Array} fileData The bytes of the file. - */ -/** - * @typedef Implementation - * @property {MessagePort} hostPort The port the host uses to communicate with the implementation. - * @property {Function} disconnectFn A function to call when the port has been disconnected. - */ -/** - * Connects a host to a compress/decompress implementation via MessagePorts. The implementation must - * have an exported connect() function that accepts a MessagePort. If the runtime support Workers - * (e.g. web browsers, deno), imports the implementation inside a Web Worker. Otherwise, it - * dynamically imports the implementation inside the current JS context (node, bun). - * @param {string} implFilename The compressor/decompressor implementation filename relative to this - * path (e.g. './unzip.js'). - * @param {Function} disconnectFn A function to run when the port is disconnected. - * @returns {Promise} The Promise resolves to the Implementation, which includes the - * MessagePort connected to the implementation that the host should use. - */ -export function getConnectedPort(implFilename: string): Promise; -export const LOCAL_FILE_HEADER_SIG: 67324752; -export const CENTRAL_FILE_HEADER_SIG: 33639248; -export const END_OF_CENTRAL_DIR_SIG: 101010256; -export const CRC32_MAGIC_NUMBER: 3988292384; -export const ARCHIVE_EXTRA_DATA_SIG: 134630224; -export const DIGITAL_SIGNATURE_SIG: 84233040; -export const END_OF_CENTRAL_DIR_LOCATOR_SIG: 117853008; -export const DATA_DESCRIPTOR_SIG: 134695760; -export type ZipCompressionMethod = number; -export namespace ZipCompressionMethod { - const STORE: number; - const DEFLATE: number; -} -/** - * An object that is sent to the implementation representing a file to compress. - */ -export type FileInfo = { - /** - * The name of the file. TODO: Includes the path? - */ - fileName: string; - /** - * The number of ms since the Unix epoch (1970-01-01 at midnight). - */ - lastModTime: number; - /** - * The bytes of the file. - */ - fileData: Uint8Array; -}; -export type Implementation = { - /** - * The port the host uses to communicate with the implementation. - */ - hostPort: MessagePort; - /** - * A function to call when the port has been disconnected. - */ - disconnectFn: Function; -}; -//# sourceMappingURL=common.d.ts.map \ No newline at end of file diff --git a/types/archive/common.d.ts.map b/types/archive/common.d.ts.map deleted file mode 100644 index 802e73b..0000000 --- a/types/archive/common.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../archive/common.js"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;GAKG;AAEH;;;;GAIG;AAEH;;;;;;;;;;GAUG;AACH,+CANW,MAAM,GAGJ,QAAQ,cAAc,CAAC,CA0BnC;AAID,6CAAgD;AAChD,+CAAkD;AAClD,+CAAiD;AACjD,4CAA6C;AAC7C,+CAAiD;AACjD,6CAAgD;AAChD,uDAAyD;AACzD,4CAA8C;mCAIpC,MAAM;;;;;;;;;;;;cA5DF,MAAM;;;;iBACN,MAAM;;;;cACN,UAAU;;;;;;cAKV,WAAW"} \ No newline at end of file diff --git a/types/archive/decompress.d.ts b/types/archive/decompress.d.ts deleted file mode 100644 index 4059430..0000000 --- a/types/archive/decompress.d.ts +++ /dev/null @@ -1,197 +0,0 @@ -/** - * Factory method that creates an unarchiver based on the byte signature found - * in the ArrayBuffer. - * @param {ArrayBuffer} ab The ArrayBuffer to unarchive. Note that this ArrayBuffer - * must not be referenced after calling this method, as the ArrayBuffer may be - * transferred to a different JS context once start() is called. - * @param {UnarchiverOptions|string} options An optional object of options, or a - * string representing where the path to the unarchiver script files. The latter - * is now deprecated (use UnarchiverOptions). - * @returns {Unarchiver} - */ -export function getUnarchiver(ab: ArrayBuffer, options?: UnarchiverOptions | string): Unarchiver; -/** - * All extracted files returned by an Unarchiver will implement - * the following interface: - * TODO: Move this interface into common.js? - */ -/** - * @typedef UnarchivedFile - * @property {string} filename - * @property {Uint8Array} fileData - */ -/** - * @typedef UnarchiverOptions - * @property {boolean=} debug Set to true for verbose unarchiver logging. - */ -/** - * Base class for all Unarchivers. - */ -export class Unarchiver extends EventTarget { - /** - * @param {ArrayBuffer} arrayBuffer The Array Buffer. Note that this ArrayBuffer must not be - * referenced once it is sent to the Unarchiver, since it is marked as Transferable and sent - * to the decompress implementation. - * @param {UnarchiverOptions|string} options An optional object of options, or a string - * representing where the BitJS files are located. The string version of this argument is - * deprecated. - */ - constructor(arrayBuffer: ArrayBuffer, options?: UnarchiverOptions | string); - /** - * The client-side port that sends messages to, and receives messages from, the - * decompressor implementation. - * @type {MessagePort} - * @private - */ - private port_; - /** - * A function to call to disconnect the implementation from the host. - * @type {Function} - * @private - */ - private disconnectFn_; - /** - * The ArrayBuffer object. - * @type {ArrayBuffer} - * @protected - */ - protected ab: ArrayBuffer; - /** - * @orivate - * @type {boolean} - */ - debugMode_: boolean; - /** - * Overridden so that the type hints for eventType are specific. Prefer onExtract(), etc. - * @param {'progress'|'extract'|'finish'} eventType - * @param {EventListenerOrEventListenerObject} listener - * @override - */ - override addEventListener(eventType: 'progress' | 'extract' | 'finish', listener: EventListenerOrEventListenerObject): void; - /** - * Type-safe way to subscribe to an UnarchiveExtractEvent. - * @param {function(UnarchiveExtractEvent)} listener - * @returns {Unarchiver} for chaining. - */ - onExtract(listener: (arg0: UnarchiveExtractEvent) => any): Unarchiver; - /** - * Type-safe way to subscribe to an UnarchiveFinishEvent. - * @param {function(UnarchiveFinishEvent)} listener - * @returns {Unarchiver} for chaining. - */ - onFinish(listener: (arg0: UnarchiveFinishEvent) => any): Unarchiver; - /** - * Type-safe way to subscribe to an UnarchiveProgressEvent. - * @param {function(UnarchiveProgressEvent)} listener - * @returns {Unarchiver} for chaining. - */ - onProgress(listener: (arg0: UnarchiveProgressEvent) => any): Unarchiver; - /** - * This method must be overridden by the subclass to return the script filename. - * @returns {string} The MIME type of the archive. - * @protected. - */ - protected getMIMEType(): string; - /** - * This method must be overridden by the subclass to return the script filename. - * @returns {string} The script filename. - * @protected. - */ - protected getScriptFileName(): string; - /** - * Create an UnarchiveEvent out of the object sent back from the implementation. - * @param {Object} obj - * @returns {UnarchiveEvent} - * @private - */ - private createUnarchiveEvent_; - /** - * Receive an event and pass it to the listener functions. - * @param {Object} obj - * @returns {boolean} Returns true if the decompression is finished. - * @private - */ - private handlePortEvent_; - /** - * Starts the unarchive by connecting the ports and sending the first ArrayBuffer. - * @returns {Promise} A Promise that resolves when the decompression is complete. While the - * decompression is proceeding, you can send more bytes of the archive to the decompressor - * using the update() method. - */ - start(): Promise; - /** - * Adds more bytes to the unarchiver. - * @param {ArrayBuffer} ab The ArrayBuffer with more bytes in it. If opt_transferable is - * set to true, this ArrayBuffer must not be referenced after calling update(), since it - * is marked as Transferable and sent to the implementation. - * @param {boolean=} opt_transferable Optional boolean whether to mark this ArrayBuffer - * as a Tranferable object, which means it can no longer be referenced outside of - * the implementation context. - */ - update(ab: ArrayBuffer, opt_transferable?: boolean | undefined): void; - /** - * Closes the port to the decompressor implementation and terminates it. - */ - stop(): void; -} -export class Unzipper extends Unarchiver { - /** - * @param {ArrayBuffer} ab - * @param {UnarchiverOptions} options - */ - constructor(ab: ArrayBuffer, options?: UnarchiverOptions); -} -export class Unrarrer extends Unarchiver { - /** - * @param {ArrayBuffer} ab - * @param {UnarchiverOptions} options - */ - constructor(ab: ArrayBuffer, options?: UnarchiverOptions); -} -export class Untarrer extends Unarchiver { - /** - * @param {ArrayBuffer} ab - * @param {UnarchiverOptions} options - */ - constructor(ab: ArrayBuffer, options?: UnarchiverOptions); -} -/** - * IMPORTANT NOTES for Gunzipper: - * 1) A Gunzipper will only ever emit one EXTRACT event, because a gzipped file only ever contains - * a single file. - * 2) If the gzipped file does not include the original filename as a FNAME block, then the - * UnarchivedFile in the UnarchiveExtractEvent will not include a filename. It will be up to the - * client to re-assemble the filename (if needed). - * 3) update() is not supported on a Gunzipper, since the current implementation relies on runtime - * support for DecompressionStream('gzip') which can throw hard-to-detect errors reading only - * only part of a file. - * 4) PROGRESS events are not yet supported in Gunzipper. - */ -export class Gunzipper extends Unarchiver { - /** - * @param {ArrayBuffer} ab - * @param {UnarchiverOptions} options - */ - constructor(ab: ArrayBuffer, options?: UnarchiverOptions); -} -export type UnarchivedFile = { - filename: string; - fileData: Uint8Array; -}; -export type UnarchiverOptions = { - /** - * Set to true for verbose unarchiver logging. - */ - debug?: boolean | undefined; -}; -import { UnarchiveAppendEvent } from "./events.js"; -import { UnarchiveErrorEvent } from "./events.js"; -import { UnarchiveEvent } from "./events.js"; -import { UnarchiveEventType } from "./events.js"; -import { UnarchiveExtractEvent } from "./events.js"; -import { UnarchiveFinishEvent } from "./events.js"; -import { UnarchiveInfoEvent } from "./events.js"; -import { UnarchiveProgressEvent } from "./events.js"; -import { UnarchiveStartEvent } from "./events.js"; -export { UnarchiveAppendEvent, UnarchiveErrorEvent, UnarchiveEvent, UnarchiveEventType, UnarchiveExtractEvent, UnarchiveFinishEvent, UnarchiveInfoEvent, UnarchiveProgressEvent, UnarchiveStartEvent }; -//# sourceMappingURL=decompress.d.ts.map \ No newline at end of file diff --git a/types/archive/decompress.d.ts.map b/types/archive/decompress.d.ts.map deleted file mode 100644 index 6c2c8a8..0000000 --- a/types/archive/decompress.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"decompress.d.ts","sourceRoot":"","sources":["../../archive/decompress.js"],"names":[],"mappings":"AA4VA;;;;;;;;;;GAUG;AACH,kCARW,WAAW,YAGX,iBAAiB,GAAC,MAAM,GAGtB,UAAU,CAoBtB;AA3VD;;;;GAIG;AAEH;;;;GAIG;AAEH;;;GAGG;AAEH;;GAEG;AACH;IAgBE;;;;;;;OAOG;IACH,yBAPW,WAAW,YAGX,iBAAiB,GAAC,MAAM,EA0BlC;IA7CD;;;;;OAKG;IACH,cAAM;IAEN;;;;OAIG;IACH,sBAAc;IAoBZ;;;;OAIG;IACH,cAHU,WAAW,CAGA;IAErB;;;OAGG;IACH,YAFU,OAAO,CAEkB;IAGrC;;;;;OAKG;IACH,qCAJW,UAAU,GAAC,SAAS,GAAC,QAAQ,YAC7B,kCAAkC,QAK5C;IAED;;;;OAIG;IACH,2BAHoB,qBAAqB,WAC5B,UAAU,CAKtB;IAED;;;;OAIG;IACH,0BAHoB,oBAAoB,WAC3B,UAAU,CAKtB;IAED;;;;OAIG;IACH,4BAHoB,sBAAsB,WAC7B,UAAU,CAKtB;IAED;;;;OAIG;IACH,yBAHa,MAAM,CAKlB;IAED;;;;OAIG;IACH,+BAHa,MAAM,CAKlB;IAED;;;;;OAKG;IACH,8BAsBC;IAED;;;;;OAKG;IACH,yBAaC;IAED;;;;;OAKG;IACH,SAJa,QAAQ,IAAI,CAAC,CAgCzB;IAGD;;;;;;;;OAQG;IACH,WAPW,WAAW,qBAGX,OAAO,oBAgBjB;IAED;;OAEG;IACH,aAOC;CACF;AAID;IACE;;;OAGG;IACH,gBAHW,WAAW,YACX,iBAAiB,EAI3B;CAIF;AAED;IACE;;;OAGG;IACH,gBAHW,WAAW,YACX,iBAAiB,EAI3B;CAIF;AAED;IACE;;;OAGG;IACH,gBAHW,WAAW,YACX,iBAAiB,EAI3B;CAIF;AAED;;;;;;;;;;;GAWG;AACH;IACE;;;OAGG;IACH,gBAHW,WAAW,YACX,iBAAiB,EAI3B;CAIF;;cAlTa,MAAM;cACN,UAAU;;;;;;YAKV,OAAO"} \ No newline at end of file diff --git a/types/archive/events.d.ts b/types/archive/events.d.ts deleted file mode 100644 index 98fa19c..0000000 --- a/types/archive/events.d.ts +++ /dev/null @@ -1,88 +0,0 @@ -export namespace UnarchiveEventType { - const START: string; - const APPEND: string; - const PROGRESS: string; - const EXTRACT: string; - const FINISH: string; - const INFO: string; - const ERROR: string; -} -/** An unarchive event. */ -export class UnarchiveEvent extends Event { - /** - * @param {string} type The event type. - */ - constructor(type: string); -} -/** Updates all Unarchiver listeners that an append has occurred. */ -export class UnarchiveAppendEvent extends UnarchiveEvent { - /** - * @param {number} numBytes The number of bytes appended. - */ - constructor(numBytes: number); - /** - * The number of appended bytes. - * @type {number} - */ - numBytes: number; -} -/** Useful for passing info up to the client (for debugging). */ -export class UnarchiveInfoEvent extends UnarchiveEvent { - /** - * The information message. - * @type {string} - */ - msg: string; -} -/** An unrecoverable error has occured. */ -export class UnarchiveErrorEvent extends UnarchiveEvent { - /** - * The information message. - * @type {string} - */ - msg: string; -} -/** Start event. */ -export class UnarchiveStartEvent extends UnarchiveEvent { - constructor(); -} -/** Finish event. */ -export class UnarchiveFinishEvent extends UnarchiveEvent { - /** - * @param {Object} metadata A collection of metadata about the archive file. - */ - constructor(metadata?: any); - metadata: any; -} -/** Progress event. */ -export class UnarchiveProgressEvent extends UnarchiveEvent { - /** - * @param {string} currentFilename - * @param {number} currentFileNumber - * @param {number} currentBytesUnarchivedInFile - * @param {number} currentBytesUnarchived - * @param {number} totalUncompressedBytesInArchive - * @param {number} totalFilesInArchive - * @param {number} totalCompressedBytesRead - */ - constructor(currentFilename: string, currentFileNumber: number, currentBytesUnarchivedInFile: number, currentBytesUnarchived: number, totalUncompressedBytesInArchive: number, totalFilesInArchive: number, totalCompressedBytesRead: number); - currentFilename: string; - currentFileNumber: number; - currentBytesUnarchivedInFile: number; - totalFilesInArchive: number; - currentBytesUnarchived: number; - totalUncompressedBytesInArchive: number; - totalCompressedBytesRead: number; -} -/** Extract event. */ -export class UnarchiveExtractEvent extends UnarchiveEvent { - /** - * @param {UnarchivedFile} unarchivedFile - */ - constructor(unarchivedFile: UnarchivedFile); - /** - * @type {UnarchivedFile} - */ - unarchivedFile: UnarchivedFile; -} -//# sourceMappingURL=events.d.ts.map \ No newline at end of file diff --git a/types/archive/events.d.ts.map b/types/archive/events.d.ts.map deleted file mode 100644 index 5d7045f..0000000 --- a/types/archive/events.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../archive/events.js"],"names":[],"mappings":";;;;;;;;;AA6BA,0BAA0B;AAC1B;IACE;;OAEG;IACH,kBAFW,MAAM,EAIhB;CACF;AAED,oEAAoE;AACpE;IACE;;OAEG;IACH,sBAFW,MAAM,EAUhB;IALC;;;OAGG;IACH,UAFU,MAAM,CAEQ;CAE3B;AAED,gEAAgE;AAChE;IAOI;;;OAGG;IACH,KAFU,MAAM,CAEF;CAEjB;AAED,0CAA0C;AAC1C;IAOI;;;OAGG;IACH,KAFU,MAAM,CAEF;CAEjB;AAED,mBAAmB;AACnB;IACE,cAEC;CACF;AAED,oBAAoB;AACpB;IACE;;OAEG;IACH,4BAGC;IADC,cAAwB;CAE3B;AAGD,sBAAsB;AACtB;IACE;;;;;;;;OAQG;IACH,6BARW,MAAM,qBACN,MAAM,gCACN,MAAM,0BACN,MAAM,mCACN,MAAM,uBACN,MAAM,4BACN,MAAM,EAchB;IAPC,wBAAsC;IACtC,0BAA0C;IAC1C,qCAAgE;IAChE,4BAA8C;IAC9C,+BAAoD;IACpD,wCAAsE;IACtE,iCAAwD;CAE3D;AAED,qBAAqB;AACrB;IACE;;OAEG;IACH,4CAOC;IAJC;;OAEG;IACH,+BAAoC;CAEvC"} \ No newline at end of file diff --git a/types/codecs/codecs.d.ts b/types/codecs/codecs.d.ts deleted file mode 100644 index e9e6362..0000000 --- a/types/codecs/codecs.d.ts +++ /dev/null @@ -1,67 +0,0 @@ -/** - * TODO: Reconcile this with file/sniffer.js findMimeType() which does signature matching. - * @param {ProbeInfo} info - * @returns {string} - */ -export function getShortMIMEString(info: ProbeInfo): string; -/** - * Accepts the ffprobe JSON output and returns an ISO MIME string with parameters (RFC6381), such - * as 'video/mp4; codecs="avc1.4D4028, mp4a.40.2"'. This string should be suitable to be used on - * the server as the Content-Type header of a media stream which can subsequently be used on the - * client as the type value of a SourceBuffer object `mediaSource.addSourceBuffer(contentType)`. - * NOTE: For now, this method fails hard (throws an error) when it encounters a format/codec it - * does not recognize. Please file a bug or send a PR. - * @param {ProbeInfo} info - * @returns {string} - */ -export function getFullMIMEString(info: ProbeInfo): string; -/** - * ffprobe -show_streams -print_format json. Only the fields we care about. - */ -export type ProbeStream = { - index: number; - codec_name: string; - codec_long_name: string; - profile: string; - /** - * Either 'audio' or 'video'. - */ - codec_type: string; - codec_tag_string: string; - id: string; - level: number | null; - width: number | null; - height: number | null; - /** - * Like "60000/1001" - */ - r_frame_rate: string; -}; -/** - * Only the fields we care about from the following command: - * ffprobe -show_format -show_streams -v quiet -print_format json -i file.mp4 - */ -export type ProbeFormat = { - filename: string; - format_name: string; - /** - * Number of seconds, as a string like "473.506367". - */ - duration: string; - /** - * Number of bytes, as a string. - */ - size: string; - /** - * Bit rate, as a string. - */ - bit_rate: string; -}; -/** - * ffprobe -show_format -show_streams -print_format json - */ -export type ProbeInfo = { - streams: ProbeStream[]; - format: ProbeFormat; -}; -//# sourceMappingURL=codecs.d.ts.map \ No newline at end of file diff --git a/types/codecs/codecs.d.ts.map b/types/codecs/codecs.d.ts.map deleted file mode 100644 index 2a0a98b..0000000 --- a/types/codecs/codecs.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"codecs.d.ts","sourceRoot":"","sources":["../../codecs/codecs.js"],"names":[],"mappings":"AA8DA;;;;GAIG;AACH,yCAHW,SAAS,GACP,MAAM,CAuDlB;AAED;;;;;;;;;GASG;AACH,wCAHW,SAAS,GACP,MAAM,CAiElB;;;;;WApLa,MAAM;gBACN,MAAM;qBACN,MAAM;aACN,MAAM;;;;gBACN,MAAM;sBACN,MAAM;QACN,MAAM;WACN,MAAM;WACN,MAAM;YACN,MAAM;;;;kBACN,MAAM;;;;;;;cAMN,MAAM;iBACN,MAAM;;;;cACN,MAAM;;;;UACN,MAAM;;;;cACN,MAAM;;;;;;aAKN,WAAW,EAAE;YACb,WAAW"} \ No newline at end of file diff --git a/types/file/sniffer.d.ts b/types/file/sniffer.d.ts deleted file mode 100644 index 76e10af..0000000 --- a/types/file/sniffer.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * This function initializes the byte tree. It is lazily called upon findMimeType(), but if you care - * about when the tree initializes (like in startup, etc), you can call it yourself here. - */ -export function initialize(): void; -/** - * Finds the likely MIME type represented by the ArrayBuffer. - * @param {ArrayBuffer} ab - * @returns {string} The MIME type of the buffer, or undefined. - */ -export function findMimeType(ab: ArrayBuffer): string; -//# sourceMappingURL=sniffer.d.ts.map \ No newline at end of file diff --git a/types/file/sniffer.d.ts.map b/types/file/sniffer.d.ts.map deleted file mode 100644 index e541a2f..0000000 --- a/types/file/sniffer.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"sniffer.d.ts","sourceRoot":"","sources":["../../file/sniffer.js"],"names":[],"mappings":"AA0FA;;;GAGG;AACH,mCAqCC;AAED;;;;GAIG;AACH,iCAHW,WAAW,GACT,MAAM,CAoBlB"} \ No newline at end of file diff --git a/types/image/parsers/exif.d.ts b/types/image/parsers/exif.d.ts deleted file mode 100644 index 88edc96..0000000 --- a/types/image/parsers/exif.d.ts +++ /dev/null @@ -1,133 +0,0 @@ -/** - * @param {ByteStream} stream - * @param {ByteStream} lookAheadStream - * @param {boolean} debug - * @returns {ExifValue} - */ -export function getExifValue(stream: ByteStream, lookAheadStream: ByteStream, DEBUG?: boolean): ExifValue; -/** - * Reads the entire EXIF profile. The first 2 bytes in the stream must be the TIFF marker (II/MM). - * @param {ByteStream} stream - * @returns {Map; -export type ExifTagNumber = number; -export namespace ExifTagNumber { - const IMAGE_DESCRIPTION: number; - const MAKE: number; - const MODEL: number; - const ORIENTATION: number; - const X_RESOLUTION: number; - const Y_RESOLUTION: number; - const RESOLUTION_UNIT: number; - const SOFTWARE: number; - const DATE_TIME: number; - const WHITE_POINT: number; - const PRIMARY_CHROMATICITIES: number; - const Y_CB_CR_COEFFICIENTS: number; - const Y_CB_CR_POSITIONING: number; - const REFERENCE_BLACK_WHITE: number; - const COPYRIGHT: number; - const EXIF_OFFSET: number; - const EXPOSURE_TIME: number; - const F_NUMBER: number; - const EXPOSURE_PROGRAM: number; - const ISO_SPEED_RATINGS: number; - const EXIF_VERSION: number; - const DATE_TIME_ORIGINAL: number; - const DATE_TIME_DIGITIZED: number; - const COMPONENT_CONFIGURATION: number; - const COMPRESSED_BITS_PER_PIXEL: number; - const SHUTTER_SPEED_VALUE: number; - const APERTURE_VALUE: number; - const BRIGHTNESS_VALUE: number; - const EXPOSURE_BIAS_VALUE: number; - const MAX_APERTURE_VALUE: number; - const SUBJECT_DISTANCE: number; - const METERING_MODE: number; - const LIGHT_SOURCE: number; - const FLASH: number; - const FOCAL_LENGTH: number; - const MAKER_NOTE: number; - const USER_COMMENT: number; - const FLASH_PIX_VERSION: number; - const COLOR_SPACE: number; - const EXIF_IMAGE_WIDTH: number; - const EXIF_IMAGE_HEIGHT: number; - const RELATED_SOUND_FILE: number; - const EXIF_INTEROPERABILITY_OFFSET: number; - const FOCAL_PLANE_X_RESOLUTION: number; - const FOCAL_PLANE_Y_RESOLUTION: number; - const FOCAL_PLANE_RESOLUTION_UNIT: number; - const SENSING_METHOD: number; - const FILE_SOURCE: number; - const SCENE_TYPE: number; - const IMAGE_WIDTH: number; - const IMAGE_LENGTH: number; - const BITS_PER_SAMPLE: number; - const COMPRESSION: number; - const PHOTOMETRIC_INTERPRETATION: number; - const STRIP_OFFSETS: number; - const SAMPLES_PER_PIXEL: number; - const ROWS_PER_STRIP: number; - const STRIP_BYTE_COUNTS: number; - const PLANAR_CONFIGURATION: number; - const JPEG_IF_OFFSET: number; - const JPEG_IF_BYTE_COUNT: number; - const Y_CB_CR_SUB_SAMPLING: number; -} -export type ExifDataFormat = number; -export namespace ExifDataFormat { - const UNSIGNED_BYTE: number; - const ASCII_STRING: number; - const UNSIGNED_SHORT: number; - const UNSIGNED_LONG: number; - const UNSIGNED_RATIONAL: number; - const SIGNED_BYTE: number; - const UNDEFINED: number; - const SIGNED_SHORT: number; - const SIGNED_LONG: number; - const SIGNED_RATIONAL: number; - const SINGLE_FLOAT: number; - const DOUBLE_FLOAT: number; -} -export type ExifValue = { - /** - * The numerical value of the tag. - */ - tagNumber: ExifTagNumber; - /** - * A string representing the tag number. - */ - tagName?: string | undefined; - /** - * The data format. - */ - dataFormat: ExifDataFormat; - /** - * Populated for SIGNED/UNSIGNED BYTE/SHORT/LONG/FLOAT. - */ - numericalValue?: number | undefined; - /** - * Populated only for ASCII_STRING. - */ - stringValue?: string | undefined; - /** - * Populated only for SIGNED/UNSIGNED RATIONAL. - */ - numeratorValue?: number | undefined; - /** - * Populated only for SIGNED/UNSIGNED RATIONAL. - */ - denominatorValue?: number | undefined; - /** - * Populated only for UNDEFINED data format. - */ - numComponents?: number | undefined; - /** - * Populated only for UNDEFINED data format. - */ - offsetValue?: number | undefined; -}; -import { ByteStream } from "../../io/bytestream.js"; -//# sourceMappingURL=exif.d.ts.map \ No newline at end of file diff --git a/types/image/parsers/exif.d.ts.map b/types/image/parsers/exif.d.ts.map deleted file mode 100644 index c82e655..0000000 --- a/types/image/parsers/exif.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"exif.d.ts","sourceRoot":"","sources":["../../../image/parsers/exif.js"],"names":[],"mappings":"AA+HA;;;;;GAKG;AACH,qCALW,UAAU,mBACV,UAAU,oBAER,SAAS,CAsGrB;AA6BD;;;;GAIG;AACH,uCAHW,UAAU,0BAyCpB;4BArSU,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BA2EN,MAAM;;;;;;;;;;;;;;;;;;;eAkBH,aAAa;;;;cACb,MAAM;;;;gBACN,cAAc;;;;qBACd,MAAM;;;;kBACN,MAAM;;;;qBACN,MAAM;;;;uBACN,MAAM;;;;oBACN,MAAM;;;;kBACN,MAAM"} \ No newline at end of file diff --git a/types/image/parsers/gif.d.ts b/types/image/parsers/gif.d.ts deleted file mode 100644 index bdd0b88..0000000 --- a/types/image/parsers/gif.d.ts +++ /dev/null @@ -1,231 +0,0 @@ -export namespace GifParseEventType { - const APPLICATION_EXTENSION: string; - const COMMENT_EXTENSION: string; - const GRAPHIC_CONTROL_EXTENSION: string; - const HEADER: string; - const LOGICAL_SCREEN: string; - const PLAIN_TEXT_EXTENSION: string; - const TABLE_BASED_IMAGE: string; - const TRAILER: string; -} -/** - * @typedef GifHeader - * @property {string} version - */ -/** - * @typedef GifColor - * @property {number} red - * @property {number} green - * @property {number} blue - */ -/** - * @typedef GifLogicalScreen - * @property {number} logicalScreenWidth - * @property {number} logicalScreenHeight - * @property {boolean} globalColorTableFlag - * @property {number} colorResolution - * @property {boolean} sortFlag - * @property {number} globalColorTableSize - * @property {number} backgroundColorIndex - * @property {number} pixelAspectRatio - * @property {GifColor[]=} globalColorTable Only if globalColorTableFlag is true. - */ -/** - * @typedef GifTableBasedImage - * @property {number} imageLeftPosition - * @property {number} imageTopPosition - * @property {number} imageWidth - * @property {number} imageHeight - * @property {boolean} localColorTableFlag - * @property {boolean} interlaceFlag - * @property {boolean} sortFlag - * @property {number} localColorTableSize - * @property {GifColor[]=} localColorTable Only if localColorTableFlag is true. - * @property {number} lzwMinimumCodeSize - * @property {Uint8Array} imageData - */ -/** - * @typedef GifGraphicControlExtension - * @property {number} disposalMethod - * @property {boolean} userInputFlag - * @property {boolean} transparentColorFlag - * @property {number} delayTime - * @property {number} transparentColorIndex - */ -/** - * @typedef GifCommentExtension - * @property {string} comment - */ -/** - * @typedef GifPlainTextExtension - * @property {number} textGridLeftPosition - * @property {number} textGridTopPosition - * @property {number} textGridWidth - * @property {number} textGridHeight - * @property {number} characterCellWidth - * @property {number} characterCellHeight - * @property {number} textForegroundColorIndex - * @property {number} textBackgroundColorIndex - * @property {string} plainText - */ -/** - * @typedef GifApplicationExtension - * @property {string} applicationIdentifier - * @property {Uint8Array} applicationAuthenticationCode - * @property {Uint8Array} applicationData - */ -/** - * The Grammar. - * - * ::= Header * Trailer - * ::= Logical Screen Descriptor [Global Color Table] - * ::= | - * - * ::= [Graphic Control Extension] - * ::= | - * Plain Text Extension - * ::= Image Descriptor [Local Color Table] Image Data - * ::= Application Extension | - * Comment Extension - */ -export class GifParser extends EventTarget { - /** @param {ArrayBuffer} ab */ - constructor(ab: ArrayBuffer); - /** - * @type {ByteStream} - * @private - */ - private bstream; - /** - * @type {string} - * @private - */ - private version; - /** - * Type-safe way to bind a listener for a GifApplicationExtension. - * @param {function(CustomEvent): void} listener - * @returns {GifParser} for chaining - */ - onApplicationExtension(listener: (arg0: CustomEvent) => void): GifParser; - /** - * Type-safe way to bind a listener for a GifCommentExtension. - * @param {function(CustomEvent): void} listener - * @returns {GifParser} for chaining - */ - onCommentExtension(listener: (arg0: CustomEvent) => void): GifParser; - /** - * Type-safe way to bind a listener for a GifGraphicControlExtension. - * @param {function(CustomEvent): void} listener - * @returns {GifParser} for chaining - */ - onGraphicControlExtension(listener: (arg0: CustomEvent) => void): GifParser; - /** - * Type-safe way to bind a listener for a GifHeader. - * @param {function(CustomEvent): void} listener - * @returns {GifParser} for chaining - */ - onHeader(listener: (arg0: CustomEvent) => void): GifParser; - /** - * Type-safe way to bind a listener for a GifLogicalScreen. - * @param {function(CustomEvent): void} listener - * @returns {GifParser} for chaining - */ - onLogicalScreen(listener: (arg0: CustomEvent) => void): GifParser; - /** - * Type-safe way to bind a listener for a GifPlainTextExtension. - * @param {function(CustomEvent): void} listener - * @returns {GifParser} for chaining - */ - onPlainTextExtension(listener: (arg0: CustomEvent) => void): GifParser; - /** - * Type-safe way to bind a listener for a GifTableBasedImage. - * @param {function(CustomEvent): void} listener - * @returns {GifParser} for chaining - */ - onTableBasedImage(listener: (arg0: CustomEvent) => void): GifParser; - /** - * Type-safe way to bind a listener for the GifTrailer. - * @param {function(CustomEvent): void} listener - * @returns {GifParser} for chaining - */ - onTrailer(listener: (arg0: CustomEvent) => void): GifParser; - /** - * @returns {Promise} A Promise that resolves when the parsing is complete. - */ - start(): Promise; - /** - * @private - * @returns {boolean} True if this was not the last block. - */ - private readGraphicBlock; - /** - * @private - * @returns {Uint8Array} Data from the sub-block, or null if this was the last, zero-length block. - */ - private readSubBlock; -} -export type GifHeader = { - version: string; -}; -export type GifColor = { - red: number; - green: number; - blue: number; -}; -export type GifLogicalScreen = { - logicalScreenWidth: number; - logicalScreenHeight: number; - globalColorTableFlag: boolean; - colorResolution: number; - sortFlag: boolean; - globalColorTableSize: number; - backgroundColorIndex: number; - pixelAspectRatio: number; - /** - * Only if globalColorTableFlag is true. - */ - globalColorTable?: GifColor[] | undefined; -}; -export type GifTableBasedImage = { - imageLeftPosition: number; - imageTopPosition: number; - imageWidth: number; - imageHeight: number; - localColorTableFlag: boolean; - interlaceFlag: boolean; - sortFlag: boolean; - localColorTableSize: number; - /** - * Only if localColorTableFlag is true. - */ - localColorTable?: GifColor[] | undefined; - lzwMinimumCodeSize: number; - imageData: Uint8Array; -}; -export type GifGraphicControlExtension = { - disposalMethod: number; - userInputFlag: boolean; - transparentColorFlag: boolean; - delayTime: number; - transparentColorIndex: number; -}; -export type GifCommentExtension = { - comment: string; -}; -export type GifPlainTextExtension = { - textGridLeftPosition: number; - textGridTopPosition: number; - textGridWidth: number; - textGridHeight: number; - characterCellWidth: number; - characterCellHeight: number; - textForegroundColorIndex: number; - textBackgroundColorIndex: number; - plainText: string; -}; -export type GifApplicationExtension = { - applicationIdentifier: string; - applicationAuthenticationCode: Uint8Array; - applicationData: Uint8Array; -}; -//# sourceMappingURL=gif.d.ts.map \ No newline at end of file diff --git a/types/image/parsers/gif.d.ts.map b/types/image/parsers/gif.d.ts.map deleted file mode 100644 index 01f4c31..0000000 --- a/types/image/parsers/gif.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"gif.d.ts","sourceRoot":"","sources":["../../../image/parsers/gif.js"],"names":[],"mappings":";;;;;;;;;;AA2BA;;;GAGG;AAEH;;;;;GAKG;AAEH;;;;;;;;;;;GAWG;AAEH;;;;;;;;;;;;;GAaG;AAEH;;;;;;;GAOG;AAEH;;;GAGG;AAEH;;;;;;;;;;;GAWG;AAEH;;;;;GAKG;AAEH;;;;;;;;;;;;;GAaG;AAEH;IAaE,8BAA8B;IAC9B,gBADY,WAAW,EAMtB;IAlBD;;;OAGG;IACH,gBAAQ;IAER;;;OAGG;IACH,gBAAQ;IAUR;;;;OAIG;IACH,wCAHoB,YAAY,uBAAuB,CAAC,KAAG,IAAI,GAClD,SAAS,CAKrB;IAED;;;;OAIG;IACH,oCAHoB,YAAY,mBAAmB,CAAC,KAAG,IAAI,GAC9C,SAAS,CAKrB;IAED;;;;OAIG;IACH,2CAHoB,YAAY,0BAA0B,CAAC,KAAG,IAAI,GACrD,SAAS,CAKrB;IAED;;;;OAIG;IACH,0BAHoB,YAAY,SAAS,CAAC,KAAG,IAAI,GACpC,SAAS,CAKrB;IAED;;;;OAIG;IACH,iCAHoB,YAAY,gBAAgB,CAAC,KAAG,IAAI,GAC3C,SAAS,CAKrB;IAED;;;;OAIG;IACH,sCAHoB,YAAY,qBAAqB,CAAC,KAAG,IAAI,GAChD,SAAS,CAKrB;IAED;;;;OAIG;IACH,mCAHoB,YAAY,kBAAkB,CAAC,KAAG,IAAI,GAC7C,SAAS,CAKrB;IAED;;;;OAIG;IACH,2BAHoB,WAAW,KAAG,IAAI,GACzB,SAAS,CAKrB;IAED;;OAEG;IACH,SAFa,QAAQ,IAAI,CAAC,CAsDzB;IAED;;;OAGG;IACH,yBAmMC;IAED;;;OAGG;IACH,qBAIC;CACF;;aAvca,MAAM;;;SAKN,MAAM;WACN,MAAM;UACN,MAAM;;;wBAKN,MAAM;yBACN,MAAM;0BACN,OAAO;qBACP,MAAM;cACN,OAAO;0BACP,MAAM;0BACN,MAAM;sBACN,MAAM;;;;uBACN,QAAQ,EAAE;;;uBAKV,MAAM;sBACN,MAAM;gBACN,MAAM;iBACN,MAAM;yBACN,OAAO;mBACP,OAAO;cACP,OAAO;yBACP,MAAM;;;;sBACN,QAAQ,EAAE;wBACV,MAAM;eACN,UAAU;;;oBAKV,MAAM;mBACN,OAAO;0BACP,OAAO;eACP,MAAM;2BACN,MAAM;;;aAKN,MAAM;;;0BAKN,MAAM;yBACN,MAAM;mBACN,MAAM;oBACN,MAAM;wBACN,MAAM;yBACN,MAAM;8BACN,MAAM;8BACN,MAAM;eACN,MAAM;;;2BAKN,MAAM;mCACN,UAAU;qBACV,UAAU"} \ No newline at end of file diff --git a/types/image/parsers/jpeg.d.ts b/types/image/parsers/jpeg.d.ts deleted file mode 100644 index 3488e76..0000000 --- a/types/image/parsers/jpeg.d.ts +++ /dev/null @@ -1,226 +0,0 @@ -export type JpegParseEventType = string; -export namespace JpegParseEventType { - const APP0_MARKER: string; - const APP0_EXTENSION: string; - const APP1_EXIF: string; - const DEFINE_QUANTIZATION_TABLE: string; - const DEFINE_HUFFMAN_TABLE: string; - const START_OF_FRAME: string; - const START_OF_SCAN: string; -} -export type JpegSegmentType = number; -export namespace JpegSegmentType { - const SOF0: number; - const SOF1: number; - const SOF2: number; - const DHT: number; - const SOI: number; - const EOI: number; - const SOS: number; - const DQT: number; - const APP0: number; - const APP1: number; -} -export type JpegDensityUnits = number; -export namespace JpegDensityUnits { - const NO_UNITS: number; - const PIXELS_PER_INCH: number; - const PIXELS_PER_CM: number; -} -export type JpegExtensionThumbnailFormat = number; -export namespace JpegExtensionThumbnailFormat { - const JPEG: number; - const ONE_BYTE_PER_PIXEL_PALETTIZED: number; - const THREE_BYTES_PER_PIXEL_RGB: number; -} -export type JpegHuffmanTableType = number; -export namespace JpegHuffmanTableType { - const DC: number; - const AC: number; -} -export type JpegDctType = number; -export namespace JpegDctType { - const BASELINE: number; - const EXTENDED_SEQUENTIAL: number; - const PROGRESSIVE: number; -} -export type JpegComponentType = number; -export namespace JpegComponentType { - const Y: number; - const CB: number; - const CR: number; - const I: number; - const Q: number; -} -/** - * @typedef JpegComponentDetail - * @property {JpegComponentType} componentId - * @property {number} verticalSamplingFactor - * @property {number} horizontalSamplingFactor - * @property {number} quantizationTableNumber - */ -/** - * @typedef JpegStartOfFrame - * @property {JpegDctType} dctType - * @property {number} dataPrecision - * @property {number} imageHeight - * @property {number} imageWidth - * @property {number} numberOfComponents Usually 1, 3, or 4. - * @property {JpegComponentDetail[]} componentDetails - */ -/** - * @typedef JpegStartOfScan - * @property {number} componentsInScan - * @property {number} componentSelectorY - * @property {number} huffmanTableSelectorY - * @property {number} componentSelectorCb - * @property {number} huffmanTableSelectorCb - * @property {number} componentSelectorCr - * @property {number} huffmanTableSelectorCr - * @property {number} scanStartPositionInBlock - * @property {number} scanEndPositionInBlock - * @property {number} successiveApproximationBitPosition - * @property {Uint8Array} rawImageData - */ -export class JpegParser extends EventTarget { - /** @param {ArrayBuffer} ab */ - constructor(ab: ArrayBuffer); - /** - * @type {ByteStream} - * @private - */ - private bstream; - /** - * @type {boolean} - * @private - */ - private hasApp0MarkerSegment; - /** - * Type-safe way to bind a listener for a JpegApp0Marker. - * @param {function(CustomEvent): void} listener - * @returns {JpegParser} for chaining - */ - onApp0Marker(listener: (arg0: CustomEvent) => void): JpegParser; - /** - * Type-safe way to bind a listener for a JpegApp0Extension. - * @param {function(CustomEvent): void} listener - * @returns {JpegParser} for chaining - */ - onApp0Extension(listener: (arg0: CustomEvent) => void): JpegParser; - /** - * Type-safe way to bind a listener for a JpegExifProfile. - * @param {function(CustomEvent): void} listener - * @returns {JpegParser} for chaining - */ - onApp1Exif(listener: (arg0: CustomEvent) => void): JpegParser; - /** - * Type-safe way to bind a listener for a JpegDefineQuantizationTable. - * @param {function(CustomEvent): void} listener - * @returns {JpegParser} for chaining - */ - onDefineQuantizationTable(listener: (arg0: CustomEvent) => void): JpegParser; - /** - * Type-safe way to bind a listener for a JpegDefineHuffmanTable. - * @param {function(CustomEvent): void} listener - * @returns {JpegParser} for chaining - */ - onDefineHuffmanTable(listener: (arg0: CustomEvent) => void): JpegParser; - /** - * Type-safe way to bind a listener for a JpegStartOfFrame. - * @param {function(CustomEvent): void} listener - * @returns {JpegParser} for chaining - */ - onStartOfFrame(listener: (arg0: CustomEvent) => void): JpegParser; - /** - * Type-safe way to bind a listener for a JpegStartOfScan. - * @param {function(CustomEvent): void} listener - * @returns {JpegParser} for chaining - */ - onStartOfScan(listener: (arg0: CustomEvent) => void): JpegParser; - /** @returns {Promise} A Promise that resolves when the parsing is complete. */ - start(): Promise; -} -export type ExifValue = import('./exif.js').ExifValue; -export type JpegApp0Marker = { - /** - * Like '1.02'. - */ - jfifVersion: string; - densityUnits: JpegDensityUnits; - xDensity: number; - yDensity: number; - xThumbnail: number; - yThumbnail: number; - /** - * RGB data. Size is 3 x thumbnailWidth x thumbnailHeight. - */ - thumbnailData: Uint8Array; -}; -export type JpegApp0Extension = { - thumbnailFormat: JpegExtensionThumbnailFormat; - /** - * Raw thumbnail data - */ - thumbnailData: Uint8Array; -}; -export type JpegExifProfile = Map; -export type JpegDefineQuantizationTable = { - /** - * Table/component number. - */ - tableNumber: number; - /** - * (0=byte, 1=word). - */ - precision: number; - /** - * 64 numbers representing the quantization table. - */ - tableValues: number[]; -}; -export type JpegDefineHuffmanTable = { - /** - * Table/component number (0-3). - */ - tableNumber: number; - /** - * Either DC or AC. - */ - tableType: JpegHuffmanTableType; - /** - * A 16-byte array specifying the # of symbols of each length. - */ - numberOfSymbols: number[]; - symbols: number[]; -}; -export type JpegComponentDetail = { - componentId: JpegComponentType; - verticalSamplingFactor: number; - horizontalSamplingFactor: number; - quantizationTableNumber: number; -}; -export type JpegStartOfFrame = { - dctType: JpegDctType; - dataPrecision: number; - imageHeight: number; - imageWidth: number; - /** - * Usually 1, 3, or 4. - */ - numberOfComponents: number; - componentDetails: JpegComponentDetail[]; -}; -export type JpegStartOfScan = { - componentsInScan: number; - componentSelectorY: number; - huffmanTableSelectorY: number; - componentSelectorCb: number; - huffmanTableSelectorCb: number; - componentSelectorCr: number; - huffmanTableSelectorCr: number; - scanStartPositionInBlock: number; - scanEndPositionInBlock: number; - successiveApproximationBitPosition: number; - rawImageData: Uint8Array; -}; -//# sourceMappingURL=jpeg.d.ts.map \ No newline at end of file diff --git a/types/image/parsers/jpeg.d.ts.map b/types/image/parsers/jpeg.d.ts.map deleted file mode 100644 index 9b2431b..0000000 --- a/types/image/parsers/jpeg.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"jpeg.d.ts","sourceRoot":"","sources":["../../../image/parsers/jpeg.js"],"names":[],"mappings":"iCAuBW,MAAM;;;;;;;;;;8BAWN,MAAM;;;;;;;;;;;;;+BAwBN,MAAM;;;;;;2CAkBN,MAAM;;;;;;mCAsBN,MAAM;;;;;0BAcN,MAAM;;;;;;gCAON,MAAM;;;;;;;;AASjB;;;;;;GAMG;AAEH;;;;;;;;GAQG;AAEH;;;;;;;;;;;;;GAaG;AAEH;IAaE,8BAA8B;IAC9B,gBADY,WAAW,EAItB;IAhBD;;;OAGG;IACH,gBAAQ;IAER;;;OAGG;IACH,6BAA6B;IAQ7B;;;;OAIG;IACH,8BAHoB,YAAY,cAAc,CAAC,KAAG,IAAI,GACzC,UAAU,CAKtB;IAED;;;;OAIG;IACH,iCAHoB,YAAY,iBAAiB,CAAC,KAAG,IAAI,GAC5C,UAAU,CAKtB;IAED;;;;OAIG;IACH,4BAHoB,YAAY,eAAe,CAAC,KAAG,IAAI,GAC1C,UAAU,CAKtB;IAED;;;;OAIG;IACH,2CAHoB,YAAY,2BAA2B,CAAC,KAAG,IAAI,GACtD,UAAU,CAKtB;IAED;;;;OAIG;IACH,sCAHoB,YAAY,sBAAsB,CAAC,KAAG,IAAI,GACjD,UAAU,CAKtB;IAED;;;;OAIG;IACH,gCAHoB,YAAY,gBAAgB,CAAC,KAAG,IAAI,GAC3C,UAAU,CAKtB;IAED;;;;OAIG;IACH,+BAHoB,YAAY,eAAe,CAAC,KAAG,IAAI,GAC1C,UAAU,CAKtB;IAED,qFAAqF;IACrF,SADc,QAAQ,IAAI,CAAC,CA8O1B;CACF;wBA3da,OAAO,WAAW,EAAE,SAAS;;;;;iBAqD7B,MAAM;kBACN,gBAAgB;cAChB,MAAM;cACN,MAAM;gBACN,MAAM;gBACN,MAAM;;;;mBACN,UAAU;;;qBAYV,4BAA4B;;;;mBAC5B,UAAU;;;;;;;iBAOV,MAAM;;;;eACN,MAAM;;;;iBACN,MAAM,EAAE;;;;;;iBAWR,MAAM;;;;eACN,oBAAoB;;;;qBACpB,MAAM,EAAE;aACR,MAAM,EAAE;;;iBAqBR,iBAAiB;4BACjB,MAAM;8BACN,MAAM;6BACN,MAAM;;;aAKN,WAAW;mBACX,MAAM;iBACN,MAAM;gBACN,MAAM;;;;wBACN,MAAM;sBACN,mBAAmB,EAAE;;;sBAKrB,MAAM;wBACN,MAAM;2BACN,MAAM;yBACN,MAAM;4BACN,MAAM;yBACN,MAAM;4BACN,MAAM;8BACN,MAAM;4BACN,MAAM;wCACN,MAAM;kBACN,UAAU"} \ No newline at end of file diff --git a/types/image/parsers/parsers.d.ts b/types/image/parsers/parsers.d.ts deleted file mode 100644 index 7b51e2d..0000000 --- a/types/image/parsers/parsers.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Creates a new event of the given type with the specified data. - * @template T - * @param {string} type The event type. - * @param {T} data The event data. - * @returns {CustomEvent} The new event. - */ -export function createEvent(type: string, data: T): CustomEvent; -//# sourceMappingURL=parsers.d.ts.map \ No newline at end of file diff --git a/types/image/parsers/parsers.d.ts.map b/types/image/parsers/parsers.d.ts.map deleted file mode 100644 index 2cfc3bf..0000000 --- a/types/image/parsers/parsers.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"parsers.d.ts","sourceRoot":"","sources":["../../../image/parsers/parsers.js"],"names":[],"mappings":"AAUA;;;;;;GAMG;AACH,qCAJW,MAAM,2BAMhB"} \ No newline at end of file diff --git a/types/image/parsers/png.d.ts b/types/image/parsers/png.d.ts deleted file mode 100644 index 84d8a76..0000000 --- a/types/image/parsers/png.d.ts +++ /dev/null @@ -1,381 +0,0 @@ -export type PngParseEventType = string; -export namespace PngParseEventType { - const IDAT: string; - const IHDR: string; - const PLTE: string; - const bKGD: string; - const cHRM: string; - const eXIf: string; - const gAMA: string; - const hIST: string; - const iTXt: string; - const pHYs: string; - const sBIT: string; - const sPLT: string; - const tEXt: string; - const tIME: string; - const tRNS: string; - const zTXt: string; -} -export type PngColorType = number; -export namespace PngColorType { - const GREYSCALE: number; - const TRUE_COLOR: number; - const INDEXED_COLOR: number; - const GREYSCALE_WITH_ALPHA: number; - const TRUE_COLOR_WITH_ALPHA: number; -} -export type PngInterlaceMethod = number; -export namespace PngInterlaceMethod { - const NO_INTERLACE: number; - const ADAM7_INTERLACE: number; -} -export namespace PngUnitSpecifier { - const UNKNOWN: number; - const METRE: number; -} -/** - * @typedef PngPhysicalPixelDimensions - * @property {number} pixelPerUnitX - * @property {number} pixelPerUnitY - * @property {PngUnitSpecifier} unitSpecifier - */ -/** @typedef {Map} PngExifProfile */ -/** - * @typedef PngHistogram - * @property {number[]} frequencies The # of frequencies matches the # of palette entries. - */ -/** - * @typedef PngSuggestedPaletteEntry - * @property {number} red - * @property {number} green - * @property {number} blue - * @property {number} alpha - * @property {number} frequency - */ -/** - * @typedef PngSuggestedPalette - * @property {string} paletteName - * @property {number} sampleDepth Either 8 or 16. - * @property {PngSuggestedPaletteEntry[]} entries - */ -/** - * @typedef PngChunk Internal use only. - * @property {number} length - * @property {string} chunkType - * @property {ByteStream} chunkStream Do not read more than length! - * @property {number} crc - */ -export class PngParser extends EventTarget { - /** @param {ArrayBuffer} ab */ - constructor(ab: ArrayBuffer); - /** - * @type {ByteStream} - * @private - */ - private bstream; - /** - * @type {PngColorType} - * @private - */ - private colorType; - /** - * @type {PngPalette} - * @private - */ - private palette; - /** - * Type-safe way to bind a listener for a PngBackgroundColor. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onBackgroundColor(listener: (arg0: CustomEvent) => void): PngParser; - /** - * Type-safe way to bind a listener for a PngChromaticities. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onChromaticities(listener: (arg0: CustomEvent) => void): PngParser; - /** - * Type-safe way to bind a listener for a PngCompressedTextualData. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onCompressedTextualData(listener: (arg0: CustomEvent) => void): PngParser; - /** - * Type-safe way to bind a listener for a PngExifProfile. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onExifProfile(listener: (arg0: CustomEvent) => void): PngParser; - /** - * Type-safe way to bind a listener for a PngImageGamma. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onGamma(listener: (arg0: CustomEvent) => void): PngParser; - /** - * Type-safe way to bind a listener for a PngHistogram. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onHistogram(listener: (arg0: CustomEvent) => void): PngParser; - /** - * Type-safe way to bind a listener for a PngImageData. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onImageData(listener: (arg0: CustomEvent) => void): PngParser; - /** - * Type-safe way to bind a listener for a PngImageHeader. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onImageHeader(listener: (arg0: CustomEvent) => void): PngParser; - /** - * Type-safe way to bind a listener for a PngIntlTextualData. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onIntlTextualData(listener: (arg0: CustomEvent) => void): PngParser; - /** - * Type-safe way to bind a listener for a PngLastModTime. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onLastModTime(listener: (arg0: CustomEvent) => void): PngParser; - /** - * Type-safe way to bind a listener for a PngPalette. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onPalette(listener: (arg0: CustomEvent) => void): PngParser; - /** - * Type-safe way to bind a listener for a PngPhysicalPixelDimensions. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onPhysicalPixelDimensions(listener: (arg0: CustomEvent) => void): PngParser; - /** - * Type-safe way to bind a listener for a PngSignificantBits. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onSignificantBits(listener: (arg0: CustomEvent) => void): PngParser; - /** - * Type-safe way to bind a listener for a PngSuggestedPalette. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onSuggestedPalette(listener: (arg0: CustomEvent) => void): PngParser; - /** - * Type-safe way to bind a listener for a PngTextualData. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onTextualData(listener: (arg0: CustomEvent) => void): PngParser; - /** - * Type-safe way to bind a listener for a PngTransparency. - * @param {function(CustomEvent): void} listener - * @returns {PngParser} for chaining - */ - onTransparency(listener: (arg0: CustomEvent) => void): PngParser; - /** @returns {Promise} A Promise that resolves when the parsing is complete. */ - start(): Promise; -} -export type ExifValue = import('./exif.js').ExifValue; -export type PngImageHeader = { - width: number; - height: number; - bitDepth: number; - colorType: PngColorType; - compressionMethod: number; - filterMethod: number; - interlaceMethod: number; -}; -export type PngSignificantBits = { - /** - * Populated for color types 0, 4. - */ - significant_greyscale?: number | undefined; - /** - * Populated for color types 2, 3, 6. - */ - significant_red?: number | undefined; - /** - * Populated for color types 2, 3, 6. - */ - significant_green?: number | undefined; - /** - * Populated for color types 2, 3, 6. - */ - significant_blue?: number | undefined; - /** - * Populated for color types 4, 6. - */ - significant_alpha?: number | undefined; -}; -export type PngChromaticities = { - whitePointX: number; - whitePointY: number; - redX: number; - redY: number; - greenX: number; - greenY: number; - blueX: number; - blueY: number; -}; -export type PngColor = { - red: number; - green: number; - blue: number; -}; -export type PngPalette = { - entries: PngColor[]; -}; -export type PngTransparency = { - /** - * Populated for color type 0. - */ - greySampleValue?: number | undefined; - /** - * Populated for color type 2. - */ - redSampleValue?: number | undefined; - /** - * Populated for color type 2. - */ - blueSampleValue?: number | undefined; - /** - * Populated for color type 2. - */ - greenSampleValue?: number | undefined; - /** - * Populated for color type 3. - */ - alphaPalette?: number[] | undefined; -}; -export type PngImageData = { - rawImageData: Uint8Array; -}; -export type PngTextualData = { - keyword: string; - textString?: string | undefined; -}; -export type PngCompressedTextualData = { - keyword: string; - /** - * Only value supported is 0 for deflate compression. - */ - compressionMethod: number; - compressedText?: Uint8Array | undefined; -}; -export type PngIntlTextualData = { - keyword: string; - /** - * 0 for uncompressed, 1 for compressed. - */ - compressionFlag: number; - /** - * 0 means zlib defalt when compressionFlag is 1. - */ - compressionMethod: number; - languageTag?: string | undefined; - translatedKeyword?: string | undefined; - /** - * The raw UTF-8 text (may be compressed). - */ - text: Uint8Array; -}; -export type PngBackgroundColor = { - /** - * Only for color types 0 and 4. - */ - greyscale?: number | undefined; - /** - * Only for color types 2 and 6. - */ - red?: number | undefined; - /** - * Only for color types 2 and 6. - */ - green?: number | undefined; - /** - * Only for color types 2 and 6. - */ - blue?: number | undefined; - /** - * Only for color type 3. - */ - paletteIndex?: number | undefined; -}; -export type PngLastModTime = { - /** - * Four-digit year. - */ - year: number; - /** - * One-based. Value from 1-12. - */ - month: number; - /** - * One-based. Value from 1-31. - */ - day: number; - /** - * Zero-based. Value from 0-23. - */ - hour: number; - /** - * Zero-based. Value from 0-59. - */ - minute: number; - /** - * Zero-based. Value from 0-60 to allow for leap-seconds. - */ - second: number; -}; -export type PngPhysicalPixelDimensions = { - pixelPerUnitX: number; - pixelPerUnitY: number; - unitSpecifier: { - UNKNOWN: number; - METRE: number; - }; -}; -export type PngExifProfile = Map; -export type PngHistogram = { - /** - * The # of frequencies matches the # of palette entries. - */ - frequencies: number[]; -}; -export type PngSuggestedPaletteEntry = { - red: number; - green: number; - blue: number; - alpha: number; - frequency: number; -}; -export type PngSuggestedPalette = { - paletteName: string; - /** - * Either 8 or 16. - */ - sampleDepth: number; - entries: PngSuggestedPaletteEntry[]; -}; -/** - * Internal use only. - */ -export type PngChunk = { - length: number; - chunkType: string; - /** - * Do not read more than length! - */ - chunkStream: ByteStream; - crc: number; -}; -import { ByteStream } from "../../io/bytestream.js"; -//# sourceMappingURL=png.d.ts.map \ No newline at end of file diff --git a/types/image/parsers/png.d.ts.map b/types/image/parsers/png.d.ts.map deleted file mode 100644 index 19fb2c7..0000000 --- a/types/image/parsers/png.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"png.d.ts","sourceRoot":"","sources":["../../../image/parsers/png.js"],"names":[],"mappings":"gCAuBW,MAAM;;;;;;;;;;;;;;;;;;;2BAuBN,MAAM;;;;;;;;iCASN,MAAM;;;;;;;;;AA+GjB;;;;;GAKG;AAEH,uDAAuD;AAEvD;;;GAGG;AAEH;;;;;;;GAOG;AAEH;;;;;GAKG;AAEH;;;;;;GAMG;AAEH;IAmBE,8BAA8B;IAC9B,gBADY,WAAW,EAKtB;IAvBD;;;OAGG;IACH,gBAAQ;IAER;;;OAGG;IACH,kBAAU;IAEV;;;OAGG;IACH,gBAAQ;IASR;;;;OAIG;IACH,mCAHoB,YAAY,kBAAkB,CAAC,KAAG,IAAI,GAC7C,SAAS,CAKrB;IAED;;;;OAIG;IACH,kCAHoB,YAAY,iBAAiB,CAAC,KAAG,IAAI,GAC5C,SAAS,CAKrB;IAED;;;;OAIG;IACH,yCAHoB,YAAY,wBAAwB,CAAC,KAAG,IAAI,GACnD,SAAS,CAKrB;IAED;;;;OAIG;IACH,+BAHoB,YAAY,cAAc,CAAC,KAAG,IAAI,GACzC,SAAS,CAKrB;IAED;;;;OAIG;IACH,yBAHoB,YAAY,MAAM,CAAC,KAAG,IAAI,GACjC,SAAS,CAKrB;IAED;;;;OAIG;IACH,6BAHoB,YAAY,YAAY,CAAC,KAAG,IAAI,GACvC,SAAS,CAKrB;IAED;;;;OAIG;IACH,6BAHoB,YAAY,YAAY,CAAC,KAAG,IAAI,GACvC,SAAS,CAKrB;IAED;;;;OAIG;IACH,+BAHoB,YAAY,cAAc,CAAC,KAAG,IAAI,GACzC,SAAS,CAKrB;IAED;;;;OAIG;IACH,mCAHoB,YAAY,kBAAkB,CAAC,KAAG,IAAI,GAC7C,SAAS,CAKrB;IAED;;;;OAIG;IACH,+BAHoB,YAAY,cAAc,CAAC,KAAG,IAAI,GACzC,SAAS,CAKrB;IAED;;;;OAIG;IACH,2BAHoB,YAAY,UAAU,CAAC,KAAG,IAAI,GACrC,SAAS,CAKrB;IAED;;;;OAIG;IACH,2CAHoB,YAAY,0BAA0B,CAAC,KAAG,IAAI,GACrD,SAAS,CAKrB;IAED;;;;OAIG;IACH,mCAHoB,YAAY,kBAAkB,CAAC,KAAG,IAAI,GAC7C,SAAS,CAKrB;IAED;;;;OAIG;IACH,oCAHoB,YAAY,mBAAmB,CAAC,KAAG,IAAI,GAC9C,SAAS,CAKrB;IAED;;;;OAIG;IACH,+BAHoB,YAAY,cAAc,CAAC,KAAG,IAAI,GACzC,SAAS,CAKrB;IAED;;;;OAIG;IACH,gCAHoB,YAAY,eAAe,CAAC,KAAG,IAAI,GAC1C,SAAS,CAKrB;IAED,qFAAqF;IACrF,SADc,QAAQ,IAAI,CAAC,CAmV1B;CACF;wBA5sBa,OAAO,WAAW,EAAE,SAAS;;WAiD7B,MAAM;YACN,MAAM;cACN,MAAM;eACN,YAAY;uBACZ,MAAM;kBACN,MAAM;qBACN,MAAM;;;;;;4BAKN,MAAM;;;;sBACN,MAAM;;;;wBACN,MAAM;;;;uBACN,MAAM;;;;wBACN,MAAM;;;iBAKN,MAAM;iBACN,MAAM;UACN,MAAM;UACN,MAAM;YACN,MAAM;YACN,MAAM;WACN,MAAM;WACN,MAAM;;;SAKN,MAAM;WACN,MAAM;UACN,MAAM;;;aAKN,QAAQ,EAAE;;;;;;sBAKV,MAAM;;;;qBACN,MAAM;;;;sBACN,MAAM;;;;uBACN,MAAM;;;;mBACN,MAAM,EAAE;;;kBAKR,UAAU;;;aAKV,MAAM;iBACN,MAAM;;;aAKN,MAAM;;;;uBACN,MAAM;qBACN,UAAU;;;aAKV,MAAM;;;;qBACN,MAAM;;;;uBACN,MAAM;kBACN,MAAM;wBACN,MAAM;;;;UACN,UAAU;;;;;;gBAKV,MAAM;;;;UACN,MAAM;;;;YACN,MAAM;;;;WACN,MAAM;;;;mBACN,MAAM;;;;;;UAKN,MAAM;;;;WACN,MAAM;;;;SACN,MAAM;;;;UACN,MAAM;;;;YACN,MAAM;;;;YACN,MAAM;;;mBAUN,MAAM;mBACN,MAAM;;;;;;;;;;;iBAQN,MAAM,EAAE;;;SAKR,MAAM;WACN,MAAM;UACN,MAAM;WACN,MAAM;eACN,MAAM;;;iBAKN,MAAM;;;;iBACN,MAAM;aACN,wBAAwB,EAAE;;;;;;YAK1B,MAAM;eACN,MAAM;;;;iBACN,UAAU;SACV,MAAM"} \ No newline at end of file diff --git a/types/image/webp-shim/webp-shim.d.ts b/types/image/webp-shim/webp-shim.d.ts deleted file mode 100644 index b6584f7..0000000 --- a/types/image/webp-shim/webp-shim.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @param {ArrayBuffer|Uint8Array} webpBuffer The byte array containing the WebP image bytes. - * @returns {Promise} A Promise resolving to a byte array containing the PNG bytes. - */ -export function convertWebPtoPNG(webpBuffer: ArrayBuffer | Uint8Array): Promise; -/** - * @param {ArrayBuffer|Uint8Array} webpBuffer The byte array containing the WebP image bytes. - * @returns {Promise} A Promise resolving to a byte array containing the JPG bytes. - */ -export function convertWebPtoJPG(webpBuffer: ArrayBuffer | Uint8Array): Promise; -//# sourceMappingURL=webp-shim.d.ts.map \ No newline at end of file diff --git a/types/image/webp-shim/webp-shim.d.ts.map b/types/image/webp-shim/webp-shim.d.ts.map deleted file mode 100644 index 37cf2f0..0000000 --- a/types/image/webp-shim/webp-shim.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"webp-shim.d.ts","sourceRoot":"","sources":["../../../image/webp-shim/webp-shim.js"],"names":[],"mappings":"AAkDA;;;GAGG;AACH,6CAHW,WAAW,GAAC,UAAU,GACpB,QAAQ,WAAW,CAAC,CAyBhC;AAED;;;GAGG;AACH,6CAHW,WAAW,GAAC,UAAU,GACpB,QAAQ,WAAW,CAAC,CAyBhC"} \ No newline at end of file diff --git a/types/index.d.ts b/types/index.d.ts deleted file mode 100644 index e401c34..0000000 --- a/types/index.d.ts +++ /dev/null @@ -1,47 +0,0 @@ -export { findMimeType } from "./file/sniffer.js"; -export { BitBuffer } from "./io/bitbuffer.js"; -export { BitStream } from "./io/bitstream.js"; -export { ByteBuffer } from "./io/bytebuffer.js"; -export { ByteStream } from "./io/bytestream.js"; -export type ProbeStream = import('./codecs/codecs.js').ProbeStream; -export type ProbeFormat = import('./codecs/codecs.js').ProbeFormat; -export type ProbeInfo = import('./codecs/codecs.js').ProbeInfo; -export type GifApplicationExtension = import('./image/parsers/gif.js').GifApplicationExtension; -export type GifColor = import('./image/parsers/gif.js').GifColor; -export type GifCommentExtension = import('./image/parsers/gif.js').GifCommentExtension; -export type GifGraphicControlExtension = import('./image/parsers/gif.js').GifGraphicControlExtension; -export type GifHeader = import('./image/parsers/gif.js').GifHeader; -export type GifLogicalScreen = import('./image/parsers/gif.js').GifLogicalScreen; -export type GifPlainTextExtension = import('./image/parsers/gif.js').GifPlainTextExtension; -export type GifTableBasedImage = import('./image/parsers/gif.js').GifTableBasedImage; -export type JpegApp0Extension = import('./image/parsers/jpeg.js').JpegApp0Extension; -export type JpegApp0Marker = import('./image/parsers/jpeg.js').JpegApp0Marker; -export type JpegComponentDetail = import('./image/parsers/jpeg.js').JpegComponentDetail; -export type JpegDefineHuffmanTable = import('./image/parsers/jpeg.js').JpegDefineHuffmanTable; -export type JpegDefineQuantizationTable = import('./image/parsers/jpeg.js').JpegDefineQuantizationTable; -export type JpegStartOfFrame = import('./image/parsers/jpeg.js').JpegStartOfFrame; -export type JpegStartOfScan = import('./image/parsers/jpeg.js').JpegStartOfScan; -export type PngBackgroundColor = import('./image/parsers/png.js').PngBackgroundColor; -export type PngChromaticies = import('./image/parsers/png.js').PngChromaticities; -export type PngColor = import('./image/parsers/png.js').PngColor; -export type PngCompressedTextualData = import('./image/parsers/png.js').PngCompressedTextualData; -export type PngHistogram = import('./image/parsers/png.js').PngHistogram; -export type PngImageData = import('./image/parsers/png.js').PngImageData; -export type PngImageGamma = import('./image/parsers/png.js').PngImageGamma; -export type PngImageHeader = import('./image/parsers/png.js').PngImageHeader; -export type PngIntlTextualData = import('./image/parsers/png.js').PngIntlTextualData; -export type PngLastModTime = import('./image/parsers/png.js').PngLastModTime; -export type PngPalette = import('./image/parsers/png.js').PngPalette; -export type PngPhysicalPixelDimensions = import('./image/parsers/png.js').PngPhysicalPixelDimensions; -export type PngSignificantBits = import('./image/parsers/png.js').PngSignificantBits; -export type PngSuggestedPalette = import('./image/parsers/png.js').PngSuggestedPalette; -export type PngSuggestedPaletteEntry = import('./image/parsers/png.js').PngSuggestedPaletteEntry; -export type PngTextualData = import('./image/parsers/png.js').PngTextualData; -export type PngTransparency = import('./image/parsers/png.js').PngTransparency; -export { UnarchiveEvent, UnarchiveEventType, UnarchiveInfoEvent, UnarchiveErrorEvent, UnarchiveStartEvent, UnarchiveFinishEvent, UnarchiveProgressEvent, UnarchiveExtractEvent, Unarchiver, Unzipper, Unrarrer, Untarrer, getUnarchiver } from "./archive/decompress.js"; -export { getFullMIMEString, getShortMIMEString } from "./codecs/codecs.js"; -export { GifParseEventType, GifParser } from "./image/parsers/gif.js"; -export { JpegComponentType, JpegDctType, JpegDensityUnits, JpegExtensionThumbnailFormat, JpegHuffmanTableType, JpegParseEventType, JpegParser, JpegSegmentType } from "./image/parsers/jpeg.js"; -export { PngColorType, PngInterlaceMethod, PngParseEventType, PngParser, PngUnitSpecifier } from "./image/parsers/png.js"; -export { convertWebPtoPNG, convertWebPtoJPG } from "./image/webp-shim/webp-shim.js"; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/types/index.d.ts.map b/types/index.d.ts.map deleted file mode 100644 index 5326334..0000000 --- a/types/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.js"],"names":[],"mappings":";;;;;0BAQc,OAAO,oBAAoB,EAAE,WAAW;0BACxC,OAAO,oBAAoB,EAAE,WAAW;wBACxC,OAAO,oBAAoB,EAAE,SAAS;sCAEtC,OAAO,wBAAwB,EAAE,uBAAuB;uBACxD,OAAO,wBAAwB,EAAE,QAAQ;kCACzC,OAAO,wBAAwB,EAAE,mBAAmB;yCACpD,OAAO,wBAAwB,EAAE,0BAA0B;wBAC3D,OAAO,wBAAwB,EAAE,SAAS;+BAC1C,OAAO,wBAAwB,EAAE,gBAAgB;oCACjD,OAAO,wBAAwB,EAAE,qBAAqB;iCACtD,OAAO,wBAAwB,EAAE,kBAAkB;gCAEnD,OAAO,yBAAyB,EAAE,iBAAiB;6BACnD,OAAO,yBAAyB,EAAE,cAAc;kCAChD,OAAO,yBAAyB,EAAE,mBAAmB;qCACrD,OAAO,yBAAyB,EAAE,sBAAsB;0CACxD,OAAO,yBAAyB,EAAE,2BAA2B;+BAC7D,OAAO,yBAAyB,EAAE,gBAAgB;8BAClD,OAAO,yBAAyB,EAAE,eAAe;iCAEjD,OAAO,wBAAwB,EAAE,kBAAkB;8BACnD,OAAO,wBAAwB,EAAE,iBAAiB;uBAClD,OAAO,wBAAwB,EAAE,QAAQ;uCACzC,OAAO,wBAAwB,EAAE,wBAAwB;2BACzD,OAAO,wBAAwB,EAAE,YAAY;2BAC7C,OAAO,wBAAwB,EAAE,YAAY;4BAC7C,OAAO,wBAAwB,EAAE,aAAa;6BAC9C,OAAO,wBAAwB,EAAE,cAAc;iCAC/C,OAAO,wBAAwB,EAAE,kBAAkB;6BACnD,OAAO,wBAAwB,EAAE,cAAc;yBAC/C,OAAO,wBAAwB,EAAE,UAAU;yCAC3C,OAAO,wBAAwB,EAAE,0BAA0B;iCAC3D,OAAO,wBAAwB,EAAE,kBAAkB;kCACnD,OAAO,wBAAwB,EAAE,mBAAmB;uCACpD,OAAO,wBAAwB,EAAE,wBAAwB;6BACzD,OAAO,wBAAwB,EAAE,cAAc;8BAC/C,OAAO,wBAAwB,EAAE,eAAe"} \ No newline at end of file diff --git a/types/io/bitbuffer.d.ts b/types/io/bitbuffer.d.ts deleted file mode 100644 index dd99d04..0000000 --- a/types/io/bitbuffer.d.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** - * A write-only Bit buffer which uses a Uint8Array as a backing store. - */ -export class BitBuffer { - /** - * @param {number} numBytes The number of bytes to allocate. - * @param {boolean} mtl The bit-packing mode. True means pack bits from most-significant (7) to - * least-significant (0). Defaults false: least-significant (0) to most-significant (8). - */ - constructor(numBytes: number, mtl?: boolean); - /** - * @type {Uint8Array} - * @public - */ - public data: Uint8Array; - /** - * Whether we pack bits from most-significant-bit to least. Defaults false (least-to-most - * significant bit packing). - * @type {boolean} - * @private - */ - private mtl; - /** - * The current byte we are filling with bits. - * @type {number} - * @public - */ - public bytePtr: number; - /** - * Points at the bit within the current byte where the next bit will go. This number ranges - * from 0 to 7 and the direction of packing is indicated by the mtl property. - * @type {number} - * @public - */ - public bitPtr: number; - /** @returns {boolean} */ - getPackingDirection(): boolean; - /** - * Sets the bit-packing direction. Default (false) is least-significant-bit (0) to - * most-significant (7). Changing the bit-packing direction when the bit pointer is in the - * middle of a byte will fill the rest of that byte with 0s using the current bit-packing - * direction and then set the bit pointer to the appropriate bit of the next byte. If there - * are no more bytes left in this buffer, it will throw an error. - */ - setPackingDirection(mtl?: boolean): void; - /** - * writeBits(3, 6) is the same as writeBits(0b000011, 6). - * Will throw an error (without writing) if this would over-flow the buffer. - * @param {number} val The bits to pack into the buffer. Negative values are not allowed. - * @param {number} numBits Must be positive, non-zero and less or equal to than 53, since - * JavaScript can only support 53-bit integers. - */ - writeBits(val: number, numBits: number): void; -} -//# sourceMappingURL=bitbuffer.d.ts.map \ No newline at end of file diff --git a/types/io/bitbuffer.d.ts.map b/types/io/bitbuffer.d.ts.map deleted file mode 100644 index cf8d0d0..0000000 --- a/types/io/bitbuffer.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"bitbuffer.d.ts","sourceRoot":"","sources":["../../io/bitbuffer.js"],"names":[],"mappings":"AAuBA;;GAEG;AACH;IACE;;;;OAIG;IACH,sBAJW,MAAM,QACN,OAAO,EAoCjB;IA5BC;;;OAGG;IACH,aAHU,UAAU,CAGgB;IAEpC;;;;;OAKG;IACH,YAAc;IAEd;;;;OAIG;IACH,gBAHU,MAAM,CAGA;IAEhB;;;;;OAKG;IACH,eAHU,MAAM,CAGc;IAKhC,yBAAyB;IACzB,uBADc,OAAO,CAGpB;IAED;;;;;;OAMG;IACH,yCAkBC;IAED;;;;;;OAMG;IACH,eAJW,MAAM,WACN,MAAM,QA4FhB;CACF"} \ No newline at end of file diff --git a/types/io/bitstream.d.ts b/types/io/bitstream.d.ts deleted file mode 100644 index 450228d..0000000 --- a/types/io/bitstream.d.ts +++ /dev/null @@ -1,132 +0,0 @@ -/** - * This object allows you to peek and consume bits and bytes out of a stream. - * Note that this stream is optimized, and thus, will *NOT* throw an error if - * the end of the stream is reached. Only use this in scenarios where you - * already have all the bits you need. - * - * Bit reading always proceeds from the first byte in the buffer, to the - * second byte, and so on. The MTL flag controls which bit is considered - * first *inside* the byte. The default is least-to-most direction. - * - * An Example for how Most-To-Least vs Least-to-Most mode works: - * - * If you have an ArrayBuffer with the following two Uint8s: - * 185 (0b10111001) and 66 (0b01000010) - * and you perform a series of readBits: 2 bits, then 3, then 5, then 6. - * - * A BitStream in "mtl" mode will yield the following: - * - readBits(2) => 2 ('10') - * - readBits(3) => 7 ('111') - * - readBits(5) => 5 ('00101') - * - readBits(6) => 2 ('000010') - * - * A BitStream in "ltm" mode will yield the following: - * - readBits(2) => 1 ('01') - * - readBits(3) => 6 ('110') - * - readBits(5) => 21 ('10101') - * - readBits(6) => 16 ('010000') - */ -export class BitStream { - /** - * @param {ArrayBuffer} ab An ArrayBuffer object. - * @param {boolean} mtl Whether the stream reads bits from the byte starting with the - * most-significant-bit (bit 7) to least-significant (bit 0). False means the direction is - * from least-significant-bit (bit 0) to most-significant (bit 7). - * @param {Number} opt_offset The offset into the ArrayBuffer - * @param {Number} opt_length The length of this BitStream - */ - constructor(ab: ArrayBuffer, mtl: boolean, opt_offset: number, opt_length: number); - /** - * The bytes in the stream. - * @type {Uint8Array} - * @private - */ - private bytes; - /** - * The byte in the stream that we are currently on. - * @type {Number} - * @private - */ - private bytePtr; - /** - * The bit in the current byte that we will read next (can have values 0 through 7). - * @type {Number} - * @private - */ - private bitPtr; - /** - * An ever-increasing number. - * @type {Number} - * @private - */ - private bitsRead_; - peekBits: (n: number, opt_movePointers: any) => number; - /** - * Returns how many bits have been read in the stream since the beginning of time. - * @returns {number} - */ - getNumBitsRead(): number; - /** - * Returns how many bits are currently in the stream left to be read. - * @returns {number} - */ - getNumBitsLeft(): number; - /** - * byte0 byte1 byte2 byte3 - * 7......0 | 7......0 | 7......0 | 7......0 - * - * The bit pointer starts at least-significant bit (0) of byte0 and moves left until it reaches - * bit7 of byte0, then jumps to bit0 of byte1, etc. - * @param {number} n The number of bits to peek, must be a positive integer. - * @param {boolean=} movePointers Whether to move the pointer, defaults false. - * @returns {number} The peeked bits, as an unsigned number. - */ - peekBits_ltm(n: number, opt_movePointers: any): number; - /** - * byte0 byte1 byte2 byte3 - * 7......0 | 7......0 | 7......0 | 7......0 - * - * The bit pointer starts at bit7 of byte0 and moves right until it reaches - * bit0 of byte0, then goes to bit7 of byte1, etc. - * @param {number} n The number of bits to peek. Must be a positive integer. - * @param {boolean=} movePointers Whether to move the pointer, defaults false. - * @returns {number} The peeked bits, as an unsigned number. - */ - peekBits_mtl(n: number, opt_movePointers: any): number; - /** - * Peek at 16 bits from current position in the buffer. - * Bit at (bytePtr,bitPtr) has the highest position in returning data. - * Taken from getbits.hpp in unrar. - * TODO: Move this out of BitStream and into unrar. - * @returns {number} - */ - getBits(): number; - /** - * Reads n bits out of the stream, consuming them (moving the bit pointer). - * @param {number} n The number of bits to read. Must be a positive integer. - * @returns {number} The read bits, as an unsigned number. - */ - readBits(n: number): number; - /** - * This returns n bytes as a sub-array, advancing the pointer if movePointers - * is true. Only use this for uncompressed blocks as this throws away remaining - * bits in the current byte. - * @param {number} n The number of bytes to peek. Must be a positive integer. - * @param {boolean=} movePointers Whether to move the pointer, defaults false. - * @returns {Uint8Array} The subarray. - */ - peekBytes(n: number, opt_movePointers: any): Uint8Array; - /** - * @param {number} n The number of bytes to read. - * @returns {Uint8Array} The subarray. - */ - readBytes(n: number): Uint8Array; - /** - * Skips n bits in the stream. Will throw an error if n is < 0 or greater than the number of - * bits left in the stream. - * @param {number} n The number of bits to skip. Must be a positive integer. - * @returns {BitStream} Returns this BitStream for chaining. - */ - skip(n: number): BitStream; -} -//# sourceMappingURL=bitstream.d.ts.map \ No newline at end of file diff --git a/types/io/bitstream.d.ts.map b/types/io/bitstream.d.ts.map deleted file mode 100644 index c8f9e71..0000000 --- a/types/io/bitstream.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"bitstream.d.ts","sourceRoot":"","sources":["../../io/bitstream.js"],"names":[],"mappings":"AAcA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH;IACE;;;;;;;OAOG;IACH,gBAPW,WAAW,OACX,OAAO,0CA2CjB;IA7BC;;;;OAIG;IACH,cAA+C;IAE/C;;;;OAIG;IACH,gBAAgB;IAEhB;;;;OAIG;IACH,eAAe;IAEf;;;;OAIG;IACH,kBAAkB;IAElB,cAsFS,MAAM,4BAEJ,MAAM,CAxF0C;IAG7D;;;OAGG;IACH,kBAFa,MAAM,CAIlB;IAED;;;OAGG;IACH,kBAFa,MAAM,CAKlB;IAED;;;;;;;;;OASG;IACH,gBAJW,MAAM,0BAEJ,MAAM,CAkDlB;IAED;;;;;;;;;OASG;IACH,gBAJW,MAAM,0BAEJ,MAAM,CAgDlB;IAED;;;;;;OAMG;IACH,WAFa,MAAM,CAMlB;IAED;;;;OAIG;IACH,YAHW,MAAM,GACJ,MAAM,CAIlB;IAED;;;;;;;OAOG;IACH,aAJW,MAAM,0BAEJ,UAAU,CAiDtB;IAED;;;OAGG;IACH,aAHW,MAAM,GACJ,UAAU,CAItB;IAED;;;;;OAKG;IACH,QAHW,MAAM,GACJ,SAAS,CAoBrB;CACF"} \ No newline at end of file diff --git a/types/io/bytebuffer.d.ts b/types/io/bytebuffer.d.ts deleted file mode 100644 index c2952be..0000000 --- a/types/io/bytebuffer.d.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * A write-only Byte buffer which uses a Uint8 Typed Array as a backing store. - */ -export class ByteBuffer { - /** - * @param {number} numBytes The number of bytes to allocate. - */ - constructor(numBytes: number); - /** - * @type {Uint8Array} - * @public - */ - public data: Uint8Array; - /** - * Points to the byte that will next be written. - * @type {number} - * @public - */ - public ptr: number; - /** - * Returns an exact copy of all the data that has been written to the ByteBuffer. - * @returns {Uint8Array} - */ - getData(): Uint8Array; - /** - * @param {number} b The byte to insert. - */ - insertByte(b: number): void; - /** - * @param {Array.|Uint8Array|Int8Array} bytes The bytes to insert. - */ - insertBytes(bytes: Array | Uint8Array | Int8Array): void; - /** - * Writes an unsigned number into the next n bytes. If the number is too large - * to fit into n bytes or is negative, an error is thrown. - * @param {number} num The unsigned number to write. - * @param {number} numBytes The number of bytes to write the number into. - */ - writeNumber(num: number, numBytes: number): void; - /** - * Writes a signed number into the next n bytes. If the number is too large - * to fit into n bytes, an error is thrown. - * @param {number} num The signed number to write. - * @param {number} numBytes The number of bytes to write the number into. - */ - writeSignedNumber(num: number, numBytes: number): void; - /** - * @param {string} str The ASCII string to write. - */ - writeASCIIString(str: string): void; -} -//# sourceMappingURL=bytebuffer.d.ts.map \ No newline at end of file diff --git a/types/io/bytebuffer.d.ts.map b/types/io/bytebuffer.d.ts.map deleted file mode 100644 index 811c7fb..0000000 --- a/types/io/bytebuffer.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"bytebuffer.d.ts","sourceRoot":"","sources":["../../io/bytebuffer.js"],"names":[],"mappings":"AAaA;;GAEG;AACH;IACE;;OAEG;IACH,sBAFW,MAAM,EAmBhB;IAZC;;;OAGG;IACH,aAHU,UAAU,CAGgB;IAEpC;;;;OAIG;IACH,YAHU,MAAM,CAGJ;IAGd;;;OAGG;IACH,WAFa,UAAU,CAMtB;IAED;;OAEG;IACH,cAFW,MAAM,QAShB;IAED;;OAEG;IACH,mBAFW,MAAO,MAAM,CAAC,GAAC,UAAU,GAAC,SAAS,QAU7C;IAED;;;;;OAKG;IACH,iBAHW,MAAM,YACN,MAAM,QAuBhB;IAED;;;;;OAKG;IACH,uBAHW,MAAM,YACN,MAAM,QAqBhB;IAED;;OAEG;IACH,sBAFW,MAAM,QAUhB;CACF"} \ No newline at end of file diff --git a/types/io/bytestream.d.ts b/types/io/bytestream.d.ts deleted file mode 100644 index cfcf1f2..0000000 --- a/types/io/bytestream.d.ts +++ /dev/null @@ -1,150 +0,0 @@ -/** - * This object allows you to peek and consume bytes as numbers and strings out - * of a stream. More bytes can be pushed into the back of the stream via the - * push() method. - * By default, the stream is Little Endian (that is the least significant byte - * is first). To change to Big Endian, use setBigEndian(). - */ -export class ByteStream { - /** - * @param {ArrayBuffer} ab The ArrayBuffer object. - * @param {number=} opt_offset The offset into the ArrayBuffer - * @param {number=} opt_length The length of this ByteStream - */ - constructor(ab: ArrayBuffer, opt_offset?: number | undefined, opt_length?: number | undefined); - /** - * The current page of bytes in the stream. - * @type {Uint8Array} - * @private - */ - private bytes; - /** - * The next pages of bytes in the stream. - * @type {Array} - * @private - */ - private pages_; - /** - * The byte in the current page that we will read next. - * @type {Number} - * @private - */ - private ptr; - /** - * An ever-increasing number. - * @type {Number} - * @private - */ - private bytesRead_; - /** - * Whether the stream is little-endian (true) or big-endian (false). - * @type {boolean} - * @private - */ - private littleEndian_; - /** @returns {boolean} Whether the stream is little-endian (least significant byte is first). */ - isLittleEndian(): boolean; - /** - * Big-Endian means the most significant byte is first. it is sometimes called Motorola-style. - * @param {boolean=} val The value to set. If not present, the stream is set to big-endian. - */ - setBigEndian(val?: boolean | undefined): void; - /** - * Little-Endian means the least significant byte is ifrst. is sometimes called Intel-style. - * @param {boolean=} val The value to set. If not present, the stream is set to little-endian. - */ - setLittleEndian(val?: boolean | undefined): void; - /** - * Returns how many bytes have been consumed (read or skipped) since the beginning of time. - * @returns {number} - */ - getNumBytesRead(): number; - /** - * Returns how many bytes are currently in the stream left to be read. - * @returns {number} - */ - getNumBytesLeft(): number; - /** - * Move the pointer ahead n bytes. If the pointer is at the end of the current array - * of bytes and we have another page of bytes, point at the new page. This is a private - * method, no validation is done. - * @param {number} n Number of bytes to increment. - * @private - */ - private movePointer_; - /** - * Peeks at the next n bytes as an unsigned number but does not advance the - * pointer. - * @param {number} n The number of bytes to peek at. Must be a positive integer. - * @returns {number} The n bytes interpreted as an unsigned number. - */ - peekNumber(n: number): number; - /** - * Returns the next n bytes as an unsigned number (or -1 on error) - * and advances the stream pointer n bytes. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {number} The n bytes interpreted as an unsigned number. - */ - readNumber(n: number): number; - /** - * Returns the next n bytes as a signed number but does not advance the - * pointer. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {number} The bytes interpreted as a signed number. - */ - peekSignedNumber(n: number): number; - /** - * Returns the next n bytes as a signed number and advances the stream pointer. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {number} The bytes interpreted as a signed number. - */ - readSignedNumber(n: number): number; - /** - * This returns n bytes as a sub-array, advancing the pointer if movePointers - * is true. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @param {boolean} movePointers Whether to move the pointers. - * @returns {Uint8Array} The subarray. - */ - peekBytes(n: number, movePointers: boolean): Uint8Array; - /** - * Reads the next n bytes as a sub-array. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {Uint8Array} The subarray. - */ - readBytes(n: number): Uint8Array; - /** - * Peeks at the next n bytes as an ASCII string but does not advance the pointer. - * @param {number} n The number of bytes to peek at. Must be a positive integer. - * @returns {string} The next n bytes as a string. - */ - peekString(n: number): string; - /** - * Returns the next n bytes as an ASCII string and advances the stream pointer - * n bytes. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {string} The next n bytes as a string. - */ - readString(n: number): string; - /** - * Skips n bytes in the stream. - * @param {number} n The number of bytes to skip. Must be a positive integer. - * @returns {ByteStream} Returns this ByteStream for chaining. - */ - skip(n: number): ByteStream; - /** - * Feeds more bytes into the back of the stream. - * @param {ArrayBuffer} ab - */ - push(ab: ArrayBuffer): void; - /** - * Creates a new ByteStream from this ByteStream that can be read / peeked. - * Note that the teed stream is a disconnected copy. If you push more bytes to the original - * stream, the copy does not get them. - * TODO: Assess whether the above causes more bugs than it avoids. (It would feel weird to me if - * the teed stream shared some state with the original stream.) - * @returns {ByteStream} A clone of this ByteStream. - */ - tee(): ByteStream; -} -//# sourceMappingURL=bytestream.d.ts.map \ No newline at end of file diff --git a/types/io/bytestream.d.ts.map b/types/io/bytestream.d.ts.map deleted file mode 100644 index 509176c..0000000 --- a/types/io/bytestream.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"bytestream.d.ts","sourceRoot":"","sources":["../../io/bytestream.js"],"names":[],"mappings":"AAWA;;;;;;GAMG;AACH;IACE;;;;OAIG;IACH,gBAJW,WAAW,eACX,MAAM,2BACN,MAAM,cA4ChB;IAlCC;;;;OAIG;IACH,cAA+C;IAE/C;;;;OAIG;IACH,eAAgB;IAEhB;;;;OAIG;IACH,YAAY;IAEZ;;;;OAIG;IACH,mBAAmB;IAEnB;;;;OAIG;IACH,sBAAyB;IAG3B,gGAAgG;IAChG,kBADc,OAAO,CAGpB;IAED;;;OAGG;IACH,mBAFW,OAAO,oBAIjB;IAED;;;OAGG;IACH,sBAFW,OAAO,oBAIjB;IAED;;;OAGG;IACH,mBAFa,MAAM,CAIlB;IAED;;;OAGG;IACH,mBAFa,MAAM,CAKlB;IAED;;;;;;OAMG;IACH,qBAOC;IAED;;;;;OAKG;IACH,cAHW,MAAM,GACJ,MAAM,CAmClB;IAGD;;;;;OAKG;IACH,cAHW,MAAM,GACJ,MAAM,CAMlB;IAGD;;;;;OAKG;IACH,oBAHW,MAAM,GACJ,MAAM,CAUlB;IAGD;;;;OAIG;IACH,oBAHW,MAAM,GACJ,MAAM,CAMlB;IAGD;;;;;;OAMG;IACH,aAJW,MAAM,gBACN,OAAO,GACL,UAAU,CAyCtB;IAED;;;;OAIG;IACH,aAHW,MAAM,GACJ,UAAU,CAItB;IAED;;;;OAIG;IACH,cAHW,MAAM,GACJ,MAAM,CA6BlB;IAED;;;;;OAKG;IACH,cAHW,MAAM,GACJ,MAAM,CAMlB;IAED;;;;OAIG;IACH,QAHW,MAAM,GACJ,UAAU,CAkBtB;IAED;;;OAGG;IACH,SAFW,WAAW,QAWrB;IAED;;;;;;;OAOG;IACH,OAFa,UAAU,CAUtB;CACF"} \ No newline at end of file