• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

actioninja / refpack-rs / 6689328355

30 Oct 2023 06:54AM UTC coverage: 89.345% (-3.0%) from 92.374%
6689328355

Pull #6

github

web-flow
Merge 6f3e6b894 into f55f4d390
Pull Request #6: Demoded controls

294 of 294 new or added lines in 7 files covered. (100.0%)

914 of 1023 relevant lines covered (89.35%)

154499.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.7
/src/data/mod.rs
1
////////////////////////////////////////////////////////////////////////////////
2
// This Source Code Form is subject to the terms of the Mozilla Public         /
3
// License, v. 2.0. If a copy of the MPL was not distributed with this         /
4
// file, You can obtain one at https://mozilla.org/MPL/2.0/.                   /
5
//                                                                             /
6
////////////////////////////////////////////////////////////////////////////////
7

8
//! things relating the actual compressed data block. Anything past the header
9
//! info, the actual compression algorithms themselves, control codes, etc.
10

11
use std::error::Error;
12
use std::fmt::{Display, Formatter};
13
use std::io::{Read, Seek};
14

15
use crate::RefPackError;
16

17
pub mod compression;
18
pub mod control;
19
pub mod decompression;
20

21
#[derive(Debug)]
×
22
pub enum DecodeError {
23
    /// Error indicating that offset was 0 in refpack control byte. This doesn't
24
    /// make sense, and likely indicated the data is corrupted or malformed.
25
    BadOffset,
26
    /// Error indicating that the requested copy offset would go past the start
27
    /// of the buffer. This indicates malformed or corrupted data.
28
    ///
29
    /// ### Fields
30
    /// - usize: buffer length
31
    /// - usize: offset requested
32
    NegativePosition(usize, usize),
33
    /// Error indicating that during decompression, the RLE decode attempted to
34
    /// write past the end of the decompression buffer
35
    ///
36
    /// This error exists to prevent maliciously constructed data from using an
37
    /// unbounded amount of memory
38
    ///
39
    /// ### Fields
40
    /// - usize: amount of bytes attempted to write past
41
    BadLength(usize),
42
}
43

44
impl Display for DecodeError {
45
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2✔
46
        match self {
2✔
47
            DecodeError::BadOffset => {
48
                write!(f, "Offset is 0 in compressed data control command")
×
49
            }
50
            DecodeError::NegativePosition(length, offset) => {
1✔
51
                write!(
1✔
52
                    f,
1✔
53
                    "Offset went past start of buffer: buffer length `{length}`, offset `{offset}`"
1✔
54
                )
1✔
55
            }
56
            DecodeError::BadLength(length) => {
1✔
57
                write!(
1✔
58
                    f,
1✔
59
                    "Decompressed data overran decompressed size in header by `{length}` bytes"
1✔
60
                )
1✔
61
            }
62
        }
63
    }
2✔
64
}
65

66
impl Error for DecodeError {}
67

68
/// Fast decoding of run length encoded data
69
/// Based on https://github.com/WanzenBug/rle-decode-helper/blob/master/src/lib.rs
70
///
71
/// Takes the last `offset` items of the buffer and repeatedly copies them
72
/// to `position` until `length` items have been copied.
73
///
74
/// If this function errors no data will have been copied
75
///
76
/// # Errors
77
/// - [DecodeError::BadOffset]: `offset` is 0
78
/// - [DecodeError::NegativePosition]: `offset` > `position`
79
/// - [DecodeError::BadLength]: `position + length` > `buffer.len()`
80
///
81
/// # Panics
82
/// - `fill_length + buffer.len()`: would overflow
83
///
84
/// # Returns
85
/// the new position of the buffer after the read
86
#[inline(always)]
87
pub(crate) fn rle_decode_fixed<T: Copy>(
45✔
88
    buffer: &mut [T],
45✔
89
    mut position: usize,
45✔
90
    mut offset: usize,
45✔
91
    mut length: usize,
45✔
92
) -> Result<usize, DecodeError> {
45✔
93
    if offset == 0 {
45✔
94
        return Err(DecodeError::BadOffset);
1✔
95
    }
44✔
96
    if offset > position {
44✔
97
        return Err(DecodeError::NegativePosition(position, offset));
1✔
98
    }
43✔
99
    if position + length > buffer.len() {
43✔
100
        return Err(DecodeError::BadLength(position + length - buffer.len()));
1✔
101
    }
42✔
102

42✔
103
    let copy_fragment_start = position - offset;
42✔
104

105
    while length > offset {
42✔
106
        buffer.copy_within(copy_fragment_start..position, position);
×
107
        length -= offset;
×
108
        position += offset;
×
109
        offset *= 2;
×
110
    }
×
111

112
    buffer.copy_within(
42✔
113
        copy_fragment_start..(copy_fragment_start + length),
42✔
114
        position,
42✔
115
    );
42✔
116
    position += length;
42✔
117

42✔
118
    Ok(position)
42✔
119
}
45✔
120

