Coverage Report

Created: 2026-08-14 08:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/exr-1.74.2/src/compression/rle.rs
Line
Count
Source
1
use super::{optimize_bytes::*, Error, Result, *};
2
3
// inspired by  https://github.com/openexr/openexr/blob/master/OpenEXR/IlmImf/ImfRle.cpp
4
5
const MIN_RUN_LENGTH: usize = 3;
6
const MAX_RUN_LENGTH: usize = 127;
7
8
0
pub fn decompress_bytes(
9
0
    channels: &ChannelList,
10
0
    compressed_le: ByteVec,
11
0
    rectangle: IntegerBounds,
12
0
    expected_byte_size: usize,
13
0
    pedantic: bool,
14
0
) -> Result<ByteVec> {
15
0
    let mut decompressed_le = unpack_rle_tokens(&compressed_le, expected_byte_size, pedantic)?;
16
0
    differences_to_samples(&mut decompressed_le);
17
0
    interleave_byte_blocks(&mut decompressed_le);
18
0
    super::convert_little_endian_to_current(decompressed_le, channels, rectangle)
19
    // TODO no alloc
20
0
}
21
22
/// Shared by this compression method and DWA's RLE scheme (both port OpenEXR's
23
/// `internal_rle_decompress`, see `compression::dwa`) - kept separate from
24
/// `decompress_bytes` because DWA does not apply the delta prediction /
25
/// byte-block interleaving done there.
26
0
pub(super) fn unpack_rle_tokens(
27
0
    compressed_le: &[u8],
28
0
    expected_byte_size: usize,
29
0
    pedantic: bool,
30
0
) -> Result<ByteVec> {
31
0
    let mut remaining_le = compressed_le;
32
0
    let mut decompressed_le = Vec::with_capacity(expected_byte_size.min(8 * 2048));
33
34
0
    while !remaining_le.is_empty() && decompressed_le.len() != expected_byte_size {
35
0
        let count = take_1(&mut remaining_le)? as i8 as i32;
36
37
0
        if count < 0 {
38
            // take the next '-count' bytes as-is
39
0
            let values = take_n(&mut remaining_le, -count as usize)?;
40
0
            decompressed_le.extend_from_slice(values);
41
        } else {
42
            // repeat the next value 'count + 1' times
43
0
            let value = take_1(&mut remaining_le)?;
44
0
            decompressed_le.resize(decompressed_le.len() + (count as usize) + 1, value);
45
        }
46
    }
47
48
0
    if pedantic && !remaining_le.is_empty() {
49
0
        return Err(Error::invalid("data amount"));
50
0
    }
51
52
0
    Ok(decompressed_le)
53
0
}
54
55
0
pub fn compress_bytes(
56
0
    channels: &ChannelList,
57
0
    uncompressed_ne: ByteVec,
58
0
    rectangle: IntegerBounds,
59
0
) -> Result<ByteVec> {
60
    // see https://github.com/AcademySoftwareFoundation/openexr/blob/3bd93f85bcb74c77255f28cdbb913fdbfbb39dfe/OpenEXR/IlmImf/ImfTiledOutputFile.cpp#L750-L842
61
0
    let mut data_le =
62
0
        super::convert_current_to_little_endian(uncompressed_ne, channels, rectangle)?; // TODO no alloc
63
64
0
    separate_bytes_fragments(&mut data_le);
65
0
    samples_to_differences(&mut data_le);
66
67
0
    Ok(pack_rle_tokens(&data_le))
68
0
}
69
70
/// Shared by this compression method and DWA's RLE section. This only emits
71
/// the byte-oriented RLE token stream; callers are responsible for any byte
72
/// prediction, byte interleaving, or zlib wrapping required by their format.
73
0
pub(super) fn pack_rle_tokens(data_le: &[u8]) -> ByteVec {
74
0
    let mut compressed_le = Vec::with_capacity(data_le.len());
75
0
    let mut run_start = 0;
76
0
    let mut run_end = 1;
77
78
0
    while run_start < data_le.len() {
79
0
        while run_end < data_le.len()
80
0
            && data_le[run_start] == data_le[run_end]
81
0
            && ((run_end - run_start) as i32) - 1 < (MAX_RUN_LENGTH as i32)
82
0
        {
83
0
            run_end += 1;
84
0
        }
85
86
0
        if run_end - run_start >= MIN_RUN_LENGTH {
87
0
            compressed_le.push((((run_end - run_start) as i32) - 1) as u8);
88
0
            compressed_le.push(data_le[run_start]);
89
0
            run_start = run_end;
90
0
        } else {
91
0
            while run_end < data_le.len()
92
0
                && (run_end + 1 >= data_le.len()
93
0
                    || data_le[run_end] != data_le[run_end + 1]
94
0
                    || run_end + 2 >= data_le.len()
95
0
                    || data_le[run_end + 1] != data_le[run_end + 2])
96
0
                && run_end - run_start < MAX_RUN_LENGTH
97
0
            {
98
0
                run_end += 1;
99
0
            }
100
101
0
            compressed_le.push(((run_start as i32) - (run_end as i32)) as u8);
102
0
            compressed_le.extend_from_slice(&data_le[run_start..run_end]);
103
104
0
            run_start = run_end;
105
0
            run_end += 1;
106
        }
107
    }
108
109
0
    compressed_le
110
0
}
111
112
0
fn take_1(slice: &mut &[u8]) -> Result<u8> {
113
0
    if !slice.is_empty() {
114
0
        let result = slice[0];
115
0
        *slice = &slice[1..];
116
0
        Ok(result)
117
    } else {
118
0
        Err(Error::invalid("compressed data"))
119
    }
120
0
}
121
122
0
fn take_n<'s>(slice: &mut &'s [u8], n: usize) -> Result<&'s [u8]> {
123
0
    if n <= slice.len() {
124
0
        let (front, back) = slice.split_at(n);
125
0
        *slice = back;
126
0
        Ok(front)
127
    } else {
128
0
        Err(Error::invalid("compressed data"))
129
    }
130
0
}