Skip to main content

mlt_core/decoder/stream/
logical.rs

1use std::fmt::Debug;
2use std::iter::repeat_n;
3
4use num_traits::{PrimInt, ToPrimitive as _};
5use usize_cast::IntoUsize as _;
6
7use crate::MltError::{ParsingLogicalTechnique, RleRunLenInvalid, UnsupportedLogicalEncoding};
8use crate::codecs::zigzag::{decode_componentwise_delta_vec2s, decode_zigzag, decode_zigzag_delta};
9use crate::decoder::{LogicalEncoding, LogicalTechnique, LogicalValue, RleMeta, StreamMeta};
10use crate::errors::{AsMltError as _, fail_if_invalid_stream_size};
11use crate::{Decoder, MltResult};
12
13impl RleMeta {
14    /// Decode RLE (Run-Length Encoding) data.
15    ///
16    /// A run-length encoded stream is a sequence of runs, each a `(run_len, value)` pair
17    /// that expands to `run_len` copies of `value`.
18    /// The two layouts differ only in how those pairs are arranged in the stream:
19    ///
20    /// ```text
21    /// logical values:   [7, 7, 7, 9, 5, 5]     runs = 3, num_rle_values = 6
22    /// Split (v1):       [3, 1, 2][7, 9, 5]     all run lengths, then all values
23    /// Interleaved (v2): [3, 7][1, 9][2, 5]     one (run_len, value) pair per run
24    /// ```
25    ///
26    /// Split reads `runs` from the stream header, so the split point is known up front.
27    /// Interleaved derives the run count from the data length, which must therefore be even.
28    /// Both know `num_rle_values`, the expanded element count that the run lengths must sum to.
29    ///
30    /// Charges the decoder for the expanded output allocation.
31    pub fn decode<T: PrimInt + Debug>(self, data: &[T], dec: &mut Decoder) -> MltResult<Vec<T>> {
32        match self {
33            Self::Split {
34                runs,
35                num_rle_values,
36            } => Self::decode_split(runs, num_rle_values, data, dec),
37            #[cfg(feature = "unstable-v2")]
38            Self::Interleaved { num_rle_values } => {
39                Self::decode_interleaved(num_rle_values, data, dec)
40            }
41        }
42    }
43
44    /// Tag `0x01` layout: `[run_len × runs][value × runs]`, with `runs` from the header.
45    fn decode_split<T: PrimInt + Debug>(
46        runs: u32,
47        num_rle_values: u32,
48        data: &[T],
49        dec: &mut Decoder,
50    ) -> MltResult<Vec<T>> {
51        let expected_len = runs.into_usize().checked_mul(2).or_overflow()?;
52        fail_if_invalid_stream_size(data.len(), expected_len)?;
53
54        let (run_lens, values) = data.split_at(runs.into_usize());
55        fail_if_invalid_stream_size(
56            num_rle_values.into_usize(),
57            Self::calc_size(run_lens)?.into_usize(),
58        )?;
59
60        let alloc_size = num_rle_values.into_usize();
61        let mut result = dec.alloc(alloc_size)?;
62        for (&run_len, &val) in run_lens.iter().zip(values.iter()) {
63            let run = run_len
64                .to_usize()
65                .ok_or_else(|| RleRunLenInvalid(run_len.to_i128().unwrap_or_default()))?;
66            result.extend(repeat_n(val, run));
67        }
68        dec.adjust_alloc(&result, alloc_size)?;
69        Ok(result)
70    }
71
72    /// Tag `0x02` layout: `(run_len, value)` pairs, with the run count derived from the data length.
73    /// `num_rle_values` comes from the stream's count context rather than its own varint.
74    #[cfg(feature = "unstable-v2")]
75    fn decode_interleaved<T: PrimInt + Debug>(
76        num_rle_values: u32,
77        data: &[T],
78        dec: &mut Decoder,
79    ) -> MltResult<Vec<T>> {
80        if !data.len().is_multiple_of(2) {
81            return Err(RleRunLenInvalid(data.len().to_i128().unwrap_or_default()));
82        }
83        let alloc_size = num_rle_values.into_usize();
84        let mut result = dec.alloc(alloc_size)?;
85        for [cnt, val] in data.as_chunks::<2>().0 {
86            let run = cnt
87                .to_usize()
88                .filter(|&run| run <= alloc_size - result.len())
89                .ok_or_else(|| RleRunLenInvalid(cnt.to_i128().unwrap_or_default()))?;
90            result.extend(repeat_n(val, run));
91        }
92        // The expanded count must exactly match the count declared by the stream context.
93        fail_if_invalid_stream_size(result.len(), alloc_size)?;
94        dec.adjust_alloc(&result, alloc_size)?;
95        Ok(result)
96    }
97
98    fn calc_size<T: PrimInt + Debug>(run_lens: &[T]) -> MltResult<u32> {
99        run_lens
100            .iter()
101            .try_fold(T::zero(), |a, v| a.checked_add(v))
102            .and_then(|v| v.to_u32())
103            .ok_or_else(|| RleRunLenInvalid(run_lens.len().to_i128().unwrap_or_default()))
104    }
105}
106
107impl LogicalTechnique {
108    pub fn parse(value: u8) -> MltResult<Self> {
109        Self::try_from(value).or(Err(ParsingLogicalTechnique(value)))
110    }
111}
112
113impl LogicalValue {
114    #[must_use]
115    pub fn new(meta: StreamMeta) -> Self {
116        Self { meta }
117    }
118
119    /// Logically decode `data` (physically decoded u32 words) into `Vec<i32>`.
120    ///
121    /// Never called for `LogicalEncoding::None` - that case is handled directly
122    /// in the bridge (physical buffer decoded into a fresh output Vec).
123    pub fn decode_i32(self, data: &[u32], dec: &mut Decoder) -> MltResult<Vec<i32>> {
124        match self.meta.encoding.logical {
125            LogicalEncoding::None => decode_zigzag(data, dec),
126            LogicalEncoding::Rle(v) => decode_zigzag(&v.decode(data, dec)?, dec),
127            LogicalEncoding::ComponentwiseDelta => decode_componentwise_delta_vec2s(data, dec),
128            LogicalEncoding::Delta => decode_zigzag_delta::<i32, _>(data, dec),
129            LogicalEncoding::DeltaRle(v) => {
130                let expanded = v.decode(data, dec)?;
131                decode_zigzag_delta::<i32, _>(&expanded, dec)
132            }
133            LogicalEncoding::Morton(v) => v.decode_codes(data, dec),
134            LogicalEncoding::MortonDelta(v) => v.decode_delta(data, dec),
135            LogicalEncoding::MortonRle(_) => Err(UnsupportedLogicalEncoding(
136                self.meta.encoding.logical,
137                "i32 (MortonRle)",
138            )),
139        }
140    }
141
142    /// Logically decode `data` (physically decoded u32 words) into `Vec<u32>`.
143    ///
144    /// Not called for `LogicalEncoding::None` - that case is handled entirely
145    /// in the bridge (physical buffer decoded directly into the output Vec).
146    pub fn decode_u32(self, data: &[u32], dec: &mut Decoder) -> MltResult<Vec<u32>> {
147        let num = self.meta.num_values.into_usize();
148        match self.meta.encoding.logical {
149            LogicalEncoding::None => {
150                // Caller should have used the direct-output path; this is a fallback.
151                dec.consume_items::<u32>(num)?;
152                Ok(data.to_vec())
153            }
154            LogicalEncoding::Rle(rle) => rle.decode(data, dec),
155            LogicalEncoding::Delta => decode_zigzag_delta::<i32, _>(data, dec),
156            LogicalEncoding::DeltaRle(rle) => {
157                decode_zigzag_delta::<i32, _>(&rle.decode(data, dec)?, dec)
158            }
159            LogicalEncoding::ComponentwiseDelta
160            | LogicalEncoding::Morton(_)
161            | LogicalEncoding::MortonDelta(_)
162            | LogicalEncoding::MortonRle(_) => Err(UnsupportedLogicalEncoding(
163                self.meta.encoding.logical,
164                "u32",
165            )),
166        }
167    }
168
169    /// Logically decode `data` (physically decoded u64 words) into `Vec<i64>`.
170    ///
171    /// Never called for `LogicalEncoding::None` - that case is handled directly
172    /// in the bridge (physical buffer decoded into a fresh output Vec).
173    pub fn decode_i64(self, data: &[u64], dec: &mut Decoder) -> MltResult<Vec<i64>> {
174        match self.meta.encoding.logical {
175            LogicalEncoding::None => decode_zigzag(data, dec),
176            LogicalEncoding::Delta => decode_zigzag_delta::<i64, _>(data, dec),
177            LogicalEncoding::DeltaRle(rle) => {
178                let expanded = rle.decode(data, dec)?;
179                decode_zigzag_delta::<i64, _>(&expanded, dec)
180            }
181            LogicalEncoding::Rle(rle) => {
182                // rle.decode() charges for expanded u64 vec; decode_zigzag charges for i64 vec
183                let expanded = rle.decode(data, dec)?;
184                decode_zigzag(&expanded, dec)
185            }
186            LogicalEncoding::ComponentwiseDelta
187            | LogicalEncoding::Morton(_)
188            | LogicalEncoding::MortonDelta(_)
189            | LogicalEncoding::MortonRle(_) => Err(UnsupportedLogicalEncoding(
190                self.meta.encoding.logical,
191                "i64",
192            )),
193        }
194    }
195
196    /// Logically decode `data` (physically decoded u64 words) into `Vec<u64>`.
197    ///
198    /// Not called for `LogicalEncoding::None` - that case is handled entirely
199    /// in the bridge (physical buffer decoded directly into the output Vec).
200    pub fn decode_u64(self, data: &[u64], dec: &mut Decoder) -> MltResult<Vec<u64>> {
201        let num = self.meta.num_values.into_usize();
202        match self.meta.encoding.logical {
203            LogicalEncoding::None => {
204                // Caller should have used the direct-output path; this is a fallback.
205                dec.consume_items::<u64>(num)?;
206                Ok(data.to_vec())
207            }
208            LogicalEncoding::Rle(rle) => rle.decode(data, dec),
209            LogicalEncoding::Delta => decode_zigzag_delta::<i64, _>(data, dec),
210            LogicalEncoding::DeltaRle(rle) => {
211                let expanded = rle.decode(data, dec)?;
212                decode_zigzag_delta::<i64, _>(&expanded, dec)
213            }
214            LogicalEncoding::ComponentwiseDelta
215            | LogicalEncoding::Morton(_)
216            | LogicalEncoding::MortonDelta(_)
217            | LogicalEncoding::MortonRle(_) => Err(UnsupportedLogicalEncoding(
218                self.meta.encoding.logical,
219                "u64",
220            )),
221        }
222    }
223}
224
225#[cfg(test)]
226mod tests {
227    use super::*;
228    use crate::MltError::InvalidDecodingStreamSize;
229    use crate::test_helpers::dec;
230
231    #[test]
232    fn test_decode_rle_empty() {
233        let rle = RleMeta::Split {
234            runs: 0,
235            num_rle_values: 0,
236        };
237        assert_eq!(rle.decode::<u32>(&[], &mut dec()).unwrap(), [] as [u32; 0]);
238    }
239
240    #[test]
241    fn test_decode_rle_invalid_stream_size() {
242        // Valid RLE for runs=2 needs 4 elements (2 run lengths + 2 values). Only 3 provided.
243        let rle = RleMeta::Split {
244            runs: 2,
245            num_rle_values: 3,
246        };
247        let data = [1u32, 2, 3];
248        let err = rle.decode::<u32>(&data, &mut dec()).unwrap_err();
249        assert!(matches!(err, InvalidDecodingStreamSize(3, 4)));
250    }
251
252    #[cfg(feature = "unstable-v2")]
253    #[test]
254    fn test_decode_rle_interleaved() {
255        let rle = RleMeta::Interleaved { num_rle_values: 6 };
256        // (3 × 7), (1 × 9), (2 × 7)
257        let data = [3u32, 7, 1, 9, 2, 7];
258        let decoded = rle.decode(&data, &mut dec()).unwrap();
259        assert_eq!(decoded, vec![7, 7, 7, 9, 7, 7]);
260    }
261
262    #[cfg(feature = "unstable-v2")]
263    #[test]
264    fn test_decode_rle_interleaved_empty() {
265        let rle = RleMeta::Interleaved { num_rle_values: 0 };
266        assert!(rle.decode::<u32>(&[], &mut dec()).unwrap().is_empty());
267    }
268
269    #[cfg(feature = "unstable-v2")]
270    #[test]
271    fn test_decode_rle_interleaved_count_mismatch() {
272        // Runs sum to 4, but the context count declares 5.
273        let rle = RleMeta::Interleaved { num_rle_values: 5 };
274        let data = [3u32, 7, 1, 9];
275        assert!(rle.decode(&data, &mut dec()).is_err());
276    }
277
278    #[cfg(feature = "unstable-v2")]
279    #[test]
280    fn test_decode_rle_interleaved_odd_length() {
281        let rle = RleMeta::Interleaved { num_rle_values: 3 };
282        let data = [3u32, 7, 1];
283        assert!(rle.decode(&data, &mut dec()).is_err());
284    }
285
286    #[cfg(feature = "unstable-v2")]
287    #[test]
288    fn test_decode_rle_interleaved_overflowing_run() {
289        // A single run larger than the declared count must not over-allocate.
290        let rle = RleMeta::Interleaved { num_rle_values: 2 };
291        let data = [u32::MAX, 7];
292        assert!(rle.decode(&data, &mut dec()).is_err());
293    }
294}