1use bitvec::vec::BitVec;
2
3use crate::decoder::{Morton, PropKind, TileLayer};
4use crate::encoder::model::{CurveParams, StagedLayer};
5use crate::encoder::property::encode::write_properties;
6use crate::encoder::{
7 Codecs, Encoder, EncoderConfig, SortStrategy, StagedId, spatial_sort_likely_to_help,
8};
9use crate::{MltError, MltResult, PropValue};
10
11impl StagedLayer {
12 #[hotpath::measure]
19 pub fn encode_into(self, mut enc: Encoder, codecs: &mut Codecs) -> MltResult<Encoder> {
20 let column_count = usize::from(!matches!(&self.id, StagedId::None))
21 + 1 + self.properties.len();
23
24 let Self {
25 name,
26 extent,
27 id,
28 geometry,
29 properties,
30 } = self;
31
32 id.write_to(&mut enc, codecs)?;
33 geometry.write_to(&mut enc, codecs)?;
34 write_properties(&properties, &mut enc, codecs)?;
35 enc.write_header(&name, extent, column_count)?;
36
37 Ok(enc)
38 }
39}
40
41fn seed_curve_caches(enc: &mut Encoder, curve_params: CurveParams) {
46 enc.hilbert_cache = Some(curve_params);
47 enc.morton_cache = Morton::new(curve_params.bits, curve_params.shift).ok();
48}
49
50const SORT_TRIAL_THRESHOLD: usize = 512;
53
54impl TileLayer {
55 #[hotpath::measure]
65 pub fn encode(self, cfg: EncoderConfig) -> MltResult<Vec<u8>> {
66 if self.features.is_empty() {
67 return Ok(Vec::new());
68 }
69
70 let mut sort_by = vec![SortStrategy::Unsorted];
71 let try_spatial_sort = cfg.try_spatial_morton_sort || cfg.try_spatial_hilbert_sort;
72 if try_spatial_sort
73 && (self.features.len() < SORT_TRIAL_THRESHOLD || spatial_sort_likely_to_help(&self))
74 {
75 if cfg.try_spatial_morton_sort {
76 sort_by.push(SortStrategy::SpatialMorton);
77 }
78 if cfg.try_spatial_hilbert_sort {
79 sort_by.push(SortStrategy::SpatialHilbert);
80 }
81 }
82 if cfg.try_id_sort {
83 sort_by.push(SortStrategy::Id);
84 }
85
86 let stats = self.analyze(cfg.allow_shared_dict)?;
87 let curve_params = self.curve_params();
90
91 let mut enc = Encoder::new(cfg);
95 seed_curve_caches(&mut enc, curve_params);
96
97 let (last, init) = sort_by.split_last().expect("at least one strategy");
98 if init.is_empty() {
99 let mut codecs = Codecs::default();
100 StagedLayer::from_tile(self, *last, &stats, cfg.tessellate, curve_params)
101 .encode_into(enc, &mut codecs)?
102 } else {
103 let mut codecs = Codecs::default();
104 enc = {
105 let first = init[0];
106 StagedLayer::from_tile(self.clone(), first, &stats, cfg.tessellate, curve_params)
107 .encode_into(enc, &mut codecs)?
108 };
109 let mut best = enc.preserve_results();
110 for &sort in &init[1..] {
112 let layer = StagedLayer::from_tile(
113 self.clone(),
114 sort,
115 &stats,
116 cfg.tessellate,
117 curve_params,
118 );
119 enc = layer.encode_into(enc, &mut codecs)?;
120 if enc.total_len() < best.total_len() {
121 best = enc.preserve_results();
122 }
123 }
124 let layer = StagedLayer::from_tile(self, *last, &stats, cfg.tessellate, curve_params);
126 enc = layer.encode_into(enc, &mut codecs)?;
127 if enc.total_len() < best.total_len() {
128 best = enc.preserve_results();
129 }
130 best
131 }
132 .into_layer_bytes()
133 }
134}
135
136#[derive(Debug, Clone, Copy, PartialEq, Eq)]
138pub enum Presence {
139 AllNull,
141 AllPresent,
143 Mixed,
145 SameAsProp(usize),
147}
148impl Presence {
149 #[must_use]
151 pub fn from_bits(bits: &BitVec<u8>, existing: &[(BitVec<u8>, usize)]) -> Self {
152 if bits.not_any() {
153 Self::AllNull
154 } else if bits.all() {
155 Self::AllPresent
156 } else if let Some((_, idx)) = existing.iter().find(|(v, _)| v == bits) {
157 Self::SameAsProp(*idx)
158 } else {
159 Self::Mixed
160 }
161 }
162}
163
164#[derive(Debug, Clone, PartialEq, Eq)]
166pub enum SharedDictRole {
167 None,
169 Owner(String),
171 Member(usize),
173}
174
175#[derive(Debug, Clone, PartialEq, Eq)]
177pub struct PropertyStats {
178 pub presence: Presence,
179 pub stats: PropertyTypedStats,
180}
181
182#[derive(Debug, Clone, PartialEq, Eq)]
184pub struct LayerStats {
185 pub id: Option<PropertyStats>,
186 pub properties: Vec<PropertyStats>,
187}
188
189#[derive(Debug, Clone, Default, PartialEq, Eq)]
191pub enum PropertyTypedStats {
192 #[default]
194 None,
195 Bool,
196 Signed {
197 min: i64,
198 max: i64,
199 },
200 Unsigned {
201 min: u64,
202 max: u64,
203 },
204 F32,
205 F64,
206 String {
207 shared_dict: SharedDictRole,
208 },
209}
210
211impl PropertyTypedStats {
212 #[must_use]
213 pub fn values_fit_u32(&self) -> bool {
214 match self {
215 Self::None | Self::Bool | Self::F32 | Self::F64 | Self::String { .. } => false,
216 Self::Signed { min, max } => *min >= 0 && u32::try_from(*max).is_ok(),
217 Self::Unsigned { max, .. } => u32::try_from(*max).is_ok(),
218 }
219 }
220
221 #[must_use]
222 pub fn shared_dict(&self) -> SharedDictRole {
223 match self {
224 Self::String { shared_dict, .. } => shared_dict.clone(),
225 _ => SharedDictRole::None,
226 }
227 }
228
229 pub(crate) fn set_shared_dict(&mut self, role: SharedDictRole) {
230 match self {
231 Self::String { shared_dict, .. } => *shared_dict = role,
232 _ => debug_assert_eq!(role, SharedDictRole::None),
233 }
234 }
235
236 pub(crate) fn push(
237 &mut self,
238 prop: &PropValue,
239 column_idx: usize,
240 property_name: &str,
241 ) -> MltResult<bool> {
242 match prop {
243 PropValue::Bool(Some(_)) => {
244 self.merge_same_kind(Self::Bool, column_idx, property_name)?;
245 }
246 PropValue::I8(Some(v)) => {
247 self.merge_signed(i64::from(*v), column_idx, property_name)?;
248 }
249 PropValue::U8(Some(v)) => {
250 self.merge_unsigned(u64::from(*v), column_idx, property_name)?;
251 }
252 PropValue::I32(Some(v)) => {
253 self.merge_signed(i64::from(*v), column_idx, property_name)?;
254 }
255 PropValue::U32(Some(v)) => {
256 self.merge_unsigned(u64::from(*v), column_idx, property_name)?;
257 }
258 PropValue::I64(Some(v)) => self.merge_signed(*v, column_idx, property_name)?,
259 PropValue::U64(Some(v)) => self.merge_unsigned(*v, column_idx, property_name)?,
260 PropValue::F32(Some(_)) => {
261 self.merge_same_kind(Self::F32, column_idx, property_name)?;
262 }
263 PropValue::F64(Some(_)) => {
264 self.merge_same_kind(Self::F64, column_idx, property_name)?;
265 }
266 PropValue::Str(Some(_)) => self.merge_string(column_idx, property_name)?,
267 _ => return Ok(false),
268 }
269 Ok(true)
270 }
271
272 fn merge_signed(
273 &mut self,
274 value: i64,
275 column_idx: usize,
276 property_name: &str,
277 ) -> MltResult<()> {
278 match self {
279 Self::None => {
280 *self = Self::Signed {
281 min: value,
282 max: value,
283 };
284 }
285 Self::Signed { min, max } => {
286 *min = (*min).min(value);
287 *max = (*max).max(value);
288 }
289 _ => return mixed_prop_err(column_idx, property_name),
290 }
291 Ok(())
292 }
293
294 fn merge_unsigned(
295 &mut self,
296 value: u64,
297 column_idx: usize,
298 property_name: &str,
299 ) -> MltResult<()> {
300 match self {
301 Self::None => {
302 *self = Self::Unsigned {
303 min: value,
304 max: value,
305 };
306 }
307 Self::Unsigned { min, max } => {
308 *min = (*min).min(value);
309 *max = (*max).max(value);
310 }
311 _ => return mixed_prop_err(column_idx, property_name),
312 }
313 Ok(())
314 }
315
316 fn merge_string(&mut self, column_idx: usize, property_name: &str) -> MltResult<()> {
317 match self {
318 Self::None => {
319 *self = Self::String {
320 shared_dict: SharedDictRole::None,
321 };
322 }
323 Self::String { .. } => {}
324 _ => return mixed_prop_err(column_idx, property_name),
325 }
326 Ok(())
327 }
328
329 fn merge_same_kind(
330 &mut self,
331 kind: Self,
332 column_idx: usize,
333 property_name: &str,
334 ) -> MltResult<()> {
335 match self {
336 Self::None => *self = kind,
337 Self::Bool if matches!(kind, Self::Bool) => {}
338 Self::F32 if matches!(kind, Self::F32) => {}
339 Self::F64 if matches!(kind, Self::F64) => {}
340 _ => return mixed_prop_err(column_idx, property_name),
341 }
342 Ok(())
343 }
344}
345
346impl TileLayer {
347 #[hotpath::measure]
349 pub(crate) fn analyze(&self, allow_shared_dict: bool) -> MltResult<LayerStats> {
350 let mut property_bits = Vec::with_capacity(self.property_names.len());
351 let mut properties = self.analyze_properties(&mut property_bits)?;
352 let id = self.analyze_ids(&property_bits);
353 if allow_shared_dict {
354 self.group_string_properties(&mut properties);
355 }
356 Ok(LayerStats { id, properties })
357 }
358
359 fn analyze_ids(&self, property_bits: &[(BitVec<u8>, usize)]) -> Option<PropertyStats> {
360 let mut min = u64::MAX;
361 let mut max = 0u64;
362 let mut bits = BitVec::<u8>::with_capacity(self.features.len());
363 for feature in &self.features {
364 if let Some(id) = feature.id {
365 min = min.min(id);
366 max = max.max(id);
367 bits.push(true);
368 } else {
369 bits.push(false);
370 }
371 }
372 let presence = Presence::from_bits(&bits, property_bits);
373 if presence == Presence::AllNull {
374 None
375 } else {
376 Some(PropertyStats {
377 presence,
378 stats: PropertyTypedStats::Unsigned { min, max },
379 })
380 }
381 }
382
383 fn analyze_properties(
384 &self,
385 property_bits: &mut Vec<(BitVec<u8>, usize)>,
386 ) -> MltResult<Vec<PropertyStats>> {
387 self.property_names
388 .iter()
389 .enumerate()
390 .map(|(col_idx, name)| -> MltResult<PropertyStats> {
391 let mut kind = None;
392 let mut stats = PropertyTypedStats::default();
393 let mut bits = BitVec::<u8>::with_capacity(self.features.len());
394 for feature in &self.features {
395 let prop = feature.properties.get(col_idx);
396 if let Some(prop_kind) = prop.map(PropKind::from) {
397 match kind {
398 Some(kind) if kind != prop_kind => {
399 return mixed_prop_err(col_idx, name.as_str());
400 }
401 None => kind = Some(prop_kind),
402 _ => {}
403 }
404 }
405 if let Some(prop) = prop
406 && stats.push(prop, col_idx, name)?
407 {
408 bits.push(true);
409 } else {
410 bits.push(false);
411 }
412 }
413
414 let presence = Presence::from_bits(&bits, property_bits);
415 if presence == Presence::Mixed {
416 property_bits.push((bits, col_idx));
417 }
418 Ok(PropertyStats { presence, stats })
419 })
420 .collect()
421 }
422}
423
424#[inline]
425fn mixed_prop_err<T>(column_idx: usize, property_name: &str) -> MltResult<T> {
426 Err(MltError::MixedPropertyTypes(
427 column_idx,
428 property_name.to_owned(),
429 ))
430}