1039 lines
24 KiB
C
Raw Normal View History

2025-01-22 17:22:38 +01:00
/*
* LWS PNG -- derived from uPNG -- derived from LodePNG version 20100808
* Stateful, linewise PNG decode requiring ~36KB fixed heap
*
* Copyright (c) 2005-2010 Lode Vandevenne (LodePNG)
* Copyright (c) 2010 Sean Middleditch (uPNG)
* Copyright (c) 2021 Andy Green <andy@warmcat.com> (Stateful, incremental)
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*
* AG: The above notice is the ZLIB license, libpng also uses it.
*
* This version was rewritten from the upng project's fork of lodepng and
* adapted to be a stateful stream parser. This rewrite retains the ZLIB
* license of the source material for simplicity.
*
* That allows it to use a fixed 32KB ringbuffer to hold decodes, and
* incrementally decode chunks into it as we want output lines that are not yet
* present there. The input png nor the output bitmap need to be all in one
* place at one time.
*/
#include <private-lib-core.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
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, 0
};
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, 127
};
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, 0, 0
};
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, 0, 0
};
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[NUM_DISTANCE_SYMBOLS * 2] = {
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(htree_t *tree, const unsigned *bitlen)
{
unsigned int tree1d[NUM_DEFLATE_CODE_SYMBOLS], /* sized to worst */
blcount[NUM_DEFLATE_CODE_SYMBOLS], /* sized to worst */
nextcode[MAX_BIT_LENGTH + 1], bits, n, i,
nodefilled = 0, treepos = 0;
memset(blcount, 0, sizeof(blcount));
memset(nextcode, 0, sizeof(nextcode));
assert(tree->numcodes <= LWS_ARRAY_SIZE(blcount));
for (bits = 0; bits < tree->numcodes; bits++) {
/* any counts exceeding our private buffer length are fatal */
if (bitlen[bits] >= LWS_ARRAY_SIZE(blcount))
return LWS_SRET_FATAL + 1;
blcount[bitlen[bits]]++;
}
assert(tree->maxbitlen && tree->maxbitlen - 1u <= LWS_ARRAY_SIZE(blcount));
assert(tree->maxbitlen - 1u <= LWS_ARRAY_SIZE(nextcode));
for (bits = 1; bits <= (unsigned int)tree->maxbitlen; bits++)
nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1;
assert(tree->numcodes <= LWS_ARRAY_SIZE(tree1d));
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);
}
lws_stateful_ret_t
_lws_upng_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;
lwsl_debug("%s: (%lu) block type %d\n", __func__,
(unsigned long)inf->archive_pos + (inf->bp >> 3),
inf->btype);
/* uncompressed starts on a byte boundary */
if (!inf->btype && (inf->bp & 7)) {
lwsl_debug("%s: skipping %d alignment bits for type 0\n",
__func__, (int)(8 - (inf->bp & 7)) & 7);
inf->bp = ((inf->bp >> 3) + 1) << 3;
}
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;
// lwsl_notice("%s: no compression block\n", __func__);
/* 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;
lwsl_debug("%s: type 0 expects len %d\n", __func__, inf->len);
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++;
inf->n++;
if (inf->outpos_linear - inf->consumed_linear >=
inf->bypl + 1) {
return LWS_SRET_WANT_OUTPUT;
}
continue;
}
inf->treepos = 0;
inf->state = UPNS_ID_BL_GB_DONE;
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_debug("%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);
// lwsl_notice("%s: dyn tree init\n", __func__);
inf->treepos = 0;
/* 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;
inf->hlit = 0;
inf->hdist = 0;
inf->hclen = 0;
/* 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->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->ct,
inf->bitlen))
return LWS_SRET_FATAL + 7;
if (huffman_tree_create_lengths(&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) /* from google fuzzer */
return LWS_SRET_FATAL + 29;
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) {
lwsl_err("%s: inf->i (%d) > %d\n", __func__,
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) {
lwsl_err("%s: invalid dist %d\n", __func__, inf->codeD);
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;
case UPNS_ID_BL_GB_GZIP_ID1:
r = read_byte(inf, &t);
if (r)
return r;
if (t != 0x1f)
return LWS_SRET_FATAL + 32;
inf->state++;
/* fallthru */
case UPNS_ID_BL_GB_GZIP_ID2:
r = read_byte(inf, &t);
if (r)
return r;
if (t != 0x8b)
return LWS_SRET_FATAL + 33;
inf->state++;
/* fallthru */
case UPNS_ID_BL_GB_GZIP_METHOD:
r = read_byte(inf, &t);
if (r)
return r;
if (t != 8)
return LWS_SRET_FATAL + 34;
inf->state++;
/* fallthru */
case UPNS_ID_BL_GB_GZIP_FLAGS:
r = read_byte(inf, &t);
if (r)
return r;
if (t & 0xe0)
return LWS_SRET_FATAL + 35;
inf->gz_flags = t;
inf->state++;
inf->ctr = 6;
/* fallthru */
case UPNS_ID_BL_GB_GZIP_EOH:
/* we want skip 6 bytes */
if (inf->ctr--) {
r = read_byte(inf, &t);
if (r)
return r;
continue;
}
if (inf->gz_flags & 4)
inf->state = UPNS_ID_BL_GB_GZIP_SKIP_EXTRA_C1;
else
if (inf->gz_flags & 8)
inf->state = UPNS_ID_BL_GB_GZIP_SKIP_FILENAME;
else
if (inf->gz_flags & 16)
inf->state = UPNS_ID_BL_GB_GZIP_SKIP_COMMENT;
else
if (inf->gz_flags & 2) {
inf->state = UPNS_ID_BL_GB_GZIP_SKIP_CRC;
inf->ctr = 2;
} else
inf->state = UPNS_ID_BL_GB_DONE;
continue;
case UPNS_ID_BL_GB_GZIP_SKIP_EXTRA_C1:
r = read_byte(inf, &t);
if (r)
return r;
inf->ctr = t;
inf->state++;
/* fallthru */
case UPNS_ID_BL_GB_GZIP_SKIP_EXTRA_C2:
r = read_byte(inf, &t);
if (r)
return r;
inf->ctr = (uint16_t)(inf->ctr | (t << 8));
inf->state++;
/* fallthru */
case UPNS_ID_BL_GB_GZIP_SKIP_EXTRA:
if (inf->ctr--) {
r = read_byte(inf, &t);
if (r)
return r;
continue;
}
if (inf->gz_flags & 8)
inf->state = UPNS_ID_BL_GB_GZIP_SKIP_FILENAME;
else
if (inf->gz_flags & 16)
inf->state = UPNS_ID_BL_GB_GZIP_SKIP_COMMENT;
else
if (inf->gz_flags & 2) {
inf->state = UPNS_ID_BL_GB_GZIP_SKIP_CRC;
inf->ctr = 2;
} else
inf->state = UPNS_ID_BL_GB_DONE;
continue;
case UPNS_ID_BL_GB_GZIP_SKIP_FILENAME: /* zero-terminated */
r = read_byte(inf, &t);
if (r)
return r;
if (t)
continue;
if (inf->gz_flags & 16)
inf->state = UPNS_ID_BL_GB_GZIP_SKIP_COMMENT;
else
if (inf->gz_flags & 2) {
inf->state = UPNS_ID_BL_GB_GZIP_SKIP_CRC;
inf->ctr = 2;
} else
inf->state = UPNS_ID_BL_GB_DONE;
continue;
case UPNS_ID_BL_GB_GZIP_SKIP_COMMENT: /* zero-terminated */
r = read_byte(inf, &t);
if (r)
return r;
if (t)
continue;
if (inf->gz_flags & 2) {
inf->state = UPNS_ID_BL_GB_GZIP_SKIP_CRC;
inf->ctr = 2;
}
else
inf->state = UPNS_ID_BL_GB_DONE;
continue;
case UPNS_ID_BL_GB_GZIP_SKIP_CRC:
if (inf->ctr--) {
r = read_byte(inf, &t);
if (r)
return r;
continue;
}
inf->state = UPNS_ID_BL_GB_DONE;
continue;
}
}
return LWS_SRET_OK;
}
struct inflator_ctx *
lws_upng_inflator_create(const uint8_t **outring, size_t *outringlen,
size_t **opl, size_t **cl)
{
inflator_ctx_t *inf = lws_zalloc(sizeof(*inf), __func__);
if (!inf) {
lwsl_notice("%s: OOM\n", __func__);
return NULL;
}
/* 32KB gz sliding window */
inf->info_size = 32768;
inf->bypl = 0;
inf->outlen = inf->info_size;
inf->outpos = 0;
inf->state = UPNS_ID_BL_GB_GZIP_ID1;
inf->out = (uint8_t *)lws_malloc(inf->info_size, __func__);
if (!inf->out) {
lwsl_notice("%s: inf malloc %u OOM\n",
__func__, (unsigned int)inf->info_size);
lws_free(inf);
return NULL;
}
*outring = inf->out;
*outringlen = inf->info_size;
*opl = &inf->outpos_linear;
*cl = &inf->consumed_linear;
return inf;
}
lws_stateful_ret_t
lws_upng_inflate_data(struct inflator_ctx *inf, const void *buf, size_t len)
{
lws_stateful_ret_t r;
if (buf) {
inf->in = buf;
inf->inlen = len;
inf->inpos = 0;
inf->bp = 0;
}
if (!inf->bypl)
inf->bypl = 4095;
r = _lws_upng_inflate_data(inf);
if ((inf->bp >> 3) == inf->inlen) {
inf->archive_pos += inf->inlen;
inf->inlen = 0;
inf->bp = 0;
}
return r;
}
void
lws_upng_inflator_destroy(struct inflator_ctx **inf)
{
lws_free_set_NULL((*inf)->out);
lws_free_set_NULL(*inf);
}