/* * bt_matchfinder.h * * Copyright (c) 2014 Eric Biggers. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include "wimlib/lz_extend.h" #include "wimlib/lz_hash3.h" #include "wimlib/matchfinder_common.h" #include "wimlib/unaligned.h" #ifndef BT_MATCHFINDER_HASH_ORDER # if MATCHFINDER_WINDOW_ORDER < 14 # define BT_MATCHFINDER_HASH_ORDER 14 # else # define BT_MATCHFINDER_HASH_ORDER 15 # endif #endif #define BT_MATCHFINDER_HASH_LENGTH (1UL << BT_MATCHFINDER_HASH_ORDER) #define BT_MATCHFINDER_TOTAL_LENGTH \ (BT_MATCHFINDER_HASH_LENGTH + (2UL * MATCHFINDER_WINDOW_SIZE)) struct bt_matchfinder { union { pos_t mf_data[BT_MATCHFINDER_TOTAL_LENGTH]; struct { pos_t hash_tab[BT_MATCHFINDER_HASH_LENGTH]; pos_t child_tab[2UL * MATCHFINDER_WINDOW_SIZE]; }; }; } _aligned_attribute(MATCHFINDER_ALIGNMENT); static inline void bt_matchfinder_init(struct bt_matchfinder *mf) { matchfinder_init(mf->hash_tab, BT_MATCHFINDER_HASH_LENGTH); } #if MATCHFINDER_IS_SLIDING static inline void bt_matchfinder_slide_window(struct bt_matchfinder *mf) { matchfinder_rebase(mf->mf_data, BT_MATCHFINDER_TOTAL_LENGTH); } #endif static inline unsigned bt_matchfinder_get_matches(struct bt_matchfinder * const restrict mf, const u8 * const in_base, const u8 * const in_next, const unsigned min_len, const unsigned max_len, const unsigned nice_len, const unsigned max_search_depth, unsigned long *prev_hash, struct lz_match * const restrict matches) { struct lz_match *lz_matchptr = matches; unsigned depth_remaining = max_search_depth; unsigned hash; pos_t cur_match; const u8 *matchptr; unsigned best_len; pos_t *pending_lt_ptr, *pending_gt_ptr; unsigned best_lt_len, best_gt_len; unsigned len; pos_t *children; if (unlikely(max_len < LZ_HASH_REQUIRED_NBYTES + 1)) return 0; hash = *prev_hash; *prev_hash = lz_hash(in_next + 1, BT_MATCHFINDER_HASH_ORDER); prefetch(&mf->hash_tab[*prev_hash]); cur_match = mf->hash_tab[hash]; mf->hash_tab[hash] = in_next - in_base; best_len = min_len - 1; pending_lt_ptr = &mf->child_tab[(in_next - in_base) << 1]; pending_gt_ptr = &mf->child_tab[((in_next - in_base) << 1) + 1]; best_lt_len = 0; best_gt_len = 0; for (;;) { if (!matchfinder_match_in_window(cur_match, in_base, in_next) || !depth_remaining--) { *pending_lt_ptr = MATCHFINDER_INITVAL; *pending_gt_ptr = MATCHFINDER_INITVAL; return lz_matchptr - matches; } matchptr = &in_base[cur_match]; len = min(best_lt_len, best_gt_len); children = &mf->child_tab[(unsigned long) matchfinder_slot_for_match(cur_match) << 1]; if (matchptr[len] == in_next[len]) { len = lz_extend(in_next, matchptr, len + 1, max_len); if (len > best_len) { best_len = len; lz_matchptr->length = len; lz_matchptr->offset = in_next - matchptr; lz_matchptr++; if (len >= nice_len) { *pending_lt_ptr = children[0]; *pending_gt_ptr = children[1]; return lz_matchptr - matches; } } } if (matchptr[len] < in_next[len]) { *pending_lt_ptr = cur_match; pending_lt_ptr = &children[1]; cur_match = *pending_lt_ptr; best_lt_len = len; } else { *pending_gt_ptr = cur_match; pending_gt_ptr = &children[0]; cur_match = *pending_gt_ptr; best_gt_len = len; } } } static inline void bt_matchfinder_skip_position(struct bt_matchfinder * const restrict mf, const u8 * const in_base, const u8 * const in_next, const u8 * const in_end, const unsigned nice_len, const unsigned max_search_depth, unsigned long *prev_hash) { unsigned depth_remaining = max_search_depth; unsigned hash; pos_t cur_match; const u8 *matchptr; pos_t *pending_lt_ptr, *pending_gt_ptr; unsigned best_lt_len, best_gt_len; unsigned len; pos_t *children; if (unlikely(in_end - in_next < LZ_HASH_REQUIRED_NBYTES + 1)) return; hash = *prev_hash; *prev_hash = lz_hash(in_next + 1, BT_MATCHFINDER_HASH_ORDER); prefetch(&mf->hash_tab[*prev_hash]); cur_match = mf->hash_tab[hash]; mf->hash_tab[hash] = in_next - in_base; depth_remaining = max_search_depth; pending_lt_ptr = &mf->child_tab[(in_next - in_base) << 1]; pending_gt_ptr = &mf->child_tab[((in_next - in_base) << 1) + 1]; best_lt_len = 0; best_gt_len = 0; for (;;) { if (!matchfinder_match_in_window(cur_match, in_base, in_next) || !depth_remaining--) { *pending_lt_ptr = MATCHFINDER_INITVAL; *pending_gt_ptr = MATCHFINDER_INITVAL; return; } matchptr = &in_base[cur_match]; len = min(best_lt_len, best_gt_len); children = &mf->child_tab[(unsigned long) matchfinder_slot_for_match(cur_match) << 1]; if (matchptr[len] == in_next[len]) { len = lz_extend(in_next, matchptr, len + 1, nice_len); if (len == nice_len) { *pending_lt_ptr = children[0]; *pending_gt_ptr = children[1]; return; } } if (matchptr[len] < in_next[len]) { *pending_lt_ptr = cur_match; pending_lt_ptr = &children[1]; cur_match = *pending_lt_ptr; best_lt_len = len; } else { *pending_gt_ptr = cur_match; pending_gt_ptr = &children[0]; cur_match = *pending_gt_ptr; best_gt_len = len; } } }