#![allow(
clippy::missing_errors_doc,
clippy::semicolon_if_nothing_returned,
clippy::use_self,
)]
use core::ffi::{c_int, c_void};
use std::ffi::CString;
use std::marker::PhantomData;
use std::slice;
use std::sync::Arc;
use std::time::Duration;
use crate::error::NetworkError;
use crate::ffi;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FramerStart {
Ready,
WillMarkReady,
}
pub trait Framer: Send {
fn on_start(&mut self, context: &mut FramerContext) -> FramerStart;
fn on_input(&mut self, context: &mut FramerContext) -> usize;
fn on_output(
&mut self,
context: &mut FramerContext,
message: Option<FramerMessageView<'_>>,
message_length: usize,
is_complete: bool,
);
fn on_stop(&mut self, context: &mut FramerContext) -> bool;
fn on_wakeup(&mut self, _context: &mut FramerContext) {}
fn on_cleanup(&mut self, _context: &mut FramerContext) {}
}
type FactoryFn = dyn Fn() -> Box<dyn Framer> + Send + Sync + 'static;
pub(crate) struct FramerCallbacksOwner {
factory: Box<FactoryFn>,
}
pub struct FramerDefinition {
handle: *mut c_void,
keepalive: Arc<FramerCallbacksOwner>,
}
unsafe impl Send for FramerDefinition {}
unsafe impl Sync for FramerDefinition {}
pub struct FramerOptions {
handle: *mut c_void,
keepalive: Arc<FramerCallbacksOwner>,
}
unsafe impl Send for FramerOptions {}
unsafe impl Sync for FramerOptions {}
pub struct FramerMessage {
handle: *mut c_void,
}
unsafe impl Send for FramerMessage {}
unsafe impl Sync for FramerMessage {}
pub struct FramerMessageView<'a> {
handle: *mut c_void,
_marker: PhantomData<&'a ()>,
}
struct AsyncCallbackHolder(Box<dyn FnMut(&mut FramerContext) + Send + 'static>);
impl FramerDefinition {
pub fn new<F, T>(identifier: &str, factory: F) -> Result<Self, NetworkError>
where
F: Fn() -> T + Send + Sync + 'static,
T: Framer + 'static,
{
let identifier = CString::new(identifier)
.map_err(|e| NetworkError::InvalidArgument(format!("identifier NUL byte: {e}")))?;
let keepalive = Arc::new(FramerCallbacksOwner {
factory: Box::new(move || Box::new(factory()) as Box<dyn Framer>),
});
let handle = unsafe {
ffi::nw_shim_framer_definition_create(
identifier.as_ptr(),
0,
create_instance_trampoline,
drop_instance_trampoline,
start_trampoline,
input_trampoline,
output_trampoline,
Some(wakeup_trampoline),
Some(stop_trampoline),
Some(cleanup_trampoline),
Arc::as_ptr(&keepalive).cast_mut().cast::<c_void>(),
)
};
if handle.is_null() {
return Err(NetworkError::InvalidArgument(
"failed to create framer definition".into(),
));
}
Ok(Self { handle, keepalive })
}
pub fn options(&self) -> Result<FramerOptions, NetworkError> {
let handle = unsafe { ffi::nw_shim_framer_create_options(self.handle) };
if handle.is_null() {
return Err(NetworkError::InvalidArgument(
"failed to create framer options".into(),
));
}
Ok(FramerOptions {
handle,
keepalive: self.keepalive.clone(),
})
}
}
impl Drop for FramerDefinition {
fn drop(&mut self) {
if !self.handle.is_null() {
unsafe { ffi::nw_shim_release_object(self.handle) };
self.handle = core::ptr::null_mut();
}
}
}
impl FramerOptions {
pub fn create_message(&self) -> Result<FramerMessage, NetworkError> {
let handle = unsafe { ffi::nw_shim_framer_message_create_from_options(self.handle) };
if handle.is_null() {
return Err(NetworkError::InvalidArgument(
"failed to create framer message".into(),
));
}
Ok(FramerMessage { handle })
}
#[must_use]
pub(crate) const fn as_ptr(&self) -> *mut c_void {
self.handle
}
#[must_use]
pub(crate) fn keepalive(&self) -> Arc<FramerCallbacksOwner> {
self.keepalive.clone()
}
}
impl Drop for FramerOptions {
fn drop(&mut self) {
if !self.handle.is_null() {
unsafe { ffi::nw_shim_release_object(self.handle) };
self.handle = core::ptr::null_mut();
}
}
}
impl FramerMessage {
pub fn set_u64(&mut self, key: &str, value: u64) -> Result<&mut Self, NetworkError> {
let key = CString::new(key)
.map_err(|e| NetworkError::InvalidArgument(format!("key NUL byte: {e}")))?;
let status = unsafe { ffi::nw_shim_framer_message_set_u64(self.handle, key.as_ptr(), value) };
if status != ffi::NW_OK {
return Err(crate::error::from_status(status));
}
Ok(self)
}
#[must_use]
pub fn get_u64(&self, key: &str) -> Option<u64> {
FramerMessageView {
handle: self.handle,
_marker: PhantomData,
}
.get_u64(key)
}
#[must_use]
pub(crate) const fn as_ptr(&self) -> *mut c_void {
self.handle
}
#[must_use]
pub(crate) const unsafe fn from_raw(handle: *mut c_void) -> Self {
Self { handle }
}
}
impl Clone for FramerMessage {
fn clone(&self) -> Self {
let handle = unsafe { ffi::nw_shim_retain_object(self.handle) };
Self { handle }
}
}
impl Drop for FramerMessage {
fn drop(&mut self) {
if !self.handle.is_null() {
unsafe { ffi::nw_shim_release_object(self.handle) };
self.handle = core::ptr::null_mut();
}
}
}
impl FramerMessageView<'_> {
#[must_use]
pub fn get_u64(&self, key: &str) -> Option<u64> {
let key = CString::new(key).ok()?;
let mut value = 0_u64;
let found = unsafe { ffi::nw_shim_framer_message_get_u64(self.handle, key.as_ptr(), &mut value) };
if found > 0 {
Some(value)
} else {
None
}
}
}
pub struct FramerContext {
handle: *mut c_void,
}
impl FramerContext {
#[must_use]
const fn new(handle: *mut c_void) -> Self {
Self { handle }
}
pub fn create_message(&mut self) -> Result<FramerMessage, NetworkError> {
let handle = unsafe { ffi::nw_shim_framer_message_create_for_instance(self.handle) };
if handle.is_null() {
return Err(NetworkError::InvalidArgument(
"failed to create inbound framer message".into(),
));
}
Ok(FramerMessage { handle })
}
pub fn parse_input<F>(
&mut self,
minimum_incomplete_length: usize,
maximum_length: usize,
temp_buffer: Option<&mut [u8]>,
mut parse: F,
) -> bool
where
F: FnMut(&[u8], bool) -> usize,
{
let (temp_buffer, max_length) = temp_buffer.map_or(
(core::ptr::null_mut(), maximum_length),
|buffer| (buffer.as_mut_ptr(), buffer.len()),
);
unsafe {
ffi::nw_shim_framer_parse_input(
self.handle,
minimum_incomplete_length,
max_length,
temp_buffer,
parse_trampoline::<F>,
std::ptr::addr_of_mut!(parse).cast(),
) != 0
}
}
pub fn parse_output<F>(
&mut self,
minimum_incomplete_length: usize,
maximum_length: usize,
temp_buffer: Option<&mut [u8]>,
mut parse: F,
) -> bool
where
F: FnMut(&[u8], bool) -> usize,
{
let (temp_buffer, max_length) = temp_buffer.map_or(
(core::ptr::null_mut(), maximum_length),
|buffer| (buffer.as_mut_ptr(), buffer.len()),
);
unsafe {
ffi::nw_shim_framer_parse_output(
self.handle,
minimum_incomplete_length,
max_length,
temp_buffer,
parse_trampoline::<F>,
std::ptr::addr_of_mut!(parse).cast(),
) != 0
}
}
pub fn pass_input_data(
&mut self,
input_length: usize,
message: Option<&FramerMessage>,
is_complete: bool,
) -> bool {
unsafe {
ffi::nw_shim_framer_pass_input_data(
self.handle,
input_length,
message.map_or(core::ptr::null_mut(), FramerMessage::as_ptr),
i32::from(is_complete),
) != 0
}
}
pub fn deliver_input_data(
&mut self,
input_buffer: &[u8],
message: Option<&FramerMessage>,
is_complete: bool,
) {
unsafe {
ffi::nw_shim_framer_deliver_input_data(
self.handle,
input_buffer.as_ptr(),
input_buffer.len(),
message.map_or(core::ptr::null_mut(), FramerMessage::as_ptr),
i32::from(is_complete),
)
};
}
pub fn pass_through_input(&mut self) {
unsafe { ffi::nw_shim_framer_pass_through_input(self.handle) };
}
pub fn pass_output_data(&mut self, output_length: usize) -> bool {
unsafe { ffi::nw_shim_framer_pass_output_data(self.handle, output_length) != 0 }
}
pub fn write_output_data(&mut self, output_buffer: &[u8]) {
unsafe {
ffi::nw_shim_framer_write_output_data(
self.handle,
output_buffer.as_ptr(),
output_buffer.len(),
)
};
}
pub fn pass_through_output(&mut self) {
unsafe { ffi::nw_shim_framer_pass_through_output(self.handle) };
}
pub fn mark_ready(&mut self) {
unsafe { ffi::nw_shim_framer_mark_ready(self.handle) };
}
#[must_use]
pub fn prepend_application_protocol(&mut self, options: &FramerOptions) -> bool {
unsafe {
ffi::nw_shim_framer_prepend_application_protocol(self.handle, options.handle) != 0
}
}
pub fn mark_failed_with_error(&mut self, error_code: i32) {
unsafe { ffi::nw_shim_framer_mark_failed_with_error(self.handle, error_code as c_int) };
}
pub fn schedule_wakeup(&mut self, after: Duration) {
let milliseconds = u64::try_from(after.as_millis()).unwrap_or(u64::MAX);
unsafe { ffi::nw_shim_framer_schedule_wakeup(self.handle, milliseconds) };
}
pub fn clear_wakeup(&mut self) {
unsafe { ffi::nw_shim_framer_schedule_wakeup(self.handle, u64::MAX) };
}
pub fn async_invoke<F>(&mut self, callback: F)
where
F: FnMut(&mut FramerContext) + Send + 'static,
{
let holder = Box::new(AsyncCallbackHolder(Box::new(callback)));
unsafe {
ffi::nw_shim_framer_async(
self.handle,
async_callback_trampoline,
Box::into_raw(holder).cast(),
)
};
}
}
unsafe extern "C" fn create_instance_trampoline(user_info: *mut c_void) -> *mut c_void {
let owner = unsafe { &*user_info.cast::<FramerCallbacksOwner>() };
let instance = (owner.factory)();
Box::into_raw(Box::new(instance)).cast()
}
unsafe extern "C" fn drop_instance_trampoline(instance: *mut c_void) {
drop(unsafe { Box::from_raw(instance.cast::<Box<dyn Framer>>()) });
}
unsafe extern "C" fn start_trampoline(instance: *mut c_void, framer: *mut c_void) -> c_int {
let framer_instance = unsafe { &mut *instance.cast::<Box<dyn Framer>>() };
let mut context = FramerContext::new(framer);
match framer_instance.as_mut().on_start(&mut context) {
FramerStart::Ready => ffi::NW_FRAMER_START_READY,
FramerStart::WillMarkReady => ffi::NW_FRAMER_START_WILL_MARK_READY,
}
}
unsafe extern "C" fn input_trampoline(instance: *mut c_void, framer: *mut c_void) -> usize {
let framer_instance = unsafe { &mut *instance.cast::<Box<dyn Framer>>() };
let mut context = FramerContext::new(framer);
framer_instance.as_mut().on_input(&mut context)
}
unsafe extern "C" fn output_trampoline(
instance: *mut c_void,
framer: *mut c_void,
message: *mut c_void,
message_length: usize,
is_complete: c_int,
) {
let framer_instance = unsafe { &mut *instance.cast::<Box<dyn Framer>>() };
let mut context = FramerContext::new(framer);
let message = (!message.is_null()).then_some(FramerMessageView {
handle: message,
_marker: PhantomData,
});
framer_instance
.as_mut()
.on_output(&mut context, message, message_length, is_complete != 0);
}
unsafe extern "C" fn wakeup_trampoline(instance: *mut c_void, framer: *mut c_void) {
let framer_instance = unsafe { &mut *instance.cast::<Box<dyn Framer>>() };
let mut context = FramerContext::new(framer);
framer_instance.as_mut().on_wakeup(&mut context);
}
unsafe extern "C" fn stop_trampoline(instance: *mut c_void, framer: *mut c_void) -> c_int {
let framer_instance = unsafe { &mut *instance.cast::<Box<dyn Framer>>() };
let mut context = FramerContext::new(framer);
i32::from(framer_instance.as_mut().on_stop(&mut context))
}
unsafe extern "C" fn cleanup_trampoline(instance: *mut c_void, framer: *mut c_void) {
let framer_instance = unsafe { &mut *instance.cast::<Box<dyn Framer>>() };
let mut context = FramerContext::new(framer);
framer_instance.as_mut().on_cleanup(&mut context);
}
unsafe extern "C" fn parse_trampoline<F>(
buffer: *const u8,
buffer_length: usize,
is_complete: c_int,
user_info: *mut c_void,
) -> usize
where
F: FnMut(&[u8], bool) -> usize,
{
let callback = unsafe { &mut *user_info.cast::<F>() };
let bytes = if buffer.is_null() || buffer_length == 0 {
&[]
} else {
unsafe { slice::from_raw_parts(buffer, buffer_length) }
};
callback(bytes, is_complete != 0)
}
unsafe extern "C" fn async_callback_trampoline(framer: *mut c_void, user_info: *mut c_void) {
let mut holder = unsafe { Box::from_raw(user_info.cast::<AsyncCallbackHolder>()) };
let mut context = FramerContext::new(framer);
holder.0.as_mut()(&mut context);
}