1 | use super::{decodebuffer::DecodebufferError, scratch::DecoderScratch}; |
2 | |
3 | #[derive (Debug, derive_more::Display, derive_more::From)] |
4 | #[cfg_attr (feature = "std" , derive(derive_more::Error))] |
5 | #[non_exhaustive ] |
6 | pub enum ExecuteSequencesError { |
7 | #[display(fmt = "{_0:?}" )] |
8 | #[from] |
9 | DecodebufferError(DecodebufferError), |
10 | #[display(fmt = "Sequence wants to copy up to byte {wanted}. Bytes in literalsbuffer: {have}" )] |
11 | NotEnoughBytesForSequence { wanted: usize, have: usize }, |
12 | #[display(fmt = "Illegal offset: 0 found" )] |
13 | ZeroOffset, |
14 | } |
15 | |
16 | pub fn execute_sequences(scratch: &mut DecoderScratch) -> Result<(), ExecuteSequencesError> { |
17 | let mut literals_copy_counter = 0; |
18 | let old_buffer_size = scratch.buffer.len(); |
19 | let mut seq_sum = 0; |
20 | |
21 | for idx in 0..scratch.sequences.len() { |
22 | let seq = scratch.sequences[idx]; |
23 | |
24 | if seq.ll > 0 { |
25 | let high = literals_copy_counter + seq.ll as usize; |
26 | if high > scratch.literals_buffer.len() { |
27 | return Err(ExecuteSequencesError::NotEnoughBytesForSequence { |
28 | wanted: high, |
29 | have: scratch.literals_buffer.len(), |
30 | }); |
31 | } |
32 | let literals = &scratch.literals_buffer[literals_copy_counter..high]; |
33 | literals_copy_counter += seq.ll as usize; |
34 | |
35 | scratch.buffer.push(literals); |
36 | } |
37 | |
38 | let actual_offset = do_offset_history(seq.of, seq.ll, &mut scratch.offset_hist); |
39 | if actual_offset == 0 { |
40 | return Err(ExecuteSequencesError::ZeroOffset); |
41 | } |
42 | if seq.ml > 0 { |
43 | scratch |
44 | .buffer |
45 | .repeat(actual_offset as usize, seq.ml as usize)?; |
46 | } |
47 | |
48 | seq_sum += seq.ml; |
49 | seq_sum += seq.ll; |
50 | } |
51 | if literals_copy_counter < scratch.literals_buffer.len() { |
52 | let rest_literals = &scratch.literals_buffer[literals_copy_counter..]; |
53 | scratch.buffer.push(rest_literals); |
54 | seq_sum += rest_literals.len() as u32; |
55 | } |
56 | |
57 | let diff = scratch.buffer.len() - old_buffer_size; |
58 | assert!( |
59 | seq_sum as usize == diff, |
60 | "Seq_sum: {} is different from the difference in buffersize: {}" , |
61 | seq_sum, |
62 | diff |
63 | ); |
64 | Ok(()) |
65 | } |
66 | |
67 | fn do_offset_history(offset_value: u32, lit_len: u32, scratch: &mut [u32; 3]) -> u32 { |
68 | let actual_offset = if lit_len > 0 { |
69 | match offset_value { |
70 | 1..=3 => scratch[offset_value as usize - 1], |
71 | _ => { |
72 | //new offset |
73 | offset_value - 3 |
74 | } |
75 | } |
76 | } else { |
77 | match offset_value { |
78 | 1..=2 => scratch[offset_value as usize], |
79 | 3 => scratch[0] - 1, |
80 | _ => { |
81 | //new offset |
82 | offset_value - 3 |
83 | } |
84 | } |
85 | }; |
86 | |
87 | //update history |
88 | if lit_len > 0 { |
89 | match offset_value { |
90 | 1 => { |
91 | //nothing |
92 | } |
93 | 2 => { |
94 | scratch[1] = scratch[0]; |
95 | scratch[0] = actual_offset; |
96 | } |
97 | _ => { |
98 | scratch[2] = scratch[1]; |
99 | scratch[1] = scratch[0]; |
100 | scratch[0] = actual_offset; |
101 | } |
102 | } |
103 | } else { |
104 | match offset_value { |
105 | 1 => { |
106 | scratch[1] = scratch[0]; |
107 | scratch[0] = actual_offset; |
108 | } |
109 | 2 => { |
110 | scratch[2] = scratch[1]; |
111 | scratch[1] = scratch[0]; |
112 | scratch[0] = actual_offset; |
113 | } |
114 | _ => { |
115 | scratch[2] = scratch[1]; |
116 | scratch[1] = scratch[0]; |
117 | scratch[0] = actual_offset; |
118 | } |
119 | } |
120 | } |
121 | |
122 | actual_offset |
123 | } |
124 | |