Skip to main content

mlt_core/utils/
analyze.rs

1use std::ops::Deref;
2
3use enum_dispatch::enum_dispatch;
4
5use crate::LazyParsed;
6use crate::decoder::{ParsedProperty, ParsedScalar, ParsedSharedDict, ParsedStrings, StreamMeta};
7
8/// What to calculate with [`Analyze::collect_statistic`].
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub enum StatType {
11    /// Geometry/Feature/id data size in bytes (excludes metadata overhead).
12    DecodedDataSize,
13    /// Metadata overhead in bytes (stream headers, names, extent, geometry types).
14    DecodedMetaSize,
15    /// Number of features (geometry entries).
16    FeatureCount,
17}
18
19/// Trait for estimating various size/count metrics.
20#[enum_dispatch]
21pub trait Analyze {
22    fn collect_statistic(&self, _stat: StatType) -> usize {
23        0
24    }
25
26    /// Call `cb` with the [`StreamMeta`] of every stream contained in `self`.
27    /// Default implementation is a no-op (types that hold no streams).
28    fn for_each_stream(&self, _cb: &mut dyn FnMut(StreamMeta)) {}
29}
30
31macro_rules! impl_statistics_fixed {
32    ($($ty:ty),+) => {
33        $(impl Analyze for $ty {
34            fn collect_statistic(&self, _stat: StatType) -> usize {
35                size_of::<$ty>()
36            }
37        }
38        impl Analyze for &[$ty] {
39            fn collect_statistic(&self, _stat: StatType) -> usize {
40                size_of::<$ty>() * self.len()
41            }
42        })+
43    };
44}
45
46impl_statistics_fixed!(bool, i8, u8, i16, u16, i32, u32, i64, u64, f32, f64);
47
48impl Analyze for String {
49    fn collect_statistic(&self, _stat: StatType) -> usize {
50        self.len()
51    }
52}
53
54impl<T: Analyze> Analyze for Option<T> {
55    fn collect_statistic(&self, stat: StatType) -> usize {
56        self.as_ref().map_or(0, |v| v.collect_statistic(stat))
57    }
58    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {
59        if let Some(v) = self {
60            v.for_each_stream(cb);
61        }
62    }
63}
64
65impl<T: Analyze> Analyze for [T] {
66    fn collect_statistic(&self, stat: StatType) -> usize {
67        self.iter().map(|v| v.collect_statistic(stat)).sum()
68    }
69    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {
70        for v in self {
71            v.for_each_stream(cb);
72        }
73    }
74}
75
76impl<T: Analyze> Analyze for Vec<T> {
77    fn collect_statistic(&self, stat: StatType) -> usize {
78        self.as_slice().collect_statistic(stat)
79    }
80    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {
81        self.as_slice().for_each_stream(cb);
82    }
83}
84
85/// Opt-in marker for blanket `Analyze` delegation via `Deref`.
86///
87/// A type that implements both `Deref<Target = T>` (with `T: Analyze`) and this
88/// marker trait automatically receives an `Analyze` impl that delegates every call
89/// to the dereferenced value.
90pub(crate) trait AnalyzeViaDeref {}
91
92impl<T: Analyze, R: Deref<Target = T> + AnalyzeViaDeref> Analyze for R {
93    fn collect_statistic(&self, stat: StatType) -> usize {
94        (**self).collect_statistic(stat)
95    }
96    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {
97        (**self).for_each_stream(cb);
98    }
99}
100
101impl<Raw: Analyze, Parsed: Analyze> Analyze for LazyParsed<Raw, Parsed> {
102    fn collect_statistic(&self, stat: StatType) -> usize {
103        match self {
104            Self::Raw(encoded) => encoded.collect_statistic(stat),
105            Self::Parsed(decoded) => decoded.collect_statistic(stat),
106            Self::ParsingFailed => 0,
107        }
108    }
109
110    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {
111        match self {
112            Self::Raw(encoded) => encoded.for_each_stream(cb),
113            Self::Parsed(decoded) => decoded.for_each_stream(cb),
114            Self::ParsingFailed => {}
115        }
116    }
117}