brocha
Version:
Universal Brotli decompressor written in 100% TypeScript with zero dependencies. Blazing fast, lightweight, and compatible with any ES2015+ environment.
624 lines (576 loc) • 21.6 kB
TypeScript
/**
* Represents a stream of input data, either as a string or binary data buffer.
*
* All input types are converted to an `Int8Array` byte buffer prior to being
* processed by the decompression algorithm.
*/
export declare class InputStream {
offset: number;
data: Int8Array;
constructor(data: BufferSource, offset?: number);
}
/**
* The core of the Brotli decompression algorithm, implemented as a stateful
* class that can be used to decompress multiple chunks of data in sequence.
*
* This is used internally by the {@linkcode decompress} function, but can also
* be used directly to decompress data in a streaming fashion. It's a low-level
* API that provides fine-grained control over the decompression process, so
* it's not as user-friendly as the top-level function.
*/
export declare class State {
#private;
static readonly offsets: Int32Array;
static readonly sizeBits: Int32Array;
/**
* Returns a boolean indicating whether or not the default dictionary has
* been initialized. This is used to ensure that the dictionary is only
* initialized once, and that it's not re-initialized if a custom dictionary
* is provided.
* @internal
*/
static readonly initialized: boolean;
/**
* Returns the default dictionary data as a `Int8Array` buffer. This is the
* data that is used to initialize the decompression state when no custom
* dictionary is provided.
* @internal
*/
static readonly dictionaryData: Int8Array;
/**
* Updates the default dictionary data with new data and size bits, after
* validating the input data to ensure that it's consistent with the size
* bits and aligned correctly. This is used to set the default dictionary
* data that is used when no custom dictionary is provided.
*
* @param newData The new dictionary data to use.
* @param newSizeBits The size bits for the new dictionary data.
* @throws {BrotliDecoderDictionaryError} If the new data is inconsistent.
*/
static setDictionaryData(newData: Int8Array, newSizeBits: Int32Array): void;
/**
* Decompresses a Brotli-compressed input source (either from a string or a
* `BufferSource` object), optionally using a custom dictionary, and returns
* the decompressed data as a `Uint8Array` binary buffer.
*
* This is a convenience method for creating a new `State` instance, adding
* the input data, configuring the custom dictionary (if necessary), and then
* calling the `decompress` method on the instance.
*
* @param bytes The Brotli-compressed data to be decoded.
* @param dict A custom dictionary to be used during decompression.
* @returns The decompressed data, as a `Uint8Array` object.
*/
static decompress(
bytes: BufferSource,
dict?: BufferSource | null,
): Uint8Array;
static readonly RFC_TRANSFORMS: Transforms;
/**
* The input stream object that is being used to read the compressed data.
* This can be useful for checking the current offset into the input data, or
* for reading the input data directly. It can also be used to implement
* custom streaming input sources.
*/
readonly input: InputStream;
/**
* The total number of bytes that have been decompressed so far. When the
* decompression job is complete, this value will represent the total length
* of the decompressed data.
*/
totalOutput: number;
/** @internal */ outputUsed: number;
/** @internal */ outputOffset: number;
/** @internal */ outputLength: number;
/** @internal */ expectedTotalSize: number;
/** @internal */ runningState: number;
/** @internal */ nextRunningState: number;
/** @internal */ chunks: Int8Array[];
/** @internal */ byteBuffer: Int8Array;
/** @internal */ output: Int8Array;
/** @internal */ ringBuffer: Int8Array;
/** @internal */ contextModes: Int8Array;
/** @internal */ contextMap: Int8Array;
/** @internal */ distContextMap: Int8Array;
/** @internal */ distExtraBits: Int8Array;
/** @internal */ shortBuffer: Int16Array;
/** @internal */ rings: Int32Array;
/** @internal */ blockTrees: Int32Array;
/** @internal */ commandTreeGroup: Int32Array;
/** @internal */ distanceTreeGroup: Int32Array;
/** @internal */ literalTreeGroup: Int32Array;
/** @internal */ distOffset: Int32Array;
/** @internal */ bitOffset: number;
/** @internal */ halfOffset: number;
/** @internal */ accumulator32: number;
/** @internal */ tailBytes: number;
/** @internal */ endOfStreamReached: number;
/** @internal */ inputEnd: number;
/** @internal */ isUncompressed: number;
/** @internal */ isMetadata: number;
/** @internal */ isEager: number;
/** @internal */ isLargeWindow: number;
/** @internal */ numLiteralBlockTypes: number;
/** @internal */ numCommandBlockTypes: number;
/** @internal */ numDistanceBlockTypes: number;
/** @internal */ trivialLiteralContext: number;
/** @internal */ literalTreeIdx: number;
/** @internal */ commandTreeIdx: number;
/** @internal */ pos: number;
/** @internal */ j: number;
/** @internal */ contextMapSlice: number;
/** @internal */ contextLookupOffset1: number;
/** @internal */ contextLookupOffset2: number;
/** @internal */ numDirectDistanceCodes: number;
/** @internal */ insertLength: number;
/** @internal */ copyLength: number;
/** @internal */ metaBlockLength: number;
/** @internal */ commandBlockLength: number;
/** @internal */ literalBlockLength: number;
/** @internal */ distanceBlockLength: number;
/** @internal */ distContextMapSlice: number;
/** @internal */ distanceCode: number;
/** @internal */ distancePostfixBits: number;
/** @internal */ distance: number;
/** @internal */ distRbIdx: number;
/** @internal */ maxDistance: number;
/** @internal */ maxBackwardDistance: number;
/** @internal */ maxRingBufferSize: number;
/** @internal */ ringBufferSize: number;
/** @internal */ ringBufferBytesWritten: number;
/** @internal */ ringBufferBytesReady: number;
/** @internal */ cdNumChunks: number;
/** @internal */ cdTotalSize: number;
/** @internal */ cdBrIndex: number;
/** @internal */ cdBrOffset: number;
/** @internal */ cdBrLength: number;
/** @internal */ cdBrCopied: number;
/** @internal */ cdChunks: Int8Array[];
/** @internal */ cdChunkOffsets: Int32Array;
/** @internal */ cdBlockBits: number;
/** @internal */ cdBlockMap: Int8Array;
/**
* Creates a new `State` instance, optionally with a custom dictionary, and
* initializes the decompression job. The input data can be provided as a
* string or a `BufferSource` object, and the dictionary can be provided as
* a string, `BufferSource`, or `null` to use the default dictionary.
*
* This is the core of the Brotli decompression algorithm.
*/
constructor(bytes?: BufferSource, dict?: BufferSource | null);
/**
* Decompresses a Brotli-compressed input source (either from a string or a
* `BufferSource` object), optionally using a custom dictionary, and returns
* the decompressed data as a `Uint8Array` binary buffer.
*/
decompress(): Uint8Array;
/**
* Reads input data from the internal {@linkcode InputStream} into a given
* `Int8Array` byte buffer, reading from `offset` up to the given `length`.
* Used internally to consume the input stream during decompression.
*
* @param data The input buffer to read into.
* @param offset The offset within the input buffer to start reading from.
* @param length The maximum number of bytes to read from the input buffer.
* @returns The number of bytes that were read from the input buffer.
*/
readInput(data: Int8Array, offset: number, length: number): number;
protected init(): this;
protected initBitReader(): this;
protected close(): void;
protected prepare(): this;
protected reload(): this;
protected attachDictionaryChunk(buf: string | BufferSource): void;
protected initializeCompoundDictionary(): void;
protected initializeCompoundDictionaryCopy(
address: number,
length: number,
): void;
protected copyFromCompoundDictionary(fence: number): number;
protected copyRawBytes(data: Int8Array, offset: number, length: number): void;
protected copyUncompressedData(): void;
protected bytesToNibbles(byteLen: number): void;
protected calculateDistanceLut(alphabetSizeLimit: number): void;
protected calculateFence(): number;
protected decompressChunk(): void;
protected doReadMoreInput(): void;
protected doUseDictionary(fence: number): void;
protected checkHealth(endOfStream: number): void;
protected readFewBits(n: number): number;
protected readManyBits(n: number): number;
protected jumpToByteBoundary(): void;
protected halfAvailable(): number;
protected decodeWindowBits(): number;
protected decodeVarLenUnsignedByte(): number;
protected decodeMetaBlockLength(): void;
protected decodeLiteralBlockSwitch(): void;
protected decodeContextMap(
contextMapSize: number,
contextMap: Int8Array,
): number;
protected decodeBlockTypeAndLength(
treeType: number,
numBlockTypes: number,
): number;
protected decodeHuffmanTreeGroup(
alphabetSizeMax: number,
alphabetSizeLimit: number,
n: number,
): Int32Array;
protected moveToFront(v: Int32Array, index: number): void;
protected inverseMoveToFrontTransform(v: Int8Array, vLen: number): void;
protected maybeReallocateRingBuffer(): void;
protected readHuffmanCodeLengths(
codeLengthCodeLengths: Int32Array,
numSymbols: number,
codeLengths: Int32Array,
): void;
protected readNextMetablockHeader(): void;
protected readMetablockPartition(
treeType: number,
numBlockTypes: number,
): number;
protected readSimpleHuffmanCode(
alphabetSizeMax: number,
alphabetSizeLimit: number,
tableGroup: Int32Array,
tableIdx: number,
): number;
protected readComplexHuffmanCode(
alphabetSizeLimit: number,
skip: number,
tableGroup: Int32Array,
tableIdx: number,
): number;
protected readHuffmanCode(
alphabetSizeMax: number,
alphabetSizeLimit: number,
tableGroup: Int32Array,
tableIdx: number,
): number;
protected readSymbol(tableGroup: Int32Array, tableIdx: number): number;
protected readBlockLength(tableGroup: Int32Array, tableIdx: number): number;
protected calculateDistanceAlphabetLimit(
maxDistance: number,
npostfix: number,
ndirect: number,
): number;
protected readMetablockHuffmanCodesAndContextMaps(): void;
protected checkDupes(symbols: Int32Array, length: number): void;
protected writeRingBuffer(): number;
}
export declare class Transforms {
readonly numTransforms: number;
readonly prefixSuffixLen: number;
readonly prefixSuffixCount: number;
readonly triplets: Int32Array;
readonly prefixSuffixStorage: Int8Array;
readonly prefixSuffixHeads: Int32Array;
readonly params: Int16Array;
constructor(
numTransforms: number,
prefixSuffixLen: number,
prefixSuffixCount: number,
);
unpack(prefixSuffixSrc: string, transformsSrc: string): this;
transformDictionaryWord(
dst: Int8Array,
dstOffset: number,
src: Int8Array,
srcOffset: number,
wordLen: number,
transformIndex: number,
): number;
}
/**
* Represents the result codes that can be returned by the Brotli decoder.
*
* @category Errors
*/
export declare const enum BrotliDecoderResult {
ERROR = 0,
SUCCESS = 1,
NEEDS_MORE_INPUT = 2,
NEEDS_MORE_OUTPUT = 3,
}
/**
* Represents the error codes that can be returned by the Brotli decoder.
*
* @category Errors
*/
export declare const enum BrotliErrorCode {
UNKNOWN = -32,
UNREACHABLE = -31,
ALLOC_BLOCK_TYPE_TREES = -30,
ALLOC_RING_BUFFER_2 = -27,
ALLOC_RING_BUFFER_1 = -26,
ALLOC_CONTEXT_MAP = -25,
ALLOC_TREE_GROUPS = -22,
ALLOC_CONTEXT_MODES = -21,
INVALID_ARGUMENTS = -20,
DICTIONARY_NOT_SET = -19,
FORMAT_DISTANCE = -16,
FORMAT_PADDING_2 = -15,
FORMAT_PADDING_1 = -14,
FORMAT_WINDOW_BITS = -13,
FORMAT_DICTIONARY = -12,
FORMAT_TRANSFORM = -11,
FORMAT_BLOCK_LENGTH_2 = -10,
FORMAT_BLOCK_LENGTH_1 = -9,
FORMAT_CONTEXT_MAP_REPEAT = -8,
FORMAT_HUFFMAN_SPACE = -7,
FORMAT_CL_SPACE = -6,
FORMAT_SIMPLE_HUFFMAN_SAME = -5,
FORMAT_SIMPLE_HUFFMAN_ALPHABET = -4,
FORMAT_EXUBERANT_META_NIBBLE = -3,
FORMAT_RESERVED = -2,
FORMAT_EXUBERANT_NIBBLE = -1,
NO_ERROR = 0,
SUCCESS = 1,
NEEDS_MORE_INPUT = 2,
NEEDS_MORE_OUTPUT = 3,
}
/**
* Represents a Brotli decoder error.
* @category Errors
*/
type BROTLI_ERROR_NAMES = keyof {
[
K in keyof typeof BrotliErrorCode as K extends `${number}` | number ? never
: K
]: K;
};
export interface BrotliErrorOptions<
K extends BROTLI_ERROR_NAMES = BROTLI_ERROR_NAMES,
> extends ErrorOptions {
code?: typeof BrotliErrorCode[K] | undefined;
name?: K | undefined;
message?: string | undefined;
cause?: string | Error;
}
export interface BrotliErrorJson<K extends BROTLI_ERROR_NAMES> {
code: typeof BrotliErrorCode[K];
name: K;
message: string;
cause?: string | Error | undefined;
}
declare abstract class BrotliError<K extends BROTLI_ERROR_NAMES> extends Error {
abstract readonly code: typeof BrotliErrorCode[K];
readonly name: K;
readonly message: string;
readonly options: BrotliErrorOptions<K>;
readonly cause: string | Error | undefined;
constructor(message?: string, options?: BrotliErrorOptions<K>);
toJSON(): BrotliErrorJson<K>;
static from<K extends BROTLI_ERROR_NAMES>(
json: BrotliErrorJson<K>,
): Extract<AnyBrotliError, { readonly name: K }>;
}
declare type AnyBrotliError =
| BrotliDecoderUnknownError
| BrotliDecoderUnreachableError
| BrotliDecoderAllocBlockTypeTreesError
| BrotliDecoderAllocRingBuffer2Error
| BrotliDecoderAllocRingBuffer1Error
| BrotliDecoderAllocContextMapError
| BrotliDecoderAllocTreeGroupsError
| BrotliDecoderAllocContextModesError
| BrotliDecoderInvalidArgumentsError
| BrotliDecoderDictionaryNotSetError
| BrotliDecoderDistanceError
| BrotliDecoderPadding2Error
| BrotliDecoderPadding1Error
| BrotliDecoderWindowBitsError
| BrotliDecoderDictionaryError
| BrotliDecoderTransformError
| BrotliDecoderBlockLength2Error
| BrotliDecoderBlockLength1Error
| BrotliDecoderContextMapRepeatError
| BrotliDecoderHuffmanSpaceError
| BrotliDecoderClSpaceError
| BrotliDecoderSimpleHuffmanSameError
| BrotliDecoderSimpleHuffmanAlphabetError
| BrotliDecoderExuberantMetaNibbleError
| BrotliDecoderReservedError
| BrotliDecoderExuberantNibbleError
| BrotliDecoderNoErrorResult
| BrotliDecoderSuccessResult
| BrotliDecoderNeedsMoreInputResult
| BrotliDecoderNeedsMoreOutputResult;
export declare class BrotliDecoderUnknownError extends BrotliError<"UNKNOWN"> {
readonly code: BrotliErrorCode.UNKNOWN;
}
export declare class BrotliDecoderUnreachableError
extends BrotliError<"UNREACHABLE"> {
readonly code: BrotliErrorCode.UNREACHABLE;
}
export declare class BrotliDecoderAllocBlockTypeTreesError
extends BrotliError<"ALLOC_BLOCK_TYPE_TREES"> {
readonly code: BrotliErrorCode.ALLOC_BLOCK_TYPE_TREES;
}
export declare class BrotliDecoderAllocRingBuffer2Error
extends BrotliError<"ALLOC_RING_BUFFER_2"> {
readonly code: BrotliErrorCode.ALLOC_RING_BUFFER_2;
}
export declare class BrotliDecoderAllocRingBuffer1Error
extends BrotliError<"ALLOC_RING_BUFFER_1"> {
readonly code: BrotliErrorCode.ALLOC_RING_BUFFER_1;
}
export declare class BrotliDecoderAllocContextMapError
extends BrotliError<"ALLOC_CONTEXT_MAP"> {
readonly code: BrotliErrorCode.ALLOC_CONTEXT_MAP;
}
export declare class BrotliDecoderAllocTreeGroupsError
extends BrotliError<"ALLOC_TREE_GROUPS"> {
readonly code: BrotliErrorCode.ALLOC_TREE_GROUPS;
}
export declare class BrotliDecoderAllocContextModesError
extends BrotliError<"ALLOC_CONTEXT_MODES"> {
readonly code: BrotliErrorCode.ALLOC_CONTEXT_MODES;
}
export declare class BrotliDecoderInvalidArgumentsError
extends BrotliError<"INVALID_ARGUMENTS"> {
readonly code: BrotliErrorCode.INVALID_ARGUMENTS;
}
export declare class BrotliDecoderDictionaryNotSetError
extends BrotliError<"DICTIONARY_NOT_SET"> {
readonly code: BrotliErrorCode.DICTIONARY_NOT_SET;
}
export declare class BrotliDecoderDistanceError
extends BrotliError<"FORMAT_DISTANCE"> {
readonly code: BrotliErrorCode.FORMAT_DISTANCE;
}
export declare class BrotliDecoderPadding2Error
extends BrotliError<"FORMAT_PADDING_2"> {
readonly code: BrotliErrorCode.FORMAT_PADDING_2;
}
export declare class BrotliDecoderPadding1Error
extends BrotliError<"FORMAT_PADDING_1"> {
readonly code: BrotliErrorCode.FORMAT_PADDING_1;
}
export declare class BrotliDecoderWindowBitsError
extends BrotliError<"FORMAT_WINDOW_BITS"> {
readonly code: BrotliErrorCode.FORMAT_WINDOW_BITS;
}
export declare class BrotliDecoderDictionaryError
extends BrotliError<"FORMAT_DICTIONARY"> {
readonly code: BrotliErrorCode.FORMAT_DICTIONARY;
}
export declare class BrotliDecoderTransformError
extends BrotliError<"FORMAT_TRANSFORM"> {
readonly code: BrotliErrorCode.FORMAT_TRANSFORM;
}
export declare class BrotliDecoderBlockLength2Error
extends BrotliError<"FORMAT_BLOCK_LENGTH_2"> {
readonly code: BrotliErrorCode.FORMAT_BLOCK_LENGTH_2;
}
export declare class BrotliDecoderBlockLength1Error
extends BrotliError<"FORMAT_BLOCK_LENGTH_1"> {
readonly code: BrotliErrorCode.FORMAT_BLOCK_LENGTH_1;
}
export declare class BrotliDecoderContextMapRepeatError
extends BrotliError<"FORMAT_CONTEXT_MAP_REPEAT"> {
readonly code: BrotliErrorCode.FORMAT_CONTEXT_MAP_REPEAT;
}
export declare class BrotliDecoderHuffmanSpaceError
extends BrotliError<"FORMAT_HUFFMAN_SPACE"> {
readonly code: BrotliErrorCode.FORMAT_HUFFMAN_SPACE;
}
export declare class BrotliDecoderClSpaceError
extends BrotliError<"FORMAT_CL_SPACE"> {
readonly code: BrotliErrorCode.FORMAT_CL_SPACE;
}
export declare class BrotliDecoderSimpleHuffmanSameError
extends BrotliError<"FORMAT_SIMPLE_HUFFMAN_SAME"> {
readonly code: BrotliErrorCode.FORMAT_SIMPLE_HUFFMAN_SAME;
}
export declare class BrotliDecoderSimpleHuffmanAlphabetError
extends BrotliError<"FORMAT_SIMPLE_HUFFMAN_ALPHABET"> {
readonly code: BrotliErrorCode.FORMAT_SIMPLE_HUFFMAN_ALPHABET;
}
export declare class BrotliDecoderExuberantMetaNibbleError
extends BrotliError<"FORMAT_EXUBERANT_META_NIBBLE"> {
readonly code: BrotliErrorCode.FORMAT_EXUBERANT_META_NIBBLE;
}
export declare class BrotliDecoderReservedError
extends BrotliError<"FORMAT_RESERVED"> {
readonly code: BrotliErrorCode.FORMAT_RESERVED;
}
export declare class BrotliDecoderExuberantNibbleError
extends BrotliError<"FORMAT_EXUBERANT_NIBBLE"> {
readonly code: BrotliErrorCode.FORMAT_EXUBERANT_NIBBLE;
}
export declare class BrotliDecoderNoErrorResult
extends BrotliError<"NO_ERROR"> {
readonly code: BrotliErrorCode.NO_ERROR;
}
export declare class BrotliDecoderSuccessResult extends BrotliError<"SUCCESS"> {
readonly code: BrotliErrorCode.SUCCESS;
}
export declare class BrotliDecoderNeedsMoreInputResult
extends BrotliError<"NEEDS_MORE_INPUT"> {
readonly code: BrotliErrorCode.NEEDS_MORE_INPUT;
}
export declare class BrotliDecoderNeedsMoreOutputResult
extends BrotliError<"NEEDS_MORE_OUTPUT"> {
readonly code: BrotliErrorCode.NEEDS_MORE_OUTPUT;
}
/**
* Options for the {@linkcode decompress} function, to customize the behavior
* of the Brotli decompression process. Currently the only supported option is
* a custom dictionary for the decompressor to use.
*
* @category Options
*/
export interface BrotliDecodeOptions {
/**
* Custom dictionary to use for the Brotli decompression process. This should
* be a BufferSource object containing text-based dictionary data, or `null`
* to use the default dictionary.
*
* @remarks
* The dictionary must be a valid Brotli dictionary, and must be the same
* dictionary that was used to compress the data. If the dictionary is not
* valid, or if it does not match the dictionary used to compress the data,
* the decompression process will almost certainly fail.
*/
customDictionary: BufferSource | null;
}
/**
* Decodes a `BufferSource` object containing Brotli-compressed data, returning
* the decompressed output as a new `Uint8Array` object.
*
* @param input The Brotli-compressed data to be decoded.
* @param [options] Custom decompression options (if any), allowing you to pass
* in a custom dictionary to the decoder. See {@linkcode BrotliDecodeOptions}
* for details on custom dictionary requirements and usage.
* @returns The decompressed data in a new `Uint8Array` buffer.
* @example Decompressing a Brotli-compressed file:
* ```ts no-eval
* import { decompress } from "brocha";
* import { readFileSync } from "node:fs";
*
* const compressed = readFileSync("data.wasm.br");
* const decompressed = decompress(compressed);
*
* const input = +(compressed.byteLength / 1024).toFixed(0);
* const output = +(decompressed.byteLength / 1024).toFixed(0);
* const delta = (
*
* console.log(`${input}K => ${output}K (${delta > 0 ? "+" : ""}${delta}%)`);
* // Example log: "115K => 483K (+4.2x)"
* ```
* @example Instantiating a brotli-compressed WebAssembly module:
* ```ts no-eval
* import { decompress } from "brocha";
* import { add, instantiate } from "./add.generated.js";
*
* await instantiate({ decompress });
* console.log(add(1, 2)); // 3
* ```
*/
export declare function decompress(
input: BufferSource,
options?: BrotliDecodeOptions,
): Uint8Array;
export default decompress;