1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-30 00:00:16 +01:00

upng: split out gzip

The adapted upng has a very compact fully-stateful lws-aligned
implementation already.

Adapt it to also be buildable and operable standalone, and to
understand gzip headers.

Provide some apis to inflate gzip simply reusing opaque inflator
contexts from upng.

Provide an api test that inflates gzip files from stdin -> stdout
This commit is contained in:
Andy Green 2022-03-01 17:29:24 +00:00
parent 48907fca0a
commit a74fe5d760
10 changed files with 1429 additions and 818 deletions

View file

@ -371,6 +371,10 @@ if (LWS_WITHOUT_SERVER)
set(LWS_WITH_SERVER)
endif()
if (LWS_WITH_UPNG)
set(LWS_WITH_GZINFLATE 1)
endif()
# using any abstract protocol enables LWS_WITH_ABSTRACT
#if (LWS_WITH_SMTP)

View file

@ -160,6 +160,7 @@ option(LWS_WITH_SYS_SMD "Lws System Message Distribution" ON)
option(LWS_WITH_SYS_FAULT_INJECTION "Enable fault injection support" OFF)
option(LWS_WITH_SYS_METRICS "Lws Metrics API" OFF)
option(LWS_WITH_UPNG "Enable stateful PNG stream decoder" ON)
option(LWS_WITH_GZINFLATE "Enable internal minimal gzip inflator" ON)
#
# Secure Streams

View file

@ -171,6 +171,7 @@
#cmakedefine LWS_WITH_GENERIC_SESSIONS
#cmakedefine LWS_WITH_GLIB
#cmakedefine LWS_WITH_GTK
#cmakedefine LWS_WITH_GZINFLATE
#cmakedefine LWS_WITH_HTTP2
#cmakedefine LWS_WITH_HTTP_BASIC_AUTH
#cmakedefine LWS_WITH_HTTP_BROTLI

View file

@ -47,6 +47,7 @@ typedef enum lws_upng_format_t {
LWS_UPNG_LUMINANCE_ALPHA8
} lws_upng_format_t;
struct inflator_ctx;
typedef struct lws_upng_t lws_upng_t;
/**
@ -116,3 +117,44 @@ lws_upng_get_pixelsize(const lws_upng_t *upng);
LWS_VISIBLE LWS_EXTERN lws_upng_format_t
lws_upng_get_format(const lws_upng_t *upng);
/**
* lws_upng_inflator_create() - create a gzip inflator context
*
* \param outring: pointer set to the output ringbuffer on exit
* \param outringlen: size of the output ringbuffer set on exit
* \param opl: pointer to set to point to ctx outpos_linear
* \param cl: pointer to set to point to ctx consumed_linear
*
* Creates an opaque gzip inflator object.
*/
LWS_VISIBLE LWS_EXTERN struct inflator_ctx *
lws_upng_inflator_create(const uint8_t **outring, size_t *outringlen,
size_t **opl, size_t **cl);
/**
* lws_upng_inflate_data() - inflate compressed data statefully
*
* \param inf: inflator context created with lws_upng_inflator_create()
* \param buf: NULL to continue consumption of existing input, or new input
* \param len: ignored if \p buf is NULL, else amount of new input at \p buf
*
* Tries to progress the inflation. If output is available, \p *opl will be
* further along than before it was called. \p *cl should be set to \p opl
* to consume the available output data.
*
* Output is into a ringfuffer, typically sized at 32KB. \p opl and \p cl
* are "linear", that is extend beyond the ringbuffer. They should be modulo
* outringlen (given when the inflator was created) when accessing outring.
*/
LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t
lws_upng_inflate_data(struct inflator_ctx *inf, const void *buf, size_t len);
/**
* lws_upng_inflator_destroy() - destroys the inflation context and ringbuffer
*
* \p inf: pointer to pointer to inflation context
*
* Frees the inflation context and its allocations, and sets \p *inf to NULL.
*/
LWS_VISIBLE LWS_EXTERN void
lws_upng_inflator_destroy(struct inflator_ctx **inf);

View file

