UNPKG

@gmod/bgzf-filehandle

Version:

read from a compressed bgzip file (with .gzi) as if it were uncompressed

126 lines (112 loc) 3.44 kB
import pkg from 'pako' import { concatUint8Array } from './util.ts' //@ts-ignore const { Z_SYNC_FLUSH, Inflate } = pkg interface VirtualOffset { blockPosition: number dataPosition: number } interface Chunk { minv: VirtualOffset maxv: VirtualOffset } // browserify-zlib, which is the zlib shim used by default in webpacked code, // does not properly uncompress bgzf chunks that contain more than one bgzf // block, so export an unzip function that uses pako directly if we are running // in a browser. export async function unzip(inputData: Uint8Array) { try { let strm let pos = 0 let inflator const blocks = [] as Uint8Array[] do { const remainingInput = inputData.subarray(pos) inflator = new Inflate() //@ts-ignore ;({ strm } = inflator) inflator.push(remainingInput, Z_SYNC_FLUSH) if (inflator.err) { throw new Error(inflator.msg) } pos += strm.next_in blocks.push(inflator.result as Uint8Array) } while (strm.avail_in) return concatUint8Array(blocks) } catch (e) { // return a slightly more informative error message if (/incorrect header check/.exec(`${e}`)) { throw new Error( 'problem decompressing block: incorrect gzip header check', ) } throw e } } // keeps track of the position of compressed blocks in terms of file offsets, // and a decompressed equivalent // // also slices (0,minv.dataPosition) and (maxv.dataPosition,end) off export async function unzipChunkSlice(inputData: Uint8Array, chunk: Chunk) { try { let strm const { minv, maxv } = chunk let cpos = minv.blockPosition let dpos = minv.dataPosition const chunks = [] as Uint8Array[] const cpositions = [] as number[] const dpositions = [] as number[] let i = 0 do { const remainingInput = inputData.subarray(cpos - minv.blockPosition) const inflator = new Inflate() // @ts-ignore ;({ strm } = inflator) inflator.push(remainingInput, Z_SYNC_FLUSH) if (inflator.err) { throw new Error(inflator.msg) } const buffer = inflator.result chunks.push(buffer as Uint8Array) let len = buffer.length cpositions.push(cpos) dpositions.push(dpos) if (chunks.length === 1 && minv.dataPosition) { // this is the first chunk, trim it chunks[0] = chunks[0]!.subarray(minv.dataPosition) len = chunks[0].length } const origCpos = cpos cpos += strm.next_in dpos += len if (origCpos >= maxv.blockPosition) { // this is the last chunk, trim it and stop decompressing. note if it is // the same block is minv it subtracts that already trimmed part of the // slice length chunks[i] = chunks[i]!.subarray( 0, maxv.blockPosition === minv.blockPosition ? maxv.dataPosition - minv.dataPosition + 1 : maxv.dataPosition + 1, ) cpositions.push(cpos) dpositions.push(dpos) break } i++ } while (strm.avail_in) return { buffer: concatUint8Array(chunks), cpositions, dpositions, } } catch (e) { // return a slightly more informative error message if (/incorrect header check/.exec(`${e}`)) { throw new Error( 'problem decompressing block: incorrect gzip header check', ) } throw e } }