Example #1
0
 void decompress(uint8_t *input, uint32_t compressed_size)
 {
 	printf("Decompressing data...\n");
 	heatshrink_decoder_reset(&hsd);
 	size_t decomp_sz = 2048; /* Maximum buffer size before compression */
	uint8_t *decomp = malloc(decomp_sz);
	memset(decomp, 0, decomp_sz);
	size_t count;
	uint32_t sunk = 0;
    uint32_t polled = 0;

    while (sunk < compressed_size) {
        heatshrink_decoder_sink(&hsd, &input[sunk], compressed_size - sunk, &count);
        sunk += count;
        if (sunk == compressed_size) {
            heatshrink_decoder_finish(&hsd);
        }
        HSD_poll_res pres;
        do {
            pres = heatshrink_decoder_poll(&hsd, &decomp[polled],
                decomp_sz - polled, &count);
            polled += count;
        } while (pres == HSDR_POLL_MORE);
        if (sunk == compressed_size) {
            heatshrink_decoder_finish(&hsd);
        }
    }
    printf("Size after decompression: %d\n", polled);
    csv = fopen("data.csv", "a");
    for (int i = 0; i < polled; ++i) {
    	fprintf(csv, "%hhu\n", decomp[i]);
    }
	fclose(csv);
	free(decomp);
 }
