Skip to main content

mlt_core/decoder/
tile.rs

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