1use std::borrow::Cow;
2use std::collections::HashSet;
3
4use derive_debug::Dbg;
5
6#[cfg(feature = "unstable-v2")]
7use crate::decoder::RleLayout;
8use crate::decoder::{DictionaryType, FastPForKind, GeometryValues, PhysicalEncoding, StreamType};
9use crate::encoder::geometry::VertexBufferType;
10use crate::encoder::{IntEncoder, StagedId, StagedProperty};
11use crate::tile::Extent;
12use crate::{MltError, MltResult};
13
14#[derive(Debug, Clone, Default, PartialEq)]
16pub struct EncodedUnknown {
17 pub(crate) tag: u8,
18 pub(crate) value: Vec<u8>,
19}
20
21impl EncodedUnknown {
22 pub fn new(tag: u8, value: Vec<u8>) -> MltResult<Self> {
23 if tag == 1 {
24 return Err(MltError::ParsingColumnType(tag));
25 }
26 Ok(Self { tag, value })
27 }
28
29 #[must_use]
30 pub fn tag(&self) -> u32 {
31 u32::from(self.tag)
32 }
33
34 #[must_use]
35 pub fn data(&self) -> &[u8] {
36 &self.value
37 }
38}
39
40#[derive(Debug, Clone, Copy, PartialEq)]
43pub struct CurveParams {
44 pub shift: u32,
45 pub bits: u32,
46}
47
48impl Default for CurveParams {
49 fn default() -> Self {
50 Self { shift: 0, bits: 1 }
51 }
52}
53
54impl CurveParams {
55 #[must_use]
57 pub fn from_vertices(vertices: &[i32]) -> Self {
58 if vertices.is_empty() {
59 return Self::default();
60 }
61 let (min, max) = vertices
62 .iter()
63 .fold((i32::MAX, i32::MIN), |(mn, mx), &v| (mn.min(v), mx.max(v)));
64 crate::codecs::hilbert::hilbert_curve_params_from_bounds(min, max)
65 }
66}
67
68#[derive(Debug, PartialEq, Clone)]
75pub struct StagedLayer {
76 pub(crate) name: String,
77 pub(crate) extent: Extent,
78 pub(crate) id: StagedId,
79 pub(crate) geometry: GeometryValues,
80 pub(crate) properties: Vec<StagedProperty>,
81}
82
83#[cfg_attr(not(feature = "__private"), allow(dead_code))]
84impl StagedLayer {
85 pub fn new(
86 name: impl Into<String>,
87 extent: u32,
88 id: StagedId,
89 geometry: GeometryValues,
90 properties: Vec<StagedProperty>,
91 ) -> MltResult<Self> {
92 let name = name.into();
93 if name.is_empty() {
94 return Err(MltError::MissingLayerName);
95 }
96 let extent = Extent::new(extent)?;
97 let feature_count = geometry.feature_count();
98 if let Some(actual) = id.feature_count()
99 && actual != feature_count
100 {
101 return Err(MltError::StagedFeatureCountMismatch {
102 column: "id".into(),
103 expected: feature_count,
104 actual,
105 });
106 }
107 {
111 let mut seen: HashSet<Cow<str>> = HashSet::new();
112 for property in &properties {
113 let actual = property.feature_count();
114 if actual != feature_count {
115 return Err(MltError::StagedFeatureCountMismatch {
116 column: property.name().to_string(),
117 expected: feature_count,
118 actual,
119 });
120 }
121 if let StagedProperty::SharedDict(sd) = property {
122 for item in &sd.items {
123 if !seen.insert(Cow::Owned(format!("{}{}", sd.prefix, item.suffix))) {
124 return Err(MltError::DuplicatePropertyName(format!(
125 "{}{}",
126 sd.prefix, item.suffix
127 )));
128 }
129 }
130 } else if !seen.insert(Cow::Borrowed(property.name())) {
131 return Err(MltError::DuplicatePropertyName(property.name().to_string()));
132 }
133 }
134 }
135 Ok(Self {
136 name,
137 extent,
138 id,
139 geometry,
140 properties,
141 })
142 }
143
144 #[must_use]
145 pub fn name(&self) -> &str {
146 &self.name
147 }
148
149 #[must_use]
150 pub fn extent(&self) -> Extent {
151 self.extent
152 }
153
154 #[must_use]
155 pub fn id(&self) -> &StagedId {
156 &self.id
157 }
158
159 #[must_use]
160 pub fn geometry(&self) -> &GeometryValues {
161 &self.geometry
162 }
163
164 #[must_use]
165 pub fn properties(&self) -> &[StagedProperty] {
166 &self.properties
167 }
168}
169
170#[cfg(feature = "unstable-v2")]
172#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
173#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
174pub enum WireVersion {
175 #[default]
177 V01,
178 V02,
182}
183
184#[cfg(feature = "unstable-v2")]
185impl WireVersion {
186 #[must_use]
188 pub(crate) fn tag(self) -> u8 {
189 match self {
190 Self::V01 => 1,
191 Self::V02 => 2,
192 }
193 }
194
195 #[must_use]
197 pub(crate) fn fastpfor_kind(self) -> FastPForKind {
198 match self {
199 Self::V01 => FastPForKind::Block256Be,
200 Self::V02 => FastPForKind::Block128Le,
201 }
202 }
203
204 #[must_use]
206 pub(crate) fn rle_layout(self) -> RleLayout {
207 match self {
208 Self::V01 => RleLayout::Split,
209 Self::V02 => RleLayout::Interleaved,
210 }
211 }
212}
213
214#[derive(Debug, Clone, Copy, PartialEq, Hash)]
216#[expect(
217 clippy::struct_excessive_bools,
218 reason = "enums would not model this better, not a state machine"
219)]
220pub struct EncoderConfig {
221 #[cfg(feature = "unstable-v2")]
223 wire_version: WireVersion,
224 tessellate: bool,
226 attempt_spatial_morton_sort: bool,
228 attempt_spatial_hilbert_sort: bool,
230 attempt_id_sort: bool,
232 allow_fsst: bool,
234 allow_fastpfor: bool,
236 allow_shared_dict: bool,
238 #[cfg(feature = "unstable-v2")]
240 allow_float_dict: bool,
241 #[cfg(feature = "unstable-v2")]
243 allow_float_alp: bool,
244}
245impl Default for EncoderConfig {
246 fn default() -> Self {
247 Self {
248 #[cfg(feature = "unstable-v2")]
249 wire_version: WireVersion::V01,
250 tessellate: false,
251 attempt_spatial_morton_sort: true,
252 attempt_spatial_hilbert_sort: true,
253 attempt_id_sort: true,
254 allow_fsst: true,
255 allow_fastpfor: true,
256 allow_shared_dict: true,
257 #[cfg(feature = "unstable-v2")]
259 allow_float_dict: false,
260 #[cfg(feature = "unstable-v2")]
261 allow_float_alp: false,
262 }
263 }
264}
265
266impl EncoderConfig {
267 #[cfg(feature = "unstable-v2")]
268 #[must_use]
269 pub fn wire_version(self) -> WireVersion {
270 self.wire_version
271 }
272
273 #[must_use]
274 pub fn tessellate(self) -> bool {
275 self.tessellate
276 }
277
278 #[must_use]
279 pub fn attempt_spatial_morton_sort(self) -> bool {
280 self.attempt_spatial_morton_sort
281 }
282
283 #[must_use]
284 pub fn attempt_spatial_hilbert_sort(self) -> bool {
285 self.attempt_spatial_hilbert_sort
286 }
287
288 #[must_use]
289 pub fn attempt_id_sort(self) -> bool {
290 self.attempt_id_sort
291 }
292
293 #[must_use]
294 pub fn allow_fsst(self) -> bool {
295 self.allow_fsst
296 }
297
298 #[must_use]
299 pub fn allow_fastpfor(self) -> bool {
300 self.allow_fastpfor
301 }
302
303 #[must_use]
305 pub(crate) fn fastpfor(self) -> Option<PhysicalEncoding> {
306 #[cfg(feature = "unstable-v2")]
307 let kind = self.wire_version.fastpfor_kind();
308 #[cfg(not(feature = "unstable-v2"))]
309 let kind = FastPForKind::Block256Be;
310 self.allow_fastpfor
311 .then_some(PhysicalEncoding::FastPFor(kind))
312 }
313
314 #[must_use]
315 pub fn allow_shared_dict(self) -> bool {
316 self.allow_shared_dict
317 }
318
319 #[cfg(feature = "unstable-v2")]
321 #[must_use]
322 pub fn allow_float_dict(self) -> bool {
323 self.allow_float_dict && self.wire_version != WireVersion::V01
324 }
325
326 #[cfg(feature = "unstable-v2")]
328 #[must_use]
329 pub fn allow_float_alp(self) -> bool {
330 self.allow_float_alp && self.wire_version != WireVersion::V01
331 }
332
333 #[cfg(feature = "unstable-v2")]
334 #[must_use]
335 pub fn with_wire_version(mut self, version: WireVersion) -> Self {
336 self.wire_version = version;
337 self
338 }
339
340 #[must_use]
341 pub fn with_tessellation(mut self, enabled: bool) -> Self {
342 self.tessellate = enabled;
343 self
344 }
345
346 #[must_use]
347 pub fn with_spatial_morton_sort(mut self, enabled: bool) -> Self {
348 self.attempt_spatial_morton_sort = enabled;
349 self
350 }
351
352 #[must_use]
353 pub fn with_spatial_hilbert_sort(mut self, enabled: bool) -> Self {
354 self.attempt_spatial_hilbert_sort = enabled;
355 self
356 }
357
358 #[must_use]
359 pub fn with_id_sort(mut self, enabled: bool) -> Self {
360 self.attempt_id_sort = enabled;
361 self
362 }
363
364 #[must_use]
365 pub fn with_fsst(mut self, enabled: bool) -> Self {
366 self.allow_fsst = enabled;
367 self
368 }
369
370 #[must_use]
371 pub fn with_fastpfor(mut self, enabled: bool) -> Self {
372 self.allow_fastpfor = enabled;
373 self
374 }
375
376 #[must_use]
377 pub fn with_shared_dict(mut self, enabled: bool) -> Self {
378 self.allow_shared_dict = enabled;
379 self
380 }
381
382 #[cfg(feature = "unstable-v2")]
386 #[must_use]
387 pub fn with_float_dict(mut self, enabled: bool) -> Self {
388 self.allow_float_dict = enabled;
389 self
390 }
391
392 #[cfg(feature = "unstable-v2")]
394 #[must_use]
395 pub fn with_float_alp(mut self, enabled: bool) -> Self {
396 self.allow_float_alp = enabled;
397 self
398 }
399}
400
401#[derive(Debug, Clone, Copy, PartialEq, Eq)]
403pub enum StrEncoding {
404 Plain,
405 Dict,
406 Fsst,
407 FsstDict,
408 FrontDict,
409 FsstFrontDict,
410}
411
412#[derive(Debug, Clone, Copy, PartialEq, Eq)]
414pub enum FloatEncoding {
415 None,
416 #[cfg(feature = "unstable-v2")]
417 Dict,
418 #[cfg(feature = "unstable-v2")]
419 Alp,
420}
421
422#[derive(Debug, Clone, Copy, PartialEq)]
423pub enum ColumnKind {
424 Id,
425 Geometry,
426 Property,
427}
428
429#[derive(Clone, Copy, Debug, PartialEq)]
431pub struct StreamCtx<'a> {
432 pub kind: ColumnKind,
433 pub stream_type: StreamType,
434 pub name: &'a str,
435 pub subname: &'a str,
436}
437impl<'a> StreamCtx<'a> {
438 #[inline]
440 #[must_use]
441 pub const fn new(
442 kind: ColumnKind,
443 stream_type: StreamType,
444 name: &'a str,
445 subname: &'a str,
446 ) -> Self {
447 Self {
448 kind,
449 stream_type,
450 name,
451 subname,
452 }
453 }
454
455 #[inline]
456 #[must_use]
457 pub const fn id(stream_type: StreamType) -> Self {
458 Self::new(ColumnKind::Id, stream_type, "", "")
459 }
460
461 #[inline]
462 #[must_use]
463 pub const fn geom(stream_type: StreamType, name: &'a str) -> Self {
464 Self::new(ColumnKind::Geometry, stream_type, name, "")
465 }
466
467 #[inline]
468 #[must_use]
469 pub const fn prop(stream_type: StreamType, name: &'a str) -> Self {
470 Self::new(ColumnKind::Property, stream_type, name, "")
471 }
472
473 #[inline]
474 #[must_use]
475 pub const fn prop_data(name: &'a str) -> Self {
476 let stream_type = StreamType::Data(DictionaryType::None);
477 Self::new(ColumnKind::Property, stream_type, name, "")
478 }
479
480 #[inline]
481 #[must_use]
482 pub const fn prop2(stream_type: StreamType, prefix: &'a str, suffix: &'a str) -> Self {
483 Self::new(ColumnKind::Property, stream_type, prefix, suffix)
484 }
485}
486
487#[derive(Dbg)]
491pub struct ExplicitEncoder {
492 pub vertex_buffer_type: VertexBufferType,
494 #[dbg(skip)]
496 pub force_stream: Box<dyn for<'a> Fn(&'a StreamCtx<'a>) -> bool>,
497 #[dbg(skip)]
499 pub get_int_encoder: Box<dyn for<'a> Fn(&'a StreamCtx<'a>) -> IntEncoder>,
500 #[dbg(skip)]
502 pub get_str_encoding: Box<dyn Fn(&str) -> StrEncoding>,
503 #[dbg(skip)]
505 pub get_float_encoding: Box<dyn Fn(&str) -> FloatEncoding>,
506}