vortex_array/arrays/struct_/vtable/
mod.rs1use itertools::Itertools;
5use kernel::PARENT_KERNELS;
6use vortex_error::VortexExpect;
7use vortex_error::VortexResult;
8use vortex_error::vortex_bail;
9use vortex_error::vortex_panic;
10use vortex_session::VortexSession;
11
12use crate::ArrayRef;
13use crate::ExecutionCtx;
14use crate::ExecutionResult;
15use crate::array::Array;
16use crate::array::ArrayView;
17use crate::array::EmptyArrayData;
18use crate::array::VTable;
19use crate::array::child_to_validity;
20use crate::arrays::struct_::array::FIELDS_OFFSET;
21use crate::arrays::struct_::array::VALIDITY_SLOT;
22use crate::arrays::struct_::array::make_struct_slots;
23use crate::arrays::struct_::compute::rules::PARENT_RULES;
24use crate::buffer::BufferHandle;
25use crate::dtype::DType;
26use crate::serde::ArrayChildren;
27use crate::validity::Validity;
28mod kernel;
29mod operations;
30mod validity;
31
32use vortex_session::registry::CachedId;
33
34use crate::array::ArrayId;
35
36pub type StructArray = Array<Struct>;
38
39impl VTable for Struct {
40 type ArrayData = EmptyArrayData;
41
42 type OperationsVTable = Self;
43 type ValidityVTable = Self;
44 fn id(&self) -> ArrayId {
45 static ID: CachedId = CachedId::new("vortex.struct");
46 *ID
47 }
48
49 fn nbuffers(_array: ArrayView<'_, Self>) -> usize {
50 0
51 }
52
53 fn validate(
54 &self,
55 _data: &EmptyArrayData,
56 dtype: &DType,
57 len: usize,
58 slots: &[Option<ArrayRef>],
59 ) -> VortexResult<()> {
60 let DType::Struct(struct_dtype, nullability) = dtype else {
61 vortex_bail!("Expected struct dtype, found {:?}", dtype)
62 };
63
64 let expected_slots = struct_dtype.nfields() + 1;
65 if slots.len() != expected_slots {
66 vortex_bail!(
67 InvalidArgument: "StructArray has {} slots but expected {}",
68 slots.len(),
69 expected_slots
70 );
71 }
72
73 let validity = child_to_validity(&slots[VALIDITY_SLOT], *nullability);
74 if let Some(validity_len) = validity.maybe_len()
75 && validity_len != len
76 {
77 vortex_bail!(
78 InvalidArgument: "StructArray validity length {} does not match outer length {}",
79 validity_len,
80 len
81 );
82 }
83
84 let field_slots = &slots[FIELDS_OFFSET..];
85 if field_slots.is_empty() {
86 return Ok(());
87 }
88
89 for (idx, (slot, field_dtype)) in field_slots.iter().zip(struct_dtype.fields()).enumerate()
90 {
91 let field = slot
92 .as_ref()
93 .ok_or_else(|| vortex_error::vortex_err!("StructArray missing field slot {idx}"))?;
94 if field.len() != len {
95 vortex_bail!(
96 InvalidArgument: "StructArray field {idx} has length {} but expected {}",
97 field.len(),
98 len
99 );
100 }
101 if field.dtype() != &field_dtype {
102 vortex_bail!(
103 InvalidArgument: "StructArray field {idx} has dtype {} but expected {}",
104 field.dtype(),
105 field_dtype
106 );
107 }
108 }
109
110 Ok(())
111 }
112
113 fn buffer(_array: ArrayView<'_, Self>, idx: usize) -> BufferHandle {
114 vortex_panic!("StructArray buffer index {idx} out of bounds")
115 }
116
117 fn buffer_name(_array: ArrayView<'_, Self>, idx: usize) -> Option<String> {
118 vortex_panic!("StructArray buffer_name index {idx} out of bounds")
119 }
120
121 fn serialize(
122 _array: ArrayView<'_, Self>,
123 _session: &VortexSession,
124 ) -> VortexResult<Option<Vec<u8>>> {
125 Ok(Some(vec![]))
126 }
127
128 fn deserialize(
129 &self,
130 dtype: &DType,
131 len: usize,
132 metadata: &[u8],
133
134 _buffers: &[BufferHandle],
135 children: &dyn ArrayChildren,
136 _session: &VortexSession,
137 ) -> VortexResult<crate::array::ArrayParts<Self>> {
138 if !metadata.is_empty() {
139 vortex_bail!(
140 "StructArray expects empty metadata, got {} bytes",
141 metadata.len()
142 );
143 }
144 let DType::Struct(struct_dtype, nullability) = dtype else {
145 vortex_bail!("Expected struct dtype, found {:?}", dtype)
146 };
147
148 let (validity, non_data_children) = if children.len() == struct_dtype.nfields() {
149 (Validity::from(*nullability), 0_usize)
150 } else if children.len() == struct_dtype.nfields() + 1 {
151 let validity = children.get(0, &Validity::DTYPE, len)?;
152 (Validity::Array(validity), 1_usize)
153 } else {
154 vortex_bail!(
155 "Expected {} or {} children, found {}",
156 struct_dtype.nfields(),
157 struct_dtype.nfields() + 1,
158 children.len()
159 );
160 };
161
162 let field_children: Vec<_> = (0..struct_dtype.nfields())
163 .map(|i| {
164 let child_dtype = struct_dtype
165 .field_by_index(i)
166 .vortex_expect("no out of bounds");
167 children.get(non_data_children + i, &child_dtype, len)
168 })
169 .try_collect()?;
170
171 let slots = make_struct_slots(&field_children, &validity, len);
172 Ok(
173 crate::array::ArrayParts::new(self.clone(), dtype.clone(), len, EmptyArrayData)
174 .with_slots(slots),
175 )
176 }
177
178 fn slot_name(array: ArrayView<'_, Self>, idx: usize) -> String {
179 if idx == VALIDITY_SLOT {
180 "validity".to_string()
181 } else {
182 array.dtype().as_struct_fields().names()[idx - FIELDS_OFFSET].to_string()
183 }
184 }
185
186 fn execute(array: Array<Self>, _ctx: &mut ExecutionCtx) -> VortexResult<ExecutionResult> {
187 Ok(ExecutionResult::done(array))
188 }
189
190 fn reduce_parent(
191 array: ArrayView<'_, Self>,
192 parent: &ArrayRef,
193 child_idx: usize,
194 ) -> VortexResult<Option<ArrayRef>> {
195 PARENT_RULES.evaluate(array, parent, child_idx)
196 }
197
198 fn execute_parent(
199 array: ArrayView<'_, Self>,
200 parent: &ArrayRef,
201 child_idx: usize,
202 ctx: &mut ExecutionCtx,
203 ) -> VortexResult<Option<ArrayRef>> {
204 PARENT_KERNELS.execute(array, parent, child_idx, ctx)
205 }
206}
207
208#[derive(Clone, Debug)]
209pub struct Struct;