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 pub(crate) fn to_byte(self) -> u8 {
137 Self::row_start(self.e) + self.f
138 }
139
140 pub(crate) fn from_byte(byte: u8) -> MltResult<Self> {
142 (0..=Self::MAX_EXPONENT)
143 .find_map(|e| {
144 let f = byte.checked_sub(Self::row_start(e))?;
145 (f <= e).then_some(Self { e, f })
146 })
147 .ok_or(MltError::InvalidAlpScale(byte))
148 }
149
150 fn row_start(e: u8) -> u8 {
152 (1..=e).sum()
153 }
154}
155
156impl std::fmt::Debug for Alp {
158 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
159 write!(
160 f,
161 "Alp {{ e: {}, f: {}, base: {} }}",
162 self.scale.e, self.scale.f, self.base
163 )
164 }
165}
166
167#[cfg(feature = "unstable-v2")]
168impl Alp {
169 #[cfg(test)]
170 pub(crate) fn new(e: u8, f: u8, base: i64) -> MltResult<Self> {
171 if e <= AlpScale::MAX_EXPONENT && f <= e {
172 Ok(Self {
173 scale: AlpScale { e, f },
174 base,
175 })
176 } else {
177 Err(MltError::InvalidAlpParams(e, f))
178 }
179 }
180
181 #[expect(
185 clippy::cast_sign_loss,
186 reason = "the bit pattern is the point; `code_at` casts it back"
187 )]
188 pub(crate) fn offset_of(self, code: i64) -> u64 {
189 (code as u64).wrapping_sub(self.base as u64)
190 }
191
192 #[expect(
194 clippy::cast_possible_wrap,
195 clippy::cast_sign_loss,
196 reason = "the bit pattern is the point; inverts `offset_of` exactly"
197 )]
198 pub(crate) fn code_at(self, offset: u64) -> i64 {
199 (self.base as u64).wrapping_add(offset) as i64
200 }
201}
202
203#[derive(Debug, Clone, Copy, PartialEq, Eq)]
206pub enum ValueKind {
207 Int,
208 Bool,
209 Float,
210 Vertex,
211}
212
213#[derive(Debug, Clone, Copy, PartialEq)]
216pub enum IntLogical {
217 None,
218 Delta,
219 Rle(RleMeta),
220 DeltaRle(RleMeta),
221}
222
223#[derive(Debug, Clone, Copy, PartialEq)]
225pub enum BoolLogical {
226 None,
228 ByteRle(RleMeta),
231}
232
233#[derive(Debug, Clone, Copy, PartialEq)]
235pub enum FloatLogical {
236 None,
238 Dict,
242 Alp(Alp),
245}
246
247#[derive(Debug, Clone, Copy, PartialEq)]
249pub enum VertexLogical {
250 None,
251 Delta,
252 ComponentwiseDelta,
253 Morton(Morton),
254 MortonDelta(Morton),
255 MortonRle(Morton),
256}
257
258#[derive(Debug, Clone, Copy, PartialEq)]
262pub enum LogicalEncoding {
263 Int(IntLogical),
264 Bool(BoolLogical),
265 Float(FloatLogical),
266 Vertex(VertexLogical),
267}
268
269impl LogicalEncoding {
270 #[must_use]
272 pub fn kind(self) -> ValueKind {
273 match self {
274 Self::Int(_) => ValueKind::Int,
275 Self::Bool(_) => ValueKind::Bool,
276 Self::Float(_) => ValueKind::Float,
277 Self::Vertex(_) => ValueKind::Vertex,
278 }
279 }
280
281 #[must_use]
283 pub(crate) fn none(kind: ValueKind) -> Self {
284 match kind {
285 ValueKind::Int => Self::Int(IntLogical::None),
286 ValueKind::Bool => Self::Bool(BoolLogical::None),
287 ValueKind::Float => Self::Float(FloatLogical::None),
288 ValueKind::Vertex => Self::Vertex(VertexLogical::None),
289 }
290 }
291
292 #[must_use]
296 pub(crate) fn is_identity(self) -> bool {
297 matches!(
298 self,
299 Self::Int(IntLogical::None)
300 | Self::Bool(BoolLogical::None)
301 | Self::Float(FloatLogical::None | FloatLogical::Dict)
302 | Self::Vertex(VertexLogical::None)
303 )
304 }
305}
306
307#[derive(Debug, PartialEq)]
313pub struct LogicalValue {
314 pub(crate) meta: StreamMeta,
315}
316
317#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, TryFromPrimitive)]
321#[repr(u8)]
322pub enum DictionaryType {
323 None = 0b0000_0000,
324 Single = 0b0000_0001,
325 Shared = 0b0000_0010,
326 Vertex = 0b0000_0011,
327 Morton = 0b0000_0100,
328 Fsst = 0b0000_0101,
329}
330
331#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, TryFromPrimitive)]
333#[repr(u8)]
334pub enum OffsetType {
335 Vertex = 0b0000_0000,
336 Index = 0b0000_0001,
337 String = 0b0000_0010,
338 Key = 0b0000_0011,
339}
340
341#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, TryFromPrimitive)]
343#[repr(u8)]
344pub enum LengthType {
345 VarBinary = 0b0000_0000,
346 Geometries = 0b0000_0001,
347 Parts = 0b0000_0010,
348 Rings = 0b0000_0011,
349 Triangles = 0b0000_0100,
350 Symbol = 0b0000_0101,
351 Dictionary = 0b0000_0110,
352 #[cfg(feature = "unstable-v2")]
354 Nested = 0b0000_0111,
355}
356
357#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
359pub enum StreamType {
360 Present,
361 Data(DictionaryType),
362 Offset(OffsetType),
363 Length(LengthType),
364}
365
366#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
368pub enum PhysicalEncoding {
369 None,
370 FastPFor(FastPForKind),
373 VarInt,
376 #[cfg(feature = "unstable-v2")]
380 BitPacked,
381}
382
383#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
385pub enum FastPForKind {
386 Block256Be,
388 #[cfg(feature = "unstable-v2")]
390 Block128Le,
391}
392
393#[derive(Debug, Clone, Copy, PartialEq)]
396pub struct IntEncoding {
397 pub logical: LogicalEncoding,
398 pub physical: PhysicalEncoding,
399}
400
401impl IntEncoding {
402 #[must_use]
403 pub(crate) const fn new(logical: LogicalEncoding, physical: PhysicalEncoding) -> Self {
404 Self { logical, physical }
405 }
406
407 #[must_use]
408 pub(crate) fn none(kind: ValueKind) -> Self {
409 Self::new(LogicalEncoding::none(kind), PhysicalEncoding::None)
410 }
411}
412
413#[derive(Clone, Copy, Dbg, PartialEq)]
415pub struct StreamMeta {
416 #[dbg(formatter = "compact_dbg")]
417 pub stream_type: StreamType,
418 #[dbg(formatter = "compact_dbg")]
419 pub encoding: IntEncoding,
420 pub(crate) num_values: u32,
421}
422
423impl StreamMeta {
424 #[inline]
425 pub(crate) fn new(stream_type: StreamType, encoding: IntEncoding, num_values: u32) -> Self {
426 Self {
427 stream_type,
428 encoding,
429 num_values,
430 }
431 }
432
433 #[inline]
434 pub(crate) fn new2(
435 stream_type: StreamType,
436 logical: LogicalEncoding,
437 physical: PhysicalEncoding,
438 num_values: usize,
439 ) -> MltResult<Self> {
440 let enc = IntEncoding::new(logical, physical);
441 Ok(Self::new(stream_type, enc, u32::try_from(num_values)?))
442 }
443
444 #[inline]
445 pub(crate) fn new_none(
446 stream_type: StreamType,
447 kind: ValueKind,
448 num_values: usize,
449 ) -> MltResult<Self> {
450 let enc = IntEncoding::none(kind);
451 Ok(Self::new(stream_type, enc, u32::try_from(num_values)?))
452 }
453}
454
455#[derive(Clone, Dbg, PartialEq)]
457pub struct RawStream<'a> {
458 pub meta: StreamMeta,
459 #[dbg(formatter = "bytes_dbg")]
460 pub(crate) data: &'a [u8],
461}
462
463impl<'a> RawStream<'a> {
464 #[must_use]
465 pub(crate) fn new(meta: StreamMeta, data: &'a [u8]) -> Self {
466 Self { meta, data }
467 }
468}
469
470impl Display for StreamType {
474 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
476 match self {
477 Self::Present => f.write_str("present"),
478 Self::Data(dict) => {
479 let name = match dict {
480 DictionaryType::None => return f.write_str("data"),
481 DictionaryType::Single => "single",
482 DictionaryType::Shared => "shared",
483 DictionaryType::Vertex => "vertex",
484 DictionaryType::Morton => "morton",
485 DictionaryType::Fsst => "fsst",
486 };
487 write!(f, "data[{name}]")
488 }
489 Self::Offset(offset) => {
490 let name = match offset {
491 OffsetType::Vertex => "vertex",
492 OffsetType::Index => "index",
493 OffsetType::String => "string",
494 OffsetType::Key => "key",
495 };
496 write!(f, "offset[{name}]")
497 }
498 Self::Length(length) => {
499 let name = match length {
500 LengthType::VarBinary => "var-binary",
501 LengthType::Geometries => "geometries",
502 LengthType::Parts => "parts",
503 LengthType::Rings => "rings",
504 LengthType::Triangles => "triangles",
505 LengthType::Symbol => "symbol",
506 LengthType::Dictionary => "dictionary",
507 #[cfg(feature = "unstable-v2")]
508 LengthType::Nested => "nested",
509 };
510 write!(f, "length[{name}]")
511 }
512 }
513 }
514}
515
516impl Display for LogicalEncoding {
517 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
521 let (kind, enc) = match self {
522 Self::Int(int) => (
523 "int",
524 match int {
525 IntLogical::None => "none",
526 IntLogical::Delta => "delta",
527 IntLogical::Rle(_) => "rle",
528 IntLogical::DeltaRle(_) => "delta-rle",
529 },
530 ),
531 Self::Bool(b) => (
532 "bool",
533 match b {
534 BoolLogical::None => "none",
535 BoolLogical::ByteRle(_) => "byte-rle",
536 },
537 ),
538 Self::Float(float) => (
539 "float",
540 match float {
541 FloatLogical::None => "none",
542 FloatLogical::Dict => "dict",
543 FloatLogical::Alp(_) => "alp",
544 },
545 ),
546 Self::Vertex(vertex) => (
547 "vertex",
548 match vertex {
549 VertexLogical::None => "none",
550 VertexLogical::Delta => "delta",
551 VertexLogical::ComponentwiseDelta => "componentwise-delta",
552 VertexLogical::Morton(_) => "morton",
553 VertexLogical::MortonDelta(_) => "morton-delta",
554 VertexLogical::MortonRle(_) => "morton-rle",
555 },
556 ),
557 };
558 write!(f, "{kind}/{enc}")
559 }
560}
561
562impl Display for PhysicalEncoding {
563 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
565 match self {
566 Self::None => f.write_str("none"),
567 Self::VarInt => f.write_str("varint"),
568 Self::FastPFor(kind) => {
569 let name = match kind {
570 FastPForKind::Block256Be => "256be",
571 #[cfg(feature = "unstable-v2")]
572 FastPForKind::Block128Le => "128le",
573 };
574 write!(f, "fastpfor[{name}]")
575 }
576 #[cfg(feature = "unstable-v2")]
577 Self::BitPacked => f.write_str("bit-packed"),
578 }
579 }
580}
581
582#[cfg(test)]
583mod tests {
584 use rstest::rstest;
585
586 use super::*;
587
588 fn morton() -> Morton {
589 Morton { bits: 4, shift: 0 }
590 }
591
592 fn alp() -> Alp {
593 Alp {
594 scale: AlpScale { e: 3, f: 1 },
595 base: -5,
596 }
597 }
598
599 fn rle() -> RleMeta {
600 RleMeta::Split {
601 runs: 2,
602 num_rle_values: 5,
603 }
604 }
605
606 #[rstest]
607 #[case::present(StreamType::Present, "present")]
608 #[case::data_none(StreamType::Data(DictionaryType::None), "data")]
609 #[case::data_single(StreamType::Data(DictionaryType::Single), "data[single]")]
610 #[case::data_shared(StreamType::Data(DictionaryType::Shared), "data[shared]")]
611 #[case::data_vertex(StreamType::Data(DictionaryType::Vertex), "data[vertex]")]
612 #[case::data_morton(StreamType::Data(DictionaryType::Morton), "data[morton]")]
613 #[case::data_fsst(StreamType::Data(DictionaryType::Fsst), "data[fsst]")]
614 #[case::offset_vertex(StreamType::Offset(OffsetType::Vertex), "offset[vertex]")]
615 #[case::offset_index(StreamType::Offset(OffsetType::Index), "offset[index]")]
616 #[case::offset_string(StreamType::Offset(OffsetType::String), "offset[string]")]
617 #[case::offset_key(StreamType::Offset(OffsetType::Key), "offset[key]")]
618 #[case::length_var_binary(StreamType::Length(LengthType::VarBinary), "length[var-binary]")]
619 #[case::length_geometries(StreamType::Length(LengthType::Geometries), "length[geometries]")]
620 #[case::length_parts(StreamType::Length(LengthType::Parts), "length[parts]")]
621 #[case::length_rings(StreamType::Length(LengthType::Rings), "length[rings]")]
622 #[case::length_triangles(StreamType::Length(LengthType::Triangles), "length[triangles]")]
623 #[case::length_symbol(StreamType::Length(LengthType::Symbol), "length[symbol]")]
624 #[case::length_dictionary(StreamType::Length(LengthType::Dictionary), "length[dictionary]")]
625 fn every_stream_type_renders_its_wire_label(
626 #[case] stream_type: StreamType,
627 #[case] expected: &str,
628 ) {
629 assert_eq!(stream_type.to_string(), expected);
630 }
631
632 #[cfg(feature = "unstable-v2")]
633 #[test]
634 fn a_nested_length_stream_renders_its_wire_label() {
635 assert_eq!(
636 StreamType::Length(LengthType::Nested).to_string(),
637 "length[nested]"
638 );
639 }
640
641 #[rstest]
642 #[case::int_none(LogicalEncoding::Int(IntLogical::None), "int/none")]
643 #[case::int_delta(LogicalEncoding::Int(IntLogical::Delta), "int/delta")]
644 #[case::int_rle(LogicalEncoding::Int(IntLogical::Rle(rle())), "int/rle")]
645 #[case::int_delta_rle(LogicalEncoding::Int(IntLogical::DeltaRle(rle())), "int/delta-rle")]
646 #[case::bool_none(LogicalEncoding::Bool(BoolLogical::None), "bool/none")]
647 #[case::bool_byte_rle(LogicalEncoding::Bool(BoolLogical::ByteRle(rle())), "bool/byte-rle")]
648 #[case::float_none(LogicalEncoding::Float(FloatLogical::None), "float/none")]
649 #[case::float_dict(LogicalEncoding::Float(FloatLogical::Dict), "float/dict")]
650 #[case::float_alp(LogicalEncoding::Float(FloatLogical::Alp(alp())), "float/alp")]
651 #[case::vertex_none(LogicalEncoding::Vertex(VertexLogical::None), "vertex/none")]
652 #[case::vertex_delta(LogicalEncoding::Vertex(VertexLogical::Delta), "vertex/delta")]
653 #[case::vertex_componentwise_delta(
654 LogicalEncoding::Vertex(VertexLogical::ComponentwiseDelta),
655 "vertex/componentwise-delta"
656 )]
657 #[case::vertex_morton(
658 LogicalEncoding::Vertex(VertexLogical::Morton(morton())),
659 "vertex/morton"
660 )]
661 #[case::vertex_morton_delta(
662 LogicalEncoding::Vertex(VertexLogical::MortonDelta(morton())),
663 "vertex/morton-delta"
664 )]
665 #[case::vertex_morton_rle(
666 LogicalEncoding::Vertex(VertexLogical::MortonRle(morton())),
667 "vertex/morton-rle"
668 )]
669 fn every_logical_encoding_renders_kind_then_encoding(
670 #[case] encoding: LogicalEncoding,
671 #[case] expected: &str,
672 ) {
673 assert_eq!(encoding.to_string(), expected);
674 }
675
676 #[rstest]
677 #[case::none(PhysicalEncoding::None, "none")]
678 #[case::varint(PhysicalEncoding::VarInt, "varint")]
679 #[case::fastpfor_256be(
680 PhysicalEncoding::FastPFor(FastPForKind::Block256Be),
681 "fastpfor[256be]"
682 )]
683 fn every_physical_encoding_renders_its_wire_label(
684 #[case] encoding: PhysicalEncoding,
685 #[case] expected: &str,
686 ) {
687 assert_eq!(encoding.to_string(), expected);
688 }
689
690 #[cfg(feature = "unstable-v2")]
691 #[rstest]
692 #[case::fastpfor_128le(
693 PhysicalEncoding::FastPFor(FastPForKind::Block128Le),
694 "fastpfor[128le]"
695 )]
696 #[case::bit_packed(PhysicalEncoding::BitPacked, "bit-packed")]
697 fn every_v2_physical_encoding_renders_its_wire_label(
698 #[case] encoding: PhysicalEncoding,
699 #[case] expected: &str,
700 ) {
701 assert_eq!(encoding.to_string(), expected);
702 }
703
704 #[rstest]
705 #[case::int(LogicalEncoding::Int(IntLogical::None), ValueKind::Int)]
706 #[case::bool(LogicalEncoding::Bool(BoolLogical::None), ValueKind::Bool)]
707 #[case::float(LogicalEncoding::Float(FloatLogical::None), ValueKind::Float)]
708 #[case::vertex(LogicalEncoding::Vertex(VertexLogical::None), ValueKind::Vertex)]
709 fn the_identity_encoding_of_a_kind_reports_that_kind_back(
710 #[case] expected: LogicalEncoding,
711 #[case] kind: ValueKind,
712 ) {
713 let none = LogicalEncoding::none(kind);
714 assert_eq!(none, expected);
715 assert_eq!(none.kind(), kind);
716 assert!(none.is_identity());
717 }
718}