Automated update from: http://smariot.no-ip.org/translate
[QuestHelper.git] / collect_lzw.lua
blobfb03dd174e95d63209c83f25fd5162b208086918
1 QuestHelper_File["collect_lzw.lua"] = "Development Version"
2 QuestHelper_Loadtime["collect_lzw.lua"] = GetTime()
4 local Merger
5 local Bitstream
7 local function cleanup(tab)
8 for _, v in pairs(tab) do
9 QuestHelper:ReleaseTable(v)
10 end
11 QuestHelper:ReleaseTable(tab)
12 end
14 local function QH_LZW_Decompress(input, tokens, outbits, inputstatic)
15 local d = QuestHelper:CreateTable("lzw")
16 local i
17 for i = 0, tokens-1 do
18 d[i] = QuestHelper:CreateTable("lzw")
19 d[i][0] = string.char(i)
20 end
22 local dsize = tokens + 1 -- we use the "tokens" value as an EOF marker
24 if inputstatic then
25 local used = QuestHelper:CreateTable("lzw")
26 for _, v in ipairs(inputstatic) do
27 for i = #v, 2, -1 do
28 local subi = v:sub(1, i)
29 if not used[subi] then
30 used[subi] = true
32 d[bit.mod(dsize, tokens)][math.floor(dsize / tokens)] = subi
33 dsize = dsize + 1
34 else
35 break
36 end
37 end
38 end
39 QuestHelper:ReleaseTable(used)
40 end
42 local bits = 1
43 local nextbits = 2
45 while nextbits < dsize do bits = bits + 1; nextbits = nextbits * 2 end
47 local i = Bitstream.Input(input, outbits)
48 local rv = {}
50 local idlect = 0
52 local tok = i:depend(bits)
53 if tok == tokens then cleanup(d) return "" end -- Okay. There's nothing. We get it.
55 Merger.Add(rv, d[bit.mod(tok, tokens)][math.floor(tok / tokens)])
56 local w = d[bit.mod(tok, tokens)][math.floor(tok / tokens)]
57 while true do
58 if idlect == 25 then
59 QH_Timeslice_Yield()
60 idlect = 0
61 else
62 idlect = idlect + 1
63 end
65 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.
66 if dsize > nextbits then
67 bits = bits + 1
68 nextbits = nextbits * 2
69 end
71 tok = i:depend(bits)
72 if tok == tokens then break end -- we're done!
74 local entry
75 if d[bit.mod(tok, tokens)][math.floor(tok / tokens)] then
76 entry = d[bit.mod(tok, tokens)][math.floor(tok / tokens)]
77 elseif tok == dsize - 1 then
78 entry = w .. w:sub(1, 1)
79 else
80 QuestHelper: Assert(false, "faaaail")
81 end
82 Merger.Add(rv, entry)
84 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.
86 w = entry
87 end
89 cleanup(d)
91 return Merger.Finish(rv)
92 end
94 local function QH_LZW_Compress(input, tokens, outbits, inputstatic)
95 -- shared init code
96 local d = {}
97 local i
98 for i = 0, tokens-1 do
99 d[string.char(i)] = {[""] = i}
102 local dsize = tokens + 1 -- we use the "tokens" value as an EOF marker
104 if inputstatic then
105 for _, v in ipairs(inputstatic) do
106 local da = d[v:sub(1, 1)]
107 for i = #v, 2, -1 do
108 local b = v:sub(2, i)
109 if not da[b] then
110 da[b] = dsize
111 dsize = dsize + 1
112 else
113 break
119 local bits = 1
120 local nextbits = 2
122 while nextbits < dsize do bits = bits + 1; nextbits = nextbits * 2 end
124 local r = Bitstream.Output(outbits)
126 local idlect = 0
128 local w = ""
129 for ci = 1, #input do
130 if idlect == 100 then
131 QH_Timeslice_Yield()
132 idlect = 0
133 else
134 idlect = idlect + 1
137 local c = input:sub(ci, ci)
138 local wcp = w .. c
139 if d[wcp:sub(1, 1)][wcp:sub(2)] then
140 w = wcp
141 else
142 r:append(d[w:sub(1, 1)][w:sub(2)], bits)
143 d[wcp:sub(1, 1)][wcp:sub(2)] = dsize
144 dsize = dsize + 1
145 if dsize > nextbits then
146 bits = bits + 1
147 nextbits = nextbits * 2
149 w = c
153 if w ~= "" then
154 r:append(d[w:sub(1, 1)][w:sub(2)], bits)
156 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.
157 if dsize > nextbits then
158 bits = bits + 1
159 nextbits = nextbits * 2
163 r:append(tokens, bits)
165 local rst = r:finish()
166 QuestHelper: Assert(QH_LZW_Decompress(rst, tokens, outbits, inputstatic) == input) -- yay
168 return rst
171 local function mdict(inputdict)
172 local idc = QuestHelper:CreateTable("lzw mdict")
173 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
174 return idc
177 local function dictize(idcl, stt)
178 local im = QuestHelper:CreateTable("lzw dictize")
179 for i = 1, #stt do
180 local subl = idcl[stt:sub(i, i)]
181 QuestHelper: Assert(subl)
182 Merger.Add(im, subl)
184 local result = Merger.Finish(im)
185 QuestHelper:ReleaseTable(im)
186 return result
189 local function QH_LZW_Prepare(inputdict, inputstatic)
190 QuestHelper: Assert(inputdict and inputstatic)
192 local idc = mdict(inputdict)
194 local inpstat = {}
195 local ct = 0
196 for _, v in ipairs(inputstatic) do
197 ct = ct + 1
198 if ct == 10 then QH_Timeslice_Yield() ct = 0 end
199 table.insert(inpstat, dictize(idc, v))
202 return idc, #inputdict, inpstat
205 local function QH_LZW_Compress_Dicts_Prepared(input, idprepped, idpreppedsize, outputdict, isprepped)
206 input = dictize(idprepped, input)
208 local bits, dsize = 1, 2
209 if not outputdict then bits = 8 else while dsize < #outputdict do bits = bits + 1 ; dsize = dsize * 2 end end
210 QuestHelper: Assert(not outputdict or #outputdict == dsize)
212 local comp = QH_LZW_Compress(input, idpreppedsize, bits, isprepped)
214 if outputdict then
215 local origcomp = comp
216 local im = {}
217 for i = 1, #origcomp do Merger.Add(im, outputdict:sub(strbyte(origcomp:sub(i, i)) + 1)) end
218 comp = Merger.Finish(im)
221 return comp
224 local function QH_LZW_Compress_Dicts(input, inputdict, outputdict, inputstatic)
225 local inproc = input
226 local inpstat = inputstatic
227 if inputdict then
228 local idc = mdict(inputdict)
230 inproc = dictize(idc, input)
232 if inputstatic then
233 inpstat = {}
234 for _, v in ipairs(inputstatic) do
235 table.insert(inpstat, dictize(idc, v))
239 QuestHelper:ReleaseTable(idc)
242 local bits, dsize = 1, 2
243 if not outputdict then bits = 8 else while dsize < #outputdict do bits = bits + 1 ; dsize = dsize * 2 end end
244 QuestHelper: Assert(not outputdict or #outputdict == dsize)
246 local comp = QH_LZW_Compress(inproc, inputdict and #inputdict or 256, bits, inpstat)
248 if outputdict then
249 local origcomp = comp
250 local im = {}
251 for i = 1, #origcomp do Merger.Add(im, outputdict:sub(strbyte(origcomp:sub(i, i)) + 1)) end
252 comp = Merger.Finish(im)
255 return comp
258 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
259 QuestHelper: Assert(not outputdict)
260 QuestHelper: Assert(inputdict)
262 local decomp = QH_LZW_Decompress(compressed, #inputdict, 8, ispreppred)
264 local ov = {}
265 for i = 1, #decomp do
266 Merger.Add(ov, inputdict:sub(decomp:byte(i) + 1, decomp:byte(i) + 1))
268 return Merger.Finish(ov)
271 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
272 QuestHelper: Assert(not outputdict)
273 QuestHelper: Assert(inputdict)
275 local inpstat = inputstatic
276 if inputdict and inputstatic then
277 local idc = mdict(inputdict)
279 inpstat = {}
280 for _, v in ipairs(inputstatic) do
281 table.insert(inpstat, dictize(idc, v))
284 QuestHelper:ReleaseTable(idc)
287 local decomp = QH_LZW_Decompress(compressed, #inputdict, 8, inpstat)
289 local ov = {}
290 for i = 1, #decomp do
291 Merger.Add(ov, inputdict:sub(decomp:byte(i) + 1, decomp:byte(i) + 1))
293 return Merger.Finish(ov)
296 QH_LZW_Prepare_Arghhacky = QH_LZW_Prepare -- need to rig up a better mechanism for this really
297 QH_LZW_Decompress_Dicts_Arghhacky = QH_LZW_Decompress_Dicts -- need to rig up a better mechanism for this really
298 QH_LZW_Decompress_Dicts_Prepared_Arghhacky = QH_LZW_Decompress_Dicts_Prepared -- need to rig up a better mechanism for this really
300 function QH_Collect_LZW_Init(_, API)
301 Merger = API.Utility_Merger
302 QuestHelper: Assert(Merger)
304 Bitstream = API.Utility_Bitstream
305 QuestHelper: Assert(Bitstream)
307 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}
310 -- old debug code :)
312 --[[
313 print("hello")
315 QH_LZW_Compress("TOBEORNOTTOBEORTOBEORNOT", 256, 8)
318 --[[
319 QuestHelper:TextOut("lulz")
321 local inq = "ABABABABA"
322 local alpha = 253
323 local bits = 7
325 str = QH_LZW_Compress(inq, alpha, bits)
326 tvr = ""
327 for i = 1, #str do
328 tvr = tvr .. string.format("%d ", strbyte(str, i))
330 QuestHelper:TextOut(tvr)
332 ret = QH_LZW_Decompress(str, alpha, bits)
333 QuestHelper:TextOut(ret)
335 QuestHelper: Assert(inq == ret)