use std::any::Any;
use std::fmt::Debug;
use std::fmt::Display;
use std::fmt::Formatter;
use std::ops::Deref;
use std::sync::Arc;
use itertools::Itertools;
use vortex_array::SerializeMetadata;
use vortex_array::dtype::DType;
use vortex_array::dtype::FieldName;
use vortex_error::VortexExpect;
use vortex_error::VortexResult;
use vortex_session::VortexSession;
use vortex_session::registry::Id;
use crate::LayoutReaderContext;
use crate::LayoutReaderRef;
use crate::children::LayoutChildren;
use crate::display::DisplayLayoutTree;
use crate::display::display_tree_with_segment_sizes;
use crate::segments::SegmentId;
use crate::segments::SegmentSource;
use crate::vtable::LayoutRef;
use crate::vtable::VTable;
pub type LayoutId = Id;
pub struct LayoutParts<V: VTable> {
vtable: V,
dtype: DType,
row_count: u64,
segment_ids: Vec<SegmentId>,
children: Arc<dyn LayoutChildren>,
data: V::LayoutData,
}
impl<V: VTable> LayoutParts<V> {
pub fn new(
vtable: V,
dtype: DType,
row_count: u64,
segment_ids: Vec<SegmentId>,
children: Arc<dyn LayoutChildren>,
data: V::LayoutData,
) -> Self {
Self {
vtable,
dtype,
row_count,
segment_ids,
children,
data,
}
}
pub fn into_typed(self) -> Layout<V> {
Layout::from_parts(self)
}
pub fn into_layout(self) -> LayoutRef {
self.into_typed().into_layout()
}
}
pub struct Layout<V: VTable> {
inner: Arc<LayoutInner<V>>,
}
struct LayoutInner<V: VTable> {
vtable: V,
dtype: DType,
row_count: u64,
segment_ids: Vec<SegmentId>,
children: Arc<dyn LayoutChildren>,
data: V::LayoutData,
}
impl<V: VTable> Layout<V> {
pub fn from_parts(parts: LayoutParts<V>) -> Self {
Self {
inner: Arc::new(LayoutInner {
vtable: parts.vtable,
dtype: parts.dtype,
row_count: parts.row_count,
segment_ids: parts.segment_ids,
children: parts.children,
data: parts.data,
}),
}
}
pub fn vtable(&self) -> &V {
&self.inner.vtable
}
pub fn data(&self) -> &V::LayoutData {
&self.inner.data
}
pub fn dtype(&self) -> &DType {
&self.inner.dtype
}
pub fn row_count(&self) -> u64 {
self.inner.row_count
}
pub fn segment_ids(&self) -> &[SegmentId] {
&self.inner.segment_ids
}
pub fn children(&self) -> &Arc<dyn LayoutChildren> {
&self.inner.children
}
pub fn nchildren(&self) -> usize {
self.inner.children.nchildren()
}
pub fn nslots(&self) -> usize {
V::nslots(self)
}
pub fn slot_to_child(&self, slot: usize) -> Option<usize> {
V::slot_to_child(self, slot)
}
pub fn slot(&self, slot: usize) -> VortexResult<Option<LayoutRef>> {
match V::slot_to_child(self, slot) {
Some(idx) => self
.inner
.children
.child(idx, &V::child_dtype(self, slot)?)
.map(Some),
None => Ok(None),
}
}
pub fn slot_type(&self, slot: usize) -> Option<LayoutChildType> {
V::slot_to_child(self, slot).map(|_| V::child_type(self, slot))
}
pub fn child_row_count(&self, idx: usize) -> u64 {
self.inner.children.child_row_count(idx)
}
pub fn to_layout(&self) -> LayoutRef {
self.clone().into_layout()
}
pub fn into_layout(self) -> LayoutRef {
Arc::new(self)
}
pub fn new_reader(
&self,
name: Arc<str>,
segment_source: Arc<dyn SegmentSource>,
session: &VortexSession,
ctx: &LayoutReaderContext,
) -> VortexResult<LayoutReaderRef> {
V::new_reader(self, name, segment_source, session, ctx)
}
}
impl<V: VTable> Clone for Layout<V> {
fn clone(&self) -> Self {
Self {
inner: Arc::clone(&self.inner),
}
}
}
impl<V: VTable> Debug for Layout<V> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Layout")
.field("encoding_id", &self.vtable().id())
.field("dtype", &self.inner.dtype)
.field("row_count", &self.inner.row_count)
.field("segment_ids", &self.inner.segment_ids)
.field("data", &self.inner.data)
.finish()
}
}
impl<V: VTable> Deref for Layout<V> {
type Target = V::LayoutData;
fn deref(&self) -> &Self::Target {
self.data()
}
}
impl<V: VTable> From<Layout<V>> for LayoutRef {
fn from(value: Layout<V>) -> Self {
value.into_layout()
}
}
pub trait DynLayout: 'static + Send + Sync + Debug {
fn as_any(&self) -> &dyn Any;
fn dyn_to_layout(&self) -> LayoutRef;
fn dyn_encoding_id(&self) -> LayoutId;
fn dyn_row_count(&self) -> u64;
fn dyn_dtype(&self) -> &DType;
fn dyn_nchildren(&self) -> usize;
fn dyn_nslots(&self) -> usize;
fn dyn_slot(&self, slot: usize) -> VortexResult<Option<LayoutRef>>;
fn dyn_slot_type(&self, slot: usize) -> Option<LayoutChildType>;
fn dyn_metadata(&self) -> Vec<u8>;
fn dyn_segment_ids(&self) -> Vec<SegmentId>;
fn dyn_new_reader(
&self,
name: Arc<str>,
segment_source: Arc<dyn SegmentSource>,
session: &VortexSession,
ctx: &LayoutReaderContext,
) -> VortexResult<LayoutReaderRef>;
fn dyn_is_indivisible(&self) -> bool {
false
}
}
impl<V: VTable> DynLayout for Layout<V> {
fn as_any(&self) -> &dyn Any {
self
}
fn dyn_to_layout(&self) -> LayoutRef {
Layout::to_layout(self)
}
fn dyn_encoding_id(&self) -> LayoutId {
self.vtable().id()
}
fn dyn_row_count(&self) -> u64 {
Layout::row_count(self)
}
fn dyn_dtype(&self) -> &DType {
Layout::dtype(self)
}
fn dyn_nchildren(&self) -> usize {
Layout::nchildren(self)
}
fn dyn_nslots(&self) -> usize {
Layout::nslots(self)
}
fn dyn_slot(&self, slot: usize) -> VortexResult<Option<LayoutRef>> {
Layout::slot(self, slot)
}
fn dyn_slot_type(&self, slot: usize) -> Option<LayoutChildType> {
Layout::slot_type(self, slot)
}
fn dyn_metadata(&self) -> Vec<u8> {
V::metadata(self).serialize()
}
fn dyn_segment_ids(&self) -> Vec<SegmentId> {
self.inner.segment_ids.clone()
}
fn dyn_new_reader(
&self,
name: Arc<str>,
segment_source: Arc<dyn SegmentSource>,
session: &VortexSession,
ctx: &LayoutReaderContext,
) -> VortexResult<LayoutReaderRef> {
Layout::new_reader(self, name, segment_source, session, ctx)
}
fn dyn_is_indivisible(&self) -> bool {
self.vtable().is_indivisible()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LayoutChildType {
Transparent(Arc<str>),
Auxiliary(Arc<str>),
Chunk((usize, u64)),
Field(FieldName),
}
impl LayoutChildType {
pub fn name(&self) -> Arc<str> {
match self {
Self::Chunk((idx, _)) => format!("[{idx}]").into(),
Self::Auxiliary(name) | Self::Transparent(name) => Arc::clone(name),
Self::Field(name) => name.clone().into(),
}
}
pub fn row_offset(&self) -> Option<u64> {
match self {
Self::Chunk((_, offset)) => Some(*offset),
Self::Auxiliary(_) => None,
Self::Transparent(_) | Self::Field(_) => Some(0),
}
}
}
impl dyn DynLayout + '_ {
pub fn to_layout(&self) -> LayoutRef {
self.dyn_to_layout()
}
pub fn encoding_id(&self) -> LayoutId {
self.dyn_encoding_id()
}
pub fn dtype(&self) -> &DType {
self.dyn_dtype()
}
pub fn row_count(&self) -> u64 {
self.dyn_row_count()
}
pub fn nchildren(&self) -> usize {
self.dyn_nchildren()
}
pub fn nslots(&self) -> usize {
self.dyn_nslots()
}
pub fn slot(&self, slot: usize) -> VortexResult<Option<LayoutRef>> {
self.dyn_slot(slot)
}
pub fn slot_type(&self, slot: usize) -> Option<LayoutChildType> {
self.dyn_slot_type(slot)
}
pub fn metadata(&self) -> Vec<u8> {
self.dyn_metadata()
}
pub fn segment_ids(&self) -> Vec<SegmentId> {
self.dyn_segment_ids()
}
pub fn new_reader(
&self,
name: Arc<str>,
segment_source: Arc<dyn SegmentSource>,
session: &VortexSession,
ctx: &LayoutReaderContext,
) -> VortexResult<LayoutReaderRef> {
self.dyn_new_reader(name, segment_source, session, ctx)
}
pub fn children(&self) -> VortexResult<Vec<LayoutRef>> {
(0..self.nslots())
.filter_map(|slot| self.slot(slot).transpose())
.try_collect()
}
pub fn child_types(&self) -> impl Iterator<Item = LayoutChildType> + '_ {
(0..self.nslots()).filter_map(|slot| self.slot_type(slot))
}
pub fn child_names(&self) -> impl Iterator<Item = Arc<str>> + '_ {
self.child_types().map(|child| child.name())
}
pub fn child_row_offsets(&self) -> impl Iterator<Item = Option<u64>> + '_ {
self.child_types().map(|child| child.row_offset())
}
pub fn is<V: VTable>(&self) -> bool {
self.as_opt::<V>().is_some()
}
pub fn as_<V: VTable>(&self) -> &Layout<V> {
self.as_opt::<V>().vortex_expect("Failed to downcast")
}
pub fn as_opt<V: VTable>(&self) -> Option<&Layout<V>> {
self.as_any().downcast_ref()
}
pub fn depth_first_traversal(&self) -> impl Iterator<Item = VortexResult<LayoutRef>> {
struct ChildrenIterator {
stack: Vec<LayoutRef>,
}
impl Iterator for ChildrenIterator {
type Item = VortexResult<LayoutRef>;
fn next(&mut self) -> Option<Self::Item> {
let next = self.stack.pop()?;
let Ok(children) = next.children() else {
return Some(Ok(next));
};
self.stack.extend(children.into_iter().rev());
Some(Ok(next))
}
}
ChildrenIterator {
stack: vec![self.to_layout()],
}
}
pub fn display_tree(&self) -> DisplayLayoutTree {
DisplayLayoutTree::new(self.to_layout(), false)
}
pub fn display_tree_verbose(&self, verbose: bool) -> DisplayLayoutTree {
DisplayLayoutTree::new(self.to_layout(), verbose)
}
pub async fn display_tree_with_segments(
&self,
segment_source: Arc<dyn SegmentSource>,
) -> VortexResult<DisplayLayoutTree> {
display_tree_with_segment_sizes(self.to_layout(), segment_source).await
}
}
impl Display for dyn DynLayout + '_ {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let segments = self.segment_ids();
if segments.is_empty() {
write!(
f,
"{}({}, rows={})",
self.encoding_id(),
self.dtype(),
self.row_count()
)
} else {
write!(
f,
"{}({}, rows={}, segments=[{}])",
self.encoding_id(),
self.dtype(),
self.row_count(),
segments.iter().map(|s| format!("{}", **s)).join(", ")
)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn layout_child_type_names_and_offsets() {
let chunk = LayoutChildType::Chunk((5, 100));
assert_eq!(chunk.name().as_ref(), "[5]");
assert_eq!(chunk.row_offset(), Some(100));
let field = LayoutChildType::Field(FieldName::from("customer_id"));
assert_eq!(field.name().as_ref(), "customer_id");
assert_eq!(field.row_offset(), Some(0));
let auxiliary = LayoutChildType::Auxiliary("zone_map".into());
assert_eq!(auxiliary.row_offset(), None);
let transparent = LayoutChildType::Transparent("compressed".into());
assert_eq!(transparent.row_offset(), Some(0));
}
}