use arrow_schema::{DataType, Field};
use crate::error::{Invariant, Result, Violation};
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Layout<'a> {
pub validity: bool,
pub values: usize,
pub children: Vec<&'a Field>,
}
pub fn layout<'a>(data_type: &'a DataType, path: &str) -> Result<Layout<'a>> {
let (validity, values, children) = match data_type {
DataType::Null => (false, 0, Vec::new()),
DataType::Boolean
| DataType::Int8
| DataType::Int16
| DataType::Int32
| DataType::Int64
| DataType::UInt8
| DataType::UInt16
| DataType::UInt32
| DataType::UInt64
| DataType::Float16
| DataType::Float32
| DataType::Float64
| DataType::Date32
| DataType::Date64
| DataType::Time32(_)
| DataType::Time64(_)
| DataType::Timestamp(_, _)
| DataType::Duration(_)
| DataType::Interval(_)
| DataType::Decimal32(_, _)
| DataType::Decimal64(_, _)
| DataType::Decimal128(_, _)
| DataType::Decimal256(_, _)
| DataType::FixedSizeBinary(_) => (true, 1, Vec::new()),
DataType::Utf8 | DataType::Binary | DataType::LargeUtf8 | DataType::LargeBinary => {
(true, 2, Vec::new())
}
DataType::List(field) | DataType::LargeList(field) | DataType::Map(field, _) => {
(true, 1, vec![field.as_ref()])
}
DataType::FixedSizeList(field, _) => (true, 0, vec![field.as_ref()]),
DataType::Struct(fields) => (true, 0, fields.iter().map(AsRef::as_ref).collect()),
other => {
return Err(Violation::at(
Invariant::Unsupported,
path,
format!("this build does not carry {other} columns"),
));
}
};
Ok(Layout {
validity,
values,
children,
})
}
pub(crate) fn slot_bits(data_type: &DataType) -> Option<u64> {
let bits = match data_type {
DataType::Boolean => 1,
DataType::Int8 | DataType::UInt8 => 8,
DataType::Int16 | DataType::UInt16 | DataType::Float16 => 16,
DataType::Int32
| DataType::UInt32
| DataType::Float32
| DataType::Date32
| DataType::Time32(_)
| DataType::Decimal32(_, _) => 32,
DataType::Int64
| DataType::UInt64
| DataType::Float64
| DataType::Date64
| DataType::Time64(_)
| DataType::Timestamp(_, _)
| DataType::Duration(_)
| DataType::Decimal64(_, _) => 64,
DataType::Decimal128(_, _) => 128,
DataType::Decimal256(_, _) => 256,
DataType::Interval(unit) => match unit {
arrow_schema::IntervalUnit::YearMonth => 32,
arrow_schema::IntervalUnit::DayTime => 64,
arrow_schema::IntervalUnit::MonthDayNano => 128,
},
DataType::FixedSizeBinary(width) => u64::try_from(*width).ok()? * 8,
_ => return None,
};
Some(bits)
}
pub(crate) const fn offset_width(data_type: &DataType) -> Option<u64> {
match data_type {
DataType::Utf8 | DataType::Binary | DataType::List(_) | DataType::Map(_, _) => Some(4),
DataType::LargeUtf8 | DataType::LargeBinary | DataType::LargeList(_) => Some(8),
_ => None,
}
}
#[cfg(test)]
mod tests {
use arrow_schema::{DataType, Field, Fields};
use super::{layout, offset_width, slot_bits};
use crate::error::Invariant;
#[test]
fn a_flat_column_takes_a_validity_buffer_and_a_values_buffer() {
let found = layout(&DataType::Int64, "a").expect("Int64 is a type iris carries");
assert!(found.validity);
assert_eq!(found.values, 1);
assert!(found.children.is_empty());
}
#[test]
fn a_struct_takes_no_values_buffer_and_one_child_per_field() {
let fields = Fields::from(vec![
Field::new("x", DataType::Int64, false),
Field::new("y", DataType::Int64, false),
]);
let point = DataType::Struct(fields);
let found = layout(&point, "p").expect("Struct is a type iris carries");
assert_eq!(found.values, 0);
assert_eq!(found.children.len(), 2);
}
#[test]
fn null_is_the_one_type_with_no_validity_buffer() {
let found = layout(&DataType::Null, "n").expect("Null is a type iris carries");
assert!(!found.validity);
assert_eq!(found.values, 0);
}
#[test]
fn a_type_this_build_does_not_carry_is_refused_by_name() {
let dictionary = DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8));
let err = layout(&dictionary, "d").expect_err("a dictionary is not carried yet");
assert_eq!(err.invariant, Invariant::Unsupported);
assert!(err.to_string().contains("Dictionary"), "{err}");
}
#[test]
fn a_boolean_slot_is_one_bit_and_a_timestamp_is_sixty_four() {
assert_eq!(slot_bits(&DataType::Boolean), Some(1));
assert_eq!(
slot_bits(&DataType::Timestamp(
arrow_schema::TimeUnit::Nanosecond,
None
)),
Some(64)
);
assert_eq!(slot_bits(&DataType::FixedSizeBinary(7)), Some(56));
}
#[test]
fn a_negative_fixed_width_is_not_a_width() {
assert_eq!(slot_bits(&DataType::FixedSizeBinary(-1)), None);
}
#[test]
fn the_large_variants_are_the_ones_with_eight_byte_offsets() {
assert_eq!(offset_width(&DataType::Utf8), Some(4));
assert_eq!(offset_width(&DataType::LargeUtf8), Some(8));
assert_eq!(offset_width(&DataType::Int64), None);
}
}