@ -815,6 +815,149 @@ lws_strdup(const char *s);
int
lws_b64_selftest(void);
#define FIRST_LENGTH_CODE_INDEX 257
#define LAST_LENGTH_CODE_INDEX 285
/*256 literals, the end code, some length codes, and 2 unused codes */
#define NUM_DEFLATE_CODE_SYMBOLS 288
/*the distance codes have their own symbols, 30 used, 2 unused */
#define NUM_DISTANCE_SYMBOLS 32
/* The code length codes. 0-15: code lengths, 16: copy previous 3-6 times,
* 17: 3-10 zeros, 18: 11-138 zeros */
#define NUM_CODE_LENGTH_CODES 19
/* largest number of symbols used by any tree type */
#define MAX_SYMBOLS 288
#define DEFLATE_CODE_BITLEN 15
#define DISTANCE_BITLEN 15
#define CODE_LENGTH_BITLEN 7
/* largest bitlen used by any tree type */
#define MAX_BIT_LENGTH 15
#define DEFLATE_CODE_BUFFER_SIZE (NUM_DEFLATE_CODE_SYMBOLS * 2)
#define DISTANCE_BUFFER_SIZE (NUM_DISTANCE_SYMBOLS * 2)
#define CODE_LENGTH_BUFFER_SIZE (NUM_DISTANCE_SYMBOLS * 2)
typedef uint16_t huff_t;
typedef enum {
UPNS_ID_BL_GB_DONE,
UPNS_ID_BL_GB_BTYPEb0,
UPNS_ID_BL_GB_BTYPEb1,
UPNS_ID_BL_GB_BTYPE_0,
UPNS_ID_BL_GB_BTYPE_1,
UPNS_ID_BL_GB_BTYPE_2,
UPNS_ID_BL_GB_BTYPE_0a,
UPNS_ID_BL_GB_BTYPE_0b,
UPNS_ID_BL_GB_BTYPE_0c,
UPNS_ID_BL_GB_BTYPE_0d,
UPNS_ID_BL_GB_BTYPE_2a,
UPNS_ID_BL_GB_BTYPE_2b,
UPNS_ID_BL_GB_BTYPE_2c,
UPNS_ID_BL_GB_BTYPE_2d,
UPNS_ID_BL_GB_BTYPE_2e,
UPNS_ID_BL_GB_BTYPE_2_16,
UPNS_ID_BL_GB_BTYPE_2_17,
UPNS_ID_BL_GB_BTYPE_2_18,
UPNS_ID_BL_GB_SPIN,
UPNS_ID_BL_GB_SPINa,
UPNS_ID_BL_GB_SPINb,
UPNS_ID_BL_GB_SPINc,
UPNS_ID_BL_GB_SPINd,
UPNS_ID_BL_GB_SPINe,
UPNS_ID_BL_GB_GZIP_ID1,
UPNS_ID_BL_GB_GZIP_ID2,
UPNS_ID_BL_GB_GZIP_METHOD,
UPNS_ID_BL_GB_GZIP_FLAGS,
UPNS_ID_BL_GB_GZIP_EOH,
UPNS_ID_BL_GB_GZIP_SKIP_EXTRA_C1,
UPNS_ID_BL_GB_GZIP_SKIP_EXTRA_C2,
UPNS_ID_BL_GB_GZIP_SKIP_EXTRA,
UPNS_ID_BL_GB_GZIP_SKIP_FILENAME,
UPNS_ID_BL_GB_GZIP_SKIP_COMMENT,
UPNS_ID_BL_GB_GZIP_SKIP_CRC,
} upng_inflate_states_t;
typedef struct htree {
huff_t *tree2d;
/*maximum number of bits a single code can get */
uint16_t maxbitlen;
/*number of symbols in the alphabet = number of codes */
uint16_t numcodes;
} htree_t;
typedef struct inflator_ctx {
unsigned int clenc[NUM_CODE_LENGTH_CODES];
unsigned int bitlen[NUM_DEFLATE_CODE_SYMBOLS];
unsigned int bitlenD[NUM_DISTANCE_SYMBOLS];
huff_t clct_buffer[CODE_LENGTH_BUFFER_SIZE];
huff_t ct_buffer[DEFLATE_CODE_BUFFER_SIZE];
huff_t ctD_buffer[DISTANCE_BUFFER_SIZE];
lws_upng_t *upng;
const uint8_t *in;
uint8_t *out;
htree_t clct;
htree_t ct;
htree_t ctD;
size_t bp;
size_t inpos;
size_t inlen;
size_t outpos;
size_t outpos_linear;
size_t consumed_linear;
size_t outlen;
size_t length;
size_t start;
size_t forward;
size_t backward;
size_t exbits;
size_t bypl;
upng_inflate_states_t state;
unsigned int len;
unsigned int nlen;
unsigned int n;
unsigned int hlit;
unsigned int hdist;
unsigned int hclen;
unsigned int i;
unsigned int t;
unsigned int codeD;
unsigned int distance;
unsigned int exbitsD;
unsigned int code;
unsigned int treepos;
unsigned int read_bits_shifter;
unsigned int read_bits_limit;
unsigned int read_bits_i;
unsigned int info_size;
uint16_t ctr;
uint8_t subsequent;
uint8_t btype;
uint8_t done;
uint8_t gz_flags;
char read_bits_ongoing;
} inflator_ctx_t;
lws_stateful_ret_t
_lws_upng_inflate_data(inflator_ctx_t *inf);
#ifndef LWS_NO_DAEMONIZE
pid_t get_daemonize_pid();

View file

@ -60,6 +60,11 @@ if (LWS_WITH_FTS)
misc/fts/trie-fd.c)
endif()
if (LWS_WITH_GZINFLATE)
list(APPEND SOURCES
misc/upng-gzip.c)
endif()
if (LWS_WITH_UPNG)
list(APPEND SOURCES
misc/upng.c)

