use crate::cast::*;
pub(crate) fn cast_numeric_arrays<FROM, TO>(
from: &dyn Array,
cast_options: &CastOptions,
) -> Result<ArrayRef, ArrowError>
where
FROM: ArrowPrimitiveType,
TO: ArrowPrimitiveType,
FROM::Native: NumCast + NumericNative,
TO::Native: NumCast + NumericNative,
{
let from = from.as_primitive::<FROM>();
let array: PrimitiveArray<TO> = if const {
is_infallible_numeric_cast(
<FROM::Native as NumericNative>::KIND,
<TO::Native as NumericNative>::KIND,
)
} {
from.unary(|v| num_cast(v).expect("numeric cast is infallible"))
} else if cast_options.safe {
from.unary_opt(num_cast)
} else {
from.try_unary(|v| {
num_cast(v).ok_or_else(|| {
ArrowError::CastError(format!("Can't cast value {v:?} to type {}", TO::DATA_TYPE))
})
})?
};
Ok(Arc::new(array))
}
#[derive(Clone, Copy)]
pub(crate) struct NumericKind {
float: bool,
signed: bool,
bits: u32,
}
pub(crate) trait NumericNative {
const KIND: NumericKind;
}
macro_rules! numeric_native {
($($t:ty => $float:literal, $signed:literal;)*) => {$(
impl NumericNative for $t {
const KIND: NumericKind = NumericKind {
float: $float,
signed: $signed,
bits: 8 * std::mem::size_of::<$t>() as u32,
};
}
)*};
}
numeric_native! {
u8 => false, false;
u16 => false, false;
u32 => false, false;
u64 => false, false;
i8 => false, true;
i16 => false, true;
i32 => false, true;
i64 => false, true;
half::f16 => true, true;
f32 => true, true;
f64 => true, true;
}
pub(crate) const fn is_infallible_numeric_cast(from: NumericKind, to: NumericKind) -> bool {
if to.float {
true
} else if from.float {
false
} else if from.signed == to.signed {
to.bits >= from.bits
} else {
!from.signed && to.bits > from.bits
}
}
#[inline]
pub fn num_cast<I, O>(value: I) -> Option<O>
where
I: NumCast,
O: NumCast,
{
num_traits::cast::cast::<I, O>(value)
}
pub(crate) fn cast_numeric_to_bool<FROM>(from: &dyn Array) -> Result<ArrayRef, ArrowError>
where
FROM: ArrowPrimitiveType,
{
Ok(Arc::new(BooleanArray::from_unary(
from.as_primitive::<FROM>(),
cast_num_to_bool,
)))
}
#[inline]
pub fn cast_num_to_bool<I>(value: I) -> bool
where
I: Default + PartialEq,
{
value != I::default()
}
pub(crate) fn cast_bool_to_numeric<TO>(
from: &dyn Array,
cast_options: &CastOptions,
) -> Result<ArrayRef, ArrowError>
where
TO: ArrowPrimitiveType,
{
Ok(Arc::new(bool_to_numeric_cast::<TO>(
from.as_any().downcast_ref::<BooleanArray>().unwrap(),
cast_options,
)))
}
fn bool_to_numeric_cast<T>(from: &BooleanArray, _cast_options: &CastOptions) -> PrimitiveArray<T>
where
T: ArrowPrimitiveType,
{
let to_numeric = |bits: u64, i: usize| {
if bits & (1 << i) != 0 {
T::Native::ONE
} else {
T::Native::ZERO
}
};
let chunks = from.values().bit_chunks();
let mut values = Vec::with_capacity(from.len());
for bits in &chunks {
values.extend((0..64).map(|i| to_numeric(bits, i)));
}
let bits = chunks.remainder_bits();
values.extend((0..chunks.remainder_len()).map(|i| to_numeric(bits, i)));
PrimitiveArray::new(values.into(), from.nulls().cloned())
}
#[inline]
pub fn single_bool_to_numeric<O>(value: bool) -> Option<O>
where
O: num_traits::NumCast + Default,
{
if value {
num_traits::cast::cast(1)
} else {
Some(O::default())
}
}