/src/libdeflate/lib/hc_matchfinder.h
Line | Count | Source |
1 | | /* |
2 | | * hc_matchfinder.h - Lempel-Ziv matchfinding with a hash table of linked lists |
3 | | * |
4 | | * Copyright 2016 Eric Biggers |
5 | | * |
6 | | * Permission is hereby granted, free of charge, to any person |
7 | | * obtaining a copy of this software and associated documentation |
8 | | * files (the "Software"), to deal in the Software without |
9 | | * restriction, including without limitation the rights to use, |
10 | | * copy, modify, merge, publish, distribute, sublicense, and/or sell |
11 | | * copies of the Software, and to permit persons to whom the |
12 | | * Software is furnished to do so, subject to the following |
13 | | * conditions: |
14 | | * |
15 | | * The above copyright notice and this permission notice shall be |
16 | | * included in all copies or substantial portions of the Software. |
17 | | * |
18 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
19 | | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
20 | | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
21 | | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
22 | | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
23 | | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
24 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
25 | | * OTHER DEALINGS IN THE SOFTWARE. |
26 | | * |
27 | | * --------------------------------------------------------------------------- |
28 | | * |
29 | | * Algorithm |
30 | | * |
31 | | * This is a Hash Chains (hc) based matchfinder. |
32 | | * |
33 | | * The main data structure is a hash table where each hash bucket contains a |
34 | | * linked list (or "chain") of sequences whose first 4 bytes share the same hash |
35 | | * code. Each sequence is identified by its starting position in the input |
36 | | * buffer. |
37 | | * |
38 | | * The algorithm processes the input buffer sequentially. At each byte |
39 | | * position, the hash code of the first 4 bytes of the sequence beginning at |
40 | | * that position (the sequence being matched against) is computed. This |
41 | | * identifies the hash bucket to use for that position. Then, this hash |
42 | | * bucket's linked list is searched for matches. Then, a new linked list node |
43 | | * is created to represent the current sequence and is prepended to the list. |
44 | | * |
45 | | * This algorithm has several useful properties: |
46 | | * |
47 | | * - It only finds true Lempel-Ziv matches; i.e., those where the matching |
48 | | * sequence occurs prior to the sequence being matched against. |
49 | | * |
50 | | * - The sequences in each linked list are always sorted by decreasing starting |
51 | | * position. Therefore, the closest (smallest offset) matches are found |
52 | | * first, which in many compression formats tend to be the cheapest to encode. |
53 | | * |
54 | | * - Although fast running time is not guaranteed due to the possibility of the |
55 | | * lists getting very long, the worst degenerate behavior can be easily |
56 | | * prevented by capping the number of nodes searched at each position. |
57 | | * |
58 | | * - If the compressor decides not to search for matches at a certain position, |
59 | | * then that position can be quickly inserted without searching the list. |
60 | | * |
61 | | * - The algorithm is adaptable to sliding windows: just store the positions |
62 | | * relative to a "base" value that is updated from time to time, and stop |
63 | | * searching each list when the sequences get too far away. |
64 | | * |
65 | | * ---------------------------------------------------------------------------- |
66 | | * |
67 | | * Optimizations |
68 | | * |
69 | | * The main hash table and chains handle length 4+ matches. Length 3 matches |
70 | | * are handled by a separate hash table with no chains. This works well for |
71 | | * typical "greedy" or "lazy"-style compressors, where length 3 matches are |
72 | | * often only helpful if they have small offsets. Instead of searching a full |
73 | | * chain for length 3+ matches, the algorithm just checks for one close length 3 |
74 | | * match, then focuses on finding length 4+ matches. |
75 | | * |
76 | | * The longest_match() and skip_bytes() functions are inlined into the |
77 | | * compressors that use them. This isn't just about saving the overhead of a |
78 | | * function call. These functions are intended to be called from the inner |
79 | | * loops of compressors, where giving the compiler more control over register |
80 | | * allocation is very helpful. There is also significant benefit to be gained |
81 | | * from allowing the CPU to predict branches independently at each call site. |
82 | | * For example, "lazy"-style compressors can be written with two calls to |
83 | | * longest_match(), each of which starts with a different 'best_len' and |
84 | | * therefore has significantly different performance characteristics. |
85 | | * |
86 | | * Although any hash function can be used, a multiplicative hash is fast and |
87 | | * works well. |
88 | | * |
89 | | * On some processors, it is significantly faster to extend matches by whole |
90 | | * words (32 or 64 bits) instead of by individual bytes. For this to be the |
91 | | * case, the processor must implement unaligned memory accesses efficiently and |
92 | | * must have either a fast "find first set bit" instruction or a fast "find last |
93 | | * set bit" instruction, depending on the processor's endianness. |
94 | | * |
95 | | * The code uses one loop for finding the first match and one loop for finding a |
96 | | * longer match. Each of these loops is tuned for its respective task and in |
97 | | * combination are faster than a single generalized loop that handles both |
98 | | * tasks. |
99 | | * |
100 | | * The code also uses a tight inner loop that only compares the last and first |
101 | | * bytes of a potential match. It is only when these bytes match that a full |
102 | | * match extension is attempted. |
103 | | * |
104 | | * Optionally, each linked list node can store not just the next node but the |
105 | | * next several nodes ("hops"); see 'next_tab'. This makes walking the lists |
106 | | * much faster on modern CPUs, at the cost of more memory and slightly slower |
107 | | * insertion. It's worthwhile for compressors that search deeply. |
108 | | * |
109 | | * ---------------------------------------------------------------------------- |
110 | | */ |
111 | | |
112 | | #ifndef LIB_HC_MATCHFINDER_H |
113 | | #define LIB_HC_MATCHFINDER_H |
114 | | |
115 | | #include "matchfinder_common.h" |
116 | | |
117 | 6.13M | #define HC_MATCHFINDER_HASH3_ORDER 15 |
118 | 6.13M | #define HC_MATCHFINDER_HASH4_ORDER 16 |
119 | | |
120 | | #define HC_MATCHFINDER_TOTAL_HASH_SIZE \ |
121 | 1.82k | (((1UL << HC_MATCHFINDER_HASH3_ORDER) + \ |
122 | 1.82k | (1UL << HC_MATCHFINDER_HASH4_ORDER)) * sizeof(mf_pos_t)) |
123 | | |
124 | | /* |
125 | | * Type of a multi-hop link in 'next_tab'. This is 'machine_word_t', annotated |
126 | | * with 'may_alias' because matchfinder_rebase() accesses it as 'mf_pos_t'. |
127 | | */ |
128 | | #if defined(__GNUC__) || __has_attribute(may_alias) |
129 | | typedef machine_word_t __attribute__((may_alias)) hc_link_t; |
130 | | #else |
131 | | typedef machine_word_t hc_link_t; |
132 | | #endif |
133 | | |
134 | | /* The number of hops in a multi-hop link; see 'next_tab'. */ |
135 | | #define HC_MATCHFINDER_HOPS_PER_WORD \ |
136 | 13.5M | ((int)(sizeof(hc_link_t) / sizeof(mf_pos_t))) |
137 | | |
138 | | struct MATCHFINDER_ALIGNED hc_matchfinder { |
139 | | |
140 | | /* The hash table for finding length 3 matches */ |
141 | | mf_pos_t hash3_tab[1UL << HC_MATCHFINDER_HASH3_ORDER]; |
142 | | |
143 | | /* The hash table which contains the first nodes of the linked lists for |
144 | | * finding length 4+ matches */ |
145 | | mf_pos_t hash4_tab[1UL << HC_MATCHFINDER_HASH4_ORDER]; |
146 | | |
147 | | /* |
148 | | * The "links" from each linked list node to the nodes that follow it. |
149 | | * The link of the node for the sequence with position 'pos' is either |
150 | | * 'single_hop[pos]' or 'multi_hop[pos]', depending on the 'multi_hop' |
151 | | * parameter of the matchfinder functions. 'multi_hop' must be a |
152 | | * compile-time constant, and it must be the same for all calls that use |
153 | | * the same matchfinder. |
154 | | * |
155 | | * A single-hop link is just the next node. A multi-hop link is the |
156 | | * next HC_MATCHFINDER_HOPS_PER_WORD nodes, packed as 16-bit fields: the |
157 | | * next node in bits 0-15, the node after that in bits 16-31, and so on. |
158 | | * |
159 | | * Multi-hop links make the linked lists faster to walk. Walking a list |
160 | | * is otherwise a chain of dependent memory loads, where each load's |
161 | | * address comes from the previous load; and since the links don't fit |
162 | | * in the L1 cache, each step can take the full latency of an L2 cache |
163 | | * access. With N hops per link, only one in every N steps needs to |
164 | | * wait for a load. |
165 | | * |
166 | | * The extra hops are easy to maintain, as the lists are only ever |
167 | | * modified by prepending a node: the new node's link is just the old |
168 | | * first node, followed by the first N-1 hops of the old first node's |
169 | | * link. The visited nodes are exactly the same as with single-hop |
170 | | * links, so the matches found are too. However, inserting a node now |
171 | | * requires loading the old first node's link, which makes insertion |
172 | | * slower. So multi-hop links are worthwhile only when searching |
173 | | * deeply. |
174 | | * |
175 | | * Each hop is a 16-bit field, so either kind of link can be rebased |
176 | | * together with the hash tables as an array of mf_pos_t. |
177 | | * |
178 | | * This must be the last member, so that the space for multi-hop links |
179 | | * needn't be allocated when they aren't used; see |
180 | | * hc_matchfinder_size(). |
181 | | */ |
182 | | union { |
183 | | mf_pos_t single_hop[MATCHFINDER_WINDOW_SIZE]; |
184 | | hc_link_t multi_hop[MATCHFINDER_WINDOW_SIZE]; |
185 | | } next_tab; |
186 | | }; |
187 | | |
188 | | /* |
189 | | * Return the number of bytes at the start of struct hc_matchfinder that are |
190 | | * used. When multi-hop links aren't used, the rest needn't be allocated. |
191 | | */ |
192 | | static forceinline size_t |
193 | | hc_matchfinder_size(bool multi_hop) |
194 | 1.49k | { |
195 | 1.49k | return offsetof(struct hc_matchfinder, next_tab) + |
196 | 1.49k | MATCHFINDER_WINDOW_SIZE * |
197 | 1.49k | (multi_hop ? sizeof(hc_link_t) : sizeof(mf_pos_t)); |
198 | 1.49k | } |
199 | | |
200 | | /* Return the multi-hop link of the node with position @node. */ |
201 | | static forceinline hc_link_t |
202 | | hc_matchfinder_get_link(const struct hc_matchfinder *mf, mf_pos_t node) |
203 | 12.9M | { |
204 | 12.9M | return mf->next_tab.multi_hop[node & (MATCHFINDER_WINDOW_SIZE - 1)]; |
205 | 12.9M | } |
206 | | |
207 | | /* Return the last node of the multi-hop link @link. */ |
208 | | static forceinline mf_pos_t |
209 | | hc_last_hop(hc_link_t link) |
210 | 6.78M | { |
211 | 6.78M | return (mf_pos_t)(link >> (16 * (HC_MATCHFINDER_HOPS_PER_WORD - 1))); |
212 | 6.78M | } |
213 | | |
214 | | /* |
215 | | * Prepend the node for the sequence with position @cur_pos to the linked list |
216 | | * whose first node is @old_first. If @multi_hop, return the link of @old_first |
217 | | * (which is needed to build the new node's link anyway); otherwise return 0. |
218 | | */ |
219 | | static forceinline hc_link_t |
220 | | hc_matchfinder_prepend(struct hc_matchfinder *mf, u32 cur_pos, |
221 | | mf_pos_t old_first, bool multi_hop) |
222 | 6.13M | { |
223 | 6.13M | hc_link_t old_first_link; |
224 | | |
225 | 6.13M | if (!multi_hop) { |
226 | 0 | mf->next_tab.single_hop[cur_pos] = old_first; |
227 | 0 | return 0; |
228 | 0 | } |
229 | 6.13M | old_first_link = hc_matchfinder_get_link(mf, old_first); |
230 | 6.13M | mf->next_tab.multi_hop[cur_pos] = |
231 | 6.13M | (old_first_link << 16) | (u16)old_first; |
232 | 6.13M | return old_first_link; |
233 | 6.13M | } |
234 | | |
235 | | /* Prepare the matchfinder for a new input buffer. */ |
236 | | static forceinline void |
237 | | hc_matchfinder_init(struct hc_matchfinder *mf, bool multi_hop) |
238 | 1.82k | { |
239 | 1.82k | STATIC_ASSERT(HC_MATCHFINDER_TOTAL_HASH_SIZE % |
240 | 1.82k | MATCHFINDER_SIZE_ALIGNMENT == 0); |
241 | | |
242 | 1.82k | matchfinder_init((mf_pos_t *)mf, HC_MATCHFINDER_TOTAL_HASH_SIZE); |
243 | | |
244 | | /* |
245 | | * With multi-hop links, the first node inserted will have the link of |
246 | | * MATCHFINDER_INITVAL, i.e. of position 0, copied into its link. The |
247 | | * copied hops will never be visited, but make sure they're initialized |
248 | | * anyway. The rest of 'next_tab' needn't be initialized, since every |
249 | | * other link that gets read is a link that was written. |
250 | | */ |
251 | 1.82k | if (multi_hop) |
252 | 1.82k | mf->next_tab.multi_hop[0] = (hc_link_t)0x0001000100010001 * |
253 | 1.82k | (u16)MATCHFINDER_INITVAL; |
254 | 1.82k | } |
255 | | |
256 | | static forceinline void |
257 | | hc_matchfinder_slide_window(struct hc_matchfinder *mf, bool multi_hop) |
258 | 0 | { |
259 | 0 | STATIC_ASSERT(sizeof(mf->next_tab.single_hop) % |
260 | 0 | MATCHFINDER_SIZE_ALIGNMENT == 0); |
261 | 0 | STATIC_ASSERT(sizeof(*mf) % MATCHFINDER_SIZE_ALIGNMENT == 0); |
262 | | |
263 | | /* Rebase the hash tables and the part of 'next_tab' that is used. */ |
264 | 0 | matchfinder_rebase((mf_pos_t *)mf, hc_matchfinder_size(multi_hop)); |
265 | 0 | } |
266 | | |
267 | | /* |
268 | | * Find the longest match longer than 'best_len' bytes. |
269 | | * |
270 | | * @mf |
271 | | * The matchfinder structure. |
272 | | * @in_base_p |
273 | | * Location of a pointer which points to the place in the input data the |
274 | | * matchfinder currently stores positions relative to. This may be updated |
275 | | * by this function. |
276 | | * @in_next |
277 | | * Pointer to the next position in the input buffer, i.e. the sequence |
278 | | * being matched against. |
279 | | * @best_len |
280 | | * Require a match longer than this length. |
281 | | * @max_len |
282 | | * The maximum permissible match length at this position. |
283 | | * @nice_len |
284 | | * Stop searching if a match of at least this length is found. |
285 | | * Must be <= @max_len. |
286 | | * @max_search_depth |
287 | | * Limit on the number of potential matches to consider. Must be >= 1. |
288 | | * @next_hashes |
289 | | * The precomputed hash codes for the sequence beginning at @in_next. |
290 | | * These will be used and then updated with the precomputed hashcodes for |
291 | | * the sequence beginning at @in_next + 1. |
292 | | * @offset_ret |
293 | | * If a match is found, its offset is returned in this location. |
294 | | * @multi_hop |
295 | | * Whether to use multi-hop links; see 'next_tab'. |
296 | | * |
297 | | * Return the length of the match found, or 'best_len' if no match longer than |
298 | | * 'best_len' was found. |
299 | | */ |
300 | | static forceinline u32 |
301 | | hc_matchfinder_longest_match(struct hc_matchfinder * const mf, |
302 | | const u8 ** const in_base_p, |
303 | | const u8 * const in_next, |
304 | | u32 best_len, |
305 | | const u32 max_len, |
306 | | const u32 nice_len, |
307 | | const u32 max_search_depth, |
308 | | u32 * const next_hashes, |
309 | | u32 * const offset_ret, |
310 | | const bool multi_hop) |
311 | 2.12M | { |
312 | 2.12M | u32 depth_remaining = max_search_depth; |
313 | 2.12M | const u8 *best_matchptr = in_next; |
314 | 2.12M | mf_pos_t cur_node3, cur_node4; |
315 | 2.12M | hc_link_t cur_link = 0; /* nodes after cur_node4 (if multi_hop) */ |
316 | 2.12M | hc_link_t next_link = 0; /* link of the last node in 'cur_link' */ |
317 | 2.12M | unsigned link_remaining = 0; /* number of nodes left in 'cur_link' */ |
318 | 2.12M | u32 hash3, hash4; |
319 | 2.12M | u32 next_hashseq; |
320 | 2.12M | u32 seq4; |
321 | 2.12M | const u8 *matchptr; |
322 | 2.12M | u32 len; |
323 | 2.12M | u32 cur_pos = in_next - *in_base_p; |
324 | 2.12M | const u8 *in_base; |
325 | 2.12M | mf_pos_t cutoff; |
326 | | |
327 | 2.12M | if (cur_pos == MATCHFINDER_WINDOW_SIZE) { |
328 | 0 | hc_matchfinder_slide_window(mf, multi_hop); |
329 | 0 | *in_base_p += MATCHFINDER_WINDOW_SIZE; |
330 | 0 | cur_pos = 0; |
331 | 0 | } |
332 | | |
333 | 2.12M | in_base = *in_base_p; |
334 | 2.12M | cutoff = cur_pos - MATCHFINDER_WINDOW_SIZE; |
335 | | |
336 | 2.12M | if (unlikely(max_len < 5)) /* can we read 4 bytes from 'in_next + 1'? */ |
337 | 795 | goto out; |
338 | | |
339 | | /* Get the precomputed hash codes. */ |
340 | 2.12M | hash3 = next_hashes[0]; |
341 | 2.12M | hash4 = next_hashes[1]; |
342 | | |
343 | | /* From the hash buckets, get the first node of each linked list. */ |
344 | 2.12M | cur_node3 = mf->hash3_tab[hash3]; |
345 | 2.12M | cur_node4 = mf->hash4_tab[hash4]; |
346 | | |
347 | | /* Update for length 3 matches. This replaces the singleton node in the |
348 | | * 'hash3' bucket with the node for the current sequence. */ |
349 | 2.12M | mf->hash3_tab[hash3] = cur_pos; |
350 | | |
351 | | /* Update for length 4 matches. This prepends the node for the current |
352 | | * sequence to the linked list in the 'hash4' bucket. */ |
353 | 2.12M | mf->hash4_tab[hash4] = cur_pos; |
354 | 2.12M | cur_link = hc_matchfinder_prepend(mf, cur_pos, cur_node4, multi_hop); |
355 | | |
356 | | /* Compute the next hash codes. */ |
357 | 2.12M | next_hashseq = get_unaligned_le32(in_next + 1); |
358 | 2.12M | next_hashes[0] = lz_hash(next_hashseq & 0xFFFFFF, HC_MATCHFINDER_HASH3_ORDER); |
359 | 2.12M | next_hashes[1] = lz_hash(next_hashseq, HC_MATCHFINDER_HASH4_ORDER); |
360 | 2.12M | prefetchw(&mf->hash3_tab[next_hashes[0]]); |
361 | 2.12M | prefetchw(&mf->hash4_tab[next_hashes[1]]); |
362 | | |
363 | | /* |
364 | | * Prepare to walk the linked list. 'cur_link' contains the nodes that follow |
365 | | * the first node. Already start loading the link of the last of them, so that |
366 | | * it will hopefully be available by the time it's needed. |
367 | | */ |
368 | 2.12M | #define HC_BEGIN_WALK() \ |
369 | 2.12M | do { \ |
370 | 647k | if (multi_hop) { \ |
371 | 647k | link_remaining = HC_MATCHFINDER_HOPS_PER_WORD; \ |
372 | 647k | next_link = hc_matchfinder_get_link(mf, \ |
373 | 647k | hc_last_hop(cur_link)); \ |
374 | 647k | } \ |
375 | 647k | } while (0) |
376 | | |
377 | | /* Advance to the next node in the linked list. */ |
378 | 2.12M | #define HC_ADVANCE() \ |
379 | 25.5M | do { \ |
380 | 25.5M | if (!multi_hop) { \ |
381 | 0 | cur_node4 = mf->next_tab.single_hop[cur_node4 & \ |
382 | 0 | (MATCHFINDER_WINDOW_SIZE - 1)]; \ |
383 | 25.5M | } else { \ |
384 | 25.5M | cur_node4 = (mf_pos_t)cur_link; \ |
385 | 25.5M | if (--link_remaining != 0) { \ |
386 | 19.4M | cur_link >>= 16; \ |
387 | 19.4M | } else { \ |
388 | | /* cur_node4 is the last node of the \ |
389 | | * old 'cur_link', so 'next_link' is \ |
390 | | * its link. Use it, and start loading \ |
391 | | * the link of its last node. */ \ |
392 | 6.13M | cur_link = next_link; \ |
393 | 6.13M | link_remaining = \ |
394 | 6.13M | HC_MATCHFINDER_HOPS_PER_WORD; \ |
395 | 6.13M | next_link = hc_matchfinder_get_link(mf, \ |
396 | 6.13M | hc_last_hop(cur_link)); \ |
397 | 6.13M | } \ |
398 | 25.5M | } \ |
399 | 25.5M | } while (0) |
400 | | |
401 | 2.12M | if (best_len < 4) { /* No match of length >= 4 found yet? */ |
402 | | |
403 | | /* Check for a length 3 match if needed. */ |
404 | | |
405 | 1.70M | if (cur_node3 <= cutoff) |
406 | 1.22M | goto out; |
407 | | |
408 | 470k | seq4 = load_u32_unaligned(in_next); |
409 | | |
410 | 470k | if (best_len < 3) { |
411 | 375k | matchptr = &in_base[cur_node3]; |
412 | 375k | if (load_u24_unaligned(matchptr) == loaded_u32_to_u24(seq4)) { |
413 | 325k | best_len = 3; |
414 | 325k | best_matchptr = matchptr; |
415 | 325k | } |
416 | 375k | } |
417 | | |
418 | | /* Check for a length 4 match. */ |
419 | | |
420 | 470k | if (cur_node4 <= cutoff) |
421 | 213k | goto out; |
422 | | |
423 | 256k | HC_BEGIN_WALK(); |
424 | 265k | for (;;) { |
425 | | /* No length 4 match found yet. Check the first 4 bytes. */ |
426 | 265k | matchptr = &in_base[cur_node4]; |
427 | | |
428 | 265k | if (load_u32_unaligned(matchptr) == seq4) |
429 | 247k | break; |
430 | | |
431 | | /* The first 4 bytes did not match. Keep trying. */ |
432 | 17.8k | HC_ADVANCE(); |
433 | 17.8k | if (cur_node4 <= cutoff || !--depth_remaining) |
434 | 9.07k | goto out; |
435 | 17.8k | } |
436 | | |
437 | | /* Found a match of length >= 4. Extend it to its full length. */ |
438 | 247k | best_matchptr = matchptr; |
439 | 247k | best_len = lz_extend(in_next, best_matchptr, 4, max_len); |
440 | 247k | if (best_len >= nice_len) |
441 | 2.55k | goto out; |
442 | 245k | HC_ADVANCE(); |
443 | 245k | if (cur_node4 <= cutoff || !--depth_remaining) |
444 | 92.9k | goto out; |
445 | 421k | } else { |
446 | 421k | if (cur_node4 <= cutoff || best_len >= nice_len) |
447 | 30.9k | goto out; |
448 | 390k | HC_BEGIN_WALK(); |
449 | 390k | } |
450 | | |
451 | | /* Check for matches of length >= 5. */ |
452 | | |
453 | 1.63M | for (;;) { |
454 | 25.2M | for (;;) { |
455 | 25.2M | matchptr = &in_base[cur_node4]; |
456 | | |
457 | | /* Already found a length 4 match. Try for a longer |
458 | | * match; start by checking either the last 4 bytes and |
459 | | * the first 4 bytes, or the last byte. (The last byte, |
460 | | * the one which would extend the match length by 1, is |
461 | | * the most important.) */ |
462 | 25.2M | #if UNALIGNED_ACCESS_IS_FAST |
463 | 25.2M | if ((load_u32_unaligned(matchptr + best_len - 3) == |
464 | 25.2M | load_u32_unaligned(in_next + best_len - 3)) && |
465 | 1.11M | (load_u32_unaligned(matchptr) == |
466 | 1.11M | load_u32_unaligned(in_next))) |
467 | | #else |
468 | | if (matchptr[best_len] == in_next[best_len]) |
469 | | #endif |
470 | 1.11M | break; |
471 | | |
472 | | /* Continue to the next node in the list. */ |
473 | 24.1M | HC_ADVANCE(); |
474 | 24.1M | if (cur_node4 <= cutoff || !--depth_remaining) |
475 | 516k | goto out; |
476 | 24.1M | } |
477 | | |
478 | 1.11M | #if UNALIGNED_ACCESS_IS_FAST |
479 | 1.11M | len = 4; |
480 | | #else |
481 | | len = 0; |
482 | | #endif |
483 | 1.11M | len = lz_extend(in_next, matchptr, len, max_len); |
484 | 1.11M | if (len > best_len) { |
485 | | /* This is the new longest match. */ |
486 | 444k | best_len = len; |
487 | 444k | best_matchptr = matchptr; |
488 | 444k | if (best_len >= nice_len) |
489 | 3.95k | goto out; |
490 | 444k | } |
491 | | |
492 | | /* Continue to the next node in the list. */ |
493 | 1.11M | HC_ADVANCE(); |
494 | 1.11M | if (cur_node4 <= cutoff || !--depth_remaining) |
495 | 22.6k | goto out; |
496 | 1.11M | } |
497 | 0 | #undef HC_BEGIN_WALK |
498 | 0 | #undef HC_ADVANCE |
499 | 2.12M | out: |
500 | 2.12M | *offset_ret = in_next - best_matchptr; |
501 | 2.12M | return best_len; |
502 | 543k | } |
503 | | |
504 | | /* |
505 | | * Advance the matchfinder, but don't search for matches. |
506 | | * |
507 | | * @mf |
508 | | * The matchfinder structure. |
509 | | * @in_base_p |
510 | | * Location of a pointer which points to the place in the input data the |
511 | | * matchfinder currently stores positions relative to. This may be updated |
512 | | * by this function. |
513 | | * @in_next |
514 | | * Pointer to the next position in the input buffer. |
515 | | * @in_end |
516 | | * Pointer to the end of the input buffer. |
517 | | * @count |
518 | | * The number of bytes to advance. Must be > 0. |
519 | | * @next_hashes |
520 | | * The precomputed hash codes for the sequence beginning at @in_next. |
521 | | * These will be used and then updated with the precomputed hashcodes for |
522 | | * the sequence beginning at @in_next + @count. |
523 | | * @multi_hop |
524 | | * Whether to use multi-hop links; see 'next_tab'. |
525 | | */ |
526 | | static forceinline void |
527 | | hc_matchfinder_skip_bytes(struct hc_matchfinder * const mf, |
528 | | const u8 ** const in_base_p, |
529 | | const u8 *in_next, |
530 | | const u8 * const in_end, |
531 | | const u32 count, |
532 | | u32 * const next_hashes, |
533 | | const bool multi_hop) |
534 | 236k | { |
535 | 236k | u32 cur_pos; |
536 | 236k | u32 hash3, hash4; |
537 | 236k | u32 next_hashseq; |
538 | 236k | u32 remaining = count; |
539 | | |
540 | 236k | if (unlikely(count + 5 > in_end - in_next)) |
541 | 1.72k | return; |
542 | | |
543 | 234k | cur_pos = in_next - *in_base_p; |
544 | 234k | hash3 = next_hashes[0]; |
545 | 234k | hash4 = next_hashes[1]; |
546 | 4.01M | do { |
547 | 4.01M | if (cur_pos == MATCHFINDER_WINDOW_SIZE) { |
548 | 0 | hc_matchfinder_slide_window(mf, multi_hop); |
549 | 0 | *in_base_p += MATCHFINDER_WINDOW_SIZE; |
550 | 0 | cur_pos = 0; |
551 | 0 | } |
552 | 4.01M | mf->hash3_tab[hash3] = cur_pos; |
553 | 4.01M | hc_matchfinder_prepend(mf, cur_pos, mf->hash4_tab[hash4], |
554 | 4.01M | multi_hop); |
555 | 4.01M | mf->hash4_tab[hash4] = cur_pos; |
556 | | |
557 | 4.01M | next_hashseq = get_unaligned_le32(++in_next); |
558 | 4.01M | hash3 = lz_hash(next_hashseq & 0xFFFFFF, HC_MATCHFINDER_HASH3_ORDER); |
559 | 4.01M | hash4 = lz_hash(next_hashseq, HC_MATCHFINDER_HASH4_ORDER); |
560 | 4.01M | cur_pos++; |
561 | 4.01M | } while (--remaining); |
562 | | |
563 | 234k | prefetchw(&mf->hash3_tab[hash3]); |
564 | 234k | prefetchw(&mf->hash4_tab[hash4]); |
565 | 234k | next_hashes[0] = hash3; |
566 | 234k | next_hashes[1] = hash4; |
567 | 234k | } |
568 | | |
569 | | #endif /* LIB_HC_MATCHFINDER_H */ |