use std::any::Any;
use std::borrow::Borrow;
use std::fmt::Debug;
use std::hash::BuildHasher;
use std::ops::Deref;
use std::sync::Arc;
use std::sync::LazyLock;
use vortex_error::VortexResult;
use vortex_session::SessionExt;
use vortex_session::SessionGuard;
use vortex_session::SessionVar;
use vortex_session::VortexSession;
use vortex_session::registry::Id;
use vortex_utils::aliases::DefaultHashBuilder;
use vortex_utils::aliases::hash_map::HashMap;
use crate::ArrayRef;
use crate::ExecutionCtx;
use crate::arc_swap_map::ArcSwapMap;
use crate::array::VTable;
use crate::arrays::Struct;
use crate::arrays::struct_::compute::rules::struct_cast_reduce_parent;
use crate::kernel::ExecuteParentKernel;
use crate::matcher::Matcher;
use crate::scalar_fn::ScalarFnVTable;
use crate::scalar_fn::fns::cast::Cast;
static FN_HASHER: LazyLock<DefaultHashBuilder> = LazyLock::new(DefaultHashBuilder::default);
pub type ReduceParentFn =
fn(child: &ArrayRef, parent: &ArrayRef, child_idx: usize) -> VortexResult<Option<ArrayRef>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[repr(transparent)]
struct ReduceParentFnId(u64);
impl From<u64> for ReduceParentFnId {
fn from(id: u64) -> Self {
Self(id)
}
}
impl Borrow<u64> for ReduceParentFnId {
fn borrow(&self) -> &u64 {
&self.0
}
}
pub type ExecuteParentFn = fn(
child: &ArrayRef,
parent: &ArrayRef,
child_idx: usize,
ctx: &mut ExecutionCtx,
) -> VortexResult<Option<ArrayRef>>;
pub trait DynExecuteParentKernel: Debug + Send + Sync + 'static {
fn execute_parent(
&self,
child: &ArrayRef,
parent: &ArrayRef,
child_idx: usize,
ctx: &mut ExecutionCtx,
) -> VortexResult<Option<ArrayRef>>;
}
pub(crate) type ExecuteParentKernelRef = Arc<dyn DynExecuteParentKernel>;
pub(crate) type ParentExecutionKernels = HashMap<ExecuteParentFnId, Arc<[ExecuteParentKernelRef]>>;
#[derive(Debug)]
struct ExecuteParentFnKernel(ExecuteParentFn);
impl DynExecuteParentKernel for ExecuteParentFnKernel {
fn execute_parent(
&self,
child: &ArrayRef,
parent: &ArrayRef,
child_idx: usize,
ctx: &mut ExecutionCtx,
) -> VortexResult<Option<ArrayRef>> {
self.0(child, parent, child_idx, ctx)
}
}
#[derive(Debug)]
struct RegisteredExecuteParentKernel<V, K> {
_child: V,
kernel: K,
}
impl<V, K> DynExecuteParentKernel for RegisteredExecuteParentKernel<V, K>
where
V: VTable,
K: ExecuteParentKernel<V>,
{
fn execute_parent(
&self,
child: &ArrayRef,
parent: &ArrayRef,
child_idx: usize,
ctx: &mut ExecutionCtx,
) -> VortexResult<Option<ArrayRef>> {
let Some(child) = child.as_opt::<V>() else {
return Ok(None);
};
let Some(parent) = K::Parent::try_match(parent) else {
return Ok(None);
};
self.kernel.execute_parent(child, parent, child_idx, ctx)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[repr(transparent)]
pub(crate) struct ExecuteParentFnId(u64);
impl From<u64> for ExecuteParentFnId {
fn from(id: u64) -> Self {
Self(id)
}
}
impl Borrow<u64> for ExecuteParentFnId {
fn borrow(&self) -> &u64 {
&self.0
}
}
#[derive(Clone, Debug)]
pub struct ArrayKernels {
reduce_parent: ArcSwapMap<ReduceParentFnId, Arc<[ReduceParentFn]>>,
execute_parent: ArcSwapMap<ExecuteParentFnId, Arc<[ExecuteParentKernelRef]>>,
}
impl Default for ArrayKernels {
fn default() -> ArrayKernels {
let this = Self::empty();
this.register_builtin_reduce_parent();
this
}
}
impl ArrayKernels {
pub fn empty() -> Self {
Self {
reduce_parent: ArcSwapMap::default(),
execute_parent: ArcSwapMap::default(),
}
}
fn register_builtin_reduce_parent(&self) {
self.register_reduce_parent(
Cast.id(),
Struct.id(),
&[struct_cast_reduce_parent as ReduceParentFn],
);
}
pub fn register_reduce_parent(&self, parent: Id, child: Id, fns: &[ReduceParentFn]) {
self.reduce_parent
.extend(hash_fn_id(parent, child).into(), fns);
}
pub fn find_reduce_parent(&self, parent: Id, child: Id) -> Option<Arc<[ReduceParentFn]>> {
self.reduce_parent.get(&hash_fn_id(parent, child))
}
pub fn register_execute_parent(&self, parent: Id, child: Id, fns: &[ExecuteParentFn]) {
let kernels: Vec<ExecuteParentKernelRef> = fns
.iter()
.map(|f| Arc::new(ExecuteParentFnKernel(*f)) as ExecuteParentKernelRef)
.collect();
self.execute_parent
.extend(hash_fn_id(parent, child).into(), kernels.as_slice());
}
pub fn register_execute_parent_kernel<V, K>(&self, parent: Id, child: V, kernel: K)
where
V: VTable,
K: ExecuteParentKernel<V>,
{
let child_id = child.id();
self.execute_parent.push(
hash_fn_id(parent, child_id).into(),
Arc::new(RegisteredExecuteParentKernel {
_child: child,
kernel,
}) as ExecuteParentKernelRef,
);
}
pub fn has_execute_parent(&self, parent: Id, child: Id) -> bool {
self.execute_parent
.get(&hash_fn_id(parent, child))
.is_some()
}
pub(crate) fn execute_parent_snapshot(&self) -> Arc<ParentExecutionKernels> {
self.execute_parent.snapshot()
}
}
fn hash_fn_id(parent: Id, child: Id) -> u64 {
FN_HASHER.hash_one((parent, child))
}
pub(crate) fn execute_parent_key(parent: Id, child: Id) -> u64 {
hash_fn_id(parent, child)
}
#[derive(Clone, Debug)]
pub struct KernelSession {
kernels: ArrayKernels,
}
impl KernelSession {
pub fn empty() -> Self {
Self {
kernels: ArrayKernels::empty(),
}
}
pub fn kernels(&self) -> &ArrayKernels {
&self.kernels
}
}
impl Deref for KernelSession {
type Target = ArrayKernels;
fn deref(&self) -> &ArrayKernels {
&self.kernels
}
}
impl Default for KernelSession {
fn default() -> Self {
let this = Self {
kernels: ArrayKernels::default(),
};
let session = VortexSession::empty().with_some(this.clone());
crate::arrays::initialize(&session);
this
}
}
impl SessionVar for KernelSession {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
pub trait ArrayKernelsExt: SessionExt {
fn kernels(&self) -> SessionGuard<'_, KernelSession> {
self.get::<KernelSession>()
}
}
impl<S: SessionExt> ArrayKernelsExt for S {}
#[cfg(test)]
mod tests {
use vortex_session::VortexSession;
use super::ArrayKernelsExt;
use super::KernelSession;
use crate::ArrayVTable;
use crate::arrays::Bool;
use crate::scalar_fn::ScalarFnVTable;
use crate::scalar_fn::fns::binary::Binary;
#[test]
fn kernel_session_default_registers_builtin_kernels() {
let session = VortexSession::empty().with::<KernelSession>();
assert!(session.kernels().has_execute_parent(Binary.id(), Bool.id()));
}
#[test]
fn initialize_registers_builtin_kernels_into_empty_kernel_session() {
let session = VortexSession::empty().with_some(KernelSession::empty());
assert!(!session.kernels().has_execute_parent(Binary.id(), Bool.id()));
crate::initialize(&session);
assert!(session.kernels().has_execute_parent(Binary.id(), Bool.id()));
}
#[test]
fn kernels_inserts_default_kernel_session() {
let session = VortexSession::empty();
assert!(session.kernels().has_execute_parent(Binary.id(), Bool.id()));
}
}