use std::fmt;
use std::sync::Arc;
use std::ops::Deref;
pub struct SliceArc<T: 'static> {
_owner: Arc<dyn std::any::Any + Send + Sync>,
ptr: *const T,
len: usize,
}
unsafe impl<T: Send + Sync + 'static> Send for SliceArc<T> {}
unsafe impl<T: Send + Sync + 'static> Sync for SliceArc<T> {}
#[allow(dead_code)]
pub(crate) struct OwnedSlice<T: 'static>(pub(crate) Arc<[T]>);
impl<T: Send + Sync + 'static> SliceArc<T> {
pub fn from_vec(v: Vec<T>) -> Self {
let arc: Arc<[T]> = Arc::from(v);
let ptr = arc.as_ptr();
let len = arc.len();
let owner: Arc<dyn std::any::Any + Send + Sync> = Arc::new(OwnedSlice(arc));
Self { _owner: owner, ptr, len }
}
pub unsafe fn from_borrowed(
owner: Arc<dyn std::any::Any + Send + Sync>,
slice: &[T],
) -> Self {
Self {
_owner: owner,
ptr: slice.as_ptr(),
len: slice.len(),
}
}
}
impl<T: 'static> SliceArc<T> {
pub fn as_slice(&self) -> &[T] {
unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
}
}
impl<T: Send + Sync + 'static> Clone for SliceArc<T> {
fn clone(&self) -> Self {
Self {
_owner: self._owner.clone(),
ptr: self.ptr,
len: self.len,
}
}
}
impl<T: 'static> Deref for SliceArc<T> {
type Target = [T];
fn deref(&self) -> &[T] {
unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
}
}
impl<T: PartialEq + 'static> PartialEq for SliceArc<T> {
fn eq(&self, other: &Self) -> bool {
if std::ptr::eq(self.ptr, other.ptr) && self.len == other.len {
return true;
}
self.as_slice() == other.as_slice()
}
}
impl<T: fmt::Debug + 'static> fmt::Debug for SliceArc<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SliceArc")
.field("len", &self.len)
.field("first", &self.as_slice().first())
.finish_non_exhaustive()
}
}
#[derive(Debug, Clone)]
pub enum Value {
U64(u64),
F64(f64),
Bool(bool),
Str(Arc<str>),
Bytes(Arc<[u8]>),
Json(Arc<serde_json::Value>),
Ext(Box<dyn ReflectedValue>),
Handle(Arc<dyn std::any::Any + Send + Sync>),
VecF32(SliceArc<f32>),
VecI32(SliceArc<i32>),
None,
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Value::U64(a), Value::U64(b)) => a == b,
(Value::F64(a), Value::F64(b)) => a == b,
(Value::Bool(a), Value::Bool(b)) => a == b,
(Value::Str(a), Value::Str(b)) => a == b,
(Value::Bytes(a), Value::Bytes(b)) => a == b,
(Value::Json(a), Value::Json(b)) => a == b,
(Value::None, Value::None) => true,
(Value::Ext(a), Value::Ext(b)) => {
a.type_name() == b.type_name() && a.display() == b.display()
}
(Value::Handle(a), Value::Handle(b)) => Arc::ptr_eq(a, b),
(Value::VecF32(a), Value::VecF32(b)) => a == b,
(Value::VecI32(a), Value::VecI32(b)) => a == b,
_ => false,
}
}
}
pub trait ReflectedValue: Send + Sync + std::fmt::Debug {
fn type_name(&self) -> &str;
fn display(&self) -> String;
fn to_json_value(&self) -> serde_json::Value {
serde_json::Value::String(self.display())
}
fn try_as_str(&self) -> Option<String> {
Some(self.display())
}
fn try_as_u64(&self) -> Option<u64> { None }
fn try_as_f64(&self) -> Option<f64> { None }
fn try_as_bytes(&self) -> Option<&[u8]> { None }
fn as_any(&self) -> &dyn std::any::Any;
fn clone_reflected(&self) -> Box<dyn ReflectedValue>;
}
impl Clone for Box<dyn ReflectedValue> {
fn clone(&self) -> Self {
self.clone_reflected()
}
}
impl Value {
pub fn as_u64(&self) -> u64 {
match self {
Value::U64(v) => *v,
_ => panic!("expected U64, got {:?}", self.port_type()),
}
}
pub fn as_f64(&self) -> f64 {
match self {
Value::F64(v) => *v,
_ => panic!("expected F64, got {:?}", self.port_type()),
}
}
pub fn as_bool(&self) -> bool {
match self {
Value::Bool(v) => *v,
_ => panic!("expected Bool, got {:?}", self.port_type()),
}
}
pub fn as_str(&self) -> &str {
match self {
Value::Str(v) => v,
_ => panic!("expected Str, got {:?}", self.port_type()),
}
}
pub fn as_bytes(&self) -> &[u8] {
match self {
Value::Bytes(v) => v,
_ => panic!("expected Bytes, got {:?}", self.port_type()),
}
}
pub fn as_json(&self) -> &serde_json::Value {
match self {
Value::Json(v) => v,
_ => panic!("expected Json, got {:?}", self.port_type()),
}
}
pub fn as_json_arc(&self) -> &Arc<serde_json::Value> {
match self {
Value::Json(v) => v,
_ => panic!("expected Json, got {:?}", self.port_type()),
}
}
pub fn port_type(&self) -> PortType {
match self {
Value::U64(_) => PortType::U64,
Value::F64(_) => PortType::F64,
Value::Bool(_) => PortType::Bool,
Value::Str(_) => PortType::Str,
Value::Bytes(_) => PortType::Bytes,
Value::Json(_) => PortType::Json,
Value::Ext(_) => PortType::Ext,
Value::Handle(_) => PortType::Handle,
Value::VecF32(_) => PortType::VecF32,
Value::VecI32(_) => PortType::VecI32,
Value::None => PortType::U64, }
}
pub fn as_vec_f32(&self) -> &[f32] {
match self {
Value::VecF32(arc) => arc,
_ => panic!("expected VecF32, got {:?}", self.port_type()),
}
}
pub fn as_vec_i32(&self) -> &[i32] {
match self {
Value::VecI32(arc) => arc,
_ => panic!("expected VecI32, got {:?}", self.port_type()),
}
}
pub fn as_handle<T: std::any::Any + Send + Sync>(&self) -> &T {
match self {
Value::Handle(arc) => arc.downcast_ref::<T>().unwrap_or_else(|| {
panic!(
"Handle downcast failed: expected {}",
std::any::type_name::<T>()
)
}),
_ => panic!("expected Handle, got {:?}", self.port_type()),
}
}
pub fn handle<T: std::any::Any + Send + Sync>(arc: Arc<T>) -> Self {
Value::Handle(arc as Arc<dyn std::any::Any + Send + Sync>)
}
pub fn to_display_string(&self) -> String {
match self {
Value::U64(v) => v.to_string(),
Value::F64(v) => format!("{v:?}"),
Value::Bool(v) => v.to_string(),
Value::Str(v) => v.to_string(),
Value::Bytes(v) => v.iter().map(|b| format!("{b:02x}")).collect(),
Value::Json(v) => v.to_string(),
Value::Ext(v) => v.display(),
Value::Handle(arc) => format!("<handle:{:?}>", arc.type_id()),
Value::VecF32(arc) => {
let mut s = String::with_capacity(arc.len() * 8 + 2);
s.push('[');
let mut first = true;
for v in arc.iter() {
if !first { s.push(','); }
first = false;
use std::fmt::Write;
let _ = write!(&mut s, "{v:?}");
}
s.push(']');
s
}
Value::VecI32(arc) => {
let mut s = String::with_capacity(arc.len() * 4 + 2);
s.push('[');
let mut first = true;
for v in arc.iter() {
if !first { s.push(','); }
first = false;
use std::fmt::Write;
let _ = write!(&mut s, "{v}");
}
s.push(']');
s
}
Value::None => String::new(),
}
}
pub fn to_display_strict(&self) -> Option<String> {
match self {
Value::None => None,
other => Some(other.to_display_string()),
}
}
pub fn to_json_value(&self) -> serde_json::Value {
match self {
Value::U64(v) => serde_json::Value::from(*v),
Value::F64(v) => serde_json::json!(*v),
Value::Bool(v) => serde_json::Value::from(*v),
Value::Str(v) => serde_json::Value::from(&**v),
Value::Bytes(v) => serde_json::Value::from(v.iter().map(|b| format!("{b:02x}")).collect::<String>()),
Value::Json(v) => (**v).clone(),
Value::Ext(v) => v.to_json_value(),
Value::Handle(_) => serde_json::Value::Null,
Value::VecF32(arc) => serde_json::Value::Array(
arc.iter().map(|f| serde_json::json!(*f)).collect()
),
Value::VecI32(arc) => serde_json::Value::Array(
arc.iter().map(|i| serde_json::Value::from(*i)).collect()
),
Value::None => serde_json::Value::Null,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PortType {
U64,
F64,
U32,
I32,
I64,
F32,
Bool,
Str,
Bytes,
Json,
Ext,
Handle,
VecF32,
VecI32,
}
impl fmt::Display for PortType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PortType::U64 => write!(f, "u64"),
PortType::F64 => write!(f, "f64"),
PortType::U32 => write!(f, "u32"),
PortType::I32 => write!(f, "i32"),
PortType::I64 => write!(f, "i64"),
PortType::F32 => write!(f, "f32"),
PortType::Bool => write!(f, "bool"),
PortType::Str => write!(f, "String"),
PortType::Bytes => write!(f, "bytes"),
PortType::Json => write!(f, "json"),
PortType::Ext => write!(f, "ext"),
PortType::Handle => write!(f, "handle"),
PortType::VecF32 => write!(f, "vec_f32"),
PortType::VecI32 => write!(f, "vec_i32"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Lifecycle {
Cycle,
Init,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum WireCost {
#[default]
Data,
Config,
}
#[derive(Debug, Clone)]
pub struct Port {
pub name: String,
pub typ: PortType,
pub lifecycle: Lifecycle,
pub wire_cost: WireCost,
pub constraint: Option<crate::dsl::const_constraints::ConstConstraint>,
}
impl Port {
pub fn new(name: impl Into<String>, typ: PortType) -> Self {
Self {
name: name.into(),
typ,
lifecycle: Lifecycle::Cycle,
wire_cost: WireCost::Data,
constraint: None,
}
}
pub fn with_lifecycle(name: impl Into<String>, typ: PortType, lifecycle: Lifecycle) -> Self {
Self {
name: name.into(),
typ,
lifecycle,
wire_cost: WireCost::Data,
constraint: None,
}
}
pub fn u64(name: impl Into<String>) -> Self {
Self::new(name, PortType::U64)
}
pub fn f64(name: impl Into<String>) -> Self {
Self::new(name, PortType::F64)
}
pub fn str(name: impl Into<String>) -> Self {
Self::new(name, PortType::Str)
}
pub fn bool(name: impl Into<String>) -> Self {
Self::new(name, PortType::Bool)
}
pub fn json(name: impl Into<String>) -> Self {
Self::new(name, PortType::Json)
}
pub fn handle(name: impl Into<String>) -> Self {
Self::new(name, PortType::Handle)
}
pub fn vec_f32(name: impl Into<String>) -> Self {
Self::new(name, PortType::VecF32)
}
pub fn vec_i32(name: impl Into<String>) -> Self {
Self::new(name, PortType::VecI32)
}
pub fn init(name: impl Into<String>, typ: PortType) -> Self {
Self::with_lifecycle(name, typ, Lifecycle::Init)
}
pub fn with_constraint(mut self, c: crate::dsl::const_constraints::ConstConstraint) -> Self {
self.constraint = Some(c);
self
}
pub fn config(mut self) -> Self {
self.wire_cost = WireCost::Config;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SlotType {
Wire,
ConstU64,
ConstF64,
ConstStr,
ConstVecU64,
ConstVecF64,
}
impl SlotType {
pub fn is_const(self) -> bool {
!matches!(self, SlotType::Wire)
}
pub fn is_wire(self) -> bool {
matches!(self, SlotType::Wire)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ConstValue {
U64(u64),
F64(f64),
Str(String),
VecU64(Vec<u64>),
VecF64(Vec<f64>),
}
impl ConstValue {
pub fn slot_type(&self) -> SlotType {
match self {
ConstValue::U64(_) => SlotType::ConstU64,
ConstValue::F64(_) => SlotType::ConstF64,
ConstValue::Str(_) => SlotType::ConstStr,
ConstValue::VecU64(_) => SlotType::ConstVecU64,
ConstValue::VecF64(_) => SlotType::ConstVecF64,
}
}
pub fn to_jit_u64s(&self) -> Vec<u64> {
match self {
ConstValue::U64(v) => vec![*v],
ConstValue::F64(v) => vec![v.to_bits()],
ConstValue::Str(_) => vec![],
ConstValue::VecU64(v) => v.clone(),
ConstValue::VecF64(v) => v.iter().map(|f| f.to_bits()).collect(),
}
}
}
#[derive(Debug, Clone)]
pub enum Slot {
Wire(Port),
Const {
name: String,
value: ConstValue,
},
}
impl Slot {
pub fn slot_type(&self) -> SlotType {
match self {
Slot::Wire(_) => SlotType::Wire,
Slot::Const { value, .. } => value.slot_type(),
}
}
pub fn wire(port: Port) -> Self { Slot::Wire(port) }
pub fn const_u64(name: impl Into<String>, v: u64) -> Self {
Slot::Const { name: name.into(), value: ConstValue::U64(v) }
}
pub fn const_f64(name: impl Into<String>, v: f64) -> Self {
Slot::Const { name: name.into(), value: ConstValue::F64(v) }
}
pub fn const_str(name: impl Into<String>, v: impl Into<String>) -> Self {
Slot::Const { name: name.into(), value: ConstValue::Str(v.into()) }
}
pub fn const_vec_u64(name: impl Into<String>, v: Vec<u64>) -> Self {
Slot::Const { name: name.into(), value: ConstValue::VecU64(v) }
}
pub fn const_vec_f64(name: impl Into<String>, v: Vec<f64>) -> Self {
Slot::Const { name: name.into(), value: ConstValue::VecF64(v) }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Default)]
pub enum Commutativity {
#[default]
Positional,
AllCommutative,
Groups(Vec<Vec<usize>>),
}
#[derive(Debug, Clone)]
pub struct NodeMeta {
pub name: String,
pub ins: Vec<Slot>,
pub outs: Vec<Port>,
}
impl NodeMeta {
pub fn wire_inputs(&self) -> Vec<&Port> {
self.ins.iter().filter_map(|s| match s {
Slot::Wire(p) => Some(p),
Slot::Const { .. } => None,
}).collect()
}
pub fn const_slots(&self) -> Vec<(&str, &ConstValue)> {
self.ins.iter().filter_map(|s| match s {
Slot::Const { name, value } => Some((name.as_str(), value)),
Slot::Wire(_) => None,
}).collect()
}
pub fn jit_constants_from_slots(&self) -> Vec<u64> {
self.const_slots().iter()
.flat_map(|(_, v)| v.to_jit_u64s())
.collect()
}
}
pub type CompiledU64Op = Box<dyn Fn(&[u64], &mut [u64]) + Send + Sync>;
pub trait GkNode: Send + Sync {
fn meta(&self) -> &NodeMeta;
fn eval(&self, inputs: &[Value], outputs: &mut [Value]);
fn commutativity(&self) -> Commutativity {
Commutativity::Positional
}
fn accepts_none_inputs(&self) -> bool {
false
}
fn compiled_u64(&self) -> Option<CompiledU64Op> {
None
}
fn jit_constants(&self) -> Vec<u64> {
Vec::new()
}
}
pub fn compile_level_of(node: &dyn GkNode) -> CompileLevel {
#[cfg(feature = "jit")]
{
let jit_op = crate::jit::classify_node(node);
if !matches!(jit_op, crate::jit::JitOp::Fallback) {
return CompileLevel::Phase3;
}
}
if node.compiled_u64().is_some() {
CompileLevel::Phase2
} else {
CompileLevel::Phase1
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompileLevel {
Phase1,
Phase2,
Phase3,
}