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_error::vortex_ensure!(
90 offsets.row_count().saturating_sub(1) == args.row_count,
91 "List offsets row count does not match parent"
92 );
93 if args.dtype.is_nullable() {
94 let validity = args
95 .children
96 .child(VALIDITY_CHILD_INDEX, &DType::Bool(Nullability::NonNullable))?;
97 vortex_error::vortex_ensure!(
98 validity.row_count() == args.row_count,
99 "List validity row count does not match parent"
100 );
101 }
102 Ok(ListData {
103 offsets_ptype: metadata.offsets_ptype(),
104 })
105 }
106
107 fn child_dtype(layout: &Layout<Self>, idx: usize) -> VortexResult<DType> {
108 match idx {
109 ELEMENTS_CHILD_INDEX => layout
110 .dtype()
111 .as_list_element_opt()
112 .map(|dtype| dtype.as_ref().clone())
113 .ok_or_else(|| vortex_err!("ListLayout requires a List dtype")),
114 OFFSETS_CHILD_INDEX => Ok(DType::Primitive(
115 layout.offsets_ptype,
116 Nullability::NonNullable,
117 )),
118 VALIDITY_CHILD_INDEX if layout.dtype().is_nullable() => {
119 Ok(DType::Bool(Nullability::NonNullable))
120 }
121 _ => vortex_bail!("Invalid child index {idx} for ListLayout"),
122 }
123 }
124
125 fn child_type(layout: &Layout<Self>, idx: usize) -> LayoutChildType {
126 match idx {
127 ELEMENTS_CHILD_INDEX => LayoutChildType::Auxiliary("elements".into()),
128 OFFSETS_CHILD_INDEX => LayoutChildType::Auxiliary("offsets".into()),
129 VALIDITY_CHILD_INDEX if layout.dtype().is_nullable() => {
130 LayoutChildType::Auxiliary("validity".into())
131 }
132 _ => vortex_panic!("Invalid child index {idx} for ListLayout"),
133 }
134 }
135
136 fn new_reader(
137 layout: &Layout<Self>,
138 name: Arc<str>,
139 segment_source: Arc<dyn SegmentSource>,
140 session: &VortexSession,
141 ctx: &LayoutReaderContext,
142 ) -> VortexResult<LayoutReaderRef> {
143 Ok(Arc::new(ListReader::try_new(
144 layout.clone(),
145 name,
146 segment_source,
147 session.clone(),
148 ctx,
149 )?))
150 }
151}
152
153impl Layout<List> {
154 pub fn new(
156 dtype: DType,
157 elements: LayoutRef,
158 offsets: LayoutRef,
159 validity: Option<LayoutRef>,
160 ) -> Self {
161 let row_count = offsets.row_count().saturating_sub(1);
162 let offsets_ptype = offsets.dtype().as_ptype();
163 let mut children = vec![elements, offsets];
164 children.extend(validity);
165 Self::validate_children(&dtype, children.len()).vortex_expect("invalid list children");
166 LayoutParts::new(
167 List,
168 dtype,
169 row_count,
170 Vec::new(),
171 OwnedLayoutChildren::layout_children(children),
172 ListData { offsets_ptype },
173 )
174 .into_typed()
175 }
176
177 pub fn elements(&self) -> VortexResult<LayoutRef> {
179 self.child(ELEMENTS_CHILD_INDEX)
180 }
181
182 pub fn offsets(&self) -> VortexResult<LayoutRef> {
184 self.child(OFFSETS_CHILD_INDEX)
185 }
186
187 pub fn validity(&self) -> VortexResult<Option<LayoutRef>> {
189 self.dtype()
190 .is_nullable()
191 .then(|| self.child(VALIDITY_CHILD_INDEX))
192 .transpose()
193 }
194
195 pub fn offsets_ptype(&self) -> PType {
197 self.offsets_ptype
198 }
199
200 pub fn elements_dtype(&self) -> &DType {
202 self.dtype()
203 .as_list_element_opt()
204 .vortex_expect("ListLayout dtype must be a List")
205 }
206
207 fn validate_children(dtype: &DType, nchildren: usize) -> VortexResult<()> {
208 let expected = NUM_CHILDREN_NON_NULLABLE + usize::from(dtype.is_nullable());
209 vortex_ensure_eq!(nchildren, expected);
210 Ok(())
211 }
212}
213
214#[derive(prost::Message)]
215pub struct ListLayoutMetadata {
216 #[prost(enumeration = "PType", tag = "1")]
217 offsets_ptype: i32,
218}
219
220impl ListLayoutMetadata {
221 pub fn new(offsets_ptype: PType) -> Self {
222 let mut metadata = Self::default();
223 metadata.set_offsets_ptype(offsets_ptype);
224 metadata
225 }
226}