121
/// Copy `length` bytes from the reader into `buffer` at `position`
122
///
123
/// # Returns
124
/// the new position of the buffer after the read
125
///
126
/// # Errors
127
/// - [RefPackError::Io]: General IO Error when reading from the reader
128
///
129
/// # Panics
130
/// Panics if a copy would go past the end of the buffer to copy to
131
#[inline(always)]
132
pub(crate) fn copy_from_reader(
206,465✔
133
    buffer: &mut [u8],
206,465✔
134
    reader: &mut (impl Read + Seek),
206,465✔
135
    position: usize,
206,465✔
136
    length: usize,
206,465✔
137
) -> Result<usize, RefPackError> {
206,465✔
138
    assert!(
206,465✔
139
        buffer.len() >= position + length,
206,465✔
140
        "Attempted to copy past end of input buffer; position: {position}; length: {length}"
×
141
    );
142

143
    reader.read_exact(&mut buffer[position..(position + length)])?;
206,465✔
144

145
    Ok(position + length)
206,465✔
146
}
206,465✔
147

148
#[cfg(test)]
149
mod test {
150
    use proptest::prelude::*;
151
    use test_strategy::proptest;
152

153
    use super::*;
154
    use crate::format::Reference;
155
    use crate::{easy_compress, easy_decompress};
156

157
    #[proptest(ProptestConfig { cases: 100_000, ..Default::default() })]
300,004✔
158
    fn symmetrical_compression(#[filter(# input.len() > 0)] input: Vec<u8>) {
101,031✔
159
        let compressed = easy_compress::<Reference>(&input).unwrap();
160
        let decompressed = easy_decompress::<Reference>(&compressed).unwrap();
161

162
        prop_assert_eq!(input, decompressed);
163
    }
164

165
    #[proptest(ProptestConfig {
1✔
166
    max_shrink_iters: 1_000_000,
772✔
167
    ..Default::default()
772✔
168
    })]
772✔
169
    fn symmetrical_compression_large_input(
170
        #[strategy(proptest::collection::vec(any::<u8>(), (2_000..=2_000)))] input: Vec<u8>,
1✔
171
    ) {
172
        let compressed = easy_compress::<Reference>(&input).unwrap();
173
        let decompressed = easy_decompress::<Reference>(&compressed).unwrap();
174

175
        prop_assert_eq!(input, decompressed);
176
    }
177

178
    mod rle_decode {
179
        use super::*;
180

181
        #[test]
1✔
182
        fn errors_on_bad_offset() {
1✔
183
            let error = rle_decode_fixed(&mut [0], 0, 0, 1).unwrap_err();
1✔
184
            assert!(matches!(error, DecodeError::BadOffset));
1✔
185
        }
1✔
186

187
        #[test]
1✔
188
        fn errors_on_negative_position() {
1✔
189
            let error = rle_decode_fixed(&mut [0], 0, 1, 1).unwrap_err();
1✔
190
            assert_eq!(
1✔
191
                error.to_string(),
1✔
192
                "Offset went past start of buffer: buffer length `0`, offset `1`"
1✔
193
            );
1✔
194
        }
1✔
195

196
        #[test]
1✔
197
        fn errors_on_bad_length() {
1✔
198
            let error = rle_decode_fixed(&mut [0, 0], 1, 1, 10).unwrap_err();
1✔
199
            assert_eq!(
1✔
200
                error.to_string(),
1✔
201
                "Decompressed data overran decompressed size in header by `9` bytes"
1✔
202
            );
1✔
203
        }
1✔
204
    }
205
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc