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};
8#[cfg(feature = "unstable-v2")]
9use crate::codecs::zigzag::decode_componentwise_delta_vec3s;
10use crate::codecs::zigzag::{
11    decode_componentwise_delta_vec2s, decode_componentwise_delta2_vec2s, decode_zigzag,
12    decode_zigzag_delta, decode_zigzag_delta2,
13};
14#[cfg(feature = "unstable-v2")]
15use crate::decoder::XyzLogical;
16use crate::decoder::{
17    FloatLogical, IntLogical, LogicalEncoding, LogicalTechnique, LogicalValue, RleMeta, StreamMeta,
18    VertexLogical,
19};
20use crate::errors::{AsMltError as _, fail_if_invalid_stream_size};
21use crate::{Decoder, MltResult};
22
23impl RleMeta {
24    /// Decode RLE (Run-Length Encoding) data.
25    ///
26    /// A run-length encoded stream is a sequence of runs, each a `(run_len, value)` pair
27    /// that expands to `run_len` copies of `value`.
28    /// The two layouts differ only in how those pairs are arranged in the stream:
29    ///
30    /// ```text
31    /// logical values:   [7, 7, 7, 9, 5, 5]     runs = 3, num_rle_values = 6
32    /// Split (v1):       [3, 1, 2][7, 9, 5]     all run lengths, then all values
33    /// Interleaved (v2): [3, 7][1, 9][2, 5]     one (run_len, value) pair per run
34    /// ```
35    ///
36    /// Split reads `runs` from the stream header, so the split point is known up front.
37    /// Interleaved derives the run count from the data length, which must therefore be even.
38    /// Both know `num_rle_values`, the expanded element count that the run lengths must sum to.
39    ///
40    /// Charges the decoder for the expanded output allocation.
41    pub fn decode<T: PrimInt + Debug>(self, data: &[T], dec: &mut Decoder) -> MltResult<Vec<T>> {
42        match self {
43            Self::Split {
44                runs,
45                num_rle_values,
46            } => Self::decode_split(runs, num_rle_values, data, dec),
47            #[cfg(feature = "unstable-v2")]
48            Self::Interleaved { num_rle_values } => {
49                Self::decode_interleaved(num_rle_values, data, dec)
50            }
51        }
52    }
53
54    /// Tag `0x01` layout: `[run_len × runs][value × runs]`, with `runs` from the header.
55    fn decode_split<T: PrimInt + Debug>(
56        runs: u32,
57        num_rle_values: u32,
58        data: &[T],
59        dec: &mut Decoder,
60    ) -> MltResult<Vec<T>> {
61        let expected_len = runs.into_usize().checked_mul(2).or_overflow()?;
62        fail_if_invalid_stream_size(data.len(), expected_len)?;
63
64        let (run_lens, values) = data.split_at(runs.into_usize());
65        fail_if_invalid_stream_size(
66            num_rle_values.into_usize(),
67            Self::calc_size(run_lens)?.into_usize(),
68        )?;
69
70        let alloc_size = num_rle_values.into_usize();
71        let mut result = dec.alloc(alloc_size)?;
72        for (&run_len, &val) in run_lens.iter().zip(values.iter()) {
73            let run = run_len
74                .to_usize()
75                .ok_or_else(|| RleRunLenInvalid(run_len.to_i128().unwrap_or_default()))?;
76            result.extend(repeat_n(val, run));
77        }
78        dec.adjust_alloc(&result, alloc_size)?;
79        Ok(result)
80    }
81
82    /// Tag `0x02` layout: `(run_len, value)` pairs, with the run count derived from the data length.
83    /// `num_rle_values` comes from the stream's count context rather than its own varint.
84    #[cfg(feature = "unstable-v2")]
85    fn decode_interleaved<T: PrimInt + Debug>(
86        num_rle_values: u32,
87        data: &[T],
88        dec: &mut Decoder,
89    ) -> MltResult<Vec<T>> {
90        if !data.len().is_multiple_of(2) {
91            return Err(RleRunLenInvalid(data.len().to_i128().unwrap_or_default()));
92        }
93        let alloc_size = num_rle_values.into_usize();
94        let mut result = dec.alloc(alloc_size)?;
95        for [cnt, val] in data.as_chunks::<2>().0 {
96            let run = cnt
97                .to_usize()
98                .filter(|&run| run <= alloc_size - result.len())
99                .ok_or_else(|| RleRunLenInvalid(cnt.to_i128().unwrap_or_default()))?;
100            result.extend(repeat_n(val, run));
101        }
102        // The expanded count must exactly match the count declared by the stream context.
103        fail_if_invalid_stream_size(result.len(), alloc_size)?;
104        dec.adjust_alloc(&result, alloc_size)?;
105        Ok(result)
106    }
107
108    fn calc_size<T: PrimInt + Debug>(run_lens: &[T]) -> MltResult<u32> {
109        run_lens
110            .iter()
111            .try_fold(T::zero(), |a, v| a.checked_add(v))
112            .and_then(|v| v.to_u32())
113            .ok_or_else(|| RleRunLenInvalid(run_lens.len().to_i128().unwrap_or_default()))
114    }
115}
116
117impl LogicalTechnique {
118    pub fn parse(value: u8) -> MltResult<Self> {
119        Self::try_from(value).or(Err(ParsingLogicalTechnique(value)))
120    }
121}
122
123impl LogicalValue {
124    #[must_use]
125    pub fn new(meta: StreamMeta) -> Self {
126        Self { meta }
127    }
128
129    /// Logically decode `data` (physically decoded u32 words) into `Vec<i32>`.
130    ///
131    /// Never called for `LogicalEncoding::None` - that case is handled directly
132    /// in the bridge (physical buffer decoded into a fresh output Vec).
133    pub fn decode_i32(self, data: &[u32], dec: &mut Decoder) -> MltResult<Vec<i32>> {
134        use IntLogical as IL;
135        use LogicalEncoding as LE;
136        use VertexLogical as VL;
137
138        match self.meta.encoding.logical {
139            LE::Int(IL::None) | LE::Vertex(VL::None) => decode_zigzag(data, dec),
140            LE::Int(IL::Rle(v)) => decode_zigzag(&v.decode(data, dec)?, dec),
141            LE::Vertex(VL::ComponentwiseDelta) => decode_componentwise_delta_vec2s(data, dec),
142            LE::Int(IL::Delta) | LE::Vertex(VL::Delta) => decode_zigzag_delta::<i32, _>(data, dec),
143            LE::Int(IL::DeltaRle(v)) => {
144                let expanded = v.decode(data, dec)?;
145                decode_zigzag_delta::<i32, _>(&expanded, dec)
146            }
147            LE::Int(IL::Delta2) => decode_zigzag_delta2::<i32, _>(data, dec),
148            LE::Vertex(VL::ComponentwiseDelta2) => decode_componentwise_delta2_vec2s(data, dec),
149            LE::Vertex(VL::MortonDelta(v)) => v.decode_delta(data, dec),
150            LE::Vertex(VL::Rans) => Err(UnsupportedLogicalEncoding(
151                self.meta.encoding.logical,
152                "a stream decoded without the layer's topology",
153            )),
154            #[cfg(feature = "unstable-v2")]
155            LE::Vertex(VL::Xyz(_, xyz)) => {
156                if !data.len().is_multiple_of(3) {
157                    return Err(crate::MltError::InvalidTripleStreamSize(data.len()));
158                }
159                match xyz {
160                    XyzLogical::None => decode_zigzag(data, dec),
161                    XyzLogical::Delta => decode_zigzag_delta::<i32, _>(data, dec),
162                    XyzLogical::ComponentwiseDelta => decode_componentwise_delta_vec3s(data, dec),
163                }
164            }
165            LE::Bool(_) | LE::Float(_) => Err(UnsupportedLogicalEncoding(
166                self.meta.encoding.logical,
167                "i32",
168            )),
169        }
170    }
171
172    /// Logically decode `data` (physically decoded u32 words) into `Vec<u32>`.
173    ///
174    /// Not called for `LogicalEncoding::None` - that case is handled entirely
175    /// in the bridge (physical buffer decoded directly into the output Vec).
176    pub fn decode_u32(self, data: &[u32], dec: &mut Decoder) -> MltResult<Vec<u32>> {
177        let num = self.meta.num_values.into_usize();
178        match self.meta.encoding.logical {
179            LogicalEncoding::Int(IntLogical::None) | LogicalEncoding::Float(FloatLogical::Dict) => {
180                // Caller should have used the direct-output path; this is a fallback.
181                dec.consume_items::<u32>(num)?;
182                Ok(data.to_vec())
183            }
184            LogicalEncoding::Int(IntLogical::Rle(rle)) => rle.decode(data, dec),
185            LogicalEncoding::Int(IntLogical::Delta) => decode_zigzag_delta::<i32, _>(data, dec),
186            LogicalEncoding::Int(IntLogical::DeltaRle(rle)) => {
187                decode_zigzag_delta::<i32, _>(&rle.decode(data, dec)?, dec)
188            }
189            LogicalEncoding::Int(IntLogical::Delta2) => decode_zigzag_delta2::<i32, _>(data, dec),
190            LogicalEncoding::Bool(_) | LogicalEncoding::Float(_) | LogicalEncoding::Vertex(_) => {
191                Err(UnsupportedLogicalEncoding(
192                    self.meta.encoding.logical,
193                    "u32",
194                ))
195            }
196        }
197    }
198
199    /// Logically decode `data` (physically decoded u64 words) into `Vec<i64>`.
200    ///
201    /// Never called for `LogicalEncoding::None` - that case is handled directly
202    /// in the bridge (physical buffer decoded into a fresh output Vec).
203    pub fn decode_i64(self, data: &[u64], dec: &mut Decoder) -> MltResult<Vec<i64>> {
204        match self.meta.encoding.logical {
205            LogicalEncoding::Int(IntLogical::None) => decode_zigzag(data, dec),
206            LogicalEncoding::Int(IntLogical::Delta) => decode_zigzag_delta::<i64, _>(data, dec),
207            LogicalEncoding::Int(IntLogical::DeltaRle(rle)) => {
208                let expanded = rle.decode(data, dec)?;
209                decode_zigzag_delta::<i64, _>(&expanded, dec)
210            }
211            LogicalEncoding::Int(IntLogical::Rle(rle)) => {
212                // rle.decode() charges for expanded u64 vec; decode_zigzag charges for i64 vec
213                let expanded = rle.decode(data, dec)?;
214                decode_zigzag(&expanded, dec)
215            }
216            LogicalEncoding::Int(IntLogical::Delta2) => decode_zigzag_delta2::<i64, _>(data, dec),
217            LogicalEncoding::Bool(_) | LogicalEncoding::Float(_) | LogicalEncoding::Vertex(_) => {
218                Err(UnsupportedLogicalEncoding(
219                    self.meta.encoding.logical,
220                    "i64",
221                ))
222            }
223        }
224    }
225
226    /// Logically decode `data` (physically decoded u64 words) into `Vec<u64>`.
227    ///
228    /// Not called for `LogicalEncoding::None` - that case is handled entirely
229    /// in the bridge (physical buffer decoded directly into the output Vec).
230    pub fn decode_u64(self, data: &[u64], dec: &mut Decoder) -> MltResult<Vec<u64>> {
231        let num = self.meta.num_values.into_usize();
232        match self.meta.encoding.logical {
233            LogicalEncoding::Int(IntLogical::None) | LogicalEncoding::Float(FloatLogical::Dict) => {
234                // Caller should have used the direct-output path; this is a fallback.
235                dec.consume_items::<u64>(num)?;
236                Ok(data.to_vec())
237            }
238            LogicalEncoding::Int(IntLogical::Rle(rle)) => rle.decode(data, dec),
239            LogicalEncoding::Int(IntLogical::Delta) => decode_zigzag_delta::<i64, _>(data, dec),
240            LogicalEncoding::Int(IntLogical::DeltaRle(rle)) => {
241                let expanded = rle.decode(data, dec)?;
242                decode_zigzag_delta::<i64, _>(&expanded, dec)
243            }
244            LogicalEncoding::Int(IntLogical::Delta2) => decode_zigzag_delta2::<i64, _>(data, dec),
245            LogicalEncoding::Bool(_) | LogicalEncoding::Float(_) | LogicalEncoding::Vertex(_) => {
246                Err(UnsupportedLogicalEncoding(
247                    self.meta.encoding.logical,
248                    "u64",
249                ))
250            }
251        }
252    }
253}
254
255#[cfg(test)]
256mod tests {
257    use super::*;
258    use crate::MltError::InvalidDecodingStreamSize;
259    use crate::test_helpers::dec;
260
261    #[test]
262    fn test_decode_rle_empty() {
263        let rle = RleMeta::Split {
264            runs: 0,
265            num_rle_values: 0,
266        };
267        assert_eq!(rle.decode::<u32>(&[], &mut dec()).unwrap(), [] as [u32; 0]);
268    }
269
270    #[test]
271    fn test_decode_rle_invalid_stream_size() {
272        // Valid RLE for runs=2 needs 4 elements (2 run lengths + 2 values). Only 3 provided.
273        let rle = RleMeta::Split {
274            runs: 2,
275            num_rle_values: 3,
276        };
277        let data = [1u32, 2, 3];
278        let err = rle.decode::<u32>(&data, &mut dec()).unwrap_err();
279        assert!(matches!(err, InvalidDecodingStreamSize(3, 4)));
280    }
281
282    fn value(logical: LogicalEncoding) -> LogicalValue {
283        use crate::decoder::{DictionaryType, IntEncoding, PhysicalEncoding, StreamType};
284
285        let encoding = IntEncoding::new(logical, PhysicalEncoding::VarInt);
286        LogicalValue::new(StreamMeta::new(
287            StreamType::Data(DictionaryType::None),
288            encoding,
289            2,
290        ))
291    }
292
293    #[test]
294    fn a_raw_u64_stream_passes_through() {
295        let decoded = value(LogicalEncoding::Int(IntLogical::None))
296            .decode_u64(&[7, 9], &mut dec())
297            .unwrap();
298        assert_eq!(decoded, [7, 9]);
299    }
300
301    #[rstest::rstest]
302    #[case::i32_bool(LogicalEncoding::Bool(crate::decoder::BoolLogical::None), "i32")]
303    #[case::i32_float(LogicalEncoding::Float(FloatLogical::None), "i32")]
304    #[case::u32_vertex(LogicalEncoding::Vertex(VertexLogical::None), "u32")]
305    #[case::i64_bool(LogicalEncoding::Bool(crate::decoder::BoolLogical::None), "i64")]
306    #[case::u64_float(LogicalEncoding::Float(FloatLogical::None), "u64")]
307    fn a_decoder_rejects_an_encoding_of_another_kind(
308        #[case] logical: LogicalEncoding,
309        #[case] target: &str,
310    ) {
311        let v = value(logical);
312        let err = match target {
313            "u32" => v.decode_u32(&[], &mut dec()).map(drop),
314            "i64" => v.decode_i64(&[], &mut dec()).map(drop),
315            "u64" => v.decode_u64(&[], &mut dec()).map(drop),
316            _ => v.decode_i32(&[], &mut dec()).map(drop),
317        }
318        .unwrap_err();
319        assert!(matches!(err, UnsupportedLogicalEncoding(l, t) if l == logical && t == target));
320    }
321
322    #[cfg(feature = "unstable-v2")]
323    #[test]
324    fn test_decode_rle_interleaved() {
325        let rle = RleMeta::Interleaved { num_rle_values: 6 };
326        // (3 × 7), (1 × 9), (2 × 7)
327        let data = [3u32, 7, 1, 9, 2, 7];
328        let decoded = rle.decode(&data, &mut dec()).unwrap();
329        assert_eq!(decoded, vec![7, 7, 7, 9, 7, 7]);
330    }
331
332    #[cfg(feature = "unstable-v2")]
333    #[test]
334    fn test_decode_rle_interleaved_empty() {
335        let rle = RleMeta::Interleaved { num_rle_values: 0 };
336        assert_eq!(
337            rle.decode::<u32>(&[], &mut dec()).unwrap(),
338            Vec::<u32>::new()
339        );
340    }
341
342    #[cfg(feature = "unstable-v2")]
343    #[test]
344    fn test_decode_rle_interleaved_count_mismatch() {
345        // Runs sum to 4, but the context count declares 5.
346        let rle = RleMeta::Interleaved { num_rle_values: 5 };
347        let data = [3u32, 7, 1, 9];
348        assert!(rle.decode(&data, &mut dec()).is_err());
349    }
350
351    #[cfg(feature = "unstable-v2")]
352    #[test]
353    fn test_decode_rle_interleaved_odd_length() {
354        let rle = RleMeta::Interleaved { num_rle_values: 3 };
355        let data = [3u32, 7, 1];
356        assert!(rle.decode(&data, &mut dec()).is_err());
357    }
358
359    #[cfg(feature = "unstable-v2")]
360    #[test]
361    fn test_decode_rle_interleaved_overflowing_run() {
362        // A single run larger than the declared count must not over-allocate.
363        let rle = RleMeta::Interleaved { num_rle_values: 2 };
364        let data = [u32::MAX, 7];
365        assert!(rle.decode(&data, &mut dec()).is_err());
366    }
367
368    #[cfg(feature = "unstable-v2")]
369    fn xyz(logical: XyzLogical) -> LogicalValue {
370        use crate::decoder::{DictionaryType, IntEncoding, PhysicalEncoding, StreamType};
371        use crate::tile::ZStep;
372
373        let encoding = IntEncoding::new(
374            LogicalEncoding::Vertex(VertexLogical::Xyz(ZStep::new(0).unwrap(), logical)),
375            PhysicalEncoding::VarInt,
376        );
377        LogicalValue::new(StreamMeta::new(
378            StreamType::Data(DictionaryType::Vertex),
379            encoding,
380            2,
381        ))
382    }
383
384    #[cfg(feature = "unstable-v2")]
385    #[rstest::rstest]
386    #[case::none(XyzLogical::None, [1, 2, -1, 3, 4, -2])]
387    #[case::delta(XyzLogical::Delta, [1, 3, 2, 5, 9, 7])]
388    #[case::componentwise_delta(XyzLogical::ComponentwiseDelta, [1, 2, -1, 4, 6, -3])]
389    fn an_xyz_stream_decodes_its_triples(#[case] logical: XyzLogical, #[case] expected: [i32; 6]) {
390        let zigzag = [2, 4, 1, 6, 8, 3];
391        assert_eq!(
392            xyz(logical).decode_i32(&zigzag, &mut dec()).unwrap(),
393            expected
394        );
395    }
396
397    #[cfg(feature = "unstable-v2")]
398    #[test]
399    fn an_xyz_stream_of_partial_triples_is_rejected() {
400        let err = xyz(XyzLogical::None)
401            .decode_i32(&[2, 4, 1, 6], &mut dec())
402            .unwrap_err();
403        assert!(matches!(err, crate::MltError::InvalidTripleStreamSize(4)));
404    }
405}