Oberon space
General Category => Общий раздел => Тема начата: ilovb от Апрель 28, 2013, 04:10:41 pm
-
Есть у меня код под LuaJIT:
local ffi = require("ffi")
ffi.cdef [[
void free(void *ptr);
]]
require 'miniz_h'
local miniz = ffi.load("miniz")
local function inflate(source)
local decomp_len = ffi.new 'size_t[1]'
local pdata = ffi.gc(miniz.tinfl_decompress_mem_to_heap(source, #source, decomp_len, 0), ffi.C.free)
return pdata ~= ffi.NULL, ffi.string(pdata, ffi.cast("int", decomp_len[0]))
end
local function NewZipWriter(fpath)
local zip = ffi.new 'mz_zip_archive'
zip.m_file_offset_alignment = 0
if miniz.mz_zip_writer_init_file(zip, fpath, 0) == miniz.MZ_FALSE then
return nil
end
function write(dpath, data, level)
if miniz.mz_zip_writer_add_mem(zip, dpath, data, #data, level) == miniz.MZ_FALSE then
mz_zip_writer_end(zip)
os.remove(fpath)
return false
end
return true
end
function finalize()
if miniz.mz_zip_writer_finalize_archive(zip) == miniz.MZ_FALSE then
miniz.mz_zip_writer_end(zip)
os.remove(fpath)
return false
end
miniz.mz_zip_writer_end(zip)
return true
end
return {
write = write,
finalize = finalize
}
end
Работает отлично. Память не течет.
Реализовал это в виде нативной луашной либы:
#include <ctype.h>
#include <lauxlib.h>
#include <lua.h>
#include <stdlib.h>
#include <string.h>
#include <miniz.h>
/*
* ** compatibility with Lua 5.2
* */
#if (LUA_VERSION_NUM == 502)
#undef luaL_register
#define luaL_register(L,n,f) \
{ if ((n) == NULL) luaL_setfuncs(L,f,0); else luaL_newlib(L,f); }
#endif
// static int lmz_inflate(lua_State *L);
static int lmz_inflate(lua_State *L) {
size_t decomp_len;
size_t comp_len;
void *pDecomp_data;
const char* pComp_data;
comp_len = 0;
pComp_data = luaL_checklstring(L, 1, &comp_len);
pDecomp_data = tinfl_decompress_mem_to_heap(pComp_data, comp_len, &decomp_len, 0);
lua_pop(L, 1);
if (!pDecomp_data) {
lua_pushnil(L);
return 1;
}
lua_pushlstring(L, (char*)pDecomp_data, decomp_len);
free(pDecomp_data);
return 1;
}
static mz_zip_archive *checkzip(lua_State *L) {
void *pZip = luaL_checkudata(L, 1, "lmz.zip_writer");
luaL_argcheck(L, pZip != NULL, 1, "'zip_writer' expected");
return (mz_zip_archive*)pZip;
}
static int lmz_new_zip_writer(lua_State *L) {
const char *pZip_filename;
mz_zip_archive *pZip;
pZip_filename = luaL_checklstring(L, 1, NULL);
lua_pop(L, 1);
// размещаем в стеке указатель на структуру
pZip = (mz_zip_archive*)lua_newuserdata(L, sizeof(*pZip));
memset(pZip, 0, sizeof(*pZip));
if (!mz_zip_writer_init_file(pZip, pZip_filename, 0)) {
lua_pushnil(L);
lua_pushstring(L, "Failed creating zip archive");
return 2;
}
luaL_getmetatable(L, "lmz.zip_writer");
lua_setmetatable(L, -2);
return 1;
}
static int lmz_zip_writer_write(lua_State *L) {
const char *dpath;
const char *data;
size_t dsize;
mz_zip_archive *pZip;
mz_uint level;
// при вызове метода zip:write()
// первым в стек помещается 'zip'
pZip = checkzip(L);
dpath = luaL_checklstring(L, 2, NULL);
data = luaL_checklstring(L, 3, &dsize);
level = luaL_checkint(L, 4);
lua_pop(L, 4);
if (!mz_zip_writer_add_mem(pZip, dpath, data, dsize, level)) {
mz_zip_writer_end(pZip);
lua_pushnil(L);
lua_pushstring(L, "Failed add to zip archive");
return 2;
}
lua_pushboolean(L, 1);
return 1;
}
// финализатор архива
static int lmz_zip_writer_finalize(lua_State *L) {
mz_zip_archive *pZip;
// при вызове метода zip:finalize()
// первым в стек помещается 'zip'
pZip = checkzip(L);
if (!mz_zip_writer_finalize_archive(pZip)) {
mz_zip_writer_end(pZip);
lua_pushnil(L);
lua_pushstring(L, "Failed creating zip archive");
return 2;
}
mz_zip_writer_end(pZip);
lua_pop(L, 1);
lua_pushboolean(L, 1);
return 1;
}
// функции библиотеки
static const luaL_Reg miniz_functions[] = {
{ "inflate", lmz_inflate },
{ "new_zip_writer", lmz_new_zip_writer },
{ NULL, NULL }
};
// методы zip_writer
static const luaL_Reg miniz_zip_writer_methods[] = {
{ "write", lmz_zip_writer_write },
{ "finalize", lmz_zip_writer_finalize },
{ NULL, NULL }
};
LUALIB_API int luaopen_lmz(lua_State * const L) {
// метатаблица для объекта zip_writer
luaL_newmetatable(L, "lmz.zip_writer");
lua_pushstring(L, "__index");
lua_pushvalue(L, -2); /* pushes the metatable */
lua_settable(L, -3); /* metatable.__index = metatable */
// регистрируем в метатаблице методы zip_writer
luaL_register(L, NULL, miniz_zip_writer_methods);
// регистрируем функции библиотеки
luaL_register(L, "lmz", miniz_functions);
return 1;
}
При использовании zip течет память. Хде повох? O_o
-
Течет конкретно в lmz_zip_writer_write()
-
Сначала думал на это место:
dpath = luaL_checklstring(L, 2, NULL);
data = luaL_checklstring(L, 3, &dsize);
level = luaL_checkint(L, 4);
lua_pop(L, 4);Но в документации сказано:
const char *luaL_checklstring (lua_State *L, int narg, size_t *l);
Checks whether the function argument narg is a string and returns this string; if l is not NULL fills *l with the string's length.
This function uses lua_tolstring to get its result, so all conversions and caveats of that function apply here.
...
Because Lua has garbage collection, there is no guarantee that the pointer returned by lua_tolstring will be valid after the corresponding value is removed from the stack.
Т.е. достаточно lua_pop(L, 4) чтобы gc собрал data.
Ну и насильно free() тоже делал... не помогло
-
А checkzip обязательно так странно объявлять? По-другому работать не будет?
Что делает mz_zip_writer_end(pZip);
Он есть в if-е функции с течью, но его нет за if-ом (как в ...finalize).
-
Он есть в if-е функции с течью, но его нет за if-ом (как в ...finalize).
if (!mz_zip_writer_add_mem(pZip, dpath, data, dsize, level)) {
mz_zip_writer_end(pZip);
...
} if (!mz_zip_writer_finalize_archive(pZip)) {
mz_zip_writer_end(pZip);
...
}
mz_zip_writer_end(pZip);
-
checkzip - это просто чтобы один и тот же код несколько раз не писать. (Это просто получение и проверка типа первого аргумента.)
mz_zip_writer_end - это завершение записи в архив (типа деструктор). Вызывать нужно только при ошибке и перед финализацией (записью таблицы содержимого zip)
-
// Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used.
// Note for the archive to be valid, it must have been finalized before ending.
mz_bool mz_zip_writer_end(mz_zip_archive *pZip);https://code.google.com/p/miniz/source/browse/tags/v113/miniz.c#624
-
... перед финализацией (записью таблицы содержимого zip)
вернее после финализации.
-
Код на луа, юзающий эту либу:
local common = require 'common'
local lmz = require 'lmz'
local cf = require 'cf_tools.cf_reader'
local SIG = string.char( 0xFF, 0xFF, 0xFF, 0x7F )
local level = {
NO_COMPRESSION = 0,
BEST_SPEED = 1,
BEST_COMPRESSION = 9,
UBER_COMPRESSION = 10,
DEFAULT_LEVEL = 6,
DEFAULT_COMPRESSION = -1
}
local function UnpackTo(path, rd, zip)
local Image = cf.ReadImage(rd)
local res
for ID, Body, Packed in Image.Rows() do
if Packed then
res = lmz.inflate(Body)
if res then
if res:sub(1, 4) == SIG then
UnpackTo(ID .. '/', cf.NewStringReader(res), zip)
else
assert(zip:write(path .. ID, res, level.BEST_SPEED))
end
else
print('inflate error', ID)
end
else
assert(zip:write(path .. ID, Body, level.BEST_SPEED))
end
end
end
local file = arg[1] and assert(io.open(arg[1], 'rb'))
if file then
local fdir, fnam, fext = common.parse_path(arg[1])
local zip = assert(lmz.new_zip_writer(arg[2] or fdir..fnam..'zip'))
UnpackTo('', cf.NewFileReader(file), zip)
assert(zip:finalize())
else
print 'Usage: cf_repack.lua myfile.cf [myfile.zip]'
end
На входе файлик 178 мег. На выходе zip 219 мег.
Старый код потребляет в среднем 6 мег. оперативы.
Новый код за несколько секунд взлетает до 1,5 гиг.
-
lua_pop(L, 4); А индексация с нуля или единицы? У L.
Если с нуля, то изначально в нём было 5 элементов, а извлечено 4.
-
Тут не индекс, а количество аргументов. Т.е. выталкивается из стека 4 аргумента (pZip, dpath, data, level)
-
Ну а про индексацию вот: http://www.lua.org/manual/5.1/manual.html#3.1
-
У меня идеи закончились.
-
Писец... приехали...
Запустил этот код на интерпретаторе Lua - НЕ ТЕЧЕТ...
Походу бага в LuaJIT :(
-
Писец... приехали...
Запустил этот код на интерпретаторе Lua - НЕ ТЕЧЕТ...
Походу бага в LuaJIT :(
Не факт что бага. Возможно просто особенность реализации именно что интерпретатора, из за которой этой утечки там случайно нет.
-
Гы гы :D
Воткнул в цикле collectgarbage():
for ID, Body, Packed in Image.Rows() do
if Packed then
res = lmz.inflate(Body)
if res then
if res:sub(1, 4) == SIG then
UnpackTo(ID .. '/', cf.NewStringReader(res), zip)
else
assert(zip:write(path .. ID, res, level.BEST_SPEED))
end
else
print('inflate error', ID)
end
else
assert(zip:write(path .. ID, Body, level.BEST_SPEED))
end
collectgarbage() -- СОБИРАЕМ МУСОР НАСИЛЬНО
endНЕ ТЕЧЕТ!
Походу LuaJIT так ентот цикл заворачивает, что gc совсем не вызывается
-
Забавно. Вместо простого collectgarbage() делаю так:
if collectgarbage('count') > 50 * 1024 then
print('collect!')
collectgarbage()
endВ результате в консоли ничего не печатает, но и память не течет. Комментирую строчку collectgarbage() и в консоль вываливает кучу 'collect!', память соответственно опять течет.