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