1022
lib/misc/upng-gzip.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -44,31 +44,6 @@
#include <string.h>
#include <limits.h>
#define FIRST_LENGTH_CODE_INDEX 257
#define LAST_LENGTH_CODE_INDEX 285
/*256 literals, the end code, some length codes, and 2 unused codes */
#define NUM_DEFLATE_CODE_SYMBOLS 288
/*the distance codes have their own symbols, 30 used, 2 unused */
#define NUM_DISTANCE_SYMBOLS 32
/* The code length codes. 0-15: code lengths, 16: copy previous 3-6 times,
* 17: 3-10 zeros, 18: 11-138 zeros */
#define NUM_CODE_LENGTH_CODES 19
/* largest number of symbols used by any tree type */
#define MAX_SYMBOLS 288
#define DEFLATE_CODE_BITLEN 15
#define DISTANCE_BITLEN 15
#define CODE_LENGTH_BITLEN 7
/* largest bitlen used by any tree type */
#define MAX_BIT_LENGTH 15
#define DEFLATE_CODE_BUFFER_SIZE (NUM_DEFLATE_CODE_SYMBOLS * 2)
#define DISTANCE_BUFFER_SIZE (NUM_DISTANCE_SYMBOLS * 2)
#define CODE_LENGTH_BUFFER_SIZE (NUM_DISTANCE_SYMBOLS * 2)
typedef uint16_t huff_t;
typedef enum upng_color {
LWS_UPNG_LUM = 0,
LWS_UPNG_RGB = 2,
@ -97,48 +72,6 @@ struct upng_unfline {
char alt;
};
typedef struct htree {
huff_t *tree2d;
/*maximum number of bits a single code can get */
uint16_t maxbitlen;
/*number of symbols in the alphabet = number of codes */
uint16_t numcodes;
} htree_t;
typedef enum {
UPNS_ID_BL_GB_DONE,
UPNS_ID_BL_GB_BTYPEb0,
UPNS_ID_BL_GB_BTYPEb1,
UPNS_ID_BL_GB_BTYPE_0,
UPNS_ID_BL_GB_BTYPE_1,
UPNS_ID_BL_GB_BTYPE_2,
UPNS_ID_BL_GB_BTYPE_0a,
UPNS_ID_BL_GB_BTYPE_0b,
UPNS_ID_BL_GB_BTYPE_0c,
UPNS_ID_BL_GB_BTYPE_0d,
UPNS_ID_BL_GB_BTYPE_2a,
UPNS_ID_BL_GB_BTYPE_2b,
UPNS_ID_BL_GB_BTYPE_2c,
UPNS_ID_BL_GB_BTYPE_2d,
UPNS_ID_BL_GB_BTYPE_2e,
UPNS_ID_BL_GB_BTYPE_2_16,
UPNS_ID_BL_GB_BTYPE_2_17,
UPNS_ID_BL_GB_BTYPE_2_18,
UPNS_ID_BL_GB_SPIN,
UPNS_ID_BL_GB_SPINa,
UPNS_ID_BL_GB_SPINb,
UPNS_ID_BL_GB_SPINc,
UPNS_ID_BL_GB_SPINd,
UPNS_ID_BL_GB_SPINe,
} upng_inflate_states_t;
typedef enum {
UOF_MAGIC,
UOF_SKIP,
@ -157,65 +90,7 @@ typedef enum {
UOF_SKIP_CHUNK_LEN,
} upng_outer_framing_t;
typedef struct inflator_ctx {
unsigned int clenc[NUM_CODE_LENGTH_CODES];
unsigned int bitlen[NUM_DEFLATE_CODE_SYMBOLS];
unsigned int bitlenD[NUM_DISTANCE_SYMBOLS];
huff_t clct_buffer[CODE_LENGTH_BUFFER_SIZE];
huff_t ct_buffer[DEFLATE_CODE_BUFFER_SIZE];
huff_t ctD_buffer[DISTANCE_BUFFER_SIZE];
lws_upng_t *upng;
const uint8_t *in;
uint8_t *out;
htree_t clct;
htree_t ct;
htree_t ctD;
size_t bp;
size_t inpos;
size_t inlen;
size_t outpos;
size_t outpos_linear;
size_t consumed_linear;
size_t outlen;
size_t length;
size_t start;
size_t forward;
size_t backward;
size_t exbits;
size_t bypl;
upng_inflate_states_t state;
unsigned int len;
unsigned int nlen;
unsigned int n;
unsigned int hlit;
unsigned int hdist;
unsigned int hclen;
unsigned int i;
unsigned int t;
unsigned int codeD;
unsigned int distance;
unsigned int exbitsD;
unsigned int code;
unsigned int treepos;
unsigned int read_bits_shifter;
unsigned int read_bits_limit;
unsigned int read_bits_i;
unsigned int info_size;
uint8_t subsequent;
uint8_t btype;
uint8_t done;
char read_bits_ongoing;
} inflator_ctx_t;
struct lws_upng_t {
struct upng_unfline u;
@ -245,698 +120,6 @@ struct lws_upng_t {
static lws_stateful_ret_t
lws_upng_decode(lws_upng_t *upng, const uint8_t **buf, size_t *size);
static const huff_t huff_length_base[] = {
/*the base lengths represented by codes 257-285 */
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59,
67, 83, 99, 115, 131, 163, 195, 227, 258
};
static const huff_t huff_length_extra[] = {
/*the extra bits used by codes 257-285 (added to base length) */
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4,
5, 5, 5, 5, 0
};
static const huff_t huff_distance_base[] = {
/*
* The base backwards distances (the bits of distance codes appear
* after length codes and use their own huffman tree)
*/
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385,
513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
};
static const huff_t huff_distance_extra[] = {
/* the extra bits of backwards distances (added to base) */
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10,
10, 11, 11, 12, 12, 13, 13
};
static const huff_t huff_cl_cl[] = {
/*
* The order in which "code length alphabet code lengths" are stored,
* out of this the huffman tree of the dynamic huffman tree lengths
* is generated
*/
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};
static const huff_t FIXED_DEFLATE_CODE_TREE[NUM_DEFLATE_CODE_SYMBOLS * 2] = {
289, 370, 290, 307, 546, 291, 561, 292, 293, 300, 294, 297, 295, 296,
0, 1, 2, 3, 298, 299, 4, 5, 6, 7, 301, 304, 302, 303, 8, 9, 10, 11, 305,
306, 12, 13, 14, 15, 308, 339, 309, 324, 310, 317, 311, 314, 312, 313,
16, 17, 18, 19, 315, 316, 20, 21, 22, 23, 318, 321, 319, 320, 24, 25,
26, 27, 322, 323, 28, 29, 30, 31, 325, 332, 326, 329, 327, 328, 32, 33,
34, 35, 330, 331, 36, 37, 38, 39, 333, 336, 334, 335, 40, 41, 42, 43,
337, 338, 44, 45, 46, 47, 340, 355, 341, 348, 342, 345, 343, 344, 48,
49, 50, 51, 346, 347, 52, 53, 54, 55, 349, 352, 350, 351, 56, 57, 58,
59, 353, 354, 60, 61, 62, 63, 356, 363, 357, 360, 358, 359, 64, 65, 66,
67, 361, 362, 68, 69, 70, 71, 364, 367, 365, 366, 72, 73, 74, 75, 368,
369, 76, 77, 78, 79, 371, 434, 372, 403, 373, 388, 374, 381, 375, 378,
376, 377, 80, 81, 82, 83, 379, 380, 84, 85, 86, 87, 382, 385, 383, 384,
88, 89, 90, 91, 386, 387, 92, 93, 94, 95, 389, 396, 390, 393, 391, 392,
96, 97, 98, 99, 394, 395, 100, 101, 102, 103, 397, 400, 398, 399, 104,
105, 106, 107, 401, 402, 108, 109, 110, 111, 404, 419, 405, 412, 406,
409, 407, 408, 112, 113, 114, 115, 410, 411, 116, 117, 118, 119, 413,
416, 414, 415, 120, 121, 122, 123, 417, 418, 124, 125, 126, 127, 420,
427, 421, 424, 422, 423, 128, 129, 130, 131, 425, 426, 132, 133, 134,
135, 428, 431, 429, 430, 136, 137, 138, 139, 432, 433, 140, 141, 142,
143, 435, 483, 436, 452, 568, 437, 438, 445, 439, 442, 440, 441, 144,
145, 146, 147, 443, 444, 148, 149, 150, 151, 446, 449, 447, 448, 152,
153, 154, 155, 450, 451, 156, 157, 158, 159, 453, 468, 454, 461, 455,
458, 456, 457, 160, 161, 162, 163, 459, 460, 164, 165, 166, 167, 462,
465, 463, 464, 168, 169, 170, 171, 466, 467, 172, 173, 174, 175, 469,
476, 470, 473, 471, 472, 176, 177, 178, 179, 474, 475, 180, 181, 182,
183, 477, 480, 478, 479, 184, 185, 186, 187, 481, 482, 188, 189, 190,
191, 484, 515, 485, 500, 486, 493, 487, 490, 488, 489, 192, 193, 194,
195, 491, 492, 196, 197, 198, 199, 494, 497, 495, 496, 200, 201, 202,
203, 498, 499, 204, 205, 206, 207, 501, 508, 502, 505, 503, 504, 208,
209, 210, 211, 506, 507, 212, 213, 214, 215, 509, 512, 510, 511, 216,
217, 218, 219, 513, 514, 220, 221, 222, 223, 516, 531, 517, 524, 518,
521, 519, 520, 224, 225, 226, 227, 522, 523, 228, 229, 230, 231, 525,
528, 526, 527, 232, 233, 234, 235, 529, 530, 236, 237, 238, 239, 532,
539, 533, 536, 534, 535, 240, 241, 242, 243, 537, 538, 244, 245, 246,
247, 540, 543, 541, 542, 248, 249, 250, 251, 544, 545, 252, 253, 254,
255, 547, 554, 548, 551, 549, 550, 256, 257, 258, 259, 552, 553, 260,
261, 262, 263, 555, 558, 556, 557, 264, 265, 266, 267, 559, 560, 268,
269, 270, 271, 562, 565, 563, 564, 272, 273, 274, 275, 566, 567, 276,
277, 278, 279, 569, 572, 570, 571, 280, 281, 282, 283, 573, 574, 284,
285, 286, 287, 0, 0
};
static const huff_t FIXED_DISTANCE_TREE[] = {
33, 48, 34, 41, 35, 38, 36, 37,
0, 1, 2, 3, 39, 40, 4, 5,
6, 7, 42, 45, 43, 44, 8, 9,
10, 11, 46, 47, 12, 13, 14, 15,
49, 56, 50, 53, 51, 52, 16, 17,
18, 19, 54, 55, 20, 21, 22, 23,
57, 60, 58, 59, 24, 25, 26, 27,
61, 62, 28, 29, 30, 31, 0, 0
};
static lws_stateful_ret_t
read_bit(inflator_ctx_t *inf, uint8_t *bits)
{
size_t bo = inf->bp >> 3;
if (bo + inf->inpos >= inf->inlen)
return LWS_SRET_WANT_INPUT;
*bits = (uint8_t)((*(inf->in + inf->inpos + bo) >> (inf->bp & 7)) & 1);
inf->bp++;
return LWS_SRET_OK;
}
/* Stateful, so it can pick up after running out of input partway thru */
static lws_stateful_ret_t
read_bits(inflator_ctx_t *inf, unsigned int nbits, unsigned int *bits)
{
lws_stateful_ret_t r;
uint8_t b;
if (!inf->read_bits_ongoing) {
inf->read_bits_ongoing = 1;
inf->read_bits_shifter = 0;
inf->read_bits_limit = nbits;
inf->read_bits_i = 0;
}
while (inf->read_bits_i < inf->read_bits_limit) {
r =read_bit(inf, &b);
if (r)
return r;
inf->read_bits_shifter = inf->read_bits_shifter | (unsigned int)(b << inf->read_bits_i);
inf->read_bits_i++;
}
inf->read_bits_ongoing = 0;
*bits = inf->read_bits_shifter;
return LWS_SRET_OK;
}
static lws_stateful_ret_t
read_byte(inflator_ctx_t *inf, uint8_t *byte)
{
size_t bo;
while (inf->bp & 7)
inf->bp++;
bo = inf->bp >> 3;
if (bo + inf->inpos >= inf->inlen)
return LWS_SRET_WANT_INPUT;
*byte = *(inf->in + inf->inpos + bo);
inf->bp += 8;
return LWS_SRET_OK;
}
/* buffer must be numcodes*2 in size! */
static void
huffman_tree_init(htree_t *tree, huff_t *buffer, uint16_t numcodes,
uint16_t maxbitlen)
{
tree->tree2d = buffer;
tree->numcodes = numcodes;
tree->maxbitlen = maxbitlen;
}
#define EMPTY 32767
/*
* Given the code lengths (as stored in the PNG file), generate the tree as
* defined by Deflate. maxbitlen is the maximum bits that a code in the tree
* can have.
*/
static lws_stateful_ret_t
huffman_tree_create_lengths(lws_upng_t *upng, htree_t *tree,
const unsigned *bitlen)
{
unsigned int tree1d[MAX_SYMBOLS], blcount[MAX_BIT_LENGTH],
nextcode[MAX_BIT_LENGTH + 1];
unsigned int bits, n, i, nodefilled = 0, treepos = 0;
memset(blcount, 0, sizeof(blcount));
memset(nextcode, 0, sizeof(nextcode));
for (bits = 0; bits < tree->numcodes; bits++)
blcount[bitlen[bits]]++;
for (bits = 1; bits <= (unsigned int)tree->maxbitlen; bits++)
nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1;
for (n = 0; n < tree->numcodes; n++)
if (bitlen[n])
tree1d[n] = nextcode[bitlen[n]]++;
for (n = 0; n < (unsigned int)tree->numcodes * 2u; n++)
tree->tree2d[n] = EMPTY;
for (n = 0; n < tree->numcodes; n++) { /* the codes */
for (i = 0; i < bitlen[n]; i++) { /* the bits for this code */
uint8_t bit = (uint8_t)((tree1d[n] >>
(bitlen[n] - i - 1)) & 1);
/* check if oversubscribed */
if (treepos > tree->numcodes - 2u)
return LWS_SRET_FATAL + 1;
if (tree->tree2d[2 * treepos + bit] == EMPTY) {
if (i + 1 == bitlen[n]) { /* ... last bit */
tree->tree2d[2 * treepos + bit] = (huff_t)n;
treepos = 0;
} else {
nodefilled++;
tree->tree2d[2 * treepos + bit] =
(huff_t)(nodefilled + tree->numcodes);
treepos = nodefilled;
}
} else
treepos = (unsigned int)(tree->tree2d[2 * treepos + bit] -
tree->numcodes);
}
}
for (n = 0; n < tree->numcodes * 2u; n++)
if (tree->tree2d[n] == EMPTY)
tree->tree2d[n] = 0;
return LWS_SRET_OK;
}
static lws_stateful_ret_t
huffman_decode_symbol(inflator_ctx_t *inf, const htree_t *ct, unsigned int *uct)
{
lws_stateful_ret_t r;
uint8_t bit;
do {
r = read_bit(inf, &bit);
if (r)
return r;
*uct = ct->tree2d[(inf->treepos << 1) | bit];
if (*uct < ct->numcodes)
return LWS_SRET_OK;
inf->treepos = *uct - ct->numcodes;
if (inf->treepos >= ct->numcodes)
return LWS_SRET_FATAL + 2;
} while (1);
}
static lws_stateful_ret_t
uz_inflate_data(inflator_ctx_t *inf)
{
unsigned int count, val, tu;
uint8_t t, done = 0;
lws_stateful_ret_t r;
size_t virt;
while (!done) {
switch (inf->state) {
case UPNS_ID_BL_GB_DONE:
r = read_bit(inf, &inf->done);
if (r)
return r;
inf->state++;
/* fallthru */
case UPNS_ID_BL_GB_BTYPEb0:
r = read_bit(inf, &inf->btype);
if (r)
return r;
inf->state++;
/* fallthru */
case UPNS_ID_BL_GB_BTYPEb1:
r = read_bit(inf, &t);
if (r)
return r;
inf->btype |= (uint8_t)(t << 1);
if (inf->btype == 3)
return LWS_SRET_FATAL + 3;
inf->i = 0;
inf->state = UPNS_ID_BL_GB_BTYPE_0 + inf->btype;
continue;
case UPNS_ID_BL_GB_BTYPE_0: /* no compression */
r = read_byte(inf, &t);
if (r)
return r;
inf->len = t;
inf->state = UPNS_ID_BL_GB_BTYPE_0a;
/* fallthru */
case UPNS_ID_BL_GB_BTYPE_0a:
r = read_byte(inf, &t);
if (r)
return r;
inf->len = inf->len | (unsigned int)(t << 8);
inf->state++;
/* fallthru */
case UPNS_ID_BL_GB_BTYPE_0b:
r = read_byte(inf, &t);
if (r)
return r;
inf->nlen = t;
inf->state++;
/* fallthru */
case UPNS_ID_BL_GB_BTYPE_0c:
r = read_byte(inf, &t);
if (r)
return r;
inf->nlen = inf->nlen | (unsigned int)(t << 8);
if (inf->len + inf->nlen != 65535)
return LWS_SRET_FATAL + 4;
inf->state++;
inf->n = 0;
/* fallthru */
case UPNS_ID_BL_GB_BTYPE_0d:
if (inf->n < inf->len) {
r = read_byte(inf, &t);
if (r)
return r;
inf->out[inf->outpos++] = t;
if (inf->outpos >= inf->outlen)
inf->outpos = 0;
inf->outpos_linear++;
if (inf->outpos_linear - inf->consumed_linear >=
inf->bypl + 1)
return LWS_SRET_WANT_OUTPUT;
inf->n++;
continue;
}
inf->treepos = 0;
inf->state = UPNS_ID_BL_GB_SPIN;
continue;
case UPNS_ID_BL_GB_BTYPE_1: /* fixed trees */
huffman_tree_init(&inf->ct,
(huff_t *)FIXED_DEFLATE_CODE_TREE,
NUM_DEFLATE_CODE_SYMBOLS,
DEFLATE_CODE_BITLEN);
huffman_tree_init(&inf->ctD,
(huff_t *)FIXED_DISTANCE_TREE,
NUM_DISTANCE_SYMBOLS,
DISTANCE_BITLEN);
lwsl_notice("%s: fixed tree init\n", __func__);
inf->treepos = 0;
inf->state = UPNS_ID_BL_GB_SPIN;
continue;
case UPNS_ID_BL_GB_BTYPE_2: /* dynamic trees */
huffman_tree_init(&inf->ct, inf->ct_buffer,
NUM_DEFLATE_CODE_SYMBOLS,
DEFLATE_CODE_BITLEN);
huffman_tree_init(&inf->ctD, inf->ctD_buffer,
NUM_DISTANCE_SYMBOLS,
DISTANCE_BITLEN);
huffman_tree_init(&inf->clct, inf->clct_buffer,
NUM_CODE_LENGTH_CODES,
CODE_LENGTH_BITLEN);
/* clear bitlen arrays */
memset(inf->bitlen, 0, sizeof(inf->bitlen));
memset(inf->bitlenD, 0, sizeof(inf->bitlenD));
inf->state = UPNS_ID_BL_GB_BTYPE_2a;
/* fallthru */
case UPNS_ID_BL_GB_BTYPE_2a:
r = read_bits(inf, 5, &inf->hlit);
if (r)
return r;
inf->hlit += 257;
inf->state++;
/* fallthru */
case UPNS_ID_BL_GB_BTYPE_2b:
r = read_bits(inf, 5, &inf->hdist);
if (r)
return r;
inf->hdist++;
inf->state++;
/* fallthru */
case UPNS_ID_BL_GB_BTYPE_2c:
r = read_bits(inf, 4, &inf->hclen);
if (r)
return r;
inf->hclen += 4;
inf->state = UPNS_ID_BL_GB_BTYPE_2d;
inf->i = 0;
/* fallthru */
case UPNS_ID_BL_GB_BTYPE_2d:
if (inf->i < NUM_CODE_LENGTH_CODES) {
if (inf->i < inf->hclen) {
r = read_bits(inf, 3,
&inf->clenc[huff_cl_cl[inf->i]]);
if (r)
return r;
} else
/*if not, it must stay 0 */
inf->clenc[huff_cl_cl[inf->i]] = 0;
inf->i++;
continue;
}
r = huffman_tree_create_lengths(inf->upng, &inf->clct,
inf->clenc);
if (r)
return r;
inf->i = 0;
inf->state = UPNS_ID_BL_GB_BTYPE_2e;
inf->treepos = 0;
/* fallthru */
case UPNS_ID_BL_GB_BTYPE_2e:
if (inf->i >= inf->hlit + inf->hdist) {
if (inf->bitlen[256] == 0)
return LWS_SRET_FATAL + 6;
if (huffman_tree_create_lengths(inf->upng,
&inf->ct,
inf->bitlen))
return LWS_SRET_FATAL + 7;
if (huffman_tree_create_lengths(inf->upng,
&inf->ctD,
inf->bitlenD))
return LWS_SRET_FATAL + 8;
inf->treepos = 0;
inf->state = UPNS_ID_BL_GB_SPIN;
continue;
}
r = huffman_decode_symbol(inf, &inf->clct, &inf->code);
if (r)
return r;
switch (inf->code) {
case 16:
inf->state = UPNS_ID_BL_GB_BTYPE_2_16;
continue;
case 17:
inf->state = UPNS_ID_BL_GB_BTYPE_2_17;
continue;
case 18:
inf->state = UPNS_ID_BL_GB_BTYPE_2_18;
continue;
default:
if (inf->code > 15)
return LWS_SRET_FATAL + 9;
if (inf->i < inf->hlit)
inf->bitlen[inf->i] = inf->code;
else
inf->bitlenD[inf->i - inf->hlit] =
inf->code;
inf->i++;
inf->treepos = 0;
/* stay in 2e */
continue;
}
case UPNS_ID_BL_GB_BTYPE_2_16: /* repeat previous */
r = read_bits(inf, 2, &tu);
if (r)
return r;
count = tu + 3;
if ((inf->i - 1) < inf->hlit)
val = inf->bitlen[inf->i - 1];
else
val = inf->bitlenD[inf->i - inf->hlit - 1];
goto fill;
case UPNS_ID_BL_GB_BTYPE_2_17: /*repeat "0" 3-10 times */
r = read_bits(inf, 3, &tu);
if (r)
return r;
count = tu + 3;
val = 0;
goto fill;
case UPNS_ID_BL_GB_BTYPE_2_18: /*repeat "0" 11-138 times */
r = read_bits(inf, 7, &tu);
if (r)
return r;
count = tu + 11;
val = 0;
fill:
if (inf->i + count > inf->hlit + inf->hdist)
return LWS_SRET_FATAL + 10;
{
unsigned int n;
for (n = 0; n < count; n++) {
if (inf->i < inf->hlit)
inf->bitlen[inf->i] = val;
else
inf->bitlenD[inf->i - inf->hlit] = val;
inf->i++;
}
}
inf->state = UPNS_ID_BL_GB_BTYPE_2e;
inf->treepos = 0;
continue;
case UPNS_ID_BL_GB_SPIN:
r = huffman_decode_symbol(inf, &inf->ct, &inf->code);
if (r)
return r;
if (inf->code >= FIRST_LENGTH_CODE_INDEX &&
inf->code - FIRST_LENGTH_CODE_INDEX <
LWS_ARRAY_SIZE(huff_length_base))
inf->length = huff_length_base[inf->code -
FIRST_LENGTH_CODE_INDEX];
else
inf->length = 0;
if (inf->code == 256) {
/*
* We're finished with this huffman block, we
* need to go back up a level
*/
done = inf->done;
inf->state = UPNS_ID_BL_GB_DONE;
continue;
}
if (inf->code <= 255) {
inf->state = UPNS_ID_BL_GB_SPINa;
continue;
}
if (inf->code < FIRST_LENGTH_CODE_INDEX ||
inf->code > LAST_LENGTH_CODE_INDEX) {
inf->treepos = 0;
continue;
}
inf->exbits = huff_length_extra[inf->code -
FIRST_LENGTH_CODE_INDEX];
inf->state = UPNS_ID_BL_GB_SPINb;
/* fallthru */
case UPNS_ID_BL_GB_SPINb:
r = read_bits(inf, (unsigned int)inf->exbits, &tu);
if (r)
return r;
inf->length += tu;
inf->state++;
inf->treepos = 0;
/* fallthru */
case UPNS_ID_BL_GB_SPINc:
/* part 3: get distance code */
r = huffman_decode_symbol(inf, &inf->ctD, &inf->codeD);
if (r)
return r;
/* invalid distance code (30-31 are never used) */
if (inf->codeD > 29)
return LWS_SRET_FATAL + 11;
inf->distance = huff_distance_base[inf->codeD];
/* part 4: get extra bits from distance */
inf->exbitsD = huff_distance_extra[inf->codeD];
inf->state++;
/* fallthru */
case UPNS_ID_BL_GB_SPINd:
r = read_bits(inf, inf->exbitsD, &tu);
if (r)
return r;
inf->distance += tu;
if (inf->distance > inf->info_size) {
lwsl_err("%s: distance %lu\n", __func__,
(unsigned long)inf->distance);
assert(0);
}
/*
* Part 5: fill in all the out[n] values based
* on the length and dist
*/
inf->start = inf->outpos;
inf->forward = 0;
inf->backward = inf->distance; /* from inf->start */
inf->state++;
/* fallthru */
case UPNS_ID_BL_GB_SPINe:
if (inf->forward >= inf->length) {
inf->treepos = 0;
inf->state = UPNS_ID_BL_GB_SPIN;
continue;
}
if (inf->backward <= inf->start)
virt = inf->start - inf->backward;
else /* wrapped... backward >= start */
virt = inf->info_size -
(inf->backward - inf->start);
if (virt >= inf->info_size)
lwsl_err("virt %d\n", (int)virt);
inf->out[inf->outpos++] = inf->out[virt];
if (inf->outpos >= inf->outlen)
inf->outpos = 0;
inf->outpos_linear++;
inf->backward--;
if (!inf->backward)
inf->backward = inf->distance;
inf->forward++;
if (inf->outpos_linear - inf->consumed_linear >=
inf->bypl + 1)
return LWS_SRET_WANT_OUTPUT;
continue;
case UPNS_ID_BL_GB_SPINa:
inf->out[inf->outpos++] = (uint8_t)inf->code;
if (inf->outpos >= inf->outlen)
inf->outpos = 0;
inf->outpos_linear++;
inf->treepos = 0;
inf->state = UPNS_ID_BL_GB_SPIN;
if (inf->outpos_linear - inf->consumed_linear >=
inf->bypl + 1)
return LWS_SRET_WANT_OUTPUT;
continue;
}
}
return LWS_SRET_OK;
}
static int
paeth(int a, int b, int c)
{
@ -1389,7 +572,7 @@ lws_upng_decode(lws_upng_t* u, const uint8_t **_pos, size_t *_size)
}
}
r = uz_inflate_data(&u->inf);
r = _lws_upng_inflate_data(&u->inf);
switch (r) {
case LWS_SRET_WANT_INPUT:

View file

@ -0,0 +1,21 @@
project(lws-api-test-gunzip C)
cmake_minimum_required(VERSION 2.8.12)
find_package(libwebsockets CONFIG REQUIRED)
list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR})
include(CheckCSourceCompiles)
include(LwsCheckRequirements)
set(requirements 1)
require_lws_config(LWS_WITH_GZINFLATE 1 requirements)
if (requirements)
add_executable(${PROJECT_NAME} main.c)
if (websockets_shared)
target_link_libraries(${PROJECT_NAME} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS})
add_dependencies(${PROJECT_NAME} websockets_shared)
else()
target_link_libraries(${PROJECT_NAME} websockets ${LIBWEBSOCKETS_DEP_LIBS})
endif()
endif()

View file

@ -0,0 +1,189 @@
/*
* lws-api-test-gunzip
*
* Written in 2010-2022 by Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* tests for LWS_WITH_GZINFLATE (inflator via upng)
*/
#include <libwebsockets.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
int fdin = 0, fdout = 1;
int
main(int argc, const char **argv)
{
int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
int result = 0, more = 1;
const char *p;
lws_stateful_ret_t r = LWS_SRET_WANT_INPUT;
struct inflator_ctx *gunz;
const uint8_t *outring;
size_t l = 0, old_op = 0, outringlen, *opl, *cl, pw = 0;
if ((p = lws_cmdline_option(argc, argv, "-d")))
logs = atoi(p);
lws_set_log_level(logs, NULL);
lwsl_user("LWS API selftest: gunzip\n");
if ((p = lws_cmdline_option(argc, argv, "--stdin"))) {
fdin = open(p, LWS_O_RDONLY, 0);
if (fdin < 0) {
result = 1;
lwsl_err("%s: unable to open stdin file\n", __func__);
goto bail;
}
}
if ((p = lws_cmdline_option(argc, argv, "--stdout"))) {
fdout = open(p, LWS_O_WRONLY | LWS_O_CREAT | LWS_O_TRUNC, 0600);
if (fdout < 0) {
result = 1;
lwsl_err("%s: unable to open stdout file\n", __func__);
goto bail;
}
}
if (!fdin) {
struct timeval timeout;
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
timeout.tv_sec = 0;
timeout.tv_usec = 1000;
if (select(fdin + 1, &fds, NULL, NULL, &timeout) < 0 ||
!FD_ISSET(0, &fds)) {
result = 1;
lwsl_err("%s: pass PNG "
"on stdin or use --stdin\n", __func__);
goto bail;
}
}
gunz = lws_upng_inflator_create(&outring, &outringlen, &opl, &cl);
if (!gunz)
goto bail;
do {
uint8_t ib[9];
const uint8_t *pib = NULL;
ssize_t s, os;
size_t ps = 0, part;
pib = NULL;
if ((r & LWS_SRET_WANT_INPUT) && more) {
s = read(fdin, ib, sizeof(ib));
if (s <= 0) {
lwsl_err("%s: failed to read: %d (after %lu)\n", __func__, errno, (unsigned long)l);
more = 0;
} else {
pib = ib;
ps = (size_t)s;
l += ps;
// lwsl_hexdump_notice(pib, ps);
lwsl_info("%s: fetched %u (%u)\n", __func__,
(unsigned int)s, (unsigned int)l);
}
}
do {
r = lws_upng_inflate_data(gunz, pib, ps);
pib = NULL;
ps = 0;
// lwsl_notice("r = %d\n", r);
if (r & LWS_SRET_FATAL) {
lwsl_err("%s: emit returned FATAL %d\n", __func__, r &0xff);
result = 1;
goto bail1;
}
if (!more && *opl == old_op) {
lwsl_notice("%s: seem finished\n", __func__);
/* no more input possible, and no output came */
goto bail1;
}
os = (ssize_t)((*opl - (size_t)old_op) % outringlen);
/* if we wrap around the ring, first do the part to the
* end of the ring */
part = (size_t)os;
if ((*opl % outringlen) < old_op)
part = outringlen - old_op;
// lwsl_notice("%s: out %d (%d -> %d)\n", __func__, (int)os, (int)old_op, (int)(old_op + part));
if (write(fdout, outring + old_op,
#if defined(WIN32)
(unsigned int)
#endif
part) < (ssize_t)part) {
lwsl_err("%s: write %d failed %d\n", __func__,
(int)os, errno);
goto bail1;
}
/* then do the remainder (if any) from the ring start */
if ((*opl % outringlen) < old_op)
if (write(fdout, outring,
#if defined(WIN32)
(unsigned int)
#endif
*opl % outringlen) < (ssize_t)(*opl % outringlen)) {
lwsl_err("%s: write %d failed %d\n", __func__,
(int)os, errno);
goto bail1;
}
old_op = *opl % outringlen;
*cl = *opl;
pw = (size_t)(pw + (size_t)os);
lwsl_debug("%s: wrote %d: r %u (left %u)\n", __func__,
(int)os, r, (unsigned int)ps);
if (r == LWS_SRET_OK) {
lwsl_notice("%s: feels OK %lu\n", __func__, (unsigned long)pw);
goto bail1;
}
if (r & LWS_SRET_WANT_INPUT)
break;
} while (ps); /* while any input left */
} while (1);
bail1:
lws_upng_inflator_destroy(&gunz);
bail:
if (fdin >= 0)
close(fdin);
if (fdout >= 0 && fdout != 1)
close(fdout);
lwsl_user("Completed\n");
return result;
}