use std::sync::Arc;
use crate::ast::SlotShape;
use crate::ast::{JitType, PortType, ReflectedValue, SliceArc, SlotType, Value};
use crate::dsl::factory::ConstArg;
pub trait Wire: Sized + 'static {
const PORT: PortType;
const JIT: Option<JitType>;
const RESOLVER: Option<crate::dsl::registry::DefaultResolver> = None;
const WIRE_COST: crate::ast::WireCost = crate::ast::WireCost::Data;
fn extract(v: &Value) -> Self;
fn inject(self) -> Value;
}
impl Wire for u64 {
const PORT: PortType = PortType::U64;
const JIT: Option<JitType> = Some(JitType::U64);
fn extract(v: &Value) -> Self {
v.as_u64()
}
fn inject(self) -> Value {
Value::U64(self)
}
}
impl Wire for u32 {
const PORT: PortType = PortType::U32;
const JIT: Option<JitType> = Some(JitType::U64);
fn extract(v: &Value) -> Self {
v.as_u64() as u32
}
fn inject(self) -> Value {
Value::U64(self as u64)
}
}
impl Wire for i32 {
const PORT: PortType = PortType::I32;
const JIT: Option<JitType> = Some(JitType::I64);
fn extract(v: &Value) -> Self {
v.as_i64() as i32
}
fn inject(self) -> Value {
Value::I64(self as i64)
}
}
impl Wire for i64 {
const PORT: PortType = PortType::I64;
const JIT: Option<JitType> = Some(JitType::I64);
fn extract(v: &Value) -> Self {
v.as_i64()
}
fn inject(self) -> Value {
Value::I64(self)
}
}
impl Wire for u8 {
const PORT: PortType = PortType::U8;
const JIT: Option<JitType> = Some(JitType::U64);
fn extract(v: &Value) -> Self {
v.as_u64() as u8
}
fn inject(self) -> Value {
Value::U64(self as u64)
}
}
impl Wire for u16 {
const PORT: PortType = PortType::U16;
const JIT: Option<JitType> = Some(JitType::U64);
fn extract(v: &Value) -> Self {
v.as_u64() as u16
}
fn inject(self) -> Value {
Value::U64(self as u64)
}
}
impl Wire for i8 {
const PORT: PortType = PortType::I8;
const JIT: Option<JitType> = Some(JitType::I64);
fn extract(v: &Value) -> Self {
v.as_i64() as i8
}
fn inject(self) -> Value {
Value::I64(self as i64)
}
}
impl Wire for i16 {
const PORT: PortType = PortType::I16;
const JIT: Option<JitType> = Some(JitType::I64);
fn extract(v: &Value) -> Self {
v.as_i64() as i16
}
fn inject(self) -> Value {
Value::I64(self as i64)
}
}
impl Wire for u128 {
const PORT: PortType = PortType::U128;
const JIT: Option<JitType> = None;
fn extract(v: &Value) -> Self {
v.as_u128()
}
fn inject(self) -> Value {
Value::U128(crate::ast::Bits128::from_u128(self))
}
}
impl Wire for i128 {
const PORT: PortType = PortType::I128;
const JIT: Option<JitType> = None;
fn extract(v: &Value) -> Self {
v.as_i128()
}
fn inject(self) -> Value {
Value::I128(crate::ast::Bits128::from_i128(self))
}
}
impl Wire for crate::ast::Bits128 {
const PORT: PortType = PortType::Reg128;
const JIT: Option<JitType> = None;
fn extract(v: &Value) -> Self {
v.as_reg_bits()
}
fn inject(self) -> Value {
Value::Reg128(self, crate::ast::RegLanes::Raw)
}
}
macro_rules! impl_wire_reg {
($arr:ty, $port:ident, $view:ident, $to:ident, $from:ident) => {
impl Wire for $arr {
const PORT: PortType = PortType::$port;
const JIT: Option<JitType> = None;
fn extract(v: &Value) -> Self {
v.as_reg_bits().$to()
}
fn inject(self) -> Value {
Value::Reg128(
crate::ast::Bits128::$from(self),
crate::ast::RegLanes::$view,
)
}
}
};
}
impl_wire_reg!([i8; 16], RegI8x16, I8x16, lanes_i8, from_lanes_i8);
impl_wire_reg!([i16; 8], RegI16x8, I16x8, lanes_i16, from_lanes_i16);
impl_wire_reg!([i32; 4], RegI32x4, I32x4, lanes_i32, from_lanes_i32);
impl_wire_reg!([i64; 2], RegI64x2, I64x2, lanes_i64, from_lanes_i64);
impl_wire_reg!([half::f16; 8], RegF16x8, F16x8, lanes_f16, from_lanes_f16);
impl_wire_reg!([f32; 4], RegF32x4, F32x4, lanes_f32, from_lanes_f32);
impl_wire_reg!([f64; 2], RegF64x2, F64x2, lanes_f64, from_lanes_f64);
impl Wire for f64 {
const PORT: PortType = PortType::F64;
const JIT: Option<JitType> = Some(JitType::F64);
fn extract(v: &Value) -> Self {
v.as_f64()
}
fn inject(self) -> Value {
Value::F64(self)
}
}
impl Wire for f32 {
const PORT: PortType = PortType::F32;
const JIT: Option<JitType> = Some(JitType::U64);
fn extract(v: &Value) -> Self {
f32::from_bits(v.as_u64() as u32)
}
fn inject(self) -> Value {
Value::U64(self.to_bits() as u64)
}
}
impl Wire for half::f16 {
const PORT: PortType = PortType::F16;
const JIT: Option<JitType> = Some(JitType::U64);
fn extract(v: &Value) -> Self {
half::f16::from_bits(v.as_u64() as u16)
}
fn inject(self) -> Value {
Value::U64(self.to_bits() as u64)
}
}
impl Wire for bool {
const PORT: PortType = PortType::Bool;
const JIT: Option<JitType> = Some(JitType::Bool);
fn extract(v: &Value) -> Self {
match v {
Value::Bool(b) => *b,
Value::U64(n) => *n != 0,
other => panic!(
"Wire<bool>::extract: type-checker routed {other:?} \
to a Bool slot"
),
}
}
fn inject(self) -> Value {
Value::Bool(self)
}
}
impl Wire for String {
const PORT: PortType = PortType::Str;
const JIT: Option<JitType> = None;
fn extract(v: &Value) -> Self {
match v {
Value::Str(s) => s.to_string(),
other => panic!("Wire<String>::extract: expected Str, got {other:?}"),
}
}
fn inject(self) -> Value {
Value::Str(self.into())
}
}
impl Wire for std::sync::Arc<str> {
const PORT: PortType = PortType::Str;
const JIT: Option<JitType> = None;
fn extract(v: &Value) -> Self {
match v {
Value::Str(s) => s.clone(),
other => panic!("Wire<Arc<str>>::extract: expected Str, got {other:?}"),
}
}
fn inject(self) -> Value {
Value::Str(self)
}
}
impl Wire for std::sync::Arc<dyn std::any::Any + Send + Sync> {
const PORT: PortType = PortType::Handle;
const JIT: Option<JitType> = None;
fn extract(v: &Value) -> Self {
match v {
Value::Handle(arc) => arc.clone(),
other => panic!("Wire<Arc<dyn Any>>::extract: expected Handle, got {other:?}"),
}
}
fn inject(self) -> Value {
Value::Handle(self)
}
}
impl Wire for Box<dyn ReflectedValue> {
const PORT: PortType = PortType::Ext;
const JIT: Option<JitType> = None;
fn extract(v: &Value) -> Self {
match v {
Value::Ext(b) => b.clone_reflected(),
other => panic!("Wire<Box<dyn ReflectedValue>>::extract: expected Ext, got {other:?}"),
}
}
fn inject(self) -> Value {
Value::Ext(self)
}
}
impl Wire for Arc<[u8]> {
const PORT: PortType = PortType::Bytes;
const JIT: Option<JitType> = None;
fn extract(v: &Value) -> Self {
match v {
Value::Bytes(b) => b.clone(),
other => panic!("Wire<Arc<[u8]>>::extract: expected Bytes, got {other:?}"),
}
}
fn inject(self) -> Value {
Value::Bytes(self)
}
}
impl Wire for Vec<u8> {
const PORT: PortType = PortType::Bytes;
const JIT: Option<JitType> = None;
fn extract(v: &Value) -> Self {
match v {
Value::Bytes(b) => b.to_vec(),
other => panic!("Wire<Vec<u8>>::extract: expected Bytes, got {other:?}"),
}
}
fn inject(self) -> Value {
Value::Bytes(self.into())
}
}
impl Wire for Arc<serde_json::Value> {
const PORT: PortType = PortType::Json;
const JIT: Option<JitType> = None;
fn extract(v: &Value) -> Self {
match v {
Value::Json(j) => j.clone(),
other => panic!("Wire<Arc<Json>>::extract: expected Json, got {other:?}"),
}
}
fn inject(self) -> Value {
Value::Json(self)
}
}
macro_rules! impl_wire_vec {
($elem:ty, $variant:ident, $port:ident) => {
impl Wire for SliceArc<$elem> {
const PORT: PortType = PortType::$port;
const JIT: Option<JitType> = None;
fn extract(v: &Value) -> Self {
match v {
Value::$variant(arc) => arc.clone(),
other => panic!(
concat!(
"Wire<SliceArc<",
stringify!($elem),
">>::extract: expected ",
stringify!($variant),
", got {:?}"
),
other
),
}
}
fn inject(self) -> Value {
Value::$variant(self)
}
}
impl Wire for Vec<$elem> {
const PORT: PortType = PortType::$port;
const JIT: Option<JitType> = None;
fn extract(v: &Value) -> Self {
match v {
Value::$variant(arc) => arc.as_slice().to_vec(),
other => panic!(
concat!(
"Wire<Vec<",
stringify!($elem),
">>::extract: expected ",
stringify!($variant),
", got {:?}"
),
other
),
}
}
fn inject(self) -> Value {
Value::$variant(SliceArc::from_vec(self))
}
}
};
}
impl_wire_vec!(f32, VecF32, VecF32);
impl_wire_vec!(i32, VecI32, VecI32);
impl_wire_vec!(f64, VecF64, VecF64);
impl_wire_vec!(i64, VecI64, VecI64);
impl_wire_vec!(half::f16, VecF16, VecF16);
impl_wire_vec!(i16, VecI16, VecI16);
impl_wire_vec!(i8, VecI8, VecI8);
impl<T: Wire> Wire for Option<T> {
const PORT: PortType = T::PORT;
const JIT: Option<JitType> = None;
fn extract(v: &Value) -> Self {
match v {
Value::None => None,
_ => Some(T::extract(v)),
}
}
fn inject(self) -> Value {
match self {
None => Value::None,
Some(t) => t.inject(),
}
}
}
#[derive(Clone)]
pub struct Ext<T>(pub T);
impl<T> std::ops::Deref for Ext<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<T> std::ops::DerefMut for Ext<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.0
}
}
impl<T: ReflectedValue + Clone + 'static> Wire for Ext<T> {
const PORT: PortType = PortType::Ext;
const JIT: Option<JitType> = None;
fn extract(v: &Value) -> Self {
match v {
Value::Ext(boxed) => {
let any = boxed.as_any();
match any.downcast_ref::<T>() {
Some(t) => Ext(t.clone()),
None => panic!(
"Wire<Ext<{}>>::extract: ReflectedValue downcast failed; \
got runtime type {:?}",
std::any::type_name::<T>(),
boxed.type_name()
),
}
}
other => panic!("Wire<Ext>::extract: expected Ext, got {other:?}"),
}
}
fn inject(self) -> Value {
Value::Ext(Box::new(self.0))
}
}
pub struct DynamicOutputs<T>(pub Vec<T>);
impl<T> std::ops::Deref for DynamicOutputs<T> {
type Target = Vec<T>;
fn deref(&self) -> &Vec<T> {
&self.0
}
}
pub struct Config<T>(pub T);
impl<T> std::ops::Deref for Config<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<T: Wire> Wire for Config<T> {
const PORT: PortType = T::PORT;
const JIT: Option<JitType> = T::JIT;
const RESOLVER: Option<crate::dsl::registry::DefaultResolver> = T::RESOLVER;
const WIRE_COST: crate::ast::WireCost = crate::ast::WireCost::Config;
fn extract(v: &Value) -> Self {
Config(T::extract(v))
}
fn inject(self) -> Value {
self.0.inject()
}
}
pub struct Resolved<R: ResolverKind, T: 'static + Send + Sync> {
inner: std::sync::Arc<T>,
_r: std::marker::PhantomData<fn() -> R>,
}
impl<R: ResolverKind, T: 'static + Send + Sync> std::ops::Deref for Resolved<R, T> {
type Target = T;
fn deref(&self) -> &T {
&self.inner
}
}
impl<R: ResolverKind, T: 'static + Send + Sync> Resolved<R, T> {
pub fn from_arc(inner: std::sync::Arc<T>) -> Self {
Self {
inner,
_r: std::marker::PhantomData,
}
}
pub fn as_arc(&self) -> &std::sync::Arc<T> {
&self.inner
}
}
pub trait ResolverKind: 'static {
const RESOLVER: crate::dsl::registry::DefaultResolver;
}
pub struct GroupResolver;
impl ResolverKind for GroupResolver {
const RESOLVER: crate::dsl::registry::DefaultResolver =
crate::dsl::registry::DefaultResolver::Group;
}
impl<R: ResolverKind, T: 'static + Send + Sync> Wire for Resolved<R, T> {
const PORT: PortType = PortType::Handle;
const JIT: Option<JitType> = None;
const RESOLVER: Option<crate::dsl::registry::DefaultResolver> =
Some(<R as ResolverKind>::RESOLVER);
fn extract(v: &Value) -> Self {
match v {
Value::Handle(arc) => {
let inner = arc.clone().downcast::<T>().unwrap_or_else(|_| {
panic!(
"Wire<Resolved<_, {}>>::extract: Handle downcast failed",
std::any::type_name::<T>()
)
});
Resolved {
inner,
_r: std::marker::PhantomData,
}
}
other => panic!("Wire<Resolved>::extract: expected Handle, got {other:?}"),
}
}
fn inject(self) -> Value {
Value::Handle(self.inner)
}
}
pub trait ConstSource: Sized + 'static {
const SLOT: SlotType;
fn extract(arg: &ConstArg) -> Self;
}
impl ConstSource for u64 {
const SLOT: SlotType = SlotType::ConstU64;
fn extract(arg: &ConstArg) -> Self {
match arg {
ConstArg::Int(v) => *v,
other => panic!("ConstSource<u64>::extract: expected Int, got {other:?}"),
}
}
}
impl ConstSource for f64 {
const SLOT: SlotType = SlotType::ConstF64;
fn extract(arg: &ConstArg) -> Self {
match arg {
ConstArg::Float(v) => *v,
ConstArg::Int(v) => *v as f64,
other => panic!("ConstSource<f64>::extract: expected Float or Int, got {other:?}"),
}
}
}
impl ConstSource for bool {
const SLOT: SlotType = SlotType::ConstU64;
fn extract(arg: &ConstArg) -> Self {
match arg {
ConstArg::Int(v) => *v != 0,
other => panic!("ConstSource<bool>::extract: expected Int, got {other:?}"),
}
}
}
impl ConstSource for String {
const SLOT: SlotType = SlotType::ConstStr;
fn extract(arg: &ConstArg) -> Self {
match arg {
ConstArg::Str(s) => s.clone(),
other => panic!("ConstSource<String>::extract: expected Str, got {other:?}"),
}
}
}
impl<C: ConstSource> ConstSource for Vec<C> {
const SLOT: SlotType = SlotType::ConstVec;
fn extract(arg: &ConstArg) -> Self {
match arg {
ConstArg::List(items) => items.iter().map(C::extract).collect(),
other => panic!("ConstSource<Vec<_>>::extract: expected List, got {other:?}"),
}
}
}
pub struct Const<T>(pub T);
impl<T> std::ops::Deref for Const<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<T> std::ops::DerefMut for Const<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.0
}
}
pub trait PolydatSetup {}
#[inline]
pub fn ref_value(slots: &[u64]) -> &Value {
static NONE: Value = Value::None;
if slots.get(1).copied().unwrap_or(0) == 0 {
return &NONE;
}
unsafe { &*(slots[0] as usize as *const Value) }
}
#[inline]
pub fn read_poly(ty: PortType, slots: &[u64]) -> Value {
crate::compile::marshal::decode_slot(slots, ty)
}
#[inline]
pub fn write_poly(
ty: PortType,
v: Value,
scratch: &mut [crate::ast::ScratchBuf],
outputs: &mut [u64],
) {
use crate::ast::ScratchBuf;
if v.port_type() != ty {
panic!(
"a node produced a {:?} on an output the graph typed {:?}; a compiled engine \
cannot carry a value of another type than the slot's (engine_parity.md, A7)",
v.port_type(),
ty
);
}
match v {
Value::U64(x) => outputs[0] = x,
Value::I64(x) => outputs[0] = x as u64,
Value::F64(x) => outputs[0] = x.to_bits(),
Value::Bool(b) => outputs[0] = b as u64,
Value::Str(s) => scratch[0].set_str(&s),
Value::Bytes(b) => scratch[0].set_bytes(&b),
Value::Json(_) | Value::Ext(_) | Value::Handle(_) => scratch[0].set_value(v),
other => panic!("a {:?} value has no compiled slot form", other.port_type()),
}
if matches!(
scratch.first(),
Some(ScratchBuf::Str(_) | ScratchBuf::Bytes(_) | ScratchBuf::Value(_))
) && ty.slot_color() == crate::ast::SlotColor::Ref2
{
let (p, l) = scratch[0].ptr_len();
outputs[0] = p;
outputs[1] = l;
}
}