1use 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]
6pub 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
16pub 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
67fn 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