#![warn(
missing_docs,
missing_copy_implementations,
missing_debug_implementations
)]
use std::collections::HashSet;
use proc_macro::TokenStream;
use quote::quote;
use syn::spanned::Spanned;
#[derive(Default)]
struct PluginRegister {
version: Option<String>,
description: Option<String>,
}
impl syn::parse::Parse for PluginRegister {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
const EXPECTED_KEYS: &[&str] = &["version", "description"];
let mut info = PluginRegister::default();
let mut seen_keys = HashSet::new();
loop {
if input.is_empty() {
break;
}
let key = syn::Ident::parse(input)?.to_string();
if seen_keys.contains(&key) {
panic!("Duplicated key \"{key}\". Keys can only be specified once.");
}
input.parse::<syn::Token![:]>()?;
match key.as_str() {
"version" => {
info.version = Some(<syn::LitStr as syn::parse::Parse>::parse(input)?.value())
}
"description" => {
info.description =
Some(<syn::LitStr as syn::parse::Parse>::parse(input)?.value())
}
_ => {
return Err(syn::Error::new(
key.span(),
format!("Unknown key \"{key}\". Valid keys are: {EXPECTED_KEYS:?}."),
));
}
}
input.parse::<syn::Token![,]>()?;
seen_keys.insert(key);
}
Ok(info)
}
}
#[proc_macro]
pub fn vlib_plugin_register(ts: TokenStream) -> TokenStream {
let PluginRegister {
version,
description,
} = syn::parse_macro_input!(ts as PluginRegister);
let mut version_elems = version
.expect("Missing required attribute \"version\"")
.into_bytes();
if version_elems.len() > 63 {
panic!("Version string exceeds limit of 63 characters");
}
version_elems.extend(std::iter::repeat_n(0, (version_elems.len()..64).count()));
let description = if let Some(description) = description {
let description = format!("{}\0", description);
quote!(#description.as_ptr() as *const ::std::os::raw::c_char)
} else {
quote!(std::ptr::null_mut())
};
let output = quote!(
#[doc(hidden)]
#[unsafe(link_section = ".vlib_plugin_registration")]
#[unsafe(no_mangle)]
#[allow(non_upper_case_globals, non_snake_case)]
#[used]
pub static mut vlib_plugin_registration: ::vpp_plugin::bindings::vlib_plugin_registration_t = ::vpp_plugin::bindings::vlib_plugin_registration_t {
version: [#(#version_elems as ::std::os::raw::c_char),*],
description: #description,
..::vpp_plugin::bindings::vlib_plugin_registration_t::new()
};
);
output.into()
}
#[proc_macro_attribute]
pub fn vlib_init_function(_attribute: TokenStream, function: TokenStream) -> TokenStream {
let item: syn::Item = syn::parse_macro_input!(function);
if let syn::Item::Fn(function) = item {
let syn::ItemFn {
attrs,
block,
vis,
sig:
syn::Signature {
ident,
unsafety,
constness,
abi,
inputs,
output,
..
},
..
} = function;
match vis {
syn::Visibility::Inherited => {}
_ => panic!("#[vlib_init_function] methods must not have visibility modifiers"),
}
let init_fn_ident = syn::parse_str::<syn::Ident>(format!("__{}", ident).as_ref())
.expect("Unable to create identifier");
let ctor_fn_ident = syn::parse_str::<syn::Ident>(
format!("__vlib_add_init_function_init_{}", ident).as_ref(),
)
.expect("Unable to create identifier");
let dtor_fn_ident =
syn::parse_str::<syn::Ident>(format!("__vlib_rm_init_function_{}", ident).as_ref())
.expect("Unable to create identifier");
let init_list_elt_ident =
syn::parse_str::<syn::Ident>(format!("_VLIB_INIT_FUNCTION_INIT_{}", ident).as_ref())
.expect("Unable to create identifier");
let ident_lit = format!("{}\0", ident);
let output = quote!(
#(#attrs)*
#vis #unsafety #abi #constness fn #ident(#inputs) #output #block
unsafe extern "C" fn #init_fn_ident(vm: *mut ::vpp_plugin::bindings::vlib_main_t) -> *mut ::vpp_plugin::bindings::clib_error_t
{
if let Err(e) = #ident(::vpp_plugin::vlib::BarrierHeldMainRef::from_ptr_mut(vm)) {
e.into_raw()
} else {
std::ptr::null_mut()
}
}
static mut #init_list_elt_ident: ::vpp_plugin::bindings::_vlib_init_function_list_elt_t = ::vpp_plugin::bindings::_vlib_init_function_list_elt_t {
f: None,
name: std::ptr::null_mut(),
next_init_function: std::ptr::null_mut(),
runs_before: std::ptr::null_mut(),
runs_after: std::ptr::null_mut(),
init_order: std::ptr::null_mut(),
};
#[::vpp_plugin::macro_support::ctor::ctor(crate_path = ::vpp_plugin::macro_support::ctor)]
unsafe fn #ctor_fn_ident () {
unsafe {
let vgm = ::vpp_plugin::bindings::vlib_helper_get_global_main();
#init_list_elt_ident.next_init_function = (*vgm).init_function_registrations;
(*vgm).init_function_registrations = std::ptr::addr_of_mut!(#init_list_elt_ident);
#init_list_elt_ident.f = Some(#init_fn_ident);
#init_list_elt_ident.name = #ident_lit.as_ptr() as *mut ::std::os::raw::c_char;
}
}
#[::vpp_plugin::macro_support::ctor::dtor(crate_path = ::vpp_plugin::macro_support::ctor)]
unsafe fn #dtor_fn_ident() {
let vgm = ::vpp_plugin::bindings::vlib_helper_get_global_main();
let mut this = (*vgm).init_function_registrations;
if this.is_null() {
return;
}
if this == std::ptr::addr_of_mut!(#init_list_elt_ident) {
(*vgm).init_function_registrations = (*this).next_init_function;
return;
}
let mut prev = this;
this = (*this).next_init_function;
while !this.is_null() {
if this == std::ptr::addr_of_mut!(#init_list_elt_ident) {
(*prev).next_init_function = (*this).next_init_function;
return;
}
prev = this;
this = (*this).next_init_function;
}
}
);
output.into()
} else {
panic!("#[vlib_init_function] items must be functions");
}
}
#[proc_macro_derive(NextNodes, attributes(next_node))]
pub fn derive_next_nodes(input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input as syn::DeriveInput);
let syn::DeriveInput { ident, data, .. } = input;
if let syn::Data::Enum(data) = data {
let mut next_nodes = vec![];
for variant in &data.variants {
if let Some((_, exp_discriminant)) = &variant.discriminant {
return syn::Error::new_spanned(
exp_discriminant,
"Use of explicit discriminants not allowed",
)
.into_compile_error()
.into();
}
if !matches!(variant.fields, syn::Fields::Unit) {
return syn::Error::new_spanned(&variant.fields, "Only unit variants can be used")
.into_compile_error()
.into();
}
let next_node_attr = variant
.attrs
.iter()
.find(|x| x.path().is_ident("next_node"))
.expect("Missing attribute \"next_node\"");
let syn::Meta::NameValue(next_node_name_value) = &next_node_attr.meta else {
return syn::Error::new_spanned(next_node_attr, "Unsupported \"next_node\" attribute syntax. Should be #[next_node = \"<node-name>\".").into_compile_error().into();
};
let syn::Expr::Lit(next_node) = &next_node_name_value.value else {
return syn::Error::new_spanned(next_node_name_value, "Unsupported \"next_node\" attribute syntax. Should be #[next_node = \"<node-name>\".").into_compile_error().into();
};
let syn::Lit::Str(next_node) = &next_node.lit else {
return syn::Error::new_spanned(next_node, "Unsupported \"next_node\" attribute syntax. Should be #[next_node = \"<node-name>\".").into_compile_error().into();
};
next_nodes.push(format!("{}\0", next_node.value()));
}
let n_next_nodes = data.variants.len();
let output = quote!(
#[automatically_derived]
unsafe impl ::vpp_plugin::vlib::node::NextNodes for #ident {
type CNamesArray = [*mut ::std::os::raw::c_char; #n_next_nodes];
const C_NAMES: Self::CNamesArray = [#(#next_nodes.as_ptr() as *mut ::std::os::raw::c_char),*];
fn into_u16(self) -> u16 {
self as u16
}
}
);
output.into()
} else {
panic!("#[derive(NextNodes)] can only be used on enums");
}
}
struct ErrorCounterAttribute {
name: Option<String>,
description: String,
severity: syn::Ident,
}
impl syn::parse::Parse for ErrorCounterAttribute {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
const VALID_SEVERITIES: &[&str] = &["INFO", "WARNING", "ERROR", "CRITICAL"];
let mut name_or_description_kw = syn::Ident::parse(input)?;
let name = if name_or_description_kw == "name" {
input.parse::<syn::Token![=]>()?;
let name = <syn::LitStr as syn::parse::Parse>::parse(input)?.value();
input.parse::<syn::Token![,]>()?;
name_or_description_kw = syn::Ident::parse(input)?;
Some(name)
} else {
None
};
if name_or_description_kw != "description" {
return Err(syn::Error::new(
name_or_description_kw.span(),
"Expected: description = \"<...>\", severity = <severity>".to_string(),
));
}
input.parse::<syn::Token![=]>()?;
let description = <syn::LitStr as syn::parse::Parse>::parse(input)?.value();
input.parse::<syn::Token![,]>()?;
let severity_kw = syn::Ident::parse(input)?;
if severity_kw != "severity" {
return Err(syn::Error::new(
severity_kw.span(),
"Expected: description = \"<...>\", severity = <severity>".to_string(),
));
}
input.parse::<syn::Token![=]>()?;
let severity = syn::Ident::parse(input)?;
if !VALID_SEVERITIES.contains(&severity.to_string().as_str()) {
return Err(syn::Error::new(
severity.span(),
format!(
"Invalid severity \"{}\". Valid severities are: {:?}.",
severity, VALID_SEVERITIES
),
));
}
Ok(Self {
name,
description,
severity,
})
}
}
#[proc_macro_derive(ErrorCounters, attributes(error_counter))]
pub fn derive_error_counters(input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input as syn::DeriveInput);
let syn::DeriveInput { ident, data, .. } = input;
if let syn::Data::Enum(data) = data {
let mut error_counter_desc = vec![];
for variant in &data.variants {
if let Some((_, exp_discriminant)) = &variant.discriminant {
return syn::Error::new_spanned(
exp_discriminant,
"Use of explicit discriminants not allowed",
)
.into_compile_error()
.into();
}
if !matches!(variant.fields, syn::Fields::Unit) {
return syn::Error::new_spanned(&variant.fields, "Only unit variants can be used")
.into_compile_error()
.into();
}
let error_counter_attr = variant
.attrs
.iter()
.find(|x| x.path().is_ident("error_counter"))
.expect("Missing attribute \"error_counter\"");
let syn::Meta::List(error_counter_list) = &error_counter_attr.meta else {
return syn::Error::new_spanned(error_counter_attr, "Unsupported \"error_counter\" attribute syntax. Should be #[error_counter(description = \"...\")]").into_compile_error().into();
};
let ErrorCounterAttribute {
name,
description,
severity,
} = match syn::parse2::<ErrorCounterAttribute>(error_counter_list.tokens.clone()) {
Ok(v) => v,
Err(e) => return proc_macro::TokenStream::from(e.to_compile_error()),
};
let name = if let Some(name) = name {
format!("{}\0", name)
} else {
format!("{}\0", variant.ident)
};
let description = format!("{}\0", description);
let severity = syn::parse_str::<syn::Ident>(&format!(
"vl_counter_severity_e_VL_COUNTER_SEVERITY_{}",
severity
))
.expect("Unable to create identifier");
error_counter_desc.push(quote!(
::vpp_plugin::bindings::vlib_error_desc_t {
name: #name.as_ptr() as *mut std::ffi::c_char,
desc: #description.as_ptr() as *mut std::ffi::c_char,
severity: ::vpp_plugin::bindings::#severity,
stats_entry_index: 0,
},
));
}
let n_error_counters = data.variants.len();
let output = quote!(
#[automatically_derived]
unsafe impl ::vpp_plugin::vlib::node::ErrorCounters for #ident {
type CDescriptionsArray = [::vpp_plugin::bindings::vlib_error_desc_t; #n_error_counters];
const C_DESCRIPTIONS: Self::CDescriptionsArray = [
#(#error_counter_desc)*
];
fn into_u16(self) -> u16 {
self as u16
}
}
);
output.into()
} else {
panic!("#[derive(ErrorCounters)] can only be used on enums");
}
}
const CPU_MARCH_TO_CPU_AND_TARGET_FEATURE: &[(&str, Option<&str>, Option<&str>)] = &[
("scalar", Some("x86_64"), None),
("hsw", Some("x86_64"), Some("avx2")),
("skx", Some("x86_64"), Some("avx512f")),
("icl", Some("x86_64"), Some("avx512bitalg")),
(
"octeontx2",
Some("aarch64"),
Some("crc,lse,rdm,pan,lor,vh,ras,dpb,sha2,aes"),
),
(
"thunderx2t99",
Some("aarch64"),
Some("crc,lse,rdm,pan,lor,vh,sha2,aes"),
),
("cortexa72", Some("aarch64"), Some("crc,sha2,aes")),
(
"neoversen1",
Some("aarch64"),
Some("crc,lse,rdm,pan,lor,vh,ras,dpb,sha2,aes"),
),
(
"neoversen2",
Some("aarch64"),
Some(
"crc,lse,rdm,pan,lor,vh,ras,dpb,rcpc,paca,pacg,jsconv,dotprod,dit,flagm,ssbs,sb,dpb2,bti,sve2",
),
),
];
#[derive(Default)]
struct Node {
name: Option<syn::LitStr>,
instance: Option<syn::Ident>,
runtime_data_default: Option<syn::Ident>,
format_trace: Option<syn::Ident>,
}
impl Node {
fn parse(&mut self, meta: syn::meta::ParseNestedMeta) -> Result<(), syn::Error> {
const EXPECTED_KEYS: &[&str] =
&["name", "instance", "runtime_data_default", "format_trace"];
if meta.path.is_ident("name") {
self.name = Some(meta.value()?.parse()?);
Ok(())
} else if meta.path.is_ident("instance") {
self.instance = Some(meta.value()?.parse()?);
Ok(())
} else if meta.path.is_ident("runtime_data_default") {
self.runtime_data_default = Some(meta.value()?.parse()?);
Ok(())
} else if meta.path.is_ident("format_trace") {
self.format_trace = Some(meta.value()?.parse()?);
Ok(())
} else {
Err(syn::Error::new(
meta.path.span(),
format!(
"Unknown attribute \"{:?}\". Valid keys are: {EXPECTED_KEYS:?}.",
meta.path.get_ident()
),
))
}
}
}
#[proc_macro_attribute]
pub fn vlib_node(attributes: TokenStream, s: TokenStream) -> TokenStream {
let mut attrs = Node::default();
let node_parser = syn::meta::parser(|meta| attrs.parse(meta));
syn::parse_macro_input!(attributes with node_parser);
let Node {
name,
instance,
runtime_data_default,
format_trace,
} = attrs;
let item: syn::Item = syn::parse_macro_input!(s);
if let syn::Item::Struct(syn::ItemStruct {
attrs,
vis,
struct_token,
ident,
generics,
fields,
semi_token,
}) = item
{
let name = name.expect("Missing attribute \"name\". This is required.");
let name_lit = format!("{}\0", name.value());
let instance = instance.expect("Missing attribute \"instance\". This is required.");
let reg_ident =
syn::parse_str::<syn::Ident>(format!("{}_NODE_REGISTRATION", ident).as_ref())
.expect("Unable to create identifier");
let add_node_fn_ident = syn::parse_str::<syn::Ident>(
format!("__vlib_add_node_registration_{}", ident).as_ref(),
)
.expect("Unable to create identifier");
let rm_node_fn_ident =
syn::parse_str::<syn::Ident>(format!("__vlib_rm_node_registration_{}", ident).as_ref())
.expect("Unable to create identifier");
let (format_trace_output, format_trace) = match format_trace {
Some(format_trace) => {
let thunk_format_trace =
syn::parse_str::<syn::Ident>(format!("__{}", format_trace).as_ref())
.expect("Unable to create identifier");
(
quote!(
unsafe extern "C" fn #thunk_format_trace(s: *mut u8, args: *mut ::vpp_plugin::bindings::va_list) -> *mut u8 {
unsafe {
let mut args = std::mem::transmute::<_, ::vpp_plugin::macro_support::va_list::VaList<'_>>(args);
let vm = args.get::<*const ::vpp_plugin::bindings::vlib_main_t>().cast_mut();
let node = args.get::<*const ::vpp_plugin::bindings::vlib_node_t>().cast_mut();
let t = args.get::<*const <#ident as ::vpp_plugin::vlib::node::Node>::TraceData>();
let str = #format_trace(&mut ::vpp_plugin::vlib::MainRef::from_ptr_mut(vm), &mut #reg_ident.node_from_ptr(node), &*t);
let mut s = ::vpp_plugin::vppinfra::vec::Vec::from_raw(s);
s.extend(str.as_bytes());
s.into_raw()
}
}
),
quote!(Some(#thunk_format_trace)),
)
}
None => (quote!(), quote!(None)),
};
let runtime_data = match runtime_data_default {
Some(runtime_data_default) => {
quote!(::std::ptr::addr_of!(#runtime_data_default) as *mut ::std::os::raw::c_void)
}
None => quote!({
::vpp_plugin::const_assert!(::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::RuntimeData>() == 0);
std::ptr::null_mut()
}),
};
let mut march_outputs = vec![];
for (cpu_march, cpu, target_feature) in CPU_MARCH_TO_CPU_AND_TARGET_FEATURE {
let raw_node_fn_ident =
syn::parse_str::<syn::Ident>(format!("__{}_{}", ident, cpu_march).as_ref())
.expect("Unable to create identifier");
let registration_ident = syn::parse_str::<syn::Ident>(
format!("{}_FN_REGISTRATION_{}", ident, cpu_march).as_ref(),
)
.expect("Unable to create identifier");
let reg_fn_ident = syn::parse_str::<syn::Ident>(
format!("{}_multiarch_register_{}", ident, cpu_march).as_ref(),
)
.expect("Unable to create identifier");
let march_variant_ident = syn::parse_str::<syn::Ident>(
format!(
"clib_march_variant_type_t_CLIB_MARCH_VARIANT_TYPE_{}",
cpu_march
)
.as_ref(),
)
.expect("Unable to create identifier");
let cpu_condition = if let Some(cpu) = cpu {
quote!(#[cfg(target_arch = #cpu)])
} else {
quote!()
};
let target_feature = if let Some(target_feature) = target_feature {
quote!(#[target_feature(enable = #target_feature)])
} else {
quote!()
};
let output = quote!(
#cpu_condition
#target_feature
#[doc(hidden)]
unsafe extern "C" fn #raw_node_fn_ident(
vm: *mut ::vpp_plugin::bindings::vlib_main_t,
node: *mut ::vpp_plugin::bindings::vlib_node_runtime_t,
frame: *mut ::vpp_plugin::bindings::vlib_frame_t,
) -> ::vpp_plugin::bindings::uword {
unsafe {
<#ident as ::vpp_plugin::vlib::node::Node>::function(
&#instance,
::vpp_plugin::vlib::MainRef::from_ptr_mut(vm),
#reg_ident.node_runtime_from_ptr(node),
#reg_ident.frame_from_ptr(frame),
)
}.into()
}
#cpu_condition
#[doc(hidden)]
static mut #registration_ident: ::vpp_plugin::bindings::vlib_node_fn_registration_t = ::vpp_plugin::bindings::vlib_node_fn_registration_t{
function: Some(#raw_node_fn_ident),
march_variant: ::vpp_plugin::bindings::#march_variant_ident,
next_registration: std::ptr::null_mut(),
};
#cpu_condition
#[doc(hidden)]
#[::vpp_plugin::macro_support::ctor::ctor(crate_path = ::vpp_plugin::macro_support::ctor)]
fn #reg_fn_ident() {
unsafe {
#reg_ident.register_node_fn(std::ptr::addr_of!(#registration_ident).cast_mut());
}
}
);
march_outputs.push(output);
}
let output = quote!(
#(#attrs)*
#vis #struct_token #ident #generics #fields #semi_token
#[::vpp_plugin::macro_support::ctor::ctor(crate_path = ::vpp_plugin::macro_support::ctor)]
fn #add_node_fn_ident() {
unsafe {
#reg_ident.register();
}
}
#[::vpp_plugin::macro_support::ctor::dtor(crate_path = ::vpp_plugin::macro_support::ctor)]
fn #rm_node_fn_ident() {
unsafe {
#reg_ident.unregister();
}
}
#format_trace_output
static #reg_ident: ::vpp_plugin::vlib::node::NodeRegistration<#ident, { <<#ident as ::vpp_plugin::vlib::node::Node>::NextNodes as ::vpp_plugin::vlib::node::NextNodes>::C_NAMES.len() } > = ::vpp_plugin::vlib::node::NodeRegistration::new(
::vpp_plugin::bindings::_vlib_node_registration {
function: None,
name: #name_lit.as_ptr() as *mut ::std::os::raw::c_char,
type_: ::vpp_plugin::bindings::vlib_node_type_t_VLIB_NODE_TYPE_INTERNAL,
error_counters: <<#ident as ::vpp_plugin::vlib::node::Node>::Errors as ::vpp_plugin::vlib::node::ErrorCounters>::C_DESCRIPTIONS.as_ptr().cast_mut(),
format_trace: #format_trace,
runtime_data: #runtime_data,
runtime_data_bytes: {
::vpp_plugin::const_assert!(::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::RuntimeData>() <= u8::MAX as usize);
::vpp_plugin::const_assert!(::std::mem::align_of::<<#ident as ::vpp_plugin::vlib::node::Node>::RuntimeData>() <= ::vpp_plugin::vlib::node::RUNTIME_DATA_ALIGN);
::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::RuntimeData>() as u8
},
vector_size: {
::vpp_plugin::const_assert!(::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::Vector>() <= u8::MAX as usize);
::vpp_plugin::const_assert!(::std::mem::align_of::<<#ident as ::vpp_plugin::vlib::node::Node>::Vector>() <= ::vpp_plugin::vlib::node::FRAME_DATA_ALIGN);
::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::Vector>() as u8
},
aux_size: {
::vpp_plugin::const_assert!(::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::Aux>() <= u8::MAX as usize);
::vpp_plugin::const_assert!(::std::mem::align_of::<<#ident as ::vpp_plugin::vlib::node::Node>::Aux>() <= ::vpp_plugin::vlib::node::FRAME_DATA_ALIGN);
::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::Aux>() as u8
},
scalar_size: {
::vpp_plugin::const_assert!(::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::Scalar>() <= u16::MAX as usize);
::vpp_plugin::const_assert!(::std::mem::align_of::<<#ident as ::vpp_plugin::vlib::node::Node>::Scalar>() <= ::vpp_plugin::vlib::node::FRAME_DATA_ALIGN);
::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::Scalar>() as u16
},
n_errors: <<#ident as ::vpp_plugin::vlib::node::Node>::Errors as ::vpp_plugin::vlib::node::ErrorCounters>::C_DESCRIPTIONS.len()
as u16,
n_next_nodes: <<#ident as ::vpp_plugin::vlib::node::Node>::NextNodes as ::vpp_plugin::vlib::node::NextNodes>::C_NAMES.len()
as u16,
next_nodes: <<#ident as ::vpp_plugin::vlib::node::Node>::NextNodes as ::vpp_plugin::vlib::node::NextNodes>::C_NAMES,
..::vpp_plugin::bindings::_vlib_node_registration::new()
});
#(#march_outputs)*
);
output.into()
} else {
panic!("#[vlib_node] items must be structs");
}
}
struct FeatureInit {
identifier: Option<syn::Ident>,
arc_name: Option<String>,
node: Option<syn::Ident>,
runs_before: Vec<String>,
runs_after: Vec<String>,
feature_data_type: Option<syn::Type>,
}
impl syn::parse::Parse for FeatureInit {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
const EXPECTED_KEYS: &[&str] = &[
"identifier",
"arc_name",
"node",
"runs_before",
"runs_after",
"feature_data_type",
];
let mut info = FeatureInit {
identifier: None,
arc_name: None,
node: None,
runs_before: Vec::new(),
runs_after: Vec::new(),
feature_data_type: None,
};
let mut seen_keys = HashSet::new();
loop {
if input.is_empty() {
break;
}
let key = syn::Ident::parse(input)?.to_string();
if seen_keys.contains(&key) {
panic!("Duplicated key \"{key}\". Keys can only be specified once.");
}
input.parse::<syn::Token![:]>()?;
match key.as_str() {
"identifier" => info.identifier = Some(syn::Ident::parse(input)?),
"arc_name" => {
info.arc_name = Some(<syn::LitStr as syn::parse::Parse>::parse(input)?.value())
}
"node" => info.node = Some(syn::Ident::parse(input)?),
"runs_before" => {
let runs_before_input;
syn::bracketed!(runs_before_input in input);
let runs_before = syn::punctuated::Punctuated::<syn::LitStr, syn::Token![,]>::parse_terminated(&runs_before_input)?;
info.runs_before = runs_before.into_iter().map(|s| s.value()).collect();
}
"runs_after" => {
let runs_after_input;
syn::bracketed!(runs_after_input in input);
let runs_after = syn::punctuated::Punctuated::<syn::LitStr, syn::Token![,]>::parse_terminated(&runs_after_input)?;
info.runs_after = runs_after.into_iter().map(|s| s.value()).collect();
}
"feature_data_type" => info.feature_data_type = Some(syn::Type::parse(input)?),
_ => {
return Err(syn::Error::new(
key.span(),
format!("Unknown key \"{key}\". Valid keys are: {EXPECTED_KEYS:?}."),
));
}
}
input.parse::<syn::Token![,]>()?;
seen_keys.insert(key);
}
Ok(info)
}
}
#[proc_macro]
pub fn vnet_feature_init(ts: TokenStream) -> TokenStream {
let FeatureInit {
identifier,
arc_name,
node,
runs_before,
runs_after,
feature_data_type,
} = syn::parse_macro_input!(ts as FeatureInit);
let ident = identifier.expect("Missing key \"identifier\". This is required.");
let ctor_fn_ident =
syn::parse_str::<syn::Ident>(format!("__vnet_add_feature_registration_{}", ident).as_ref())
.expect("Unable to create identifier");
let dtor_fn_ident =
syn::parse_str::<syn::Ident>(format!("__vnet_rm_feature_registration_{}", ident).as_ref())
.expect("Unable to create identifier");
let arc_name = arc_name.expect("Missing key \"arc_name\". This is required.");
let node = node.expect("Missing key \"node\". This is required.");
let reg_ident = syn::parse_str::<syn::Ident>(format!("{}_NODE_REGISTRATION", node).as_ref())
.expect("Unable to create identifier");
let arc_name_lit = format!("{arc_name}\0");
let runs_before_output = if runs_before.is_empty() {
quote!(std::ptr::null_mut())
} else {
let runs_before = runs_before.iter().map(|s| {
let s = format!("{s}\0");
quote!(#s.as_ptr() as *mut ::std::os::raw::c_char)
});
quote!(&[#(#runs_before),*, std::ptr::null_mut()] as *const *mut ::std::os::raw::c_char as *mut *mut ::std::os::raw::c_char)
};
let runs_after_output = if runs_after.is_empty() {
quote!(std::ptr::null_mut())
} else {
let runs_after = runs_after.iter().map(|s| {
let s = format!("{s}\0");
quote!(#s.as_ptr() as *mut ::std::os::raw::c_char)
});
quote!(&[#(#runs_after),*, std::ptr::null_mut()] as *const *mut ::std::os::raw::c_char as *mut *mut ::std::os::raw::c_char)
};
let feature_data_type = feature_data_type
.unwrap_or_else(|| syn::parse_str::<syn::Type>("()").expect("Unable to create identifier"));
let output = quote!(
#[doc(hidden)]
#[::vpp_plugin::macro_support::ctor::ctor(crate_path = ::vpp_plugin::macro_support::ctor)]
fn #ctor_fn_ident() {
unsafe { #ident.register(); }
}
#[doc(hidden)]
#[::vpp_plugin::macro_support::ctor::dtor(crate_path = ::vpp_plugin::macro_support::ctor)]
fn #dtor_fn_ident() {
unsafe { #ident.unregister(); }
}
static #ident: ::vpp_plugin::vnet::feature::FeatureRegistration<#feature_data_type> = unsafe {
::vpp_plugin::vnet::feature::FeatureRegistration::new(
::vpp_plugin::bindings::vnet_feature_registration_t {
arc_name: #arc_name_lit.as_ptr() as *mut ::std::os::raw::c_char,
runs_before: #runs_before_output,
runs_after: #runs_after_output,
enable_disable_cb: None,
..::vpp_plugin::bindings::vnet_feature_registration_t::new()
},
&#reg_ident,
)
};
);
output.into()
}
#[proc_macro_attribute]
pub fn vlib_cli_command(attributes: TokenStream, function: TokenStream) -> TokenStream {
let mut path: Option<syn::LitStr> = None;
let mut short_help: Option<syn::LitStr> = None;
let attr_parser = syn::meta::parser(|meta| {
if meta.path.is_ident("path") {
path = Some(meta.value()?.parse()?);
Ok(())
} else if meta.path.is_ident("short_help") {
short_help = Some(meta.value()?.parse()?);
Ok(())
} else {
Err(meta.error("unsupported vlib_cli_command property"))
}
});
syn::parse_macro_input!(attributes with attr_parser);
let item: syn::Item = syn::parse_macro_input!(function);
if let syn::Item::Fn(function) = item {
let syn::ItemFn {
attrs,
block,
vis,
sig:
syn::Signature {
ident,
unsafety,
constness,
abi,
inputs,
output,
..
},
..
} = function;
let path = path.expect(
"Missing attribute 'path'. Example: #[vlib_cli_command(path = \"rust-example\")]",
);
let path = format!("{}\0", path.value());
let short_help = if let Some(short_help) = short_help {
let short_help = format!("{}\0", short_help.value());
quote!(#short_help.as_ptr() as *mut ::std::os::raw::c_char)
} else {
quote!(std::ptr::null_mut())
};
let raw_fn_ident = syn::parse_str::<syn::Ident>(format!("__{}", ident).as_ref())
.expect("Unable to create identifier");
let ctor_fn_ident = syn::parse_str::<syn::Ident>(
format!("__vlib_cli_command_registration_{}", ident).as_ref(),
)
.expect("Unable to create identifier");
let dtor_fn_ident = syn::parse_str::<syn::Ident>(
format!("__vlib_cli_command_unregistration_{}", ident).as_ref(),
)
.expect("Unable to create identifier");
let cli_command_ident =
syn::parse_str::<syn::Ident>(format!("_VLIB_CLI_COMMAND_{}", ident).as_ref())
.expect("Unable to create identifier");
let output = quote!(
#(#attrs)*
#vis #unsafety #abi #constness fn #ident(#inputs) #output #block
#[doc(hidden)]
unsafe extern "C" fn #raw_fn_ident(
vm: *mut ::vpp_plugin::bindings::vlib_main_t,
input: *mut ::vpp_plugin::bindings::unformat_input_t,
_cmd: *mut ::vpp_plugin::bindings::vlib_cli_command_t,
) -> *mut ::vpp_plugin::bindings::clib_error_t
{
let s = ::vpp_plugin::vppinfra::unformat::raw_unformat_line_input_to_string(input);
if let Err(e) = #ident(::vpp_plugin::vlib::BarrierHeldMainRef::from_ptr_mut(vm), s.as_str()) {
e.into_raw()
} else {
std::ptr::null_mut()
}
}
static #cli_command_ident: ::vpp_plugin::vlib::cli::CommandRegistration = ::vpp_plugin::vlib::cli::CommandRegistration::new(::vpp_plugin::bindings::vlib_cli_command_t {
path: #path.as_ptr() as *mut ::std::os::raw::c_char,
short_help: #short_help,
long_help: std::ptr::null_mut(), function: Some(#raw_fn_ident),
is_mp_safe: 0,
..::vpp_plugin::bindings::vlib_cli_command_t::new()
});
#[::vpp_plugin::macro_support::ctor::ctor(crate_path = ::vpp_plugin::macro_support::ctor)]
unsafe fn #ctor_fn_ident () {
unsafe {
#cli_command_ident.register();
}
}
#[::vpp_plugin::macro_support::ctor::dtor(crate_path = ::vpp_plugin::macro_support::ctor)]
unsafe fn #dtor_fn_ident() {
unsafe {
#cli_command_ident.unregister();
}
}
);
output.into()
} else {
panic!("#[vlib_cli_command] items must be functions");
}
}
#[proc_macro_attribute]
pub fn vlib_process_node(attributes: TokenStream, item: TokenStream) -> TokenStream {
#[derive(Default)]
struct ProcessNodeAttrs {
name: Option<String>,
instance: Option<syn::Ident>,
runtime_data_default: Option<syn::Ident>,
log2_stack_bytes: Option<u16>,
}
impl ProcessNodeAttrs {
fn parse(&mut self, meta: syn::meta::ParseNestedMeta) -> syn::Result<()> {
if meta.path.is_ident("name") {
self.name = Some(meta.value()?.parse::<syn::LitStr>()?.value());
Ok(())
} else if meta.path.is_ident("log2_stack_bytes") {
self.log2_stack_bytes = Some(meta.value()?.parse::<syn::LitInt>()?.base10_parse()?);
Ok(())
} else if meta.path.is_ident("instance") {
self.instance = Some(meta.value()?.parse()?);
Ok(())
} else if meta.path.is_ident("runtime_data_default") {
self.runtime_data_default = Some(meta.value()?.parse()?);
Ok(())
} else {
Err(syn::Error::new(
meta.path.span(),
"Unknown attribute. Valid attributes are: name, instance, log2_stack_bytes and runtime_data_default",
))
}
}
}
let mut attrs = ProcessNodeAttrs::default();
let attr_parser = syn::meta::parser(|meta| attrs.parse(meta));
syn::parse_macro_input!(attributes with attr_parser);
let ProcessNodeAttrs {
name,
instance,
log2_stack_bytes,
runtime_data_default,
} = attrs;
let item: syn::Item = syn::parse_macro_input!(item);
if let syn::Item::Struct(syn::ItemStruct {
attrs,
vis,
struct_token,
ident,
generics,
fields,
semi_token,
}) = item
{
let name = name.expect("Missing attribute \"name\". This is required.");
let name_lit = format!("{}\0", name);
let instance = instance.expect("Missing attribute \"instance\". This is required.");
let reg_ident =
syn::parse_str::<syn::Ident>(format!("{}_NODE_REGISTRATION", ident).as_ref())
.expect("Unable to create identifier");
let add_node_fn_ident = syn::parse_str::<syn::Ident>(
format!("__vlib_add_node_registration_{}", ident).as_ref(),
)
.expect("Unable to create identifier");
let rm_node_fn_ident =
syn::parse_str::<syn::Ident>(format!("__vlib_rm_node_registration_{}", ident).as_ref())
.expect("Unable to create identifier");
let raw_node_fn_ident = syn::parse_str::<syn::Ident>(format!("__fn_{}", ident).as_ref())
.expect("Unable to create identifier");
let runtime_data = match runtime_data_default {
Some(runtime_data_default) => {
quote!(::std::ptr::addr_of!(#runtime_data_default) as *mut ::std::os::raw::c_void)
}
None => quote!({
::vpp_plugin::const_assert!(::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::ProcessNode>::RuntimeData>() == 0);
std::ptr::null_mut()
}),
};
let log2_stack_bytes = log2_stack_bytes.unwrap_or_default();
let output = quote!(
#(#attrs)*
#vis #struct_token #ident #generics #fields #semi_token
#[::vpp_plugin::macro_support::ctor::ctor(crate_path = ::vpp_plugin::macro_support::ctor)]
fn #add_node_fn_ident() {
unsafe {
#reg_ident.register();
}
}
#[::vpp_plugin::macro_support::ctor::dtor(crate_path = ::vpp_plugin::macro_support::ctor)]
fn #rm_node_fn_ident() {
unsafe {
#reg_ident.unregister();
}
}
#[doc(hidden)]
unsafe extern "C" fn #raw_node_fn_ident(
vm: *mut ::vpp_plugin::bindings::vlib_main_t,
node: *mut ::vpp_plugin::bindings::vlib_node_runtime_t,
frame: *mut ::vpp_plugin::bindings::vlib_frame_t,
) -> ::vpp_plugin::bindings::uword {
let fut = <#ident as ::vpp_plugin::vlib::ProcessNode>::function(
&#instance,
::vpp_plugin::vlib::MainRef::from_ptr_mut(vm),
#reg_ident.node_runtime_from_ptr(node),
);
let pinned_fut = ::std::pin::pin!(fut);
let context = ::vpp_plugin::vlib::process_node::ProcessAsyncContext::new(
::vpp_plugin::vlib::MainRef::from_ptr_mut(vm),
#reg_ident.node_runtime_from_ptr(node),
::vpp_plugin::vlib::process_node::LocalFutureObj::new(pinned_fut)
);
context.run();
}
static #reg_ident: ::vpp_plugin::vlib::process_node::ProcessNodeRegistration<#ident, { <<#ident as ::vpp_plugin::vlib::ProcessNode>::NextNodes as ::vpp_plugin::vlib::node::NextNodes>::C_NAMES.len() }> = ::vpp_plugin::vlib::process_node::ProcessNodeRegistration::new(
::vpp_plugin::bindings::_vlib_node_registration {
function: Some(#raw_node_fn_ident),
name: #name_lit.as_ptr() as *mut ::std::os::raw::c_char,
type_: ::vpp_plugin::bindings::vlib_node_type_t_VLIB_NODE_TYPE_PROCESS,
error_counters: <<#ident as ::vpp_plugin::vlib::ProcessNode>::Errors as ::vpp_plugin::vlib::node::ErrorCounters>::C_DESCRIPTIONS.as_ptr().cast_mut(),
runtime_data: #runtime_data,
runtime_data_bytes: {
::vpp_plugin::const_assert!(::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::ProcessNode>::RuntimeData>() <= u8::MAX as usize);
::vpp_plugin::const_assert!(::std::mem::align_of::<<#ident as ::vpp_plugin::vlib::ProcessNode>::RuntimeData>() <= ::vpp_plugin::vlib::node::RUNTIME_DATA_ALIGN);
::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::ProcessNode>::RuntimeData>() as u8
},
n_errors: <<#ident as ::vpp_plugin::vlib::ProcessNode>::Errors as ::vpp_plugin::vlib::node::ErrorCounters>::C_DESCRIPTIONS.len()
as u16,
n_next_nodes: <<#ident as ::vpp_plugin::vlib::ProcessNode>::NextNodes as ::vpp_plugin::vlib::node::NextNodes>::C_NAMES.len()
as u16,
process_log2_n_stack_bytes: #log2_stack_bytes,
next_nodes: <<#ident as ::vpp_plugin::vlib::ProcessNode>::NextNodes as ::vpp_plugin::vlib::node::NextNodes>::C_NAMES,
..::vpp_plugin::bindings::_vlib_node_registration::new()
});
);
output.into()
} else {
panic!("#[vlib_process_node] items must be structs");
}
}