1use std::fmt::{Display, Formatter, Result as FmtResult};
2
3use derive_debug::Dbg;
4use num_enum::TryFromPrimitive;
5
6use crate::utils::formatter::{bytes_dbg, compact_dbg};
7use crate::{MltError, MltResult};
8
9#[derive(Debug, Clone, Copy, PartialEq, TryFromPrimitive)]
15#[repr(u8)]
16pub enum LogicalTechnique {
17 None = 0b0000_0000,
18 Delta = 0b0010_0000,
19 ComponentwiseDelta = 0b0100_0000,
20 Rle = 0b0110_0000,
21 Morton = 0b1000_0000,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, TryFromPrimitive)]
30#[repr(u8)]
31pub enum LogicalCombination {
32 None = 0b0000_0000,
33 Delta = 0b0010_0000,
34 DeltaRle = 0b0010_1100,
35 ComponentwiseDelta = 0b0100_0000,
36 Rle = 0b0110_0000,
37 Morton = 0b1000_0000,
38 MortonDelta = 0b1000_0100,
39 MortonRle = 0b1000_1100,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum RleLayout {
49 Split,
51 #[cfg(feature = "unstable-v2")]
53 Interleaved,
54}
55
56#[derive(Debug, Clone, Copy, PartialEq)]
58pub enum RleMeta {
59 Split { runs: u32, num_rle_values: u32 },
62 #[cfg(feature = "unstable-v2")]
67 Interleaved { num_rle_values: u32 },
68}
69
70impl RleMeta {
71 #[cfg(feature = "unstable-v2")]
73 #[must_use]
74 pub(crate) fn num_rle_values(self) -> u32 {
75 match self {
76 Self::Split { num_rle_values, .. } => num_rle_values,
77 #[cfg(feature = "unstable-v2")]
78 Self::Interleaved { num_rle_values } => num_rle_values,
79 }
80 }
81}
82
83#[derive(Debug, Clone, Copy, PartialEq)]
85pub struct Morton {
86 pub(crate) bits: u32,
88 pub(crate) shift: u32,
90}
91
92impl Morton {
93 pub fn new(bits: u32, shift: u32) -> MltResult<Self> {
94 if bits <= 16 {
95 Ok(Self { bits, shift })
96 } else {
97 Err(MltError::InvalidMortonBits(bits))
98 }
99 }
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105pub struct AlpScale {
106 pub(crate) e: u8,
108 pub(crate) f: u8,
110}
111
112#[derive(Clone, Copy, PartialEq, Eq)]
115pub struct Alp {
116 pub(crate) scale: AlpScale,
117 pub(crate) base: i64,
119}
120
121#[cfg(feature = "unstable-v2")]
122impl AlpScale {
123 pub(crate) const MAX_EXPONENT: u8 = 18;
126
127 #[cfg(test)]
131 pub(crate) fn net(self) -> u8 {
132 self.e - self.f
133 }
134}
135
136impl std::fmt::Debug for Alp {
138 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
139 write!(
140 f,
141 "Alp {{ e: {}, f: {}, base: {} }}",
142 self.scale.e, self.scale.f, self.base
143 )
144 }
145}
146
147#[cfg(feature = "unstable-v2")]
148impl Alp {
149 pub(crate) fn new(e: u8, f: u8, base: i64) -> MltResult<Self> {
150 if e <= AlpScale::MAX_EXPONENT && f <= e {
151 Ok(Self {
152 scale: AlpScale { e, f },
153 base,
154 })
155 } else {
156 Err(MltError::InvalidAlpParams(e, f))
157 }
158 }
159
160 #[expect(
164 clippy::cast_sign_loss,
165 reason = "the bit pattern is the point; `code_at` casts it back"
166 )]
167 pub(crate) fn offset_of(self, code: i64) -> u64 {
168 (code as u64).wrapping_sub(self.base as u64)
169 }
170
171 #[expect(
173 clippy::cast_possible_wrap,
174 clippy::cast_sign_loss,
175 reason = "the bit pattern is the point; inverts `offset_of` exactly"
176 )]
177 pub(crate) fn code_at(self, offset: u64) -> i64 {
178 (self.base as u64).wrapping_add(offset) as i64
179 }
180}
181
182#[derive(Debug, Clone, Copy, PartialEq, Eq)]
185pub enum ValueKind {
186 Int,
187 Bool,
188 Float,
189 Vertex,
190}
191
192#[derive(Debug, Clone, Copy, PartialEq)]
195pub enum IntLogical {
196 None,
197 Delta,
198 Rle(RleMeta),
199 DeltaRle(RleMeta),
200}
201
202#[derive(Debug, Clone, Copy, PartialEq)]
204pub enum BoolLogical {
205 None,
207 ByteRle(RleMeta),
210}
211
212#[derive(Debug, Clone, Copy, PartialEq)]
214pub enum FloatLogical {
215 None,
217 Dict,
221 Alp(Alp),
224}
225
226#[derive(Debug, Clone, Copy, PartialEq)]
228pub enum VertexLogical {
229 None,
230 Delta,
231 ComponentwiseDelta,
232 Morton(Morton),
233 MortonDelta(Morton),
234 MortonRle(Morton),
235}
236
237#[derive(Debug, Clone, Copy, PartialEq)]
241pub enum LogicalEncoding {
242 Int(IntLogical),
243 Bool(BoolLogical),
244 Float(FloatLogical),
245 Vertex(VertexLogical),
246}
247
248impl LogicalEncoding {
249 #[must_use]
251 pub fn kind(self) -> ValueKind {
252 match self {
253 Self::Int(_) => ValueKind::Int,
254 Self::Bool(_) => ValueKind::Bool,
255 Self::Float(_) => ValueKind::Float,
256 Self::Vertex(_) => ValueKind::Vertex,
257 }
258 }
259
260 #[must_use]
262 pub(crate) fn none(kind: ValueKind) -> Self {
263 match kind {
264 ValueKind::Int => Self::Int(IntLogical::None),
265 ValueKind::Bool => Self::Bool(BoolLogical::None),
266 ValueKind::Float => Self::Float(FloatLogical::None),
267 ValueKind::Vertex => Self::Vertex(VertexLogical::None),
268 }
269 }
270
271 #[must_use]
275 pub(crate) fn is_identity(self) -> bool {
276 matches!(
277 self,
278 Self::Int(IntLogical::None)
279 | Self::Bool(BoolLogical::None)
280 | Self::Float(FloatLogical::None | FloatLogical::Dict)
281 | Self::Vertex(VertexLogical::None)
282 )
283 }
284}
285
286#[derive(Debug, PartialEq)]
292pub struct LogicalValue {
293 pub(crate) meta: StreamMeta,
294}
295
296#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, TryFromPrimitive)]
300#[repr(u8)]
301pub enum DictionaryType {
302 None = 0b0000_0000,
303 Single = 0b0000_0001,
304 Shared = 0b0000_0010,
305 Vertex = 0b0000_0011,
306 Morton = 0b0000_0100,
307 Fsst = 0b0000_0101,
308}
309
310#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, TryFromPrimitive)]
312#[repr(u8)]
313pub enum OffsetType {
314 Vertex = 0b0000_0000,
315 Index = 0b0000_0001,
316 String = 0b0000_0010,
317 Key = 0b0000_0011,
318}
319
320#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, TryFromPrimitive)]
322#[repr(u8)]
323pub enum LengthType {
324 VarBinary = 0b0000_0000,
325 Geometries = 0b0000_0001,
326 Parts = 0b0000_0010,
327 Rings = 0b0000_0011,
328 Triangles = 0b0000_0100,
329 Symbol = 0b0000_0101,
330 Dictionary = 0b0000_0110,
331 #[cfg(feature = "unstable-v2")]
333 Nested = 0b0000_0111,
334}
335
336#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
338pub enum StreamType {
339 Present,
340 Data(DictionaryType),
341 Offset(OffsetType),
342 Length(LengthType),
343}
344
345#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
347pub enum PhysicalEncoding {
348 None,
349 FastPFor(FastPForKind),
352 VarInt,
355 #[cfg(feature = "unstable-v2")]
359 BitPacked,
360}
361
362#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
364pub enum FastPForKind {
365 Block256Be,
367 #[cfg(feature = "unstable-v2")]
369 Block128Le,
370}
371
372#[derive(Debug, Clone, Copy, PartialEq)]
375pub struct IntEncoding {
376 pub logical: LogicalEncoding,
377 pub physical: PhysicalEncoding,
378}
379
380impl IntEncoding {
381 #[must_use]
382 pub(crate) const fn new(logical: LogicalEncoding, physical: PhysicalEncoding) -> Self {
383 Self { logical, physical }
384 }
385
386 #[must_use]
387 pub(crate) fn none(kind: ValueKind) -> Self {
388 Self::new(LogicalEncoding::none(kind), PhysicalEncoding::None)
389 }
390}
391
392#[derive(Clone, Copy, Dbg, PartialEq)]
394pub struct StreamMeta {
395 #[dbg(formatter = "compact_dbg")]
396 pub stream_type: StreamType,
397 #[dbg(formatter = "compact_dbg")]
398 pub encoding: IntEncoding,
399 pub(crate) num_values: u32,
400}
401
402impl StreamMeta {
403 #[inline]
404 pub(crate) fn new(stream_type: StreamType, encoding: IntEncoding, num_values: u32) -> Self {
405 Self {
406 stream_type,
407 encoding,
408 num_values,
409 }
410 }
411
412 #[inline]
413 pub(crate) fn new2(
414 stream_type: StreamType,
415 logical: LogicalEncoding,
416 physical: PhysicalEncoding,
417 num_values: usize,
418 ) -> MltResult<Self> {
419 let enc = IntEncoding::new(logical, physical);
420 Ok(Self::new(stream_type, enc, u32::try_from(num_values)?))
421 }
422
423 #[inline]
424 pub(crate) fn new_none(
425 stream_type: StreamType,
426 kind: ValueKind,
427 num_values: usize,
428 ) -> MltResult<Self> {
429 let enc = IntEncoding::none(kind);
430 Ok(Self::new(stream_type, enc, u32::try_from(num_values)?))
431 }
432}
433
434#[derive(Clone, Dbg, PartialEq)]
436pub struct RawStream<'a> {
437 pub meta: StreamMeta,
438 #[dbg(formatter = "bytes_dbg")]
439 pub(crate) data: &'a [u8],
440}
441
442impl<'a> RawStream<'a> {
443 #[must_use]
444 pub(crate) fn new(meta: StreamMeta, data: &'a [u8]) -> Self {
445 Self { meta, data }
446 }
447}
448
449impl Display for StreamType {
453 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
455 match self {
456 Self::Present => f.write_str("present"),
457 Self::Data(dict) => {
458 let name = match dict {
459 DictionaryType::None => return f.write_str("data"),
460 DictionaryType::Single => "single",
461 DictionaryType::Shared => "shared",
462 DictionaryType::Vertex => "vertex",
463 DictionaryType::Morton => "morton",
464 DictionaryType::Fsst => "fsst",
465 };
466 write!(f, "data[{name}]")
467 }
468 Self::Offset(offset) => {
469 let name = match offset {
470 OffsetType::Vertex => "vertex",
471 OffsetType::Index => "index",
472 OffsetType::String => "string",
473 OffsetType::Key => "key",
474 };
475 write!(f, "offset[{name}]")
476 }
477 Self::Length(length) => {
478 let name = match length {
479 LengthType::VarBinary => "var-binary",
480 LengthType::Geometries => "geometries",
481 LengthType::Parts => "parts",
482 LengthType::Rings => "rings",
483 LengthType::Triangles => "triangles",
484 LengthType::Symbol => "symbol",
485 LengthType::Dictionary => "dictionary",
486 #[cfg(feature = "unstable-v2")]
487 LengthType::Nested => "nested",
488 };
489 write!(f, "length[{name}]")
490 }
491 }
492 }
493}
494
495impl Display for LogicalEncoding {
496 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
500 let (kind, enc) = match self {
501 Self::Int(int) => (
502 "int",
503 match int {
504 IntLogical::None => "none",
505 IntLogical::Delta => "delta",
506 IntLogical::Rle(_) => "rle",
507 IntLogical::DeltaRle(_) => "delta-rle",
508 },
509 ),
510 Self::Bool(b) => (
511 "bool",
512 match b {
513 BoolLogical::None => "none",
514 BoolLogical::ByteRle(_) => "byte-rle",
515 },
516 ),
517 Self::Float(float) => (
518 "float",
519 match float {
520 FloatLogical::None => "none",
521 FloatLogical::Dict => "dict",
522 FloatLogical::Alp(_) => "alp",
523 },
524 ),
525 Self::Vertex(vertex) => (
526 "vertex",
527 match vertex {
528 VertexLogical::None => "none",
529 VertexLogical::Delta => "delta",
530 VertexLogical::ComponentwiseDelta => "componentwise-delta",
531 VertexLogical::Morton(_) => "morton",
532 VertexLogical::MortonDelta(_) => "morton-delta",
533 VertexLogical::MortonRle(_) => "morton-rle",
534 },
535 ),
536 };
537 write!(f, "{kind}/{enc}")
538 }
539}
540
541impl Display for PhysicalEncoding {
542 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
544 match self {
545 Self::None => f.write_str("none"),
546 Self::VarInt => f.write_str("varint"),
547 Self::FastPFor(kind) => {
548 let name = match kind {
549 FastPForKind::Block256Be => "256be",
550 #[cfg(feature = "unstable-v2")]
551 FastPForKind::Block128Le => "128le",
552 };
553 write!(f, "fastpfor[{name}]")
554 }
555 #[cfg(feature = "unstable-v2")]
556 Self::BitPacked => f.write_str("bit-packed"),
557 }
558 }
559}
560
561#[cfg(test)]
562mod tests {
563 use rstest::rstest;
564
565 use super::*;
566
567 fn morton() -> Morton {
568 Morton { bits: 4, shift: 0 }
569 }
570
571 fn alp() -> Alp {
572 Alp {
573 scale: AlpScale { e: 3, f: 1 },
574 base: -5,
575 }
576 }
577
578 fn rle() -> RleMeta {
579 RleMeta::Split {
580 runs: 2,
581 num_rle_values: 5,
582 }
583 }
584
585 #[rstest]
586 #[case::present(StreamType::Present, "present")]
587 #[case::data_none(StreamType::Data(DictionaryType::None), "data")]
588 #[case::data_single(StreamType::Data(DictionaryType::Single), "data[single]")]
589 #[case::data_shared(StreamType::Data(DictionaryType::Shared), "data[shared]")]
590 #[case::data_vertex(StreamType::Data(DictionaryType::Vertex), "data[vertex]")]
591 #[case::data_morton(StreamType::Data(DictionaryType::Morton), "data[morton]")]
592 #[case::data_fsst(StreamType::Data(DictionaryType::Fsst), "data[fsst]")]
593 #[case::offset_vertex(StreamType::Offset(OffsetType::Vertex), "offset[vertex]")]
594 #[case::offset_index(StreamType::Offset(OffsetType::Index), "offset[index]")]
595 #[case::offset_string(StreamType::Offset(OffsetType::String), "offset[string]")]
596 #[case::offset_key(StreamType::Offset(OffsetType::Key), "offset[key]")]
597 #[case::length_var_binary(StreamType::Length(LengthType::VarBinary), "length[var-binary]")]
598 #[case::length_geometries(StreamType::Length(LengthType::Geometries), "length[geometries]")]
599 #[case::length_parts(StreamType::Length(LengthType::Parts), "length[parts]")]
600 #[case::length_rings(StreamType::Length(LengthType::Rings), "length[rings]")]
601 #[case::length_triangles(StreamType::Length(LengthType::Triangles), "length[triangles]")]
602 #[case::length_symbol(StreamType::Length(LengthType::Symbol), "length[symbol]")]
603 #[case::length_dictionary(StreamType::Length(LengthType::Dictionary), "length[dictionary]")]
604 fn every_stream_type_renders_its_wire_label(
605 #[case] stream_type: StreamType,
606 #[case] expected: &str,
607 ) {
608 assert_eq!(stream_type.to_string(), expected);
609 }
610
611 #[cfg(feature = "unstable-v2")]
612 #[test]
613 fn a_nested_length_stream_renders_its_wire_label() {
614 assert_eq!(
615 StreamType::Length(LengthType::Nested).to_string(),
616 "length[nested]"
617 );
618 }
619
620 #[rstest]
621 #[case::int_none(LogicalEncoding::Int(IntLogical::None), "int/none")]
622 #[case::int_delta(LogicalEncoding::Int(IntLogical::Delta), "int/delta")]
623 #[case::int_rle(LogicalEncoding::Int(IntLogical::Rle(rle())), "int/rle")]
624 #[case::int_delta_rle(LogicalEncoding::Int(IntLogical::DeltaRle(rle())), "int/delta-rle")]
625 #[case::bool_none(LogicalEncoding::Bool(BoolLogical::None), "bool/none")]
626 #[case::bool_byte_rle(LogicalEncoding::Bool(BoolLogical::ByteRle(rle())), "bool/byte-rle")]
627 #[case::float_none(LogicalEncoding::Float(FloatLogical::None), "float/none")]
628 #[case::float_dict(LogicalEncoding::Float(FloatLogical::Dict), "float/dict")]
629 #[case::float_alp(LogicalEncoding::Float(FloatLogical::Alp(alp())), "float/alp")]
630 #[case::vertex_none(LogicalEncoding::Vertex(VertexLogical::None), "vertex/none")]
631 #[case::vertex_delta(LogicalEncoding::Vertex(VertexLogical::Delta), "vertex/delta")]
632 #[case::vertex_componentwise_delta(
633 LogicalEncoding::Vertex(VertexLogical::ComponentwiseDelta),
634 "vertex/componentwise-delta"
635 )]
636 #[case::vertex_morton(
637 LogicalEncoding::Vertex(VertexLogical::Morton(morton())),
638 "vertex/morton"
639 )]
640 #[case::vertex_morton_delta(
641 LogicalEncoding::Vertex(VertexLogical::MortonDelta(morton())),
642 "vertex/morton-delta"
643 )]
644 #[case::vertex_morton_rle(
645 LogicalEncoding::Vertex(VertexLogical::MortonRle(morton())),
646 "vertex/morton-rle"
647 )]
648 fn every_logical_encoding_renders_kind_then_encoding(
649 #[case] encoding: LogicalEncoding,
650 #[case] expected: &str,
651 ) {
652 assert_eq!(encoding.to_string(), expected);
653 }
654
655 #[rstest]
656 #[case::none(PhysicalEncoding::None, "none")]
657 #[case::varint(PhysicalEncoding::VarInt, "varint")]
658 #[case::fastpfor_256be(
659 PhysicalEncoding::FastPFor(FastPForKind::Block256Be),
660 "fastpfor[256be]"
661 )]
662 fn every_physical_encoding_renders_its_wire_label(
663 #[case] encoding: PhysicalEncoding,
664 #[case] expected: &str,
665 ) {
666 assert_eq!(encoding.to_string(), expected);
667 }
668
669 #[cfg(feature = "unstable-v2")]
670 #[rstest]
671 #[case::fastpfor_128le(
672 PhysicalEncoding::FastPFor(FastPForKind::Block128Le),
673 "fastpfor[128le]"
674 )]
675 #[case::bit_packed(PhysicalEncoding::BitPacked, "bit-packed")]
676 fn every_v2_physical_encoding_renders_its_wire_label(
677 #[case] encoding: PhysicalEncoding,
678 #[case] expected: &str,
679 ) {
680 assert_eq!(encoding.to_string(), expected);
681 }
682
683 #[rstest]
684 #[case::int(LogicalEncoding::Int(IntLogical::None), ValueKind::Int)]
685 #[case::bool(LogicalEncoding::Bool(BoolLogical::None), ValueKind::Bool)]
686 #[case::float(LogicalEncoding::Float(FloatLogical::None), ValueKind::Float)]
687 #[case::vertex(LogicalEncoding::Vertex(VertexLogical::None), ValueKind::Vertex)]
688 fn the_identity_encoding_of_a_kind_reports_that_kind_back(
689 #[case] expected: LogicalEncoding,
690 #[case] kind: ValueKind,
691 ) {
692 let none = LogicalEncoding::none(kind);
693 assert_eq!(none, expected);
694 assert_eq!(none.kind(), kind);
695 assert!(none.is_identity());
696 }
697}