/*
 * 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_l;

		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_l;

		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_l:

			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);
}


