1use super::encode::BROTLI_NUM_DISTANCE_SHORT_CODES;
2use super::util::Log2FloorNonZero;
3#[derive(Copy, Clone, Debug)]
4pub struct BrotliDistanceParams {
5 pub distance_postfix_bits: u32,
6 pub num_direct_distance_codes: u32,
7 pub alphabet_size: u32,
8 pub max_distance: usize,
9}
10
11#[derive(Clone, Copy, Debug, Default)]
12pub struct Command {
13 pub insert_len_: u32,
15 pub copy_len_: u32,
16 pub dist_extra_: u32,
18 pub cmd_prefix_: u16,
19 pub dist_prefix_: u16,
21}
22
23impl Command {
24 pub fn copy_len(&self) -> u32 {
25 self.copy_len_ & 0x01ff_ffff
26 }
27
28 pub fn distance_context(&self) -> u32 {
29 let r: u32 = (self.cmd_prefix_ as i32 >> 6) as u32;
30 let c: u32 = (self.cmd_prefix_ as i32 & 7i32) as u32;
31 if (r == 0 || r == 2 || r == 4 || r == 7) && c <= 2 {
32 c
33 } else {
34 3
35 }
36 }
37
38 pub fn init_insert(&mut self, insertlen: usize) {
39 self.insert_len_ = insertlen as u32;
40 self.copy_len_ = (4i32 << 25) as u32;
41 self.dist_extra_ = 0u32;
42 self.dist_prefix_ = (1u16 << 10) | BROTLI_NUM_DISTANCE_SHORT_CODES as u16;
43 get_length_code(insertlen, 4usize, false, &mut self.cmd_prefix_);
44 }
45}
46
47#[inline(always)]
48pub fn ComputeDistanceCode(distance: usize, max_distance: usize, dist_cache: &[i32]) -> usize {
49 if distance <= max_distance {
50 let distance_plus_3: usize = distance.wrapping_add(3);
51 let offset0: usize = distance_plus_3.wrapping_sub(dist_cache[0] as usize);
52 let offset1: usize = distance_plus_3.wrapping_sub(dist_cache[1] as usize);
53 if distance == dist_cache[0] as usize {
54 return 0usize;
55 } else if distance == dist_cache[1] as usize {
56 return 1;
57 } else if offset0 < 7usize {
58 return (0x0975_0468_i32 >> (4usize).wrapping_mul(offset0) & 0xfi32) as usize;
59 } else if offset1 < 7usize {
60 return (0x0fdb_1ace_i32 >> (4usize).wrapping_mul(offset1) & 0xfi32) as usize;
61 } else if distance == dist_cache[2] as usize {
62 return 2usize;
63 } else if distance == dist_cache[3] as usize {
64 return 3usize;
65 }
66 }
67 distance.wrapping_add(16).wrapping_sub(1)
68}
69
70#[inline(always)]
71pub fn GetInsertLengthCode(insertlen: usize) -> u16 {
72 if insertlen < 6usize {
73 insertlen as u16
74 } else if insertlen < 130usize {
75 let nbits: u32 = Log2FloorNonZero(insertlen.wrapping_sub(2) as u64).wrapping_sub(1);
76 ((nbits << 1) as usize)
77 .wrapping_add(insertlen.wrapping_sub(2) >> nbits)
78 .wrapping_add(2) as u16
79 } else if insertlen < 2114usize {
80 Log2FloorNonZero(insertlen.wrapping_sub(66) as u64).wrapping_add(10) as u16
81 } else if insertlen < 6210usize {
82 21u32 as u16
83 } else if insertlen < 22594usize {
84 22u32 as u16
85 } else {
86 23u32 as u16
87 }
88}
89
90#[inline(always)]
91pub fn GetCopyLengthCode(copylen: usize) -> u16 {
92 if copylen < 10usize {
93 copylen.wrapping_sub(2) as u16
94 } else if copylen < 134usize {
95 let nbits: u32 = Log2FloorNonZero(copylen.wrapping_sub(6) as u64).wrapping_sub(1);
96 ((nbits << 1) as usize)
97 .wrapping_add(copylen.wrapping_sub(6) >> nbits)
98 .wrapping_add(4) as u16
99 } else if copylen < 2118usize {
100 Log2FloorNonZero(copylen.wrapping_sub(70) as u64).wrapping_add(12) as u16
101 } else {
102 23u32 as u16
103 }
104}
105
106#[deprecated(note = "Use combine_length_codes instead")]
107#[inline(always)]
108pub fn CombineLengthCodes(inscode: u16, copycode: u16, use_last_distance: i32) -> u16 {
109 combine_length_codes(inscode, copycode, use_last_distance != 0)
110}
111
112#[inline(always)]
113pub(crate) fn combine_length_codes(inscode: u16, copycode: u16, use_last_distance: bool) -> u16 {
114 let bits64: u16 = (copycode as u32 & 0x7u32 | (inscode as u32 & 0x7u32) << 3) as u16;
115 if use_last_distance && inscode < 8 && copycode < 16 {
116 if (copycode as i32) < 8i32 {
117 bits64
118 } else {
119 let s64: u16 = 64u16;
120 (bits64 as i32 | s64 as i32) as u16
121 }
122 } else {
123 let sub_offset: i32 = 2i32 * ((copycode as i32 >> 3) + 3i32 * (inscode as i32 >> 3));
124 let offset = (sub_offset << 5) + 0x40i32 + (0x520d40i32 >> sub_offset & 0xc0i32);
125 (offset as u16 as i32 | bits64 as i32) as u16
126 }
127}
128
129#[deprecated(note = "Use get_length_code instead")]
130#[inline(always)]
131pub fn GetLengthCode(insertlen: usize, copylen: usize, use_last_distance: i32, code: &mut u16) {
132 get_length_code(insertlen, copylen, use_last_distance != 0, code)
133}
134
135#[inline(always)]
136pub(crate) fn get_length_code(
137 insertlen: usize,
138 copylen: usize,
139 use_last_distance: bool,
140 code: &mut u16,
141) {
142 let inscode: u16 = GetInsertLengthCode(insertlen);
143 let copycode: u16 = GetCopyLengthCode(copylen);
144 *code = combine_length_codes(inscode, copycode, use_last_distance);
145}
146pub fn PrefixEncodeCopyDistance(
147 distance_code: usize,
148 num_direct_codes: usize,
149 postfix_bits: u64,
150 code: &mut u16,
151 extra_bits: &mut u32,
152) {
153 if distance_code < (BROTLI_NUM_DISTANCE_SHORT_CODES as usize).wrapping_add(num_direct_codes) {
154 *code = distance_code as u16;
155 *extra_bits = 0u32;
156 } else {
157 let dist: u64 = (1u64 << postfix_bits.wrapping_add(2u32 as (u64))).wrapping_add(
158 (distance_code as u64)
159 .wrapping_sub(BROTLI_NUM_DISTANCE_SHORT_CODES as u64)
160 .wrapping_sub(num_direct_codes as u64),
161 );
162 let bucket: u64 = Log2FloorNonZero(dist).wrapping_sub(1) as (u64);
163 let postfix_mask: u64 = (1u32 << postfix_bits).wrapping_sub(1) as (u64);
164 let postfix: u64 = dist & postfix_mask;
165 let prefix: u64 = (dist >> bucket) & 1;
166 let offset: u64 = (2u64).wrapping_add(prefix) << bucket;
167 let nbits: u64 = bucket.wrapping_sub(postfix_bits);
168 *code = ((nbits << 10)
169 | ((BROTLI_NUM_DISTANCE_SHORT_CODES as u64)
170 .wrapping_add(num_direct_codes as u64)
171 .wrapping_add(
172 2u64.wrapping_mul(nbits.wrapping_sub(1))
173 .wrapping_add(prefix)
174 << postfix_bits,
175 )
176 .wrapping_add(postfix))) as u16;
177 *extra_bits = (dist.wrapping_sub(offset) >> postfix_bits) as u32;
178 }
185}
186
187impl Command {
188 pub fn restore_distance_code(&self, dist: &BrotliDistanceParams) -> u32 {
189 if (self.dist_prefix_ as i32 & 0x3ff)
190 < BROTLI_NUM_DISTANCE_SHORT_CODES as i32 + dist.num_direct_distance_codes as i32
191 {
192 self.dist_prefix_ as u32 & 0x3ff
193 } else {
194 let dcode = self.dist_prefix_ as u32 & 0x3ff;
195 let nbits: u32 = u32::from(self.dist_prefix_ >> 10);
196 let extra: u32 = self.dist_extra_;
197 let postfix_mask = (1u32 << dist.distance_postfix_bits) - 1;
198 let hcode = dcode
199 .wrapping_sub(dist.num_direct_distance_codes)
200 .wrapping_sub(BROTLI_NUM_DISTANCE_SHORT_CODES)
201 >> dist.distance_postfix_bits;
202 let lcode = dcode
203 .wrapping_sub(dist.num_direct_distance_codes)
204 .wrapping_sub(BROTLI_NUM_DISTANCE_SHORT_CODES)
205 & postfix_mask;
206 let offset = (2u32.wrapping_add((hcode & 1)) << nbits).wrapping_sub(4);
207 (offset.wrapping_add(extra) << dist.distance_postfix_bits)
208 .wrapping_add(lcode)
209 .wrapping_add(dist.num_direct_distance_codes)
210 .wrapping_add(BROTLI_NUM_DISTANCE_SHORT_CODES)
211 }
212 }
213
214 pub fn distance_index_and_offset(&self, dist: &BrotliDistanceParams) -> (usize, isize) {
216 let n_postfix = dist.distance_postfix_bits;
217 let n_direct = dist.num_direct_distance_codes;
218 let dextra = self.dist_extra_;
219 let dprefix = self.dist_prefix_ & 0x3ff;
220 let n_dist_bits = self.dist_prefix_ >> 10;
221 if u32::from(dprefix) < BROTLI_NUM_DISTANCE_SHORT_CODES {
222 let table: [(usize, isize); 16] = [
223 (1, 0),
224 (2, 0),
225 (3, 0),
226 (4, 0),
227 (1, -1),
228 (1, 1),
229 (1, -2),
230 (1, 2),
231 (1, -3),
232 (1, 3),
233 (2, -1),
234 (2, 1),
235 (2, -2),
236 (2, 2),
237 (2, -3),
238 (2, 3),
239 ];
240 return table[dprefix as usize];
242 }
243 if (dprefix as usize) < BROTLI_NUM_DISTANCE_SHORT_CODES as usize + n_direct as usize {
244 let ret = dprefix as isize + 1 - BROTLI_NUM_DISTANCE_SHORT_CODES as isize;
245 return (0, ret);
247 }
248 let postfix_mask = (1 << n_postfix) - 1;
249 let dcode = dprefix as u32 - BROTLI_NUM_DISTANCE_SHORT_CODES - n_direct;
250 let hcode = dcode >> n_postfix;
251 let lcode = dcode & postfix_mask;
252 let offset = ((2 + (hcode & 1)) << n_dist_bits) - 4;
253
254 let ret = (((offset + dextra) << n_postfix) + lcode + n_direct + 1) as isize;
255 (0, ret)
257 }
258}
259
260pub fn RecomputeDistancePrefixes(
261 cmds: &mut [Command],
262 num_commands: usize,
263 num_direct_distance_codes: u32,
264 distance_postfix_bits: u32,
265 dist: &BrotliDistanceParams,
266) {
267 if num_direct_distance_codes == 0u32 && (distance_postfix_bits == 0u32) {
268 return;
269 }
270 for i in 0usize..num_commands {
271 let cmd: &mut Command = &mut cmds[i];
272 if cmd.copy_len() != 0 && cmd.cmd_prefix_ >= 128 {
273 PrefixEncodeCopyDistance(
274 cmd.restore_distance_code(dist) as usize,
275 num_direct_distance_codes as usize,
276 distance_postfix_bits as (u64),
277 &mut cmd.dist_prefix_,
278 &mut cmd.dist_extra_,
279 );
280 }
281 }
282}
283
284impl Command {
285 pub fn init(
286 &mut self,
287 dist: &BrotliDistanceParams,
288 insertlen: usize,
289 copylen: usize,
290 copylen_code: usize,
291 distance_code: usize,
292 ) {
293 self.insert_len_ = insertlen as u32;
294 let copylen_code_delta = (copylen_code as i32 - copylen as i32) as i8;
295 self.copy_len_ = (copylen as u32 | (u32::from(copylen_code_delta as u8) << 25));
296 PrefixEncodeCopyDistance(
297 distance_code,
298 dist.num_direct_distance_codes as usize,
299 u64::from(dist.distance_postfix_bits),
300 &mut self.dist_prefix_,
301 &mut self.dist_extra_,
302 );
303 get_length_code(
304 insertlen,
305 copylen_code,
306 (self.dist_prefix_ & 0x3ff) == 0,
307 &mut self.cmd_prefix_,
308 );
309 }
310
311 pub fn new(
312 dist: &BrotliDistanceParams,
313 insertlen: usize,
314 copylen: usize,
315 copylen_code: usize,
316 distance_code: usize,
317 ) -> Self {
318 let mut cmd = Command {
319 insert_len_: insertlen as u32,
320 copy_len_: (copylen | ((copylen_code ^ copylen) << 25)) as u32,
321 dist_extra_: 0,
322 cmd_prefix_: 0,
323 dist_prefix_: 0,
324 };
325 cmd.init(dist, insertlen, copylen, copylen_code, distance_code);
326 cmd
327 }
328}
329
330#[cfg(test)]
331mod test {
332 pub fn helperCommandDistanceIndexAndOffset(
334 cmd: &super::Command,
335 dist: &super::BrotliDistanceParams,
336 ) -> (usize, isize) {
337 let n_postfix = dist.distance_postfix_bits;
338 let n_direct = dist.num_direct_distance_codes;
339 let dextra = cmd.dist_extra_;
340 let dist_prefix = cmd.dist_prefix_ & 0x3ff;
341 if dist_prefix < 16 {
342 let table: [(usize, isize); 16] = [
343 (1, 0),
344 (2, 0),
345 (3, 0),
346 (4, 0),
347 (1, -1),
348 (1, 1),
349 (1, -2),
350 (1, 2),
351 (1, -3),
352 (1, 3),
353 (2, -1),
354 (2, 1),
355 (2, -2),
356 (2, 2),
357 (2, -3),
358 (2, 3),
359 ];
360 return table[cmd.dist_prefix_ as usize];
361 }
362 if (dist_prefix as usize) < 16 + n_direct as usize {
363 return (0, dist_prefix as isize + 1 - 16);
364 }
365 let postfix_mask = (1 << n_postfix) - 1;
366 let dcode = dist_prefix as u32 - 16 - n_direct;
367 let n_dist_bits = 1 + (dcode >> (n_postfix + 1));
368
369 let hcode = dcode >> n_postfix;
370 let lcode = dcode & postfix_mask;
371 let offset = ((2 + (hcode & 1)) << n_dist_bits) - 4;
372 (
373 0,
374 (((offset + dextra) << n_postfix) + lcode + n_direct + 1) as isize,
375 )
376 }
377 #[test]
378 fn test_command_return_distance_index_offset() {
379 let param = super::BrotliDistanceParams {
380 distance_postfix_bits: 2,
381 num_direct_distance_codes: 16,
382 alphabet_size: 224,
383 max_distance: 268435456,
384 };
385 let mut cmd = super::Command::default();
386 cmd.insert_len_ = 63;
387 cmd.copy_len_ = 3;
388 cmd.dist_extra_ = 3;
389 cmd.cmd_prefix_ = 297;
390 cmd.dist_prefix_ = 2089;
391
392 assert_eq!(cmd.distance_index_and_offset(¶m), (0, 46));
393 assert_eq!(
394 cmd.distance_index_and_offset(¶m),
395 helperCommandDistanceIndexAndOffset(&cmd, ¶m)
396 );
397 cmd = super::Command {
398 insert_len_: 27,
399 copy_len_: 3,
400 dist_extra_: 0,
401 cmd_prefix_: 281,
402 dist_prefix_: 6,
403 };
404 assert_eq!(cmd.distance_index_and_offset(¶m), (1, -2));
405 assert_eq!(
406 cmd.distance_index_and_offset(¶m),
407 helperCommandDistanceIndexAndOffset(&cmd, ¶m)
408 );
409 cmd = super::Command {
410 insert_len_: 1,
411 copy_len_: 3,
412 dist_extra_: 0,
413 cmd_prefix_: 137,
414 dist_prefix_: 27,
415 };
416 assert_eq!(cmd.distance_index_and_offset(¶m), (0, 12));
417 assert_eq!(
418 cmd.distance_index_and_offset(¶m),
419 helperCommandDistanceIndexAndOffset(&cmd, ¶m)
420 );
421 cmd = super::Command {
422 insert_len_: 5,
423 copy_len_: 4,
424 dist_extra_: 297,
425 cmd_prefix_: 170,
426 dist_prefix_: 11377,
427 };
428 assert_eq!(cmd.distance_index_and_offset(¶m), (0, 17574));
429 assert_eq!(
430 cmd.distance_index_and_offset(¶m),
431 helperCommandDistanceIndexAndOffset(&cmd, ¶m)
432 );
433 cmd.init_insert(24);
434 assert_eq!(cmd.distance_index_and_offset(¶m), (0, 1));
435 }
436 }