2012-01-01 04:01:18 +01:00
local GetTime = QuestHelper_GetTime
2010-11-08 14:28:59 +01:00
QuestHelper_File [ " collect_lzw.lua " ] = " 4.0.1.$svnversion$ "
2010-10-24 23:17:33 +02:00
QuestHelper_Loadtime [ " collect_lzw.lua " ] = GetTime ( )
local Merger
local Bitstream
local function cleanup ( tab )
for _ , v in pairs ( tab ) do
QuestHelper : ReleaseTable ( v )
end
QuestHelper : ReleaseTable ( tab )
end
local function QH_LZW_Decompress ( input , tokens , outbits , inputstatic )
local d = QuestHelper : CreateTable ( " lzw " )
local i
for i = 0 , tokens - 1 do
d [ i ] = QuestHelper : CreateTable ( " lzw " )
d [ i ] [ 0 ] = string.char ( i )
end
local dsize = tokens + 1 -- we use the "tokens" value as an EOF marker
if inputstatic then
local used = QuestHelper : CreateTable ( " lzw " )
for _ , v in ipairs ( inputstatic ) do
for i = # v , 2 , - 1 do
local subi = v : sub ( 1 , i )
if not used [ subi ] then
used [ subi ] = true
d [ bit.mod ( dsize , tokens ) ] [ math.floor ( dsize / tokens ) ] = subi
dsize = dsize + 1
else
break
end
end
end
QuestHelper : ReleaseTable ( used )
end
local bits = 1
local nextbits = 2
while nextbits < dsize do bits = bits + 1 ; nextbits = nextbits * 2 end
local i = Bitstream.Input ( input , outbits )
local rv = { }
local idlect = 0
local tok = i : depend ( bits )
if tok == tokens then cleanup ( d ) return " " end -- Okay. There's nothing. We get it.
Merger.Add ( rv , d [ bit.mod ( tok , tokens ) ] [ math.floor ( tok / tokens ) ] )
local w = d [ bit.mod ( tok , tokens ) ] [ math.floor ( tok / tokens ) ]
while true do
if idlect == 25 then
QH_Timeslice_Yield ( )
idlect = 0
else
idlect = idlect + 1
end
dsize = dsize + 1 -- We haven't actually added the next element yet. However, we could in theory include it in the stream, so we need to adjust the number of bits properly.
if dsize > nextbits then
bits = bits + 1
nextbits = nextbits * 2
end
tok = i : depend ( bits )
if tok == tokens then break end -- we're done!
local entry
if d [ bit.mod ( tok , tokens ) ] [ math.floor ( tok / tokens ) ] then
entry = d [ bit.mod ( tok , tokens ) ] [ math.floor ( tok / tokens ) ]
elseif tok == dsize - 1 then
entry = w .. w : sub ( 1 , 1 )
else
QuestHelper : Assert ( false , " faaaail " )
end
Merger.Add ( rv , entry )
d [ bit.mod ( dsize - 1 , tokens ) ] [ math.floor ( ( dsize - 1 ) / tokens ) ] = w .. entry : sub ( 1 , 1 ) -- Naturally, we're writing to one *less* than dsize, since we already incremented.
w = entry
end
cleanup ( d )
return Merger.Finish ( rv )
end
local function QH_LZW_Compress ( input , tokens , outbits , inputstatic )
-- shared init code
local d = { }
local i
for i = 0 , tokens - 1 do
d [ string.char ( i ) ] = { [ " " ] = i }
end
local dsize = tokens + 1 -- we use the "tokens" value as an EOF marker
if inputstatic then
for _ , v in ipairs ( inputstatic ) do
local da = d [ v : sub ( 1 , 1 ) ]
for i = # v , 2 , - 1 do
local b = v : sub ( 2 , i )
if not da [ b ] then
da [ b ] = dsize
dsize = dsize + 1
else
break
end
end
end
end
local bits = 1
local nextbits = 2
while nextbits < dsize do bits = bits + 1 ; nextbits = nextbits * 2 end
local r = Bitstream.Output ( outbits )
local idlect = 0
local w = " "
for ci = 1 , # input do
if idlect == 100 then
QH_Timeslice_Yield ( )
idlect = 0
else
idlect = idlect + 1
end
local c = input : sub ( ci , ci )
local wcp = w .. c
if d [ wcp : sub ( 1 , 1 ) ] [ wcp : sub ( 2 ) ] then
w = wcp
else
r : append ( d [ w : sub ( 1 , 1 ) ] [ w : sub ( 2 ) ] , bits )
d [ wcp : sub ( 1 , 1 ) ] [ wcp : sub ( 2 ) ] = dsize
dsize = dsize + 1
if dsize > nextbits then
bits = bits + 1
nextbits = nextbits * 2
end
w = c
end
end
if w ~= " " then
r : append ( d [ w : sub ( 1 , 1 ) ] [ w : sub ( 2 ) ] , bits )
dsize = dsize + 1 -- Our decompressor doesn't realize we're ending here, so it will have added a table entry for that last token. Sigh.
if dsize > nextbits then
bits = bits + 1
nextbits = nextbits * 2
end
end
r : append ( tokens , bits )
local rst = r : finish ( )
QuestHelper : Assert ( QH_LZW_Decompress ( rst , tokens , outbits , inputstatic ) == input ) -- yay
return rst
end
local function mdict ( inputdict )
local idc = QuestHelper : CreateTable ( " lzw mdict " )
for i = 1 , # inputdict do if math.fmod ( i , 100 ) == 0 then QH_Timeslice_Yield ( ) end idc [ inputdict : sub ( i , i ) ] = strchar ( i - 1 ) end
return idc
end
local function dictize ( idcl , stt )
local im = QuestHelper : CreateTable ( " lzw dictize " )
for i = 1 , # stt do
local subl = idcl [ stt : sub ( i , i ) ]
QuestHelper : Assert ( subl )
Merger.Add ( im , subl )
end
local result = Merger.Finish ( im )
QuestHelper : ReleaseTable ( im )
return result
end
local function QH_LZW_Prepare ( inputdict , inputstatic )
QuestHelper : Assert ( inputdict and inputstatic )
local idc = mdict ( inputdict )
local inpstat = { }
local ct = 0
for _ , v in ipairs ( inputstatic ) do
ct = ct + 1
if ct == 10 then QH_Timeslice_Yield ( ) ct = 0 end
table.insert ( inpstat , dictize ( idc , v ) )
end
return idc , # inputdict , inpstat
end
local function QH_LZW_Compress_Dicts_Prepared ( input , idprepped , idpreppedsize , outputdict , isprepped )
input = dictize ( idprepped , input )
local bits , dsize = 1 , 2
if not outputdict then bits = 8 else while dsize < # outputdict do bits = bits + 1 ; dsize = dsize * 2 end end
QuestHelper : Assert ( not outputdict or # outputdict == dsize )
local comp = QH_LZW_Compress ( input , idpreppedsize , bits , isprepped )
if outputdict then
local origcomp = comp
local im = { }
for i = 1 , # origcomp do Merger.Add ( im , outputdict : sub ( strbyte ( origcomp : sub ( i , i ) ) + 1 ) ) end
comp = Merger.Finish ( im )
end
return comp
end
local function QH_LZW_Compress_Dicts ( input , inputdict , outputdict , inputstatic )
local inproc = input
local inpstat = inputstatic
if inputdict then
local idc = mdict ( inputdict )
inproc = dictize ( idc , input )
if inputstatic then
inpstat = { }
for _ , v in ipairs ( inputstatic ) do
table.insert ( inpstat , dictize ( idc , v ) )
end
end
QuestHelper : ReleaseTable ( idc )
end
local bits , dsize = 1 , 2
if not outputdict then bits = 8 else while dsize < # outputdict do bits = bits + 1 ; dsize = dsize * 2 end end
QuestHelper : Assert ( not outputdict or # outputdict == dsize )
local comp = QH_LZW_Compress ( inproc , inputdict and # inputdict or 256 , bits , inpstat )
if outputdict then
local origcomp = comp
local im = { }
for i = 1 , # origcomp do Merger.Add ( im , outputdict : sub ( strbyte ( origcomp : sub ( i , i ) ) + 1 ) ) end
comp = Merger.Finish ( im )
end
return comp
end
local function QH_LZW_Decompress_Dicts_Prepared ( compressed , inputdict , outputdict , ispreppred ) -- this is kind of backwards - we assume that "outputdict" is the dictionary that "compressed" is encoded in
QuestHelper : Assert ( not outputdict )
QuestHelper : Assert ( inputdict )
local decomp = QH_LZW_Decompress ( compressed , # inputdict , 8 , ispreppred )
local ov = { }
for i = 1 , # decomp do
Merger.Add ( ov , inputdict : sub ( decomp : byte ( i ) + 1 , decomp : byte ( i ) + 1 ) )
end
return Merger.Finish ( ov )
end
local function QH_LZW_Decompress_Dicts ( compressed , inputdict , outputdict , inputstatic ) -- this is kind of backwards - we assume that "outputdict" is the dictionary that "compressed" is encoded in
QuestHelper : Assert ( not outputdict )
QuestHelper : Assert ( inputdict )
local inpstat = inputstatic
if inputdict and inputstatic then
local idc = mdict ( inputdict )
inpstat = { }
for _ , v in ipairs ( inputstatic ) do
table.insert ( inpstat , dictize ( idc , v ) )
end
QuestHelper : ReleaseTable ( idc )
end
local decomp = QH_LZW_Decompress ( compressed , # inputdict , 8 , inpstat )
local ov = { }
for i = 1 , # decomp do
Merger.Add ( ov , inputdict : sub ( decomp : byte ( i ) + 1 , decomp : byte ( i ) + 1 ) )
end
return Merger.Finish ( ov )
end
QH_LZW_Prepare_Arghhacky = QH_LZW_Prepare -- need to rig up a better mechanism for this really
QH_LZW_Decompress_Dicts_Arghhacky = QH_LZW_Decompress_Dicts -- need to rig up a better mechanism for this really
QH_LZW_Decompress_Dicts_Prepared_Arghhacky = QH_LZW_Decompress_Dicts_Prepared -- need to rig up a better mechanism for this really
function QH_Collect_LZW_Init ( _ , API )
Merger = API.Utility_Merger
QuestHelper : Assert ( Merger )
Bitstream = API.Utility_Bitstream
QuestHelper : Assert ( Bitstream )
API.Utility_LZW = { Compress = QH_LZW_Compress , Decompress = QH_LZW_Decompress , Compress_Dicts = QH_LZW_Compress_Dicts , Decompress_Dicts = QH_LZW_Decompress_Dicts , Prepare = QH_LZW_Prepare , Compress_Dicts_Prepared = QH_LZW_Compress_Dicts_Prepared , Decompress_Dicts_Prepared = QH_LZW_Decompress_Dicts_Prepared }
end
-- old debug code :)
--[[
print ( " hello " )
QH_LZW_Compress ( " TOBEORNOTTOBEORTOBEORNOT " , 256 , 8 )
] ]
--[[
QuestHelper : TextOut ( " lulz " )
local inq = " ABABABABA "
local alpha = 253
local bits = 7
str = QH_LZW_Compress ( inq , alpha , bits )
tvr = " "
for i = 1 , # str do
tvr = tvr .. string.format ( " %d " , strbyte ( str , i ) )
end
QuestHelper : TextOut ( tvr )
ret = QH_LZW_Decompress ( str , alpha , bits )
QuestHelper : TextOut ( ret )
QuestHelper : Assert ( inq == ret )
] ]