1use arrow_schema::{DataType, Field};
28
29use crate::error::{Invariant, Result, Violation};
30
31#[derive(Clone, PartialEq, Eq, Debug)]
33pub struct Layout<'a> {
34 pub validity: bool,
36 pub values: usize,
38 pub children: Vec<&'a Field>,
40}
41
42pub fn layout<'a>(data_type: &'a DataType, path: &str) -> Result<Layout<'a>> {
49 let (validity, values, children) = match data_type {
50 DataType::Null => (false, 0, Vec::new()),
51
52 DataType::Boolean
53 | DataType::Int8
54 | DataType::Int16
55 | DataType::Int32
56 | DataType::Int64
57 | DataType::UInt8
58 | DataType::UInt16
59 | DataType::UInt32
60 | DataType::UInt64
61 | DataType::Float16
62 | DataType::Float32
63 | DataType::Float64
64 | DataType::Date32
65 | DataType::Date64
66 | DataType::Time32(_)
67 | DataType::Time64(_)
68 | DataType::Timestamp(_, _)
69 | DataType::Duration(_)
70 | DataType::Interval(_)
71 | DataType::Decimal32(_, _)
72 | DataType::Decimal64(_, _)
73 | DataType::Decimal128(_, _)
74 | DataType::Decimal256(_, _)
75 | DataType::FixedSizeBinary(_) => (true, 1, Vec::new()),
76
77 DataType::Utf8 | DataType::Binary | DataType::LargeUtf8 | DataType::LargeBinary => {
78 (true, 2, Vec::new())
79 }
80
81 DataType::List(field) | DataType::LargeList(field) | DataType::Map(field, _) => {
82 (true, 1, vec![field.as_ref()])
83 }
84
85 DataType::FixedSizeList(field, _) => (true, 0, vec![field.as_ref()]),
86
87 DataType::Struct(fields) => (true, 0, fields.iter().map(AsRef::as_ref).collect()),
88
89 other => {
90 return Err(Violation::at(
91 Invariant::Unsupported,
92 path,
93 format!("this build does not carry {other} columns"),
94 ));
95 }
96 };
97
98 Ok(Layout {
99 validity,
100 values,
101 children,
102 })
103}
104
105pub(crate) fn slot_bits(data_type: &DataType) -> Option<u64> {
110 let bits = match data_type {
111 DataType::Boolean => 1,
112 DataType::Int8 | DataType::UInt8 => 8,
113 DataType::Int16 | DataType::UInt16 | DataType::Float16 => 16,
114 DataType::Int32
115 | DataType::UInt32
116 | DataType::Float32
117 | DataType::Date32
118 | DataType::Time32(_)
119 | DataType::Decimal32(_, _) => 32,
120 DataType::Int64
121 | DataType::UInt64
122 | DataType::Float64
123 | DataType::Date64
124 | DataType::Time64(_)
125 | DataType::Timestamp(_, _)
126 | DataType::Duration(_)
127 | DataType::Decimal64(_, _) => 64,
128 DataType::Decimal128(_, _) => 128,
129 DataType::Decimal256(_, _) => 256,
130 DataType::Interval(unit) => match unit {
131 arrow_schema::IntervalUnit::YearMonth => 32,
132 arrow_schema::IntervalUnit::DayTime => 64,
133 arrow_schema::IntervalUnit::MonthDayNano => 128,
134 },
135 DataType::FixedSizeBinary(width) => u64::try_from(*width).ok()? * 8,
138 _ => return None,
139 };
140 Some(bits)
141}
142
143pub(crate) const fn offset_width(data_type: &DataType) -> Option<u64> {
145 match data_type {
146 DataType::Utf8 | DataType::Binary | DataType::List(_) | DataType::Map(_, _) => Some(4),
147 DataType::LargeUtf8 | DataType::LargeBinary | DataType::LargeList(_) => Some(8),
148 _ => None,
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use arrow_schema::{DataType, Field, Fields};
155
156 use super::{layout, offset_width, slot_bits};
157 use crate::error::Invariant;
158
159 #[test]
160 fn a_flat_column_takes_a_validity_buffer_and_a_values_buffer() {
161 let found = layout(&DataType::Int64, "a").expect("Int64 is a type iris carries");
162 assert!(found.validity);
163 assert_eq!(found.values, 1);
164 assert!(found.children.is_empty());
165 }
166
167 #[test]
168 fn a_struct_takes_no_values_buffer_and_one_child_per_field() {
169 let fields = Fields::from(vec![
170 Field::new("x", DataType::Int64, false),
171 Field::new("y", DataType::Int64, false),
172 ]);
173 let point = DataType::Struct(fields);
174 let found = layout(&point, "p").expect("Struct is a type iris carries");
175 assert_eq!(found.values, 0);
176 assert_eq!(found.children.len(), 2);
177 }
178
179 #[test]
180 fn null_is_the_one_type_with_no_validity_buffer() {
181 let found = layout(&DataType::Null, "n").expect("Null is a type iris carries");
182 assert!(!found.validity);
183 assert_eq!(found.values, 0);
184 }
185
186 #[test]
187 fn a_type_this_build_does_not_carry_is_refused_by_name() {
188 let dictionary = DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8));
189 let err = layout(&dictionary, "d").expect_err("a dictionary is not carried yet");
190 assert_eq!(err.invariant, Invariant::Unsupported);
191 assert!(err.to_string().contains("Dictionary"), "{err}");
192 }
193
194 #[test]
195 fn a_boolean_slot_is_one_bit_and_a_timestamp_is_sixty_four() {
196 assert_eq!(slot_bits(&DataType::Boolean), Some(1));
197 assert_eq!(
198 slot_bits(&DataType::Timestamp(
199 arrow_schema::TimeUnit::Nanosecond,
200 None
201 )),
202 Some(64)
203 );
204 assert_eq!(slot_bits(&DataType::FixedSizeBinary(7)), Some(56));
205 }
206
207 #[test]
208 fn a_negative_fixed_width_is_not_a_width() {
209 assert_eq!(slot_bits(&DataType::FixedSizeBinary(-1)), None);
210 }
211
212 #[test]
213 fn the_large_variants_are_the_ones_with_eight_byte_offsets() {
214 assert_eq!(offset_width(&DataType::Utf8), Some(4));
215 assert_eq!(offset_width(&DataType::LargeUtf8), Some(8));
216 assert_eq!(offset_width(&DataType::Int64), None);
217 }
218}