mirror of
https://github.com/ctruLua/ctruLua.git
synced 2025-10-27 16:39:29 +00:00
986 lines
27 KiB
C
986 lines
27 KiB
C
/************************************************************************
|
|
* Author : Tiago Dionizio <tiago.dionizio@gmail.com> *
|
|
* Library : lzlib - Lua 5 interface to access zlib library functions *
|
|
* *
|
|
* Permission is hereby granted, free of charge, to any person obtaining *
|
|
* a copy of this software and associated documentation files (the *
|
|
* "Software"), to deal in the Software without restriction, including *
|
|
* without limitation the rights to use, copy, modify, merge, publish, *
|
|
* distribute, sublicense, and/or sell copies of the Software, and to *
|
|
* permit persons to whom the Software is furnished to do so, subject to *
|
|
* the following conditions: *
|
|
* *
|
|
* The above copyright notice and this permission notice shall be *
|
|
* included in all copies or substantial portions of the Software. *
|
|
* *
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY *
|
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, *
|
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE *
|
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
|
************************************************************************/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "lua.h"
|
|
#include "lauxlib.h"
|
|
|
|
#include "zlib.h"
|
|
|
|
/*
|
|
** =========================================================================
|
|
** compile time options wich determine available functionality
|
|
** =========================================================================
|
|
*/
|
|
|
|
/* TODO
|
|
|
|
- also call flush on table/userdata when flush function is detected
|
|
- remove io_cb check inflate_block if condition
|
|
- only set eos when ZSTREAM_END is reached
|
|
- check for stream errors to close stream when really needed
|
|
|
|
*/
|
|
|
|
|
|
/*
|
|
** =========================================================================
|
|
** zlib stream metamethods
|
|
** =========================================================================
|
|
*/
|
|
#define ZSTREAMMETA "zlib:zstream"
|
|
|
|
#define LZ_ANY -1
|
|
#define LZ_NONE 0
|
|
#define LZ_DEFLATE 1
|
|
#define LZ_INFLATE 2
|
|
|
|
#if 0
|
|
#define LZ_BUFFER_SIZE LUAL_BUFFERSIZE
|
|
#else
|
|
#define LZ_BUFFER_SIZE 8192
|
|
#endif
|
|
|
|
typedef struct {
|
|
/* zlib structures */
|
|
z_stream zstream;
|
|
/* stream state. LZ_DEFLATE | LZ_INFLATE */
|
|
int state;
|
|
int error;
|
|
int peek;
|
|
int eos;
|
|
/* user callback source for reading/writing */
|
|
int io_cb;
|
|
/* input buffer */
|
|
int i_buffer_ref;
|
|
size_t i_buffer_pos;
|
|
size_t i_buffer_len;
|
|
const char *i_buffer;
|
|
/* output buffer */
|
|
size_t o_buffer_len;
|
|
size_t o_buffer_max;
|
|
char o_buffer[LZ_BUFFER_SIZE];
|
|
/* dictionary */
|
|
const Bytef *dictionary;
|
|
size_t dictionary_len;
|
|
} lz_stream;
|
|
|
|
|
|
/* forward declarations */
|
|
static int lzstream_docompress(lua_State *L, lz_stream *s, int from, int to, int flush);
|
|
|
|
|
|
static lz_stream *lzstream_new(lua_State *L, int src) {
|
|
lz_stream *s = (lz_stream*)lua_newuserdata(L, sizeof(lz_stream));
|
|
|
|
luaL_getmetatable(L, ZSTREAMMETA);
|
|
lua_setmetatable(L, -2); /* set metatable */
|
|
|
|
s->state = LZ_NONE;
|
|
s->error = Z_OK;
|
|
s->eos = 0;
|
|
s->io_cb = LUA_REFNIL;
|
|
|
|
s->i_buffer = NULL;
|
|
s->i_buffer_ref = LUA_REFNIL;
|
|
s->i_buffer_pos = 0;
|
|
s->i_buffer_len = 0;
|
|
|
|
s->peek = 0;
|
|
s->o_buffer_len = 0;
|
|
s->o_buffer_max = sizeof(s->o_buffer) / sizeof(s->o_buffer[0]);
|
|
|
|
s->zstream.zalloc = Z_NULL;
|
|
s->zstream.zfree = Z_NULL;
|
|
|
|
/* prepare source */
|
|
if (lua_isstring(L, src)) {
|
|
lua_pushvalue(L, src);
|
|
s->i_buffer_ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
s->i_buffer = lua_tolstring(L, src, &s->i_buffer_len);
|
|
} else {
|
|
/* table | function | userdata */
|
|
lua_pushvalue(L, src);
|
|
s->io_cb = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static void lzstream_cleanup(lua_State *L, lz_stream *s) {
|
|
if (s && s->state != LZ_NONE) {
|
|
if (s->state == LZ_INFLATE) {
|
|
inflateEnd(&s->zstream);
|
|
}
|
|
if (s->state == LZ_DEFLATE) {
|
|
deflateEnd(&s->zstream);
|
|
}
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, s->io_cb);
|
|
luaL_unref(L, LUA_REGISTRYINDEX, s->i_buffer_ref);
|
|
s->state = LZ_NONE;
|
|
}
|
|
}
|
|
|
|
/* ====================================================================== */
|
|
|
|
static lz_stream *lzstream_get(lua_State *L, int index) {
|
|
lz_stream *s = (lz_stream*)luaL_checkudata(L, index, ZSTREAMMETA);
|
|
if (s == NULL) luaL_argerror(L, index, "bad zlib stream");
|
|
return s;
|
|
}
|
|
|
|
static lz_stream *lzstream_check(lua_State *L, int index, int state) {
|
|
lz_stream *s = lzstream_get(L, index);
|
|
if ((state != LZ_ANY && s->state != state) || s->state == LZ_NONE) {
|
|
luaL_argerror(L, index, "attempt to use invalid zlib stream");
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/* ====================================================================== */
|
|
|
|
static int lzstream_tostring(lua_State *L) {
|
|
lz_stream *s = (lz_stream*)luaL_checkudata(L, 1, ZSTREAMMETA);
|
|
if (s == NULL) luaL_argerror(L, 1, "bad zlib stream");
|
|
|
|
if (s->state == LZ_NONE) {
|
|
lua_pushstring(L, "zlib stream (closed)");
|
|
} else if (s->state == LZ_DEFLATE) {
|
|
lua_pushfstring(L, "zlib deflate stream (%p)", (void*)s);
|
|
} else if (s->state == LZ_INFLATE) {
|
|
lua_pushfstring(L, "zlib inflate stream (%p)", (void*)s);
|
|
} else {
|
|
lua_pushfstring(L, "%p", (void*)s);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* ====================================================================== */
|
|
|
|
static int lzstream_gc(lua_State *L) {
|
|
lz_stream *s = lzstream_get(L, 1);
|
|
lzstream_cleanup(L, s);
|
|
return 0;
|
|
}
|
|
|
|
/* ====================================================================== */
|
|
|
|
static int lzstream_close(lua_State *L) {
|
|
lz_stream *s = lzstream_get(L, 1);
|
|
|
|
if (s->state == LZ_DEFLATE) {
|
|
lua_settop(L, 0);
|
|
lua_pushliteral(L, "");
|
|
return lzstream_docompress(L, s, 1, 1, Z_FINISH);
|
|
}
|
|
|
|
lzstream_cleanup(L, s);
|
|
lua_pushboolean(L, 1);
|
|
return 1;
|
|
}
|
|
|
|
/* ====================================================================== */
|
|
|
|
static int lzstream_adler(lua_State *L) {
|
|
lz_stream *s = lzstream_check(L, 1, LZ_ANY);
|
|
lua_pushnumber(L, s->zstream.adler);
|
|
return 1;
|
|
}
|
|
|
|
/* ====================================================================== */
|
|
|
|
/*
|
|
zlib.deflate(
|
|
sink: function | { write: function [, close: function, flush: function] },
|
|
compression level, [Z_DEFAILT_COMPRESSION]
|
|
method, [Z_DEFLATED]
|
|
windowBits, [15]
|
|
memLevel, [8]
|
|
strategy, [Z_DEFAULT_STRATEGY]
|
|
dictionary: [""]
|
|
)
|
|
*/
|
|
static int lzlib_deflate(lua_State *L) {
|
|
int level, method, windowBits, memLevel, strategy;
|
|
lz_stream *s;
|
|
const char *dictionary;
|
|
size_t dictionary_len;
|
|
|
|
if (lua_istable(L, 1) || lua_isuserdata(L, 1)) {
|
|
/* is there a :write function? */
|
|
lua_getfield(L, 1, "write");
|
|
if (!lua_isfunction(L, -1)) {
|
|
luaL_argerror(L, 1, "output parameter does not provide :write function");
|
|
}
|
|
lua_pop(L, 1);
|
|
}
|
|
else if (!lua_isfunction(L, 1)) {
|
|
luaL_argerror(L, 1, "output parameter must be a function, table or userdata value");
|
|
}
|
|
|
|
level = (int) luaL_optinteger(L, 2, Z_DEFAULT_COMPRESSION);
|
|
method = (int) luaL_optinteger(L, 3, Z_DEFLATED);
|
|
windowBits = (int) luaL_optinteger(L, 4, 15);
|
|
memLevel = (int) luaL_optinteger(L, 5, 8);
|
|
strategy = (int) luaL_optinteger(L, 6, Z_DEFAULT_STRATEGY);
|
|
dictionary = luaL_optlstring(L, 7, NULL, &dictionary_len);
|
|
|
|
s = lzstream_new(L, 1);
|
|
|
|
if (deflateInit2(&s->zstream, level, method, windowBits, memLevel, strategy) != Z_OK) {
|
|
lua_pushliteral(L, "call to deflateInit2 failed");
|
|
lua_error(L);
|
|
}
|
|
|
|
if (dictionary) {
|
|
if (deflateSetDictionary(&s->zstream, (const Bytef *) dictionary, dictionary_len) != Z_OK) {
|
|
lua_pushliteral(L, "call to deflateSetDictionnary failed");
|
|
lua_error(L);
|
|
}
|
|
}
|
|
|
|
s->state = LZ_DEFLATE;
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
zlib.inflate(
|
|
source: string | function | { read: function, close: function },
|
|
windowBits: number, [15]
|
|
dictionary: [""]
|
|
)
|
|
*/
|
|
static int lzlib_inflate(lua_State *L)
|
|
{
|
|
int windowBits;
|
|
lz_stream *s;
|
|
int have_peek = 0;
|
|
const char *dictionary;
|
|
size_t dictionary_len;
|
|
|
|
if (lua_istable(L, 1) || lua_isuserdata(L, 1)) {
|
|
/* is there a :read function? */
|
|
lua_getfield(L, 1, "read");
|
|
if (!lua_isfunction(L, -1)) {
|
|
luaL_argerror(L, 1, "input parameter does not provide :read function");
|
|
}
|
|
lua_pop(L, 1);
|
|
/* check for peek function */
|
|
lua_getfield(L, 1, "peek");
|
|
have_peek = lua_isfunction(L, -1);
|
|
lua_pop(L, 1);
|
|
}
|
|
else if (!lua_isstring(L, 1) && !lua_isfunction(L, 1)) {
|
|
luaL_argerror(L, 1, "input parameter must be a string, function, table or userdata value");
|
|
}
|
|
|
|
windowBits = (int) luaL_optinteger(L, 2, 15);
|
|
dictionary = luaL_optlstring(L, 3, NULL, &dictionary_len);
|
|
|
|
s = lzstream_new(L, 1);
|
|
|
|
if (windowBits > 0 && windowBits < 16) {
|
|
windowBits |= 32;
|
|
}
|
|
|
|
if (inflateInit2(&s->zstream, windowBits) != Z_OK) {
|
|
lua_pushliteral(L, "call to inflateInit2 failed");
|
|
lua_error(L);
|
|
}
|
|
|
|
if (dictionary) {
|
|
s->dictionary = (const Bytef *) dictionary;
|
|
s->dictionary_len = dictionary_len;
|
|
}
|
|
|
|
s->peek = have_peek;
|
|
s->state = LZ_INFLATE;
|
|
return 1;
|
|
}
|
|
|
|
/* ====================================================================== */
|
|
|
|
static int lz_pushresult (lua_State *L, lz_stream *s) {
|
|
if (s->error == Z_OK) {
|
|
lua_pushboolean(L, 1);
|
|
return 1;
|
|
} else {
|
|
lua_pushnil(L);
|
|
lua_pushstring(L, zError(s->error));
|
|
lua_pushinteger(L, s->error);
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Get block to process:
|
|
- top of stack gets
|
|
*/
|
|
static const char* lzstream_fetch_block(lua_State *L, lz_stream *s, int hint) {
|
|
if (s->i_buffer_pos >= s->i_buffer_len) {
|
|
luaL_unref(L, LUA_REGISTRYINDEX, s->i_buffer_ref);
|
|
s->i_buffer_ref = LUA_NOREF;
|
|
s->i_buffer = NULL;
|
|
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, s->io_cb);
|
|
if (!lua_isnil(L, -1)) {
|
|
if (lua_isfunction(L, -1)) {
|
|
lua_pushinteger(L, hint);
|
|
lua_call(L, 1, 1);
|
|
} else {
|
|
lua_getfield(L, -1, (s->peek ? "peek" : "read"));
|
|
lua_insert(L, -2);
|
|
lua_pushinteger(L, hint);
|
|
lua_call(L, 2, 1);
|
|
}
|
|
|
|
if (lua_isstring(L, -1)) {
|
|
s->i_buffer_pos = 0;
|
|
s->i_buffer = lua_tolstring(L, -1, &s->i_buffer_len);
|
|
if (s->i_buffer_len > 0) {
|
|
s->i_buffer_ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
} else {
|
|
lua_pop(L, 1);
|
|
}
|
|
} else if (lua_isnil(L, -1)) {
|
|
lua_pop(L, 1);
|
|
} else {
|
|
lua_pushliteral(L, "deflate callback must return string or nil");
|
|
lua_error(L);
|
|
}
|
|
} else {
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
|
|
return s->i_buffer;
|
|
}
|
|
|
|
static int lzstream_inflate_block(lua_State *L, lz_stream *s) {
|
|
if (lzstream_fetch_block(L, s, LZ_BUFFER_SIZE) || !s->eos) {
|
|
int r;
|
|
|
|
if (s->i_buffer_len == s->i_buffer_pos) {
|
|
s->zstream.next_in = NULL;
|
|
s->zstream.avail_in = 0;
|
|
} else {
|
|
s->zstream.next_in = (unsigned char*)(s->i_buffer + s->i_buffer_pos);
|
|
s->zstream.avail_in = s->i_buffer_len - s->i_buffer_pos;
|
|
}
|
|
|
|
s->zstream.next_out = (unsigned char*)s->o_buffer + s->o_buffer_len;
|
|
s->zstream.avail_out = s->o_buffer_max - s->o_buffer_len;
|
|
|
|
/* munch some more */
|
|
r = inflate(&s->zstream, Z_SYNC_FLUSH);
|
|
|
|
if (r == Z_NEED_DICT) {
|
|
if (s->dictionary == NULL) {
|
|
lua_pushliteral(L, "no inflate dictionary provided");
|
|
lua_error(L);
|
|
}
|
|
|
|
if (inflateSetDictionary(&s->zstream, s->dictionary, s->dictionary_len) != Z_OK) {
|
|
lua_pushliteral(L, "call to inflateSetDictionnary failed");
|
|
lua_error(L);
|
|
}
|
|
|
|
r = inflate(&s->zstream, Z_SYNC_FLUSH);
|
|
}
|
|
|
|
if (r != Z_OK && r != Z_STREAM_END && r != Z_BUF_ERROR) {
|
|
lzstream_cleanup(L, s);
|
|
s->error = r;
|
|
#if 1
|
|
lua_pushfstring(L, "failed to decompress [%d]", r);
|
|
lua_error(L);
|
|
#endif
|
|
}
|
|
|
|
if (r == Z_STREAM_END) {
|
|
luaL_unref(L, LUA_REGISTRYINDEX, s->i_buffer_ref);
|
|
s->i_buffer_ref = LUA_NOREF;
|
|
s->i_buffer = NULL;
|
|
|
|
s->eos = 1;
|
|
}
|
|
|
|
/* number of processed bytes */
|
|
if (s->peek) {
|
|
size_t processed = s->i_buffer_len - s->i_buffer_pos - s->zstream.avail_in;
|
|
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, s->io_cb);
|
|
lua_getfield(L, -1, "read");
|
|
lua_insert(L, -2);
|
|
lua_pushinteger(L, processed);
|
|
lua_call(L, 2, 0);
|
|
}
|
|
|
|
s->i_buffer_pos = s->i_buffer_len - s->zstream.avail_in;
|
|
s->o_buffer_len = s->o_buffer_max - s->zstream.avail_out;
|
|
}
|
|
|
|
return s->o_buffer_len;
|
|
}
|
|
|
|
/*
|
|
** Remove n bytes from the output buffer.
|
|
*/
|
|
static void lzstream_remove(lz_stream *s, size_t n) {
|
|
memmove(s->o_buffer, s->o_buffer + n, s->o_buffer_len - n);
|
|
s->o_buffer_len -= n;
|
|
}
|
|
|
|
/*
|
|
** Copy at most n bytes to buffer b and remove them from the
|
|
** output stream buffer.
|
|
*/
|
|
static int lzstream_flush_buffer(lua_State *L, lz_stream *s, size_t n, luaL_Buffer *b) {
|
|
/* check output */
|
|
if (n > s->o_buffer_len) {
|
|
n = s->o_buffer_len;
|
|
}
|
|
|
|
if (n > 0) {
|
|
lua_pushlstring(L, s->o_buffer, n);
|
|
luaL_addvalue(b);
|
|
|
|
lzstream_remove(s, n);
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
z:read(
|
|
{number | '*l' | '*a'}*
|
|
)
|
|
*/
|
|
static int lz_test_eof(lua_State *L, lz_stream *s) {
|
|
lua_pushlstring(L, NULL, 0);
|
|
if (s->o_buffer_len > 0) {
|
|
return 1;
|
|
} else if (s->eos) {
|
|
return 0;
|
|
} else {
|
|
return lzstream_inflate_block(L, s);
|
|
}
|
|
}
|
|
|
|
static int lz_read_line(lua_State *L, lz_stream *s) {
|
|
luaL_Buffer b;
|
|
size_t l = 0, n;
|
|
|
|
luaL_buffinit(L, &b);
|
|
|
|
if (s->o_buffer_len > 0 || !s->eos) do {
|
|
char *p = s->o_buffer;
|
|
size_t len = s->o_buffer_len;
|
|
|
|
/* find newline in output buffer */
|
|
for (n = 0; n < len; ++n, ++p) {
|
|
if (*p == '\n' || *p == '\r') {
|
|
int eat_nl = *p == '\r';
|
|
luaL_addlstring(&b, s->o_buffer, n);
|
|
lzstream_remove(s, n+1);
|
|
l += n;
|
|
|
|
if (eat_nl && lzstream_inflate_block(L, s)) {
|
|
if (s->o_buffer_len > 0 && *s->o_buffer == '\n') {
|
|
lzstream_remove(s, 1);
|
|
}
|
|
}
|
|
|
|
luaL_pushresult(&b);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (len > 0) {
|
|
luaL_addlstring(&b, s->o_buffer, len);
|
|
lzstream_remove(s, len);
|
|
l += len;
|
|
}
|
|
} while (lzstream_inflate_block(L, s));
|
|
|
|
luaL_pushresult(&b);
|
|
return l > 0 || !s->eos || s->o_buffer_len > 0;
|
|
}
|
|
|
|
|
|
static int lz_read_chars(lua_State *L, lz_stream *s, size_t n) {
|
|
size_t len;
|
|
luaL_Buffer b;
|
|
luaL_buffinit(L, &b);
|
|
|
|
if (s->o_buffer_len > 0 || !s->eos) do {
|
|
size_t rlen = lzstream_flush_buffer(L, s, n, &b);
|
|
n -= rlen;
|
|
} while (n > 0 && lzstream_inflate_block(L, s));
|
|
|
|
luaL_pushresult(&b);
|
|
lua_tolstring(L, -1, &len);
|
|
return n == 0 || len > 0;
|
|
}
|
|
|
|
static int lzstream_decompress(lua_State *L) {
|
|
lz_stream *s = lzstream_check(L, 1, LZ_INFLATE);
|
|
int nargs = lua_gettop(L) - 1;
|
|
int success;
|
|
int n;
|
|
if (nargs == 0) { /* no arguments? */
|
|
success = lz_read_line(L, s);
|
|
n = 3; /* to return 1 result */
|
|
}
|
|
else { /* ensure stack space for all results and for auxlib's buffer */
|
|
luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
|
|
success = 1;
|
|
for (n = 2; nargs-- && success; n++) {
|
|
if (lua_type(L, n) == LUA_TNUMBER) {
|
|
size_t l = (size_t)lua_tointeger(L, n);
|
|
success = (l == 0) ? lz_test_eof(L, s) : lz_read_chars(L, s, l);
|
|
}
|
|
else {
|
|
const char *p = lua_tostring(L, n);
|
|
luaL_argcheck(L, p && p[0] == '*', n, "invalid option");
|
|
switch (p[1]) {
|
|
case 'l': /* line */
|
|
success = lz_read_line(L, s);
|
|
break;
|
|
case 'a': /* file */
|
|
lz_read_chars(L, s, ~((size_t)0)); /* read MAX_SIZE_T chars */
|
|
success = 1; /* always success */
|
|
break;
|
|
default:
|
|
return luaL_argerror(L, n, "invalid format");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (s->error != Z_OK) {
|
|
return lz_pushresult(L, s);
|
|
}
|
|
if (!success) {
|
|
lua_pop(L, 1); /* remove last result */
|
|
lua_pushnil(L); /* push nil instead */
|
|
}
|
|
return n - 2;
|
|
}
|
|
|
|
|
|
static int lzstream_readline(lua_State *L) {
|
|
lz_stream *s;
|
|
int sucess;
|
|
|
|
s = lzstream_check(L, lua_upvalueindex(1), LZ_INFLATE);
|
|
sucess = lz_read_line(L, s);
|
|
|
|
if (s->error != Z_OK) {
|
|
return lz_pushresult(L, s);
|
|
}
|
|
|
|
if (sucess) {
|
|
return 1;
|
|
} else {
|
|
/* EOF */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int lzstream_lines(lua_State *L) {
|
|
lzstream_check(L, 1, LZ_INFLATE);
|
|
lua_settop(L, 1);
|
|
lua_pushcclosure(L, lzstream_readline, 1);
|
|
return 1;
|
|
}
|
|
|
|
/* ====================================================================== */
|
|
|
|
static int lzstream_docompress(lua_State *L, lz_stream *s, int from, int to, int flush) {
|
|
int r, arg;
|
|
int self = 0;
|
|
size_t b_size = s->o_buffer_max;
|
|
unsigned char *b = (unsigned char *)s->o_buffer;
|
|
|
|
/* number of processed bytes */
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, s->io_cb);
|
|
if (!lua_isfunction(L, -1)) {
|
|
self = 1;
|
|
lua_getfield(L, -1, "write");
|
|
}
|
|
|
|
for (arg = from; arg <= to; arg++) {
|
|
s->zstream.next_in = (unsigned char*)luaL_checklstring(L, arg, (size_t*)&s->zstream.avail_in);
|
|
|
|
do {
|
|
s->zstream.next_out = b;
|
|
s->zstream.avail_out = b_size;
|
|
|
|
/* bake some more */
|
|
r = deflate(&s->zstream, flush);
|
|
if (r != Z_OK && r != Z_STREAM_END && r != Z_BUF_ERROR) {
|
|
lzstream_cleanup(L, s);
|
|
lua_pushboolean(L, 0);
|
|
lua_pushfstring(L, "failed to compress [%d]", r);
|
|
return 2;
|
|
}
|
|
|
|
if (s->zstream.avail_out != b_size) {
|
|
/* write output */
|
|
lua_pushvalue(L, -1); /* function */
|
|
if (self) lua_pushvalue(L, -3); /* self */
|
|
lua_pushlstring(L, (char*)b, b_size - s->zstream.avail_out); /* data */
|
|
lua_call(L, (self ? 2 : 1), 0);
|
|
}
|
|
|
|
if (r == Z_STREAM_END) {
|
|
lzstream_cleanup(L, s);
|
|
break;
|
|
}
|
|
|
|
/* process all input */
|
|
} while (s->zstream.avail_in > 0 || s->zstream.avail_out == 0);
|
|
}
|
|
|
|
lua_pushboolean(L, 1);
|
|
return 1;
|
|
}
|
|
|
|
static int lzstream_compress(lua_State *L) {
|
|
lz_stream *s = lzstream_check(L, 1, LZ_DEFLATE);
|
|
return lzstream_docompress(L, s, 2, lua_gettop(L), Z_NO_FLUSH);
|
|
}
|
|
|
|
|
|
/* ====================================================================== */
|
|
|
|
static int lzstream_flush(lua_State *L) {
|
|
static int flush_values[] = { Z_SYNC_FLUSH, Z_FULL_FLUSH, Z_FINISH };
|
|
static const char *const flush_opts[] = { "sync", "full", "finish" };
|
|
|
|
lz_stream *s = lzstream_check(L, 1, LZ_DEFLATE);
|
|
int flush = luaL_checkoption(L, 2, flush_opts[0], flush_opts);
|
|
|
|
lua_settop(L, 0);
|
|
lua_pushliteral(L, "");
|
|
return lzstream_docompress(L, s, 1, 1, flush_values[flush]);
|
|
}
|
|
|
|
/*
|
|
** =========================================================================
|
|
** zlib functions
|
|
** =========================================================================
|
|
*/
|
|
|
|
static int lzlib_version(lua_State *L)
|
|
{
|
|
lua_pushstring(L, zlibVersion());
|
|
return 1;
|
|
}
|
|
|
|
/* ====================================================================== */
|
|
static int lzlib_adler32(lua_State *L)
|
|
{
|
|
if (lua_gettop(L) == 0)
|
|
{
|
|
/* adler32 initial value */
|
|
lua_pushnumber(L, adler32(0L, Z_NULL, 0));
|
|
}
|
|
else
|
|
{
|
|
/* update adler32 checksum */
|
|
size_t len;
|
|
int adler = (int) luaL_checkinteger(L, 1);
|
|
const unsigned char* buf = (unsigned char*)luaL_checklstring(L, 2, &len);
|
|
|
|
lua_pushnumber(L, adler32(adler, buf, len));
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* ====================================================================== */
|
|
static int lzlib_crc32(lua_State *L)
|
|
{
|
|
if (lua_gettop(L) == 0)
|
|
{
|
|
/* crc32 initial value */
|
|
lua_pushnumber(L, crc32(0L, Z_NULL, 0));
|
|
}
|
|
else
|
|
{
|
|
/* update crc32 checksum */
|
|
size_t len;
|
|
int crc = (int) luaL_checkinteger(L, 1);
|
|
const unsigned char* buf = (unsigned char*)luaL_checklstring(L, 2, &len);
|
|
|
|
lua_pushnumber(L, crc32(crc, buf, len));
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* ====================================================================== */
|
|
|
|
|
|
static int lzlib_compress(lua_State *L) {
|
|
size_t avail_in;
|
|
const char *next_in = luaL_checklstring(L, 1, &avail_in);
|
|
int level = (int) luaL_optinteger(L, 2, Z_DEFAULT_COMPRESSION);
|
|
int method = (int) luaL_optinteger(L, 3, Z_DEFLATED);
|
|
int windowBits = (int) luaL_optinteger(L, 4, 15);
|
|
int memLevel = (int) luaL_optinteger(L, 5, 8);
|
|
int strategy = (int) luaL_optinteger(L, 6, Z_DEFAULT_STRATEGY);
|
|
|
|
int ret;
|
|
luaL_Buffer b;
|
|
z_stream zs;
|
|
|
|
luaL_buffinit(L, &b);
|
|
|
|
zs.zalloc = Z_NULL;
|
|
zs.zfree = Z_NULL;
|
|
|
|
zs.next_out = Z_NULL;
|
|
zs.avail_out = 0;
|
|
zs.next_in = Z_NULL;
|
|
zs.avail_in = 0;
|
|
|
|
ret = deflateInit2(&zs, level, method, windowBits, memLevel, strategy);
|
|
|
|
if (ret != Z_OK)
|
|
{
|
|
lua_pushnil(L);
|
|
lua_pushnumber(L, ret);
|
|
return 2;
|
|
}
|
|
|
|
zs.next_in = (unsigned char*)next_in;
|
|
zs.avail_in = avail_in;
|
|
|
|
for(;;)
|
|
{
|
|
zs.next_out = (unsigned char*)luaL_prepbuffer(&b);
|
|
zs.avail_out = LUAL_BUFFERSIZE;
|
|
|
|
/* munch some more */
|
|
ret = deflate(&zs, Z_FINISH);
|
|
|
|
/* push gathered data */
|
|
luaL_addsize(&b, LUAL_BUFFERSIZE - zs.avail_out);
|
|
|
|
/* done processing? */
|
|
if (ret == Z_STREAM_END)
|
|
break;
|
|
|
|
/* error condition? */
|
|
if (ret != Z_OK)
|
|
break;
|
|
}
|
|
|
|
/* cleanup */
|
|
deflateEnd(&zs);
|
|
|
|
luaL_pushresult(&b);
|
|
lua_pushnumber(L, ret);
|
|
return 2;
|
|
}
|
|
|
|
/* ====================================================================== */
|
|
|
|
static int lzlib_decompress(lua_State *L)
|
|
{
|
|
size_t avail_in;
|
|
const char *next_in = luaL_checklstring(L, 1, &avail_in);
|
|
int windowBits = (int) luaL_optinteger(L, 2, 15);
|
|
|
|
int ret;
|
|
luaL_Buffer b;
|
|
z_stream zs;
|
|
|
|
luaL_buffinit(L, &b);
|
|
|
|
zs.zalloc = Z_NULL;
|
|
zs.zfree = Z_NULL;
|
|
|
|
zs.next_out = Z_NULL;
|
|
zs.avail_out = 0;
|
|
zs.next_in = Z_NULL;
|
|
zs.avail_in = 0;
|
|
|
|
ret = inflateInit2(&zs, windowBits);
|
|
|
|
if (ret != Z_OK) {
|
|
lua_pushliteral(L, "failed to initialize zstream structures");
|
|
lua_error(L);
|
|
}
|
|
|
|
zs.next_in = (unsigned char*)next_in;
|
|
zs.avail_in = avail_in;
|
|
|
|
for (;;) {
|
|
zs.next_out = (unsigned char*)luaL_prepbuffer(&b);
|
|
zs.avail_out = LUAL_BUFFERSIZE;
|
|
|
|
/* bake some more */
|
|
ret = inflate(&zs, Z_FINISH);
|
|
|
|
/* push gathered data */
|
|
luaL_addsize(&b, LUAL_BUFFERSIZE - zs.avail_out);
|
|
|
|
/* done processing? */
|
|
if (ret == Z_STREAM_END)
|
|
break;
|
|
|
|
if (ret != Z_OK && ret != Z_BUF_ERROR) {
|
|
/* cleanup */
|
|
inflateEnd(&zs);
|
|
|
|
lua_pushliteral(L, "failed to process zlib stream");
|
|
lua_error(L);
|
|
}
|
|
}
|
|
|
|
/* cleanup */
|
|
inflateEnd(&zs);
|
|
|
|
luaL_pushresult(&b);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
** =========================================================================
|
|
** Register functions
|
|
** =========================================================================
|
|
*/
|
|
|
|
#if (LUA_VERSION_NUM >= 502)
|
|
|
|
#define luaL_register(L,n,f) luaL_setfuncs(L,f,0)
|
|
|
|
#endif
|
|
|
|
LUALIB_API int luaopen_zlib(lua_State *L)
|
|
{
|
|
const luaL_Reg lzstream_meta[] =
|
|
{
|
|
{"write", lzstream_compress },
|
|
{"read", lzstream_decompress },
|
|
{"lines", lzstream_lines },
|
|
{"flush", lzstream_flush },
|
|
{"close", lzstream_close },
|
|
|
|
{"adler", lzstream_adler },
|
|
|
|
{"__tostring", lzstream_tostring },
|
|
{"__gc", lzstream_gc },
|
|
{NULL, NULL}
|
|
};
|
|
|
|
const luaL_Reg zlib[] =
|
|
{
|
|
{"version", lzlib_version },
|
|
{"adler32", lzlib_adler32 },
|
|
{"crc32", lzlib_crc32 },
|
|
|
|
{"deflate", lzlib_deflate },
|
|
{"inflate", lzlib_inflate },
|
|
|
|
{"compress", lzlib_compress },
|
|
{"decompress", lzlib_decompress },
|
|
|
|
{NULL, NULL}
|
|
};
|
|
|
|
/* ====================================================================== */
|
|
|
|
/* create new metatable for zlib compression structures */
|
|
luaL_newmetatable(L, ZSTREAMMETA);
|
|
lua_pushliteral(L, "__index");
|
|
lua_pushvalue(L, -2); /* push metatable */
|
|
lua_rawset(L, -3); /* metatable.__index = metatable */
|
|
|
|
/*
|
|
** Stack: metatable
|
|
*/
|
|
luaL_register(L, NULL, lzstream_meta);
|
|
|
|
lua_pop(L, 1); /* remove metatable from stack */
|
|
|
|
/*
|
|
** Stack:
|
|
*/
|
|
lua_newtable(L);
|
|
|
|
lua_pushliteral (L, "_COPYRIGHT");
|
|
lua_pushliteral (L, "Copyright (C) 2003-2010 Tiago Dionizio");
|
|
lua_settable (L, -3);
|
|
lua_pushliteral (L, "_DESCRIPTION");
|
|
lua_pushliteral (L, "Lua 5 interface to access zlib library functions");
|
|
lua_settable (L, -3);
|
|
lua_pushliteral (L, "_VERSION");
|
|
lua_pushliteral (L, "lzlib 0.4-work3");
|
|
lua_settable (L, -3);
|
|
|
|
#define PUSH_LITERAL(name) \
|
|
lua_pushliteral (L, #name); \
|
|
lua_pushinteger (L, Z_##name); \
|
|
lua_settable (L, -3);
|
|
|
|
#define PUSH_NUMBER(name, value) \
|
|
lua_pushliteral (L, #name); \
|
|
lua_pushinteger (L, value); \
|
|
lua_settable (L, -3);
|
|
|
|
PUSH_LITERAL(NO_COMPRESSION)
|
|
PUSH_LITERAL(BEST_SPEED)
|
|
PUSH_LITERAL(BEST_COMPRESSION)
|
|
PUSH_LITERAL(DEFAULT_COMPRESSION)
|
|
|
|
PUSH_LITERAL(FILTERED)
|
|
PUSH_LITERAL(HUFFMAN_ONLY)
|
|
PUSH_LITERAL(RLE)
|
|
PUSH_LITERAL(FIXED)
|
|
PUSH_LITERAL(DEFAULT_STRATEGY)
|
|
|
|
PUSH_NUMBER(MINIMUM_MEMLEVEL, 1)
|
|
PUSH_NUMBER(MAXIMUM_MEMLEVEL, 9)
|
|
PUSH_NUMBER(DEFAULT_MEMLEVEL, 8)
|
|
|
|
PUSH_NUMBER(DEFAULT_WINDOWBITS, 15)
|
|
PUSH_NUMBER(MINIMUM_WINDOWBITS, 8)
|
|
PUSH_NUMBER(MAXIMUM_WINDOWBITS, 15)
|
|
|
|
PUSH_NUMBER(GZIP_WINDOWBITS, 16)
|
|
PUSH_NUMBER(RAW_WINDOWBITS, -1)
|
|
|
|
luaL_register(L, NULL, zlib);
|
|
|
|
/*
|
|
** Stack: zlib table
|
|
*/
|
|
return 1;
|
|
}
|