Skip to main content

mlt_core/decoder/
into_tile.rs

1//! Conversion from the decoded columnar form into the row-oriented
2//! [`crate::tile`] model.
3//!
4//! [`ParsedLayer01::into_tile`] materializes one [`TileFeature`] per map
5//! feature, owning its geometry and property values outright. That is the form
6//! the optimizer, the sorting pipeline, and the converters work in.
7
8use crate::decoder::{Layer01, ParsedLayer01, ParsedProperty, PropValueRef};
9use crate::errors::AsMltError as _;
10use crate::tile::{PropValue, TileFeature, TileLayer};
11use crate::{Decoder, LendingIterator, MltResult};
12
13impl ParsedLayer01<'_> {
14    /// Decode and convert into a row-oriented [`TileLayer`], charging every
15    /// heap allocation against `dec`.
16    pub fn into_tile(self, dec: &mut Decoder) -> MltResult<TileLayer> {
17        // Extract owned/copied fields before borrowing self for the feature iterator.
18        let name = self.name().to_string();
19        let extent = self.extent().get();
20        let names: Vec<String> = self.iterate_prop_names().map(|n| n.to_string()).collect();
21        let col_nulls = typed_nulls(&self.properties);
22        let mut features = dec.alloc::<TileFeature>(self.feature_count())?;
23        let mut feat_iter = self.iter_features();
24        while let Some(feat) = feat_iter.next() {
25            let feat = feat?;
26            let mut values = dec.alloc::<PropValue>(names.len())?;
27            for (col_idx, value) in feat.iter_all_properties().enumerate() {
28                values.push(match value {
29                    Some(v) => prop_value_from_ref(v),
30                    None => col_nulls[col_idx].clone(),
31                });
32            }
33
34            charge_str_props(dec, &values)?;
35
36            features.push(TileFeature {
37                id: feat.id(),
38                geometry: feat.geometry().clone(),
39                properties: values,
40            });
41        }
42
43        TileLayer::from_parts(name, extent, names, features)
44    }
45}
46
47impl Layer01<'_> {
48    /// Decode and convert into a row-oriented [`TileLayer`]
49    pub fn into_tile(self, dec: &mut Decoder) -> MltResult<TileLayer> {
50        self.decode_all(dec)?.into_tile(dec)
51    }
52}
53
54/// Convert a [`PropValueRef`] (as yielded by [`crate::FeatureRef::iter_all_properties`])
55/// into an owned [`PropValue`].
56fn prop_value_from_ref(value: PropValueRef<'_>) -> PropValue {
57    match value {
58        PropValueRef::Bool(v) => PropValue::Bool(Some(v)),
59        PropValueRef::I8(v) => PropValue::I8(Some(v)),
60        PropValueRef::U8(v) => PropValue::U8(Some(v)),
61        PropValueRef::I32(v) => PropValue::I32(Some(v)),
62        PropValueRef::U32(v) => PropValue::U32(Some(v)),
63        PropValueRef::I64(v) => PropValue::I64(Some(v)),
64        PropValueRef::U64(v) => PropValue::U64(Some(v)),
65        PropValueRef::F32(v) => PropValue::F32(Some(v)),
66        PropValueRef::F64(v) => PropValue::F64(Some(v)),
67        PropValueRef::Str(s) => PropValue::Str(Some(s.to_string())),
68    }
69}
70
71/// Build a flat list of typed null [`PropValue`]s, one per logical column position
72/// as yielded by [`crate::FeatureRef::iter_all_properties`].
73///
74/// Each scalar column contributes one entry with its specific null variant (e.g.
75/// `PropValue::Bool(None)`).  A `SharedDict` column expands to one `PropValue::Str(None)`
76/// entry per sub-item.
77fn typed_nulls(properties: &[ParsedProperty<'_>]) -> Vec<PropValue> {
78    use ParsedProperty as PP;
79    use PropValue as PV;
80    let mut nulls = Vec::new();
81    for prop in properties {
82        match prop {
83            PP::Bool(_) => nulls.push(PV::Bool(None)),
84            PP::I8(_) => nulls.push(PV::I8(None)),
85            PP::U8(_) => nulls.push(PV::U8(None)),
86            PP::I32(_) => nulls.push(PV::I32(None)),
87            PP::U32(_) => nulls.push(PV::U32(None)),
88            PP::I64(_) => nulls.push(PV::I64(None)),
89            PP::U64(_) => nulls.push(PV::U64(None)),
90            PP::F32(_) => nulls.push(PV::F32(None)),
91            PP::F64(_) => nulls.push(PV::F64(None)),
92            PP::Str(_) => nulls.push(PV::Str(None)),
93            PP::SharedDict(d) => {
94                for _ in &d.items {
95                    nulls.push(PV::Str(None));
96                }
97            }
98        }
99    }
100    nulls
101}
102
103/// Charge `dec` for the heap bytes of owned `String` values inside `PropValue::Str`.
104fn charge_str_props(dec: &mut Decoder, props: &[PropValue]) -> MltResult<()> {
105    let str_bytes = props
106        .iter()
107        .filter_map(|p| {
108            if let PropValue::Str(Some(s)) = p {
109                Some(s.len())
110            } else {
111                None
112            }
113        })
114        .try_fold(0u32, |acc, n| {
115            acc.checked_add(u32::try_from(n).or_overflow()?)
116                .or_overflow()
117        })?;
118    if str_bytes > 0 {
119        dec.consume(str_bytes)?;
120    }
121    Ok(())
122}