use core::{
ffi::c_void,
fmt::{Debug, Display},
num::NonZeroUsize,
ptr::NonNull,
};
use crate::furi;
use flipperzero_sys::{self as sys, furi::Status};
use ufmt::{derive::uDebug, uDebug, uDisplay, uwrite};
#[cfg_attr(not(feature = "alloc"), doc = "`Sender`")]
#[cfg_attr(feature = "alloc", doc = "[`Sender`]")]
#[cfg_attr(not(feature = "alloc"), doc = "`Receiver`,")]
#[cfg_attr(feature = "alloc", doc = "[`Receiver`],")]
#[derive(Debug)]
pub struct StreamBuffer(NonNull<sys::FuriStreamBuffer>);
impl uDebug for StreamBuffer {
fn fmt<W>(&self, f: &mut ufmt::Formatter<'_, W>) -> Result<(), W::Error>
where
W: ufmt::uWrite + ?Sized,
{
f.debug_tuple("StreamBuffer")?
.field(&self.0.as_ptr())?
.finish()
}
}
unsafe impl Send for StreamBuffer {}
unsafe impl Sync for StreamBuffer {}
impl StreamBuffer {
#[cfg_attr(not(feature = "alloc"), doc = "`into_stream`")]
#[cfg_attr(feature = "alloc", doc = "[`into_stream`](Self::into_stream)")]
pub fn new(size: NonZeroUsize, trigger_level: usize) -> Self {
let size: usize = size.into();
let ptr =
unsafe { NonNull::new_unchecked(sys::furi_stream_buffer_alloc(size, trigger_level)) };
Self(ptr)
}
pub fn set_trigger_level(&self, trigger_level: usize) -> Result<(), SetTriggerLevelError> {
let self_ptr = self.0.as_ptr();
let updated = unsafe { sys::furi_stream_set_trigger_level(self_ptr, trigger_level) };
if updated {
Ok(())
} else {
Err(SetTriggerLevelError)
}
}
#[cfg_attr(not(feature = "alloc"), doc = "`Sender`")]
#[cfg_attr(feature = "alloc", doc = "[`Sender`]")]
pub unsafe fn send(&self, data: &[u8], timeout: furi::time::FuriDuration) -> usize {
let self_ptr = self.0.as_ptr();
let data_ptr = data.as_ptr().cast();
let data_len = data.len();
let timeout = timeout.0;
unsafe { sys::furi_stream_buffer_send(self_ptr, data_ptr, data_len, timeout) }
}
#[cfg_attr(not(feature = "alloc"), doc = "`Receiver`")]
#[cfg_attr(feature = "alloc", doc = "[`Receiver`]")]
pub unsafe fn receive(&self, data: &mut [u8], timeout: furi::time::FuriDuration) -> usize {
let self_ptr = self.0.as_ptr();
let data_ptr: *mut c_void = data.as_mut_ptr().cast();
let data_len = data.len();
let timeout = timeout.0;
unsafe { sys::furi_stream_buffer_receive(self_ptr, data_ptr, data_len, timeout) }
}
pub fn bytes_available(&self) -> usize {
let self_ptr = self.0.as_ptr();
unsafe { sys::furi_stream_buffer_bytes_available(self_ptr) }
}
pub fn spaces_available(&self) -> usize {
let self_ptr = self.0.as_ptr();
unsafe { sys::furi_stream_buffer_spaces_available(self_ptr) }
}
pub fn is_full(&self) -> bool {
let self_ptr = self.0.as_ptr();
unsafe { sys::furi_stream_buffer_is_full(self_ptr) }
}
pub fn is_empty(&self) -> bool {
let self_ptr = self.0.as_ptr();
unsafe { sys::furi_stream_buffer_is_empty(self_ptr) }
}
pub fn reset(&self) -> furi::Result<()> {
let status = Status::from(unsafe { sys::furi_stream_buffer_reset(self.0.as_ptr()) });
let _ = status.into_result()?;
Ok(())
}
}
impl Drop for StreamBuffer {
fn drop(&mut self) {
unsafe {
sys::furi_stream_buffer_free(self.0.as_ptr());
}
}
}
#[derive(Debug, uDebug)]
pub struct SetTriggerLevelError;
impl Display for SetTriggerLevelError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "specified trigger level exceeds the buffer's length")
}
}
impl uDisplay for SetTriggerLevelError {
fn fmt<W>(&self, f: &mut ufmt::Formatter<'_, W>) -> Result<(), W::Error>
where
W: ufmt::uWrite + ?Sized,
{
uwrite!(f, "specified trigger level exceeds the buffer's length")
}
}
impl core::error::Error for SetTriggerLevelError {}
#[cfg(feature = "alloc")]
pub use stream::*;
#[cfg(feature = "alloc")]
mod stream {
use crate::furi;
use super::*;
use alloc::sync::Arc;
use core::{cell::Cell, marker::PhantomData};
type PhantomUnsync = PhantomData<Cell<()>>;
impl StreamBuffer {
pub fn into_stream(self) -> (Sender, Receiver) {
let stream_buffer = Arc::new(self);
let sender = Sender {
buffer_ref: stream_buffer.clone(),
_unsync: PhantomUnsync::default(),
};
let receiver = Receiver {
buffer_ref: stream_buffer,
_unsync: PhantomUnsync::default(),
};
(sender, receiver)
}
}
pub struct Sender {
buffer_ref: Arc<StreamBuffer>,
_unsync: PhantomUnsync,
}
impl Debug for Sender {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Sender")
.field("buffer_ref", &self.buffer_ref)
.field("receiver_alive", &self.is_receiver_alive())
.finish()
}
}
impl uDebug for Sender {
fn fmt<W>(&self, f: &mut ufmt::Formatter<'_, W>) -> Result<(), W::Error>
where
W: ufmt::uWrite + ?Sized,
{
f.debug_struct("Sender")?
.field("buffer_ref", &self.buffer_ref.0.as_ptr())?
.field("receiver_alive", &self.is_receiver_alive())?
.finish()
}
}
impl Sender {
pub fn send(&self, data: &[u8]) -> usize {
unsafe { self.buffer_ref.send(data, furi::time::FuriDuration::ZERO) }
}
pub fn send_blocking(&self, data: &[u8]) -> usize {
unsafe {
self.buffer_ref
.send(data, furi::time::FuriDuration::WAIT_FOREVER)
}
}
pub fn send_with_timeout(&self, data: &[u8], timeout: furi::time::FuriDuration) -> usize {
unsafe { self.buffer_ref.send(data, timeout) }
}
}
impl Sender {
pub fn is_receiver_alive(&self) -> bool {
Arc::strong_count(&self.buffer_ref) == 2
}
pub fn as_stream_buffer(&self) -> &StreamBuffer {
&self.buffer_ref
}
pub fn into_stream_buffer(self) -> Option<StreamBuffer> {
Arc::into_inner(self.buffer_ref)
}
}
pub struct Receiver {
buffer_ref: Arc<StreamBuffer>,
_unsync: PhantomUnsync,
}
impl Debug for Receiver {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Receiver")
.field("buffer_ref", &self.buffer_ref)
.field("sender_alive", &self.is_sender_alive())
.finish()
}
}
impl uDebug for Receiver {
fn fmt<W>(&self, f: &mut ufmt::Formatter<'_, W>) -> Result<(), W::Error>
where
W: ufmt::uWrite + ?Sized,
{
f.debug_struct("Receiver")?
.field("buffer_ref", &self.buffer_ref.0.as_ptr())?
.field("sender_alive", &self.is_sender_alive())?
.finish()
}
}
impl Receiver {
pub fn recv(&self, data: &mut [u8]) -> usize {
unsafe {
self.buffer_ref
.receive(data, furi::time::FuriDuration::ZERO)
}
}
pub fn recv_blocking(&self, data: &mut [u8]) -> usize {
unsafe {
self.buffer_ref
.receive(data, furi::time::FuriDuration::WAIT_FOREVER)
}
}
pub fn recv_with_timeout(
&self,
data: &mut [u8],
timeout: furi::time::FuriDuration,
) -> usize {
unsafe { self.buffer_ref.receive(data, timeout) }
}
}
impl Receiver {
pub fn is_sender_alive(&self) -> bool {
Arc::strong_count(&self.buffer_ref) == 2
}
pub fn as_stream_buffer(&self) -> &StreamBuffer {
&self.buffer_ref
}
pub fn into_stream_buffer(self) -> Option<StreamBuffer> {
Arc::into_inner(self.buffer_ref)
}
}
}