1use std::borrow::Cow;
2use std::collections::HashSet;
3
4use derive_debug::Dbg;
5
6use crate::decoder::{DictionaryType, GeometryValues, RleLayout, StreamType};
7use crate::encoder::geometry::VertexBufferType;
8use crate::encoder::{IntEncoder, StagedId, StagedProperty};
9use crate::tile::Extent;
10use crate::{MltError, MltResult};
11
12#[derive(Debug, Clone, Default, PartialEq)]
14pub struct EncodedUnknown {
15 pub(crate) tag: u8,
16 pub(crate) value: Vec<u8>,
17}
18
19impl EncodedUnknown {
20 pub fn new(tag: u8, value: Vec<u8>) -> MltResult<Self> {
21 if tag == 1 {
22 return Err(MltError::ParsingColumnType(tag));
23 }
24 Ok(Self { tag, value })
25 }
26
27 #[must_use]
28 pub fn tag(&self) -> u32 {
29 u32::from(self.tag)
30 }
31
32 #[must_use]
33 pub fn data(&self) -> &[u8] {
34 &self.value
35 }
36}
37
38#[derive(Debug, Clone, Copy, PartialEq)]
41pub struct CurveParams {
42 pub shift: u32,
43 pub bits: u32,
44}
45
46impl Default for CurveParams {
47 fn default() -> Self {
48 Self { shift: 0, bits: 1 }
49 }
50}
51
52impl CurveParams {
53 #[must_use]
55 pub fn from_vertices(vertices: &[i32]) -> Self {
56 if vertices.is_empty() {
57 return Self::default();
58 }
59 let (min, max) = vertices
60 .iter()
61 .fold((i32::MAX, i32::MIN), |(mn, mx), &v| (mn.min(v), mx.max(v)));
62 crate::codecs::hilbert::hilbert_curve_params_from_bounds(min, max)
63 }
64}
65
66#[derive(Debug, PartialEq, Clone)]
73pub struct StagedLayer {
74 pub(crate) name: String,
75 pub(crate) extent: Extent,
76 pub(crate) id: StagedId,
77 pub(crate) geometry: GeometryValues,
78 pub(crate) properties: Vec<StagedProperty>,
79}
80
81#[cfg_attr(not(feature = "__private"), allow(dead_code))]
82impl StagedLayer {
83 pub fn new(
84 name: impl Into<String>,
85 extent: u32,
86 id: StagedId,
87 geometry: GeometryValues,
88 properties: Vec<StagedProperty>,
89 ) -> MltResult<Self> {
90 let name = name.into();
91 if name.is_empty() {
92 return Err(MltError::MissingLayerName);
93 }
94 let extent = Extent::new(extent)?;
95 let feature_count = geometry.feature_count();
96 if let Some(actual) = id.feature_count()
97 && actual != feature_count
98 {
99 return Err(MltError::StagedFeatureCountMismatch {
100 column: "id".into(),
101 expected: feature_count,
102 actual,
103 });
104 }
105 {
109 let mut seen: HashSet<Cow<str>> = HashSet::new();
110 for property in &properties {
111 let actual = property.feature_count();
112 if actual != feature_count {
113 return Err(MltError::StagedFeatureCountMismatch {
114 column: property.name().to_string(),
115 expected: feature_count,
116 actual,
117 });
118 }
119 if let StagedProperty::SharedDict(sd) = property {
120 for item in &sd.items {
121 if !seen.insert(Cow::Owned(format!("{}{}", sd.prefix, item.suffix))) {
122 return Err(MltError::DuplicatePropertyName(format!(
123 "{}{}",
124 sd.prefix, item.suffix
125 )));
126 }
127 }
128 } else if !seen.insert(Cow::Borrowed(property.name())) {
129 return Err(MltError::DuplicatePropertyName(property.name().to_string()));
130 }
131 }
132 }
133 Ok(Self {
134 name,
135 extent,
136 id,
137 geometry,
138 properties,
139 })
140 }
141
142 #[must_use]
143 pub fn name(&self) -> &str {
144 &self.name
145 }
146
147 #[must_use]
148 pub fn extent(&self) -> Extent {
149 self.extent
150 }
151
152 #[must_use]
153 pub fn id(&self) -> &StagedId {
154 &self.id
155 }
156
157 #[must_use]
158 pub fn geometry(&self) -> &GeometryValues {
159 &self.geometry
160 }
161
162 #[must_use]
163 pub fn properties(&self) -> &[StagedProperty] {
164 &self.properties
165 }
166}
167
168#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
170#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
171pub enum WireVersion {
172 #[default]
174 V01,
175 #[cfg(feature = "unstable-v2")]
181 V02,
182}
183
184impl WireVersion {
185 #[must_use]
187 pub(crate) fn tag(self) -> u8 {
188 match self {
189 Self::V01 => 1,
190 #[cfg(feature = "unstable-v2")]
191 Self::V02 => 2,
192 }
193 }
194
195 #[must_use]
197 pub(crate) fn rle_layout(self) -> RleLayout {
198 match self {
199 Self::V01 => RleLayout::Split,
200 #[cfg(feature = "unstable-v2")]
201 Self::V02 => RleLayout::Interleaved,
202 }
203 }
204}
205
206#[derive(Debug, Clone, Copy, PartialEq, Hash)]
208#[expect(
209 clippy::struct_excessive_bools,
210 reason = "enums would not model this better, not a state machine"
211)]
212pub struct EncoderConfig {
213 wire_version: WireVersion,
215 tessellate: bool,
217 attempt_spatial_morton_sort: bool,
219 attempt_spatial_hilbert_sort: bool,
221 attempt_id_sort: bool,
223 allow_fsst: bool,
225 allow_fastpfor: bool,
227 allow_shared_dict: bool,
229}
230impl Default for EncoderConfig {
231 fn default() -> Self {
232 Self {
233 wire_version: WireVersion::V01,
234 tessellate: false,
235 attempt_spatial_morton_sort: true,
236 attempt_spatial_hilbert_sort: true,
237 attempt_id_sort: true,
238 allow_fsst: true,
239 allow_fastpfor: true,
240 allow_shared_dict: true,
241 }
242 }
243}
244
245impl EncoderConfig {
246 #[must_use]
247 pub fn wire_version(self) -> WireVersion {
248 self.wire_version
249 }
250
251 #[must_use]
252 pub fn tessellate(self) -> bool {
253 self.tessellate
254 }
255
256 #[must_use]
257 pub fn attempt_spatial_morton_sort(self) -> bool {
258 self.attempt_spatial_morton_sort
259 }
260
261 #[must_use]
262 pub fn attempt_spatial_hilbert_sort(self) -> bool {
263 self.attempt_spatial_hilbert_sort
264 }
265
266 #[must_use]
267 pub fn attempt_id_sort(self) -> bool {
268 self.attempt_id_sort
269 }
270
271 #[must_use]
272 pub fn allow_fsst(self) -> bool {
273 self.allow_fsst
274 }
275
276 #[must_use]
277 pub fn allow_fastpfor(self) -> bool {
278 self.allow_fastpfor && self.wire_version == WireVersion::V01
282 }
283
284 #[must_use]
285 pub fn allow_shared_dict(self) -> bool {
286 self.allow_shared_dict
287 }
288
289 #[must_use]
290 pub fn with_wire_version(mut self, version: WireVersion) -> Self {
291 self.wire_version = version;
292 self
293 }
294
295 #[must_use]
296 pub fn with_tessellation(mut self, enabled: bool) -> Self {
297 self.tessellate = enabled;
298 self
299 }
300
301 #[must_use]
302 pub fn with_spatial_morton_sort(mut self, enabled: bool) -> Self {
303 self.attempt_spatial_morton_sort = enabled;
304 self
305 }
306
307 #[must_use]
308 pub fn with_spatial_hilbert_sort(mut self, enabled: bool) -> Self {
309 self.attempt_spatial_hilbert_sort = enabled;
310 self
311 }
312
313 #[must_use]
314 pub fn with_id_sort(mut self, enabled: bool) -> Self {
315 self.attempt_id_sort = enabled;
316 self
317 }
318
319 #[must_use]
320 pub fn with_fsst(mut self, enabled: bool) -> Self {
321 self.allow_fsst = enabled;
322 self
323 }
324
325 #[must_use]
326 pub fn with_fastpfor(mut self, enabled: bool) -> Self {
327 self.allow_fastpfor = enabled;
328 self
329 }
330
331 #[must_use]
332 pub fn with_shared_dict(mut self, enabled: bool) -> Self {
333 self.allow_shared_dict = enabled;
334 self
335 }
336}
337
338#[derive(Debug, Clone, Copy, PartialEq, Eq)]
347pub enum StrEncoding {
348 Plain,
349 Dict,
350 Fsst,
351 FsstDict,
352}
353
354#[derive(Debug, Clone, Copy, PartialEq)]
355pub enum ColumnKind {
356 Id,
357 Geometry,
358 Property,
359}
360
361#[derive(Clone, Copy, Debug, PartialEq)]
363pub struct StreamCtx<'a> {
364 pub kind: ColumnKind,
365 pub stream_type: StreamType,
366 pub name: &'a str,
367 pub subname: &'a str,
368}
369impl<'a> StreamCtx<'a> {
370 #[inline]
372 #[must_use]
373 pub const fn new(
374 kind: ColumnKind,
375 stream_type: StreamType,
376 name: &'a str,
377 subname: &'a str,
378 ) -> Self {
379 Self {
380 kind,
381 stream_type,
382 name,
383 subname,
384 }
385 }
386
387 #[inline]
388 #[must_use]
389 pub const fn id(stream_type: StreamType) -> Self {
390 Self::new(ColumnKind::Id, stream_type, "", "")
391 }
392
393 #[inline]
394 #[must_use]
395 pub const fn geom(stream_type: StreamType, name: &'a str) -> Self {
396 Self::new(ColumnKind::Geometry, stream_type, name, "")
397 }
398
399 #[inline]
400 #[must_use]
401 pub const fn prop(stream_type: StreamType, name: &'a str) -> Self {
402 Self::new(ColumnKind::Property, stream_type, name, "")
403 }
404
405 #[inline]
406 #[must_use]
407 pub const fn prop_data(name: &'a str) -> Self {
408 let stream_type = StreamType::Data(DictionaryType::None);
409 Self::new(ColumnKind::Property, stream_type, name, "")
410 }
411
412 #[inline]
413 #[must_use]
414 pub const fn prop2(stream_type: StreamType, prefix: &'a str, suffix: &'a str) -> Self {
415 Self::new(ColumnKind::Property, stream_type, prefix, suffix)
416 }
417}
418
419#[derive(Dbg)]
427pub struct ExplicitEncoder {
428 pub vertex_buffer_type: VertexBufferType,
430 #[dbg(skip)]
432 pub force_stream: Box<dyn for<'a> Fn(&'a StreamCtx<'a>) -> bool>,
433 #[dbg(skip)]
435 pub get_int_encoder: Box<dyn for<'a> Fn(&'a StreamCtx<'a>) -> IntEncoder>,
436 #[dbg(skip)]
438 pub get_str_encoding: Box<dyn Fn(&str) -> StrEncoding>,
439}