Skip to main content

vortex_fastlanes/delta/compute/
cast.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use vortex_array::ArrayRef;
5use vortex_array::IntoArray;
6use vortex_array::builtins::ArrayBuiltins;
7use vortex_array::dtype::DType;
8use vortex_array::dtype::Nullability::NonNullable;
9use vortex_array::scalar_fn::fns::cast::CastReduce;
10use vortex_error::VortexResult;
11use vortex_error::vortex_panic;
12
13use crate::delta::DeltaArray;
14use crate::delta::DeltaVTable;
15
16impl CastReduce for DeltaVTable {
17    fn cast(array: &DeltaArray, dtype: &DType) -> VortexResult<Option<ArrayRef>> {
18        // Delta encoding stores differences between consecutive values, which requires
19        // unsigned integers to avoid overflow issues. Signed integers could produce
20        // negative deltas that wouldn't fit in the unsigned delta representation.
21        // This encoding is optimized for monotonically increasing sequences.
22        let DType::Primitive(target_ptype, _) = dtype else {
23            return Ok(None);
24        };
25
26        let DType::Primitive(source_ptype, _) = array.dtype() else {
27            vortex_panic!("delta should be primitive typed");
28        };
29
30        // TODO(DK): narrows can be safe but we must decompress to compute the maximum value.
31        if target_ptype.is_signed_int() || source_ptype.bit_width() > target_ptype.bit_width() {
32            return Ok(None);
33        }
34
35        // Cast both bases and deltas to the target type
36        let casted_bases = array.bases().cast(dtype.with_nullability(NonNullable))?;
37        let casted_deltas = array.deltas().cast(dtype.clone())?;
38
39        // Create a new DeltaArray with the casted components
40        Ok(Some(
41            DeltaArray::try_from_delta_compress_parts(casted_bases, casted_deltas)?.into_array(),
42        ))
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use rstest::rstest;
49    use vortex_array::arrays::PrimitiveArray;
50    use vortex_array::assert_arrays_eq;
51    use vortex_array::builtins::ArrayBuiltins;
52    use vortex_array::compute::conformance::cast::test_cast_conformance;
53    use vortex_array::dtype::DType;
54    use vortex_array::dtype::Nullability;
55    use vortex_array::dtype::PType;
56    use vortex_buffer::Buffer;
57
58    use crate::delta::DeltaArray;
59
60    #[test]
61    fn test_cast_delta_u8_to_u32() {
62        let primitive = PrimitiveArray::new(
63            Buffer::copy_from(vec![10u8, 20, 30, 40, 50]),
64            vortex_array::validity::Validity::NonNullable,
65        );
66        let array = DeltaArray::try_from_primitive_array(&primitive).unwrap();
67
68        let casted = array
69            .to_array()
70            .cast(DType::Primitive(PType::U32, Nullability::NonNullable))
71            .unwrap();
72        assert_eq!(
73            casted.dtype(),
74            &DType::Primitive(PType::U32, Nullability::NonNullable)
75        );
76
77        // Verify by decoding
78        assert_arrays_eq!(casted, PrimitiveArray::from_iter([10u32, 20, 30, 40, 50]));
79    }
80
81    #[test]
82    fn test_cast_delta_nullable() {
83        // DeltaArray doesn't support nullable arrays - the validity is handled at the DeltaArray level
84        // Create a non-nullable array and then add validity to the DeltaArray
85        let values = PrimitiveArray::new(
86            Buffer::copy_from(vec![100u16, 0, 200, 300, 0]),
87            vortex_array::validity::Validity::NonNullable,
88        );
89        let array = DeltaArray::try_from_primitive_array(&values).unwrap();
90
91        let casted = array
92            .to_array()
93            .cast(DType::Primitive(PType::U32, Nullability::Nullable))
94            .unwrap();
95        assert_eq!(
96            casted.dtype(),
97            &DType::Primitive(PType::U32, Nullability::Nullable)
98        );
99    }
100
101    #[rstest]
102    #[case::u8(
103        PrimitiveArray::new(
104            Buffer::copy_from(vec![0u8, 10, 20, 30, 40, 50]),
105            vortex_array::validity::Validity::NonNullable,
106        )
107    )]
108    #[case::u16(
109        PrimitiveArray::new(
110            Buffer::copy_from(vec![0u16, 100, 200, 300, 400, 500]),
111            vortex_array::validity::Validity::NonNullable,
112        )
113    )]
114    #[case::u32(
115        PrimitiveArray::new(
116            Buffer::copy_from(vec![0u32, 1000, 2000, 3000, 4000]),
117            vortex_array::validity::Validity::NonNullable,
118        )
119    )]
120    #[case::u64(
121        PrimitiveArray::new(
122            Buffer::copy_from(vec![0u64, 10000, 20000, 30000]),
123            vortex_array::validity::Validity::NonNullable,
124        )
125    )]
126    fn test_cast_delta_conformance(#[case] primitive: PrimitiveArray) {
127        let delta_array = DeltaArray::try_from_primitive_array(&primitive).unwrap();
128        test_cast_conformance(delta_array.as_ref());
129    }
130}