1#[inline]
10pub fn decode_strict(input: &[u8], offset: usize, output: &mut [u8]) -> bool {
11 decode::<false, true>(input, offset, output)
12}
13
14#[inline]
17pub fn merge_strict(input: &[u8], offset: usize, output: &mut [u8]) -> bool {
18 decode::<true, true>(input, offset, output)
19}
20
21#[inline]
24pub fn decode_clamped(input: &[u8], offset: usize, output: &mut [u8]) {
25 output.fill(0);
26 let _ = decode::<false, false>(input, offset, output);
27}
28
29#[inline]
32pub fn merge_clamped(input: &[u8], offset: usize, output: &mut [u8]) {
33 let _ = decode::<true, false>(input, offset, output);
34}
35
36#[inline]
37fn decode<const MERGE: bool, const STRICT: bool>(
38 input: &[u8],
39 offset: usize,
40 output: &mut [u8],
41) -> bool {
42 let mut source = offset;
43 let mut destination = 0usize;
44 while destination < output.len() {
45 let Some(&value) = input.get(source) else {
46 return false;
47 };
48 source += 1;
49 if value != 0 {
50 if MERGE {
51 output[destination] |= value;
52 } else {
53 output[destination] = value;
54 }
55 destination += 1;
56 continue;
57 }
58 let Some(&run) = input.get(source) else {
59 return false;
60 };
61 source += 1;
62 let available = output.len() - destination;
63 if STRICT && (run == 0 || run as usize > available) {
64 return false;
65 }
66 let end = destination + (run as usize).min(available);
67 if !MERGE {
68 output[destination..end].fill(0);
69 }
70 destination = end;
71 }
72 true
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn literal_zero_runs_and_or_merging_preserve_rows() {
81 let input = [0xaa, 0x11, 0, 2, 0x80];
82 let mut row = [0xcc; 4];
83 assert!(decode_strict(&input, 1, &mut row));
84 assert_eq!(row, [0x11, 0, 0, 0x80]);
85 row = [0x22; 4];
86 assert!(merge_strict(&input, 1, &mut row));
87 assert_eq!(row, [0x33, 0x22, 0x22, 0xa2]);
88 }
89
90 #[test]
91 fn strict_failures_preserve_the_decoded_prefix_and_unwritten_tail() {
92 for input in [&[0x11][..], &[0x11, 0], &[0x11, 0, 0], &[0x11, 0, 4]] {
93 let mut row = [0xcc; 4];
94 assert!(!decode_strict(input, 0, &mut row));
95 assert_eq!(row, [0x11, 0xcc, 0xcc, 0xcc]);
96 let mut merged = [0x22; 4];
97 assert!(!merge_strict(input, 0, &mut merged));
98 assert_eq!(merged, [0x33, 0x22, 0x22, 0x22]);
99 }
100 assert!(decode_strict(&[], usize::MAX, &mut []));
101 assert!(!decode_strict(&[], usize::MAX, &mut [0]));
102 }
103
104 #[test]
105 fn goldsrc_clips_runs_accepts_empty_runs_and_zeroes_truncated_tail() {
106 for input in [&[0x11][..], &[0x11, 0], &[0x11, 0, 0], &[0x11, 0, 99]] {
107 let mut row = [0xcc; 4];
108 decode_clamped(input, 0, &mut row);
109 assert_eq!(row, [0x11, 0, 0, 0]);
110 let mut merged = [0x22; 4];
111 merge_clamped(input, 0, &mut merged);
112 assert_eq!(merged, [0x33, 0x22, 0x22, 0x22]);
113 }
114 let mut row = [0xcc; 2];
115 decode_clamped(&[0, 0, 0, 0, 0x11, 0x80], 0, &mut row);
116 assert_eq!(row, [0x11, 0x80]);
117 decode_clamped(&[], usize::MAX, &mut row);
118 assert_eq!(row, [0, 0]);
119 }
120}