deflate logic

[lempelziv77, packagemergecodelengths, canonicalcodes]を経由してencoderを使用可能。

encoder

declare

1. declare getWriter
const getWriter=(buffer,state,c)=>{
    let b=0,controller
    const blob=new Response(new ReadableStream({
        start:v=>{controller=v}
    })).blob()
    return{
        write:async v=>{
            let offset=0
            while(offset<v.length){
                const l=(buffer.length-state.position).limit(true,v.length-offset)
                buffer.set(v.subarray(offset,offset+=l),state.position)
                if(buffer.length===(state.position+=l)){
                    c(b,controller)
                    b|=0b01
                }
            }
        }
        ,close:async()=>{
            c(b|0b10,controller)
            return blob
        }
    }
}

2. declare getLengthCode
,getLengthCode=(vs=>v=>{
    if(258===v)return 285<<16|0
    const i=v.toBinarySearchIndex(vs.length,i=>vs[i][0])
    return 257+i<<16|v-vs[i][0]<<8|vs[i][1]
})((vs=>{
    for(let i=0,v=3;i<28;v+=1<<vs[i][1],i++)vs[i]=[v,i<8?0:(i>>>2)-1]
    return vs
})(Array(28)))

3. declare getDistCode
,getDistCode=(vs=>v=>{
    const i=v.toBinarySearchIndex(vs.length,i=>vs[i][0])
    return i<<24|v-vs[i][0]<<8|vs[i][1]
})((vs=>{
    for(let i=0,v=1;i<30;v+=1<<vs[i][1],i++)vs[i]=[v,i<4?0:(i>>>1)-1]
    return vs
})(Array(30)))

getType-1Writer

1. declare this.getType-1Writer
this[`getType-1Writer`]=()=>{
    const compressionStream=new CompressionStream(`deflate-raw`),writer=compressionStream.writable.getWriter()
    ,blob=new Response(compressionStream.readable).blob()
    return{
        write:v=>writer.write(v)
        ,close:async()=>{
            await writer.close()
            return blob
        }
    }
}

getType0Writer

2. declare this.getType0Writer
this[`getType0Writer`]=()=>{
    const buffer=new Uint8Array(65535)
    ,state={
         position:0
    }
    return getWriter(buffer,state,(b,controller)=>{
        if(!state.position&&!(b>>>1))return
        const vs=new Uint8Array(5+state.position)
        vs[0]=b>>>1
        vs.set(state.position.to2Array(),1)
        vs.set((~state.position).to2Array(),3)
        vs.set(buffer.subarray(0,state.position),5)
        controller.enqueue(vs)
        state.position=0
        if(b>>>1)controller.close()
    })
}

getType1Writer

4. declare this.getType1Writer
this[`getType1Writer`]=(blockSize,dictionarySize,searchSize)=>{
    if(65536>blockSize||32768<dictionarySize||258<searchSize)throw Error()
    const buffer=new Uint8Array(blockSize)
    ,state={
         position:0
        ,buffer:0
        ,count:0
    }
    ,getFixedHuffman=(vs=>i=>vs[i])((vs=>{
        vs.fill(8)
        vs.fill(9,144,256)
        vs.fill(7,256,280)
        return vs.toCanonicalCodes(bit.reverse)
    })(new Uint8Array(288)))
    return getWriter(buffer,state,(b,controller)=>{
        if(!state.position&&!(b>>>1))return
        const vs=[(2|b>>>1)<<8|3]
        buffer.subarray(0,state.position).seal(0b01&b,dictionarySize,searchSize,258,v=>{
            if(255<v){
                const lengthCode=getLengthCode(v>>>16),distCode=getDistCode(0xffff&v)
                vs.push(getFixedHuffman(lengthCode>>>16))
                if(0xff&lengthCode)vs.push(0xffff&lengthCode)
                vs.push(bit.reverse(distCode>>>24,5)<<8|5)
                if(0xff&distCode)vs.push(0xffffff&distCode)
            }else vs.push(getFixedHuffman(v))
        })
        vs.push(getFixedHuffman(256))
        controller.enqueue(vs.packUint8Array(state))
        if(b>>>1){
            if(state.count){
                controller.enqueue(new Uint8Array([0xff&state.buffer]))
                state.buffer=state.count=0
            }
            controller.close()
        }else{
            buffer.copyWithin(0,state.position-32768,state.position)
            state.position=32768
        }
    })
}

getType2Writer

