-- This is released into the public domain. -- No warranty is provided, implied or otherwise. -- THIS NEXT LINE IS CLEARLY AWFUL $bdBDWindow = nil -- THE DEFLATE DECOMPRESSOR OF MADNESS -- -- Core I/O -- -- $dfAlignToByteRemaining is -- set to 8 in the outer engine ${ function $dfGetIntField($L|lLen, $L|lVal) $lVal = 0 for $L|lI = 0, $lLen - 1 do if coroutine.yield() then $lVal = $lVal + 2^$lI end end return $lVal end $} -- Huffman Core -- -- The approach here is based around 1-prefixed integers. -- These are stored in a flat table. -- So 0b1000 is the pattern 000. ${ function $dfReadHuffmanSymbol($L|lTree, $L|lVal) $lVal = 1 while not $lTree[$lVal] do $lVal = ($lVal * 2) + $dfGetIntField(1) end return $lTree[$lVal] end $} ${ function $dfGenHuffmanTree($L|lCodeLens) -- $L|lI (used everywhere; defining inside creates a bug because it gets globalized) $L|lNextCode = {} ${ -- To explain: -- lNextCode is needed all the way to the end. -- But lBLCount isn't needed after it's used to -- generate lNextCode. -- And lCode is very, very temporary. -- Hence this massive block. $L|lBLCount = {} -- Note the 0 for $lI = 0, 15 do $lBLCount[$lI] = 0 end for $lI = 0, #$lCodeLens do ${ $L|lCodeLen = $lCodeLens[$lI] if $lCodeLen ~= 0 then $lBLCount[$lCodeLen] = $lBLCount[$lCodeLen] + 1 end $} end $L|lCode = 0 for $lI = 1, 15 do $lCode = ($lCode + $lBLCount[$lI - 1]) * 2 $lNextCode[$lI] = $lCode end $} $L|lTree = {} for $lI = 0, #$lCodeLens do ${ $L|lCodeLen = $lCodeLens[$lI] if $lCodeLen ~= 0 then $L|lPow = math.floor(2 ^ $lCodeLen) assert($lNextCode[$lCodeLen] < $lPow, "Tl" .. $lCodeLen) $L|lK = $lNextCode[$lCodeLen] + $lPow assert(not $lTree[$lK], "conflict @ " .. $lK) $lTree[$lK] = $lI $lNextCode[$lCodeLen] = $lNextCode[$lCodeLen] + 1 end $} end return $lTree end $} -- DEFLATE fixed trees -- ${ $L|dfFixedTL = {} for $L|lI = 0, 143 do $dfFixedTL[$lI] = 8 end for $lI = 144, 255 do $dfFixedTL[$lI] = 9 end for $lI = 256, 279 do $dfFixedTL[$lI] = 7 end for $lI = 280, 287 do $dfFixedTL[$lI] = 8 end $dfFixedLit = $dfGenHuffmanTree($dfFixedTL) -- last line possibly destroyed dfFixedTL, but that's alright $dfFixedTL = {} for $lI = 0, 31 do $dfFixedTL[$lI] = 5 end $dfFixedDst = $dfGenHuffmanTree($dfFixedTL) $} -- DEFLATE LZ Core -- $dfWindow = ("\x00"):rep(2^15) $dfPushBuf = "" ${ function $dfOutput($L|lData) $dfWindow = ($dfWindow .. $lData):sub(-2^15) $dfPushBuf = $dfPushBuf .. $lData end $} $dfBittblLength = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 } $dfBasetblLength = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 } $dfBittblDist = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 } $dfBasetblDist = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 } ${ function $dfReadBlockBody($L|lLit, $L|lDst, $L|lLitSym, $L|lLen, $L|lDCode, $L|lPtr) while true do $lLitSym = $dfReadHuffmanSymbol($lLit) if $lLitSym <= 255 then $dfOutput(string.char($lLitSym)) elseif $lLitSym == 256 then return elseif $lLitSym <= 285 then $lLen = $dfBasetblLength[$lLitSym - 256] + $dfGetIntField($dfBittblLength[$lLitSym - 256]) $lDCode = $dfReadHuffmanSymbol($lDst) $lPtr = 32769 - ($dfBasetblDist[$lDCode + 1] + $dfGetIntField($dfBittblDist[$lDCode + 1])) for $L|lI = 1, $lLen do $dfOutput($dfWindow:sub($lPtr, $lPtr)) end else error("nt" .. v) end end end $} -- Huffman Dynamics -- ${ function $dfReadHuffmanDynamicSubcodes($L|lDistLens, $L|lCount, $L|lMetatree, $L|lCode) -- used a lot: $L|lI $lCode = 0 $lDistLens[-1] = 0 while $lCode < $lCount do -- use a tmpglb since it's just for the chain $L|lInstr = $dfReadHuffmanSymbol($lMetatree) if $lInstr < 16 then $lDistLens[$lCode] = $lInstr $lCode = $lCode + 1 elseif $lInstr == 16 then for $lI = 1, 3 + $dfGetIntField(2) do $lDistLens[$lCode] = $lDistLens[$lCode - 1] $lCode = $lCode + 1 assert($lCode <= $lCount, "sc.over") end elseif $lInstr == 17 then for $lI = 1, 3 + $dfGetIntField(3) do $lDistLens[$lCode] = 0 $lCode = $lCode + 1 assert($lCode <= $lCount, "sc.over") end elseif $lInstr == 18 then for $lI = 1, 11 + $dfGetIntField(7) do $lDistLens[$lCode] = 0 $lCode = $lCode + 1 assert($lCode <= $lCount, "sc.over") end else -- is this even possible? error("sc.unki") end end $lDistLens[-1] = nil return $lDistLens end $} $dfDynamicMetalensScramble = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15} ${ function $dfReadHuffmanDynamic($L|lState, $L|lLiteralSize, $L|lDistanceSize, $L|lDistanceLens) -- $L|lI loop variable, used all over -- to save on locals, this is reused -- as metalens $lState = {} for $lI = 0, 18 do $lState[$lI] = 0 end $lLiteralSize = $dfGetIntField(5) + 257 $lDistanceSize = $dfGetIntField(5) + 1 for $lI = 1, $dfGetIntField(4) + 4 do $lState[$dfDynamicMetalensScramble[$lI]] = $dfGetIntField(3) end -- as metatree $lState = $dfGenHuffmanTree($lState) -- as concatenated subcodes $lState = $dfReadHuffmanDynamicSubcodes({}, $lLiteralSize + $lDistanceSize, $lState) -- The distance lengths are removed from lState, -- while being added to lDistanceLens -- The result is completion $lDistanceLens = {} for $lI = 0, $lDistanceSize - 1 do $lDistanceLens[$lI] = $lState[$lLiteralSize + $lI] $lState[$lLiteralSize + $lI] = nil end return $dfGenHuffmanTree($lState), $dfGenHuffmanTree($lDistanceLens) end $} -- Main Thread -- ${ $dfThread = coroutine.create(function ($L|lFinal, $L|lLitLen) while true do $lFinal = coroutine.yield() $L|lBlockType = $dfGetIntField(2) if $lBlockType == 0 then -- literal $dfGetIntField($dfAlignToByteRemaining) $lLitLen = $dfGetIntField(16) -- this is weird, ignore it $dfGetIntField(16) for $L|lI = 1, $lLitLen do $dfOutput(string.char($dfGetIntField(8))) end elseif $lBlockType == 1 then -- fixed Huffman $dfReadBlockBody($dfFixedLit, $dfFixedDst) elseif $lBlockType == 2 then -- dynamic Huffman $dfReadBlockBody($dfReadHuffmanDynamic()) else error("b3") end while $lFinal do coroutine.yield() end end end) $} -- The Outer Engine -- coroutine.resume($dfThread) ${ function $engineInput($L|lData, $L|lByte) for $L|lI = 1, #$lData do $lByte = $lData:byte($lI) $dfAlignToByteRemaining = 8 while $dfAlignToByteRemaining > 0 do -- If we're providing the first bit (v = 8), then there are 7 bits remaining. -- So this hits 0 when the *next* 8 yields will provide an as-is byte. $dfAlignToByteRemaining = $dfAlignToByteRemaining - 1 assert(coroutine.resume($dfThread, $lByte % 2 == 1)) $lByte = math.floor($lByte / 2) end end -- flush prepared buffer $engineOutput($dfPushBuf) $dfPushBuf = "" end $}