1use std::any::Any;
5use std::fmt::Debug;
6use std::fmt::Display;
7use std::fmt::Formatter;
8use std::sync::Arc;
9
10use itertools::Itertools;
11use vortex_array::SerializeMetadata;
12use vortex_array::dtype::DType;
13use vortex_array::dtype::FieldName;
14use vortex_error::VortexExpect;
15use vortex_error::VortexResult;
16use vortex_error::vortex_err;
17use vortex_session::VortexSession;
18use vortex_session::registry::Id;
19
20use crate::LayoutEncodingId;
21use crate::LayoutEncodingRef;
22use crate::LayoutReaderContext;
23use crate::LayoutReaderRef;
24use crate::VTable;
25use crate::display::DisplayLayoutTree;
26use crate::display::display_tree_with_segment_sizes;
27use crate::segments::SegmentId;
28use crate::segments::SegmentSource;
29
30pub type LayoutId = Id;
32
33pub type LayoutRef = Arc<dyn Layout>;
35
36pub trait Layout: 'static + Send + Sync + Debug + private::Sealed {
42 fn as_any(&self) -> &dyn Any;
44
45 fn as_any_arc(self: Arc<Self>) -> Arc<dyn Any + Send + Sync>;
47
48 fn to_layout(&self) -> LayoutRef;
50
51 fn encoding(&self) -> LayoutEncodingRef;
53
54 fn row_count(&self) -> u64;
56
57 fn dtype(&self) -> &DType;
59
60 fn nchildren(&self) -> usize;
62
63 fn child(&self, idx: usize) -> VortexResult<LayoutRef>;
68
69 fn child_type(&self, idx: usize) -> LayoutChildType;
72
73 fn metadata(&self) -> Vec<u8>;
75
76 fn segment_ids(&self) -> Vec<SegmentId>;
78
79 fn new_reader(
94 &self,
95 name: Arc<str>,
96 segment_source: Arc<dyn SegmentSource>,
97 session: &VortexSession,
98 ctx: &LayoutReaderContext,
99 ) -> VortexResult<LayoutReaderRef>;
100}
101
102pub trait IntoLayout {
104 fn into_layout(self) -> LayoutRef;
106}
107
108#[derive(Debug, Clone, PartialEq, Eq)]
110pub enum LayoutChildType {
111 Transparent(Arc<str>),
113 Auxiliary(Arc<str>),
116 Chunk((usize, u64)),
119 Field(FieldName),
122}
123
124impl LayoutChildType {
125 pub fn name(&self) -> Arc<str> {
127 match self {
128 LayoutChildType::Chunk((idx, _offset)) => format!("[{idx}]").into(),
129 LayoutChildType::Auxiliary(name) => Arc::clone(name),
130 LayoutChildType::Transparent(name) => Arc::clone(name),
131 LayoutChildType::Field(name) => name.clone().into(),
132 }
133 }
134
135 pub fn row_offset(&self) -> Option<u64> {
138 match self {
139 LayoutChildType::Chunk((_idx, offset)) => Some(*offset),
140 LayoutChildType::Auxiliary(_) => None,
141 LayoutChildType::Transparent(_) => Some(0),
142 LayoutChildType::Field(_) => Some(0),
143 }
144 }
145}
146
147impl dyn Layout + '_ {
148 pub fn encoding_id(&self) -> LayoutEncodingId {
150 self.encoding().id()
151 }
152
153 pub fn children(&self) -> VortexResult<Vec<LayoutRef>> {
155 (0..self.nchildren()).map(|i| self.child(i)).try_collect()
156 }
157
158 pub fn child_types(&self) -> impl Iterator<Item = LayoutChildType> {
160 (0..self.nchildren()).map(|i| self.child_type(i))
161 }
162
163 pub fn child_names(&self) -> impl Iterator<Item = Arc<str>> {
165 self.child_types().map(|child| child.name())
166 }
167
168 pub fn child_row_offsets(&self) -> impl Iterator<Item = Option<u64>> {
170 self.child_types().map(|child| child.row_offset())
171 }
172
173 pub fn is<V: VTable>(&self) -> bool {
174 self.as_opt::<V>().is_some()
175 }
176
177 pub fn as_<V: VTable>(&self) -> &V::Layout {
179 self.as_opt::<V>().vortex_expect("Failed to downcast")
180 }
181
182 pub fn as_opt<V: VTable>(&self) -> Option<&V::Layout> {
184 self.as_any()
185 .downcast_ref::<LayoutAdapter<V>>()
186 .map(|adapter| &adapter.0)
187 }
188
189 pub fn into<V: VTable>(self: Arc<Self>) -> Arc<V::Layout> {
191 let layout_adapter = self
192 .as_any_arc()
193 .downcast::<LayoutAdapter<V>>()
194 .map_err(|_| vortex_err!("Invalid layout type"))
195 .vortex_expect("Invalid layout type");
196
197 unsafe { std::mem::transmute::<Arc<LayoutAdapter<V>>, Arc<V::Layout>>(layout_adapter) }
202 }
203
204 pub fn depth_first_traversal(&self) -> impl Iterator<Item = VortexResult<LayoutRef>> {
206 struct ChildrenIterator {
208 stack: Vec<LayoutRef>,
209 }
210
211 impl Iterator for ChildrenIterator {
212 type Item = VortexResult<LayoutRef>;
213
214 fn next(&mut self) -> Option<Self::Item> {
215 let next = self.stack.pop()?;
216 let Ok(children) = next.children() else {
217 return Some(Ok(next));
218 };
219 for child in children.into_iter().rev() {
220 self.stack.push(child);
221 }
222 Some(Ok(next))
223 }
224 }
225
226 ChildrenIterator {
227 stack: vec![self.to_layout()],
228 }
229 }
230
231 pub fn display_tree(&self) -> DisplayLayoutTree {
233 DisplayLayoutTree::new(self.to_layout(), false)
234 }
235
236 pub fn display_tree_verbose(&self, verbose: bool) -> DisplayLayoutTree {
238 DisplayLayoutTree::new(self.to_layout(), verbose)
239 }
240
241 pub async fn display_tree_with_segments(
248 &self,
249 segment_source: Arc<dyn SegmentSource>,
250 ) -> VortexResult<DisplayLayoutTree> {
251 display_tree_with_segment_sizes(self.to_layout(), segment_source).await
252 }
253}
254
255impl Display for dyn Layout + '_ {
257 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
258 let segment_ids = self.segment_ids();
259 if segment_ids.is_empty() {
260 write!(
261 f,
262 "{}({}, rows={})",
263 self.encoding_id(),
264 self.dtype(),
265 self.row_count()
266 )
267 } else {
268 write!(
269 f,
270 "{}({}, rows={}, segments=[{}])",
271 self.encoding_id(),
272 self.dtype(),
273 self.row_count(),
274 segment_ids
275 .iter()
276 .map(|s| format!("{}", **s))
277 .collect::<Vec<_>>()
278 .join(", ")
279 )
280 }
281 }
282}
283
284#[repr(transparent)]
285pub struct LayoutAdapter<V: VTable>(V::Layout);
286
287impl<V: VTable> Debug for LayoutAdapter<V> {
288 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
289 self.0.fmt(f)
290 }
291}
292
293impl<V: VTable> Layout for LayoutAdapter<V> {
294 fn as_any(&self) -> &dyn Any {
295 self
296 }
297
298 fn as_any_arc(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> {
299 self
300 }
301
302 fn to_layout(&self) -> LayoutRef {
303 Arc::new(LayoutAdapter::<V>(self.0.clone()))
304 }
305
306 fn encoding(&self) -> LayoutEncodingRef {
307 V::encoding(&self.0)
308 }
309
310 fn row_count(&self) -> u64 {
311 V::row_count(&self.0)
312 }
313
314 fn dtype(&self) -> &DType {
315 V::dtype(&self.0)
316 }
317
318 fn nchildren(&self) -> usize {
319 V::nchildren(&self.0)
320 }
321
322 fn child(&self, idx: usize) -> VortexResult<LayoutRef> {
323 V::child(&self.0, idx)
324 }
325
326 fn child_type(&self, idx: usize) -> LayoutChildType {
327 V::child_type(&self.0, idx)
328 }
329
330 fn metadata(&self) -> Vec<u8> {
331 V::metadata(&self.0).serialize()
332 }
333
334 fn segment_ids(&self) -> Vec<SegmentId> {
335 V::segment_ids(&self.0)
336 }
337
338 fn new_reader(
339 &self,
340 name: Arc<str>,
341 segment_source: Arc<dyn SegmentSource>,
342 session: &VortexSession,
343 ctx: &LayoutReaderContext,
344 ) -> VortexResult<LayoutReaderRef> {
345 V::new_reader(&self.0, name, segment_source, session, ctx)
346 }
347}
348
349mod private {
350 use super::*;
351 use crate::layouts::foreign::ForeignLayout;
352
353 pub trait Sealed {}
354
355 impl<V: VTable> Sealed for LayoutAdapter<V> {}
356 impl Sealed for ForeignLayout {}
357}
358
359#[cfg(test)]
360mod tests {
361 use rstest::rstest;
362 use vortex_session::registry::ReadContext;
363
364 use super::*;
365
366 #[test]
367 fn test_layout_child_type_name() {
368 let chunk = LayoutChildType::Chunk((5, 100));
370 assert_eq!(chunk.name().as_ref(), "[5]");
371
372 let field = LayoutChildType::Field(FieldName::from("customer_id"));
374 assert_eq!(field.name().as_ref(), "customer_id");
375
376 let aux = LayoutChildType::Auxiliary(Arc::from("zone_map"));
378 assert_eq!(aux.name().as_ref(), "zone_map");
379
380 let transparent = LayoutChildType::Transparent(Arc::from("compressed"));
382 assert_eq!(transparent.name().as_ref(), "compressed");
383 }
384
385 #[test]
386 fn test_layout_child_type_row_offset() {
387 let chunk = LayoutChildType::Chunk((0, 42));
389 assert_eq!(chunk.row_offset(), Some(42));
390
391 let field = LayoutChildType::Field(FieldName::from("field1"));
393 assert_eq!(field.row_offset(), Some(0));
394
395 let aux = LayoutChildType::Auxiliary(Arc::from("metadata"));
397 assert_eq!(aux.row_offset(), None);
398
399 let transparent = LayoutChildType::Transparent(Arc::from("wrapper"));
401 assert_eq!(transparent.row_offset(), Some(0));
402 }
403
404 #[test]
405 fn test_layout_child_type_equality() {
406 let chunk1 = LayoutChildType::Chunk((1, 100));
408 let chunk2 = LayoutChildType::Chunk((1, 100));
409 let chunk3 = LayoutChildType::Chunk((2, 100));
410 let chunk4 = LayoutChildType::Chunk((1, 200));
411
412 assert_eq!(chunk1, chunk2);
413 assert_ne!(chunk1, chunk3);
414 assert_ne!(chunk1, chunk4);
415
416 let field1 = LayoutChildType::Field(FieldName::from("name"));
418 let field2 = LayoutChildType::Field(FieldName::from("name"));
419 let field3 = LayoutChildType::Field(FieldName::from("age"));
420
421 assert_eq!(field1, field2);
422 assert_ne!(field1, field3);
423
424 let aux1 = LayoutChildType::Auxiliary(Arc::from("stats"));
426 let aux2 = LayoutChildType::Auxiliary(Arc::from("stats"));
427 let aux3 = LayoutChildType::Auxiliary(Arc::from("index"));
428
429 assert_eq!(aux1, aux2);
430 assert_ne!(aux1, aux3);
431
432 let trans1 = LayoutChildType::Transparent(Arc::from("enc"));
434 let trans2 = LayoutChildType::Transparent(Arc::from("enc"));
435 let trans3 = LayoutChildType::Transparent(Arc::from("dec"));
436
437 assert_eq!(trans1, trans2);
438 assert_ne!(trans1, trans3);
439
440 assert_ne!(chunk1, field1);
442 assert_ne!(field1, aux1);
443 assert_ne!(aux1, trans1);
444 }
445
446 #[rstest]
447 #[case(LayoutChildType::Chunk((0, 0)), "[0]", Some(0))]
448 #[case(LayoutChildType::Chunk((999, 1000000)), "[999]", Some(1000000))]
449 #[case(LayoutChildType::Field(FieldName::from("")), "", Some(0))]
450 #[case(
451 LayoutChildType::Field(FieldName::from("very_long_field_name_that_is_quite_lengthy")),
452 "very_long_field_name_that_is_quite_lengthy",
453 Some(0)
454 )]
455 #[case(LayoutChildType::Auxiliary(Arc::from("aux")), "aux", None)]
456 #[case(LayoutChildType::Transparent(Arc::from("t")), "t", Some(0))]
457 fn test_layout_child_type_parameterized(
458 #[case] child_type: LayoutChildType,
459 #[case] expected_name: &str,
460 #[case] expected_offset: Option<u64>,
461 ) {
462 assert_eq!(child_type.name().as_ref(), expected_name);
463 assert_eq!(child_type.row_offset(), expected_offset);
464 }
465
466 #[test]
467 fn test_chunk_with_different_indices_and_offsets() {
468 let chunks = [
469 LayoutChildType::Chunk((0, 0)),
470 LayoutChildType::Chunk((1, 100)),
471 LayoutChildType::Chunk((2, 200)),
472 LayoutChildType::Chunk((100, 10000)),
473 ];
474
475 for chunk in chunks.iter() {
476 let name = chunk.name();
477 assert!(name.starts_with('['));
478 assert!(name.ends_with(']'));
479
480 if let LayoutChildType::Chunk((idx, offset)) = chunk {
481 assert_eq!(name.as_ref(), format!("[{}]", idx));
482 assert_eq!(chunk.row_offset(), Some(*offset));
483 }
484 }
485 }
486
487 #[test]
488 fn test_field_names_with_special_characters() {
489 let special_fields: Vec<Arc<str>> = vec![
490 Arc::from("field-with-dashes"),
491 Arc::from("field_with_underscores"),
492 Arc::from("field.with.dots"),
493 Arc::from("field::with::colons"),
494 Arc::from("field/with/slashes"),
495 Arc::from("field@with#symbols"),
496 ];
497
498 for field_name in special_fields {
499 let field = LayoutChildType::Field(Arc::clone(&field_name).into());
500 assert_eq!(field.name(), field_name);
501 assert_eq!(field.row_offset(), Some(0));
502 }
503 }
504
505 #[test]
506 fn test_struct_layout_display() {
507 use vortex_array::dtype::Nullability::NonNullable;
508 use vortex_array::dtype::PType;
509 use vortex_array::dtype::StructFields;
510
511 use crate::IntoLayout;
512 use crate::layouts::chunked::ChunkedLayout;
513 use crate::layouts::dict::DictLayout;
514 use crate::layouts::flat::FlatLayout;
515 use crate::layouts::struct_::StructLayout;
516 use crate::segments::SegmentId;
517
518 let ctx = ReadContext::new([]);
519
520 let dict_values =
522 FlatLayout::new(3, DType::Utf8(NonNullable), SegmentId::from(0), ctx.clone())
523 .into_layout();
524
525 assert_eq!(
527 format!("{}", dict_values),
528 "vortex.flat(utf8, rows=3, segments=[0])"
529 );
530
531 let dict_codes = FlatLayout::new(
533 10,
534 DType::Primitive(PType::U16, NonNullable),
535 SegmentId::from(1),
536 ctx.clone(),
537 )
538 .into_layout();
539
540 assert_eq!(
542 format!("{}", dict_codes),
543 "vortex.flat(u16, rows=10, segments=[1])"
544 );
545
546 let dict_layout =
548 DictLayout::new(Arc::clone(&dict_values), Arc::clone(&dict_codes)).into_layout();
549
550 assert_eq!(format!("{}", dict_layout), "vortex.dict(utf8, rows=10)");
552
553 let chunk1 = FlatLayout::new(
555 5,
556 DType::Primitive(PType::I64, NonNullable),
557 SegmentId::from(2),
558 ctx.clone(),
559 )
560 .into_layout();
561
562 let chunk2 = FlatLayout::new(
563 5,
564 DType::Primitive(PType::I64, NonNullable),
565 SegmentId::from(3),
566 ctx,
567 )
568 .into_layout();
569
570 let chunked_layout = ChunkedLayout::new(
572 10,
573 DType::Primitive(PType::I64, NonNullable),
574 crate::OwnedLayoutChildren::layout_children(vec![
575 Arc::clone(&chunk1),
576 Arc::clone(&chunk2),
577 ]),
578 )
579 .into_layout();
580
581 assert_eq!(
583 format!("{}", chunked_layout),
584 "vortex.chunked(i64, rows=10)"
585 );
586
587 assert_eq!(
589 format!("{}", chunk1),
590 "vortex.flat(i64, rows=5, segments=[2])"
591 );
592 assert_eq!(
593 format!("{}", chunk2),
594 "vortex.flat(i64, rows=5, segments=[3])"
595 );
596
597 let field_names: Vec<Arc<str>> = vec!["name".into(), "value".into()];
599 let struct_dtype = DType::Struct(
600 StructFields::new(
601 field_names.into(),
602 vec![
603 DType::Utf8(NonNullable),
604 DType::Primitive(PType::I64, NonNullable),
605 ],
606 ),
607 NonNullable,
608 );
609
610 let struct_layout =
611 StructLayout::new(10, struct_dtype, vec![dict_layout, chunked_layout]).into_layout();
612
613 println!("{}", struct_layout.display_tree_verbose(true));
614
615 assert_eq!(
617 format!("{}", struct_layout),
618 "vortex.struct({name=utf8, value=i64}, rows=10)"
619 );
620 }
621}