4. declare this.getType2Writer
this[`getType2Writer`]=(blockSize,dictionarySize,searchSize)=>{
    if(65536>blockSize||32768<dictionarySize||258<searchSize)throw Error()
    const buffer=new Uint8Array(blockSize)
    ,state={
         position:0
        ,buffer:0
        ,count:0
    }
    ,lengthOffset=257,distOffset=1,codeLengthOffset=4
    ,codeLengthOrder=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]
    ,lengthCounts=new Uint32Array(286),distCounts=new Uint32Array(30),codeLengthCounts=new Uint32Array(19)
    ,encode=(l,getCodeLength)=>{
        const vs=[]
        return reduce(false,l,(n=>(c=>(a,_,i)=>{
            const v=1+i<l?getCodeLength(1+i):-1
            n++
            if(a!==v){
                if(a)vs.push(c(a))
                if(a)while(0<n)vs.push(2<n?c(16,n.limit(true,6),-3,2):c(a))
                else while(0<n)vs.push(10<n?c(18,n.limit(true,138),-11,7):2<n?c(17,n.limit(true,10),-3,3):c(0))
            }
            return -1<v?v:vs
        })((v0,v1,v2,v3)=>{
            n-=v1?v1:1
            return ++codeLengthCounts[v0]&&v0<<16|v1+v2<<8|v3
        }))(0),getCodeLength(0))
    }
    return getWriter(buffer,state,(b,controller)=>{
        if(!state.position&&!(b>>>1))return
        lengthCounts[256]++
        const vs=[(4|b>>>1)<<8|3],sealed=[]
        buffer.subarray(0,state.position).seal(0b01&b,dictionarySize,searchSize,258,v=>{
            sealed.push(v)
            if(255<v){
                lengthCounts[getLengthCode(v>>>16)>>>16]++
                distCounts[getDistCode(0xffff&v)>>>24]++
            }else lengthCounts[v]++
        })
        const lengthLengths=(15).toPMCodeLengths(lengthCounts),distLengths=(15).toPMCodeLengths(distCounts)
        let length=lengthLengths.length,dist=distLengths.length
        while(lengthOffset<length&&!lengthLengths[length-1])length--
        while(distOffset<dist&&!distLengths[dist-1])dist--
        const encoded=encode(length+dist,i=>i<length?lengthLengths[i]:distLengths[i-length])
        ,codeLengthLengths=(7).toPMCodeLengths(codeLengthCounts)
        let codeLength=19
        while(codeLengthOffset<codeLength&&!codeLengthLengths[codeLengthOrder[codeLength-1]])codeLength--
        vs.push(length-lengthOffset<<8|5,dist-distOffset<<8|5,codeLength-codeLengthOffset<<8|4)
        forEach(false,codeLength,(_,i)=>vs.push(codeLengthLengths[codeLengthOrder[i]]<<8|3))
        {
            const codeLengthCodes=codeLengthLengths.toCanonicalCodes(bit.reverse)
            forEach(false,encoded,v=>{
                vs.push(codeLengthCodes[v>>>16])
                if(0xff&v)vs.push(0xffff&v)
            })
        }
        {
            const lengthCodes=lengthLengths.toCanonicalCodes(bit.reverse),distCodes=distLengths.toCanonicalCodes(bit.reverse)
            forEach(false,sealed,v=>{
                if(255<v){
                    const lengthCode=getLengthCode(v>>>16),distCode=getDistCode(0xffff&v)
                    vs.push(lengthCodes[lengthCode>>>16])
                    if(0xff&lengthCode)vs.push(0xffff&lengthCode)
                    vs.push(distCodes[distCode>>>24])
                    if(0xff&distCode)vs.push(0xffffff&distCode)
                }else vs.push(lengthCodes[v])
            })
            vs.push(lengthCodes[256])
        }
        controller.enqueue(vs.packUint8Array(state))
        lengthCounts.fill(0)
        distCounts.fill(0)
        codeLengthCounts.fill(0)
        if(b>>>1){
            if(state.count){
                controller.enqueue(new Uint8Array([0xff&state.buffer]))
                state.buffer=state.count=0
            }
            controller.close()
        }else{
            buffer.copyWithin(0,state.position-32768,state.position)
            state.position=32768
        }
    })
}

getWriter

5. declare this.getWriter
this.getWriter=(type,blockSize,dictionarySize,searchSize)=>{
    if(~3&1+type)throw Error()
    return this[`getType${type}Writer`](blockSize,dictionarySize,searchSize)
}