@brianpugh/tamp
Version:
Tamp compression library for JavaScript/TypeScript using WebAssembly
2 lines (1 loc) • 13 kB
JavaScript
var lt=Object.create;var $=Object.defineProperty;var at=Object.getOwnPropertyDescriptor;var ct=Object.getOwnPropertyNames;var ut=Object.getPrototypeOf,dt=Object.prototype.hasOwnProperty;var ht=(s,t)=>{for(var e in t)$(s,e,{get:t[e],enumerable:!0})},G=(s,t,e,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of ct(t))!dt.call(s,n)&&n!==e&&$(s,n,{get:()=>t[n],enumerable:!(r=at(t,n))||r.enumerable});return s};var mt=(s,t,e)=>(e=s!=null?lt(ut(s)):{},G(t||!s||!s.__esModule?$(e,"default",{value:s,enumerable:!0}):e,s)),pt=s=>G($({},"__esModule",{value:!0}),s);var St={};ht(St,{TampCompressionStream:()=>H,TampDecompressionStream:()=>v,collectStream:()=>zt,compressStream:()=>Pt,createReadableStream:()=>_t,decompressStream:()=>gt});module.exports=pt(St);var J=mt(require("./tamp-module.js"),1),Q=0,ft=-1,wt=-2,yt=-3,bt=-4,U=class extends Error{constructor(t,e,r={}){super(e),this.name="TampError",this.code=t,this.details=r}},q=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"}},O=class extends U{constructor(t="Decompression operation failed",e={}){super(-1,t,e),this.name="DecompressionError"}},K=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(0,J.default)(),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}},D=new K;async function Y(){return await D.initialize()}function E(s,t){if(s<0){let e={operation:t,code:s};switch(s){case wt:throw new q(`${t}: Symbol has more bits than configured literal size`,e);case yt:throw new RangeError(`${t}: Invalid configuration parameters`);case bt:throw new RangeError(`${t}: Out-of-bounds access detected in compressed data`);case ft:default:throw t.toLowerCase().includes("compress")?new R(`${t}: Operation failed`,e):t.toLowerCase().includes("decompress")?new O(`${t}: Operation failed`,e):new U(s,`${t}: Unknown error`,e)}}return s}var V=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 Y();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}=D.structSizes,i=r+n+t;if(this.compressorPtr=this.module._malloc(i),this.compressorPtr===0)throw new RangeError(`Failed to allocate memory for compressor (${i} bytes)`);let o=this.compressorPtr+r;this.windowPtr=o+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(o,l,"i32");let c=this.module.ccall("tamp_compressor_init","number",["number","number","number"],[this.compressorPtr,o,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,i=[],{signal:o,pollIntervalMs:l=100,pollIntervalBytes:c=65536}=e,d=performance.now(),m=d,u=0,p=0,h=()=>{if(o!=null&&o.aborted)throw new R("Compression was aborted",{aborted:!0,reason:o.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,F=S,I=0,_=0;for(;F>0&&n-_>0;){this.module.ccall("tamp_compressor_sink",null,["number","number","number","number"],[this.compressorPtr,T,F,y]);let x=this.module.getValue(y,"i32");if(T+=x,F-=x,I+=x,this.module.ccall("tamp_compressor_full","number",["number"],[this.compressorPtr])){let X=this.module.ccall("tamp_compressor_poll","number",["number","number","number","number"],[this.compressorPtr,w+_,n-_,b]),j=this.module.getValue(b,"i32");if(_+=j,X!==Q){E(X,"Compression poll");break}if(r&&typeof r=="function"){let M=performance.now(),A=k+I,et=M-m,rt=A-u;if(et>=l||rt>=c||A===t.length){h();let L=(M-d)/1e3,N=L>0?A/L:0,nt=t.length>0?A/t.length*100:0,st=t.length-A,it=N>0?st/N:0,ot={bytesProcessed:A,totalBytes:t.length,percent:nt,bytesPerSecond:Math.round(N),estimatedTimeRemaining:it,chunksProcessed:p,elapsedTime:L,chunkSize:j,outputSize:_};await Promise.resolve(r(ot)),m=M,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();i.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 tt=i.reduce((S,T)=>S+T.length,0),W=new Uint8Array(tt),Z=0;for(let S of i)W.set(S,Z),Z+=S.length;return W}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,i=r+32;try{let o=this.module.ccall("tamp_compressor_flush","number",["number","number","number","number","number"],[this.compressorPtr,n,32,i,t?1:0]);E(o,"Flush");let l=this.module.getValue(i,"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,i=r+t;try{let o=this.module.ccall("tamp_compressor_reset_dictionary","number",["number","number","number","number"],[this.compressorPtr,n,t,i]);E(o,"Reset dictionary");let l=this.module.getValue(i,"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)}},B=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 Y();let{conf:t}=D.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!==Q)return this.pendingInput=t.slice(0,e),t.length;let n=this.module.getValue(this.inputConsumedPtr,"i32"),i=this.module.getValue(this.confPtr,"i32"),o=i&15,l=i>>4&15,c=i>>8&1,d=i>>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<<o,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}=D.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,o]);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 O("Decompression was aborted",{aborted:!0,reason:r.reason})};n();let i=new Uint8Array(this.pendingInput.length+t.length);i.set(this.pendingInput),i.set(t,this.pendingInput.length);let o=0;if(!this.headerRead){let f=await this._readHeader(i);if(o+=f,!this.headerRead||o>=i.length)return this.headerRead&&(this.pendingInput=new Uint8Array(0)),new Uint8Array(0)}let l=i.slice(o);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 H=class extends TransformStream{constructor(t={}){let e=null;super({start(r){e=new V(t)},async transform(r,n){try{let i=r instanceof Uint8Array?r:new Uint8Array(r),o=await e.compress(i);o.length>0&&n.enqueue(o)}catch(i){n.error(i)}},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)}})}},v=class extends TransformStream{constructor(t={}){let e=null;super({start(r){e=new B(t)},async transform(r,n){try{let i=r instanceof Uint8Array?r:new Uint8Array(r),o=await e.decompress(i);o.length>0&&n.enqueue(o)}catch(i){n.error(i)}},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(s,t={}){return s.pipeThrough(new H(t))}function gt(s,t={}){return s.pipeThrough(new v(t))}function _t(s,t=8192){let e=0;return new ReadableStream({pull(r){if(e>=s.length){r.close();return}let n=s.slice(e,e+t);e+=t,r.enqueue(n)}})}async function zt(s){let t=[],e=s.getReader();try{for(;;){let{done:o,value:l}=await e.read();if(o)break;t.push(l)}}finally{e.releaseLock()}let r=t.reduce((o,l)=>o+l.length,0),n=new Uint8Array(r),i=0;for(let o of t)n.set(o,i),i+=o.length;return n}0&&(module.exports={TampCompressionStream,TampDecompressionStream,collectStream,compressStream,createReadableStream,decompressStream});