RosettaCodeData/Task/LZW-compression/Phix/lzw-compression-2.phix
2026-04-30 12:34:36 -04:00

195 lines
5.6 KiB
Text

-- demo\rosetta\LZW_compression.exw
with javascript_semantics
constant CLR = 256, -- Clear table marker
EOD = 257, -- End-of-data marker
NEW = 258 -- New code index
integer bits, tmp, oBits
function finished_bytes(integer x)
sequence bytes = {}
tmp = (tmp << bits) + x
oBits += bits
while oBits >= 8 do
oBits -= 8
assert(floor(tmp/power(2,oBits)) <= 255)
bytes &= floor(tmp/power(2,oBits))
tmp = and_bits(tmp, power(2,oBits)-1)
end while
return bytes
end function
function lzwEncode(sequence inp, integer maxBits)
assert(maxBits>=9 and maxBits<=16)
// Encode dictionary array. For encoding, entry at code index
// is a list of indices that follow current one, for example
// if code 97 is 'a', code 287 is 'ab', and code 922 is 'abc',
// then dict[97]['b'+1] = 287, dict[287]['c'+1] = 922, etc.
sequence dict = repeat(repeat(0,256),512),
result = {}
oBits = 0
tmp = 0
bits = 9
integer nextCode = NEW,
nextShift = 512
integer code = inp[1]
for c in inp from 2 do
integer nc = dict[code+1][c+1]
if nc!=0 then
code = nc
else
result &= finished_bytes(code)
nc = nextCode
dict[code+1][c+1] = nc
nextCode += 1
code = c
end if
// Next new code would be too long for current table.
if nextCode = nextShift then
// "Early increment", matches compress and GIF use,
// some TIFF variants do a "Late increment" instead.
bits += 1
// Either reset table back to 9 bits...
if bits>maxBits then
// Table clear marker must occur before bit reset.
result &= finished_bytes(CLR)
bits = 9
nextShift = 512
nextCode = NEW
dict = repeat(repeat(0,256),512)
else // ... or extend table.
nextShift *= 2
while length(dict)<nextShift do
dict = append(dict,repeat(0,256))
end while
end if
end if
end for
result &= finished_bytes(code)
result &= finished_bytes(EOD)
if tmp!=0 then
assert(tmp<=#FFFF,"tmp > 16 bits")
result &= finished_bytes(tmp)
end if
return result
end function
--
-- For decoding, dictionary contains index of whatever prefix
-- index plus trailing ubyte. i.e. like previous example,
-- dict[1022] = { c: 'c', prev: 387 },
-- dict[387] = { c: 'b', prev: 97 },
-- dict[97] = { c: 'a', prev: 0 }
-- the "back" element is used for temporarily chaining indices
-- when resolving a code to bytes.
--
enum PREV, BACK, CH -- decode dict entry subscripts
sequence dict
integer nextShift, nextCode -- (and re-using bits)
procedure clearTable()
dict = repeat({0,0,0},512)
for j=0 to 255 do
dict[j+1][CH] = j
end for
nextCode = NEW
nextShift = 512
bits = 9
end procedure
function lzwDecode(sequence inp)
sequence result = {}
integer len = length(inp),
idx = 1,
code = 0,
nBits = 0,
tmp = 0
clearTable() // In case encoded bits didn't start with CLR.
while idx<=len do
while nBits < bits do
if idx<=len then
tmp = (tmp << 8) + inp[idx]
idx += 1
nBits += 8
else
tmp = tmp << (bits-nBits)
nBits = bits
end if
end while
nBits -= bits
code = floor(tmp/power(2,nBits))
tmp = and_bits(tmp,power(2,nBits)-1)
if code=EOD then exit end if
if code=CLR then
clearTable()
elsif code>=nextCode then
crash("Bad sequence")
else
integer c = code
dict[nextCode+1][PREV] = c
while c>255 do
integer t = dict[c+1][PREV]
dict[t+1][BACK] = c
c = t
end while
dict[nextCode][CH] = c
integer cc = c+1
while dict[cc][BACK]!=0 do
result &= dict[cc][CH]
integer t = dict[cc][BACK]
dict[cc][BACK] = 0
cc = t+1
end while
result &= dict[cc][CH]
nextCode += 1
if nextCode>=nextShift then
// "Early increment", must match encode.
bits += 1
// If input was correct, we'd have hit a CLR.
if bits>16 then crash("Too many bits") end if
nextShift *= 2
while length(dict)<nextShift do
dict = append(dict,repeat(0,3))
end while
end if
end if
end while
if code!=EOD then crash("Bits did not end in EOD") end if
return result
end function
procedure main()
sequence inputData = get_text(`demo\unixdict.txt`,GT_BINARY)
integer li = length(inputData)
printf(1,"Input size: %d\n",li)
sequence encoded = lzwEncode(inputData,12)
integer le = length(encoded)
printf(1,"Encoded size: %d (%d%%)\n",{le,le/li*100})
sequence decoded = lzwDecode(encoded)
printf(1,"Decoded size: %d\n",length(decoded))
if length(inputData)!=length(decoded) then
printf(1,"Error: decoded size differs\n")
return
end if
for i=1 to length(inputData) do
if inputData[i]!=decoded[i] then
printf(1,"Bad decode at %d\n",i)
return
end if
end for
puts(1,"Decoded OK.\n")
end procedure
main()