1use bitvec::vec::BitVec;
2
3use crate::decoder::Morton;
4#[cfg(feature = "unstable-v2")]
5use crate::encoder::encode02;
6use crate::encoder::model::{CurveParams, StagedLayer};
7use crate::encoder::{
8 Codecs, Encoder, EncoderConfig, SortStrategy, WireVersion, encode01,
9 spatial_sort_likely_to_help,
10};
11use crate::tile::{PropKind, TileLayer};
12use crate::{MltError, MltResult, PropValue};
13
14impl StagedLayer {
15 #[hotpath::measure]
22 pub fn encode_into(self, enc: Encoder, codecs: &mut Codecs) -> MltResult<Encoder> {
23 if self.name.is_empty() {
24 return Err(MltError::MissingLayerName);
25 }
26 match enc.config().wire_version() {
27 WireVersion::V01 => encode01::encode_into01(self, enc, codecs),
28 #[cfg(feature = "unstable-v2")]
29 WireVersion::V02 => encode02::encode_into02(self, enc, codecs),
30 }
31 }
32}
33
34fn seed_curve_caches(enc: &mut Encoder, curve_params: CurveParams) {
39 enc.hilbert_cache = Some(curve_params);
40 enc.morton_cache = Morton::new(curve_params.bits, curve_params.shift).ok();
41}
42
43const SORT_TRIAL_THRESHOLD: usize = 512;
46
47impl TileLayer {
48 #[hotpath::measure]
58 pub fn encode(self, cfg: EncoderConfig) -> MltResult<Vec<u8>> {
59 if self.name().is_empty() {
60 return Err(MltError::MissingLayerName);
61 }
62 if self.features().is_empty() {
63 return Ok(Vec::new());
64 }
65
66 let mut sort_by = vec![SortStrategy::Unsorted];
67 let try_spatial_sort =
68 cfg.attempt_spatial_morton_sort() || cfg.attempt_spatial_hilbert_sort();
69 if try_spatial_sort
70 && (self.feature_count() < SORT_TRIAL_THRESHOLD || spatial_sort_likely_to_help(&self))
71 {
72 if cfg.attempt_spatial_morton_sort() {
73 sort_by.push(SortStrategy::SpatialMorton);
74 }
75 if cfg.attempt_spatial_hilbert_sort() {
76 sort_by.push(SortStrategy::SpatialHilbert);
77 }
78 }
79 if cfg.attempt_id_sort() {
80 sort_by.push(SortStrategy::Id);
81 }
82
83 let stats = self.analyze(cfg.allow_shared_dict())?;
84 let curve_params = self.curve_params();
87
88 let mut enc = Encoder::new(cfg);
92 seed_curve_caches(&mut enc, curve_params);
93
94 let (last, init) = sort_by.split_last().expect("at least one strategy");
95 if init.is_empty() {
96 let mut codecs = Codecs::default();
97 StagedLayer::from_tile(self, *last, &stats, cfg.tessellate(), curve_params)
98 .encode_into(enc, &mut codecs)?
99 } else {
100 let mut codecs = Codecs::default();
101 enc = {
102 let first = init[0];
103 StagedLayer::from_tile(self.clone(), first, &stats, cfg.tessellate(), curve_params)
104 .encode_into(enc, &mut codecs)?
105 };
106 let mut best = enc.preserve_results();
107 for &sort in &init[1..] {
109 let layer = StagedLayer::from_tile(
110 self.clone(),
111 sort,
112 &stats,
113 cfg.tessellate(),
114 curve_params,
115 );
116 enc = layer.encode_into(enc, &mut codecs)?;
117 if enc.total_len() < best.total_len() {
118 best = enc.preserve_results();
119 } else {
120 enc.clear_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),
152}
153impl Presence {
154 #[must_use]
156 pub fn from_bits(bits: &BitVec<u8>, existing: &[(BitVec<u8>, usize)]) -> Self {
157 if bits.not_any() {
158 Self::AllNull
159 } else if bits.all() {
160 Self::AllPresent
161 } else if let Some((_, idx)) = existing.iter().find(|(v, _)| v == bits) {
162 Self::SameAsProp(*idx)
163 } else {
164 Self::Mixed
165 }
166 }
167}
168
169#[derive(Debug, Clone, PartialEq, Eq)]
171pub enum SharedDictRole {
172 None,
174 Owner(String),
176 Member(usize),
178}
179
180#[derive(Debug, Clone, PartialEq, Eq)]
182pub struct PropertyStats {
183 pub presence: Presence,
184 pub stats: PropertyTypedStats,
185}
186
187#[derive(Debug, Clone, PartialEq, Eq)]
189pub struct LayerStats {
190 pub id: Option<PropertyStats>,
191 pub properties: Vec<PropertyStats>,
192}
193
194#[derive(Debug, Clone, Default, PartialEq, Eq)]
196pub enum PropertyTypedStats {
197 #[default]
199 None,
200 Bool,
201 Signed {
202 min: i64,
203 max: i64,
204 },
205 Unsigned {
206 min: u64,
207 max: u64,
208 },
209 F32,
210 F64,
211 String {
212 shared_dict: SharedDictRole,
213 },
214}
215
216impl PropertyTypedStats {
217 #[must_use]
218 pub fn values_fit_u32(&self) -> bool {
219 match self {
220 Self::None | Self::Bool | Self::F32 | Self::F64 | Self::String { .. } => false,
221 Self::Signed { min, max } => *min >= 0 && u32::try_from(*max).is_ok(),
222 Self::Unsigned { max, .. } => u32::try_from(*max).is_ok(),
223 }
224 }
225
226 #[must_use]
229 pub fn values_fit_i32(&self) -> bool {
230 match self {
231 Self::None | Self::Bool | Self::F32 | Self::F64 | Self::String { .. } => false,
232 Self::Signed { min, max } => i32::try_from(*min).is_ok() && i32::try_from(*max).is_ok(),
233 Self::Unsigned { max, .. } => i32::try_from(*max).is_ok(),
234 }
235 }
236
237 #[must_use]
238 pub fn shared_dict(&self) -> SharedDictRole {
239 match self {
240 Self::String { shared_dict, .. } => shared_dict.clone(),
241 Self::None
242 | Self::Bool
243 | Self::Signed { .. }
244 | Self::Unsigned { .. }
245 | Self::F32
246 | Self::F64 => SharedDictRole::None,
247 }
248 }
249
250 pub(crate) fn set_shared_dict(&mut self, role: SharedDictRole) {
251 match self {
252 Self::String { shared_dict, .. } => *shared_dict = role,
253 Self::None
254 | Self::Bool
255 | Self::Signed { .. }
256 | Self::Unsigned { .. }
257 | Self::F32
258 | Self::F64 => debug_assert_eq!(role, SharedDictRole::None),
259 }
260 }
261
262 pub(crate) fn push(
263 &mut self,
264 prop: &PropValue,
265 column_idx: usize,
266 property_name: &str,
267 ) -> MltResult<bool> {
268 match prop {
269 PropValue::Bool(Some(_)) => {
270 self.merge_same_kind(Self::Bool, column_idx, property_name)?;
271 }
272 PropValue::I8(Some(v)) => {
273 self.merge_signed(i64::from(*v), column_idx, property_name)?;
274 }
275 PropValue::U8(Some(v)) => {
276 self.merge_unsigned(u64::from(*v), column_idx, property_name)?;
277 }
278 PropValue::I32(Some(v)) => {
279 self.merge_signed(i64::from(*v), column_idx, property_name)?;
280 }
281 PropValue::U32(Some(v)) => {
282 self.merge_unsigned(u64::from(*v), column_idx, property_name)?;
283 }
284 PropValue::I64(Some(v)) => self.merge_signed(*v, column_idx, property_name)?,
285 PropValue::U64(Some(v)) => self.merge_unsigned(*v, column_idx, property_name)?,
286 PropValue::F32(Some(_)) => {
287 self.merge_same_kind(Self::F32, column_idx, property_name)?;
288 }
289 PropValue::F64(Some(_)) => {
290 self.merge_same_kind(Self::F64, column_idx, property_name)?;
291 }
292 PropValue::Str(Some(_)) => self.merge_string(column_idx, property_name)?,
293 PropValue::Bool(None)
294 | PropValue::I8(None)
295 | PropValue::U8(None)
296 | PropValue::I32(None)
297 | PropValue::U32(None)
298 | PropValue::I64(None)
299 | PropValue::U64(None)
300 | PropValue::F32(None)
301 | PropValue::F64(None)
302 | PropValue::Str(None) => return Ok(false),
303 }
304 Ok(true)
305 }
306
307 fn merge_signed(
308 &mut self,
309 value: i64,
310 column_idx: usize,
311 property_name: &str,
312 ) -> MltResult<()> {
313 match self {
314 Self::None => {
315 *self = Self::Signed {
316 min: value,
317 max: value,
318 };
319 }
320 Self::Signed { min, max } => {
321 *min = (*min).min(value);
322 *max = (*max).max(value);
323 }
324 Self::Bool | Self::Unsigned { .. } | Self::F32 | Self::F64 | Self::String { .. } => {
325 return mixed_prop_err(column_idx, property_name);
326 }
327 }
328 Ok(())
329 }
330
331 fn merge_unsigned(
332 &mut self,
333 value: u64,
334 column_idx: usize,
335 property_name: &str,
336 ) -> MltResult<()> {
337 match self {
338 Self::None => {
339 *self = Self::Unsigned {
340 min: value,
341 max: value,
342 };
343 }
344 Self::Unsigned { min, max } => {
345 *min = (*min).min(value);
346 *max = (*max).max(value);
347 }
348 Self::Bool | Self::Signed { .. } | Self::F32 | Self::F64 | Self::String { .. } => {
349 return mixed_prop_err(column_idx, property_name);
350 }
351 }
352 Ok(())
353 }
354
355 fn merge_string(&mut self, column_idx: usize, property_name: &str) -> MltResult<()> {
356 match self {
357 Self::None => {
358 *self = Self::String {
359 shared_dict: SharedDictRole::None,
360 };
361 }
362 Self::String { .. } => {}
363 Self::Bool | Self::Signed { .. } | Self::Unsigned { .. } | Self::F32 | Self::F64 => {
364 return mixed_prop_err(column_idx, property_name);
365 }
366 }
367 Ok(())
368 }
369
370 fn merge_same_kind(
371 &mut self,
372 kind: Self,
373 column_idx: usize,
374 property_name: &str,
375 ) -> MltResult<()> {
376 match self {
377 Self::None => *self = kind,
378 Self::Bool if matches!(kind, Self::Bool) => {}
379 Self::F32 if matches!(kind, Self::F32) => {}
380 Self::F64 if matches!(kind, Self::F64) => {}
381 Self::Bool
382 | Self::Signed { .. }
383 | Self::Unsigned { .. }
384 | Self::F32
385 | Self::F64
386 | Self::String { .. } => return mixed_prop_err(column_idx, property_name),
387 }
388 Ok(())
389 }
390}
391
392impl TileLayer {
393 #[hotpath::measure]
395 pub(crate) fn analyze(&self, allow_shared_dict: bool) -> MltResult<LayerStats> {
396 let mut property_bits = Vec::with_capacity(self.property_names().len());
397 let mut properties = self.analyze_properties(&mut property_bits)?;
398 let id = self.analyze_ids(&property_bits);
399 if allow_shared_dict {
400 self.group_string_properties(&mut properties);
401 }
402 Ok(LayerStats { id, properties })
403 }
404
405 fn analyze_ids(&self, property_bits: &[(BitVec<u8>, usize)]) -> Option<PropertyStats> {
406 let mut min = u64::MAX;
407 let mut max = 0u64;
408 let mut bits = BitVec::<u8>::with_capacity(self.feature_count());
409 for feature in self.features() {
410 if let Some(id) = feature.id() {
411 min = min.min(id);
412 max = max.max(id);
413 bits.push(true);
414 } else {
415 bits.push(false);
416 }
417 }
418 let presence = Presence::from_bits(&bits, property_bits);
419 if presence == Presence::AllNull {
420 None
421 } else {
422 Some(PropertyStats {
423 presence,
424 stats: PropertyTypedStats::Unsigned { min, max },
425 })
426 }
427 }
428
429 fn analyze_properties(
430 &self,
431 property_bits: &mut Vec<(BitVec<u8>, usize)>,
432 ) -> MltResult<Vec<PropertyStats>> {
433 self.property_names()
434 .iter()
435 .enumerate()
436 .map(|(col_idx, name)| -> MltResult<PropertyStats> {
437 let mut kind = None;
438 let mut stats = PropertyTypedStats::default();
439 let mut bits = BitVec::<u8>::with_capacity(self.feature_count());
440 for feature in self.features() {
441 let prop = feature.properties().get(col_idx);
442 if let Some(prop_kind) = prop.map(PropKind::from) {
443 match kind {
444 Some(kind) if kind != prop_kind => {
445 return mixed_prop_err(col_idx, name.as_str());
446 }
447 None => kind = Some(prop_kind),
448 _ => {}
449 }
450 }
451 if let Some(prop) = prop
452 && stats.push(prop, col_idx, name)?
453 {
454 bits.push(true);
455 } else {
456 bits.push(false);
457 }
458 }
459
460 let presence = Presence::from_bits(&bits, property_bits);
461 if presence == Presence::Mixed {
462 property_bits.push((bits, col_idx));
463 }
464 Ok(PropertyStats { presence, stats })
465 })
466 .collect()
467 }
468}
469
470#[inline]
471fn mixed_prop_err<T>(column_idx: usize, property_name: &str) -> MltResult<T> {
472 Err(MltError::MixedPropertyTypes(
473 column_idx,
474 property_name.to_owned(),
475 ))
476}