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(|| {
222                    vortex_err!("Unknown layout encoding index: {}", fb_child.encoding())
223                })?;
224            let Some(encoding) = self.layouts.find(&encoding_id) else {
225                if self.allow_unknown {
226                    return viewed_children.foreign_layout_from_fb(fb_child, dtype);
227                }
228                vortex_bail!("Unknown layout encoding: {encoding_id}");
229            };
230
231            let build_ctx = LayoutBuildContext {
232                session: &self.session,
233                array_read_ctx: &self.array_read_ctx,
234            };
235            encoding.build(
236                dtype,
237                fb_child.row_count(),
238                fb_child
239                    .metadata()
240                    .map(|m| m.bytes())
241                    .unwrap_or_else(|| &[]),
242                fb_child
243                    .segments()
244                    .unwrap_or_default()
245                    .iter()
246                    .map(SegmentId::from)
247                    .collect_vec(),
248                &viewed_children,
249                &build_ctx,
250            )
251        })?;
252        Ok(Arc::clone(layout_ref))
253    }
254
255    fn child_row_count(&self, idx: usize) -> u64 {
256        // Efficiently get the row count of the child at the given index, without a full
257        // deserialization.
258        self.flatbuffer()
259            .children()
260            .unwrap_or_default()
261            .get(idx)
262            .row_count()
263    }
264
265    fn nchildren(&self) -> usize {
266        self.cache.len()
267    }
268}