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