diff --git a/inst/deflate/compress.lua b/inst/deflate/compress.lua new file mode 100644 index 0000000..9ddbca0 --- /dev/null +++ b/inst/deflate/compress.lua @@ -0,0 +1,14 @@ +-- This is released into the public domain. +-- No warranty is provided, implied or otherwise. + +-- This is a wrapper around (i.e. does not contain) Zopfli. +local frw = require("libs.frw") + +return function (data, lexCrunch) + frw.write("tempData.bin", data) + os.execute("zopfli --i1 --deflate -c tempData.bin > tempZopf.bin") + local res = frw.read("tempZopf.bin") + os.execute("rm tempData.bin tempZopf.bin") + return lexCrunch(frw.read("deflate/instdeco.lua"), {}), res +end + diff --git a/inst/deflate/instdeco.lua b/inst/deflate/instdeco.lua new file mode 100644 index 0000000..89d628c --- /dev/null +++ b/inst/deflate/instdeco.lua @@ -0,0 +1,263 @@ +-- This is released into the public domain. +-- No warranty is provided, implied or otherwise. + +-- THE DEFLATE DECOMPRESSOR OF MADNESS -- + +-- Core I/O -- + +-- $dfAlignToByteRemaining is +-- set to 8 in the outer engine + +function $dfGetIntField($a0, $a1) + $NTdfForLoopVar + $a1 = 0 + for $dfForLoopVar = 1, $a0 do + if coroutine.yield() then + $a1 = $a1 + 2^($dfForLoopVar - 1) + end + end + $DTdfForLoopVar + return $a1 +end + +function $dfAlignToByte() + while $dfAlignToByteRemaining do + coroutine.yield() + end +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($a0, $a1) + $a1 = 1 + while not $a0[$a1] do + $a1 = ($a1 * 2) + $dfGetIntField(1) + end + return $a0[$a1] +end + +function $dfGenHuffmanTree($a0) + local blCount = {} + -- Note the 0 + for loopVar = 0, 15 do + blCount[loopVar] = 0 + end + for loopVar = 0, #$a0 do + local cl = $a0[loopVar] + if cl ~= 0 then + blCount[cl] = blCount[cl] + 1 + end + end + + local code = 0 + local nextCode = {} + for loopVar = 1, 15 do + code = (code + blCount[loopVar - 1]) * 2 + nextCode[loopVar] = code + end + + local tbl = {} + for loopVar = 0, #$a0 do + local cl = $a0[loopVar] + if cl ~= 0 then + local pow = math.floor(2 ^ cl) + if nextCode[cl] >= pow then error("To L1 not valid for " .. n .. "," .. cl .. ".") end + local k = nextCode[cl] + pow + assert(not tbl[k], "conflict @ " .. k) + tbl[k] = loopVar + nextCode[cl] = nextCode[cl] + 1 + end + end + return tbl +end + +-- DEFLATE fixed trees -- +$NTdfFixedTL +$NTdfFlv +$dfFixedTL = {} +for $dfFlv = 0, 143 do $dfFixedTL[$dfFlv] = 8 end +for $dfFlv = 144, 255 do $dfFixedTL[$dfFlv] = 9 end +for $dfFlv = 256, 279 do $dfFixedTL[$dfFlv] = 7 end +for $dfFlv = 280, 287 do $dfFixedTL[$dfFlv] = 8 end +$dfFixedLit = $dfGenHuffmanTree($dfFixedTL) +$dfFixedTL = {} +for $dfFlv = 0, 31 do + $dfFixedTL[$dfFlv] = 5 +end +$dfFixedDst = $dfGenHuffmanTree($dfFixedTL) +$DTdfFlv +$DTdfFixedTL + +-- DEFLATE LZ Core -- + +$dfWindow = ("\x00"):rep(2^16) +$dfPushBuf = "" +function $dfOutput($a0) + $dfWindow = ($dfWindow .. $a0):sub(-2^16) + $dfPushBuf = $dfPushBuf .. $a0 +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(lit, dst) + while true do + local litSym = $dfReadHuffmanSymbol(lit) + if litSym <= 255 then + $dfOutput(string.char(litSym)) + elseif litSym == 256 then + return + elseif litSym <= 285 then + local len = $dfBasetblLength[litSym - 256] + $dfGetIntField($dfBittblLength[litSym - 256]) + local dCode = $dfReadHuffmanSymbol(dst) + local dst = $dfBasetblDist[dCode + 1] + $dfGetIntField($dfBittblDist[dCode + 1]) + local ptr = 65537 - dst + for loopVar = 1, len do + $dfOutput($dfWindow:sub(ptr, ptr)) + end + else + error("nt" .. v) + end + end +end + +-- Huffman Dynamics -- + +function $dfReadHuffmanDynamicSubcodes(distlens, dst, metatree) + local loopVar = 0 + distlens[-1] = 0 + while loopVar < dst do + local instr = $dfReadHuffmanSymbol(metatree) + if instr < 16 then + distlens[loopVar] = instr + loopVar = loopVar + 1 + elseif instr == 16 then + for loopVar2 = 1, 3 + $dfGetIntField(2) do + distlens[loopVar] = distlens[loopVar - 1] + loopVar = loopVar + 1 + if loopVar > dst then error("Overflow") end + end + elseif instr == 17 then + for loopVar2 = 1, 3 + $dfGetIntField(3) do + distlens[loopVar] = 0 + loopVar = loopVar + 1 + if loopVar > dst then error("Overflow") end + end + elseif instr == 18 then + for loopVar2 = 1, 11 + $dfGetIntField(7) do + distlens[loopVar] = 0 + loopVar = loopVar + 1 + if loopVar > dst then error("Overflow") end + end + else + error("unable to handle cl instruction " .. instr) + end + end + distlens[-1] = nil +end + +function $dfReadHuffmanDynamic() + local metalensi = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15} + local metalens = {} + for loopVar = 0, 18 do metalens[loopVar] = 0 end + local ltl = $dfGetIntField(5) + 257 + local dst = $dfGetIntField(5) + 1 + local cln = $dfGetIntField(4) + 4 + for loopVar = 1, cln do + metalens[metalensi[loopVar]] = $dfGetIntField(3) + end + local metatree = $dfGenHuffmanTree(metalens) + local alllens = {} + $dfReadHuffmanDynamicSubcodes(alllens, ltl + dst, metatree) + local litlens = {} + local distlens = {} + for loopVar = 0, ltl - 1 do + litlens[loopVar] = alllens[loopVar] + end + for loopVar = 0, dst - 1 do + distlens[loopVar] = alllens[ltl + loopVar] + end + return $dfGenHuffmanTree(litlens), $dfGenHuffmanTree(distlens) +end + +-- Main Thread -- + +$dfThread = coroutine.create(function ($a0, $a1) + while true do + $a0 = coroutine.yield() + $NTdfBlockType + $dfBlockType = $dfGetIntField(2) + if $dfBlockType == 0 then + -- literal + $dfAlignToByte() + $a1 = $dfGetIntField(16) + -- this is weird, ignore it + $dfGetIntField(16) + for loopVar = 1, $a1 do + $dfOutput(string.char($dfGetIntField(8))) + end + elseif $dfBlockType == 1 then + -- fixed Huffman + $dfReadBlockBody($dfFixedLit, $dfFixedDst) + elseif $dfBlockType == 2 then + -- dynamic Huffman + $dfReadBlockBody($dfReadHuffmanDynamic()) + else + error("b3") + end + $DTdfBlockType + while $a0 do + coroutine.yield() + end + end +end) + +-- The Outer Engine -- + +coroutine.resume($dfThread) +function $engineInput($a0, $a1) + $NTdfForLoopVar + $NTdfForLoopVar2 + for $dfForLoopVar = 1, #$a0 do + $a1 = $a0:byte($dfForLoopVar) + $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, $a1 % 2 == 1)) + $a1 = math.floor($a1 / 2) + end + end + $DTdfForLoopVar2 + $DTdfForLoopVar + -- flush prepared buffer + $engineOutput($dfPushBuf) + $dfPushBuf = "" +end +