deflate logic
[lempelziv77, packagemergecodelengths, canonicalcodes]を経由してencoderを使用可能。encoder
declare
1. declare getWriterconst 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-1Writerthis[`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.getType0Writerthis[`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.getType1Writerthis[`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.getType2Writerthis[`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.getWriterthis.getWriter=(type,blockSize,dictionarySize,searchSize)=>{
if(~3&1+type)throw Error()
return this[`getType${type}Writer`](blockSize,dictionarySize,searchSize)
}