Skip to main content

vortex_layout/
children.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use std::fmt::Debug;
5use std::fmt::Formatter;
6use std::sync::Arc;
7
8use flatbuffers::Follow;
9use itertools::Itertools;
10use once_cell::sync::OnceCell;
11use vortex_array::dtype::DType;
12use vortex_error::VortexResult;
13use vortex_error::vortex_bail;
14use vortex_error::vortex_err;
15use vortex_flatbuffers::FlatBuffer;
16use vortex_flatbuffers::layout as fbl;
17use vortex_session::VortexSession;
18use vortex_session::registry::ReadContext;
19
20use crate::LayoutBuildContext;
21use crate::LayoutRef;
22use crate::layouts::foreign::new_foreign_layout;
23use crate::segments::SegmentId;
24use crate::session::LayoutRegistry;
25
26/// Abstract way of accessing the children of a layout.
27///
28/// This allows us to abstract over the lazy flatbuffer-based layouts, as well as the in-memory
29/// layout trees.
30pub trait LayoutChildren: 'static + Send + Sync {
31    fn to_arc(&self) -> Arc<dyn LayoutChildren>;
32
33    fn child(&self, idx: usize, dtype: &DType) -> VortexResult<LayoutRef>;
34
35    fn child_row_count(&self, idx: usize) -> u64;
36
37    fn nchildren(&self) -> usize;
38}
39
40impl Debug for dyn LayoutChildren {
41    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
42        f.debug_struct("LayoutChildren")
43            .field("nchildren", &self.nchildren())
44            .finish()
45    }
46}
47
48impl LayoutChildren for Arc<dyn LayoutChildren> {
49    fn to_arc(&self) -> Arc<dyn LayoutChildren> {
50        Arc::clone(self)
51    }
52
53    fn child(&self, idx: usize, dtype: &DType) -> VortexResult<LayoutRef> {
54        self.as_ref().child(idx, dtype)
55    }
56
57    fn child_row_count(&self, idx: usize) -> u64 {
58        self.as_ref().child_row_count(idx)
59    }
60
61    fn nchildren(&self) -> usize {
62        self.as_ref().nchildren()
63    }
64}
65
66/// An implementation of [`LayoutChildren`] for in-memory owned children.
67#[derive(Clone)]
68pub(crate) struct OwnedLayoutChildren(Vec<LayoutRef>);
69
70impl OwnedLayoutChildren {
71    pub fn layout_children(children: Vec<LayoutRef>) -> Arc<dyn LayoutChildren> {
72        Arc::new(Self(children))
73    }
74}
75
76/// In-memory implementation of [`LayoutChildren`].
77impl LayoutChildren for OwnedLayoutChildren {
78    fn to_arc(&self) -> Arc<dyn LayoutChildren> {
79        Arc::new(self.clone())
80    }
81
82    fn child(&self, idx: usize, dtype: &DType) -> VortexResult<LayoutRef> {
83        if idx >= self.0.len() {
84            vortex_bail!("Child index out of bounds: {} of {}", idx, self.0.len());
85        }
86        let child = &self.0[idx];
87        if child.dtype() != dtype {
88            vortex_bail!("Child dtype mismatch: {} != {}", child.dtype(), dtype);
89        }
90        Ok(Arc::clone(child))
91    }
92
93    fn child_row_count(&self, idx: usize) -> u64 {
94        self.0[idx].row_count()
95    }
96
97    fn nchildren(&self) -> usize {
98        self.0.len()
99    }
100}
101
102#[derive(Clone)]
103pub(crate) struct ViewedLayoutChildren {
104    flatbuffer: FlatBuffer,
105    flatbuffer_loc: usize,
106    array_read_ctx: ReadContext,
107    layout_read_ctx: ReadContext,
108    layouts: LayoutRegistry,
109    allow_unknown: bool,
110    session: VortexSession,
111    cache: Arc<[OnceCell<LayoutRef>]>,
112}
113
114impl ViewedLayoutChildren {
115    /// Create a new [`ViewedLayoutChildren`] from the given parameters.
116    ///
117    /// # Safety
118    ///
119    /// Assumes the flatbuffer is validated and that the `flatbuffer_loc` is the correct offset
120    pub(super) unsafe fn new_unchecked(
121        flatbuffer: FlatBuffer,
122        flatbuffer_loc: usize,
123        array_read_ctx: ReadContext,
124        layout_read_ctx: ReadContext,
125        layouts: LayoutRegistry,
126        allow_unknown: bool,
127        session: VortexSession,
128    ) -> Self {
129        // SAFETY: guaranteed by caller
130        let nchildren = unsafe { fbl::Layout::follow(flatbuffer.as_ref(), flatbuffer_loc) }
131            .children()
132            .unwrap_or_default()
133            .len();
134        let cache = vec![OnceCell::new(); nchildren].into_boxed_slice().into();
135        Self {
136            flatbuffer,
137            flatbuffer_loc,
138            array_read_ctx,
139            layout_read_ctx,
140            layouts,
141            allow_unknown,
142            session,
143            cache,
144        }
145    }
146
147    /// Return the flatbuffer layout message.
148    fn flatbuffer(&self) -> fbl::Layout<'_> {
149        // SAFETY: flatbuffer_loc is guaranteed to be a valid offset into the flatbuffer
150        // as it was constructed from a validated flatbuffer in ViewedLayoutChildren::try_new.
151        // The lifetime of the returned Layout is tied to self, ensuring the buffer remains valid.
152        unsafe { fbl::Layout::follow(self.flatbuffer.as_ref(), self.flatbuffer_loc) }
153    }
154
155    fn foreign_layout_from_fb(
156        &self,
157        fb_layout: fbl::Layout<'_>,
158        dtype: &DType,
159    ) -> VortexResult<LayoutRef> {
160        let encoding_id = self
161            .layout_read_ctx
162            .resolve(fb_layout.encoding())
163            .ok_or_else(|| vortex_err!("Encoding not found: {}", fb_layout.encoding()))?;
164
165        let children = fb_layout
166            .children()
167            .unwrap_or_default()
168            .iter()
169            .map(|child| self.foreign_layout_from_fb(child, dtype))
170            .collect::<VortexResult<Vec<_>>>()?;
171
172        Ok(new_foreign_layout(
173            encoding_id,
174            dtype.clone(),
175            fb_layout.row_count(),
176            fb_layout
177                .metadata()
178                .map(|m| m.bytes().to_vec())
179                .unwrap_or_default(),
180            fb_layout
181                .segments()
182                .unwrap_or_default()
183                .iter()
184                .map(SegmentId::from)
185                .collect_vec(),
186            children,
187        ))
188    }
189}
190
191impl LayoutChildren for ViewedLayoutChildren {
192    fn to_arc(&self) -> Arc<dyn LayoutChildren> {
193        Arc::new(self.clone())
194    }
195
196    fn child(&self, idx: usize, dtype: &DType) -> VortexResult<LayoutRef> {
197        if idx >= self.nchildren() {
198            vortex_bail!("Child index out of bounds: {} of {}", idx, self.nchildren());
199        }
200
201        let layout_ref = self.cache[idx].get_or_try_init(|| {
202            let fb_child = self.flatbuffer().children().unwrap_or_default().get(idx);
203
204            // SAFETY: same validated flatbuffer; fb_child._tab.loc() is a valid offset
205            // We need this to avoid re-initializing cache here
206            let viewed_children = unsafe {
207                ViewedLayoutChildren::new_unchecked(
208                    self.flatbuffer.clone(),
209                    fb_child._tab.loc(),
210                    self.array_read_ctx.clone(),
211                    self.layout_read_ctx.clone(),
212                    self.layouts.clone(),
213                    self.allow_unknown,
214                    self.session.clone(),
215                )
216            };
217
218            let encoding_id = self
219                .layout_read_ctx
220                .resolve(fb_child.encoding())
221                .ok_or_else(|| vortex_err!("Encoding not found: {}", fb_child.encoding()))?;
222            let Some(encoding) = self.layouts.find(&encoding_id) else {
223                if self.allow_unknown {
224                    return viewed_children.foreign_layout_from_fb(fb_child, dtype);
225                }
226                return Err(vortex_err!(
227                    "Encoding not found in registry: {}",
228                    fb_child.encoding()
229                ));
230            };
231
232            let build_ctx = LayoutBuildContext {
233                session: &self.session,
234                array_read_ctx: &self.array_read_ctx,
235            };
236            encoding.build(
237                dtype,
238                fb_child.row_count(),
239                fb_child
240                    .metadata()
241                    .map(|m| m.bytes())
242                    .unwrap_or_else(|| &[]),
243                fb_child
244                    .segments()
245                    .unwrap_or_default()
246                    .iter()
247                    .map(SegmentId::from)
248                    .collect_vec(),
249                &viewed_children,
250                &build_ctx,
251            )
252        })?;
253        Ok(Arc::clone(layout_ref))
254    }
255
256    fn child_row_count(&self, idx: usize) -> u64 {
257        // Efficiently get the row count of the child at the given index, without a full
258        // deserialization.
259        self.flatbuffer()
260            .children()
261            .unwrap_or_default()
262            .get(idx)
263            .row_count()
264    }
265
266    fn nchildren(&self) -> usize {
267        self.cache.len()
268    }
269}