Squashed 'src/deps/src/lua-ffi-zlib/' content from commit 1fb69ca50

git-subtree-dir: src/deps/src/lua-ffi-zlib
git-subtree-split: 1fb69ca505444097c82d2b72e87904f3ed923ae9
This commit is contained in:
Théophile Diot 2023-06-30 15:38:00 -04:00
commit c46cd666ab
7 changed files with 678 additions and 0 deletions

21
LICENSE.txt Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 Hamish Forbes
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.

133
README.md Normal file
View File

@ -0,0 +1,133 @@
# lua-ffi-zlib
A [Lua](http://www.lua.org) module using LuaJIT's [FFI](http://luajit.org/ext_ffi.html) feature to access zlib.
Intended primarily for use within [OpenResty](http://openresty.org) to allow manipulation of gzip encoded HTTP responses.
# Methods
Basic methods allowing for simple compression or decompression of gzip data
## inflateGzip
`Syntax: ok, err = inflateGzip(input, output, chunk?, windowBits?)`
* `input` should be a function that accepts a chunksize as its only argument and return that many bytes of the gzip stream
* `output` will receive a string of decompressed data as its only argument, do with it as you will!
* `chunk` is the size of the input and output buffers, optional and defaults to 16KB
* `windowBits` is passed to `inflateInit2()`, should be left as default for most cases.
See [zlib manual](http://zlib.net/manual.html) for details
On error returns `false` and the error message, otherwise `true` and the last status message
## deflateGzip
`Syntax: ok, err = deflateGzip(input, output, chunk?, options?)`
* `input` should be a function that accepts a chunksize as its only argument and return that many bytes of uncompressed data.
* `output` will receive a string of compressed data as its only argument, do with it as you will!
* `chunk` is the size of the input and output buffers, optional and defaults to 16KB
* `options` is a table of options to pass to `deflateInit2()`
Valid options are level, memLevel, strategy and windowBits, see [zlib manual](http://zlib.net/manual.html) for details
On error returns `false` and the error message, otherwise `true` and the last status message
# Example
Reads a file and output the decompressed version.
Roughly equivalent to running `gzip -dc file.gz > out_file | tee`
```lua
local table_insert = table.insert
local table_concat = table.concat
local zlib = require('lib.ffi-zlib')
local f = io.open(arg[1], "rb")
local out_f = io.open(arg[2], "w")
local input = function(bufsize)
-- Read the next chunk
local d = f:read(bufsize)
if d == nil then
return nil
end
return d
end
local output_table = {}
local output = function(data)
table_insert(output_table, data)
local ok, err = out_f:write(data)
if not ok then
-- abort decompression when error occurs
return nil, err
end
end
-- Decompress the data
local ok, err = zlib.inflateGzip(input, output)
if not ok then
print(err)
return
end
local decompressed = table_concat(output_table,'')
print(decompressed)
```
# Advanced Usage
Several other methods are available for advanced usage.
Some of these map directly to functions in the zlib library itself, see the [manual](http://zlib.net/manual.html) for full details.
Others are lower level utility functions.
## createStream
`Synax: stream, inbuf, outbuf = createStream(bufsize)`
Returns a z_stream struct, input buffer and output buffer of length `bufsize`
## initInflate
`Syntax: ok = initInflate(stream, windowBits?)`
Calls zlib's inflateInit2 with given stream, defaults to automatic header detection.
## initDeflate
`Syntax: ok = initDeflate(stream, options?)`
Calls zlib's deflateInit2 with the given stream.
`options` is an optional table that can set level, memLevel, strategy and windowBits
## deflate
`Syntax: ok, err = deflate(input, output, bufsize, stream, inbuf, outbuf)`
* `input` is a function that takes a chunk size argument and returns at most that many input bytes
* `output` is a function that takes a string argument of output data
* `bufsize` is the length of the output buffer
* `inbuf` cdata input buffer
* `outpuf` ccdata output buffer
This function will loop until all input data is consumed (`input` returns nil) or an error occurs.
It will then clean up the stream and return an error code
## inflate
`Syntax: ok, err = inflate(input, output, bufsize, stream, inbuf, outbuf)`
* `input` is a function that takes a chunk size argument and returns at most that many input bytes
* `output` is a function that takes a string argument of output data
* `bufsize` is the length of the output buffer
* `inbuf` cdata input buffer
* `outpuf` ccdata output buffer
This function will loop until all input data is consumed (`input` returns nil) or an error occurs.
It will then clean up the stream and return an error code
## adler
`Syntax: chksum = adler(str, chksum?)`
Computes an adler32 checksum for a string, updates an existing checksum if provided
## crc
`Syntax: chksum = crc(str, chksum?)`
Computes an crc32 checksum for a string, updates an existing checksum if provided
## zlib_err
`Syntax: err = zlib_err(code)`
Returns the string representation of a zlib error code

9
dist.ini Normal file
View File

@ -0,0 +1,9 @@
name=lua-ffi-zlib
abstract=Luajit FFI binding for zlib
author=Hamish Forbes
is_original=yes
license=mit
lib_dir=lib
repo_link=https://github.com/hamishforbes/lua-ffi-zlib
main_module=lib/ffi-zlib.lua
requires = luajit

330
lib/ffi-zlib.lua Normal file
View File

@ -0,0 +1,330 @@
local ffi = require "ffi"
local ffi_new = ffi.new
local ffi_str = ffi.string
local ffi_sizeof = ffi.sizeof
local ffi_copy = ffi.copy
local tonumber = tonumber
local _M = {
_VERSION = '0.5.0',
}
local mt = { __index = _M }
ffi.cdef([[
enum {
Z_NO_FLUSH = 0,
Z_PARTIAL_FLUSH = 1,
Z_SYNC_FLUSH = 2,
Z_FULL_FLUSH = 3,
Z_FINISH = 4,
Z_BLOCK = 5,
Z_TREES = 6,
/* Allowed flush values; see deflate() and inflate() below for details */
Z_OK = 0,
Z_STREAM_END = 1,
Z_NEED_DICT = 2,
Z_ERRNO = -1,
Z_STREAM_ERROR = -2,
Z_DATA_ERROR = -3,
Z_MEM_ERROR = -4,
Z_BUF_ERROR = -5,
Z_VERSION_ERROR = -6,
/* Return codes for the compression/decompression functions. Negative values
* are errors, positive values are used for special but normal events.
*/
Z_NO_COMPRESSION = 0,
Z_BEST_SPEED = 1,
Z_BEST_COMPRESSION = 9,
Z_DEFAULT_COMPRESSION = -1,
/* compression levels */
Z_FILTERED = 1,
Z_HUFFMAN_ONLY = 2,
Z_RLE = 3,
Z_FIXED = 4,
Z_DEFAULT_STRATEGY = 0,
/* compression strategy; see deflateInit2() below for details */
Z_BINARY = 0,
Z_TEXT = 1,
Z_ASCII = Z_TEXT, /* for compatibility with 1.2.2 and earlier */
Z_UNKNOWN = 2,
/* Possible values of the data_type field (though see inflate()) */
Z_DEFLATED = 8,
/* The deflate compression method (the only one supported in this version) */
Z_NULL = 0, /* for initializing zalloc, zfree, opaque */
};
typedef void* (* z_alloc_func)( void* opaque, unsigned items, unsigned size );
typedef void (* z_free_func) ( void* opaque, void* address );
typedef struct z_stream_s {
char* next_in;
unsigned avail_in;
unsigned long total_in;
char* next_out;
unsigned avail_out;
unsigned long total_out;
char* msg;
void* state;
z_alloc_func zalloc;
z_free_func zfree;
void* opaque;
int data_type;
unsigned long adler;
unsigned long reserved;
} z_stream;
const char* zlibVersion();
const char* zError(int);
int inflate(z_stream*, int flush);
int inflateEnd(z_stream*);
int inflateInit2_(z_stream*, int windowBits, const char* version, int stream_size);
int deflate(z_stream*, int flush);
int deflateEnd(z_stream* );
int deflateInit2_(z_stream*, int level, int method, int windowBits, int memLevel,int strategy, const char *version, int stream_size);
unsigned long adler32(unsigned long adler, const char *buf, unsigned len);
unsigned long crc32(unsigned long crc, const char *buf, unsigned len);
unsigned long adler32_combine(unsigned long, unsigned long, long);
unsigned long crc32_combine(unsigned long, unsigned long, long);
]])
local zlib = ffi.load(ffi.os == "Windows" and "zlib1" or "z")
_M.zlib = zlib
-- Default to 16k output buffer
local DEFAULT_CHUNK = 16384
local Z_OK = zlib.Z_OK
local Z_NO_FLUSH = zlib.Z_NO_FLUSH
local Z_STREAM_END = zlib.Z_STREAM_END
local Z_FINISH = zlib.Z_FINISH
local Z_NEED_DICT = zlib.Z_NEED_DICT
local Z_BUF_ERROR = zlib.Z_BUF_ERROR
local Z_STREAM_ERROR = zlib.Z_STREAM_ERROR
local function zlib_err(err)
return ffi_str(zlib.zError(err))
end
_M.zlib_err = zlib_err
local function createStream(bufsize)
-- Setup Stream
local stream = ffi_new("z_stream")
-- Create input buffer var
local inbuf = ffi_new('char[?]', bufsize+1)
stream.next_in, stream.avail_in = inbuf, 0
-- create the output buffer
local outbuf = ffi_new('char[?]', bufsize)
stream.next_out, stream.avail_out = outbuf, 0
return stream, inbuf, outbuf
end
_M.createStream = createStream
local function initInflate(stream, windowBits)
-- Setup inflate process
local windowBits = windowBits or (15 + 32) -- +32 sets automatic header detection
local version = ffi_str(zlib.zlibVersion())
return zlib.inflateInit2_(stream, windowBits, version, ffi_sizeof(stream))
end
_M.initInflate = initInflate
local function initDeflate(stream, options)
-- Setup deflate process
local method = zlib.Z_DEFLATED
local level = options.level or zlib.Z_DEFAULT_COMPRESSION
local memLevel = options.memLevel or 8
local strategy = options.strategy or zlib.Z_DEFAULT_STRATEGY
local windowBits = options.windowBits or (15 + 16) -- +16 sets gzip wrapper not zlib
local version = ffi_str(zlib.zlibVersion())
return zlib.deflateInit2_(stream, level, method, windowBits, memLevel, strategy, version, ffi_sizeof(stream))
end
_M.initDeflate = initDeflate
local function flushOutput(stream, bufsize, output, outbuf)
-- Calculate available output bytes
local out_sz = bufsize - stream.avail_out
if out_sz == 0 then
return
end
-- Read bytes from output buffer and pass to output function
local ok, err = output(ffi_str(outbuf, out_sz))
if not ok then
return err
end
end
local function inflate(input, output, bufsize, stream, inbuf, outbuf)
local zlib_flate = zlib.inflate
local zlib_flateEnd = zlib.inflateEnd
-- Inflate a stream
local err = 0
repeat
-- Read some input
local data = input(bufsize)
if data ~= nil then
ffi_copy(inbuf, data)
stream.next_in, stream.avail_in = inbuf, #data
else
-- no more input data
stream.avail_in = 0
end
if stream.avail_in == 0 then
-- When decompressing we *must* have input bytes
zlib_flateEnd(stream)
return false, "INFLATE: Data error, no input bytes"
end
-- While the output buffer is being filled completely just keep going
repeat
stream.next_out = outbuf
stream.avail_out = bufsize
-- Process the stream, always Z_NO_FLUSH in inflate mode
err = zlib_flate(stream, Z_NO_FLUSH)
-- Buffer errors are OK here
if err == Z_BUF_ERROR then
err = Z_OK
end
if err < Z_OK or err == Z_NEED_DICT then
-- Error, clean up and return
zlib_flateEnd(stream)
return false, "INFLATE: "..zlib_err(err), stream
end
-- Write the data out
local err = flushOutput(stream, bufsize, output, outbuf)
if err then
zlib_flateEnd(stream)
return false, "INFLATE: "..err
end
until stream.avail_out ~= 0
until err == Z_STREAM_END
-- Stream finished, clean up and return
zlib_flateEnd(stream)
return true, zlib_err(err)
end
_M.inflate = inflate
local function deflate(input, output, bufsize, stream, inbuf, outbuf)
local zlib_flate = zlib.deflate
local zlib_flateEnd = zlib.deflateEnd
-- Deflate a stream
local err = 0
local mode = Z_NO_FLUSH
repeat
-- Read some input
local data = input(bufsize)
if data ~= nil then
ffi_copy(inbuf, data)
stream.next_in, stream.avail_in = inbuf, #data
else
-- EOF, try and finish up
mode = Z_FINISH
stream.avail_in = 0
end
-- While the output buffer is being filled completely just keep going
repeat
stream.next_out = outbuf
stream.avail_out = bufsize
-- Process the stream
err = zlib_flate(stream, mode)
-- Only possible *bad* return value here
if err == Z_STREAM_ERROR then
-- Error, clean up and return
zlib_flateEnd(stream)
return false, "DEFLATE: "..zlib_err(err), stream
end
-- Write the data out
local err = flushOutput(stream, bufsize, output, outbuf)
if err then
zlib_flateEnd(stream)
return false, "DEFLATE: "..err
end
until stream.avail_out ~= 0
-- In deflate mode all input must be used by this point
if stream.avail_in ~= 0 then
zlib_flateEnd(stream)
return false, "DEFLATE: Input not used"
end
until err == Z_STREAM_END
-- Stream finished, clean up and return
zlib_flateEnd(stream)
return true, zlib_err(err)
end
_M.deflate = deflate
local function adler(str, chksum)
local chksum = chksum or 0
local str = str or ""
return zlib.adler32(chksum, str, #str)
end
_M.adler = adler
local function crc(str, chksum)
local chksum = chksum or 0
local str = str or ""
return zlib.crc32(chksum, str, #str)
end
_M.crc = crc
function _M.inflateGzip(input, output, bufsize, windowBits)
local bufsize = bufsize or DEFAULT_CHUNK
-- Takes 2 functions that provide input data from a gzip stream and receives output data
-- Returns uncompressed string
local stream, inbuf, outbuf = createStream(bufsize)
local init = initInflate(stream, windowBits)
if init == Z_OK then
return inflate(input, output, bufsize, stream, inbuf, outbuf)
else
-- Init error
zlib.inflateEnd(stream)
return false, "INIT: "..zlib_err(init)
end
end
function _M.deflateGzip(input, output, bufsize, options)
local bufsize = bufsize or DEFAULT_CHUNK
options = options or {}
-- Takes 2 functions that provide plain input data and receives output data
-- Returns gzip compressed string
local stream, inbuf, outbuf = createStream(bufsize)
local init = initDeflate(stream, options)
if init == Z_OK then
return deflate(input, output, bufsize, stream, inbuf, outbuf)
else
-- Init error
zlib.deflateEnd(stream)
return false, "INIT: "..zlib_err(init)
end
end
function _M.version()
return ffi_str(zlib.zlibVersion())
end
return _M

View File

@ -0,0 +1,20 @@
package = "lua-ffi-zlib"
version = "0.4-0"
source = {
url = "git://github.com/hamishforbes/lua-ffi-zlib",
tag = "v0.4"
}
description = {
summary = "A Lua module using LuaJIT's FFI feature to access zlib.",
homepage = "https://github.com/hamishforbes/lua-ffi-zlib",
maintainer = "Hamish Forbes"
}
dependencies = {
"lua >= 5.1",
}
build = {
type = "builtin",
modules = {
["ffi-zlib"] = "lib/ffi-zlib.lua",
}
}

View File

@ -0,0 +1,20 @@
package = "lua-ffi-zlib"
version = "0.5-0"
source = {
url = "git://github.com/hamishforbes/lua-ffi-zlib",
tag = "v0.5"
}
description = {
summary = "A Lua module using LuaJIT's FFI feature to access zlib.",
homepage = "https://github.com/hamishforbes/lua-ffi-zlib",
maintainer = "Hamish Forbes"
}
dependencies = {
"lua >= 5.1",
}
build = {
type = "builtin",
modules = {
["ffi-zlib"] = "lib/ffi-zlib.lua",
}
}

145
test.lua Normal file
View File

@ -0,0 +1,145 @@
local table_insert = table.insert
local table_concat = table.concat
local zlib = require('lib.ffi-zlib')
local chunk = tonumber(arg[2]) or 16384
local uncompressed = ''
local input
local f
local passing = true
local in_adler
local out_adler
local in_crc
local out_crc
if arg[1] == nil then
print("No file provided")
return
else
f = io.open(arg[1], "rb")
input = function(bufsize)
local d = f:read(bufsize)
if d == nil then
return nil
end
in_crc = zlib.crc(d, in_crc)
in_adler = zlib.adler(d, in_adler)
uncompressed = uncompressed..d
return d
end
end
print('zlib version: '..zlib.version())
print()
local output_table = {}
local output = function(data)
out_crc = zlib.crc(data, out_crc)
out_adler = zlib.adler(data, out_adler)
table_insert(output_table, data)
end
-- Compress the data
print('Compressing')
local ok, err = zlib.deflateGzip(input, output, chunk)
if not ok then
-- Err message
print(err)
end
local compressed = table_concat(output_table,'')
local orig_in_crc = in_crc
local orig_in_adler = in_adler
print('Input crc32: ', in_crc)
print('Output crc32: ', out_crc)
print('Input adler32: ', in_adler)
print('Output adler32: ', out_adler)
-- Decompress it again
print()
print('Decompressing')
-- Reset vars
in_adler = nil
out_adler = nil
in_crc = nil
out_crc = nil
output_table = {}
local count = 0
local input = function(bufsize)
local start = count > 0 and bufsize*count or 1
local finish = (bufsize*(count+1)-1)
count = count + 1
if bufsize == 1 then
start = count
finish = count
end
local data = compressed:sub(start, finish)
in_crc = zlib.crc(data, in_crc)
in_adler = zlib.adler(data, in_adler)
return data
end
local ok, err = zlib.inflateGzip(input, output, chunk)
if not ok then
-- Err message
print(err)
end
local output_data = table_concat(output_table,'')
print('Input crc32: ', in_crc)
print('Output crc32: ', out_crc)
print('Input adler32: ', in_adler)
print('Output adler32: ', out_adler)
print()
if output_data ~= uncompressed then
passing = false
print("inflateGzip / deflateGzip failed")
end
if orig_in_adler ~= out_adler then
passing = false
print("Adler checksum failed")
end
if orig_in_crc ~= out_crc then
passing = false
print("CRC checksum failed")
end
local bad_output = function(data)
return nil, "bad output"
end
if not passing then
print(":(")
else
print(":)")
end
local dump_input = function(bufsize)
return compressed
end
local ok, err = zlib.deflateGzip(dump_input, bad_output, chunk)
if not ok then
if err ~= "DEFLATE: bad output" then
print(err)
else
print("abort deflation: ok")
end
end
local ok, err = zlib.inflateGzip(dump_input, bad_output, chunk)
if not ok then
if err ~= "INFLATE: bad output" then
print(err)
else
print("abort inflation: ok")
end
end