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 vortex_session::registry::CachedId;
33
34use crate::array::ArrayId;
35
36/// A [`Struct`]-encoded Vortex array.
37pub 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;