use crate::error::NylonRingHostError;
use crate::stream_channel::{StreamChannelReceiver, StreamSender};
use crate::{PluginCallGuard, context, context::HostContext};
use dashmap::DashMap;
use nylon_ring::NrStatus;
use rustc_hash::FxBuildHasher;
use std::collections::HashMap;
use std::sync::Arc;
use std::task::Waker;
pub type Result<T> = std::result::Result<T, NylonRingHostError>;
#[derive(Debug)]
pub(crate) enum Pending {
Unary(UnaryPending),
Stream(StreamSender),
}
#[derive(Debug)]
pub(crate) enum UnaryPending {
Waiting {
waker: Option<Waker>,
lease: Option<Vec<u8>>,
},
Ready(NrStatus, ResponsePayload),
}
#[derive(Debug)]
pub(crate) struct ForeignBytes {
ptr: *const u8,
len: usize,
owner_ctx: *mut std::ffi::c_void,
release: Option<unsafe extern "C" fn(*mut std::ffi::c_void, *const u8, u64)>,
}
unsafe impl Send for ForeignBytes {}
unsafe impl Sync for ForeignBytes {}
impl ForeignBytes {
pub(crate) fn from_abi(payload: nylon_ring::NrOwnedBytes) -> Self {
Self {
ptr: payload.ptr,
len: payload.len as usize,
owner_ctx: payload.owner_ctx,
release: payload.release,
}
}
pub(crate) fn as_slice(&self) -> &[u8] {
if self.ptr.is_null() || self.len == 0 {
return &[];
}
unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
}
}
impl Drop for ForeignBytes {
fn drop(&mut self) {
if let Some(release) = self.release {
unsafe { release(self.owner_ctx, self.ptr, self.len as u64) };
}
}
}
#[derive(Debug)]
pub(crate) enum ResponsePayload {
Owned(Vec<u8>),
Foreign(ForeignBytes),
}
impl ResponsePayload {
pub(crate) fn as_slice(&self) -> &[u8] {
match self {
Self::Owned(data) => data,
Self::Foreign(foreign) => foreign.as_slice(),
}
}
pub(crate) fn into_vec(self) -> Vec<u8> {
match self {
Self::Owned(data) => data,
Self::Foreign(foreign) => foreign.as_slice().to_vec(),
}
}
}
pub struct ResponseBytes {
payload: ResponsePayload,
_guard: Option<PluginCallGuard>,
}
impl ResponseBytes {
pub(crate) fn new(payload: ResponsePayload, guard: Option<PluginCallGuard>) -> Self {
Self {
payload,
_guard: guard,
}
}
pub fn into_vec(self) -> Vec<u8> {
let Self { payload, _guard } = self;
payload.into_vec()
}
}
impl std::ops::Deref for ResponseBytes {
type Target = [u8];
fn deref(&self) -> &[u8] {
self.payload.as_slice()
}
}
impl AsRef<[u8]> for ResponseBytes {
fn as_ref(&self) -> &[u8] {
self.payload.as_slice()
}
}
impl std::fmt::Debug for ResponseBytes {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ResponseBytes")
.field("len", &self.payload.as_slice().len())
.field(
"foreign",
&matches!(self.payload, ResponsePayload::Foreign(_)),
)
.finish()
}
}
impl UnaryPending {
pub(crate) const fn waiting() -> Self {
Self::Waiting {
waker: None,
lease: None,
}
}
}
#[derive(Debug)]
pub struct StreamFrame {
pub status: NrStatus,
pub data: Vec<u8>,
}
pub struct StreamReceiver {
inner: StreamChannelReceiver,
host_ctx: Option<Arc<HostContext>>,
sid: u64,
call_guard: Option<PluginCallGuard>,
}
impl StreamReceiver {
pub(crate) fn new(
inner: StreamChannelReceiver,
host_ctx: Option<Arc<HostContext>>,
sid: u64,
call_guard: Option<PluginCallGuard>,
) -> Self {
debug_assert!(
host_ctx.is_some() || call_guard.is_some(),
"stream receiver needs a context source"
);
Self {
inner,
host_ctx,
sid,
call_guard,
}
}
fn host_ctx(&self) -> &HostContext {
match (&self.call_guard, &self.host_ctx) {
(Some(guard), _) => &guard.plugin().host_ctx,
(None, Some(host_ctx)) => host_ctx,
(None, None) => unreachable!("checked at construction"),
}
}
pub async fn recv(&mut self) -> Option<StreamFrame> {
self.inner.recv().await
}
}
impl Drop for StreamReceiver {
fn drop(&mut self) {
context::cleanup_sid(self.host_ctx(), self.sid);
self.inner.recycle();
}
}
pub(crate) type FastPendingMap = DashMap<u64, Pending, FxBuildHasher>;
pub(crate) type FastStateMap = DashMap<u64, HashMap<String, Vec<u8>>, FxBuildHasher>;
pub(crate) struct UnaryResultSlot {
pub(crate) sid: u64,
pub(crate) result: Option<(NrStatus, Vec<u8>)>,
pub(crate) lease: Option<Vec<u8>>,
}