Skip to main content

vortex_layout/
vtable.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use std::fmt::Debug;
5use std::ops::Deref;
6use std::sync::Arc;
7
8use vortex_array::DeserializeMetadata;
9use vortex_array::SerializeMetadata;
10use vortex_array::dtype::DType;
11use vortex_error::VortexResult;
12use vortex_error::vortex_bail;
13use vortex_session::VortexSession;
14use vortex_session::registry::ReadContext;
15
16use crate::IntoLayout;
17use crate::Layout;
18use crate::LayoutChildType;
19use crate::LayoutEncoding;
20use crate::LayoutEncodingRef;
21use crate::LayoutId;
22use crate::LayoutReaderContext;
23use crate::LayoutReaderRef;
24use crate::LayoutRef;
25use crate::children::LayoutChildren;
26use crate::segments::SegmentId;
27use crate::segments::SegmentSource;
28
29/// Context available while constructing a layout from serialized metadata.
30pub struct LayoutBuildContext<'a> {
31    /// The session used to resolve plugin-owned metadata such as aggregate function options.
32    pub session: &'a VortexSession,
33    /// The array read context referenced by serialized array metadata in descendant layouts.
34    pub array_read_ctx: &'a ReadContext,
35}
36
37/// Typed implementation contract for a layout encoding.
38///
39/// A layout vtable connects a concrete layout node type, its registered encoding object, and the
40/// metadata representation used for serialization. The object-safe [`Layout`] and
41/// [`LayoutEncoding`] APIs delegate to this trait through adapters.
42pub trait VTable: 'static + Sized + Send + Sync + Debug {
43    /// Concrete layout node type for this encoding.
44    type Layout: 'static + Send + Sync + Clone + Debug + Deref<Target = dyn Layout> + IntoLayout;
45    /// Concrete encoding object registered in the session.
46    type Encoding: 'static + Send + Sync + Deref<Target = dyn LayoutEncoding>;
47    /// Serialized layout metadata type.
48    type Metadata: SerializeMetadata + DeserializeMetadata + Debug;
49
50    /// Returns the ID of the layout encoding.
51    fn id(encoding: &Self::Encoding) -> LayoutId;
52
53    /// Returns the encoding for the layout.
54    fn encoding(layout: &Self::Layout) -> LayoutEncodingRef;
55
56    /// Returns the row count for the layout.
57    fn row_count(layout: &Self::Layout) -> u64;
58
59    /// Returns the dtype for the layout reader.
60    fn dtype(layout: &Self::Layout) -> &DType;
61
62    /// Returns the metadata for the layout.
63    fn metadata(layout: &Self::Layout) -> Self::Metadata;
64
65    /// Returns the segment IDs for the layout.
66    fn segment_ids(layout: &Self::Layout) -> Vec<SegmentId>;
67
68    /// Returns the number of children for the layout.
69    fn nchildren(layout: &Self::Layout) -> usize;
70
71    /// Return the child at the given index.
72    fn child(layout: &Self::Layout, idx: usize) -> VortexResult<LayoutRef>;
73
74    /// Return the type of the child at the given index.
75    fn child_type(layout: &Self::Layout, idx: usize) -> LayoutChildType;
76
77    /// Create a new reader for the layout.
78    ///
79    /// **Layouts with children MUST propagate `ctx` to descendants** by passing it
80    /// through `Layout::new_reader` (or `LazyReaderChildren::new`) when constructing
81    /// child readers. If `ctx` is dropped at any link in the chain, ancestor-published
82    /// values won't reach affected descendants — a silent runtime regression for any
83    /// descendant that looked up an ancestor-published value via `ctx.get::<T>()`.
84    /// There is no compile-time check that catches this; reviewer discipline + the
85    /// integration tests in `vortex-layout` are the only safety net.
86    fn new_reader(
87        layout: &Self::Layout,
88        name: Arc<str>,
89        segment_source: Arc<dyn SegmentSource>,
90        session: &VortexSession,
91        ctx: &LayoutReaderContext,
92    ) -> VortexResult<LayoutReaderRef>;
93
94    /// Construct a new [`Layout`] from deserialized parts.
95    ///
96    /// Implementations should validate child count, child types, row counts, segment references,
97    /// and dtype consistency for their encoding. The generic adapter checks the returned layout's
98    /// top-level dtype and row count, but encoding-specific invariants belong here.
99    fn build(
100        encoding: &Self::Encoding,
101        dtype: &DType,
102        row_count: u64,
103        metadata: &<Self::Metadata as DeserializeMetadata>::Output,
104        segment_ids: Vec<SegmentId>,
105        children: &dyn LayoutChildren,
106        build_ctx: &LayoutBuildContext<'_>,
107    ) -> VortexResult<Self::Layout>;
108
109    /// Replaces the children of the layout with the given layout references.
110    ///
111    /// The count and types of children must match the layout's requirements.
112    /// This method is used for transforming layout trees by replacing child layouts.
113    fn with_children(_layout: &mut Self::Layout, _children: Vec<LayoutRef>) -> VortexResult<()> {
114        vortex_bail!("with_children not implemented for this layout")
115    }
116}
117
118#[macro_export]
119macro_rules! vtable {
120    ($V:ident) => {
121        $crate::aliases::paste::paste! {
122            #[derive(Debug)]
123            pub struct $V;
124
125            impl AsRef<dyn $crate::Layout> for [<$V Layout>] {
126                fn as_ref(&self) -> &dyn $crate::Layout {
127                    // SAFETY: LayoutAdapter is #[repr(transparent)] over the Layout type,
128                    // which guarantees identical memory layout. This cast is safe because
129                    // we're only changing the type metadata, not the actual data.
130                    unsafe { &*(self as *const [<$V Layout>] as *const $crate::LayoutAdapter<$V>) }
131                }
132            }
133
134            impl std::ops::Deref for [<$V Layout>] {
135                type Target = dyn $crate::Layout;
136
137                fn deref(&self) -> &Self::Target {
138                    // SAFETY: LayoutAdapter is #[repr(transparent)] over the Layout type,
139                    // which guarantees identical memory layout. This cast is safe because
140                    // we're only changing the type metadata, not the actual data.
141                    unsafe { &*(self as *const [<$V Layout>] as *const $crate::LayoutAdapter<$V>) }
142                }
143            }
144
145            impl $crate::IntoLayout for [<$V Layout>] {
146                fn into_layout(self) -> $crate::LayoutRef {
147                    // SAFETY: LayoutAdapter is #[repr(transparent)] over the Layout type,
148                    // guaranteeing identical memory layout and alignment. The transmute is safe
149                    // because both types have the same size and representation.
150                    std::sync::Arc::new(unsafe { std::mem::transmute::<[<$V Layout>], $crate::LayoutAdapter::<$V>>(self) })
151                }
152            }
153
154            impl From<[<$V Layout>]> for $crate::LayoutRef {
155                fn from(value: [<$V Layout>]) -> $crate::LayoutRef {
156                    use $crate::IntoLayout;
157                    value.into_layout()
158                }
159            }
160
161            impl AsRef<dyn $crate::LayoutEncoding> for [<$V LayoutEncoding>] {
162                fn as_ref(&self) -> &dyn $crate::LayoutEncoding {
163                    // SAFETY: LayoutEncodingAdapter is #[repr(transparent)] over the LayoutEncoding type,
164                    // which guarantees identical memory layout. This cast is safe because
165                    // we're only changing the type metadata, not the actual data.
166                    unsafe { &*(self as *const [<$V LayoutEncoding>] as *const $crate::LayoutEncodingAdapter<$V>) }
167                }
168            }
169
170            impl std::ops::Deref for [<$V LayoutEncoding>] {
171                type Target = dyn $crate::LayoutEncoding;
172
173                fn deref(&self) -> &Self::Target {
174                    // SAFETY: LayoutEncodingAdapter is #[repr(transparent)] over the LayoutEncoding type,
175                    // which guarantees identical memory layout. This cast is safe because
176                    // we're only changing the type metadata, not the actual data.
177                    unsafe { &*(self as *const [<$V LayoutEncoding>] as *const $crate::LayoutEncodingAdapter<$V>) }
178                }
179            }
180        }
181    };
182}