ruzstd/decoding/
sequence_execution.rs

1use super::{decodebuffer::DecodeBufferError, scratch::DecoderScratch};
2
3#[derive(Debug)]
4#[non_exhaustive]
5pub enum ExecuteSequencesError {
6    DecodebufferError(DecodeBufferError),
7    NotEnoughBytesForSequence { wanted: usize, have: usize },
8    ZeroOffset,
9}
10
11impl core::fmt::Display for ExecuteSequencesError {
12    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
13        match self {
14            ExecuteSequencesError::DecodebufferError(e) => {
15                write!(f, "{:?}", e)
16            }
17            ExecuteSequencesError::NotEnoughBytesForSequence { wanted, have } => {
18                write!(
19                    f,
20                    "Sequence wants to copy up to byte {}. Bytes in literalsbuffer: {}",
21                    wanted, have
22                )
23            }
24            ExecuteSequencesError::ZeroOffset => {
25                write!(f, "Illegal offset: 0 found")
26            }
27        }
28    }
29}
30
31#[cfg(feature = "std")]
32impl std::error::Error for ExecuteSequencesError {
33    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
34        match self {
35            ExecuteSequencesError::DecodebufferError(source) => Some(source),
36            _ => None,
37        }
38    }
39}
40
41impl From<DecodeBufferError> for ExecuteSequencesError {
42    fn from(val: DecodeBufferError) -> Self {
43        Self::DecodebufferError(val)
44    }
45}
46
47/// Take the provided decoder and execute the sequences stored within
48pub fn execute_sequences(scratch: &mut DecoderScratch) -> Result<(), ExecuteSequencesError> {
49    let mut literals_copy_counter = 0;
50    let old_buffer_size = scratch.buffer.len();
51    let mut seq_sum = 0;
52
53    for idx in 0..scratch.sequences.len() {
54        let seq = scratch.sequences[idx];
55
56        if seq.ll > 0 {
57            let high = literals_copy_counter + seq.ll as usize;
58            if high > scratch.literals_buffer.len() {
59                return Err(ExecuteSequencesError::NotEnoughBytesForSequence {
60                    wanted: high,
61                    have: scratch.literals_buffer.len(),
62                });
63            }
64            let literals = &scratch.literals_buffer[literals_copy_counter..high];
65            literals_copy_counter += seq.ll as usize;
66
67            scratch.buffer.push(literals);
68        }
69
70        let actual_offset = do_offset_history(seq.of, seq.ll, &mut scratch.offset_hist);
71        if actual_offset == 0 {
72            return Err(ExecuteSequencesError::ZeroOffset);
73        }
74        if seq.ml > 0 {
75            scratch
76                .buffer
77                .repeat(actual_offset as usize, seq.ml as usize)?;
78        }
79
80        seq_sum += seq.ml;
81        seq_sum += seq.ll;
82    }
83    if literals_copy_counter < scratch.literals_buffer.len() {
84        let rest_literals = &scratch.literals_buffer[literals_copy_counter..];
85        scratch.buffer.push(rest_literals);
86        seq_sum += rest_literals.len() as u32;
87    }
88
89    let diff = scratch.buffer.len() - old_buffer_size;
90    assert!(
91        seq_sum as usize == diff,
92        "Seq_sum: {} is different from the difference in buffersize: {}",
93        seq_sum,
94        diff
95    );
96    Ok(())
97}
98
99/// Update the most recently used offsets to reflect the provided offset value, and return the
100/// "actual" offset needed because offsets are not stored in a raw way, some transformations are needed
101/// before you get a functional number.
102fn do_offset_history(offset_value: u32, lit_len: u32, scratch: &mut [u32; 3]) -> u32 {
103    let actual_offset = if lit_len > 0 {
104        match offset_value {
105            1..=3 => scratch[offset_value as usize - 1],
106            _ => {
107                //new offset
108                offset_value - 3
109            }
110        }
111    } else {
112        match offset_value {
113            1..=2 => scratch[offset_value as usize],
114            3 => scratch[0] - 1,
115            _ => {
116                //new offset
117                offset_value - 3
118            }
119        }
120    };
121
122    //update history
123    if lit_len > 0 {
124        match offset_value {
125            1 => {
126                //nothing
127            }
128            2 => {
129                scratch[1] = scratch[0];
130                scratch[0] = actual_offset;
131            }
132            _ => {
133                scratch[2] = scratch[1];
134                scratch[1] = scratch[0];
135                scratch[0] = actual_offset;
136            }
137        }
138    } else {
139        match offset_value {
140            1 => {
141                scratch[1] = scratch[0];
142                scratch[0] = actual_offset;
143            }
144            2 => {
145                scratch[2] = scratch[1];
146                scratch[1] = scratch[0];
147                scratch[0] = actual_offset;
148            }
149            _ => {
150                scratch[2] = scratch[1];
151                scratch[1] = scratch[0];
152                scratch[0] = actual_offset;
153            }
154        }
155    }
156
157    actual_offset
158}