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}