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 nslots(_layout: &Layout<Self>) -> usize {
108 VALIDITY_CHILD_INDEX + 1
110 }
111
112 fn slot_to_child(layout: &Layout<Self>, slot: usize) -> Option<usize> {
113 match slot {
114 ELEMENTS_CHILD_INDEX | OFFSETS_CHILD_INDEX => Some(slot),
115 VALIDITY_CHILD_INDEX => layout.dtype().is_nullable().then_some(VALIDITY_CHILD_INDEX),
116 _ => None,
117 }
118 }
119
120 fn child_dtype(layout: &Layout<Self>, idx: usize) -> VortexResult<DType> {
121 match idx {
122 ELEMENTS_CHILD_INDEX => layout
123 .dtype()
124 .as_list_element_opt()
125 .map(|dtype| dtype.as_ref().clone())
126 .ok_or_else(|| vortex_err!("ListLayout requires a List dtype")),
127 OFFSETS_CHILD_INDEX => Ok(DType::Primitive(
128 layout.offsets_ptype,
129 Nullability::NonNullable,
130 )),
131 VALIDITY_CHILD_INDEX if layout.dtype().is_nullable() => {
132 Ok(DType::Bool(Nullability::NonNullable))
133 }
134 _ => vortex_bail!("Invalid child index {idx} for ListLayout"),
135 }
136 }
137
138 fn child_type(layout: &Layout<Self>, idx: usize) -> LayoutChildType {
139 match idx {
140 ELEMENTS_CHILD_INDEX => LayoutChildType::Auxiliary("elements".into()),
141 OFFSETS_CHILD_INDEX => LayoutChildType::Auxiliary("offsets".into()),
142 VALIDITY_CHILD_INDEX if layout.dtype().is_nullable() => {
143 LayoutChildType::Auxiliary("validity".into())
144 }
145 _ => vortex_panic!("Invalid child index {idx} for ListLayout"),
146 }
147 }
148
149 fn new_reader(
150 layout: &Layout<Self>,
151 name: Arc<str>,
152 segment_source: Arc<dyn SegmentSource>,
153 session: &VortexSession,
154 ctx: &LayoutReaderContext,
155 ) -> VortexResult<LayoutReaderRef> {
156 Ok(Arc::new(ListReader::try_new(
157 layout.clone(),
158 name,
159 segment_source,
160 session.clone(),
161 ctx,
162 )?))
163 }
164}
165
166impl Layout<List> {
167 pub fn new(
169 dtype: DType,
170 elements: LayoutRef,
171 offsets: LayoutRef,
172 validity: Option<LayoutRef>,
173 ) -> Self {
174 let row_count = offsets.row_count().saturating_sub(1);
175 let offsets_ptype = offsets.dtype().as_ptype();
176 let mut children = vec![elements, offsets];
177 children.extend(validity);
178 Self::validate_children(&dtype, children.len()).vortex_expect("invalid list children");
179 LayoutParts::new(
180 List,
181 dtype,
182 row_count,
183 Vec::new(),
184 OwnedLayoutChildren::layout_children(children),
185 ListData { offsets_ptype },
186 )
187 .into_typed()
188 }
189
190 pub fn elements(&self) -> VortexResult<LayoutRef> {
192 self.slot(ELEMENTS_CHILD_INDEX)?
193 .ok_or_else(|| vortex_err!("ListLayout elements slot is absent"))
194 }
195
196 pub fn offsets(&self) -> VortexResult<LayoutRef> {
198 self.slot(OFFSETS_CHILD_INDEX)?
199 .ok_or_else(|| vortex_err!("ListLayout offsets slot is absent"))
200 }
201
202 pub fn validity(&self) -> VortexResult<Option<LayoutRef>> {
204 self.slot(VALIDITY_CHILD_INDEX)
205 }
206
207 pub fn offsets_ptype(&self) -> PType {
209 self.offsets_ptype
210 }
211
212 pub fn elements_dtype(&self) -> &DType {
214 self.dtype()
215 .as_list_element_opt()
216 .vortex_expect("ListLayout dtype must be a List")
217 }
218
219 fn validate_children(dtype: &DType, nchildren: usize) -> VortexResult<()> {
220 let expected = NUM_CHILDREN_NON_NULLABLE + usize::from(dtype.is_nullable());
221 vortex_ensure_eq!(nchildren, expected);
222 Ok(())
223 }
224}
225
226#[derive(prost::Message)]
227pub struct ListLayoutMetadata {
228 #[prost(enumeration = "PType", tag = "1")]
229 offsets_ptype: i32,
230}
231
232impl ListLayoutMetadata {
233 pub fn new(offsets_ptype: PType) -> Self {
234 let mut metadata = Self::default();
235 metadata.set_offsets_ptype(offsets_ptype);
236 metadata
237 }
238}