UNPKG

@brianpugh/tamp

Version:

Tamp compression library for JavaScript/TypeScript using WebAssembly

2 lines (1 loc) 12.2 kB
import st from"./tamp-module.mjs";var j=0,it=-1,ot=-2,lt=-3,at=-4,U=class extends Error{constructor(t,e,r={}){super(e),this.name="TampError",this.code=t,this.details=r}},M=class extends U{constructor(t="Provided data has more bits than expected literal bits",e={}){super(-2,t,e),this.name="ExcessBitsError"}},R=class extends U{constructor(t="Compression operation failed",e={}){super(-1,t,e),this.name="CompressionError"}},$=class extends U{constructor(t="Decompression operation failed",e={}){super(-1,t,e),this.name="DecompressionError"}},L=class{#t=null;#e=null;#r=null;async initialize(){return this.#e||(this.#e=this.#n()),await this.#e}async#n(){return this.#t=await st(),this.#r={compressor:this.#t.ccall("tamp_compressor_sizeof","number",[],[]),decompressor:this.#t.ccall("tamp_decompressor_sizeof","number",[],[]),conf:this.#t.ccall("tamp_conf_sizeof","number",[],[])},this.#t}get module(){return this.#t}get structSizes(){return this.#r}get isInitialized(){return this.#t!==null}},O=new L;async function G(){return await O.initialize()}function E(o,t){if(o<0){let e={operation:t,code:o};switch(o){case ot:throw new M(`${t}: Symbol has more bits than configured literal size`,e);case lt:throw new RangeError(`${t}: Invalid configuration parameters`);case at:throw new RangeError(`${t}: Out-of-bounds access detected in compressed data`);case it:default:throw t.toLowerCase().includes("compress")?new R(`${t}: Operation failed`,e):t.toLowerCase().includes("decompress")?new $(`${t}: Operation failed`,e):new U(o,`${t}: Unknown error`,e)}}return o}var D=class{constructor(t={}){if(this.options={window:10,literal:8,dictionary:null,extended:!0,lazy_matching:!1,dictionary_reset:!1,append:!1,...t},this.options.window<8||this.options.window>15)throw new RangeError(`window must be between 8 and 15, got ${this.options.window}`);if(this.options.literal<5||this.options.literal>8)throw new RangeError(`literal must be between 5 and 8, got ${this.options.literal}`);this.compressorPtr=null,this.windowPtr=null,this.module=null,this._initPromise=null}async initialize(){return this._initPromise||(this._initPromise=this._doInitialize()),this._initPromise}async _doInitialize(){this.module=await G();let t=1<<this.options.window,e=this.options.dictionary?new Uint8Array(this.options.dictionary):null;if(e&&e.length!==t)throw new RangeError(`Dictionary size (${e.length}) must match window size (${t})`);let{compressor:r,conf:n}=O.structSizes,s=r+n+t;if(this.compressorPtr=this.module._malloc(s),this.compressorPtr===0)throw new RangeError(`Failed to allocate memory for compressor (${s} bytes)`);let i=this.compressorPtr+r;this.windowPtr=i+n,e?this.module.HEAPU8.set(e,this.windowPtr):this.module.ccall("tamp_initialize_dictionary",null,["number","number","number"],[this.windowPtr,t,this.options.extended?this.options.literal:8]);try{let l=this.options.window&15|(this.options.literal&15)<<4|(this.options.dictionary?1:0)<<8|(this.options.extended?1:0)<<9|(this.options.dictionary_reset?1:0)<<10|(this.options.append?1:0)<<11|(this.options.lazy_matching?1:0)<<12;this.module.setValue(i,l,"i32");let c=this.module.ccall("tamp_compressor_init","number",["number","number","number"],[this.compressorPtr,i,this.windowPtr]);E(c,"Compressor initialization")}catch(l){throw this.module._free(this.compressorPtr),this.compressorPtr=null,this.windowPtr=null,l}}async compress(t,e={}){let r=e.onPoll;if(await this.initialize(),!this.compressorPtr)throw new Error("Compressor has been destroyed");let n=1<<20,s=[],{signal:i,pollIntervalMs:l=100,pollIntervalBytes:c=65536}=e,d=performance.now(),m=d,u=0,p=0,h=()=>{if(i!=null&&i.aborted)throw new R("Compression was aborted",{aborted:!0,reason:i.reason})},a=n+n+16,C=this.module._malloc(a);if(C===0)throw new RangeError(`Failed to allocate memory (${a} bytes)`);let f=C,w=f+n,P=w+n,g=P+4,y=g+4,b=y+4;try{h();let z=t.length,k=0;for(;z>0;){h();let S=Math.min(z,n);this.module.HEAPU8.set(t.subarray(k,k+S),f);let T=f,B=S,I=0,_=0;for(;B>0&&n-_>0;){this.module.ccall("tamp_compressor_sink",null,["number","number","number","number"],[this.compressorPtr,T,B,y]);let x=this.module.getValue(y,"i32");if(T+=x,B-=x,I+=x,this.module.ccall("tamp_compressor_full","number",["number"],[this.compressorPtr])){let Z=this.module.ccall("tamp_compressor_poll","number",["number","number","number","number"],[this.compressorPtr,w+_,n-_,b]),X=this.module.getValue(b,"i32");if(_+=X,Z!==j){E(Z,"Compression poll");break}if(r&&typeof r=="function"){let H=performance.now(),A=k+I,Q=H-m,Y=A-u;if(Q>=l||Y>=c||A===t.length){h();let v=(H-d)/1e3,F=v>0?A/v:0,tt=t.length>0?A/t.length*100:0,et=t.length-A,rt=F>0?et/F:0,nt={bytesProcessed:A,totalBytes:t.length,percent:tt,bytesPerSecond:Math.round(F),estimatedTimeRemaining:rt,chunksProcessed:p,elapsedTime:v,chunkSize:X,outputSize:_};await Promise.resolve(r(nt)),m=H,u=A}}h(),p++}}if(this.module.setValue(P,_,"i32"),this.module.setValue(g,I,"i32"),_>0){let x=new Uint8Array(this.module.HEAPU8.buffer,w,_).slice();s.push(x)}if(k+=I,z-=I,I===0&&z>0)throw new R("No input consumed in compression loop",{inputRemaining:z,inputConsumed:I})}let J=s.reduce((S,T)=>S+T.length,0),K=new Uint8Array(J),W=0;for(let S of s)K.set(S,W),W+=S.length;return K}finally{this.module._free(C)}}async flush(t=!0){if(await this.initialize(),!this.compressorPtr)throw new Error("Compressor has been destroyed");let e=36,r=this.module._malloc(e);if(r===0)throw new RangeError(`Failed to allocate flush memory (${e} bytes)`);let n=r,s=r+32;try{let i=this.module.ccall("tamp_compressor_flush","number",["number","number","number","number","number"],[this.compressorPtr,n,32,s,t?1:0]);E(i,"Flush");let l=this.module.getValue(s,"i32");return new Uint8Array(this.module.HEAPU8.buffer,n,l).slice()}finally{this.module._free(r)}}async resetDictionary(){if(await this.initialize(),!this.compressorPtr)throw new Error("Compressor has been destroyed");let t=32,e=t+4,r=this.module._malloc(e);if(r===0)throw new RangeError(`Failed to allocate reset memory (${e} bytes)`);let n=r,s=r+t;try{let i=this.module.ccall("tamp_compressor_reset_dictionary","number",["number","number","number","number"],[this.compressorPtr,n,t,s]);E(i,"Reset dictionary");let l=this.module.getValue(s,"i32");return new Uint8Array(this.module.HEAPU8.buffer,n,l).slice()}finally{this.module._free(r)}}destroy(){this.compressorPtr&&(this.module._free(this.compressorPtr),this.compressorPtr=null,this.windowPtr=null)}},V=class{constructor(t={}){this.options={dictionary:null,...t},this.decompressorPtr=null,this.windowPtr=null,this.module=null,this._initPromise=null,this.initialPtr=null,this.confPtr=null,this.inputConsumedPtr=null,this.headerInputPtr=null,this.headerRead=!1,this.decompressorInitialized=!1,this.pendingInput=new Uint8Array(0)}async initialize(){return this._initPromise||(this._initPromise=this._doInitialize()),this._initPromise}async _doInitialize(){this.module=await G();let{conf:t}=O.structSizes,e=t+4+2;if(this.initialPtr=this.module._malloc(e),this.initialPtr===0)throw new RangeError(`Failed to allocate initial memory (${e} bytes)`);this.confPtr=this.initialPtr,this.inputConsumedPtr=this.initialPtr+t,this.headerInputPtr=this.inputConsumedPtr+4}async _readHeader(t){if(this.headerRead||t.length===0)return 0;let e=Math.min(t.length,2);for(let a=0;a<e;a++)this.module.setValue(this.headerInputPtr+a,t[a],"i8");let r=this.module.ccall("tamp_decompressor_read_header","number",["number","number","number","number"],[this.confPtr,this.headerInputPtr,e,this.inputConsumedPtr]);if(E(r,"Header reading"),r!==j)return this.pendingInput=t.slice(0,e),t.length;let n=this.module.getValue(this.inputConsumedPtr,"i32"),s=this.module.getValue(this.confPtr,"i32"),i=s&15,l=s>>4&15,c=s>>8&1,d=s>>9&1;if(c&&!this.options.dictionary)throw new TypeError("Compressed data requires custom dictionary but none provided");if(!c&&this.options.dictionary)throw new TypeError("Compressed data does not use custom dictionary but one was provided");let m=1<<i,u=c?new Uint8Array(this.options.dictionary):null;if(u&&u.length!==m)throw new RangeError(`Dictionary size (${u.length}) must match header window size (${m})`);let{decompressor:p}=O.structSizes,h=p+m;if(this.decompressorPtr=this.module._malloc(h),this.decompressorPtr===0)throw new RangeError(`Failed to allocate memory for decompressor (${h} bytes)`);this.windowPtr=this.decompressorPtr+p,u?this.module.HEAPU8.set(u,this.windowPtr):this.module.ccall("tamp_initialize_dictionary",null,["number","number","number"],[this.windowPtr,m,d?l:8]);try{let a=this.module.ccall("tamp_decompressor_init","number",["number","number","number","number"],[this.decompressorPtr,this.confPtr,this.windowPtr,i]);E(a,"Decompressor initialization"),this.decompressorInitialized=!0}catch(a){throw this.module._free(this.decompressorPtr),this.decompressorPtr=null,this.windowPtr=null,a}return this.headerRead=!0,n}async decompress(t,e={}){if(await this.initialize(),!this.initialPtr)throw new Error("Decompressor has been destroyed");let{signal:r}=e,n=()=>{if(r!=null&&r.aborted)throw new $("Decompression was aborted",{aborted:!0,reason:r.reason})};n();let s=new Uint8Array(this.pendingInput.length+t.length);s.set(this.pendingInput),s.set(t,this.pendingInput.length);let i=0;if(!this.headerRead){let f=await this._readHeader(s);if(i+=f,!this.headerRead||i>=s.length)return this.headerRead&&(this.pendingInput=new Uint8Array(0)),new Uint8Array(0)}let l=s.slice(i);if(l.length===0)return this.pendingInput=new Uint8Array(0),new Uint8Array(0);let c=1<<22,d=[],m=8+l.length+c,u=this.module._malloc(m);if(u===0)throw new RangeError(`Failed to allocate decompress memory (${m} bytes)`);let p=u,h=p+4,a=h+4,C=a+l.length;try{this.module.HEAPU8.set(l,a);let f=0,w=l.length;for(;w>0;){n();let P=this.module.ccall("tamp_decompressor_decompress_cb","number",["number","number","number","number","number","number","number","number","number"],[this.decompressorPtr,C,c,p,a+f,w,h,0,0]),g=this.module.getValue(p,"i32"),y=this.module.getValue(h,"i32");if(w-=y,f+=y,g>0){let b=new Uint8Array(this.module.HEAPU8.buffer,C,g).slice();d.push(b)}if(P===2){w>0?this.pendingInput=l.slice(f):this.pendingInput=new Uint8Array(0);break}else P<0&&E(P,"Decompression");if(w===0){this.pendingInput=new Uint8Array(0);break}}if(d.length===0)return new Uint8Array(0);if(d.length===1)return d[0];{let P=d.reduce((b,z)=>b+z.length,0),g=new Uint8Array(P),y=0;for(let b of d)g.set(b,y),y+=b.length;return g}}finally{this.module._free(u)}}destroy(){this.decompressorPtr&&(this.module._free(this.decompressorPtr),this.decompressorPtr=null,this.windowPtr=null),this.initialPtr&&(this.module._free(this.initialPtr),this.initialPtr=null,this.confPtr=null,this.inputConsumedPtr=null,this.headerInputPtr=null),this.headerRead=!1,this.decompressorInitialized=!1,this.pendingInput=new Uint8Array(0)}};var N=class extends TransformStream{constructor(t={}){let e=null;super({start(r){e=new D(t)},async transform(r,n){try{let s=r instanceof Uint8Array?r:new Uint8Array(r),i=await e.compress(s);i.length>0&&n.enqueue(i)}catch(s){n.error(s)}},async flush(r){try{let n=await e.flush(!1);n.length>0&&r.enqueue(n)}catch(n){r.error(n)}finally{e&&(e.destroy(),e=null)}},cancel(r){e&&(e.destroy(),e=null)}})}},q=class extends TransformStream{constructor(t={}){let e=null;super({start(r){e=new V(t)},async transform(r,n){try{let s=r instanceof Uint8Array?r:new Uint8Array(r),i=await e.decompress(s);i.length>0&&n.enqueue(i)}catch(s){n.error(s)}},async flush(r){try{let n=await e.decompress(new Uint8Array(0));n.length>0&&r.enqueue(n)}catch(n){r.error(n)}finally{e&&(e.destroy(),e=null)}},cancel(r){e&&(e.destroy(),e=null)}})}};function pt(o,t={}){return o.pipeThrough(new N(t))}function ft(o,t={}){return o.pipeThrough(new q(t))}function wt(o,t=8192){let e=0;return new ReadableStream({pull(r){if(e>=o.length){r.close();return}let n=o.slice(e,e+t);e+=t,r.enqueue(n)}})}async function yt(o){let t=[],e=o.getReader();try{for(;;){let{done:i,value:l}=await e.read();if(i)break;t.push(l)}}finally{e.releaseLock()}let r=t.reduce((i,l)=>i+l.length,0),n=new Uint8Array(r),s=0;for(let i of t)n.set(i,s),s+=i.length;return n}export{N as TampCompressionStream,q as TampDecompressionStream,yt as collectStream,pt as compressStream,wt as createReadableStream,ft as decompressStream};