UNPKG

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
/** * 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;