1use std::borrow::Cow;
2use std::collections::HashSet;
3
4use derive_debug::Dbg;
5
6use crate::decoder::{DictionaryType, Extent, GeometryValues, StreamType};
7use crate::encoder::geometry::VertexBufferType;
8use crate::encoder::{IntEncoder, StagedId, StagedProperty};
9use crate::{MltError, MltResult};
10
11#[derive(Debug, Clone, Default, PartialEq)]
13pub struct EncodedUnknown {
14 pub(crate) tag: u8,
15 pub(crate) value: Vec<u8>,
16}
17
18impl EncodedUnknown {
19 pub fn new(tag: u8, value: Vec<u8>) -> MltResult<Self> {
20 if tag == 1 {
21 return Err(MltError::ParsingColumnType(tag));
22 }
23 Ok(Self { tag, value })
24 }
25
26 #[must_use]
27 pub fn tag(&self) -> u32 {
28 u32::from(self.tag)
29 }
30
31 #[must_use]
32 pub fn data(&self) -> &[u8] {
33 &self.value
34 }
35}
36
37#[derive(Debug, Clone, Copy, PartialEq)]
40pub struct CurveParams {
41 pub shift: u32,
42 pub bits: u32,
43}
44
45impl Default for CurveParams {
46 fn default() -> Self {
47 Self { shift: 0, bits: 1 }
48 }
49}
50
51impl CurveParams {
52 #[must_use]
54 pub fn from_vertices(vertices: &[i32]) -> Self {
55 if vertices.is_empty() {
56 return Self::default();
57 }
58 let (min, max) = vertices
59 .iter()
60 .fold((i32::MAX, i32::MIN), |(mn, mx), &v| (mn.min(v), mx.max(v)));
61 crate::codecs::hilbert::hilbert_curve_params_from_bounds(min, max)
62 }
63}
64
65#[derive(Debug, PartialEq, Clone)]
72pub struct StagedLayer {
73 pub(crate) name: String,
74 pub(crate) extent: Extent,
75 pub(crate) id: StagedId,
76 pub(crate) geometry: GeometryValues,
77 pub(crate) properties: Vec<StagedProperty>,
78}
79
80#[cfg_attr(not(feature = "__private"), allow(dead_code))]
81impl StagedLayer {
82 pub fn new(
83 name: impl Into<String>,
84 extent: u32,
85 id: StagedId,
86 geometry: GeometryValues,
87 properties: Vec<StagedProperty>,
88 ) -> MltResult<Self> {
89 let name = name.into();
90 if name.is_empty() {
91 return Err(MltError::MissingLayerName);
92 }
93 let extent = Extent::new(extent)?;
94 let feature_count = geometry.feature_count();
95 if let Some(actual) = id.feature_count()
96 && actual != feature_count
97 {
98 return Err(MltError::StagedFeatureCountMismatch {
99 column: "id".into(),
100 expected: feature_count,
101 actual,
102 });
103 }
104 {
108 let mut seen: HashSet<Cow<str>> = HashSet::new();
109 for property in &properties {
110 let actual = property.feature_count();
111 if actual != feature_count {
112 return Err(MltError::StagedFeatureCountMismatch {
113 column: property.name().to_string(),
114 expected: feature_count,
115 actual,
116 });
117 }
118 match property {
119 StagedProperty::SharedDict(sd) => {
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 }
129 _ => {
130 if !seen.insert(Cow::Borrowed(property.name())) {
131 return Err(MltError::DuplicatePropertyName(
132 property.name().to_string(),
133 ));
134 }
135 }
136 }
137 }
138 }
139 Ok(Self {
140 name,
141 extent,
142 id,
143 geometry,
144 properties,
145 })
146 }
147
148 #[must_use]
149 pub fn name(&self) -> &str {
150 &self.name
151 }
152
153 #[must_use]
154 pub fn extent(&self) -> Extent {
155 self.extent
156 }
157
158 #[must_use]
159 pub fn id(&self) -> &StagedId {
160 &self.id
161 }
162
163 #[must_use]
164 pub fn geometry(&self) -> &GeometryValues {
165 &self.geometry
166 }
167
168 #[must_use]
169 pub fn properties(&self) -> &[StagedProperty] {
170 &self.properties
171 }
172}
173
174#[derive(Debug, Clone, Copy, PartialEq, Hash)]
176#[expect(
177 clippy::struct_excessive_bools,
178 reason = "enums would not model this better, not a state machine"
179)]
180pub struct EncoderConfig {
181 tessellate: bool,
183 attempt_spatial_morton_sort: bool,
185 attempt_spatial_hilbert_sort: bool,
187 attempt_id_sort: bool,
189 allow_fsst: bool,
191 allow_fastpfor: bool,
193 allow_shared_dict: bool,
195}
196impl Default for EncoderConfig {
197 fn default() -> Self {
198 Self {
199 tessellate: false,
200 attempt_spatial_morton_sort: true,
201 attempt_spatial_hilbert_sort: true,
202 attempt_id_sort: true,
203 allow_fsst: true,
204 allow_fastpfor: true,
205 allow_shared_dict: true,
206 }
207 }
208}
209
210impl EncoderConfig {
211 #[must_use]
212 pub fn tessellate(self) -> bool {
213 self.tessellate
214 }
215
216 #[must_use]
217 pub fn attempt_spatial_morton_sort(self) -> bool {
218 self.attempt_spatial_morton_sort
219 }
220
221 #[must_use]
222 pub fn attempt_spatial_hilbert_sort(self) -> bool {
223 self.attempt_spatial_hilbert_sort
224 }
225
226 #[must_use]
227 pub fn attempt_id_sort(self) -> bool {
228 self.attempt_id_sort
229 }
230
231 #[must_use]
232 pub fn allow_fsst(self) -> bool {
233 self.allow_fsst
234 }
235
236 #[must_use]
237 pub fn allow_fastpfor(self) -> bool {
238 self.allow_fastpfor
239 }
240
241 #[must_use]
242 pub fn allow_shared_dict(self) -> bool {
243 self.allow_shared_dict
244 }
245
246 #[must_use]
247 pub fn with_tessellation(mut self, enabled: bool) -> Self {
248 self.tessellate = enabled;
249 self
250 }
251
252 #[must_use]
253 pub fn with_spatial_morton_sort(mut self, enabled: bool) -> Self {
254 self.attempt_spatial_morton_sort = enabled;
255 self
256 }
257
258 #[must_use]
259 pub fn with_spatial_hilbert_sort(mut self, enabled: bool) -> Self {
260 self.attempt_spatial_hilbert_sort = enabled;
261 self
262 }
263
264 #[must_use]
265 pub fn with_id_sort(mut self, enabled: bool) -> Self {
266 self.attempt_id_sort = enabled;
267 self
268 }
269
270 #[must_use]
271 pub fn with_fsst(mut self, enabled: bool) -> Self {
272 self.allow_fsst = enabled;
273 self
274 }
275
276 #[must_use]
277 pub fn with_fastpfor(mut self, enabled: bool) -> Self {
278 self.allow_fastpfor = enabled;
279 self
280 }
281
282 #[must_use]
283 pub fn with_shared_dict(mut self, enabled: bool) -> Self {
284 self.allow_shared_dict = enabled;
285 self
286 }
287}
288
289#[derive(Debug, Clone, Copy, PartialEq, Eq)]
298pub enum StrEncoding {
299 Plain,
300 Dict,
301 Fsst,
302 FsstDict,
303}
304
305#[derive(Debug, Clone, Copy, PartialEq)]
306pub enum ColumnKind {
307 Id,
308 Geometry,
309 Property,
310}
311
312#[derive(Clone, Copy, Debug, PartialEq)]
314pub struct StreamCtx<'a> {
315 pub kind: ColumnKind,
316 pub stream_type: StreamType,
317 pub name: &'a str,
318 pub subname: &'a str,
319}
320impl<'a> StreamCtx<'a> {
321 #[inline]
323 #[must_use]
324 pub const fn new(
325 kind: ColumnKind,
326 stream_type: StreamType,
327 name: &'a str,
328 subname: &'a str,
329 ) -> Self {
330 Self {
331 kind,
332 stream_type,
333 name,
334 subname,
335 }
336 }
337
338 #[inline]
339 #[must_use]
340 pub const fn id(stream_type: StreamType) -> Self {
341 Self::new(ColumnKind::Id, stream_type, "", "")
342 }
343
344 #[inline]
345 #[must_use]
346 pub const fn geom(stream_type: StreamType, name: &'a str) -> Self {
347 Self::new(ColumnKind::Geometry, stream_type, name, "")
348 }
349
350 #[inline]
351 #[must_use]
352 pub const fn prop(stream_type: StreamType, name: &'a str) -> Self {
353 Self::new(ColumnKind::Property, stream_type, name, "")
354 }
355
356 #[inline]
357 #[must_use]
358 pub const fn prop_data(name: &'a str) -> Self {
359 let stream_type = StreamType::Data(DictionaryType::None);
360 Self::new(ColumnKind::Property, stream_type, name, "")
361 }
362
363 #[inline]
364 #[must_use]
365 pub const fn prop2(stream_type: StreamType, prefix: &'a str, suffix: &'a str) -> Self {
366 Self::new(ColumnKind::Property, stream_type, prefix, suffix)
367 }
368}
369
370#[derive(Dbg)]
378pub struct ExplicitEncoder {
379 pub vertex_buffer_type: VertexBufferType,
381 #[dbg(skip)]
383 pub force_stream: Box<dyn for<'a> Fn(&'a StreamCtx<'a>) -> bool>,
384 #[dbg(skip)]
386 pub get_int_encoder: Box<dyn for<'a> Fn(&'a StreamCtx<'a>) -> IntEncoder>,
387 #[dbg(skip)]
389 pub get_str_encoding: Box<dyn Fn(&str) -> StrEncoding>,
390}