vortex-array 0.54.0

Vortex in memory columnar data format
Documentation
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright the Vortex contributors

use std::ops::Range;

use vortex_buffer::Buffer;
use vortex_dtype::DecimalDType;
use vortex_scalar::{DecimalValue, NativeDecimalType, Scalar, match_each_decimal_value_type};

use crate::arrays::{DecimalArray, DecimalVTable};
use crate::validity::Validity;
use crate::vtable::OperationsVTable;
use crate::{ArrayRef, IntoArray};

impl OperationsVTable<DecimalVTable> for DecimalVTable {
    fn slice(array: &DecimalArray, range: Range<usize>) -> ArrayRef {
        match_each_decimal_value_type!(array.values_type(), |D| {
            slice_typed(
                array.buffer::<D>(),
                range,
                array.decimal_dtype(),
                array.validity.clone(),
            )
        })
    }

    fn scalar_at(array: &DecimalArray, index: usize) -> Scalar {
        match_each_decimal_value_type!(array.values_type(), |D| {
            Scalar::decimal(
                DecimalValue::from(array.buffer::<D>()[index]),
                array.decimal_dtype(),
                array.dtype().nullability(),
            )
        })
    }
}

fn slice_typed<T: NativeDecimalType>(
    values: Buffer<T>,
    range: Range<usize>,
    decimal_dtype: DecimalDType,
    validity: Validity,
) -> ArrayRef {
    let sliced = values.slice(range.clone());
    let validity = validity.slice(range);
    // SAFETY: Slicing preserves all DecimalArray invariants:
    // - Buffer is correctly typed and sized from the slice operation.
    // - Decimal dtype is preserved from the parent array.
    // - Validity is correctly sliced to match the new length.
    unsafe { DecimalArray::new_unchecked(sliced, decimal_dtype, validity) }.into_array()
}

#[cfg(test)]
mod tests {
    use vortex_buffer::buffer;
    use vortex_dtype::{DecimalDType, Nullability};
    use vortex_scalar::{DecimalValue, Scalar};

    use crate::Array;
    use crate::arrays::{DecimalArray, DecimalVTable};
    use crate::validity::Validity;

    #[test]
    fn test_slice() {
        let array = DecimalArray::new(
            buffer![100i128, 200i128, 300i128, 4000i128],
            DecimalDType::new(3, 2),
            Validity::NonNullable,
        )
        .to_array();

        let sliced = array.slice(1..3);
        assert_eq!(sliced.len(), 2);

        let decimal = sliced.as_::<DecimalVTable>();
        assert_eq!(decimal.buffer::<i128>(), buffer![200i128, 300i128]);
    }

    #[test]
    fn test_slice_nullable() {
        let array = DecimalArray::new(
            buffer![100i128, 200i128, 300i128, 4000i128],
            DecimalDType::new(3, 2),
            Validity::from_iter([false, true, false, true]),
        )
        .to_array();

        let sliced = array.slice(1..3);
        assert_eq!(sliced.len(), 2);
    }

    #[test]
    fn test_scalar_at() {
        let array = DecimalArray::new(
            buffer![100i128],
            DecimalDType::new(3, 2),
            Validity::NonNullable,
        );

        assert_eq!(
            array.scalar_at(0),
            Scalar::decimal(
                DecimalValue::I128(100),
                DecimalDType::new(3, 2),
                Nullability::NonNullable
            )
        );
    }
}