use std::any::Any;
use std::mem::ManuallyDrop;
use std::ops::Deref;
use std::sync::Arc;
use ndarray::ArrayD;
use super::dtype::DType;
use crate::types::{F, I, U};
pub struct ColumnHolder<T> {
array: ManuallyDrop<ArrayD<T>>,
foreign_keeper: Option<Box<dyn Any + Send + Sync>>,
}
impl<T> ColumnHolder<T> {
pub fn from_owned(arr: ArrayD<T>) -> Self {
Self {
array: ManuallyDrop::new(arr),
foreign_keeper: None,
}
}
pub unsafe fn from_foreign<K: Any + Send + Sync>(arr: ArrayD<T>, keeper: K) -> Self {
Self {
array: ManuallyDrop::new(arr),
foreign_keeper: Some(Box::new(keeper)),
}
}
pub fn is_foreign(&self) -> bool {
self.foreign_keeper.is_some()
}
#[inline]
pub fn array(&self) -> &ArrayD<T> {
&self.array
}
}
impl<T> Deref for ColumnHolder<T> {
type Target = ArrayD<T>;
#[inline]
fn deref(&self) -> &ArrayD<T> {
&self.array
}
}
impl<T: Clone> Clone for ColumnHolder<T> {
fn clone(&self) -> Self {
Self::from_owned(ArrayD::clone(&self.array))
}
}
impl<T> Drop for ColumnHolder<T> {
fn drop(&mut self) {
if let Some(keeper) = self.foreign_keeper.take() {
drop(keeper);
} else {
unsafe { ManuallyDrop::drop(&mut self.array) }
}
}
}
impl<T: std::fmt::Debug> std::fmt::Debug for ColumnHolder<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ColumnHolder")
.field("shape", &self.array.shape())
.field("foreign", &self.is_foreign())
.finish()
}
}
#[derive(Clone)]
pub enum Column {
Float(Arc<ColumnHolder<F>>),
Int(Arc<ColumnHolder<I>>),
Bool(Arc<ColumnHolder<bool>>),
UInt(Arc<ColumnHolder<U>>),
U8(Arc<ColumnHolder<u8>>),
String(Arc<ColumnHolder<String>>),
}
fn realize_owned_mut<T: Clone>(arc: &mut Arc<ColumnHolder<T>>) -> &mut ArrayD<T> {
if arc.is_foreign() {
*arc = Arc::new((**arc).clone());
}
let holder = Arc::make_mut(arc);
&mut holder.array
}
impl Column {
pub fn from_float(arr: ArrayD<F>) -> Self {
Column::Float(Arc::new(ColumnHolder::from_owned(arr)))
}
pub fn from_int(arr: ArrayD<I>) -> Self {
Column::Int(Arc::new(ColumnHolder::from_owned(arr)))
}
pub fn from_bool(arr: ArrayD<bool>) -> Self {
Column::Bool(Arc::new(ColumnHolder::from_owned(arr)))
}
pub fn from_uint(arr: ArrayD<U>) -> Self {
Column::UInt(Arc::new(ColumnHolder::from_owned(arr)))
}
pub fn from_u8(arr: ArrayD<u8>) -> Self {
Column::U8(Arc::new(ColumnHolder::from_owned(arr)))
}
pub fn from_string(arr: ArrayD<String>) -> Self {
Column::String(Arc::new(ColumnHolder::from_owned(arr)))
}
pub fn from_float_holder(holder: ColumnHolder<F>) -> Self {
Column::Float(Arc::new(holder))
}
pub fn from_int_holder(holder: ColumnHolder<I>) -> Self {
Column::Int(Arc::new(holder))
}
pub fn from_bool_holder(holder: ColumnHolder<bool>) -> Self {
Column::Bool(Arc::new(holder))
}
pub fn from_uint_holder(holder: ColumnHolder<U>) -> Self {
Column::UInt(Arc::new(holder))
}
pub fn from_u8_holder(holder: ColumnHolder<u8>) -> Self {
Column::U8(Arc::new(holder))
}
pub fn from_string_holder(holder: ColumnHolder<String>) -> Self {
Column::String(Arc::new(holder))
}
pub fn nrows(&self) -> Option<usize> {
match self {
Column::Float(a) => a.shape().first().copied(),
Column::Int(a) => a.shape().first().copied(),
Column::Bool(a) => a.shape().first().copied(),
Column::UInt(a) => a.shape().first().copied(),
Column::U8(a) => a.shape().first().copied(),
Column::String(a) => a.shape().first().copied(),
}
}
pub fn dtype(&self) -> DType {
match self {
Column::Float(_) => DType::Float,
Column::Int(_) => DType::Int,
Column::Bool(_) => DType::Bool,
Column::UInt(_) => DType::UInt,
Column::U8(_) => DType::U8,
Column::String(_) => DType::String,
}
}
pub fn shape(&self) -> &[usize] {
match self {
Column::Float(a) => a.shape(),
Column::Int(a) => a.shape(),
Column::Bool(a) => a.shape(),
Column::UInt(a) => a.shape(),
Column::U8(a) => a.shape(),
Column::String(a) => a.shape(),
}
}
pub fn raw_bytes(&self) -> Option<Vec<u8>> {
match self {
Column::Float(h) => {
let a = h.array().as_standard_layout();
let mut out = Vec::with_capacity(a.len() * 8);
for &v in a.iter() {
out.extend_from_slice(&v.to_le_bytes());
}
Some(out)
}
Column::Int(h) => {
let a = h.array().as_standard_layout();
let mut out = Vec::with_capacity(a.len() * 4);
for &v in a.iter() {
out.extend_from_slice(&v.to_le_bytes());
}
Some(out)
}
Column::UInt(h) => {
let a = h.array().as_standard_layout();
let mut out = Vec::with_capacity(a.len() * 4);
for &v in a.iter() {
out.extend_from_slice(&v.to_le_bytes());
}
Some(out)
}
Column::U8(h) => Some(h.array().as_standard_layout().iter().copied().collect()),
Column::Bool(h) => Some(
h.array()
.as_standard_layout()
.iter()
.map(|&b| b as u8)
.collect(),
),
Column::String(_) => None,
}
}
pub fn select_rows(&self, indices: &[usize]) -> Column {
use ndarray::Axis;
match self {
Column::Float(h) => Column::from_float(h.array().select(Axis(0), indices)),
Column::Int(h) => Column::from_int(h.array().select(Axis(0), indices)),
Column::Bool(h) => Column::from_bool(h.array().select(Axis(0), indices)),
Column::UInt(h) => Column::from_uint(h.array().select(Axis(0), indices)),
Column::U8(h) => Column::from_u8(h.array().select(Axis(0), indices)),
Column::String(h) => Column::from_string(h.array().select(Axis(0), indices)),
}
}
pub fn is_foreign(&self) -> bool {
match self {
Column::Float(a) => a.is_foreign(),
Column::Int(a) => a.is_foreign(),
Column::Bool(a) => a.is_foreign(),
Column::UInt(a) => a.is_foreign(),
Column::U8(a) => a.is_foreign(),
Column::String(a) => a.is_foreign(),
}
}
pub fn as_float(&self) -> Option<&ArrayD<F>> {
match self {
Column::Float(a) => Some(a.array()),
_ => None,
}
}
pub fn as_float_mut(&mut self) -> Option<&mut ArrayD<F>> {
match self {
Column::Float(a) => Some(realize_owned_mut(a)),
_ => None,
}
}
pub fn as_int(&self) -> Option<&ArrayD<I>> {
match self {
Column::Int(a) => Some(a.array()),
_ => None,
}
}
pub fn as_int_mut(&mut self) -> Option<&mut ArrayD<I>> {
match self {
Column::Int(a) => Some(realize_owned_mut(a)),
_ => None,
}
}
pub fn as_bool(&self) -> Option<&ArrayD<bool>> {
match self {
Column::Bool(a) => Some(a.array()),
_ => None,
}
}
pub fn as_bool_mut(&mut self) -> Option<&mut ArrayD<bool>> {
match self {
Column::Bool(a) => Some(realize_owned_mut(a)),
_ => None,
}
}
pub fn as_uint(&self) -> Option<&ArrayD<U>> {
match self {
Column::UInt(a) => Some(a.array()),
_ => None,
}
}
pub fn as_uint_mut(&mut self) -> Option<&mut ArrayD<U>> {
match self {
Column::UInt(a) => Some(realize_owned_mut(a)),
_ => None,
}
}
pub fn as_u8(&self) -> Option<&ArrayD<u8>> {
match self {
Column::U8(a) => Some(a.array()),
_ => None,
}
}
pub fn as_u8_mut(&mut self) -> Option<&mut ArrayD<u8>> {
match self {
Column::U8(a) => Some(realize_owned_mut(a)),
_ => None,
}
}
pub fn as_string(&self) -> Option<&ArrayD<String>> {
match self {
Column::String(a) => Some(a.array()),
_ => None,
}
}
pub fn as_string_mut(&mut self) -> Option<&mut ArrayD<String>> {
match self {
Column::String(a) => Some(realize_owned_mut(a)),
_ => None,
}
}
pub fn float_arc(&self) -> Option<Arc<ColumnHolder<F>>> {
match self {
Column::Float(a) => Some(Arc::clone(a)),
_ => None,
}
}
pub fn int_arc(&self) -> Option<Arc<ColumnHolder<I>>> {
match self {
Column::Int(a) => Some(Arc::clone(a)),
_ => None,
}
}
pub fn bool_arc(&self) -> Option<Arc<ColumnHolder<bool>>> {
match self {
Column::Bool(a) => Some(Arc::clone(a)),
_ => None,
}
}
pub fn uint_arc(&self) -> Option<Arc<ColumnHolder<U>>> {
match self {
Column::UInt(a) => Some(Arc::clone(a)),
_ => None,
}
}
pub fn u8_arc(&self) -> Option<Arc<ColumnHolder<u8>>> {
match self {
Column::U8(a) => Some(Arc::clone(a)),
_ => None,
}
}
pub fn string_arc(&self) -> Option<Arc<ColumnHolder<String>>> {
match self {
Column::String(a) => Some(Arc::clone(a)),
_ => None,
}
}
pub fn resize(&mut self, new_nrows: usize) {
use ndarray::{Axis, IxDyn, concatenate};
let current = self.shape()[0];
if new_nrows == current {
return;
}
match self {
Column::Float(a) => {
let view = a.view();
let new_arr = if new_nrows < current {
view.slice_axis(Axis(0), (..new_nrows).into()).to_owned()
} else {
let mut pad_shape = a.shape().to_vec();
pad_shape[0] = new_nrows - current;
let pad = ArrayD::<F>::zeros(IxDyn(&pad_shape));
concatenate(Axis(0), &[view, pad.view()]).unwrap()
};
*a = Arc::new(ColumnHolder::from_owned(new_arr));
}
Column::Int(a) => {
let view = a.view();
let new_arr = if new_nrows < current {
view.slice_axis(Axis(0), (..new_nrows).into()).to_owned()
} else {
let mut pad_shape = a.shape().to_vec();
pad_shape[0] = new_nrows - current;
let pad = ArrayD::<I>::zeros(IxDyn(&pad_shape));
concatenate(Axis(0), &[view, pad.view()]).unwrap()
};
*a = Arc::new(ColumnHolder::from_owned(new_arr));
}
Column::UInt(a) => {
let view = a.view();
let new_arr = if new_nrows < current {
view.slice_axis(Axis(0), (..new_nrows).into()).to_owned()
} else {
let mut pad_shape = a.shape().to_vec();
pad_shape[0] = new_nrows - current;
let pad = ArrayD::<U>::zeros(IxDyn(&pad_shape));
concatenate(Axis(0), &[view, pad.view()]).unwrap()
};
*a = Arc::new(ColumnHolder::from_owned(new_arr));
}
Column::U8(a) => {
let view = a.view();
let new_arr = if new_nrows < current {
view.slice_axis(Axis(0), (..new_nrows).into()).to_owned()
} else {
let mut pad_shape = a.shape().to_vec();
pad_shape[0] = new_nrows - current;
let pad = ArrayD::<u8>::zeros(IxDyn(&pad_shape));
concatenate(Axis(0), &[view, pad.view()]).unwrap()
};
*a = Arc::new(ColumnHolder::from_owned(new_arr));
}
Column::Bool(a) => {
let view = a.view();
let new_arr = if new_nrows < current {
view.slice_axis(Axis(0), (..new_nrows).into()).to_owned()
} else {
let mut pad_shape = a.shape().to_vec();
pad_shape[0] = new_nrows - current;
let pad = ArrayD::<bool>::default(IxDyn(&pad_shape));
concatenate(Axis(0), &[view, pad.view()]).unwrap()
};
*a = Arc::new(ColumnHolder::from_owned(new_arr));
}
Column::String(a) => {
let view = a.view();
let new_arr = if new_nrows < current {
view.slice_axis(Axis(0), (..new_nrows).into()).to_owned()
} else {
let mut pad_shape = a.shape().to_vec();
pad_shape[0] = new_nrows - current;
let pad = ArrayD::<String>::default(IxDyn(&pad_shape));
concatenate(Axis(0), &[view, pad.view()]).unwrap()
};
*a = Arc::new(ColumnHolder::from_owned(new_arr));
}
}
}
}
impl std::fmt::Debug for Column {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Column::Float(a) => write!(f, "Column::Float(shape={:?})", a.shape()),
Column::Int(a) => write!(f, "Column::Int(shape={:?})", a.shape()),
Column::Bool(a) => write!(f, "Column::Bool(shape={:?})", a.shape()),
Column::UInt(a) => write!(f, "Column::UInt(shape={:?})", a.shape()),
Column::U8(a) => write!(f, "Column::U8(shape={:?})", a.shape()),
Column::String(a) => write!(f, "Column::String(shape={:?})", a.shape()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{F, I, U};
use ndarray::{Array1, ArrayD};
fn float_col(n: usize) -> Column {
Column::from_float(Array1::from_vec(vec![0.0 as F; n]).into_dyn())
}
fn int_col(n: usize) -> Column {
Column::from_int(Array1::from_vec(vec![0 as I; n]).into_dyn())
}
fn bool_col(n: usize) -> Column {
Column::from_bool(Array1::from_vec(vec![false; n]).into_dyn())
}
fn uint_col(n: usize) -> Column {
Column::from_uint(Array1::from_vec(vec![0 as U; n]).into_dyn())
}
fn u8_col(n: usize) -> Column {
Column::from_u8(Array1::from_vec(vec![0u8; n]).into_dyn())
}
fn string_col(n: usize) -> Column {
Column::from_string(Array1::from_vec(vec![String::new(); n]).into_dyn())
}
#[test]
fn test_nrows() {
assert_eq!(float_col(5).nrows(), Some(5));
assert_eq!(int_col(3).nrows(), Some(3));
assert_eq!(bool_col(7).nrows(), Some(7));
assert_eq!(uint_col(2).nrows(), Some(2));
assert_eq!(u8_col(4).nrows(), Some(4));
assert_eq!(string_col(1).nrows(), Some(1));
let rank0 = Column::from_float(ArrayD::<F>::from_elem(vec![], 1.0));
assert_eq!(rank0.nrows(), None);
}
#[test]
fn test_dtype() {
assert_eq!(float_col(1).dtype(), DType::Float);
assert_eq!(int_col(1).dtype(), DType::Int);
assert_eq!(bool_col(1).dtype(), DType::Bool);
assert_eq!(uint_col(1).dtype(), DType::UInt);
assert_eq!(u8_col(1).dtype(), DType::U8);
assert_eq!(string_col(1).dtype(), DType::String);
}
#[test]
fn test_shape() {
assert_eq!(float_col(4).shape(), &[4]);
let col2d = Column::from_int(ArrayD::<I>::from_elem(vec![3, 2], 0));
assert_eq!(col2d.shape(), &[3, 2]);
}
#[test]
fn test_as_float_on_float() {
let col = float_col(3);
assert!(col.as_float().is_some());
assert_eq!(col.as_float().unwrap().len(), 3);
}
#[test]
fn test_as_float_on_wrong_type() {
assert!(int_col(2).as_float().is_none());
assert!(bool_col(2).as_float().is_none());
assert!(uint_col(2).as_float().is_none());
assert!(u8_col(2).as_float().is_none());
assert!(string_col(2).as_float().is_none());
}
#[test]
fn test_as_int() {
let col = int_col(4);
assert!(col.as_int().is_some());
assert_eq!(col.as_int().unwrap().len(), 4);
assert!(float_col(1).as_int().is_none());
assert!(bool_col(1).as_int().is_none());
}
#[test]
fn test_as_bool() {
let col = bool_col(2);
assert!(col.as_bool().is_some());
assert_eq!(col.as_bool().unwrap().len(), 2);
assert!(float_col(1).as_bool().is_none());
assert!(int_col(1).as_bool().is_none());
}
#[test]
fn test_as_uint() {
let col = uint_col(6);
assert!(col.as_uint().is_some());
assert_eq!(col.as_uint().unwrap().len(), 6);
assert!(float_col(1).as_uint().is_none());
assert!(int_col(1).as_uint().is_none());
}
#[test]
fn test_as_u8() {
let col = u8_col(3);
assert!(col.as_u8().is_some());
assert_eq!(col.as_u8().unwrap().len(), 3);
assert!(float_col(1).as_u8().is_none());
assert!(uint_col(1).as_u8().is_none());
}
#[test]
fn test_as_string() {
let col = string_col(2);
assert!(col.as_string().is_some());
assert_eq!(col.as_string().unwrap().len(), 2);
assert!(float_col(1).as_string().is_none());
assert!(int_col(1).as_string().is_none());
}
#[test]
fn test_as_float_mut() {
let mut col =
Column::from_float(Array1::from_vec(vec![1.0 as F, 2.0 as F, 3.0 as F]).into_dyn());
{
let arr = col.as_float_mut().unwrap();
arr[0] = 99.0;
}
let arr = col.as_float().unwrap();
assert!((arr[0] - 99.0).abs() < F::EPSILON);
let mut int = int_col(1);
assert!(int.as_float_mut().is_none());
}
#[test]
fn test_debug_format() {
let dbg = format!("{:?}", float_col(3));
assert!(dbg.contains("Column::Float"));
assert!(dbg.contains("shape="));
}
#[test]
fn test_clone_shares_buffer() {
let col = Column::from_float(Array1::from_vec(vec![1.0 as F, 2.0, 3.0]).into_dyn());
let cloned = col.clone();
let p1 = col.as_float().unwrap().as_ptr();
let p2 = cloned.as_float().unwrap().as_ptr();
assert_eq!(p1, p2, "clone must share the buffer");
}
#[test]
fn test_as_float_mut_cow_when_shared() {
let col = Column::from_float(Array1::from_vec(vec![1.0 as F, 2.0, 3.0]).into_dyn());
let mut cloned = col.clone();
cloned.as_float_mut().unwrap()[0] = 99.0;
assert_eq!(col.as_float().unwrap()[0], 1.0);
assert_eq!(cloned.as_float().unwrap()[0], 99.0);
}
#[test]
fn test_foreign_holder_readable() {
let source: Vec<F> = vec![1.0, 2.0, 3.0, 4.0];
let ptr = source.as_ptr() as *mut F;
let len = source.len();
let forged = unsafe {
let vec = Vec::from_raw_parts(ptr, len, len);
ArrayD::from_shape_vec(ndarray::IxDyn(&[len]), vec).unwrap()
};
let holder = unsafe { ColumnHolder::from_foreign(forged, source) };
let col = Column::from_float_holder(holder);
assert!(col.is_foreign());
let arr = col.as_float().unwrap();
assert_eq!(arr.as_slice().unwrap(), &[1.0, 2.0, 3.0, 4.0]);
}
#[test]
fn test_foreign_holder_cow_on_mut() {
let source: Vec<F> = vec![10.0, 20.0, 30.0];
let ptr = source.as_ptr() as *mut F;
let len = source.len();
let forged = unsafe {
let vec = Vec::from_raw_parts(ptr, len, len);
ArrayD::from_shape_vec(ndarray::IxDyn(&[len]), vec).unwrap()
};
let source_clone = source.clone();
let holder = unsafe { ColumnHolder::from_foreign(forged, source_clone) };
let mut col = Column::from_float_holder(holder);
assert!(col.is_foreign());
col.as_float_mut().unwrap()[0] = 999.0;
assert!(!col.is_foreign(), "after mut, holder must be Rust-owned");
assert_eq!(col.as_float().unwrap()[0], 999.0);
assert_eq!(source[0], 10.0);
}
#[test]
fn test_foreign_holder_clone_detaches() {
let source: Vec<F> = vec![7.0, 8.0];
let ptr = source.as_ptr() as *mut F;
let len = source.len();
let forged = unsafe {
let vec = Vec::from_raw_parts(ptr, len, len);
ArrayD::from_shape_vec(ndarray::IxDyn(&[len]), vec).unwrap()
};
let source_clone = source.clone();
let holder = unsafe { ColumnHolder::from_foreign(forged, source_clone) };
let holder_clone = holder.clone();
assert!(holder.is_foreign());
assert!(!holder_clone.is_foreign());
assert_eq!(holder_clone.array().as_slice().unwrap(), &[7.0, 8.0]);
}
#[test]
fn test_raw_bytes_lengths() {
assert_eq!(float_col(3).raw_bytes().map(|b| b.len()), Some(24));
assert_eq!(int_col(4).raw_bytes().map(|b| b.len()), Some(16));
assert_eq!(uint_col(2).raw_bytes().map(|b| b.len()), Some(8));
assert_eq!(u8_col(5).raw_bytes().map(|b| b.len()), Some(5));
assert_eq!(bool_col(7).raw_bytes().map(|b| b.len()), Some(7));
assert!(string_col(3).raw_bytes().is_none());
}
#[test]
fn test_raw_bytes_little_endian_values() {
let col = Column::from_float(Array1::from_vec(vec![1.0 as F, 2.0]).into_dyn());
let b = col.raw_bytes().unwrap();
assert_eq!(&b[0..8], &1.0f64.to_le_bytes());
assert_eq!(&b[8..16], &2.0f64.to_le_bytes());
}
#[test]
fn test_raw_bytes_multidim_length() {
let col = Column::from_int(
ArrayD::<I>::from_shape_vec(ndarray::IxDyn(&[2, 3]), vec![0 as I; 6]).unwrap(),
);
assert_eq!(col.raw_bytes().map(|b| b.len()), Some(24));
}
#[test]
fn test_raw_bytes_strided_materializes() {
let base = Array1::from_vec(vec![1.0 as F, 2.0, 3.0, 4.0, 5.0, 6.0]);
let strided = base.slice_move(ndarray::s![..;2]).into_dyn(); let col = Column::from_float(strided);
let b = col.raw_bytes().unwrap();
assert_eq!(b.len(), 24);
assert_eq!(&b[0..8], &1.0f64.to_le_bytes());
assert_eq!(&b[8..16], &3.0f64.to_le_bytes());
assert_eq!(&b[16..24], &5.0f64.to_le_bytes());
}
}