1use std::{fmt, str::FromStr, sync::Arc};
4
5use crate::{LadduDataError, LadduDataResult};
6
7macro_rules! integer_columns {
8 ($(($variant:ident, $ty:ty, $name:literal, $alias:literal)),* $(,)?) => {
9 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
11 pub enum ColumnDType {
12 $(#[doc = concat!("An exact `", $name, "` column.")]
13 $variant,)*
14 }
15
16 impl ColumnDType {
17 pub const fn name(self) -> &'static str {
19 match self { $(Self::$variant => $name,)* }
20 }
21
22 pub const fn width(self) -> usize {
24 match self { $(Self::$variant => std::mem::size_of::<$ty>(),)* }
25 }
26 }
27
28 impl FromStr for ColumnDType {
29 type Err = LadduDataError;
30 fn from_str(value: &str) -> LadduDataResult<Self> {
31 match value {
32 $($name | $alias => Ok(Self::$variant),)*
33 _ => Err(LadduDataError::Schema(format!("unsupported column dtype: {value}"))),
34 }
35 }
36 }
37
38 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
40 pub enum ColumnValue {
41 $(#[doc = concat!("An exact `", $name, "` value.")]
42 $variant($ty),)*
43 }
44
45 impl ColumnValue {
46 pub const fn dtype(self) -> ColumnDType {
48 match self { $(Self::$variant(_) => ColumnDType::$variant,)* }
49 }
50 }
51
52 #[derive(Clone, Debug, PartialEq, Eq)]
57 pub enum Column {
58 $(#[doc = concat!("An immutable `", $name, "` buffer.")]
59 $variant(Arc<[$ty]>),)*
60 }
61
62 impl Column {
63 pub fn dtype(&self) -> ColumnDType {
65 match self { $(Self::$variant(_) => ColumnDType::$variant,)* }
66 }
67
68 pub fn len(&self) -> usize {
70 match self { $(Self::$variant(values) => values.len(),)* }
71 }
72
73 pub fn at(&self, row: usize) -> ColumnValue {
78 match self { $(Self::$variant(values) => ColumnValue::$variant(values[row]),)* }
79 }
80
81 pub fn append_le_bytes(&self, output: &mut Vec<u8>) {
83 match self { $(Self::$variant(values) => {
84 for value in values.iter() { output.extend_from_slice(&value.to_le_bytes()); }
85 },)* }
86 }
87
88 pub fn from_le_bytes(dtype: ColumnDType, bytes: &[u8]) -> LadduDataResult<Self> {
93 if !bytes.len().is_multiple_of(dtype.width()) {
94 return Err(LadduDataError::Schema("invalid integer column byte count".into()));
95 }
96 Ok(match dtype { $(ColumnDType::$variant =>
97 Self::$variant(bytes.as_chunks::<{ std::mem::size_of::<$ty>() }>().0.iter()
98 .map(|bytes| <$ty>::from_le_bytes(*bytes)).collect()),)* })
99 }
100
101 pub fn select(&self, rows: &[usize]) -> Self {
106 match self { $(Self::$variant(values) => Self::$variant(rows.iter().map(|&row| values[row]).collect()),)* }
107 }
108
109 pub fn slice(&self, start: usize, end: usize) -> Self {
114 match self { $(Self::$variant(values) => Self::$variant(Arc::from(&values[start..end])),)* }
115 }
116
117 }
118
119 pub(crate) enum ColumnBuffer {
120 $($variant(Vec<$ty>),)*
121 }
122
123 impl ColumnBuffer {
124 pub(crate) fn new(dtype: ColumnDType, capacity: usize) -> Self {
125 match dtype { $(ColumnDType::$variant => Self::$variant(Vec::with_capacity(capacity)),)* }
126 }
127
128 pub(crate) fn push(&mut self, value: ColumnValue) -> LadduDataResult<()> {
129 match (self, value) {
130 $((Self::$variant(values), ColumnValue::$variant(value)) => values.push(value),)*
131 _ => return Err(LadduDataError::Schema("typed row value dtype does not match schema".into())),
132 }
133 Ok(())
134 }
135
136 pub(crate) fn extend(&mut self, column: &Column) -> LadduDataResult<()> {
137 match (self, column) {
138 $((Self::$variant(values), Column::$variant(column)) => values.extend_from_slice(column),)*
139 _ => return Err(LadduDataError::Schema("typed column dtype does not match schema".into())),
140 }
141 Ok(())
142 }
143
144 pub(crate) fn finish(self) -> Column {
145 match self { $(Self::$variant(values) => Column::$variant(values.into()),)* }
146 }
147 }
148 };
149}
150
151integer_columns!(
152 (I8, i8, "int8", "i8"),
153 (U8, u8, "uint8", "u8"),
154 (I16, i16, "int16", "i16"),
155 (U16, u16, "uint16", "u16"),
156 (I32, i32, "int32", "i32"),
157 (U32, u32, "uint32", "u32"),
158 (I64, i64, "int64", "i64"),
159 (U64, u64, "uint64", "u64"),
160);
161
162impl fmt::Display for ColumnDType {
163 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
164 formatter.write_str(self.name())
165 }
166}
167
168impl Column {
169 pub fn is_empty(&self) -> bool {
171 self.len() == 0
172 }
173
174 pub fn empty(dtype: ColumnDType) -> Self {
176 ColumnBuffer::new(dtype, 0).finish()
177 }
178
179 pub fn concat(dtype: ColumnDType, columns: &[&Self]) -> LadduDataResult<Self> {
184 let mut output = ColumnBuffer::new(dtype, columns.iter().map(|column| column.len()).sum());
185 for column in columns {
186 output.extend(column)?;
187 }
188 Ok(output.finish())
189 }
190}