mirror of https://github.com/kepler155c/opus
143 lines
2.7 KiB
Lua
143 lines
2.7 KiB
Lua
-- see: https://github.com/Rochet2/lualzw
|
|
-- MIT License - Copyright (c) 2016 Rochet2
|
|
|
|
local char = string.char
|
|
local type = type
|
|
local sub = string.sub
|
|
local tconcat = table.concat
|
|
|
|
local SIGC = 'LZWC'
|
|
|
|
local basedictcompress = {}
|
|
local basedictdecompress = {}
|
|
for i = 0, 255 do
|
|
local ic, iic = char(i), char(i, 0)
|
|
basedictcompress[ic] = iic
|
|
basedictdecompress[iic] = ic
|
|
end
|
|
|
|
local function dictAddA(str, dict, a, b)
|
|
if a >= 256 then
|
|
a, b = 0, b+1
|
|
if b >= 256 then
|
|
dict = {}
|
|
b = 1
|
|
end
|
|
end
|
|
dict[str] = char(a,b)
|
|
a = a+1
|
|
return dict, a, b
|
|
end
|
|
|
|
local function compress(input)
|
|
if type(input) ~= "string" then
|
|
error ("string expected, got "..type(input))
|
|
end
|
|
local len = #input
|
|
if len <= 1 then
|
|
return input
|
|
end
|
|
|
|
local dict = {}
|
|
local a, b = 0, 1
|
|
|
|
local result = { SIGC }
|
|
local resultlen = 1
|
|
local n = 2
|
|
local word = ""
|
|
for i = 1, len do
|
|
local c = sub(input, i, i)
|
|
local wc = word..c
|
|
if not (basedictcompress[wc] or dict[wc]) then
|
|
local write = basedictcompress[word] or dict[word]
|
|
if not write then
|
|
error "algorithm error, could not fetch word"
|
|
end
|
|
result[n] = write
|
|
resultlen = resultlen + #write
|
|
n = n+1
|
|
if len <= resultlen then
|
|
return input
|
|
end
|
|
dict, a, b = dictAddA(wc, dict, a, b)
|
|
word = c
|
|
else
|
|
word = wc
|
|
end
|
|
end
|
|
result[n] = basedictcompress[word] or dict[word]
|
|
resultlen = resultlen+#result[n]
|
|
if len <= resultlen then
|
|
return input
|
|
end
|
|
return tconcat(result)
|
|
end
|
|
|
|
local function dictAddB(str, dict, a, b)
|
|
if a >= 256 then
|
|
a, b = 0, b+1
|
|
if b >= 256 then
|
|
dict = {}
|
|
b = 1
|
|
end
|
|
end
|
|
dict[char(a,b)] = str
|
|
a = a+1
|
|
return dict, a, b
|
|
end
|
|
|
|
local function decompress(input)
|
|
if type(input) ~= "string" then
|
|
error( "string expected, got "..type(input))
|
|
end
|
|
|
|
if #input < 4 then
|
|
return input
|
|
end
|
|
|
|
local control = sub(input, 1, 4)
|
|
if control ~= SIGC then
|
|
return input
|
|
end
|
|
input = sub(input, 5)
|
|
local len = #input
|
|
|
|
if len < 2 then
|
|
error("invalid input - not a compressed string")
|
|
end
|
|
|
|
local dict = {}
|
|
local a, b = 0, 1
|
|
|
|
local result = {}
|
|
local n = 1
|
|
local last = sub(input, 1, 2)
|
|
result[n] = basedictdecompress[last] or dict[last]
|
|
n = n+1
|
|
for i = 3, len, 2 do
|
|
local code = sub(input, i, i+1)
|
|
local lastStr = basedictdecompress[last] or dict[last]
|
|
if not lastStr then
|
|
error( "could not find last from dict. Invalid input?")
|
|
end
|
|
local toAdd = basedictdecompress[code] or dict[code]
|
|
if toAdd then
|
|
result[n] = toAdd
|
|
n = n+1
|
|
dict, a, b = dictAddB(lastStr..sub(toAdd, 1, 1), dict, a, b)
|
|
else
|
|
local tmp = lastStr..sub(lastStr, 1, 1)
|
|
result[n] = tmp
|
|
n = n+1
|
|
dict, a, b = dictAddB(tmp, dict, a, b)
|
|
end
|
|
last = code
|
|
end
|
|
return tconcat(result)
|
|
end
|
|
|
|
return {
|
|
compress = compress,
|
|
decompress = decompress,
|
|
}
|