@fengqinghai/bzip2-wasm
Version:
compress and decompress bzip2 buffers in node and browser
159 lines (120 loc) • 4.66 kB
JavaScript
import loadBZip2WASM from './bzip2-1.0.8/bzip2.mjs';
const ERROR_MESSAGES = {
'-2': 'BZ_PARAM_ERROR: incorrect parameters',
'-3': "BZ_MEM_ERROR: couldn't allocate enough memory",
'-4': 'BZ_DATA_ERROR: data integrity error when decompressing',
'-5': 'BZ_DATA_ERROR_MAGIC: compressed data has incorrect header',
'-7': 'BZ_UNEXPECTED_EOF: compressed data ends too early',
'-8': 'BZ_OUTBUFF_FULL: destination buffer is full'
};
class BZip2 {
constructor() {
this.wasmModule = undefined;
}
// fetch the wasm and initialize it
async init() {
if (this.wasmModule) {
return;
}
// check if node
// http://philiplassen.com/2021/08/11/node-es6-emscripten.html
if (typeof process !== 'undefined') {
const { dirname } = await import/* webpackIgnore: true */('path');
const { createRequire } = await import(/* webpackIgnore: true */'module');
globalThis.__dirname = dirname(import.meta.url);
globalThis.require = createRequire(import.meta.url);
}
this.wasmModule = await loadBZip2WASM();
}
ensureInitialized() {
if (!this.wasmModule) {
throw new Error(
`${this.constructor.name} not initalized. call .init()`
);
}
}
// turn bzip's integer return values into an error message and throw it.
// also free the destination pointers
handleError(returnValue, destPtr, destLengthPtr) {
if (returnValue === 0) {
return;
}
this.wasmModule._free(destPtr);
this.wasmModule._free(destLengthPtr);
const errorMessage = ERROR_MESSAGES[returnValue];
if (errorMessage) {
throw new Error(errorMessage);
}
throw new Error(`error code: ${returnValue}`);
}
// create source, destination and length buffers
createWASMBuffers(source, destLength) {
const { _malloc, setValue, HEAPU8 } = this.wasmModule;
const sourcePtr = _malloc(source.length);
HEAPU8.set(source, sourcePtr);
const destPtr = _malloc(destLength);
const destLengthPtr = _malloc(4);
setValue(destLengthPtr, destLength, 'i32');
return { sourcePtr, destPtr, destLengthPtr };
}
// read the length returned by bzip, create a new Uint8Array of that size
// and copy the decompressed/compressed data into it
createBuffer(destPtr, destLengthPtr) {
const { _free, getValue, HEAPU8 } = this.wasmModule;
const destLength = getValue(destLengthPtr, 'i32');
const dest = new Uint8Array(destLength);
dest.set(HEAPU8.subarray(destPtr, destPtr + destLength));
_free(destPtr);
_free(destLengthPtr);
return dest;
}
decompress(compressed, decompressedLength) {
this.ensureInitialized();
const {
sourcePtr: compressedPtr,
destPtr: decompressedPtr,
destLengthPtr: decompressedLengthPtr
} = this.createWASMBuffers(compressed, decompressedLength);
const returnValue = this.wasmModule._BZ2_bzBuffToBuffDecompress(
decompressedPtr,
decompressedLengthPtr,
compressedPtr,
compressed.length,
0,
0
);
this.wasmModule._free(compressedPtr);
this.handleError(returnValue, decompressedPtr, decompressedLengthPtr);
return this.createBuffer(decompressedPtr, decompressedLengthPtr);
}
compress(decompressed, blockSize = 5, compressedLength) {
this.ensureInitialized();
if (!compressedLength) {
compressedLength = decompressed.length;
}
if (compressedLength < 128) {
compressedLength = 128;
}
if (blockSize <= 0 || blockSize > 9) {
throw new RangeError('blockSize should be between 1-9');
}
const {
sourcePtr: decompressedPtr,
destPtr: compressedPtr,
destLengthPtr: compressedLengthPtr
} = this.createWASMBuffers(decompressed, compressedLength);
const returnValue = this.wasmModule._BZ2_bzBuffToBuffCompress(
compressedPtr,
compressedLengthPtr,
decompressedPtr,
decompressed.length,
blockSize,
0,
30
);
this.wasmModule._free(decompressedPtr);
this.handleError(returnValue, compressedPtr, compressedLengthPtr);
return this.createBuffer(compressedPtr, compressedLengthPtr);
}
}
export default BZip2;