TEST decoder_poll_should_expand_short_literal_and_backref() {
    uint8_t input[] = {0xb3, 0x5b, 0xed, 0xe0, 0x40, 0x80}; //"foofoo"
    uint8_t output[6];
    heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 7, 7);
    memset(output, 0, sizeof(*output));
    uint16_t count = 0;
    
    HSD_sink_res sres = heatshrink_decoder_sink(hsd, input, sizeof(input), &count);
    ASSERT_EQ(HSDR_SINK_OK, sres);

    uint16_t out_sz = 0;
    (void)heatshrink_decoder_poll(hsd, output, 6, &out_sz);

    if (0) dump_buf("output", output, out_sz);
    ASSERT_EQ(6, out_sz);
    ASSERT_EQ('f', output[0]);
    ASSERT_EQ('o', output[1]);
    ASSERT_EQ('o', output[2]);
    ASSERT_EQ('f', output[3]);
    ASSERT_EQ('o', output[4]);
    ASSERT_EQ('o', output[5]);

    heatshrink_decoder_free(hsd);
    PASS();
}
TEST decoder_finish_should_note_when_done() {
    uint8_t input[] = {0xb3, 0x5b, 0xed, 0xe0, 0x40, 0x80}; //"foofoo"

    uint8_t output[7];
    heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 7, 7);
    memset(output, 0, sizeof(*output));
    uint16_t count = 0;
    
    HSD_sink_res sres = heatshrink_decoder_sink(hsd, input, sizeof(input), &count);
    ASSERT_EQ(HSDR_SINK_OK, sres);

    uint16_t out_sz = 0;
    HSD_poll_res pres = heatshrink_decoder_poll(hsd, output, sizeof(output), &out_sz);
    ASSERT_EQ(HSDR_POLL_EMPTY, pres);
    ASSERT_EQ(6, out_sz);
    ASSERT_EQ('f', output[0]);
    ASSERT_EQ('o', output[1]);
    ASSERT_EQ('o', output[2]);
    ASSERT_EQ('f', output[3]);
    ASSERT_EQ('o', output[4]);
    ASSERT_EQ('o', output[5]);

    HSD_finish_res fres = heatshrink_decoder_finish(hsd);
    ASSERT_EQ(HSDR_FINISH_DONE, fres);

    heatshrink_decoder_free(hsd);
    PASS();
}
Example #4
0
int decode_data
(uint8_t * data, int sz)
{   
    size_t count = 0;
    size_t desz = sz + sz / 2 + 4;
    int sunk = 0;
    int polled = 0;
    uint8_t *decomp = malloc(desz);

    while (sunk < sz) {
        heatshrink_decoder_sink(&hsd, &data[sunk], sz - sunk, &count);
        sunk += count;
        HSD_poll_res pres;

        do {
            pres = heatshrink_decoder_poll(&hsd, &decomp[polled], desz - polled, &count);
            polled += count;
        } while (pres == HSDR_POLL_MORE);

        if (sunk == sz) {
            HSD_finish_res fres = heatshrink_decoder_finish(&hsd);
        }
    }
    
    fprintf(stdout, "%s", (char *) decomp);
    free(decomp);
}
TEST decoder_poll_should_expand_short_literal_and_backref_when_fed_input_byte_by_byte() {
    uint8_t input[] = {0xb3, 0x5b, 0xed, 0xe0, 0x40, 0x80}; //"foofoo"
    uint8_t output[7];
    heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 7, 7);
    memset(output, 0, sizeof(*output));
    uint16_t count = 0;
    
    HSD_sink_res sres;
    for (int i=0; i<6; i++) {
        sres = heatshrink_decoder_sink(hsd, &input[i], 1, &count);
        ASSERT_EQ(HSDR_SINK_OK, sres);
    }
    heatshrink_decoder_finish(hsd);

    uint16_t out_sz = 0;
    HSD_poll_res pres = heatshrink_decoder_poll(hsd, output, 7, &out_sz);
    ASSERT_EQ(6, out_sz);
    ASSERT_EQ(HSDR_POLL_EMPTY, pres);
    ASSERT_EQ('f', output[0]);
    ASSERT_EQ('o', output[1]);
    ASSERT_EQ('o', output[2]);
    ASSERT_EQ('f', output[3]);
    ASSERT_EQ('o', output[4]);
    ASSERT_EQ('o', output[5]);

    heatshrink_decoder_free(hsd);
    PASS();
}
TEST decoder_sink_should_reject_null_count_pointer() {
    uint8_t input[] = {0,1,2,3,4,5};
    heatshrink_decoder *hsd = heatshrink_decoder_alloc(256,
        HEATSHRINK_MIN_WINDOW_BITS, 4);
    ASSERT_EQ(HSDR_SINK_ERROR_NULL, heatshrink_decoder_sink(hsd, input, 6, NULL));
    heatshrink_decoder_free(hsd);
    PASS();
}
TEST decoder_sink_should_reject_excessively_large_input() {
    uint8_t input[] = {0,1,2,3,4,5};
    heatshrink_decoder *hsd = heatshrink_decoder_alloc(1,
        HEATSHRINK_MIN_WINDOW_BITS, 4);
    uint16_t count = 0;
    // Sink as much as will fit
    HSD_sink_res res = heatshrink_decoder_sink(hsd, input, 6, &count);
    ASSERT_EQ(HSDR_SINK_OK, res);
    ASSERT_EQ(1, count);

    // And now, no more should fit.
    res = heatshrink_decoder_sink(hsd, &input[count], sizeof(input) - count, &count);
    ASSERT_EQ(HSDR_SINK_FULL, res);
    ASSERT_EQ(0, count);
    heatshrink_decoder_free(hsd);
    PASS();
}
TEST decoder_sink_should_reject_null_input_pointer() {
    heatshrink_decoder *hsd = heatshrink_decoder_alloc(256,
        HEATSHRINK_MIN_WINDOW_BITS, 4);
    uint16_t count = 0;
    ASSERT_EQ(HSDR_SINK_ERROR_NULL, heatshrink_decoder_sink(hsd, NULL, 6, &count));
    heatshrink_decoder_free(hsd);
    PASS();
}
TEST decoder_sink_should_sink_data_when_preconditions_hold() {
    uint8_t input[] = {0,1,2,3,4,5};
    heatshrink_decoder *hsd = heatshrink_decoder_alloc(256,
        HEATSHRINK_MIN_WINDOW_BITS, 4);
    uint16_t count = 0;
    HSD_sink_res res = heatshrink_decoder_sink(hsd, input, 6, &count);
    ASSERT_EQ(HSDR_SINK_OK, res);
    ASSERT_EQ(hsd->input_size, 6);
    ASSERT_EQ(hsd->input_index, 0);
    heatshrink_decoder_free(hsd);
    PASS();
}
Example #10
0
/** gets data from callback, writes it into array if nonzero. Returns total length */
uint32_t heatshrink_decode(int (*callback)(uint32_t *cbdata), uint32_t *cbdata, unsigned char *data) {
  heatshrink_decoder hsd;
  uint8_t inBuf[BUFFERSIZE];
  uint8_t outBuf[BUFFERSIZE];
  heatshrink_decoder_reset(&hsd);

  size_t count = 0;
  size_t sunk = 0;
  size_t polled = 0;
  int lastByte = 0;
  size_t inBufCount = 0;
  while (lastByte >= 0 || inBufCount>0) {
    // Read data from flash
    while (inBufCount<BUFFERSIZE && lastByte>=0) {
      lastByte = callback(cbdata);
      if (lastByte >= 0)
        inBuf[inBufCount++] = (uint8_t)lastByte;
    }
    // decode
    bool ok = heatshrink_decoder_sink(&hsd, inBuf, inBufCount, &count) >= 0;
    // if not all the data was read, shift what's left to the start of our buffer
    if (count < inBufCount) {
      size_t i;
      for (i=count;i<inBufCount;i++)
        inBuf[i-count] = inBuf[i];
    }
    inBufCount -= count;
    assert(ok);NOT_USED(ok);
    sunk += count;
    if (lastByte < 0) {
      heatshrink_decoder_finish(&hsd);
    }

    HSE_poll_res pres;
    do {
      pres = heatshrink_decoder_poll(&hsd, outBuf, sizeof(outBuf), &count);
      assert(pres >= 0);
      if (data) memcpy(&data[polled], outBuf, count);
      polled += count;
    } while (pres == HSER_POLL_MORE);
    assert(pres == HSER_POLL_EMPTY);
    if (lastByte < 0) {
      heatshrink_decoder_finish(&hsd);
    }
  }
  return (uint32_t)polled;
}
TEST decoder_poll_should_suspend_if_out_of_space_in_output_buffer_during_literal_expansion() {
    uint8_t input[] = {0xb3, 0x5b, 0xed, 0xe0, 0x40, 0x80};
    uint8_t output[1];
    heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 7, 7);
    uint16_t count = 0;
    
    HSD_sink_res sres = heatshrink_decoder_sink(hsd, input, sizeof(input), &count);
    ASSERT_EQ(HSDR_SINK_OK, sres);

    uint16_t out_sz = 0;
    HSD_poll_res pres = heatshrink_decoder_poll(hsd, output, 1, &out_sz);
    ASSERT_EQ(HSDR_POLL_MORE, pres);
    ASSERT_EQ(1, out_sz);
    ASSERT_EQ('f', output[0]);

    heatshrink_decoder_free(hsd);
    PASS();
}
TEST decoder_poll_should_expand_short_literal() {
    uint8_t input[] = {0xb3, 0x5b, 0xed, 0xe0 }; //"foo"
    uint8_t output[4];
    heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 7, 3);
    uint16_t count = 0;

    HSD_sink_res sres = heatshrink_decoder_sink(hsd, input, sizeof(input), &count);
    ASSERT_EQ(HSDR_SINK_OK, sres);

    uint16_t out_sz = 0;
    HSD_poll_res pres = heatshrink_decoder_poll(hsd, output, 4, &out_sz);
    ASSERT_EQ(HSDR_POLL_EMPTY, pres);
    ASSERT_EQ(3, out_sz);
    ASSERT_EQ('f', output[0]);
    ASSERT_EQ('o', output[1]);
    ASSERT_EQ('o', output[2]);

    heatshrink_decoder_free(hsd);
    PASS();
}
TEST data_with_simple_repetition_should_match_with_absurdly_tiny_buffers() {
    heatshrink_encoder *hse = heatshrink_encoder_alloc(8, 3);
    heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 8, 3);
    uint8_t input[] = {'a', 'b', 'c', 'a', 'b', 'c', 'd', 'a', 'b',
                       'c', 'd', 'e', 'a', 'b', 'c', 'd', 'e', 'f',
                       'a', 'b', 'c', 'd', 'e', 'f', 'g', 'a', 'b',
                       'c', 'd', 'e', 'f', 'g', 'h'};
    uint8_t comp[60];
    uint8_t decomp[60];
    uint16_t count = 0;
    int log = 0;

    if (log) dump_buf("input", input, sizeof(input));
    for (int i=0; i<sizeof(input); i++) {
        ASSERT(heatshrink_encoder_sink(hse, &input[i], 1, &count) >= 0);
    }
    ASSERT_EQ(HSER_FINISH_MORE, heatshrink_encoder_finish(hse));

    uint16_t packed_count = 0;
    do {
        ASSERT(heatshrink_encoder_poll(hse, &comp[packed_count], 1, &count) >= 0);
        packed_count += count;
    } while (heatshrink_encoder_finish(hse) == HSER_FINISH_MORE);

    if (log) dump_buf("comp", comp, packed_count);
    for (int i=0; i<packed_count; i++) {
        HSD_sink_res sres = heatshrink_decoder_sink(hsd, &comp[i], 1, &count);
        //printf("sres is %d\n", sres);
        ASSERT(sres >= 0);
    }

    for (int i=0; i<sizeof(input); i++) {
        ASSERT(heatshrink_decoder_poll(hsd, &decomp[i], 1, &count) >= 0);
    }

    if (log) dump_buf("decomp", decomp, sizeof(input));
    for (int i=0; i<sizeof(input); i++) ASSERT_EQ(input[i], decomp[i]);
    heatshrink_encoder_free(hse);
    heatshrink_decoder_free(hsd);
    PASS();
}
TEST decoder_poll_should_expand_short_self_overlapping_backref() {
    /* "aaaaa" == (literal, 1), ('a'), (backref, 1 back, 4 bytes) */
    uint8_t input[] = {0xb0, 0x80, 0x01, 0x80};
    uint8_t output[6];
    uint8_t expected[] = {'a', 'a', 'a', 'a', 'a'};
    heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 8, 7);
    uint16_t count = 0;
    
    HSD_sink_res sres = heatshrink_decoder_sink(hsd, input, sizeof(input), &count);
    ASSERT_EQ(HSDR_SINK_OK, sres);

    uint16_t out_sz = 0;
    (void)heatshrink_decoder_poll(hsd, output, sizeof(output), &out_sz);

    if (0) dump_buf("output", output, out_sz);
    ASSERT_EQ(sizeof(expected), out_sz);
    for (int i=0; i<sizeof(expected); i++) ASSERT_EQ(expected[i], output[i]);

    heatshrink_decoder_free(hsd);
    PASS();
}
TEST data_without_duplication_should_match_with_absurdly_tiny_buffers(void) {
    heatshrink_encoder *hse = heatshrink_encoder_alloc(8, 3);
    heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 8, 3);
    uint8_t input[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
                       'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
                       's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
    uint8_t comp[60];
    uint8_t decomp[60];
    size_t count = 0;
    int log = 0;

    if (log) dump_buf("input", input, sizeof(input));
    for (uint32_t i=0; i<sizeof(input); i++) {
        ASSERT(heatshrink_encoder_sink(hse, &input[i], 1, &count) >= 0);
    }
    ASSERT_EQ(HSER_FINISH_MORE, heatshrink_encoder_finish(hse));

    size_t packed_count = 0;
    do {
        ASSERT(heatshrink_encoder_poll(hse, &comp[packed_count], 1, &count) >= 0);
        packed_count += count;
    } while (heatshrink_encoder_finish(hse) == HSER_FINISH_MORE);

    if (log) dump_buf("comp", comp, packed_count);
    for (uint32_t i=0; i<packed_count; i++) {
        HSD_sink_res sres = heatshrink_decoder_sink(hsd, &comp[i], 1, &count);
        //printf("sres is %d\n", sres);
        ASSERT(sres >= 0);
    }

    for (uint32_t i=0; i<sizeof(input); i++) {
        ASSERT(heatshrink_decoder_poll(hsd, &decomp[i], 1, &count) >= 0);
    }

    if (log) dump_buf("decomp", decomp, sizeof(input));
    for (uint32_t i=0; i<sizeof(input); i++) ASSERT_EQ(input[i], decomp[i]);
    heatshrink_encoder_free(hse);
    heatshrink_decoder_free(hsd);
    PASS();
}
Example #16
0
int main(int argc, char *argv[])
{	
	int data_size = atoi(argv[argc - 1]);
	uint8_t data[data_size];

	for (int i = 0; i < data_size; ++i) {
		data[i] = (uint8_t) argv[1][i];
	}
	
	size_t count = 0;
	size_t decomp_sz = data_size + (data_size / 2) + 4;
	int sunk = 0;
	int polled = 0;
	uint8_t *decomp = malloc(decomp_sz);

	while (sunk < data_size) {
		heatshrink_decoder_sink(&hsd, &data[sunk], data_size - sunk, &count);
		sunk += count;
		HSD_poll_res pres;
		do {
			pres = heatshrink_decoder_poll(&hsd, &decomp[polled],
				decomp_sz - polled, &count);
			printf("%zu\n", count);
			polled += count;
		} while (pres == HSDR_POLL_MORE);
		if (sunk == data_size) {
			HSD_finish_res fres = heatshrink_decoder_finish(&hsd);
		}
	}

	uint8_t buf[polled];
	for (int i = 0; i < polled; ++i) {
		buf[i] = decomp[i];
		printf("%c", buf[i]);
	}
	buf[polled] = '\0';
	printf("%s", buf);
	free(decomp);
	return 0;
}
TEST decoder_poll_should_suspend_if_out_of_space_in_output_buffer_during_backref_expansion() {
    uint8_t input[] = {0xb3, 0x5b, 0xed, 0xe0, 0x40, 0x80}; //"foofoo"
    uint8_t output[4];
    heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 7, 7);
    memset(output, 0, sizeof(*output));
    uint16_t count = 0;
    
    HSD_sink_res sres = heatshrink_decoder_sink(hsd, input, 6, &count);
    ASSERT_EQ(HSDR_SINK_OK, sres);

    uint16_t out_sz = 0;
    HSD_poll_res pres = heatshrink_decoder_poll(hsd, output, 4, &out_sz);
    ASSERT_EQ(HSDR_POLL_MORE, pres);
    ASSERT_EQ(4, out_sz);
    ASSERT_EQ('f', output[0]);
    ASSERT_EQ('o', output[1]);
    ASSERT_EQ('o', output[2]);
    ASSERT_EQ('f', output[3]);

    heatshrink_decoder_free(hsd);
    PASS();
}
TEST decoder_should_not_get_stuck_with_finish_yielding_MORE_but_0_bytes_output_from_poll(void) {
    uint8_t input[512];
    memset(input, 0xff, 256);

    uint8_t output[1024];
    heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 8, 4);
    ASSERT(hsd);

    /* Confirm that no byte of trailing context can lead to
     * heatshrink_decoder_finish erroneously returning HSDR_FINISH_MORE
     * when heatshrink_decoder_poll will yield 0 bytes.
     *
     * Before 0.3.1, a final byte of 0xFF could potentially cause
     * this to happen, if at exactly the byte boundary. */
    for (uint16_t byte = 0; byte < 256; byte++) {
        for (int i = 1; i < 512; i++) {
            input[i] = byte;
            heatshrink_decoder_reset(hsd);
            memset(output, 0, sizeof(*output));
            size_t count = 0;
            
            HSD_sink_res sres = heatshrink_decoder_sink(hsd, input, i, &count);
            ASSERT_EQ(HSDR_SINK_OK, sres);
            
            size_t out_sz = 0;
            HSD_poll_res pres = heatshrink_decoder_poll(hsd, output, sizeof(output), &out_sz);
            ASSERT_EQ(HSDR_POLL_EMPTY, pres);
            
            HSD_finish_res fres = heatshrink_decoder_finish(hsd);
            ASSERT_EQ(HSDR_FINISH_DONE, fres);
            input[i] = 0xff;
        }
    }

    heatshrink_decoder_free(hsd);
    PASS();
}
Example #19
0
static int decoder_sink_read(config *cfg, heatshrink_decoder *hsd,
        uint8_t *data, size_t data_sz) {
    io_handle *out = cfg->out;
    size_t sink_sz = 0;
    size_t poll_sz = 0;
    size_t out_sz = 4096;
    uint8_t out_buf[out_sz];
    memset(out_buf, 0, out_sz);

    HSD_sink_res sres;
    HSD_poll_res pres;
    HSD_finish_res fres;

    size_t sunk = 0;
    do {
        if (data_sz > 0) {
            sres = heatshrink_decoder_sink(hsd, &data[sunk], data_sz - sunk, &sink_sz);
            if (sres < 0) { die("sink"); }
            sunk += sink_sz;
        }

        do {
            pres = heatshrink_decoder_poll(hsd, out_buf, out_sz, &poll_sz);
            if (pres < 0) { die("poll"); }
            if (handle_sink(out, poll_sz, out_buf) < 0) die("handle_sink");
        } while (pres == HSDR_POLL_MORE);
        
        if (data_sz == 0 && poll_sz == 0) {
            fres = heatshrink_decoder_finish(hsd);
            if (fres < 0) { die("finish"); }
            if (fres == HSDR_FINISH_DONE) { return 1; }
        }
    } while (sunk < data_sz);

    return 0;
}
TEST decoder_sink_should_reject_null_hsd_pointer() {
    uint8_t input[] = {0,1,2,3,4,5};
    uint16_t count = 0;
    ASSERT_EQ(HSDR_SINK_ERROR_NULL, heatshrink_decoder_sink(NULL, input, 6, &count));
    PASS();
}
Example #21
0
static ZEND_FUNCTION(hs_decompress)
{
    zval *data;
    size_t data_len;
    size_t sink_sz = 0;
    size_t poll_sz = 0;
    HSD_sink_res sres;
    HSD_poll_res pres;
    HSD_finish_res fres;
    //Default window_sz2 and lookahead_sz2 values
    uint8_t window = DEF_WINDOW_SZ2;
    uint8_t lookahead = DEF_LOOKAHEAD_SZ2;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
                              "z|ll", &data, &window,
                              &lookahead) == FAILURE) {
        RETURN_FALSE;
    }

    if (Z_TYPE_P(data) != IS_STRING) {
        zend_error(E_WARNING, "hs_decompress : expects parameter to be string.");
        RETURN_FALSE;
    }

    //Allocate decoder
    //TODO: How we calculate input buffer size?
    heatshrink_decoder *hsd = heatshrink_decoder_alloc(DEF_DECODER_INPUT_BUFFER_SIZE, window, lookahead);
    if (hsd == NULL) {
        zend_error(E_WARNING, "hs_decompress : heatshrink_decoder_alloc error. Check window and lookahead values.");
        RETURN_FALSE;
    }

    data_len = Z_STRLEN_P(data);
    //TODO: think of better way to compute outbuff size
    size_t outbuff_len = data_len*2;
    unsigned char *outbuff = (unsigned char *) emalloc(outbuff_len);

    size_t sunk = 0;
    do {
        sres = heatshrink_decoder_sink(hsd, (unsigned char *) &(Z_STRVAL_P(data)[sunk]), data_len - sunk, &sink_sz);
        if (sres < 0) {
            zend_error(E_WARNING, "hs_decompress : heatshrink_decoder_sink error");
            efree(outbuff);
            heatshrink_decoder_free(hsd);
            RETURN_FALSE;
        }
        sunk += sink_sz;
        
        do {
            do {
                pres = heatshrink_decoder_poll(hsd, &outbuff[poll_sz], outbuff_len - poll_sz, &sink_sz);

                if (pres < 0) {
                    zend_error(E_WARNING, "hs_compress : heatshrink_decoder_poll error");
                    efree(outbuff);
                    heatshrink_decoder_free(hsd);
                    RETURN_FALSE;
                }
                poll_sz += sink_sz;
                if (poll_sz == outbuff_len && pres == HSDR_POLL_MORE) {
                    //TODO: think of bettery way to compute buffer reallocation size
                    outbuff_len += data_len/2;
                    outbuff = erealloc(outbuff, outbuff_len);
                }

            } while (pres == HSDR_POLL_MORE);
            
            if (sunk == data_len) {
                fres = heatshrink_decoder_finish(hsd);
                if (fres < 0) {
                    zend_error(E_WARNING, "hs_decompress : heatshrink_decoder_finish error");
                    efree(outbuff);
                    heatshrink_decoder_free(hsd);
                    RETURN_FALSE;
                }
                if (fres == HSDR_FINISH_DONE) {
                    RETVAL_STRINGL((char *) outbuff, poll_sz, 1);
                    efree(outbuff);
                    heatshrink_decoder_free(hsd);
                    return;
                }
            }
        } while (fres == HSDR_FINISH_MORE);
    } while (sunk < data_len);

    zend_error(E_WARNING, "hs_decompress : general error");
    efree(outbuff);
    heatshrink_decoder_free(hsd);
    RETURN_FALSE;
}
static int compress_and_expand_and_check(uint8_t *input, uint32_t input_size, cfg_info *cfg) {
    heatshrink_encoder *hse = heatshrink_encoder_alloc(cfg->window_sz2,
        cfg->lookahead_sz2);
    heatshrink_decoder *hsd = heatshrink_decoder_alloc(cfg->decoder_input_buffer_size,
        cfg->window_sz2, cfg->lookahead_sz2);
    size_t comp_sz = input_size + (input_size/2) + 4;
    size_t decomp_sz = input_size + (input_size/2) + 4;
    uint8_t *comp = malloc(comp_sz);
    uint8_t *decomp = malloc(decomp_sz);
    if (comp == NULL) FAILm("malloc fail");
    if (decomp == NULL) FAILm("malloc fail");
    memset(comp, 0, comp_sz);
    memset(decomp, 0, decomp_sz);

    uint16_t count = 0;

    if (cfg->log_lvl > 1) {
        printf("\n^^ COMPRESSING\n");
        dump_buf("input", input, input_size);
    }

    uint32_t sunk = 0;
    uint32_t polled = 0;
    while (sunk < input_size) {
        ASSERT(heatshrink_encoder_sink(hse, &input[sunk], input_size - sunk, &count) >= 0);
        sunk += count;
        if (cfg->log_lvl > 1) printf("^^ sunk %d\n", count);
        if (sunk == input_size) {
            ASSERT_EQ(HSER_FINISH_MORE, heatshrink_encoder_finish(hse));
        }

        HSE_poll_res pres;
        do {                    /* "turn the crank" */
            pres = heatshrink_encoder_poll(hse, &comp[polled], comp_sz - polled, &count);
            ASSERT(pres >= 0);
            polled += count;
            if (cfg->log_lvl > 1) printf("^^ polled %d\n", count);
        } while (pres == HSER_POLL_MORE);
        ASSERT_EQ(HSER_POLL_EMPTY, pres);
        if (polled >= comp_sz) FAILm("compression should never expand that much");
        if (sunk == input_size) {
            ASSERT_EQ(HSER_FINISH_DONE, heatshrink_encoder_finish(hse));
        }
    }
    if (cfg->log_lvl > 0) printf("in: %u compressed: %u ", input_size, polled);
    uint32_t compressed_size = polled;
    sunk = 0;
    polled = 0;
    
    if (cfg->log_lvl > 1) {
        printf("\n^^ DECOMPRESSING\n");
        dump_buf("comp", comp, compressed_size);
    }
    while (sunk < compressed_size) {
        ASSERT(heatshrink_decoder_sink(hsd, &comp[sunk], compressed_size - sunk, &count) >= 0);
        sunk += count;
        if (cfg->log_lvl > 1) printf("^^ sunk %d\n", count);
        if (sunk == compressed_size) {
            ASSERT_EQ(HSDR_FINISH_MORE, heatshrink_decoder_finish(hsd));
        }

        HSD_poll_res pres;
        do {
            pres = heatshrink_decoder_poll(hsd, &decomp[polled],
                decomp_sz - polled, &count);
            ASSERT(pres >= 0);
            ASSERT(count > 0);
            polled += count;
            if (cfg->log_lvl > 1) printf("^^ polled %d\n", count);
        } while (pres == HSDR_POLL_MORE);
        ASSERT_EQ(HSDR_POLL_EMPTY, pres);
        if (sunk == compressed_size) {
            HSD_finish_res fres = heatshrink_decoder_finish(hsd);
            ASSERT_EQ(HSDR_FINISH_DONE, fres);
        }

        if (polled > input_size) {
            printf("\nExpected %d, got %d\n", input_size, polled);
            FAILm("Decompressed data is larger than original input");
        }
    }
    if (cfg->log_lvl > 0) printf("decompressed: %u\n", polled);
    if (polled != input_size) {
        FAILm("Decompressed length does not match original input length");
    }

    if (cfg->log_lvl > 1) dump_buf("decomp", decomp, polled);
    for (int i=0; i<input_size; i++) {
        if (input[i] != decomp[i]) {
            printf("*** mismatch at %d\n", i);
            if (0) {
                for (int j=0; j<=/*i*/ input_size; j++) {
                    printf("in[%d] == 0x%02x ('%c') => out[%d] == 0x%02x ('%c')  %c\n",
                        j, input[j], isprint(input[j]) ? input[j] : '.',
                        j, decomp[j], isprint(decomp[j]) ? decomp[j] : '.',
                        input[j] == decomp[j] ? ' ' : 'X');
                }
            }
        }
        ASSERT_EQ(input[i], decomp[i]);
    }
    free(comp);
    free(decomp);
    heatshrink_encoder_free(hse);
    heatshrink_decoder_free(hsd);
    PASS();
}