vortex_array/vtable/
dyn_.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use std::any::Any;
5use std::fmt::Debug;
6use std::fmt::Display;
7use std::fmt::Formatter;
8use std::mem::transmute;
9use std::sync::Arc;
10
11use arcref::ArcRef;
12use vortex_buffer::BufferHandle;
13use vortex_dtype::DType;
14use vortex_error::VortexExpect;
15use vortex_error::VortexResult;
16use vortex_error::vortex_bail;
17use vortex_error::vortex_err;
18
19use crate::Array;
20use crate::ArrayRef;
21use crate::Canonical;
22use crate::IntoArray;
23use crate::serde::ArrayChildren;
24use crate::vtable::EncodeVTable;
25use crate::vtable::VTable;
26
27/// ArrayId is a globally unique name for the array's vtable.
28pub type ArrayId = ArcRef<str>;
29pub type ArrayVTable = ArcRef<dyn DynVTable>;
30
31/// Dynamically typed trait for invoking array vtables.
32pub trait DynVTable: 'static + private::Sealed + Send + Sync + Debug {
33    /// Downcast the encoding to [`Any`].
34    fn as_any(&self) -> &dyn Any;
35
36    /// Returns the ID of the encoding.
37    fn id(&self) -> ArrayId;
38
39    /// Build an array from its parts.
40    fn build(
41        &self,
42        dtype: &DType,
43        len: usize,
44        metadata: &[u8],
45        buffers: &[BufferHandle],
46        children: &dyn ArrayChildren,
47    ) -> VortexResult<ArrayRef>;
48
49    fn with_children(
50        &self,
51        array: &dyn Array,
52        children: &dyn ArrayChildren,
53    ) -> VortexResult<ArrayRef>;
54
55    /// Encode the canonical array into this encoding implementation.
56    /// Returns `None` if this encoding does not support the given canonical array, for example
57    /// if the data type is incompatible.
58    ///
59    /// Panics if `like` is encoded with a different encoding.
60    fn encode(&self, input: &Canonical, like: Option<&dyn Array>)
61    -> VortexResult<Option<ArrayRef>>;
62}
63
64/// Adapter struct used to lift the [`VTable`] trait into an object-safe [`DynVTable`]
65/// implementation.
66///
67/// Since this is a unit struct with `repr(transparent)`, we are able to turn un-adapted array
68/// structs into [`DynVTable`] using some cheeky casting inside [`std::ops::Deref`] and
69/// [`AsRef`]. See the `vtable!` macro for more details.
70#[repr(transparent)]
71pub struct ArrayVTableAdapter<V: VTable>(V);
72
73impl<V: VTable> DynVTable for ArrayVTableAdapter<V> {
74    fn as_any(&self) -> &dyn Any {
75        self
76    }
77
78    fn id(&self) -> ArrayId {
79        V::id(&self.0)
80    }
81
82    fn build(
83        &self,
84        dtype: &DType,
85        len: usize,
86        metadata_bytes: &[u8],
87        buffers: &[BufferHandle],
88        children: &dyn ArrayChildren,
89    ) -> VortexResult<ArrayRef> {
90        let metadata = V::deserialize(metadata_bytes)?;
91        let array = V::build(&self.0, dtype, len, &metadata, buffers, children)?;
92        assert_eq!(array.len(), len, "Array length mismatch after building");
93        assert_eq!(array.dtype(), dtype, "Array dtype mismatch after building");
94        Ok(array.to_array())
95    }
96
97    fn with_children(
98        &self,
99        array: &dyn Array,
100        children: &dyn ArrayChildren,
101    ) -> VortexResult<ArrayRef> {
102        let buffers: Vec<BufferHandle> = array
103            .buffers()
104            .into_iter()
105            .map(BufferHandle::Buffer)
106            .collect();
107        V::build(
108            &self.0,
109            array.dtype(),
110            array.len(),
111            &V::metadata(array.as_::<V>())?,
112            &buffers,
113            children,
114        )
115        .map(|a| a.into_array())
116    }
117
118    fn encode(
119        &self,
120        input: &Canonical,
121        like: Option<&dyn Array>,
122    ) -> VortexResult<Option<ArrayRef>> {
123        let downcast_like = like
124            .map(|like| {
125                like.as_opt::<V>().ok_or_else(|| {
126                    vortex_err!(
127                        "Like array {} does not match requested encoding {}",
128                        like.encoding_id(),
129                        self.id()
130                    )
131                })
132            })
133            .transpose()?;
134
135        let Some(array) =
136            <V::EncodeVTable as EncodeVTable<V>>::encode(&self.0, input, downcast_like)?
137        else {
138            return Ok(None);
139        };
140
141        let input = input.as_ref();
142        if array.len() != input.len() {
143            vortex_bail!(
144                "Array length mismatch after encoding: {} != {}",
145                array.len(),
146                input.len()
147            );
148        }
149        if array.dtype() != input.dtype() {
150            vortex_bail!(
151                "Array dtype mismatch after encoding: {} != {}",
152                array.dtype(),
153                input.dtype()
154            );
155        }
156
157        Ok(Some(array.into_array()))
158    }
159}
160
161impl<V: VTable> Debug for ArrayVTableAdapter<V> {
162    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
163        f.debug_struct("Encoding").field("id", &self.id()).finish()
164    }
165}
166
167impl Display for dyn DynVTable + '_ {
168    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
169        write!(f, "{}", self.id())
170    }
171}
172
173impl PartialEq for dyn DynVTable + '_ {
174    fn eq(&self, other: &Self) -> bool {
175        self.id() == other.id()
176    }
177}
178
179impl Eq for dyn DynVTable + '_ {}
180
181impl dyn DynVTable + '_ {
182    pub fn as_<V: VTable>(&self) -> &V {
183        self.as_any()
184            .downcast_ref::<ArrayVTableAdapter<V>>()
185            .map(|e| &e.0)
186            .vortex_expect("Encoding is not of the expected type")
187    }
188}
189
190pub trait ArrayVTableExt {
191    /// Wraps the vtable into an `ArrayVTable` by static reference.
192    fn as_vtable(&'static self) -> ArrayVTable;
193
194    /// Wraps the vtable into an `ArrayVTable` by owned reference.
195    fn into_vtable(self) -> ArrayVTable;
196
197    fn to_vtable(&self) -> ArrayVTable
198    where
199        Self: Clone;
200}
201
202impl<V: VTable> ArrayVTableExt for V {
203    fn as_vtable(&'static self) -> ArrayVTable {
204        let dyn_vtable: &'static ArrayVTableAdapter<V> =
205            unsafe { transmute::<&'static V, &'static ArrayVTableAdapter<V>>(self) };
206        ArrayVTable::new_ref(dyn_vtable)
207    }
208
209    fn into_vtable(self) -> ArrayVTable {
210        ArrayVTable::new_arc(Arc::new(ArrayVTableAdapter(self)))
211    }
212
213    fn to_vtable(&self) -> ArrayVTable
214    where
215        Self: Clone,
216    {
217        ArrayVTable::new_arc(Arc::new(ArrayVTableAdapter(self.clone())))
218    }
219}
220
221mod private {
222    use crate::vtable::ArrayVTableAdapter;
223    use crate::vtable::VTable;
224
225    pub trait Sealed {}
226    impl<V: VTable> Sealed for ArrayVTableAdapter<V> {}
227}