]> wimlib.net Git - wimlib/blob - include/wimlib/hc_matchfinder.h
Merge LZX compression updates
[wimlib] / include / wimlib / hc_matchfinder.h
1 /*
2  * hc_matchfinder.h
3  *
4  * Author:      Eric Biggers
5  * Year:        2014, 2015
6  *
7  * The author dedicates this file to the public domain.
8  * You can do whatever you want with this file.
9  *
10  * ---------------------------------------------------------------------------
11  *
12  *                                 Algorithm
13  *
14  * This is a Hash Chains (hc) based matchfinder.
15  *
16  * The data structure is a hash table where each hash bucket contains a linked
17  * list (or "chain") of sequences whose first 3 bytes share the same hash code.
18  * Each sequence is identified by its starting position in the input buffer.
19  *
20  * The algorithm processes the input buffer sequentially.  At each byte
21  * position, the hash code of the first 3 bytes of the sequence beginning at
22  * that position (the sequence being matched against) is computed.  This
23  * identifies the hash bucket to use for that position.  Then, this hash
24  * bucket's linked list is searched for matches.  Then, a new linked list node
25  * is created to represent the current sequence and is prepended to the list.
26  *
27  * This algorithm has several useful properties:
28  *
29  * - It only finds true Lempel-Ziv matches; i.e., those where the matching
30  *   sequence occurs prior to the sequence being matched against.
31  *
32  * - The sequences in each linked list are always sorted by decreasing starting
33  *   position.  Therefore, the closest (smallest offset) matches are found
34  *   first, which in many compression formats tend to be the cheapest to encode.
35  *
36  * - Although fast running time is not guaranteed due to the possibility of the
37  *   lists getting very long, the worst degenerate behavior can be easily
38  *   prevented by capping the number of nodes searched at each position.
39  *
40  * - If the compressor decides not to search for matches at a certain position,
41  *   then that position can be quickly inserted without searching the list.
42  *
43  * - The algorithm is adaptable to sliding windows: just store the positions
44  *   relative to a "base" value that is updated from time to time, and stop
45  *   searching each list when the sequences get too far away.
46  *
47  * ---------------------------------------------------------------------------
48  *
49  *                              Notes on usage
50  *
51  * You must define MATCHFINDER_MAX_WINDOW_ORDER before including this header
52  * because that determines which integer type to use for positions.  Since
53  * 16-bit integers are faster than 32-bit integers due to reduced memory usage
54  * (and therefore reduced cache pressure), the code only uses 32-bit integers if
55  * they are needed to represent all possible positions.
56  *
57  * You must allocate the 'struct hc_matchfinder' on a
58  * MATCHFINDER_ALIGNMENT-aligned boundary, and its necessary allocation size
59  * must be gotten by calling hc_matchfinder_size().
60  *
61  * ----------------------------------------------------------------------------
62  *
63  *                               Optimizations
64  *
65  * The longest_match() and skip_positions() functions are inlined into the
66  * compressors that use them.  This isn't just about saving the overhead of a
67  * function call.  These functions are intended to be called from the inner
68  * loops of compressors, where giving the compiler more control over register
69  * allocation is very helpful.  There is also significant benefit to be gained
70  * from allowing the CPU to predict branches independently at each call site.
71  * For example, "lazy"-style compressors can be written with two calls to
72  * longest_match(), each of which starts with a different 'best_len' and
73  * therefore has significantly different performance characteristics.
74  *
75  * Although any hash function can be used, a multiplicative hash is fast and
76  * works well.
77  *
78  * On some processors, it is significantly faster to extend matches by whole
79  * words (32 or 64 bits) instead of by individual bytes.  For this to be the
80  * case, the processor must implement unaligned memory accesses efficiently and
81  * must have either a fast "find first set bit" instruction or a fast "find last
82  * set bit" instruction, depending on the processor's endianness.
83  *
84  * The code uses one loop for finding the first match and one loop for finding a
85  * longer match.  Each of these loops is tuned for its respective task and in
86  * combination are faster than a single generalized loop that handles both
87  * tasks.
88  *
89  * The code also uses a tight inner loop that only compares the last and first
90  * bytes of a potential match.  It is only when these bytes match that a full
91  * match extension is attempted.
92  *
93  * ----------------------------------------------------------------------------
94  */
95
96 #ifndef _HC_MATCHFINDER_H
97 #define _HC_MATCHFINDER_H
98
99 #include "wimlib/lz_extend.h"
100 #include "wimlib/lz_hash.h"
101 #include "wimlib/matchfinder_common.h"
102 #include "wimlib/unaligned.h"
103
104 #if MATCHFINDER_MAX_WINDOW_ORDER < 14
105 #  define HC_MATCHFINDER_HASH_ORDER 14
106 #else
107 #  define HC_MATCHFINDER_HASH_ORDER 15
108 #endif
109
110 #define HC_MATCHFINDER_HASH_LENGTH      (1UL << HC_MATCHFINDER_HASH_ORDER)
111
112 struct hc_matchfinder {
113         pos_t hash_tab[HC_MATCHFINDER_HASH_LENGTH];
114         pos_t next_tab[];
115 } _aligned_attribute(MATCHFINDER_ALIGNMENT);
116
117 /* Return the number of bytes that must be allocated for a 'hc_matchfinder' that
118  * can work with buffers up to the specified size.  */
119 static inline size_t
120 hc_matchfinder_size(size_t max_bufsize)
121 {
122         return sizeof(pos_t) * (HC_MATCHFINDER_HASH_LENGTH + max_bufsize);
123 }
124
125 /* Prepare the matchfinder for a new input buffer.  */
126 static inline void
127 hc_matchfinder_init(struct hc_matchfinder *mf)
128 {
129         matchfinder_init(mf->hash_tab, HC_MATCHFINDER_HASH_LENGTH);
130 }
131
132 /*
133  * Find the longest match longer than 'best_len' bytes.
134  *
135  * @mf
136  *      The matchfinder structure.
137  * @in_begin
138  *      Pointer to the beginning of the input buffer.
139  * @in_next
140  *      Pointer to the next byte in the input buffer to process.  This is the
141  *      pointer to the sequence being matched against.
142  * @best_len
143  *      Require a match longer than this length.
144  * @max_len
145  *      The maximum permissible match length at this position.
146  * @nice_len
147  *      Stop searching if a match of at least this length is found.
148  * @max_search_depth
149  *      Limit on the number of potential matches to consider.
150  * @offset_ret
151  *      If a match is found, its offset is returned in this location.
152  *
153  * Return the length of the match found, or 'best_len' if no match longer than
154  * 'best_len' was found.
155  */
156 static inline unsigned
157 hc_matchfinder_longest_match(struct hc_matchfinder * const restrict mf,
158                              const u8 * const in_begin,
159                              const u8 * const in_next,
160                              unsigned best_len,
161                              const unsigned max_len,
162                              const unsigned nice_len,
163                              const unsigned max_search_depth,
164                              unsigned *offset_ret)
165 {
166         unsigned depth_remaining = max_search_depth;
167         const u8 *best_matchptr = best_matchptr; /* uninitialized */
168         const u8 *matchptr;
169         unsigned len;
170         u32 first_3_bytes;
171         u32 hash;
172         pos_t cur_node;
173
174         /* Insert the current sequence into the appropriate linked list.  */
175         if (unlikely(max_len < LZ_HASH_REQUIRED_NBYTES))
176                 goto out;
177         first_3_bytes = load_u24_unaligned(in_next);
178         hash = lz_hash(first_3_bytes, HC_MATCHFINDER_HASH_ORDER);
179         cur_node = mf->hash_tab[hash];
180         mf->next_tab[in_next - in_begin] = cur_node;
181         mf->hash_tab[hash] = in_next - in_begin;
182
183         if (unlikely(best_len >= max_len))
184                 goto out;
185
186         /* Search the appropriate linked list for matches.  */
187
188         if (!(matchfinder_node_valid(cur_node)))
189                 goto out;
190
191         if (best_len < 3) {
192                 for (;;) {
193                         /* No length 3 match found yet.
194                          * Check the first 3 bytes.  */
195                         matchptr = &in_begin[cur_node];
196
197                         if (load_u24_unaligned(matchptr) == first_3_bytes)
198                                 break;
199
200                         /* The first 3 bytes did not match.  Keep trying.  */
201                         cur_node = mf->next_tab[cur_node];
202                         if (!matchfinder_node_valid(cur_node) || !--depth_remaining)
203                                 goto out;
204                 }
205
206                 /* Found a match of length >= 3.  Extend it to its full length.  */
207                 best_matchptr = matchptr;
208                 best_len = lz_extend(in_next, best_matchptr, 3, max_len);
209                 if (best_len >= nice_len)
210                         goto out;
211                 cur_node = mf->next_tab[cur_node];
212                 if (!matchfinder_node_valid(cur_node) || !--depth_remaining)
213                         goto out;
214         }
215
216         for (;;) {
217                 for (;;) {
218                         matchptr = &in_begin[cur_node];
219
220                         /* Already found a length 3 match.  Try for a longer match;
221                          * start by checking the last 2 bytes and the first 4 bytes.  */
222                 #if UNALIGNED_ACCESS_IS_FAST
223                         if ((load_u32_unaligned(matchptr + best_len - 3) ==
224                              load_u32_unaligned(in_next + best_len - 3)) &&
225                             (load_u32_unaligned(matchptr) ==
226                              load_u32_unaligned(in_next)))
227                 #else
228                         if (matchptr[best_len] == in_next[best_len])
229                 #endif
230                                 break;
231
232                         cur_node = mf->next_tab[cur_node];
233                         if (!matchfinder_node_valid(cur_node) || !--depth_remaining)
234                                 goto out;
235                 }
236
237         #if UNALIGNED_ACCESS_IS_FAST
238                 len = 4;
239         #else
240                 len = 0;
241         #endif
242                 len = lz_extend(in_next, matchptr, len, max_len);
243                 if (len > best_len) {
244                         best_len = len;
245                         best_matchptr = matchptr;
246                         if (best_len >= nice_len)
247                                 goto out;
248                 }
249                 cur_node = mf->next_tab[cur_node];
250                 if (!matchfinder_node_valid(cur_node) || !--depth_remaining)
251                         goto out;
252         }
253 out:
254         *offset_ret = in_next - best_matchptr;
255         return best_len;
256 }
257
258 /*
259  * Advance the matchfinder, but don't search for matches.
260  *
261  * @mf
262  *      The matchfinder structure.
263  * @in_begin
264  *      Pointer to the beginning of the input buffer.
265  * @in_next
266  *      Pointer to the next byte in the input buffer to process.
267  * @in_end
268  *      Pointer to the end of the input buffer.
269  * @count
270  *      The number of bytes to advance.  Must be > 0.
271  */
272 static inline void
273 hc_matchfinder_skip_positions(struct hc_matchfinder * restrict mf,
274                               const u8 *in_begin,
275                               const u8 *in_next,
276                               const u8 *in_end,
277                               unsigned count)
278 {
279         u32 hash;
280
281         if (unlikely(in_next + count >= in_end - LZ_HASH_REQUIRED_NBYTES))
282                 return;
283
284         do {
285                 hash = lz_hash_3_bytes(in_next, HC_MATCHFINDER_HASH_ORDER);
286                 mf->next_tab[in_next - in_begin] = mf->hash_tab[hash];
287                 mf->hash_tab[hash] = in_next - in_begin;
288                 in_next++;
289         } while (--count);
290 }
291
292 #endif /* _HC_MATCHFINDER_H */