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;
}
Esempio n. 3
0
/* 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;
}