vortex_layout/layouts/list/
mod.rs1mod expr;
7mod reader;
8pub mod writer;
9
10use std::sync::Arc;
11
12use reader::ListReader;
13use vortex_array::ProstMetadata;
14use vortex_array::dtype::DType;
15use vortex_array::dtype::Nullability;
16use vortex_array::dtype::PType;
17use vortex_error::VortexExpect;
18use vortex_error::VortexResult;
19use vortex_error::vortex_bail;
20use vortex_error::vortex_ensure_eq;
21use vortex_error::vortex_err;
22use vortex_error::vortex_panic;
23use vortex_session::VortexSession;
24use vortex_session::registry::CachedId;
25
26use crate::Layout;
27use crate::LayoutChildType;
28use crate::LayoutDeserializeArgs;
29use crate::LayoutId;
30use crate::LayoutParts;
31use crate::LayoutReaderContext;
32use crate::LayoutReaderRef;
33use crate::LayoutRef;
34use crate::VTable;
35use crate::children::OwnedLayoutChildren;
36use crate::segments::SegmentSource;
37
38pub const ELEMENTS_CHILD_INDEX: usize = 0;
40pub const OFFSETS_CHILD_INDEX: usize = 1;
42pub const VALIDITY_CHILD_INDEX: usize = 2;
44pub const NUM_CHILDREN_NON_NULLABLE: usize = 2;
46
47#[derive(Clone, Debug)]
49pub struct List;
50
51pub use List as ListLayoutEncoding;
53
54#[derive(Clone, Debug)]
56pub struct ListData {
57 offsets_ptype: PType,
58}
59
60pub type ListLayout = Layout<List>;
62
63impl VTable for List {
64 type LayoutData = ListData;
65 type Metadata = ProstMetadata<ListLayoutMetadata>;
66
67 fn id(&self) -> LayoutId {
68 static ID: CachedId = CachedId::new("vortex.list");
69 *ID
70 }
71
72 fn metadata(layout: &Layout<Self>) -> Self::Metadata {
73 ProstMetadata(ListLayoutMetadata::new(layout.offsets_ptype))
74 }
75
76 fn deserialize(
77 &self,
78 args: &LayoutDeserializeArgs<'_>,
79 metadata: &ListLayoutMetadata,
80 ) -> VortexResult<Self::LayoutData> {
81 ListLayout::validate_children(args.dtype, args.children.nchildren())?;
82 let elements_dtype = args
83 .dtype
84 .as_list_element_opt()
85 .ok_or_else(|| vortex_err!("ListLayout requires a List dtype, got {}", args.dtype))?;
86 args.children.child(ELEMENTS_CHILD_INDEX, elements_dtype)?;
87 let offsets_dtype = DType::Primitive(metadata.offsets_ptype(), Nullability::NonNullable);
88 let offsets = args.children.child(OFFSETS_CHILD_INDEX, &offsets_dtype)?;
89 vortex_ensure_eq!(
90 offsets.row_count().saturating_sub(1),
91 args.row_count,
92 "List offsets row count does not match parent"
93 );
94 if args.dtype.is_nullable() {
95 let validity = args
96 .children
97 .child(VALIDITY_CHILD_INDEX, &DType::Bool(Nullability::NonNullable))?;
98 vortex_ensure_eq!(
99 validity.row_count(),
100 args.row_count,
101 "List validity row count does not match parent"
102 );
103 }
104 Ok(ListData {
105 offsets_ptype: metadata.offsets_ptype(),
106 })
107 }
108
109 fn nslots(_layout: &Layout<Self>) -> usize {
110 VALIDITY_CHILD_INDEX + 1
112 }
113
114 fn slot_to_child(layout: &Layout<Self>, slot: usize) -> Option<usize> {
115 match slot {
116 ELEMENTS_CHILD_INDEX | OFFSETS_CHILD_INDEX => Some(slot),
117 VALIDITY_CHILD_INDEX => layout.dtype().is_nullable().then_some(VALIDITY_CHILD_INDEX),
118 _ => None,
119 }
120 }
121
122 fn child_dtype(layout: &Layout<Self>, idx: usize) -> VortexResult<DType> {
123 match idx {
124 ELEMENTS_CHILD_INDEX => layout
125 .dtype()
126 .as_list_element_opt()
127 .map(|dtype| dtype.as_ref().clone())
128 .ok_or_else(|| vortex_err!("ListLayout requires a List dtype")),
129 OFFSETS_CHILD_INDEX => Ok(DType::Primitive(
130 layout.offsets_ptype,
131 Nullability::NonNullable,
132 )),
133 VALIDITY_CHILD_INDEX if layout.dtype().is_nullable() => {
134 Ok(DType::Bool(Nullability::NonNullable))
135 }
136 _ => vortex_bail!("Invalid child index {idx} for ListLayout"),
137 }
138 }
139
140 fn child_type(layout: &Layout<Self>, idx: usize) -> LayoutChildType {
141 match idx {
142 ELEMENTS_CHILD_INDEX => LayoutChildType::Auxiliary("elements".into()),
143 OFFSETS_CHILD_INDEX => LayoutChildType::Auxiliary("offsets".into()),
144 VALIDITY_CHILD_INDEX if layout.dtype().is_nullable() => {
145 LayoutChildType::Auxiliary("validity".into())
146 }
147 _ => vortex_panic!("Invalid child index {idx} for ListLayout"),
148 }
149 }
150
151 fn new_reader(
152 layout: &Layout<Self>,
153 name: Arc<str>,
154 segment_source: Arc<dyn SegmentSource>,
155 session: &VortexSession,
156 ctx: &LayoutReaderContext,
157 ) -> VortexResult<LayoutReaderRef> {
158 Ok(Arc::new(ListReader::try_new(
159 layout.clone(),
160 name,
161 segment_source,
162 session.clone(),
163 ctx,
164 )?))
165 }
166}
167
168impl Layout<List> {
169 pub fn new(
171 dtype: DType,
172 elements: LayoutRef,
173 offsets: LayoutRef,
174 validity: Option<LayoutRef>,
175 ) -> Self {
176 let row_count = offsets.row_count().saturating_sub(1);
177 let offsets_ptype = offsets.dtype().as_ptype();
178 let mut children = vec![elements, offsets];
179 children.extend(validity);
180 Self::validate_children(&dtype, children.len()).vortex_expect("invalid list children");
181 LayoutParts::new(
182 List,
183 dtype,
184 row_count,
185 Vec::new(),
186 OwnedLayoutChildren::layout_children(children),
187 ListData { offsets_ptype },
188 )
189 .into_typed()
190 }
191
192 pub fn elements(&self) -> VortexResult<LayoutRef> {
194 self.slot(ELEMENTS_CHILD_INDEX)?
195 .ok_or_else(|| vortex_err!("ListLayout elements slot is absent"))
196 }
197
198 pub fn offsets(&self) -> VortexResult<LayoutRef> {
200 self.slot(OFFSETS_CHILD_INDEX)?
201 .ok_or_else(|| vortex_err!("ListLayout offsets slot is absent"))
202 }
203
204 pub fn validity(&self) -> VortexResult<Option<LayoutRef>> {
206 self.slot(VALIDITY_CHILD_INDEX)
207 }
208
209 pub fn offsets_ptype(&self) -> PType {
211 self.offsets_ptype
212 }
213
214 pub fn elements_dtype(&self) -> &DType {
216 self.dtype()
217 .as_list_element_opt()
218 .vortex_expect("ListLayout dtype must be a List")
219 }
220
221 fn validate_children(dtype: &DType, nchildren: usize) -> VortexResult<()> {
222 let expected = NUM_CHILDREN_NON_NULLABLE + usize::from(dtype.is_nullable());
223 vortex_ensure_eq!(nchildren, expected);
224 Ok(())
225 }
226}
227
228#[derive(prost::Message)]
229pub struct ListLayoutMetadata {
230 #[prost(enumeration = "PType", tag = "1")]
231 offsets_ptype: i32,
232}
233
234impl ListLayoutMetadata {
235 pub fn new(offsets_ptype: PType) -> Self {
236 let mut metadata = Self::default();
237 metadata.set_offsets_ptype(offsets_ptype);
238 metadata
239 }
240}