Coverage Report

Created: 2026-10-28 07:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 */