use crate::buffer::{Buffer, FixedBuffer};
use crate::math::vector::scalar::ScalarVector4;
use crate::math::vector::traits::Vector as VecTrait;
use crate::math::Transcendental;
use crate::time::RenderContext;
use crate::traits::algorithm::Algorithm;
use crate::traits::node::NodeId;
use crate::traits::processable::Processable;
use crate::traits::{Node, ProcessResult};
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PortType {
Signal,
Control,
Clock,
Feedback,
Param,
}
impl PortType {
pub const fn name(&self) -> &'static str {
match self {
Self::Signal => "signal",
Self::Control => "control",
Self::Clock => "clock",
Self::Feedback => "feedback",
Self::Param => "param",
}
}
pub const fn is_signal_rate(&self) -> bool {
matches!(self, Self::Signal)
}
pub const fn is_control_rate(&self) -> bool {
matches!(self, Self::Control)
}
pub const fn is_clock(&self) -> bool {
matches!(self, Self::Clock)
}
}
impl fmt::Display for PortType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PortDirection {
Input,
Output,
}
impl PortDirection {
pub const fn name(&self) -> &'static str {
match self {
Self::Input => "input",
Self::Output => "output",
}
}
pub const fn is_input(&self) -> bool {
matches!(self, Self::Input)
}
pub const fn is_output(&self) -> bool {
matches!(self, Self::Output)
}
}
impl fmt::Display for PortDirection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PortId {
node: NodeId,
port_type: PortType,
direction: PortDirection,
index: u16,
}
impl PortId {
pub const fn new(
node: NodeId,
port_type: PortType,
direction: PortDirection,
index: u16,
) -> Self {
Self {
node,
port_type,
direction,
index,
}
}
pub const fn signal_in(node: NodeId, index: u16) -> Self {
Self::new(node, PortType::Signal, PortDirection::Input, index)
}
pub const fn signal_out(node: NodeId, index: u16) -> Self {
Self::new(node, PortType::Signal, PortDirection::Output, index)
}
pub const fn control_in(node: NodeId, index: u16) -> Self {
Self::new(node, PortType::Control, PortDirection::Input, index)
}
pub const fn control_out(node: NodeId, index: u16) -> Self {
Self::new(node, PortType::Control, PortDirection::Output, index)
}
pub const fn clock_in(node: NodeId, index: u16) -> Self {
Self::new(node, PortType::Clock, PortDirection::Input, index)
}
pub const fn clock_out(node: NodeId, index: u16) -> Self {
Self::new(node, PortType::Clock, PortDirection::Output, index)
}
pub const fn feedback_in(node: NodeId, index: u16) -> Self {
Self::new(node, PortType::Feedback, PortDirection::Input, index)
}
pub const fn feedback_out(node: NodeId, index: u16) -> Self {
Self::new(node, PortType::Feedback, PortDirection::Output, index)
}
pub const fn param(node: NodeId, index: u16) -> Self {
Self::new(node, PortType::Param, PortDirection::Input, index)
}
pub const fn node_id(&self) -> NodeId {
self.node
}
pub const fn port_type(&self) -> PortType {
self.port_type
}
pub const fn direction(&self) -> PortDirection {
self.direction
}
pub const fn index(&self) -> u16 {
self.index
}
pub const fn is_input(&self) -> bool {
self.direction.is_input()
}
pub const fn is_output(&self) -> bool {
self.direction.is_output()
}
pub const fn is_signal(&self) -> bool {
matches!(self.port_type, PortType::Signal)
}
pub const fn is_control(&self) -> bool {
matches!(self.port_type, PortType::Control)
}
pub const fn is_clock(&self) -> bool {
matches!(self.port_type, PortType::Clock)
}
pub const fn is_feedback(&self) -> bool {
matches!(self.port_type, PortType::Feedback)
}
pub const fn is_param(&self) -> bool {
matches!(self.port_type, PortType::Param)
}
}
impl fmt::Display for PortId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Node({}).{}_{}[{}]",
self.node.inner(),
self.port_type.name(),
self.direction.name(),
self.index
)
}
}
pub struct Port<T: Transcendental, const BUF_SIZE: usize> {
pub id: PortId,
pub name: String,
pub direction: PortDirection,
action: Option<Box<dyn Algorithm<T>>>,
pending_command: Option<T>,
buffer: FixedBuffer<T, BUF_SIZE>,
feedback_buffer: Option<FixedBuffer<T, BUF_SIZE>>,
downstream: Vec<(usize, usize)>,
downstream_input_ptrs: Vec<*mut Port<T, BUF_SIZE>>,
downstream_nodes: Vec<*mut crate::traits::NodeVariant<T, BUF_SIZE>>,
upstream_buffer: Option<*const FixedBuffer<T, BUF_SIZE>>,
feedback_downstream: Vec<(usize, usize)>,
feedback_ptrs: Vec<*mut Option<FixedBuffer<T, BUF_SIZE>>>,
data_received: bool,
upstream_node: *mut crate::traits::NodeVariant<T, BUF_SIZE>,
}
impl<T: Transcendental, const BUF_SIZE: usize> fmt::Debug for Port<T, BUF_SIZE> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Port")
.field("id", &self.id)
.field("name", &self.name)
.field("direction", &self.direction)
.field("has_action", &self.action.is_some())
.field("has_feedback", &self.feedback_buffer.is_some())
.field("downstream_len", &self.downstream.len())
.finish()
}
}
impl<T: Transcendental, const BUF_SIZE: usize> Port<T, BUF_SIZE> {
pub fn output(node_id: NodeId, index: u16, name: &str) -> Self {
Self {
id: PortId::signal_out(node_id, index),
name: name.to_string(),
direction: PortDirection::Output,
action: None,
pending_command: None,
buffer: FixedBuffer::new(),
feedback_buffer: None,
downstream: Vec::new(),
feedback_downstream: Vec::new(),
feedback_ptrs: Vec::new(),
downstream_input_ptrs: Vec::new(),
downstream_nodes: Vec::new(),
upstream_buffer: None,
upstream_node: std::ptr::null_mut(),
data_received: false,
}
}
pub fn input(node_id: NodeId, index: u16, name: &str) -> Self {
Self {
id: PortId::signal_in(node_id, index),
name: name.to_string(),
direction: PortDirection::Input,
action: None,
pending_command: None,
buffer: FixedBuffer::new(),
feedback_buffer: None,
downstream: Vec::new(),
feedback_downstream: Vec::new(),
feedback_ptrs: Vec::new(),
downstream_input_ptrs: Vec::new(),
downstream_nodes: Vec::new(),
upstream_buffer: None,
upstream_node: std::ptr::null_mut(),
data_received: false,
}
}
pub fn control_output(node_id: NodeId, index: u16, name: &str) -> Self {
Self {
id: PortId::control_out(node_id, index),
name: name.to_string(),
direction: PortDirection::Output,
action: None,
pending_command: None,
buffer: FixedBuffer::new(),
feedback_buffer: None,
downstream: Vec::new(),
feedback_downstream: Vec::new(),
feedback_ptrs: Vec::new(),
downstream_input_ptrs: Vec::new(),
downstream_nodes: Vec::new(),
upstream_buffer: None,
upstream_node: std::ptr::null_mut(),
data_received: false,
}
}
pub fn control_output_with_action(
node_id: NodeId,
index: u16,
name: &str,
action: Box<dyn Algorithm<T>>,
) -> Self {
Self {
id: PortId::control_out(node_id, index),
name: name.to_string(),
direction: PortDirection::Output,
action: Some(action),
pending_command: None,
buffer: FixedBuffer::new(),
feedback_buffer: None,
downstream: Vec::new(),
feedback_downstream: Vec::new(),
feedback_ptrs: Vec::new(),
downstream_input_ptrs: Vec::new(),
downstream_nodes: Vec::new(),
upstream_buffer: None,
upstream_node: std::ptr::null_mut(),
data_received: false,
}
}
pub fn control_input(node_id: NodeId, index: u16, name: &str) -> Self {
Self {
id: PortId::control_in(node_id, index),
name: name.to_string(),
direction: PortDirection::Input,
action: None,
pending_command: None,
buffer: FixedBuffer::new(),
feedback_buffer: None,
downstream: Vec::new(),
feedback_downstream: Vec::new(),
feedback_ptrs: Vec::new(),
downstream_input_ptrs: Vec::new(),
downstream_nodes: Vec::new(),
upstream_buffer: None,
upstream_node: std::ptr::null_mut(),
data_received: false,
}
}
pub fn id(&self) -> PortId {
self.id
}
pub fn name(&self) -> &str {
&self.name
}
pub fn is_input(&self) -> bool {
self.direction.is_input()
}
pub fn is_output(&self) -> bool {
self.direction.is_output()
}
pub fn buffer(&self) -> &FixedBuffer<T, BUF_SIZE> {
&self.buffer
}
pub(crate) fn buffer_mut(&mut self) -> &mut FixedBuffer<T, BUF_SIZE> {
&mut self.buffer
}
#[inline]
pub fn read(&self) -> &[T; BUF_SIZE] {
self.signal_buffer().as_array()
}
#[inline]
pub fn write(&mut self) -> &mut [T; BUF_SIZE] {
self.buffer_mut().as_mut_array()
}
#[inline]
pub fn write_from(&mut self, src: &[T; BUF_SIZE]) {
self.write().copy_from_slice(src);
}
#[inline]
pub fn feedback(&self) -> Option<&[T; BUF_SIZE]> {
self.feedback_buffer.as_ref().map(|b| b.as_array())
}
#[inline]
pub fn data_received(&self) -> bool {
self.data_received
}
#[inline]
pub fn set_data_received(&mut self, value: bool) {
self.data_received = value;
}
#[inline]
pub fn downstream(&self) -> &[(usize, usize)] {
&self.downstream
}
#[inline]
pub fn feedback_downstream(&self) -> &[(usize, usize)] {
&self.feedback_downstream
}
#[inline]
pub fn downstream_input_ptrs(&self) -> &[*mut Port<T, BUF_SIZE>] {
&self.downstream_input_ptrs
}
#[inline]
pub fn downstream_nodes(&self) -> &[*mut crate::traits::NodeVariant<T, BUF_SIZE>] {
&self.downstream_nodes
}
#[inline]
pub fn upstream_node(&self) -> *mut crate::traits::NodeVariant<T, BUF_SIZE> {
self.upstream_node
}
#[inline]
pub fn has_upstream_buffer(&self) -> bool {
self.upstream_buffer.is_some()
}
#[inline]
pub fn add_downstream(&mut self, target_node: usize, target_port: usize) {
self.downstream.push((target_node, target_port));
}
#[inline]
pub fn add_feedback_downstream(&mut self, target_node: usize, target_port: usize) {
self.feedback_downstream.push((target_node, target_port));
}
#[inline]
pub fn add_downstream_input_ptr(&mut self, ptr: *mut Port<T, BUF_SIZE>) {
self.downstream_input_ptrs.push(ptr);
}
#[inline]
pub fn add_downstream_node(&mut self, node: *mut crate::traits::NodeVariant<T, BUF_SIZE>) {
let val = node as usize;
if !self.downstream_nodes.iter().any(|&p| p as usize == val) {
self.downstream_nodes.push(node);
}
}
#[inline]
pub fn set_upstream_node(&mut self, node: *mut crate::traits::NodeVariant<T, BUF_SIZE>) {
self.upstream_node = node;
}
#[inline]
pub fn set_upstream_buffer(&mut self, buffer: Option<*const FixedBuffer<T, BUF_SIZE>>) {
self.upstream_buffer = buffer;
}
#[inline]
pub fn init_feedback_buffer(&mut self) {
self.feedback_buffer = Some(FixedBuffer::new());
}
#[inline]
pub fn feedback_buffer_ptr(&self) -> *mut Option<FixedBuffer<T, BUF_SIZE>> {
&self.feedback_buffer as *const Option<FixedBuffer<T, BUF_SIZE>>
as *mut Option<FixedBuffer<T, BUF_SIZE>>
}
#[inline]
pub fn add_feedback_ptr(&mut self, ptr: *mut Option<FixedBuffer<T, BUF_SIZE>>) {
self.feedback_ptrs.push(ptr);
}
#[inline]
pub fn is_zero_copy(&self) -> bool {
self.upstream_buffer.is_some()
&& self.action.is_none()
&& self.pending_command.is_none()
&& self.feedback_buffer.is_none()
}
#[allow(unsafe_code)]
pub fn signal_buffer(&self) -> &FixedBuffer<T, BUF_SIZE> {
match self.upstream_buffer {
Some(ptr) if self.is_zero_copy() => unsafe { &*ptr },
_ => &self.buffer,
}
}
pub fn pre_process(&mut self) {
if let Some(ref fb) = self.feedback_buffer {
let arr = self.buffer.as_mut_array();
let fb_arr = fb.as_array();
let chunks = BUF_SIZE / 4;
for chunk in 0..chunks {
let o = chunk * 4;
let a = ScalarVector4::load(&arr[o..o + 4]);
let b = ScalarVector4::load(&fb_arr[o..o + 4]);
a.add(&b).store(&mut arr[o..o + 4]);
}
for i in chunks * 4..BUF_SIZE {
arr[i] += fb_arr[i];
}
}
}
#[allow(unsafe_code)]
pub fn snapshot_feedback(&mut self) {
if let Some(ref mut fb) = self.feedback_buffer {
fb.copy_from(self.buffer.as_array());
for &ptr in &self.feedback_ptrs {
unsafe {
if let Some(ref mut target) = *ptr {
target.copy_from(fb.as_array());
}
}
}
}
}
#[allow(unsafe_code)]
pub fn propagate(
&self,
ctx: &RenderContext,
tick: &crate::time::ClockTick,
) -> ProcessResult<()> {
let buffer = self.buffer.as_array();
for &ptr in &self.downstream_input_ptrs {
unsafe {
let p = &mut *ptr;
if !p.is_zero_copy() {
p.run_action(Some(buffer))?;
}
p.data_received = true;
}
}
for &parent in &self.downstream_nodes {
unsafe {
let nv = &mut *parent;
for pi in 0..nv.num_signal_inputs() {
if let Some(p) = nv.input_port_mut(pi) {
p.pre_process();
}
}
nv.process_block(ctx, tick)?;
for po in 0..nv.num_signal_outputs() {
if let Some(p) = nv.output_port_mut(po) {
p.snapshot_feedback();
}
}
for po in 0..nv.num_signal_outputs() {
if let Some(p) = nv.output_port(po) {
p.propagate(ctx, tick)?;
}
}
}
}
Ok(())
}
pub fn run_action(
&mut self,
input: Option<&[T; BUF_SIZE]>,
) -> crate::traits::ProcessResult<()> {
match &mut self.action {
Some(action) => {
if let Some(cmd) = self.pending_command.take() {
action.apply_command(cmd);
}
let input_slice = input.map(|arr| arr.as_slice());
action.process(input_slice, self.buffer.as_mut_slice())
}
None => {
if let Some(cmd) = self.pending_command.take() {
self.buffer.fill(cmd);
} else if let Some(input_data) = input {
self.buffer.copy_from(input_data);
} else {
self.buffer.fill(T::ZERO);
}
Ok(())
}
}
}
pub fn set_value(&mut self, value: T) {
self.pending_command = Some(value);
}
}
#[allow(unsafe_code)]
unsafe impl<T: Transcendental + Send, const BUF_SIZE: usize> Send for Port<T, BUF_SIZE> {}
#[allow(unsafe_code)]
unsafe impl<T: Transcendental + Sync, const BUF_SIZE: usize> Sync for Port<T, BUF_SIZE> {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_port_id_creation() {
let node = NodeId(42);
let signal_in = PortId::signal_in(node, 0);
assert_eq!(signal_in.port_type(), PortType::Signal);
assert!(signal_in.is_input());
let clock_out = PortId::clock_out(node, 0);
assert_eq!(clock_out.port_type(), PortType::Clock);
assert!(clock_out.is_output());
let feedback_in = PortId::feedback_in(node, 0);
assert_eq!(feedback_in.port_type(), PortType::Feedback);
assert!(feedback_in.is_input());
}
}