vortex_layout/layouts/struct_/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4mod reader;
5pub mod writer;
6
7use std::sync::Arc;
8
9use reader::StructReader;
10use vortex_array::ArrayContext;
11use vortex_array::DeserializeMetadata;
12use vortex_array::EmptyMetadata;
13use vortex_dtype::DType;
14use vortex_dtype::Field;
15use vortex_dtype::FieldMask;
16use vortex_dtype::Nullability;
17use vortex_dtype::StructFields;
18use vortex_error::VortexExpect;
19use vortex_error::VortexResult;
20use vortex_error::vortex_bail;
21use vortex_error::vortex_ensure;
22use vortex_error::vortex_err;
23use vortex_session::SessionExt;
24use vortex_session::VortexSession;
25
26use crate::LayoutChildType;
27use crate::LayoutEncodingRef;
28use crate::LayoutId;
29use crate::LayoutReaderRef;
30use crate::LayoutRef;
31use crate::VTable;
32use crate::children::LayoutChildren;
33use crate::children::OwnedLayoutChildren;
34use crate::segments::SegmentId;
35use crate::segments::SegmentSource;
36use crate::vtable;
37
38vtable!(Struct);
39
40impl VTable for StructVTable {
41    type Layout = StructLayout;
42    type Encoding = StructLayoutEncoding;
43    type Metadata = EmptyMetadata;
44
45    fn id(_encoding: &Self::Encoding) -> LayoutId {
46        LayoutId::new_ref("vortex.struct")
47    }
48
49    fn encoding(_layout: &Self::Layout) -> LayoutEncodingRef {
50        LayoutEncodingRef::new_ref(StructLayoutEncoding.as_ref())
51    }
52
53    fn row_count(layout: &Self::Layout) -> u64 {
54        layout.row_count
55    }
56
57    fn dtype(layout: &Self::Layout) -> &DType {
58        &layout.dtype
59    }
60
61    fn metadata(_layout: &Self::Layout) -> Self::Metadata {
62        EmptyMetadata
63    }
64
65    fn segment_ids(_layout: &Self::Layout) -> Vec<SegmentId> {
66        vec![]
67    }
68
69    fn nchildren(layout: &Self::Layout) -> usize {
70        let validity_children = if layout.dtype.is_nullable() { 1 } else { 0 };
71        layout.struct_fields().nfields() + validity_children
72    }
73
74    fn child(layout: &Self::Layout, index: usize) -> VortexResult<LayoutRef> {
75        let schema_index = if layout.dtype.is_nullable() {
76            index.saturating_sub(1)
77        } else {
78            index
79        };
80
81        let child_dtype = if index == 0 && layout.dtype.is_nullable() {
82            DType::Bool(Nullability::NonNullable)
83        } else {
84            layout
85                .struct_fields()
86                .field_by_index(schema_index)
87                .ok_or_else(|| vortex_err!("Missing field {schema_index}"))?
88        };
89
90        layout.children.child(index, &child_dtype)
91    }
92
93    fn child_type(layout: &Self::Layout, idx: usize) -> LayoutChildType {
94        let schema_index = if layout.dtype.is_nullable() {
95            idx.saturating_sub(1)
96        } else {
97            idx
98        };
99
100        if idx == 0 && layout.dtype.is_nullable() {
101            LayoutChildType::Auxiliary("validity".into())
102        } else {
103            LayoutChildType::Field(
104                layout
105                    .struct_fields()
106                    .field_name(schema_index)
107                    .vortex_expect("Field index out of bounds")
108                    .clone(),
109            )
110        }
111    }
112
113    fn new_reader(
114        layout: &Self::Layout,
115        name: Arc<str>,
116        segment_source: Arc<dyn SegmentSource>,
117        session: &VortexSession,
118    ) -> VortexResult<LayoutReaderRef> {
119        Ok(Arc::new(StructReader::try_new(
120            layout.clone(),
121            name,
122            segment_source,
123            session.session(),
124        )?))
125    }
126
127    #[cfg(gpu_unstable)]
128    fn new_gpu_reader(
129        layout: &Self::Layout,
130        name: Arc<str>,
131        segment_source: Arc<dyn SegmentSource>,
132        ctx: Arc<cudarc::driver::CudaContext>,
133    ) -> VortexResult<crate::gpu::GpuLayoutReaderRef> {
134        Ok(Arc::new(
135            crate::gpu::layouts::struct_::GpuStructReader::try_new(
136                layout.clone(),
137                name,
138                segment_source,
139                ctx,
140            )?,
141        ))
142    }
143
144    fn build(
145        _encoding: &Self::Encoding,
146        dtype: &DType,
147        row_count: u64,
148        _metadata: &<Self::Metadata as DeserializeMetadata>::Output,
149        _segment_ids: Vec<SegmentId>,
150        children: &dyn LayoutChildren,
151        _ctx: ArrayContext,
152    ) -> VortexResult<Self::Layout> {
153        let struct_dt = dtype
154            .as_struct_fields_opt()
155            .ok_or_else(|| vortex_err!("Expected struct dtype"))?;
156
157        let expected_children = struct_dt.nfields() + (dtype.is_nullable() as usize);
158        vortex_ensure!(
159            children.nchildren() == expected_children,
160            "Struct layout has {} children, but dtype has {} fields",
161            children.nchildren(),
162            struct_dt.nfields()
163        );
164
165        Ok(StructLayout {
166            row_count,
167            dtype: dtype.clone(),
168            children: children.to_arc(),
169        })
170    }
171
172    fn with_children(layout: &mut Self::Layout, children: Vec<LayoutRef>) -> VortexResult<()> {
173        let struct_dt = layout
174            .dtype
175            .as_struct_fields_opt()
176            .ok_or_else(|| vortex_err!("Expected struct dtype"))?;
177
178        let expected_children = struct_dt.nfields() + (layout.dtype.is_nullable() as usize);
179        vortex_ensure!(
180            children.len() == expected_children,
181            "StructLayout expects {} children, got {}",
182            expected_children,
183            children.len()
184        );
185
186        layout.children = OwnedLayoutChildren::layout_children(children);
187        Ok(())
188    }
189}
190
191#[derive(Debug)]
192pub struct StructLayoutEncoding;
193
194#[derive(Clone, Debug)]
195pub struct StructLayout {
196    row_count: u64,
197    dtype: DType,
198    children: Arc<dyn LayoutChildren>,
199}
200
201impl StructLayout {
202    pub fn new(row_count: u64, dtype: DType, children: Vec<LayoutRef>) -> Self {
203        Self {
204            row_count,
205            dtype,
206            children: OwnedLayoutChildren::layout_children(children),
207        }
208    }
209
210    pub fn struct_fields(&self) -> &StructFields {
211        self.dtype
212            .as_struct_fields_opt()
213            .vortex_expect("Struct layout dtype must be a struct")
214    }
215
216    #[inline]
217    pub fn row_count(&self) -> u64 {
218        self.row_count
219    }
220
221    #[inline]
222    pub fn children(&self) -> &Arc<dyn LayoutChildren> {
223        &self.children
224    }
225
226    pub fn matching_fields<F>(&self, field_mask: &[FieldMask], mut per_child: F) -> VortexResult<()>
227    where
228        F: FnMut(FieldMask, usize) -> VortexResult<()>,
229    {
230        // If the field mask contains an `All` fields, then enumerate all fields.
231        if field_mask.iter().any(|mask| mask.matches_all()) {
232            for idx in 0..self.struct_fields().nfields() {
233                per_child(FieldMask::All, idx)?;
234            }
235            return Ok(());
236        }
237
238        // Enumerate each field in the mask
239        for path in field_mask {
240            let Some(field) = path.starting_field()? else {
241                // skip fields not in mask
242                continue;
243            };
244            let Field::Name(field_name) = field else {
245                vortex_bail!("Expected field name, got {field:?}");
246            };
247            let idx = self
248                .struct_fields()
249                .find(field_name)
250                .ok_or_else(|| vortex_err!("Field not found: {field_name}"))?;
251
252            per_child(path.clone().step_into()?, idx)?;
253        }
254
255        Ok(())
256    }
257}