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::sync::Arc;
6
7use vortex_array::SerializeMetadata;
8use vortex_array::dtype::DType;
9use vortex_error::VortexResult;
10use vortex_session::VortexSession;
11
12use crate::DynLayout;
13use crate::Layout;
14use crate::LayoutBuildContext;
15use crate::LayoutChildType;
16use crate::LayoutChildren;
17use crate::LayoutDeserializeArgs;
18use crate::LayoutId;
19use crate::LayoutParts;
20use crate::LayoutReaderContext;
21use crate::LayoutReaderRef;
22use crate::segments::SegmentId;
23use crate::segments::SegmentSource;
24
25/// Shared, erased handle to a layout tree node.
26pub type LayoutRef = Arc<dyn DynLayout>;
27
28/// Layout-specific behavior for a typed [`Layout`].
29///
30/// Common serialized fields are stored by [`Layout`]. Implementations own only their
31/// layout-specific data, metadata codec, child typing, and reader construction.
32pub trait VTable: 'static + Clone + Send + Sync + Debug {
33 /// Layout-specific data.
34 type LayoutData: 'static + Clone + Send + Sync + Debug;
35 /// Serialized metadata type.
36 type Metadata: SerializeMetadata + vortex_array::DeserializeMetadata + Debug;
37
38 /// Returns the globally unique layout ID.
39 fn id(&self) -> LayoutId;
40
41 /// Returns the serializable metadata for a layout.
42 fn metadata(layout: &Layout<Self>) -> Self::Metadata;
43
44 /// Deserialize and validate layout-specific data.
45 fn deserialize(
46 &self,
47 args: &LayoutDeserializeArgs<'_>,
48 metadata: &<Self::Metadata as vortex_array::DeserializeMetadata>::Output,
49 ) -> VortexResult<Self::LayoutData>;
50
51 /// Construct a typed layout from deserialized common fields.
52 fn build(
53 vtable: &Self,
54 dtype: &DType,
55 row_count: u64,
56 metadata: &<Self::Metadata as vortex_array::DeserializeMetadata>::Output,
57 segment_ids: Vec<SegmentId>,
58 children: &dyn LayoutChildren,
59 build_ctx: &LayoutBuildContext<'_>,
60 ) -> VortexResult<Layout<Self>> {
61 let args = LayoutDeserializeArgs {
62 session: build_ctx.session,
63 array_read_ctx: build_ctx.array_read_ctx,
64 dtype,
65 row_count,
66 segment_ids,
67 children,
68 };
69 let data = vtable.deserialize(&args, metadata)?;
70 Ok(LayoutParts::new(
71 vtable.clone(),
72 dtype.clone(),
73 row_count,
74 args.segment_ids,
75 children.to_arc(),
76 data,
77 )
78 .into_typed())
79 }
80
81 /// Returns the number of logical child *slots* of this layout.
82 ///
83 /// Slots are fixed logical positions: a given child always occupies the same slot index
84 /// regardless of which optional siblings are present. A slot may be absent (see
85 /// [`slot_to_child`](VTable::slot_to_child)), in which case it has no corresponding serialized
86 /// child. The default implementation reports one slot per serialized child, i.e. every slot is
87 /// always present.
88 fn nslots(layout: &Layout<Self>) -> usize {
89 layout.nchildren()
90 }
91
92 /// Maps a logical `slot` to the index of its serialized (dense) child, or `None` if the slot
93 /// is absent for this layout instance.
94 ///
95 /// Serialized children are stored densely (present-only), so an absent slot shifts the dense
96 /// indices of the slots that follow it. This mapping centralizes that arithmetic; the default
97 /// implementation is the identity, treating slot indices and dense child indices as equal.
98 fn slot_to_child(layout: &Layout<Self>, slot: usize) -> Option<usize> {
99 (slot < Self::nslots(layout)).then_some(slot)
100 }
101
102 /// Returns the expected dtype of the child in logical `slot`.
103 fn child_dtype(layout: &Layout<Self>, slot: usize) -> VortexResult<DType>;
104
105 /// Returns the relationship between the child in logical `slot` and its parent.
106 fn child_type(layout: &Layout<Self>, slot: usize) -> LayoutChildType;
107
108 /// Construct a reader for this layout.
109 fn new_reader(
110 layout: &Layout<Self>,
111 name: Arc<str>,
112 segment_source: Arc<dyn SegmentSource>,
113 session: &VortexSession,
114 ctx: &LayoutReaderContext,
115 ) -> VortexResult<LayoutReaderRef>;
116
117 /// Returns `true` if this layout is indivisible: its readers never register natural split
118 /// boundaries strictly inside their row range (see [`crate::LayoutReader::register_splits`]).
119 ///
120 /// Indivisible layouts — like flat, whose readers only ever push the end of the requested
121 /// range — let parent layouts skip materializing the child entirely during split collection.
122 fn is_indivisible(&self) -> bool {
123 false
124 }
125}