mod matcher;
mod vtable;
use std::any::Any;
use std::any::type_name;
use std::fmt::Debug;
use std::fmt::Display;
use std::fmt::Formatter;
use std::hash::Hash;
use std::hash::Hasher;
use std::sync::Arc;
use arcref::ArcRef;
pub use matcher::*;
use vortex_error::VortexExpect;
use vortex_error::VortexResult;
use vortex_error::vortex_err;
pub use vtable::*;
use crate::DType;
use crate::Nullability;
pub type ExtID = ArcRef<str>;
#[derive(Clone)]
pub struct ExtDType<V: ExtDTypeVTable>(Arc<ExtDTypeAdapter<V>>);
impl<V: ExtDTypeVTable + Default> ExtDType<V> {
pub fn try_new(metadata: V::Metadata, storage_dtype: DType) -> VortexResult<Self> {
Self::try_with_vtable(V::default(), metadata, storage_dtype)
}
}
impl<V: ExtDTypeVTable> ExtDType<V> {
pub fn try_with_vtable(
vtable: V,
metadata: V::Metadata,
storage_dtype: DType,
) -> VortexResult<Self> {
vtable.validate_dtype(&metadata, &storage_dtype)?;
Ok(Self(Arc::new(ExtDTypeAdapter::<V> {
vtable,
metadata,
storage_dtype,
})))
}
pub fn id(&self) -> ExtID {
self.0.id()
}
pub fn metadata(&self) -> &V::Metadata {
&self.0.metadata
}
pub fn storage_dtype(&self) -> &DType {
&self.0.storage_dtype
}
pub fn erased(self) -> ExtDTypeRef {
ExtDTypeRef(self.0)
}
}
#[derive(Clone)]
pub struct ExtDTypeRef(Arc<dyn ExtDTypeImpl>);
impl Display for ExtDTypeRef {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let metadata = format!("{}", self.metadata_erased());
if metadata.is_empty() {
write!(f, "{}", self.id())?;
} else {
write!(f, "{}[{}]", self.id(), metadata)?;
}
write!(f, "({})", self.storage_dtype())
}
}
impl Debug for ExtDTypeRef {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ExtDType")
.field("id", &self.id())
.field("metadata", &self.metadata_erased())
.field("storage_dtype", &self.storage_dtype())
.finish()
}
}
impl PartialEq for ExtDTypeRef {
fn eq(&self, other: &Self) -> bool {
self.id() == other.id()
&& self.metadata_erased() == other.metadata_erased()
&& self.storage_dtype() == other.storage_dtype()
}
}
impl Eq for ExtDTypeRef {}
impl Hash for ExtDTypeRef {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id().hash(state);
self.metadata_erased().hash(state);
self.storage_dtype().hash(state);
}
}
impl ExtDTypeRef {
pub fn id(&self) -> ExtID {
self.0.id()
}
pub fn metadata_erased(&self) -> ExtDTypeMetadata<'_> {
ExtDTypeMetadata { ext_dtype: self }
}
pub fn storage_dtype(&self) -> &DType {
self.0.storage_dtype()
}
pub fn with_nullability(&self, nullability: Nullability) -> Self {
if self.storage_dtype().nullability() == nullability {
self.clone()
} else {
self.0.with_nullability(nullability)
}
}
pub fn eq_ignore_nullability(&self, other: &Self) -> bool {
self.id() == other.id()
&& self.metadata_erased() == other.metadata_erased()
&& self
.storage_dtype()
.eq_ignore_nullability(other.storage_dtype())
}
}
impl ExtDTypeRef {
pub fn is<M: Matcher>(&self) -> bool {
M::matches(self)
}
pub fn metadata_opt<M: Matcher>(&self) -> Option<M::Match<'_>> {
M::try_match(self)
}
pub fn metadata<M: Matcher>(&self) -> M::Match<'_> {
self.metadata_opt::<M>()
.vortex_expect("Failed to downcast DynExtDType")
}
pub fn try_downcast<V: ExtDTypeVTable>(self) -> Result<ExtDType<V>, ExtDTypeRef> {
if self.0.as_any().is::<ExtDTypeAdapter<V>>() {
let ptr = Arc::into_raw(self.0) as *const ExtDTypeAdapter<V>;
let inner = unsafe { Arc::from_raw(ptr) };
Ok(ExtDType(inner))
} else {
Err(self)
}
}
pub fn downcast<V: ExtDTypeVTable>(self) -> ExtDType<V> {
self.try_downcast::<V>()
.map_err(|this| {
vortex_err!(
"Failed to downcast DynExtDType {} to {}",
this.0.id(),
type_name::<V>(),
)
})
.vortex_expect("Failed to downcast DynExtDType")
}
}
pub struct ExtDTypeMetadata<'a> {
pub(super) ext_dtype: &'a ExtDTypeRef,
}
impl ExtDTypeMetadata<'_> {
pub fn serialize(&self) -> VortexResult<Vec<u8>> {
self.ext_dtype.0.metadata_serialize()
}
}
impl Display for ExtDTypeMetadata<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.ext_dtype.0.metadata_display(f)
}
}
impl Debug for ExtDTypeMetadata<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.ext_dtype.0.metadata_debug(f)
}
}
impl PartialEq for ExtDTypeMetadata<'_> {
fn eq(&self, other: &Self) -> bool {
self.ext_dtype
.0
.metadata_eq(other.ext_dtype.0.metadata_any())
}
}
impl Eq for ExtDTypeMetadata<'_> {}
impl Hash for ExtDTypeMetadata<'_> {
fn hash<H: Hasher>(&self, mut state: &mut H) {
self.ext_dtype.0.metadata_hash(&mut state);
}
}
trait ExtDTypeImpl: 'static + Send + Sync + private::Sealed {
fn as_any(&self) -> &dyn Any;
fn id(&self) -> ExtID;
fn storage_dtype(&self) -> &DType;
fn metadata_any(&self) -> &dyn Any;
fn metadata_debug(&self, f: &mut Formatter<'_>) -> std::fmt::Result;
fn metadata_display(&self, f: &mut Formatter<'_>) -> std::fmt::Result;
fn metadata_eq(&self, other: &dyn Any) -> bool;
fn metadata_hash(&self, state: &mut dyn Hasher);
fn metadata_serialize(&self) -> VortexResult<Vec<u8>>;
fn with_nullability(&self, nullability: Nullability) -> ExtDTypeRef;
}
struct ExtDTypeAdapter<V: ExtDTypeVTable> {
vtable: V,
metadata: V::Metadata,
storage_dtype: DType,
}
impl<V: ExtDTypeVTable> ExtDTypeImpl for ExtDTypeAdapter<V> {
fn as_any(&self) -> &dyn Any {
self
}
fn id(&self) -> ExtID {
self.vtable.id()
}
fn storage_dtype(&self) -> &DType {
&self.storage_dtype
}
fn metadata_any(&self) -> &dyn Any {
&self.metadata
}
fn metadata_debug(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
<V::Metadata as Debug>::fmt(&self.metadata, f)
}
fn metadata_display(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
<V::Metadata as Display>::fmt(&self.metadata, f)
}
fn metadata_eq(&self, other: &dyn Any) -> bool {
let Some(other) = other.downcast_ref::<V::Metadata>() else {
return false;
};
<V::Metadata as PartialEq>::eq(&self.metadata, other)
}
fn metadata_hash(&self, mut state: &mut dyn Hasher) {
<V::Metadata as Hash>::hash(&self.metadata, &mut state);
}
fn metadata_serialize(&self) -> VortexResult<Vec<u8>> {
V::serialize(&self.vtable, &self.metadata)
}
fn with_nullability(&self, nullability: Nullability) -> ExtDTypeRef {
let storage_dtype = self.storage_dtype.with_nullability(nullability);
ExtDType::<V>::try_with_vtable(self.vtable.clone(), self.metadata.clone(), storage_dtype)
.vortex_expect("Extension DType {} incorrect fails validation with the same storage type but different nullability").erased()
}
}
mod private {
use super::ExtDTypeAdapter;
pub trait Sealed {}
impl<V: super::ExtDTypeVTable> Sealed for ExtDTypeAdapter<V> {}
}