static void do_indexing(heatshrink_encoder *hse) { #if HEATSHRINK_USE_INDEX /* Build an index array I that contains flattened linked lists * for the previous instances of every byte in the buffer. * * For example, if buf[200] == 'x', then index[200] will either * be an offset i such that buf[i] == 'x', or a negative offset * to indicate end-of-list. This significantly speeds up matching, * while only using sizeof(uint16_t)*sizeof(buffer) bytes of RAM. * * Future optimization options: * 1. Since any negative value represents end-of-list, the other * 15 bits could be used to improve the index dynamically. * * 2. Likewise, the last lookahead_sz bytes of the index will * not be usable, so temporary data could be stored there to * dynamically improve the index. * */ struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse); int16_t last[256]; memset(last, 0xFF, sizeof(last)); uint8_t * const data = hse->buffer; int16_t * const index = hsi->index; const uint16_t input_offset = get_input_offset(hse); const uint16_t end = input_offset + hse->input_size; for (uint16_t i=0; i<end; i++) { uint8_t v = data[i]; int16_t lv = last[v]; index[i] = lv; last[v] = i; } #else (void)hse; #endif }
/* Return the longest match for the bytes at buf[end:end+maxlen] between * buf[start] and buf[end-1]. If no match is found, return -1. */ static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start, uint16_t end, const uint16_t maxlen, uint16_t *match_length) { LOG("-- scanning for match of buf[%u:%u] between buf[%u:%u] (max %u bytes)\n", end, end + maxlen, start, end + maxlen - 1, maxlen); uint8_t *buf = hse->buffer; uint16_t match_maxlen = 0; uint16_t match_index = MATCH_NOT_FOUND; uint16_t len = 0; uint8_t * const needlepoint = &buf[end]; #if HEATSHRINK_USE_INDEX struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse); int16_t pos = hsi->index[end]; while (pos - (int16_t)start >= 0) { uint8_t * const pospoint = &buf[pos]; len = 0; /* Only check matches that will potentially beat the current maxlen. * This is redundant with the index if match_maxlen is 0, but the * added branch overhead to check if it == 0 seems to be worse. */ if (pospoint[match_maxlen] != needlepoint[match_maxlen]) { pos = hsi->index[pos]; continue; } for (len = 1; len < maxlen; len++) { if (pospoint[len] != needlepoint[len]) break; } if (len > match_maxlen) { match_maxlen = len; match_index = pos; if (len == maxlen) { break; } /* won't find better */ } pos = hsi->index[pos]; } #else for (int16_t pos=end - 1; pos - (int16_t)start >= 0; pos--) { uint8_t * const pospoint = &buf[pos]; if ((pospoint[match_maxlen] == needlepoint[match_maxlen]) && (*pospoint == *needlepoint)) { for (len=1; len<maxlen; len++) { if (0) { LOG(" --> cmp buf[%d] == 0x%02x against %02x (start %u)\n", pos + len, pospoint[len], needlepoint[len], start); } if (pospoint[len] != needlepoint[len]) { break; } } if (len > match_maxlen) { match_maxlen = len; match_index = pos; if (len == maxlen) { break; } /* don't keep searching */ } } } #endif const size_t break_even_point = (1 + HEATSHRINK_ENCODER_WINDOW_BITS(hse) + HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse)); /* Instead of comparing break_even_point against 8*match_maxlen, * compare match_maxlen against break_even_point/8 to avoid * overflow. Since MIN_WINDOW_BITS and MIN_LOOKAHEAD_BITS are 4 and * 3, respectively, break_even_point/8 will always be at least 1. */ if (match_maxlen > (break_even_point / 8)) { LOG("-- best match: %u bytes at -%u\n", match_maxlen, end - match_index); *match_length = match_maxlen; return end - match_index; } LOG("-- none found\n"); return MATCH_NOT_FOUND; }
/* Return the longest match for the bytes at buf[end:end+maxlen] between * buf[start] and buf[end-1]. If no match is found, return -1. */ static uint16_t ICACHE_FLASH_ATTR find_longest_match(heatshrink_encoder *hse, uint16_t start, uint16_t end, const uint16_t maxlen, uint16_t *match_length) { LOG("-- scanning for match of buf[%u:%u] between buf[%u:%u] (max %u bytes)\n", end, end + maxlen, start, end + maxlen - 1, maxlen); uint8_t *buf = hse->buffer; uint16_t match_maxlen = 0; uint16_t match_index = MATCH_NOT_FOUND; const uint16_t break_even_point = 3; uint16_t len = 0; uint8_t * const needlepoint = &buf[end]; #if HEATSHRINK_USE_INDEX struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse); int16_t pos = hsi->index[end]; while (pos >= start) { uint8_t * const pospoint = &buf[pos]; len = 0; /* Only check matches that will potentially beat the current maxlen. * This is redundant with the index if match_maxlen is 0, but the * added branch overhead to check if it == 0 seems to be worse. */ if (pospoint[match_maxlen] != needlepoint[match_maxlen]) { pos = hsi->index[pos]; continue; } for (len = 1; len < maxlen; len++) { if (pospoint[len] != needlepoint[len]) break; } if (len > match_maxlen) { match_maxlen = len; match_index = pos; if (len == maxlen) { break; } /* won't find better */ } pos = hsi->index[pos]; } #else for (int16_t pos=end - 1; pos >= start; pos--) { uint8_t * const pospoint = &buf[pos]; if ((pospoint[match_maxlen] == needlepoint[match_maxlen]) && (*pospoint == *needlepoint)) { for (len=1; len<maxlen; len++) { if (0) { LOG(" --> cmp buf[%d] == 0x%02x against %02x (start %u)\n", pos + len, pospoint[len], needlepoint[len], start); } if (pospoint[len] != needlepoint[len]) { break; } } if (len > match_maxlen) { match_maxlen = len; match_index = pos; if (len == maxlen) { break; } /* don't keep searching */ } } } #endif if (match_maxlen >= break_even_point) { LOG("-- best match: %u bytes at -%u\n", match_maxlen, end - match_index); *match_length = match_maxlen; return end - match_index; } LOG("-- none found\n"); return MATCH_NOT_FOUND; }