use std::{
borrow::Cow,
fmt,
hash::{Hash, Hasher},
marker::PhantomData,
mem::{self, MaybeUninit},
ops::{Deref, DerefMut},
ptr::{self, NonNull},
slice,
str::Utf8Error,
};
use crate::{
bindings::{
vl_api_helper_client_index_to_registration, vl_api_helper_send_msg, vl_api_registration_t,
vl_msg_api_alloc, vl_msg_api_free,
},
vlib::BarrierHeldMainRef,
};
pub mod num_unaligned;
pub struct Message<T: ?Sized> {
pointer: NonNull<T>,
}
impl<T> Message<T> {
pub fn new(value: T) -> Self {
if std::mem::align_of::<T>() != 1 {
panic!("Messages must only contain #[repr(packed)] types");
}
unsafe {
let mut me = Self {
pointer: NonNull::new_unchecked(
vl_msg_api_alloc(std::mem::size_of::<T>() as i32) as *mut T
),
};
ptr::copy_nonoverlapping(&value, me.pointer.as_mut(), 1);
me
}
}
pub fn new_uninit() -> Message<MaybeUninit<T>> {
if std::mem::align_of::<T>() != 1 {
panic!("Messages must only contain #[repr(packed)] types");
}
unsafe {
Message {
pointer: NonNull::new_unchecked(vl_msg_api_alloc(
std::mem::size_of::<MaybeUninit<T>>() as i32,
) as *mut MaybeUninit<T>),
}
}
}
}
impl Message<u8> {
pub fn new_bytes(nbytes: u32) -> Message<u8> {
unsafe {
let mut me = Message {
pointer: NonNull::new_unchecked(vl_msg_api_alloc(nbytes as i32) as *mut u8),
};
ptr::write_bytes(me.pointer.as_mut(), 0, nbytes as usize);
me
}
}
}
impl<T: ?Sized> Message<T> {
pub fn into_raw(m: Self) -> *mut T {
let m = mem::ManuallyDrop::new(m);
m.pointer.as_ptr()
}
}
impl<T> Message<MaybeUninit<T>> {
pub unsafe fn assume_init(self) -> Message<T> {
unsafe {
let pointer = Message::into_raw(self);
Message {
pointer: NonNull::new_unchecked(pointer as *mut T),
}
}
}
pub fn write(mut self, value: T) -> Message<T> {
unsafe {
(*self).write(value);
self.assume_init()
}
}
}
impl<T: ?Sized> Deref for Message<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { self.pointer.as_ref() }
}
}
impl<T: ?Sized> DerefMut for Message<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.pointer.as_mut() }
}
}
impl<T: Default> Default for Message<T> {
fn default() -> Self {
Self::new_uninit().write(Default::default())
}
}
impl<T: ?Sized> Drop for Message<T> {
fn drop(&mut self) {
unsafe {
ptr::drop_in_place(self.pointer.as_ptr());
vl_msg_api_free(self.pointer.as_ptr().cast());
}
}
}
impl<T> From<T> for Message<T> {
fn from(value: T) -> Self {
Self::new(value)
}
}
impl<T: ?Sized + PartialOrd> PartialOrd for Message<T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
(**self).partial_cmp(&**other)
}
}
impl<T: ?Sized + Ord> Ord for Message<T> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
(**self).cmp(&**other)
}
}
impl<T: ?Sized + PartialEq> PartialEq for Message<T> {
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}
impl<T: ?Sized + Eq> Eq for Message<T> {}
impl<T: ?Sized + Hash> Hash for Message<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
impl<T: fmt::Display + ?Sized> fmt::Display for Message<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}
impl<T: fmt::Debug + ?Sized> fmt::Debug for Message<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<T: ?Sized> fmt::Pointer for Message<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let ptr: *const T = &**self;
fmt::Pointer::fmt(&ptr, f)
}
}
pub trait EndianSwap {
unsafe fn endian_swap(&mut self, to_net: bool);
}
#[repr(transparent)]
pub struct Registration(foreign_types::Opaque);
impl Registration {
pub unsafe fn from_ptr_mut<'a>(ptr: *mut vl_api_registration_t) -> &'a mut Self {
unsafe { &mut *(ptr as *mut _) }
}
pub fn as_ptr(&self) -> *mut vl_api_registration_t {
self as *const _ as *mut _
}
pub fn send_message<T>(&mut self, message: Message<T>) {
unsafe {
vl_api_helper_send_msg(self.as_ptr(), Message::into_raw(message).cast());
}
}
}
pub struct RegistrationScope<'scope>(PhantomData<&'scope ()>);
impl<'scope> RegistrationScope<'scope> {
pub fn from_client_index(
&self,
_vm: &BarrierHeldMainRef,
client_index: u32,
) -> Option<&'scope mut Registration> {
unsafe {
let ptr = vl_api_helper_client_index_to_registration(client_index.to_be());
if ptr.is_null() {
None
} else {
Some(Registration::from_ptr_mut(ptr))
}
}
}
}
pub fn registration_scope<F, T>(f: F) -> T
where
F: for<'scope> FnOnce(&'scope RegistrationScope<'scope>) -> T,
{
let scope = RegistrationScope(PhantomData);
f(&scope)
}
pub struct Stream<'scope, T> {
registration: &'scope mut Registration,
_phantom: PhantomData<T>,
}
impl<'scope, T> Stream<'scope, T> {
pub fn new(registration: &'scope mut Registration) -> Self {
Self {
registration,
_phantom: PhantomData,
}
}
pub fn send_message_ne(&mut self, message: Message<T>) {
self.registration.send_message(message);
}
pub fn into_inner(self) -> &'scope mut Registration {
self.registration
}
}
impl<'scope, T: EndianSwap> Stream<'scope, T> {
pub unsafe fn send_message(&mut self, mut message: Message<T>) {
unsafe {
message.endian_swap(true);
self.send_message_ne(message);
}
}
}
#[repr(C, packed)]
#[derive(Copy, Clone, Default)]
pub struct ApiString {
length: u32,
buf: [u8; 0],
}
impl ApiString {
pub const fn len(&self) -> u32 {
self.length
}
pub const fn is_empty(&self) -> bool {
self.length == 0
}
pub fn as_bytes(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
std::ptr::addr_of!(self.buf) as *const u8,
self.length as usize,
)
}
}
fn as_bytes_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
std::ptr::addr_of_mut!(self.buf) as *mut u8,
self.length as usize,
)
}
}
pub fn to_str(&self) -> Result<&str, Utf8Error> {
str::from_utf8(self.as_bytes())
}
pub fn to_string_lossy(&self) -> Cow<'_, str> {
String::from_utf8_lossy(self.as_bytes())
}
pub unsafe fn set_len(&mut self, length: u32) {
self.length = length;
}
pub fn copy_from_str(&mut self, s: &str) {
self.as_bytes_mut().copy_from_slice(s.as_bytes());
}
}
impl std::fmt::Debug for ApiString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(&self.to_string_lossy(), f)
}
}
impl EndianSwap for ApiString {
unsafe fn endian_swap(&mut self, _to_net: bool) {
self.length = self.length.to_be();
}
}
#[repr(C)]
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct ApiFixedString<const N: usize> {
buf: [u8; N],
}
impl<const N: usize> ApiFixedString<N> {
pub const fn len(&self) -> usize {
let mut len = 0;
while self.buf[len] != 0 {
len += 1;
}
len
}
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn as_bytes(&self) -> &[u8] {
&self.buf[..self.len()]
}
pub fn to_str(&self) -> Result<&str, Utf8Error> {
str::from_utf8(self.as_bytes())
}
pub fn to_string_lossy(&self) -> Cow<'_, str> {
String::from_utf8_lossy(self.as_bytes())
}
pub fn copy_from_str(&mut self, s: &str) {
let bytes = s.as_bytes();
self.buf[0..bytes.len()].copy_from_slice(bytes);
if bytes.len() + 1 < self.buf.len() {
self.buf[bytes.len()..].fill(0);
}
}
}
impl<const N: usize> std::fmt::Debug for ApiFixedString<N> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(&self.to_string_lossy(), f)
}
}
impl<const N: usize> EndianSwap for ApiFixedString<N> {
unsafe fn endian_swap(&mut self, _to_net: bool) {
}
}
impl<const N: usize> Default for ApiFixedString<N> {
fn default() -> Self {
Self { buf: [0; N] }
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::borrow::Cow;
#[test]
fn test_fixed_string_default() {
let s: ApiFixedString<10> = Default::default();
assert_eq!(s.len(), 0);
assert!(s.is_empty());
assert_eq!(s.as_bytes(), &[]);
assert_eq!(s.to_string_lossy(), Cow::Borrowed(""));
}
#[test]
fn test_fixed_string_copy_from_str() {
let mut s: ApiFixedString<10> = Default::default();
s.copy_from_str("hello");
assert_eq!(s.len(), 5);
assert!(!s.is_empty());
assert_eq!(s.as_bytes(), b"hello");
assert_eq!(s.to_string_lossy(), Cow::Borrowed("hello"));
}
#[test]
fn test_fixed_string_copy_from_str_with_padding() {
let mut s: ApiFixedString<10> = Default::default();
s.copy_from_str("hi");
assert_eq!(s.len(), 2);
assert_eq!(s.as_bytes(), b"hi");
assert_eq!(s.buf[2..], [0; 8]);
}
#[test]
fn test_fixed_string_copy_from_str_empty() {
let mut s: ApiFixedString<10> = Default::default();
s.copy_from_str("");
assert_eq!(s.len(), 0);
assert!(s.is_empty());
}
#[test]
fn test_fixed_string_copy_from_str_max_length() {
let mut s: ApiFixedString<5> = Default::default();
s.copy_from_str("abcd"); assert_eq!(s.len(), 4);
assert_eq!(s.as_bytes(), b"abcd");
}
#[test]
#[should_panic]
fn test_fixed_string_copy_from_str_too_long() {
let mut s: ApiFixedString<5> = Default::default();
s.copy_from_str("abcdef"); }
#[test]
fn test_fixed_string_to_str_valid_utf8() {
let mut s: ApiFixedString<10> = Default::default();
s.copy_from_str("hello");
assert_eq!(s.to_str().unwrap(), "hello");
}
#[test]
fn test_fixed_string_to_str_invalid_utf8() {
let mut s: ApiFixedString<10> = Default::default();
s.buf[0] = 0xff;
s.buf[1] = 0xfe;
s.buf[2] = 0;
assert!(s.to_str().is_err());
}
#[test]
fn test_fixed_string_to_string_lossy_invalid_utf8() {
let mut s: ApiFixedString<10> = Default::default();
s.copy_from_str("hello");
assert_eq!(s.to_string_lossy(), "hello");
s.buf[0] = 0xff;
s.buf[1] = 0;
assert_eq!(s.to_string_lossy(), "�");
}
#[test]
fn test_fixed_string_debug() {
let mut s: ApiFixedString<10> = Default::default();
s.copy_from_str("test");
assert_eq!(format!("{:?}", s), "\"test\"");
}
#[test]
fn test_fixed_string_partialeq() {
let mut s1: ApiFixedString<10> = Default::default();
s1.copy_from_str("test");
assert_eq!(s1.to_string_lossy(), "test");
s1.copy_from_str("te");
let mut s2: ApiFixedString<10> = Default::default();
s2.copy_from_str("te");
assert_eq!(s1, s2);
}
}