use std::{cell::UnsafeCell, sync::atomic::AtomicU64};
use arrayvec::ArrayVec;
use bitflags::bitflags;
#[cfg(feature = "process-node")]
use crate::vlib::ProcessNode;
use crate::{
bindings::{
_vlib_node_registration, VLIB_NODE_FLAG_ADAPTIVE_MODE,
VLIB_NODE_FLAG_ALLOW_LAZY_NEXT_NODES, VLIB_NODE_FLAG_FRAME_NO_FREE_AFTER_DISPATCH,
VLIB_NODE_FLAG_IS_DROP, VLIB_NODE_FLAG_IS_HANDOFF, VLIB_NODE_FLAG_IS_OUTPUT,
VLIB_NODE_FLAG_IS_PUNT, VLIB_NODE_FLAG_SWITCH_FROM_INTERRUPT_TO_POLLING_MODE,
VLIB_NODE_FLAG_SWITCH_FROM_POLLING_TO_INTERRUPT_MODE, VLIB_NODE_FLAG_TRACE,
VLIB_NODE_FLAG_TRACE_SUPPORTED, vlib_error_desc_t, vlib_frame_t,
vlib_helper_get_global_main, vlib_helper_remove_node_from_registrations,
vlib_node_fn_registration_t, vlib_node_registration_t, vlib_node_runtime_t, vlib_node_t,
},
vlib::{MainRef, buffer::BufferRef},
vppinfra::VecRef,
};
pub const FRAME_SIZE: usize = crate::bindings::VLIB_FRAME_SIZE as usize;
pub const FRAME_DATA_ALIGN: usize = crate::bindings::VLIB_FRAME_DATA_ALIGN as usize;
pub const RUNTIME_DATA_ALIGN: usize = 8;
pub unsafe trait NextNodes {
type CNamesArray: AsRef<[*mut ::std::os::raw::c_char]>;
const C_NAMES: Self::CNamesArray;
fn into_u16(self) -> u16;
}
pub unsafe trait ErrorCounters {
type CDescriptionsArray: AsRef<[vlib_error_desc_t]>;
const C_DESCRIPTIONS: Self::CDescriptionsArray;
fn into_u16(self) -> u16;
}
impl<N: Node, const N_NEXT_NODES: usize> NodeRegistration<N, N_NEXT_NODES> {
pub const fn new(
registration: _vlib_node_registration<[*mut std::os::raw::c_char; N_NEXT_NODES]>,
) -> Self {
Self {
registration: UnsafeCell::new(registration),
_marker: ::std::marker::PhantomData,
}
}
pub unsafe fn register(&'static self) {
unsafe {
let vgm = vlib_helper_get_global_main();
let reg = self.registration.get();
(*reg).next_registration = (*vgm).node_registrations;
(*vgm).node_registrations = reg as *mut vlib_node_registration_t;
}
}
pub unsafe fn unregister(&self) {
unsafe {
let vgm = vlib_helper_get_global_main();
vlib_helper_remove_node_from_registrations(
vgm,
self.registration.get() as *mut vlib_node_registration_t,
);
}
}
pub unsafe fn register_node_fn(&self, node_fn: *mut vlib_node_fn_registration_t) {
unsafe {
let reg = self.registration.get();
(*node_fn).next_registration = (*reg).node_fn_registrations;
(*reg).node_fn_registrations = node_fn;
}
}
pub unsafe fn node_runtime_from_ptr<'a>(
&self,
ptr: *mut vlib_node_runtime_t,
) -> &'a mut NodeRuntimeRef<N> {
unsafe { NodeRuntimeRef::from_ptr_mut(ptr) }
}
pub unsafe fn frame_from_ptr<'a>(&self, ptr: *mut vlib_frame_t) -> &'a mut FrameRef<N> {
unsafe { FrameRef::from_ptr_mut(ptr) }
}
pub unsafe fn node_from_ptr<'a>(&self, ptr: *mut vlib_node_t) -> &'a mut NodeRef<N> {
unsafe { NodeRef::from_ptr_mut(ptr) }
}
pub const fn name_ptr(&self) -> *const std::os::raw::c_char {
unsafe { (*self.registration.get()).name }
}
}
bitflags! {
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct NodeFlags: u16 {
const FRAME_NO_FREE_AFTER_DISPATCH = VLIB_NODE_FLAG_FRAME_NO_FREE_AFTER_DISPATCH as u16;
const IS_OUTPUT = VLIB_NODE_FLAG_IS_OUTPUT as u16;
const IS_DROP = VLIB_NODE_FLAG_IS_DROP as u16;
const IS_PUNT = VLIB_NODE_FLAG_IS_PUNT as u16;
const IS_HANDOFF = VLIB_NODE_FLAG_IS_HANDOFF as u16;
const TRACE = VLIB_NODE_FLAG_TRACE as u16;
const SWITCH_FROM_INTERRUPT_TO_POLLING_MODE = VLIB_NODE_FLAG_SWITCH_FROM_INTERRUPT_TO_POLLING_MODE as u16;
const SWITCH_FROM_POLLING_TO_INTERRUPT_MODE = VLIB_NODE_FLAG_SWITCH_FROM_POLLING_TO_INTERRUPT_MODE as u16;
const TRACE_SUPPORTED = VLIB_NODE_FLAG_TRACE_SUPPORTED as u16;
const ADAPTIVE_MODE = VLIB_NODE_FLAG_ADAPTIVE_MODE as u16;
const ALLOW_LAZY_NEXT_NODES = VLIB_NODE_FLAG_ALLOW_LAZY_NEXT_NODES as u16;
}
}
unsafe impl<N: Node, const N_NEXT_NODES: usize> Send for NodeRegistration<N, N_NEXT_NODES> {}
unsafe impl<N: Node, const N_NEXT_NODES: usize> Sync for NodeRegistration<N, N_NEXT_NODES> {}
pub struct NodeRegistration<N: Node, const N_NEXT_NODES: usize> {
registration: UnsafeCell<_vlib_node_registration<[*mut std::os::raw::c_char; N_NEXT_NODES]>>,
_marker: std::marker::PhantomData<N>,
}
#[repr(transparent)]
pub struct NodeRuntimeRef<N: ?Sized>(foreign_types::Opaque, std::marker::PhantomData<N>);
impl<N> NodeRuntimeRef<N> {
#[inline(always)]
pub unsafe fn from_ptr_mut<'a>(ptr: *mut vlib_node_runtime_t) -> &'a mut Self {
unsafe { &mut *(ptr as *mut _) }
}
#[inline(always)]
pub fn as_ptr(&self) -> *mut vlib_node_runtime_t {
self as *const _ as *mut _
}
#[inline(always)]
pub fn flags(&self) -> NodeFlags {
unsafe { NodeFlags::from_bits_truncate((*self.as_ptr()).flags) }
}
pub fn node_index(&self) -> u32 {
unsafe { (*self.as_ptr()).node_index }
}
pub fn node(&self, vm: &MainRef) -> &NodeRef<N> {
unsafe {
let nodes = VecRef::from_raw_mut((*vm.as_ptr()).node_main.nodes);
NodeRef::from_ptr_mut(*nodes.get_unchecked(self.node_index() as usize) as *mut _)
}
}
}
impl<N: Node> NodeRuntimeRef<N> {
pub fn runtime_data(&self) -> &N::RuntimeData {
unsafe { &*((*self.as_ptr()).runtime_data.as_ptr() as *const N::RuntimeData) }
}
pub fn runtime_data_mut(&mut self) -> &mut N::RuntimeData {
unsafe { &mut *((*self.as_ptr()).runtime_data.as_ptr() as *mut N::RuntimeData) }
}
pub fn increment_error_counter(&self, vm: &MainRef, counter: N::Errors, increment: u64) {
self.node(vm)
.increment_error_counter(vm, counter, increment)
}
}
#[cfg(feature = "process-node")]
impl<N: ProcessNode> NodeRuntimeRef<N> {
pub fn increment_process_error_counter(
&self,
vm: &MainRef,
counter: N::Errors,
increment: u64,
) {
self.node(vm)
.increment_process_error_counter(vm, counter, increment)
}
}
pub struct FrameRef<N: Node + ?Sized>(foreign_types::Opaque, std::marker::PhantomData<N>);
impl<N: Node + ?Sized> FrameRef<N> {
pub unsafe fn from_ptr_mut<'a>(ptr: *mut vlib_frame_t) -> &'a mut Self {
unsafe { &mut *(ptr as *mut _) }
}
pub fn as_ptr(&self) -> *mut vlib_frame_t {
self as *const _ as *mut _
}
pub fn vector(&self) -> &[N::Vector] {
unsafe {
let vec = (self.as_ptr() as *const u8).add((*(self.as_ptr())).vector_offset as usize)
as *const N::Vector;
std::slice::from_raw_parts(vec, (*(self.as_ptr())).n_vectors as usize)
}
}
pub fn scalar(&self) -> &N::Scalar {
unsafe {
&*((self.as_ptr() as *const u8).add((*(self.as_ptr())).scalar_offset as usize)
as *const N::Scalar)
}
}
pub fn aux(&self) -> &[N::Aux] {
unsafe {
let aux = (self.as_ptr() as *const u8).add((*(self.as_ptr())).aux_offset as usize)
as *const N::Aux;
std::slice::from_raw_parts(aux, (*(self.as_ptr())).n_vectors as usize)
}
}
}
pub trait VectorBufferIndex: Send + Copy {
fn as_u32_slice(slice: &[Self]) -> &[u32];
}
impl<N, V> FrameRef<N>
where
N: Node<Vector = V> + ?Sized,
V: VectorBufferIndex,
{
#[inline(always)]
pub unsafe fn get_buffers<'me, 'vm, 'buf: 'vm + 'me, const ARRAY_N: usize>(
&'me self,
vm: &'vm MainRef,
to: &mut ArrayVec<&'buf mut BufferRef<N::FeatureData>, ARRAY_N>,
) -> &'me [N::Vector] {
unsafe {
let from = self.vector();
vm.get_buffers(N::Vector::as_u32_slice(from), to);
from
}
}
}
#[repr(transparent)]
pub struct NodeRef<N>(foreign_types::Opaque, std::marker::PhantomData<N>);
impl<N> NodeRef<N> {
pub unsafe fn from_ptr_mut<'a>(ptr: *mut vlib_node_t) -> &'a mut Self {
unsafe { &mut *(ptr as *mut _) }
}
pub fn as_ptr(&self) -> *mut vlib_node_t {
self as *const _ as *mut _
}
}
impl<N: Node> NodeRef<N> {
pub fn increment_error_counter(&self, vm: &MainRef, counter: N::Errors, increment: u64) {
unsafe {
let em = &(*vm.as_ptr()).error_main;
let node_counter_base_index = (*self.as_ptr()).error_heap_index;
let ptr = em
.counters
.add(node_counter_base_index as usize + counter.into_u16() as usize);
AtomicU64::from_ptr(ptr).store(*ptr + increment, std::sync::atomic::Ordering::Relaxed);
}
}
}
#[cfg(feature = "process-node")]
impl<N: ProcessNode> NodeRef<N> {
pub fn increment_process_error_counter(
&self,
vm: &MainRef,
counter: N::Errors,
increment: u64,
) {
unsafe {
let em = &(*vm.as_ptr()).error_main;
let node_counter_base_index = (*self.as_ptr()).error_heap_index;
let ptr = em
.counters
.add(node_counter_base_index as usize + counter.into_u16() as usize);
AtomicU64::from_ptr(ptr).store(*ptr + increment, std::sync::atomic::Ordering::Relaxed);
}
}
}
pub trait Node {
type Vector;
type Scalar;
type Aux;
type NextNodes: NextNodes;
type RuntimeData: Send + Copy;
type TraceData: Send + Copy;
type Errors: ErrorCounters;
type FeatureData: Send;
unsafe fn function(
&self,
vm: &mut MainRef,
node: &mut NodeRuntimeRef<Self>,
frame: &mut FrameRef<Self>,
) -> u16;
}
#[cfg(test)]
mod tests {
use crate::{
bindings::{vlib_error_desc_t, vlib_node_registration_t},
vlib::{self, node::NodeRegistration},
};
enum Errors {}
unsafe impl vlib::node::ErrorCounters for Errors {
type CDescriptionsArray = [vlib_error_desc_t; 0];
const C_DESCRIPTIONS: Self::CDescriptionsArray = [];
fn into_u16(self) -> u16 {
todo!()
}
}
#[derive(Copy, Clone)]
enum NextNodes {
_Drop,
}
unsafe impl vlib::node::NextNodes for NextNodes {
type CNamesArray = [*mut ::std::os::raw::c_char; 1];
const C_NAMES: Self::CNamesArray = [c"drop".as_ptr().cast_mut()];
fn into_u16(self) -> u16 {
self as u16
}
}
struct Node;
impl vlib::node::Node for Node {
type Vector = vlib::BufferIndex;
type Scalar = ();
type Aux = ();
type NextNodes = NextNodes;
type RuntimeData = ();
type TraceData = ();
type Errors = Errors;
type FeatureData = ();
unsafe fn function(
&self,
_vm: &mut vlib::MainRef,
_node: &mut vlib::NodeRuntimeRef<Self>,
_frame: &mut vlib::FrameRef<Self>,
) -> u16 {
unreachable!()
}
}
#[test]
fn test_node_reg() {
let node: NodeRegistration<Node, 0> =
std::hint::black_box(NodeRegistration::new(vlib_node_registration_t::default()));
let node = Box::new(node);
let node = Box::into_raw(node);
unsafe {
(*node).register();
(*node).unregister();
let _ = Box::from_raw(node);
}
}
}