Понадобилось мне тут распаковывать данные из deflate.
Сначала взял бинд к zlib тут:
https://code.google.com/p/lua-files/source/browse/zlib.luaПоправил инициализацию, т.к. у меня данные без заголовка и контрольной суммы:
local function init_inflate(finally)
local strm = ffi.new'z_stream'
check(C.inflateInit2_(strm, -15, M.version(), ffi.sizeof(strm)))
finally(function() check(C.inflateEnd(strm)) end)
return strm, inflate
end
Все отлично работает, но мне захотелось избавиться от зависимостей, которые тянет этот бинд. Кроме того он слишком сложно написан для меня. И из функционала мне нужна только функция inflate().
В общем взял из этого бинда только
zlib_h.lua (привязка через ffi) и переписал на lua функцию
int inf(FILE *source, FILE *dest) из этого примера:
http://www.zlib.net/zpipe.clocal function inflate(source)
local ssize = #source
local spos = 0
local function read(start, len)
spos = math.min(ssize, start + len)
return source:sub(start + 1, spos), spos - start
end
local dest = {}
local have = 0
local strm = ffi.new 'z_stream'
local out = ffi.new('uint8_t[?]', CHUNK)
-- allocate inflate state
strm.zalloc = nil;
strm.zfree = nil;
strm.opaque = nil;
strm.avail_in, strm.next_in = 0, nil;
local ret = zlib.inflateInit2_(strm, -15, zlib_version, ffi.sizeof(strm));
if ret ~= zlib.Z_OK then return ret end
-- decompress until deflate stream ends or end of file
repeat
strm.next_in, strm.avail_in = read(spos, CHUNK)
if strm.avail_in == 0 then break end
-- run inflate() on input until output buffer not full
repeat
strm.avail_out = CHUNK;
strm.next_out = out;
ret = zlib.inflate(strm, zlib.Z_NO_FLUSH)
assert(ret ~= zlib.Z_STREAM_ERROR); -- state not clobbered
if ret == zlib.Z_NEED_DICT then
ret = zlib.Z_DATA_ERROR -- and fall through
-- elseif zlib.Z_DATA_ERROR then
elseif ret == zlib.Z_MEM_ERROR then
zlib.inflateEnd(strm)
return ret
end
have = CHUNK - strm.avail_out;
if have > 0 then
dest[#dest+1] = ffi.string(out, have)
end
until strm.avail_out ~= 0
-- done when inflate() says it's done
until ret == zlib.Z_STREAM_END
-- clean up and return
zlib.inflateEnd(strm)
return ret == zlib.Z_STREAM_END and zlib.Z_OK or zlib.Z_DATA_ERROR, table.concat(dest)
end
Принимает и возвращает строку.
И оно работает, но если я распаковываю в цикле много файлов (около 16000), то моя реализация выдает пару-тройку битых файлов (всегда разных). Т.е. они распаковываются, zlib ошибок не выдает, но результат кривой. Если использую бинд из lua-files, то никаких глюков нет.
Кусок кода, вызывающий inflate(), выглядит так:
RowBody = ReadRowBody(rd)
if RowBody:sub(1, 3) ~= BOM then
local ret, res = inflate(RowBody)
if ret == zlib.Z_OK then
if res:sub(1, 3) == BOM then
local file = assert(io.open("c:\\temp\\test2\\"..rd.pos()..".txt", "wb"))
file:write(res:sub(4))
file:close()
end
else
print(zerr[ret])
end
end
Где я накололся? O_o