mirror of
https://codeberg.org/1414codeforge/ubgpsuite.git
synced 2025-06-05 21:29:11 +02:00
[*] Initial commit
This commit is contained in:
387
lonetix/cpr/flate.c
Executable file
387
lonetix/cpr/flate.c
Executable file
@@ -0,0 +1,387 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file cpr/flate.c
|
||||
*
|
||||
* Interfaces with `zlib` and implements INFLATE/DEFLATE.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "cpr/flate.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <zlib.h>
|
||||
|
||||
typedef struct ZlibStmObj ZlibStmObj;
|
||||
struct ZlibStmObj {
|
||||
z_stream zs;
|
||||
void *streamp;
|
||||
const StmOps *ops;
|
||||
unsigned bufsiz;
|
||||
Boolean8 deflating;
|
||||
Uint8 buf[FLEX_ARRAY]; // `bufsiz` bytes
|
||||
};
|
||||
|
||||
#define ZSTM_BUFSIZ (32 * 1024)
|
||||
|
||||
#define ZSTM_EIO -1LL
|
||||
#define ZSTM_EBADSTREAM -2LL
|
||||
|
||||
static Sint64 Zlib_FlushData(ZlibStmHn hn)
|
||||
{
|
||||
size_t nbytes = hn->bufsiz - hn->zs.avail_out;
|
||||
|
||||
Sint64 n = hn->ops->Write(hn->streamp, hn->buf, nbytes);
|
||||
if (n == -1)
|
||||
return -1;
|
||||
|
||||
size_t left = nbytes - n;
|
||||
memmove(hn->buf, hn->buf + n, left);
|
||||
|
||||
hn->zs.next_out = hn->buf + left;
|
||||
hn->zs.avail_out = hn->bufsiz - left;
|
||||
return n;
|
||||
}
|
||||
|
||||
static Sint64 Zlib_StmRead(void *streamp, void *buf, size_t nbytes)
|
||||
{
|
||||
return Zlib_Read((ZlibStmHn) streamp, buf, nbytes);
|
||||
}
|
||||
|
||||
static Sint64 Zlib_StmWrite(void *streamp, const void *buf, size_t nbytes)
|
||||
{
|
||||
return Zlib_Write((ZlibStmHn) streamp, buf, nbytes);
|
||||
}
|
||||
|
||||
static Sint64 Zlib_StmTell(void *streamp)
|
||||
{
|
||||
ZlibStmHn hn = (ZlibStmHn) streamp;
|
||||
return hn->deflating ? hn->zs.total_out : hn->zs.total_in;
|
||||
}
|
||||
|
||||
static Judgement Zlib_StmFinish(void *streamp)
|
||||
{
|
||||
return Zlib_Finish((ZlibStmHn) streamp);
|
||||
}
|
||||
|
||||
static void Zlib_StmClose(void *streamp)
|
||||
{
|
||||
Zlib_Close((ZlibStmHn) streamp);
|
||||
}
|
||||
|
||||
static const StmOps zlib_stmOps = {
|
||||
Zlib_StmRead,
|
||||
Zlib_StmWrite,
|
||||
NULL,
|
||||
Zlib_StmTell,
|
||||
Zlib_StmFinish,
|
||||
Zlib_StmClose
|
||||
};
|
||||
static const StmOps zlib_ncStmOps = {
|
||||
Zlib_StmRead,
|
||||
Zlib_StmWrite,
|
||||
NULL,
|
||||
Zlib_StmTell,
|
||||
Zlib_StmFinish,
|
||||
NULL
|
||||
};
|
||||
|
||||
const StmOps *const Zlib_StmOps = &zlib_stmOps;
|
||||
const StmOps *const Zlib_NcStmOps = &zlib_ncStmOps;
|
||||
|
||||
static THREAD_LOCAL ZlibRet zlib_errStat = 0;
|
||||
|
||||
static void Zlib_SetErrStat(ZlibRet ret)
|
||||
{
|
||||
if (ret == Z_ERRNO) {
|
||||
Sint64 err = errno;
|
||||
|
||||
ret |= (Sint32) (err << 32);
|
||||
}
|
||||
zlib_errStat = ret;
|
||||
}
|
||||
|
||||
ZlibRet Zlib_GetErrStat(void)
|
||||
{
|
||||
return zlib_errStat;
|
||||
}
|
||||
|
||||
const char *Zlib_ErrorString(ZlibRet ret)
|
||||
{
|
||||
if (ret == ZSTM_EIO)
|
||||
return "I/O error";
|
||||
if (ret == ZSTM_EBADSTREAM)
|
||||
return "Bad stream operation";
|
||||
|
||||
Sint32 zerrno = (Sint32) (ret & 0xffffffffu);
|
||||
Sint32 err = (Sint32) (ret >> 32);
|
||||
|
||||
switch (zerrno) {
|
||||
case Z_OK: return "Success";
|
||||
case Z_ERRNO: return strerror(err);
|
||||
case Z_STREAM_ERROR: return "Stream error";
|
||||
case Z_DATA_ERROR: return "Data error";
|
||||
case Z_MEM_ERROR: return "Memory allocation failure";
|
||||
case Z_BUF_ERROR: return "Buffer error";
|
||||
case Z_VERSION_ERROR: return "Zlib version error";
|
||||
default: return "Unknown Zlib error";
|
||||
}
|
||||
}
|
||||
|
||||
ZlibStmHn Zlib_InflateOpen(void *streamp,
|
||||
const StmOps *ops,
|
||||
const InflateOpts *opts)
|
||||
{
|
||||
const InflateOpts default_opts = {
|
||||
15,
|
||||
ZFMT_RFC1952,
|
||||
ZSTM_BUFSIZ
|
||||
};
|
||||
|
||||
if (!ops->Write) {
|
||||
Zlib_SetErrStat(ZSTM_EBADSTREAM);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!opts)
|
||||
opts = &default_opts;
|
||||
|
||||
int wbits = CLAMP(opts->win_bits, 8, 15);
|
||||
|
||||
// Mangle window bits according to the required RFC
|
||||
switch (opts->format) {
|
||||
case ZFMT_RFC1951:
|
||||
wbits = -wbits;
|
||||
break;
|
||||
case ZFMT_RFC1950:
|
||||
break;
|
||||
|
||||
case ZFMT_RFC1952:
|
||||
default:
|
||||
wbits += 16;
|
||||
break;
|
||||
}
|
||||
|
||||
size_t bufsiz = MIN(opts->bufsiz, INT_MAX); // safety to avoid short reads
|
||||
if (bufsiz == 0)
|
||||
bufsiz = ZSTM_BUFSIZ;
|
||||
|
||||
ZlibStmObj *hn = (ZlibStmObj *) malloc(offsetof(ZlibStmObj, buf[bufsiz]));
|
||||
if (!hn) {
|
||||
Zlib_SetErrStat(Z_MEM_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&hn->zs, 0, sizeof(hn->zs));
|
||||
|
||||
int err = inflateInit2(&hn->zs, wbits);
|
||||
if (err != Z_OK) {
|
||||
Zlib_SetErrStat(err);
|
||||
free(hn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hn->streamp = streamp;
|
||||
hn->ops = ops;
|
||||
hn->bufsiz = bufsiz;
|
||||
hn->deflating = FALSE;
|
||||
Zlib_SetErrStat(Z_OK);
|
||||
return hn;
|
||||
}
|
||||
|
||||
ZlibStmHn Zlib_DeflateOpen(void *streamp,
|
||||
const StmOps *ops,
|
||||
const DeflateOpts *opts)
|
||||
{
|
||||
const DeflateOpts default_opts = {
|
||||
Z_DEFAULT_COMPRESSION,
|
||||
15,
|
||||
ZFMT_RFC1952,
|
||||
ZSTM_BUFSIZ
|
||||
};
|
||||
|
||||
if (!ops->Read) {
|
||||
Zlib_SetErrStat(ZSTM_EBADSTREAM);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!opts)
|
||||
opts = &default_opts;
|
||||
|
||||
// Setup open options
|
||||
int compression = opts->compression;
|
||||
if (compression != Z_DEFAULT_COMPRESSION)
|
||||
compression = CLAMP(compression, 0, 9);
|
||||
|
||||
int wbits = CLAMP(opts->win_bits, 8, 15);
|
||||
|
||||
// Mangle window bits according to the required RFC
|
||||
switch (opts->format) {
|
||||
case ZFMT_RFC1951:
|
||||
wbits = -wbits;
|
||||
break;
|
||||
case ZFMT_RFC1950:
|
||||
break;
|
||||
|
||||
case ZFMT_RFC1952:
|
||||
default:
|
||||
wbits += 16;
|
||||
break;
|
||||
}
|
||||
|
||||
size_t bufsiz = MIN(opts->bufsiz, INT_MAX); // safety to avoid short reads
|
||||
if (bufsiz == 0)
|
||||
bufsiz = ZSTM_BUFSIZ;
|
||||
|
||||
ZlibStmObj *hn = (ZlibStmObj *) malloc(offsetof(ZlibStmObj, buf[bufsiz]));
|
||||
if (!hn) {
|
||||
Zlib_SetErrStat(Z_MEM_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&hn->zs, 0, sizeof(hn->zs));
|
||||
hn->streamp = streamp;
|
||||
hn->ops = ops;
|
||||
hn->bufsiz = bufsiz;
|
||||
hn->deflating = TRUE;
|
||||
|
||||
int err = deflateInit2(
|
||||
&hn->zs,
|
||||
compression,
|
||||
Z_DEFLATED,
|
||||
wbits,
|
||||
8,
|
||||
Z_DEFAULT_STRATEGY
|
||||
);
|
||||
if (err != Z_OK) {
|
||||
Zlib_SetErrStat(err);
|
||||
free(hn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hn->zs.next_out = hn->buf;
|
||||
hn->zs.avail_out = hn->bufsiz;
|
||||
Zlib_SetErrStat(Z_OK);
|
||||
return hn;
|
||||
}
|
||||
|
||||
Sint64 Zlib_Read(ZlibStmHn hn, void *buf, size_t nbytes)
|
||||
{
|
||||
if (hn->deflating) {
|
||||
Zlib_SetErrStat(ZSTM_EBADSTREAM);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ZlibRet ret = Z_OK; // unless found otherwise
|
||||
|
||||
hn->zs.next_out = (Uint8 *) buf;
|
||||
hn->zs.avail_out = nbytes;
|
||||
while (hn->zs.avail_out > 0) {
|
||||
if (hn->zs.avail_in == 0) {
|
||||
// Fill buffer
|
||||
Sint64 n = hn->ops->Read(hn->streamp, hn->buf, hn->bufsiz);
|
||||
if (n <= 0) {
|
||||
if (n < 0) ret = ZSTM_EIO;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
hn->zs.next_in = hn->buf;
|
||||
hn->zs.avail_in = n;
|
||||
}
|
||||
|
||||
int err = inflate(&hn->zs, Z_NO_FLUSH);
|
||||
if (err == Z_NEED_DICT)
|
||||
err = Z_DATA_ERROR;
|
||||
if (err != Z_OK && err != Z_STREAM_END) {
|
||||
ret = err;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Zlib_SetErrStat(ret);
|
||||
return nbytes - hn->zs.avail_out;
|
||||
}
|
||||
|
||||
Sint64 Zlib_Write(ZlibStmHn hn, const void *buf, size_t nbytes)
|
||||
{
|
||||
if (!hn->deflating) {
|
||||
Zlib_SetErrStat(ZSTM_EBADSTREAM);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ZlibRet ret = Z_OK;
|
||||
|
||||
hn->zs.next_in = (Uint8 *) buf;
|
||||
hn->zs.avail_in = nbytes;
|
||||
while (hn->zs.avail_in > 0) {
|
||||
if (hn->zs.avail_out == 0) {
|
||||
Sint64 n = Zlib_FlushData(hn);
|
||||
if (n <= 0) {
|
||||
if (n < 0) ret = ZSTM_EIO;
|
||||
|
||||
break; // short-write
|
||||
}
|
||||
}
|
||||
|
||||
int err = deflate(&hn->zs, Z_NO_FLUSH);
|
||||
if (err == Z_NEED_DICT)
|
||||
err = Z_DATA_ERROR;
|
||||
if (err != Z_OK) {
|
||||
ret = err;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Zlib_SetErrStat(ret);
|
||||
return nbytes - hn->zs.avail_in;
|
||||
}
|
||||
|
||||
Judgement Zlib_Finish(ZlibStmHn hn)
|
||||
{
|
||||
if (!hn->deflating) {
|
||||
Zlib_SetErrStat(ZSTM_EBADSTREAM);
|
||||
return NG;
|
||||
}
|
||||
|
||||
int err;
|
||||
do {
|
||||
err = deflate(&hn->zs, Z_FINISH);
|
||||
if (err != Z_STREAM_END && err != Z_BUF_ERROR && err != Z_OK) {
|
||||
Zlib_SetErrStat(err);
|
||||
return NG;
|
||||
}
|
||||
if (Zlib_FlushData(hn) == -1) {
|
||||
Zlib_SetErrStat(ZSTM_EIO);
|
||||
return NG;
|
||||
}
|
||||
} while (err != Z_STREAM_END);
|
||||
|
||||
if (hn->zs.avail_out != hn->bufsiz) {
|
||||
Zlib_SetErrStat(Z_BUF_ERROR);
|
||||
return NG;
|
||||
}
|
||||
|
||||
Zlib_SetErrStat(Z_OK);
|
||||
return OK;
|
||||
}
|
||||
|
||||
void Zlib_Close(ZlibStmHn hn)
|
||||
{
|
||||
// Close stream
|
||||
if (hn->ops->Close)
|
||||
hn->ops->Close(hn->streamp);
|
||||
|
||||
// Finalize Z_stream
|
||||
if (hn->deflating)
|
||||
deflateEnd(&hn->zs);
|
||||
else
|
||||
inflateEnd(&hn->zs);
|
||||
|
||||
free(hn); // Free memory
|
||||
}
|
Reference in New Issue
Block a user