1use chronix_core::config::FloatEncoding;
18
19use crate::alp::AlpDecoder;
20use crate::bitmap::{BitmapDecoder, BitmapEncoder};
21use crate::chimp::{Chimp128Decoder, ChimpDecoder};
22use crate::delta::{DeltaOfDeltaDecoder, DeltaOfDeltaEncoder};
23use crate::dictionary::{DictionaryDecoder, DictionaryEncoder};
24use crate::error::{EncodingError, Result};
25use crate::for_encoding::{ForDecoder, ForEncoder};
26use crate::gorilla::{GorillaDecoder, GorillaEncoder};
27use crate::integer::{IntegerDecoder, IntegerEncoder, VarintDecoder, VarintEncoder};
28use crate::patas::PatasDecoder;
29use crate::plain::{PlainDecoder, PlainEncoder};
30use crate::rle::{RleDecoder, RleEncoder};
31
32macro_rules! encoding_types {
48 ($(
49 $(#[$meta:meta])*
50 $variant:ident = $tag:literal, $name:literal;
51 )+) => {
52 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
54 #[repr(u8)]
55 pub enum EncodingType {
56 $(
57 $(#[$meta])*
58 $variant = $tag,
59 )+
60 }
61
62 impl EncodingType {
63 pub const ALL: &'static [Self] = &[$(Self::$variant),+];
69
70 pub fn from_tag(tag: u8) -> Result<Self> {
76 match tag {
77 $($tag => Ok(Self::$variant),)+
78 other => Err(EncodingError::UnsupportedEncoding { tag: other }),
79 }
80 }
81
82 #[must_use]
84 pub fn tag(self) -> u8 {
85 self as u8
86 }
87 }
88
89 impl std::fmt::Display for EncodingType {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 f.write_str(match self {
92 $(Self::$variant => $name,)+
93 })
94 }
95 }
96 };
97}
98
99encoding_types! {
100 DeltaOfDelta = 0, "delta-of-delta";
105 Chimp = 1, "chimp";
107 Gorilla = 2, "gorilla";
109 IntegerI64 = 3, "integer-i64";
111 IntegerU64 = 4, "integer-u64";
113 Dictionary = 5, "dictionary";
115 Bitmap = 6, "bitmap";
117 PlainF64 = 7, "plain-f64";
119 PlainI64 = 8, "plain-i64";
121 PlainU64 = 9, "plain-u64";
123 PlainBool = 10, "plain-bool";
125 PlainString = 11, "plain-string";
127 Nullable = 12, "nullable";
136 Rle = 13, "rle";
138 VarintI64 = 14, "varint-i64";
144 VarintU64 = 15, "varint-u64";
146 Chimp128 = 16, "chimp128";
148 ForI64 = 17, "for-i64";
150 ForU64 = 18, "for-u64";
152 Patas = 19, "patas";
154 Alp = 20, "alp";
160 Pco = 21, "pco";
163 PcoI64 = 22, "pco_i64";
165 PcoU64 = 23, "pco_u64";
167 DecimalI128 = 24, "decimal-i128";
173}
174
175#[derive(Debug, Clone)]
183pub struct EncodedBlock {
184 pub encoding: EncodingType,
186 pub payload: Vec<u8>,
188}
189
190impl EncodedBlock {
191 #[must_use]
193 pub fn to_bytes(&self) -> Vec<u8> {
194 let mut out = Vec::with_capacity(1 + self.payload.len());
195 out.push(self.encoding.tag());
196 out.extend_from_slice(&self.payload);
197 out
198 }
199
200 pub fn from_bytes(data: &[u8]) -> Result<Self> {
206 if data.is_empty() {
207 return Err(EncodingError::CorruptData {
208 detail: "empty encoded block".to_string(),
209 });
210 }
211 let encoding = EncodingType::from_tag(data[0])?;
212 let payload = data[1..].to_vec();
213 Ok(Self { encoding, payload })
214 }
215}
216
217const MIN_COMPRESSION_RATIO: f64 = 1.5;
224
225#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
238pub enum TimestampEncoding {
239 #[default]
241 Auto,
242 Pco,
244 DeltaOfDelta,
246 Plain,
248}
249
250#[derive(Debug, Clone, Copy)]
266pub struct ColumnEncoder;
267
268impl ColumnEncoder {
269 pub fn encode_timestamps(values: &[i64]) -> Result<EncodedBlock> {
279 Self::encode_timestamps_with(values, TimestampEncoding::Auto)
280 }
281
282 pub fn encode_timestamps_with(
317 values: &[i64],
318 strategy: TimestampEncoding,
319 ) -> Result<EncodedBlock> {
320 let raw_size = values.len() * 8;
321
322 let (encoding, payload) = match strategy {
323 TimestampEncoding::Plain => {
324 return Ok(EncodedBlock {
325 encoding: EncodingType::PlainI64,
326 payload: PlainEncoder::encode_i64(values)?,
327 })
328 }
329 TimestampEncoding::DeltaOfDelta => (
330 EncodingType::DeltaOfDelta,
331 DeltaOfDeltaEncoder::encode(values)?,
332 ),
333 TimestampEncoding::Pco => (
334 EncodingType::PcoI64,
335 crate::pco::PcoEncoder::encode_timestamps(values)?,
336 ),
337 TimestampEncoding::Auto => {
338 let pco = crate::pco::PcoEncoder::encode_timestamps(values)?;
339 let dod = DeltaOfDeltaEncoder::encode(values)?;
340 if dod.len() < pco.len() {
343 (EncodingType::DeltaOfDelta, dod)
344 } else {
345 (EncodingType::PcoI64, pco)
346 }
347 }
348 };
349
350 if should_use_specialized(&payload, raw_size) {
351 Ok(EncodedBlock { encoding, payload })
352 } else {
353 Ok(EncodedBlock {
354 encoding: EncodingType::PlainI64,
355 payload: PlainEncoder::encode_i64(values)?,
356 })
357 }
358 }
359
360 pub fn encode_f64(values: &[f64], strategy: FloatEncoding) -> Result<EncodedBlock> {
370 match strategy {
371 FloatEncoding::Chimp => {
372 let selector = crate::adaptive::AdaptiveSelector::new();
376 selector.encode_f64_adaptive(values)
377 }
378 FloatEncoding::Gorilla => {
379 let raw_size = values.len() * 8;
380 let gorilla_payload = GorillaEncoder::encode(values)?;
381 if should_use_specialized(&gorilla_payload, raw_size) {
382 return Ok(EncodedBlock {
383 encoding: EncodingType::Gorilla,
384 payload: gorilla_payload,
385 });
386 }
387 Ok(EncodedBlock {
388 encoding: EncodingType::PlainF64,
389 payload: PlainEncoder::encode_f64(values)?,
390 })
391 }
392 FloatEncoding::Plain => Ok(EncodedBlock {
393 encoding: EncodingType::PlainF64,
394 payload: PlainEncoder::encode_f64(values)?,
395 }),
396 }
397 }
398
399 pub fn encode_i64(values: &[i64]) -> Result<EncodedBlock> {
409 let selector = crate::adaptive::AdaptiveSelector::new();
410 let pattern = selector.analyze_integers(values);
411
412 if pattern == crate::adaptive::IntegerPattern::Constant {
414 let raw_size = values.len() * 8;
415 let rle_payload = RleEncoder::encode_i64(values)?;
416 if should_use_specialized(&rle_payload, raw_size) {
417 return Ok(EncodedBlock {
418 encoding: EncodingType::Rle,
419 payload: rle_payload,
420 });
421 }
422 }
423
424 let raw_size = values.len() * 8;
425
426 let fixed_payload = IntegerEncoder::encode_i64(values)?;
428 let varint_payload = VarintEncoder::encode_i64(values)?;
429 let for_payload = ForEncoder::encode_i64(values)?;
430 let pco_payload = crate::pco::PcoEncoder::encode_i64(values)?;
431
432 let mut best_enc = EncodingType::IntegerI64;
433 let mut best_payload = fixed_payload;
434
435 if varint_payload.len() < best_payload.len() {
436 best_enc = EncodingType::VarintI64;
437 best_payload = varint_payload;
438 }
439 if for_payload.len() < best_payload.len() {
440 best_enc = EncodingType::ForI64;
441 best_payload = for_payload;
442 }
443 if pco_payload.len() < best_payload.len() {
444 best_enc = EncodingType::PcoI64;
445 best_payload = pco_payload;
446 }
447
448 if should_use_specialized(&best_payload, raw_size) {
449 Ok(EncodedBlock {
450 encoding: best_enc,
451 payload: best_payload,
452 })
453 } else {
454 Ok(EncodedBlock {
455 encoding: EncodingType::PlainI64,
456 payload: PlainEncoder::encode_i64(values)?,
457 })
458 }
459 }
460
461 pub fn encode_u64(values: &[u64]) -> Result<EncodedBlock> {
471 let as_i64: Vec<i64> = values.iter().map(|&v| v as i64).collect();
473 let selector = crate::adaptive::AdaptiveSelector::new();
474 if selector.analyze_integers(&as_i64) == crate::adaptive::IntegerPattern::Constant {
475 let raw_size = values.len() * 8;
476 let rle_payload = RleEncoder::encode_u64(values)?;
477 if should_use_specialized(&rle_payload, raw_size) {
478 return Ok(EncodedBlock {
479 encoding: EncodingType::Rle,
480 payload: rle_payload,
481 });
482 }
483 }
484
485 let raw_size = values.len() * 8;
486 let fixed_payload = IntegerEncoder::encode_u64(values)?;
487 let varint_payload = VarintEncoder::encode_u64(values)?;
488 let for_payload = ForEncoder::encode_u64(values)?;
489 let pco_payload = crate::pco::PcoEncoder::encode_u64(values)?;
490
491 let mut best_enc = EncodingType::IntegerU64;
493 let mut best_payload = fixed_payload;
494
495 if varint_payload.len() < best_payload.len() {
496 best_enc = EncodingType::VarintU64;
497 best_payload = varint_payload;
498 }
499 if for_payload.len() < best_payload.len() {
500 best_enc = EncodingType::ForU64;
501 best_payload = for_payload;
502 }
503 if pco_payload.len() < best_payload.len() {
504 best_enc = EncodingType::PcoU64;
505 best_payload = pco_payload;
506 }
507
508 if should_use_specialized(&best_payload, raw_size) {
509 Ok(EncodedBlock {
510 encoding: best_enc,
511 payload: best_payload,
512 })
513 } else {
514 Ok(EncodedBlock {
515 encoding: EncodingType::PlainU64,
516 payload: PlainEncoder::encode_u64(values)?,
517 })
518 }
519 }
520
521 pub fn encode_string(values: &[&str]) -> Result<EncodedBlock> {
530 let raw_size: usize = values.iter().map(|s| 4 + s.len()).sum();
531
532 match DictionaryEncoder::encode(values) {
533 Ok(payload) if should_use_specialized(&payload, raw_size) => Ok(EncodedBlock {
534 encoding: EncodingType::Dictionary,
535 payload,
536 }),
537 Ok(_) | Err(EncodingError::DictionaryOverflow { .. }) => Ok(EncodedBlock {
539 encoding: EncodingType::PlainString,
540 payload: PlainEncoder::encode_string(values)?,
541 }),
542 Err(e) => Err(e),
544 }
545 }
546
547 pub fn encode_bool(values: &[bool]) -> Result<EncodedBlock> {
555 if !values.is_empty() {
557 let first = values[0];
558 if values.iter().all(|&v| v == first) {
559 let raw_size = values.len(); let rle_payload = RleEncoder::encode_bool(values)?;
561 if should_use_specialized(&rle_payload, raw_size) {
562 return Ok(EncodedBlock {
563 encoding: EncodingType::Rle,
564 payload: rle_payload,
565 });
566 }
567 }
568 }
569 Ok(EncodedBlock {
570 encoding: EncodingType::Bitmap,
571 payload: BitmapEncoder::encode(values)?,
572 })
573 }
574
575 pub fn encode_decimal(values: &[i128]) -> Result<EncodedBlock> {
588 Ok(EncodedBlock {
589 encoding: EncodingType::DecimalI128,
590 payload: crate::decimal::DecimalEncoder::encode(values)?,
591 })
592 }
593
594 pub fn encode_f64_nullable(
606 values: &[Option<f64>],
607 strategy: FloatEncoding,
608 ) -> Result<EncodedBlock> {
609 if values.is_empty() {
610 return Err(EncodingError::EmptyInput {
611 context: "nullable f64 encoder",
612 });
613 }
614 let (non_null, validity) = extract_non_null_f64(values);
615 if non_null.is_empty() {
616 return Ok(wrap_nullable(
618 EncodingType::PlainF64,
619 values.len(),
620 &validity,
621 &[],
622 ));
623 }
624 let inner = Self::encode_f64(&non_null, strategy)?;
625 Ok(wrap_nullable(
626 inner.encoding,
627 values.len(),
628 &validity,
629 &inner.payload,
630 ))
631 }
632
633 pub fn encode_i64_nullable(values: &[Option<i64>]) -> Result<EncodedBlock> {
639 if values.is_empty() {
640 return Err(EncodingError::EmptyInput {
641 context: "nullable i64 encoder",
642 });
643 }
644 let (non_null, validity) = extract_non_null_i64(values);
645 if non_null.is_empty() {
646 return Ok(wrap_nullable(
647 EncodingType::PlainI64,
648 values.len(),
649 &validity,
650 &[],
651 ));
652 }
653 let inner = Self::encode_i64(&non_null)?;
654 Ok(wrap_nullable(
655 inner.encoding,
656 values.len(),
657 &validity,
658 &inner.payload,
659 ))
660 }
661
662 pub fn encode_u64_nullable(values: &[Option<u64>]) -> Result<EncodedBlock> {
668 if values.is_empty() {
669 return Err(EncodingError::EmptyInput {
670 context: "nullable u64 encoder",
671 });
672 }
673 let (non_null, validity) = extract_non_null_u64(values);
674 if non_null.is_empty() {
675 return Ok(wrap_nullable(
676 EncodingType::PlainU64,
677 values.len(),
678 &validity,
679 &[],
680 ));
681 }
682 let inner = Self::encode_u64(&non_null)?;
683 Ok(wrap_nullable(
684 inner.encoding,
685 values.len(),
686 &validity,
687 &inner.payload,
688 ))
689 }
690
691 pub fn encode_string_nullable(values: &[Option<&str>]) -> Result<EncodedBlock> {
697 if values.is_empty() {
698 return Err(EncodingError::EmptyInput {
699 context: "nullable string encoder",
700 });
701 }
702 let (non_null, validity): (Vec<&str>, Vec<bool>) = values
703 .iter()
704 .map(|v| match v {
705 Some(s) => (*s, true),
706 None => ("", false),
707 })
708 .unzip();
709 let non_null_only: Vec<&str> = non_null
710 .into_iter()
711 .zip(validity.iter())
712 .filter(|(_, v)| **v)
713 .map(|(s, _)| s)
714 .collect();
715 if non_null_only.is_empty() {
716 return Ok(wrap_nullable(
717 EncodingType::PlainString,
718 values.len(),
719 &validity,
720 &[],
721 ));
722 }
723 let inner = Self::encode_string(&non_null_only)?;
724 Ok(wrap_nullable(
725 inner.encoding,
726 values.len(),
727 &validity,
728 &inner.payload,
729 ))
730 }
731
732 pub fn encode_decimal_nullable(values: &[Option<i128>]) -> Result<EncodedBlock> {
738 if values.is_empty() {
739 return Err(EncodingError::EmptyInput {
740 context: "nullable decimal encoder",
741 });
742 }
743 let validity: Vec<bool> = values.iter().map(std::option::Option::is_some).collect();
744 let non_null: Vec<i128> = values.iter().filter_map(|v| *v).collect();
745 if non_null.is_empty() {
746 return Ok(wrap_nullable(
747 EncodingType::DecimalI128,
748 values.len(),
749 &validity,
750 &[],
751 ));
752 }
753 let inner = Self::encode_decimal(&non_null)?;
754 Ok(wrap_nullable(
755 inner.encoding,
756 values.len(),
757 &validity,
758 &inner.payload,
759 ))
760 }
761
762 pub fn encode_bool_nullable(values: &[Option<bool>]) -> Result<EncodedBlock> {
768 if values.is_empty() {
769 return Err(EncodingError::EmptyInput {
770 context: "nullable bool encoder",
771 });
772 }
773 let validity: Vec<bool> = values.iter().map(std::option::Option::is_some).collect();
774 let non_null: Vec<bool> = values.iter().filter_map(|v| *v).collect();
775 if non_null.is_empty() {
776 return Ok(wrap_nullable(
777 EncodingType::Bitmap,
778 values.len(),
779 &validity,
780 &[],
781 ));
782 }
783 let inner = Self::encode_bool(&non_null)?;
784 Ok(wrap_nullable(
785 inner.encoding,
786 values.len(),
787 &validity,
788 &inner.payload,
789 ))
790 }
791}
792
793#[derive(Debug, Clone)]
805pub enum DecodedColumn {
806 I64(Vec<i64>),
808 U64(Vec<u64>),
810 F64(Vec<f64>),
812 Bool(Vec<bool>),
814 String(Vec<String>),
816 Decimal(Vec<i128>),
819 NullableI64(Vec<Option<i64>>),
821 NullableU64(Vec<Option<u64>>),
823 NullableF64(Vec<Option<f64>>),
825 NullableBool(Vec<Option<bool>>),
827 NullableString(Vec<Option<String>>),
829 NullableDecimal(Vec<Option<i128>>),
831}
832
833fn f64_slice_eq(a: &[f64], b: &[f64]) -> bool {
835 a.len() == b.len() && a.iter().zip(b).all(|(x, y)| x.to_bits() == y.to_bits())
836}
837
838fn opt_f64_slice_eq(a: &[Option<f64>], b: &[Option<f64>]) -> bool {
840 a.len() == b.len()
841 && a.iter().zip(b).all(|(x, y)| match (x, y) {
842 (Some(x), Some(y)) => x.to_bits() == y.to_bits(),
843 (None, None) => true,
844 _ => false,
845 })
846}
847
848impl PartialEq for DecodedColumn {
849 fn eq(&self, other: &Self) -> bool {
850 match (self, other) {
851 (Self::I64(a), Self::I64(b)) => a == b,
852 (Self::U64(a), Self::U64(b)) => a == b,
853 (Self::F64(a), Self::F64(b)) => f64_slice_eq(a, b),
854 (Self::Bool(a), Self::Bool(b)) => a == b,
855 (Self::String(a), Self::String(b)) => a == b,
856 (Self::NullableI64(a), Self::NullableI64(b)) => a == b,
857 (Self::NullableU64(a), Self::NullableU64(b)) => a == b,
858 (Self::NullableF64(a), Self::NullableF64(b)) => opt_f64_slice_eq(a, b),
859 (Self::NullableBool(a), Self::NullableBool(b)) => a == b,
860 (Self::NullableString(a), Self::NullableString(b)) => a == b,
861 (Self::Decimal(a), Self::Decimal(b)) => a == b,
862 (Self::NullableDecimal(a), Self::NullableDecimal(b)) => a == b,
863 _ => false,
864 }
865 }
866}
867
868impl DecodedColumn {
869 #[must_use]
871 pub fn len(&self) -> usize {
872 match self {
873 Self::I64(v) => v.len(),
874 Self::U64(v) => v.len(),
875 Self::F64(v) => v.len(),
876 Self::Bool(v) => v.len(),
877 Self::String(v) => v.len(),
878 Self::NullableI64(v) => v.len(),
879 Self::NullableU64(v) => v.len(),
880 Self::NullableF64(v) => v.len(),
881 Self::NullableBool(v) => v.len(),
882 Self::NullableString(v) => v.len(),
883 Self::Decimal(v) => v.len(),
884 Self::NullableDecimal(v) => v.len(),
885 }
886 }
887
888 #[must_use]
890 pub fn is_empty(&self) -> bool {
891 self.len() == 0
892 }
893}
894
895#[derive(Debug, Clone, Copy)]
903pub struct ColumnDecoder;
904
905impl ColumnDecoder {
906 pub fn decode(block: &EncodedBlock) -> Result<DecodedColumn> {
913 match block.encoding {
914 EncodingType::DeltaOfDelta => {
915 let values = DeltaOfDeltaDecoder::decode(&block.payload)?;
916 Ok(DecodedColumn::I64(values))
917 }
918 EncodingType::Chimp => {
919 let values = ChimpDecoder::decode(&block.payload)?;
920 Ok(DecodedColumn::F64(values))
921 }
922 EncodingType::Chimp128 => {
923 let values = Chimp128Decoder::decode(&block.payload)?;
924 Ok(DecodedColumn::F64(values))
925 }
926 EncodingType::Patas => {
927 let values = PatasDecoder::decode(&block.payload)?;
928 Ok(DecodedColumn::F64(values))
929 }
930 EncodingType::Alp => {
931 let values = AlpDecoder::decode(&block.payload)?;
932 Ok(DecodedColumn::F64(values))
933 }
934 EncodingType::Pco => {
935 let values = crate::pco::PcoDecoder::decode_f64(&block.payload)?;
936 Ok(DecodedColumn::F64(values))
937 }
938 EncodingType::PcoI64 => {
939 let values = crate::pco::PcoDecoder::decode_i64(&block.payload)?;
940 Ok(DecodedColumn::I64(values))
941 }
942 EncodingType::PcoU64 => {
943 let values = crate::pco::PcoDecoder::decode_u64(&block.payload)?;
944 Ok(DecodedColumn::U64(values))
945 }
946 EncodingType::Gorilla => {
947 let values = GorillaDecoder::decode(&block.payload)?;
948 Ok(DecodedColumn::F64(values))
949 }
950 EncodingType::IntegerI64 => {
951 let values = IntegerDecoder::decode_i64(&block.payload)?;
952 Ok(DecodedColumn::I64(values))
953 }
954 EncodingType::IntegerU64 => {
955 let values = IntegerDecoder::decode_u64(&block.payload)?;
956 Ok(DecodedColumn::U64(values))
957 }
958 EncodingType::Dictionary => {
959 let values = DictionaryDecoder::decode(&block.payload)?;
960 Ok(DecodedColumn::String(values))
961 }
962 EncodingType::Bitmap => {
963 let values = BitmapDecoder::decode(&block.payload)?;
964 Ok(DecodedColumn::Bool(values))
965 }
966 EncodingType::PlainF64 => {
967 let values = PlainDecoder::decode_f64(&block.payload)?;
968 Ok(DecodedColumn::F64(values))
969 }
970 EncodingType::PlainI64 => {
971 let values = PlainDecoder::decode_i64(&block.payload)?;
972 Ok(DecodedColumn::I64(values))
973 }
974 EncodingType::PlainU64 => {
975 let values = PlainDecoder::decode_u64(&block.payload)?;
976 Ok(DecodedColumn::U64(values))
977 }
978 EncodingType::PlainBool => {
979 let values = PlainDecoder::decode_bool(&block.payload)?;
980 Ok(DecodedColumn::Bool(values))
981 }
982 EncodingType::PlainString => {
983 let values = PlainDecoder::decode_string(&block.payload)?;
984 Ok(DecodedColumn::String(values))
985 }
986 EncodingType::Nullable => Self::decode_nullable(&block.payload),
987 EncodingType::Rle => Self::decode_rle(&block.payload),
988 EncodingType::VarintI64 => {
989 let values = VarintDecoder::decode_i64(&block.payload)?;
990 Ok(DecodedColumn::I64(values))
991 }
992 EncodingType::VarintU64 => {
993 let values = VarintDecoder::decode_u64(&block.payload)?;
994 Ok(DecodedColumn::U64(values))
995 }
996 EncodingType::ForI64 => {
997 let values = ForDecoder::decode_i64(&block.payload)?;
998 Ok(DecodedColumn::I64(values))
999 }
1000 EncodingType::ForU64 => {
1001 let values = ForDecoder::decode_u64(&block.payload)?;
1002 Ok(DecodedColumn::U64(values))
1003 }
1004 EncodingType::DecimalI128 => {
1005 let values = crate::decimal::DecimalDecoder::decode(&block.payload)?;
1006 Ok(DecodedColumn::Decimal(values))
1007 }
1008 }
1009 }
1010
1011 fn decode_nullable(payload: &[u8]) -> Result<DecodedColumn> {
1019 if payload.len() < 5 {
1020 return Err(EncodingError::CorruptData {
1021 detail: "nullable payload too short".to_string(),
1022 });
1023 }
1024
1025 let inner_enc = EncodingType::from_tag(payload[0])?;
1026 let total_count = crate::coding::checked_decode_count(
1030 u32::from_le_bytes([payload[1], payload[2], payload[3], payload[4]]) as usize,
1031 "nullable",
1032 )?;
1033 let bitmap_bytes = total_count.div_ceil(8);
1034 let header_len = 5 + bitmap_bytes;
1035
1036 if payload.len() < header_len {
1037 return Err(EncodingError::CorruptData {
1038 detail: "nullable bitmap truncated".to_string(),
1039 });
1040 }
1041
1042 let bitmap = &payload[5..header_len];
1043 let inner_payload = &payload[header_len..];
1044
1045 let validity: Vec<bool> = (0..total_count)
1047 .map(|i| bitmap[i / 8] & (1 << (i % 8)) != 0)
1048 .collect();
1049
1050 let non_null_count = validity.iter().filter(|v| **v).count();
1051
1052 if non_null_count == 0 {
1054 return match inner_enc {
1055 EncodingType::PlainF64
1056 | EncodingType::Chimp
1057 | EncodingType::Chimp128
1058 | EncodingType::Gorilla
1059 | EncodingType::Patas
1060 | EncodingType::Alp
1061 | EncodingType::Pco => Ok(DecodedColumn::NullableF64(vec![None; total_count])),
1062 EncodingType::PlainI64
1063 | EncodingType::IntegerI64
1064 | EncodingType::VarintI64
1065 | EncodingType::ForI64
1066 | EncodingType::PcoI64
1067 | EncodingType::DeltaOfDelta => {
1068 Ok(DecodedColumn::NullableI64(vec![None; total_count]))
1069 }
1070 EncodingType::PlainU64
1071 | EncodingType::IntegerU64
1072 | EncodingType::VarintU64
1073 | EncodingType::ForU64
1074 | EncodingType::PcoU64 => Ok(DecodedColumn::NullableU64(vec![None; total_count])),
1075 EncodingType::PlainString | EncodingType::Dictionary => {
1076 Ok(DecodedColumn::NullableString(vec![None; total_count]))
1077 }
1078 EncodingType::Bitmap | EncodingType::PlainBool => {
1079 Ok(DecodedColumn::NullableBool(vec![None; total_count]))
1080 }
1081 EncodingType::DecimalI128 => {
1082 Ok(DecodedColumn::NullableDecimal(vec![None; total_count]))
1083 }
1084 _ => Err(EncodingError::CorruptData {
1085 detail: format!("unexpected inner encoding in nullable: {inner_enc}"),
1086 }),
1087 };
1088 }
1089
1090 let inner_block = EncodedBlock {
1092 encoding: inner_enc,
1093 payload: inner_payload.to_vec(),
1094 };
1095 let decoded = Self::decode(&inner_block)?;
1096
1097 match decoded {
1099 DecodedColumn::F64(vals) => Ok(DecodedColumn::NullableF64(scatter(vals, &validity))),
1100 DecodedColumn::I64(vals) => Ok(DecodedColumn::NullableI64(scatter(vals, &validity))),
1101 DecodedColumn::U64(vals) => Ok(DecodedColumn::NullableU64(scatter(vals, &validity))),
1102 DecodedColumn::Bool(vals) => Ok(DecodedColumn::NullableBool(scatter(vals, &validity))),
1103 DecodedColumn::String(vals) => {
1104 Ok(DecodedColumn::NullableString(scatter(vals, &validity)))
1105 }
1106 DecodedColumn::Decimal(vals) => {
1107 Ok(DecodedColumn::NullableDecimal(scatter(vals, &validity)))
1108 }
1109 _ => Err(EncodingError::CorruptData {
1110 detail: "unexpected decoded type in nullable wrapper".to_string(),
1111 }),
1112 }
1113 }
1114
1115 fn decode_rle(payload: &[u8]) -> Result<DecodedColumn> {
1117 let vtype = RleDecoder::value_type(payload)?;
1118 match vtype {
1119 0 => Ok(DecodedColumn::I64(RleDecoder::decode_i64(payload)?)),
1120 1 => Ok(DecodedColumn::U64(RleDecoder::decode_u64(payload)?)),
1121 2 => Ok(DecodedColumn::F64(RleDecoder::decode_f64(payload)?)),
1122 3 => Ok(DecodedColumn::Bool(RleDecoder::decode_bool(payload)?)),
1123 4 => Ok(DecodedColumn::String(RleDecoder::decode_string(payload)?)),
1124 other => Err(EncodingError::CorruptData {
1125 detail: format!("unknown RLE value type: {other}"),
1126 }),
1127 }
1128 }
1129
1130 pub fn decode_bytes(data: &[u8]) -> Result<DecodedColumn> {
1137 let block = EncodedBlock::from_bytes(data)?;
1138 Self::decode(&block)
1139 }
1140}
1141
1142pub(crate) fn should_use_specialized(encoded: &[u8], raw_size: usize) -> bool {
1149 if raw_size == 0 {
1150 return false;
1151 }
1152 #[allow(clippy::cast_precision_loss)]
1153 let ratio = raw_size as f64 / encoded.len() as f64;
1154 ratio >= MIN_COMPRESSION_RATIO
1155}
1156
1157fn encode_validity_bitmap(validity: &[bool]) -> Vec<u8> {
1165 let byte_count = validity.len().div_ceil(8);
1166 let mut bitmap = vec![0u8; byte_count];
1167 for (i, &valid) in validity.iter().enumerate() {
1168 if valid {
1169 bitmap[i / 8] |= 1 << (i % 8);
1170 }
1171 }
1172 bitmap
1173}
1174
1175fn wrap_nullable(
1177 inner_enc: EncodingType,
1178 total_count: usize,
1179 validity: &[bool],
1180 inner_payload: &[u8],
1181) -> EncodedBlock {
1182 let bitmap = encode_validity_bitmap(validity);
1183 let mut payload = Vec::with_capacity(1 + 4 + bitmap.len() + inner_payload.len());
1184 payload.push(inner_enc.tag());
1185 payload.extend_from_slice(&(total_count as u32).to_le_bytes());
1186 payload.extend_from_slice(&bitmap);
1187 payload.extend_from_slice(inner_payload);
1188 EncodedBlock {
1189 encoding: EncodingType::Nullable,
1190 payload,
1191 }
1192}
1193
1194fn extract_non_null_f64(values: &[Option<f64>]) -> (Vec<f64>, Vec<bool>) {
1196 let validity: Vec<bool> = values.iter().map(std::option::Option::is_some).collect();
1197 let non_null: Vec<f64> = values.iter().filter_map(|v| *v).collect();
1198 (non_null, validity)
1199}
1200
1201fn extract_non_null_i64(values: &[Option<i64>]) -> (Vec<i64>, Vec<bool>) {
1203 let validity: Vec<bool> = values.iter().map(std::option::Option::is_some).collect();
1204 let non_null: Vec<i64> = values.iter().filter_map(|v| *v).collect();
1205 (non_null, validity)
1206}
1207
1208fn extract_non_null_u64(values: &[Option<u64>]) -> (Vec<u64>, Vec<bool>) {
1210 let validity: Vec<bool> = values.iter().map(std::option::Option::is_some).collect();
1211 let non_null: Vec<u64> = values.iter().filter_map(|v| *v).collect();
1212 (non_null, validity)
1213}
1214
1215fn scatter<T: Default + Clone>(values: Vec<T>, validity: &[bool]) -> Vec<Option<T>> {
1220 let mut result = Vec::with_capacity(validity.len());
1221 let mut val_iter = values.into_iter();
1222 for &valid in validity {
1223 if valid {
1224 result.push(val_iter.next().map(Some).unwrap_or(None));
1225 } else {
1226 result.push(None);
1227 }
1228 }
1229 result
1230}
1231
1232#[cfg(test)]
1237mod tests {
1238 use super::*;
1239
1240 #[test]
1243 fn encoding_type_tag_roundtrip() {
1244 let all = [
1245 EncodingType::DeltaOfDelta,
1246 EncodingType::Chimp,
1247 EncodingType::Gorilla,
1248 EncodingType::IntegerI64,
1249 EncodingType::IntegerU64,
1250 EncodingType::Dictionary,
1251 EncodingType::Bitmap,
1252 EncodingType::PlainF64,
1253 EncodingType::PlainI64,
1254 EncodingType::PlainU64,
1255 EncodingType::PlainBool,
1256 EncodingType::PlainString,
1257 EncodingType::Nullable,
1258 EncodingType::Rle,
1259 ];
1260 for enc in &all {
1261 let tag = enc.tag();
1262 let recovered = EncodingType::from_tag(tag).unwrap();
1263 assert_eq!(*enc, recovered);
1264 }
1265 }
1266
1267 #[test]
1268 fn encoding_type_unknown_tag() {
1269 assert!(EncodingType::from_tag(255).is_err());
1270 }
1271
1272 #[test]
1277 fn all_lists_every_variant_and_agrees_with_from_tag() {
1278 let scanned: Vec<EncodingType> = (0..=u8::MAX)
1279 .filter_map(|t| EncodingType::from_tag(t).ok())
1280 .collect();
1281 assert_eq!(EncodingType::ALL, scanned.as_slice());
1282 for enc in EncodingType::ALL {
1283 assert_eq!(EncodingType::from_tag(enc.tag()).unwrap(), *enc);
1284 }
1285 for (i, enc) in EncodingType::ALL.iter().enumerate() {
1288 assert_eq!(usize::from(enc.tag()), i, "{enc} has a non-dense tag");
1289 }
1290 }
1291
1292 #[test]
1295 fn encoded_block_roundtrip() {
1296 let block = EncodedBlock {
1297 encoding: EncodingType::Chimp,
1298 payload: vec![1, 2, 3, 4],
1299 };
1300 let bytes = block.to_bytes();
1301 assert_eq!(bytes[0], EncodingType::Chimp.tag());
1302 let recovered = EncodedBlock::from_bytes(&bytes).unwrap();
1303 assert_eq!(recovered.encoding, block.encoding);
1304 assert_eq!(recovered.payload, block.payload);
1305 }
1306
1307 #[test]
1308 fn encoded_block_empty_error() {
1309 assert!(EncodedBlock::from_bytes(&[]).is_err());
1310 }
1311
1312 #[test]
1315 fn encode_timestamps_monotonic() {
1316 let ts: Vec<i64> = (0..1000).map(|i| 1_000_000 + i * 10_000).collect();
1317 let block = ColumnEncoder::encode_timestamps(&ts).unwrap();
1318 assert_eq!(block.encoding, EncodingType::PcoI64);
1322 let decoded = ColumnDecoder::decode(&block).unwrap();
1323 assert_eq!(decoded, DecodedColumn::I64(ts));
1324 }
1325
1326 #[test]
1327 fn encode_timestamps_honours_an_explicit_codec() {
1328 let ts: Vec<i64> = (0..1000).map(|i| 1_000_000 + i * 10_000).collect();
1329 for (strategy, expected) in [
1330 (TimestampEncoding::Pco, EncodingType::PcoI64),
1331 (TimestampEncoding::DeltaOfDelta, EncodingType::DeltaOfDelta),
1332 (TimestampEncoding::Plain, EncodingType::PlainI64),
1333 (TimestampEncoding::Auto, EncodingType::PcoI64),
1334 ] {
1335 let block = ColumnEncoder::encode_timestamps_with(&ts, strategy).unwrap();
1336 assert_eq!(block.encoding, expected, "{strategy:?}");
1337 assert_eq!(
1338 ColumnDecoder::decode(&block).unwrap(),
1339 DecodedColumn::I64(ts.clone())
1340 );
1341 }
1342 }
1343
1344 #[test]
1346 fn auto_timestamps_never_loses_to_delta_of_delta() {
1347 let mut seed = 7u64;
1350 let ts: Vec<i64> = (0..2000)
1351 .map(|_| {
1352 seed = seed.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1);
1353 (seed >> 20) as i64
1354 })
1355 .collect();
1356 let auto = ColumnEncoder::encode_timestamps_with(&ts, TimestampEncoding::Auto).unwrap();
1357 let dod =
1358 ColumnEncoder::encode_timestamps_with(&ts, TimestampEncoding::DeltaOfDelta).unwrap();
1359 let pco = ColumnEncoder::encode_timestamps_with(&ts, TimestampEncoding::Pco).unwrap();
1360 assert!(auto.payload.len() <= dod.payload.len().min(pco.payload.len()));
1361 assert_eq!(
1362 ColumnDecoder::decode(&auto).unwrap(),
1363 DecodedColumn::I64(ts)
1364 );
1365 }
1366
1367 #[test]
1368 fn encode_timestamps_bytes_roundtrip() {
1369 let ts: Vec<i64> = (0..100).map(|i| 1_000_000 + i * 10_000).collect();
1370 let block = ColumnEncoder::encode_timestamps(&ts).unwrap();
1371 let raw = block.to_bytes();
1372 let decoded = ColumnDecoder::decode_bytes(&raw).unwrap();
1373 assert_eq!(decoded, DecodedColumn::I64(ts));
1374 }
1375
1376 #[test]
1379 fn encode_f64_adaptive_strategy() {
1380 let values: Vec<f64> = (0..500).map(|i| 20.0 + (i as f64) * 0.01).collect();
1383 let block = ColumnEncoder::encode_f64(&values, FloatEncoding::Chimp).unwrap();
1384 assert_eq!(block.encoding, EncodingType::Pco);
1385 let decoded = ColumnDecoder::decode(&block).unwrap();
1386 assert_eq!(decoded, DecodedColumn::F64(values));
1387 }
1388
1389 #[test]
1390 fn encode_f64_gorilla_strategy() {
1391 let values: Vec<f64> = (0..500).map(|i| 20.0 + (i as f64) * 0.01).collect();
1392 let block = ColumnEncoder::encode_f64(&values, FloatEncoding::Gorilla).unwrap();
1393 assert!(
1394 block.encoding == EncodingType::Gorilla || block.encoding == EncodingType::PlainF64
1395 );
1396 let decoded = ColumnDecoder::decode(&block).unwrap();
1397 assert_eq!(decoded, DecodedColumn::F64(values));
1398 }
1399
1400 #[test]
1401 fn encode_f64_plain_strategy() {
1402 let values = vec![1.0, 2.0, 3.0];
1403 let block = ColumnEncoder::encode_f64(&values, FloatEncoding::Plain).unwrap();
1404 assert_eq!(block.encoding, EncodingType::PlainF64);
1405 let decoded = ColumnDecoder::decode(&block).unwrap();
1406 assert_eq!(decoded, DecodedColumn::F64(values));
1407 }
1408
1409 #[test]
1412 fn encode_i64_with_fallback() {
1413 let values: Vec<i64> = (0..1000).collect();
1414 let block = ColumnEncoder::encode_i64(&values).unwrap();
1415 assert!(
1416 block.encoding == EncodingType::PcoI64
1417 || block.encoding == EncodingType::IntegerI64
1418 || block.encoding == EncodingType::PlainI64
1419 );
1420 let decoded = ColumnDecoder::decode(&block).unwrap();
1421 assert_eq!(decoded, DecodedColumn::I64(values));
1422 }
1423
1424 #[test]
1425 fn encode_u64_with_fallback() {
1426 let values: Vec<u64> = (0..1000).collect();
1427 let block = ColumnEncoder::encode_u64(&values).unwrap();
1428 assert!(
1429 block.encoding == EncodingType::PcoU64
1430 || block.encoding == EncodingType::IntegerU64
1431 || block.encoding == EncodingType::PlainU64
1432 );
1433 let decoded = ColumnDecoder::decode(&block).unwrap();
1434 assert_eq!(decoded, DecodedColumn::U64(values));
1435 }
1436
1437 #[test]
1440 fn encode_string_low_cardinality() {
1441 let raw = vec!["us-east-1"; 500];
1442 let block = ColumnEncoder::encode_string(&raw).unwrap();
1443 assert_eq!(block.encoding, EncodingType::Dictionary);
1444 let decoded = ColumnDecoder::decode(&block).unwrap();
1445 let expected: Vec<String> = raw.iter().map(|s| (*s).to_owned()).collect();
1446 assert_eq!(decoded, DecodedColumn::String(expected));
1447 }
1448
1449 #[test]
1450 fn encode_string_high_cardinality_fallback() {
1451 let raw: Vec<String> = (0..200).map(|i| format!("uuid-{i}")).collect();
1453 let refs: Vec<&str> = raw.iter().map(String::as_str).collect();
1454 let block = ColumnEncoder::encode_string(&refs).unwrap();
1455 let decoded = ColumnDecoder::decode(&block).unwrap();
1456 assert_eq!(decoded, DecodedColumn::String(raw));
1457 }
1458
1459 #[test]
1462 fn encode_bool_bitmap() {
1463 let values: Vec<bool> = (0..100).map(|i| i % 3 == 0).collect();
1464 let block = ColumnEncoder::encode_bool(&values).unwrap();
1465 assert_eq!(block.encoding, EncodingType::Bitmap);
1466 let decoded = ColumnDecoder::decode(&block).unwrap();
1467 assert_eq!(decoded, DecodedColumn::Bool(values));
1468 }
1469
1470 #[test]
1473 fn empty_inputs_error() {
1474 assert!(ColumnEncoder::encode_timestamps(&[]).is_err());
1475 assert!(ColumnEncoder::encode_f64(&[], FloatEncoding::Chimp).is_err());
1476 assert!(ColumnEncoder::encode_i64(&[]).is_err());
1477 assert!(ColumnEncoder::encode_u64(&[]).is_err());
1478 let empty: &[&str] = &[];
1479 assert!(ColumnEncoder::encode_string(empty).is_err());
1480 assert!(ColumnEncoder::encode_bool(&[]).is_err());
1481 }
1482
1483 #[test]
1486 fn should_use_specialized_logic() {
1487 assert!(should_use_specialized(&[0u8; 50], 100));
1489 assert!(!should_use_specialized(&[0u8; 80], 100));
1491 assert!(!should_use_specialized(&[0u8; 10], 0));
1493 }
1494
1495 #[test]
1498 fn nullable_f64_mixed() {
1499 let values = vec![Some(1.0), None, Some(3.0), None, Some(5.0)];
1500 let block = ColumnEncoder::encode_f64_nullable(&values, FloatEncoding::Plain).unwrap();
1501 assert_eq!(block.encoding, EncodingType::Nullable);
1502 let decoded = ColumnDecoder::decode(&block).unwrap();
1503 assert_eq!(decoded, DecodedColumn::NullableF64(values));
1504 }
1505
1506 #[test]
1507 fn nullable_f64_all_present() {
1508 let values = vec![Some(1.0), Some(2.0), Some(3.0)];
1509 let block = ColumnEncoder::encode_f64_nullable(&values, FloatEncoding::Plain).unwrap();
1510 let decoded = ColumnDecoder::decode(&block).unwrap();
1511 assert_eq!(decoded, DecodedColumn::NullableF64(values));
1512 }
1513
1514 #[test]
1515 fn nullable_f64_all_null() {
1516 let values: Vec<Option<f64>> = vec![None, None, None];
1517 let block = ColumnEncoder::encode_f64_nullable(&values, FloatEncoding::Plain).unwrap();
1518 let decoded = ColumnDecoder::decode(&block).unwrap();
1519 assert_eq!(decoded, DecodedColumn::NullableF64(values));
1520 }
1521
1522 #[test]
1523 fn nullable_i64_mixed() {
1524 let values = vec![Some(10), None, Some(30), Some(40), None];
1525 let block = ColumnEncoder::encode_i64_nullable(&values).unwrap();
1526 assert_eq!(block.encoding, EncodingType::Nullable);
1527 let decoded = ColumnDecoder::decode(&block).unwrap();
1528 assert_eq!(decoded, DecodedColumn::NullableI64(values));
1529 }
1530
1531 #[test]
1532 fn nullable_u64_mixed() {
1533 let values = vec![None, Some(100u64), Some(200), None];
1534 let block = ColumnEncoder::encode_u64_nullable(&values).unwrap();
1535 let decoded = ColumnDecoder::decode(&block).unwrap();
1536 assert_eq!(decoded, DecodedColumn::NullableU64(values));
1537 }
1538
1539 #[test]
1540 fn nullable_string_mixed() {
1541 let values = vec![Some("hello"), None, Some("world"), None];
1542 let block = ColumnEncoder::encode_string_nullable(&values).unwrap();
1543 let decoded = ColumnDecoder::decode(&block).unwrap();
1544 let expected: Vec<Option<String>> =
1545 values.iter().map(|v| v.map(ToString::to_string)).collect();
1546 assert_eq!(decoded, DecodedColumn::NullableString(expected));
1547 }
1548
1549 #[test]
1550 fn nullable_f64_bytes_roundtrip() {
1551 let values = vec![Some(1.0), None, Some(3.0)];
1552 let block = ColumnEncoder::encode_f64_nullable(&values, FloatEncoding::Plain).unwrap();
1553 let raw = block.to_bytes();
1554 let decoded = ColumnDecoder::decode_bytes(&raw).unwrap();
1555 assert_eq!(decoded, DecodedColumn::NullableF64(values));
1556 }
1557
1558 #[test]
1559 fn nullable_empty_errors() {
1560 let empty_f64: &[Option<f64>] = &[];
1561 assert!(ColumnEncoder::encode_f64_nullable(empty_f64, FloatEncoding::Plain).is_err());
1562 let empty_i64: &[Option<i64>] = &[];
1563 assert!(ColumnEncoder::encode_i64_nullable(empty_i64).is_err());
1564 let empty_u64: &[Option<u64>] = &[];
1565 assert!(ColumnEncoder::encode_u64_nullable(empty_u64).is_err());
1566 let empty_str: &[Option<&str>] = &[];
1567 assert!(ColumnEncoder::encode_string_nullable(empty_str).is_err());
1568 let empty_bool: &[Option<bool>] = &[];
1569 assert!(ColumnEncoder::encode_bool_nullable(empty_bool).is_err());
1570 }
1571
1572 #[test]
1573 fn nullable_distinguishes_zero_from_null() {
1574 let values = vec![Some(0.0), None, Some(0.0), None];
1577 let block = ColumnEncoder::encode_f64_nullable(&values, FloatEncoding::Plain).unwrap();
1578 let decoded = ColumnDecoder::decode(&block).unwrap();
1579 match &decoded {
1580 DecodedColumn::NullableF64(vals) => {
1581 assert_eq!(vals[0], Some(0.0));
1582 assert_eq!(vals[1], None); assert_eq!(vals[2], Some(0.0));
1584 assert_eq!(vals[3], None); }
1586 other => panic!("Expected NullableF64, got {other:?}"),
1587 }
1588 }
1589
1590 #[test]
1591 fn nullable_i64_distinguishes_zero_from_null() {
1592 let values = vec![Some(0i64), None, Some(0), None, Some(42)];
1593 let block = ColumnEncoder::encode_i64_nullable(&values).unwrap();
1594 let decoded = ColumnDecoder::decode(&block).unwrap();
1595 match &decoded {
1596 DecodedColumn::NullableI64(vals) => {
1597 assert_eq!(vals[0], Some(0));
1598 assert_eq!(vals[1], None);
1599 assert_eq!(vals[2], Some(0));
1600 assert_eq!(vals[3], None);
1601 assert_eq!(vals[4], Some(42));
1602 }
1603 other => panic!("Expected NullableI64, got {other:?}"),
1604 }
1605 }
1606}
1607
1608#[cfg(test)]
1609mod proptests {
1610 use super::*;
1611 use proptest::prelude::*;
1612
1613 proptest! {
1614 #[test]
1615 fn roundtrip_timestamps(mut values in proptest::collection::vec(any::<i64>(), 1..300)) {
1616 values.sort_unstable();
1617 let block = ColumnEncoder::encode_timestamps(&values).unwrap();
1618 let decoded = ColumnDecoder::decode(&block).unwrap();
1619 prop_assert_eq!(DecodedColumn::I64(values), decoded);
1620 }
1621
1622 #[test]
1623 fn roundtrip_i64(values in proptest::collection::vec(any::<i64>(), 1..300)) {
1624 let block = ColumnEncoder::encode_i64(&values).unwrap();
1625 let decoded = ColumnDecoder::decode(&block).unwrap();
1626 prop_assert_eq!(DecodedColumn::I64(values), decoded);
1627 }
1628
1629 #[test]
1630 fn roundtrip_u64(values in proptest::collection::vec(any::<u64>(), 1..300)) {
1631 let block = ColumnEncoder::encode_u64(&values).unwrap();
1632 let decoded = ColumnDecoder::decode(&block).unwrap();
1633 prop_assert_eq!(DecodedColumn::U64(values), decoded);
1634 }
1635
1636 #[test]
1637 fn roundtrip_f64(values in proptest::collection::vec(any::<f64>(), 1..300)) {
1638 let block = ColumnEncoder::encode_f64(&values, FloatEncoding::Gorilla).unwrap();
1639 let decoded = ColumnDecoder::decode(&block).unwrap();
1640 prop_assert_eq!(DecodedColumn::F64(values), decoded);
1641 }
1642
1643 #[test]
1644 fn roundtrip_bool(values in proptest::collection::vec(any::<bool>(), 1..300)) {
1645 let block = ColumnEncoder::encode_bool(&values).unwrap();
1646 let decoded = ColumnDecoder::decode(&block).unwrap();
1647 prop_assert_eq!(DecodedColumn::Bool(values), decoded);
1648 }
1649
1650 #[test]
1651 fn roundtrip_string(values in proptest::collection::vec("[a-z]{0,8}", 1..100)) {
1652 let refs: Vec<&str> = values.iter().map(String::as_str).collect();
1653 let block = ColumnEncoder::encode_string(&refs).unwrap();
1654 let decoded = ColumnDecoder::decode(&block).unwrap();
1655 prop_assert_eq!(DecodedColumn::String(values), decoded);
1656 }
1657
1658 #[test]
1659 fn roundtrip_nullable_i64(values in proptest::collection::vec(any::<Option<i64>>(), 1..300)) {
1660 let block = ColumnEncoder::encode_i64_nullable(&values).unwrap();
1661 let decoded = ColumnDecoder::decode(&block).unwrap();
1662 prop_assert_eq!(DecodedColumn::NullableI64(values), decoded);
1663 }
1664
1665 #[test]
1666 fn roundtrip_nullable_u64(values in proptest::collection::vec(any::<Option<u64>>(), 1..300)) {
1667 let block = ColumnEncoder::encode_u64_nullable(&values).unwrap();
1668 let decoded = ColumnDecoder::decode(&block).unwrap();
1669 prop_assert_eq!(DecodedColumn::NullableU64(values), decoded);
1670 }
1671
1672 #[test]
1673 fn roundtrip_nullable_f64(values in proptest::collection::vec(any::<Option<f64>>(), 1..300)) {
1674 let block = ColumnEncoder::encode_f64_nullable(&values, FloatEncoding::Gorilla).unwrap();
1675 let decoded = ColumnDecoder::decode(&block).unwrap();
1676 prop_assert_eq!(DecodedColumn::NullableF64(values), decoded);
1677 }
1678
1679 #[test]
1680 fn roundtrip_nullable_bool(values in proptest::collection::vec(any::<Option<bool>>(), 1..300)) {
1681 let block = ColumnEncoder::encode_bool_nullable(&values).unwrap();
1682 let decoded = ColumnDecoder::decode(&block).unwrap();
1683 prop_assert_eq!(DecodedColumn::NullableBool(values), decoded);
1684 }
1685
1686 #[test]
1687 fn roundtrip_nullable_string(values in proptest::collection::vec(
1688 proptest::option::of("[a-z]{0,8}"), 1..100
1689 )) {
1690 let refs: Vec<Option<&str>> = values.iter().map(|o| o.as_deref()).collect();
1691 let block = ColumnEncoder::encode_string_nullable(&refs).unwrap();
1692 let decoded = ColumnDecoder::decode(&block).unwrap();
1693 let expected: Vec<Option<String>> = values.clone();
1694 prop_assert_eq!(DecodedColumn::NullableString(expected), decoded);
1695 }
1696 }
1697}