slimpack/lib/zlib.c
2025-03-08 23:30:39 +02:00

137 lines
3.4 KiB
C

#include <zlib.h>
#include <lauxlib.h>
#include <lua.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "lib.h"
#define DECOMPRESS_META "zlib.decompress.meta"
typedef struct {
z_stream stream;
size_t processed;
uint8_t buff[4096];
bool finalized;
} lua_zlib_stream_t;
static int lib_decompress_iter(lua_State *ctx) {
size_t size;
uint8_t *data = (uint8_t*)lua_tolstring(ctx, lua_upvalueindex(1), &size);
lua_zlib_stream_t *stream = lua_touserdata(ctx, lua_upvalueindex(2));
if (stream->finalized) {
lua_pushnil(ctx);
return 1;
}
stream->stream.next_in = data + stream->processed;
stream->stream.avail_in = size - stream->processed;
stream->stream.next_out = stream->buff;
stream->stream.avail_out = sizeof stream->buff;
int res = inflate(&stream->stream, Z_NO_FLUSH);
switch (res) {
case Z_STREAM_END:
inflateEnd(&stream->stream);
stream->finalized = true;
case Z_OK: {
lua_pushlstring(ctx, (const char*)stream->buff, sizeof stream->buff - stream->stream.avail_out);
stream->processed = size - stream->stream.avail_in;
return 1;
}
case Z_BUF_ERROR:
inflateEnd(&stream->stream);
stream->finalized = true;
return luaL_error(ctx, "zlib error: incorrect C parameters passed");
case Z_MEM_ERROR:
inflateEnd(&stream->stream);
stream->finalized = true;
return luaL_error(ctx, "zlib error: memory ran out");
case Z_STREAM_ERROR:
inflateEnd(&stream->stream);
stream->finalized = true;
return luaL_error(ctx, "zlib error: invalid state");
case Z_DATA_ERROR:
inflateEnd(&stream->stream);
stream->finalized = true;
return luaL_error(ctx, "zlib error: %s", stream->stream.msg);
default:
return luaL_error(ctx, "wtf");
}
}
static int lib_decompress_free(lua_State *ctx) {
lua_zlib_stream_t *stream = lua_touserdata(ctx, 1);
if (stream->finalized) return 0;
inflateEnd(&stream->stream);
stream->finalized = true;
return 0;
}
static int lib_decompress_create(lua_State *ctx, int window_size) {
// size_t data_size;
// uint8_t *data = (uint8_t*)luaL_checklstring(ctx, 1, &data_size);
lua_pushvalue(ctx, 1);
lua_zlib_stream_t *stream = lua_newuserdatauv(ctx, sizeof *stream, 0);
memset(stream, 0, sizeof *stream);
luaL_getmetatable(ctx, DECOMPRESS_META);
lua_setmetatable(ctx, -2);
// stream->stream.avail_out = sizeof stream->buff;
// stream->stream.next_out = stream->buff;
// stream->stream.avail_in = data_size;
// stream->stream.next_in = data;
if (inflateInit2(&stream->stream, window_size) != Z_OK) {
return luaL_error(ctx, "failed to initialize inflate stream");
}
lua_pushcclosure(ctx, lib_decompress_iter, 2);
return 1;
}
static int lib_inflate(lua_State *ctx) {
return lib_decompress_create(ctx, MAX_WBITS);
}
static int lib_inflate_raw(lua_State *ctx) {
return lib_decompress_create(ctx, -MAX_WBITS);
}
static int lib_gunzip(lua_State *ctx) {
return lib_decompress_create(ctx, MAX_WBITS + 16);
}
static int lib_decompress(lua_State *ctx) {
return lib_decompress_create(ctx, MAX_WBITS + 32);
}
int zlib_open_lib(lua_State *ctx) {
luaL_newmetatable(ctx, DECOMPRESS_META);
luaL_setfuncs(ctx, (luaL_Reg[]) {
{ "__gc", lib_decompress_free },
{ NULL, NULL },
}, 0);
lua_pop(ctx, 2);
luaL_newlib(ctx, ((luaL_Reg[]) {
{ "inflate", lib_inflate },
{ "inflate_raw", lib_inflate_raw },
{ "gunzip", lib_gunzip },
{ "decompress", lib_decompress },
{ NULL, NULL },
}));
return 1;
}