Skip to main content

vortex_array/arrays/struct_/vtable/
mod.rs

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