1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
//! Defines generics suitable to perform operations to [`PrimitiveArray`] in-place.
use super::utils::check_same_len;
use crate::{array::PrimitiveArray, types::NativeType};
use either::Either;
/// Applies an unary function to a [`PrimitiveArray`], optionally in-place.
///
/// # Implementation
/// This function tries to apply the function directly to the values of the array.
/// If that region is shared, this function creates a new region and writes to it.
///
/// # Panics
/// This function panics iff
/// * the arrays have a different length.
/// * the function itself panics.
#[inline]
pub fn unary<I, F>(array: &mut PrimitiveArray<I>, op: F)
where
I: NativeType,
F: Fn(I) -> I,
{
if let Some(values) = array.get_mut_values() {
// mutate in place
values.iter_mut().for_each(|l| *l = op(*l));
} else {
// alloc and write to new region
let values = array.values().iter().map(|l| op(*l)).collect::<Vec<_>>();
array.set_values(values.into());
}
}
/// Applies a binary function to two [`PrimitiveArray`]s, optionally in-place, returning
/// a new [`PrimitiveArray`].
///
/// # Implementation
/// This function tries to apply the function directly to the values of the array.
/// If that region is shared, this function creates a new region and writes to it.
/// # Panics
/// This function panics iff
/// * the arrays have a different length.
/// * the function itself panics.
#[inline]
pub fn binary<T, D, F>(lhs: &mut PrimitiveArray<T>, rhs: &PrimitiveArray<D>, op: F)
where
T: NativeType,
D: NativeType,
F: Fn(T, D) -> T,
{
check_same_len(lhs, rhs).unwrap();
// both for the validity and for the values
// we branch to check if we can mutate in place
// if we can, great that is fastest.
// if we cannot, we allocate a new buffer and assign values to that
// new buffer, that is benchmarked to be ~2x faster than first memcpy and assign in place
// for the validity bits it can be much faster as we might need to iterate all bits if the
// bitmap has an offset.
if let Some(rhs) = rhs.validity() {
if lhs.validity().is_none() {
lhs.set_validity(Some(rhs.clone()));
} else {
lhs.apply_validity(|bitmap| {
match bitmap.into_mut() {
Either::Left(immutable) => {
// alloc new region
&immutable & rhs
}
Either::Right(mutable) => {
// mutate in place
(mutable & rhs).into()
}
}
});
}
};
if let Some(values) = lhs.get_mut_values() {
// mutate values in place
values
.iter_mut()
.zip(rhs.values().iter())
.for_each(|(l, r)| *l = op(*l, *r));
} else {
// alloc new region
let values = lhs
.values()
.iter()
.zip(rhs.values().iter())
.map(|(l, r)| op(*l, *r))
.collect::<Vec<_>>();
lhs.set_values(values.into());
}
}