mirror of
				https://github.com/ctruLua/ctruLua.git
				synced 2025-10-28 16:59:30 +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;
 | |
| }
 | 
