use core::cell::Cell;
use core::ffi::c_void;
use core::marker::PhantomData;
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
use crate::kernel::stream_buffer::xStreamBufferCreate;
use crate::kernel::stream_buffer::{
sbTYPE_STREAM_BUFFER, vStreamBufferDelete, xStreamBufferBytesAvailable,
xStreamBufferGenericCreateStatic, xStreamBufferIsEmpty, xStreamBufferIsFull,
xStreamBufferReceive, xStreamBufferReceiveFromISR, xStreamBufferReset,
xStreamBufferResetFromISR, xStreamBufferSend, xStreamBufferSendFromISR,
xStreamBufferSpacesAvailable, StaticStreamBuffer_t, StreamBufferHandle_t,
};
use crate::sync::task::TaskContext;
use crate::sync::{assert_can_block, assert_task_context};
use crate::types::*;
pub struct StreamBuffer {
handle: StreamBufferHandle_t,
capacity: usize,
_not_sync: PhantomData<Cell<()>>,
}
unsafe impl Send for StreamBuffer {}
pub struct StreamBufferSender<'a> {
handle: StreamBufferHandle_t,
capacity: usize,
_owner: PhantomData<&'a mut StreamBuffer>,
_not_sync: PhantomData<Cell<()>>,
}
unsafe impl Send for StreamBufferSender<'_> {}
pub struct StreamBufferReceiver<'a> {
handle: StreamBufferHandle_t,
capacity: usize,
_owner: PhantomData<&'a mut StreamBuffer>,
_not_sync: PhantomData<Cell<()>>,
}
unsafe impl Send for StreamBufferReceiver<'_> {}
impl StreamBuffer {
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
pub fn new(_context: &TaskContext, size: usize, trigger_level: usize) -> Option<Self> {
assert_task_context("StreamBuffer::new");
if size == 0 || trigger_level == 0 || trigger_level > size || size.checked_add(1).is_none()
{
return None;
}
let handle = unsafe { xStreamBufferCreate(size, trigger_level) };
Self::from_created_handle(handle, size)
}
pub fn new_static(
_context: &TaskContext,
storage: &'static mut [u8],
trigger_level: usize,
stream_buffer: &'static mut StaticStreamBuffer_t,
) -> Option<Self> {
assert_task_context("StreamBuffer::new_static");
let capacity = storage.len().checked_sub(1)?;
if capacity == 0 || trigger_level == 0 || trigger_level > capacity {
return None;
}
let handle = unsafe {
xStreamBufferGenericCreateStatic(
storage.len(),
trigger_level,
sbTYPE_STREAM_BUFFER,
storage.as_mut_ptr(),
stream_buffer as *mut StaticStreamBuffer_t,
None,
None,
)
};
Self::from_created_handle(handle, capacity)
}
fn from_created_handle(handle: StreamBufferHandle_t, capacity: usize) -> Option<Self> {
if handle.is_null() {
None
} else {
Some(Self {
handle,
capacity,
_not_sync: PhantomData,
})
}
}
pub fn split(&mut self) -> (StreamBufferSender<'_>, StreamBufferReceiver<'_>) {
(
StreamBufferSender {
handle: self.handle,
capacity: self.capacity,
_owner: PhantomData,
_not_sync: PhantomData,
},
StreamBufferReceiver {
handle: self.handle,
capacity: self.capacity,
_owner: PhantomData,
_not_sync: PhantomData,
},
)
}
pub const fn capacity(&self) -> usize {
self.capacity
}
pub fn send(&mut self, _context: &TaskContext, data: &[u8]) -> usize {
send_task(self.handle, data, portMAX_DELAY)
}
pub fn try_send(&mut self, _context: &TaskContext, data: &[u8]) -> usize {
send_task(self.handle, data, 0)
}
pub fn send_timeout(
&mut self,
_context: &TaskContext,
data: &[u8],
ticks: TickType_t,
) -> usize {
send_task(self.handle, data, ticks)
}
pub unsafe fn send_from_isr(&mut self, data: &[u8]) -> (usize, bool) {
unsafe { send_isr(self.handle, data) }
}
pub fn receive(&mut self, _context: &TaskContext, output: &mut [u8]) -> usize {
receive_task(self.handle, output, portMAX_DELAY)
}
pub fn try_receive(&mut self, _context: &TaskContext, output: &mut [u8]) -> usize {
receive_task(self.handle, output, 0)
}
pub fn receive_timeout(
&mut self,
_context: &TaskContext,
output: &mut [u8],
ticks: TickType_t,
) -> usize {
receive_task(self.handle, output, ticks)
}
pub unsafe fn receive_from_isr(&mut self, output: &mut [u8]) -> (usize, bool) {
unsafe { receive_isr(self.handle, output) }
}
pub fn available(&self, _context: &TaskContext) -> usize {
assert_task_context("StreamBuffer::available");
available(self.handle)
}
pub fn spaces(&self, _context: &TaskContext) -> usize {
assert_task_context("StreamBuffer::spaces");
spaces(self.handle)
}
pub fn is_empty(&self, _context: &TaskContext) -> bool {
assert_task_context("StreamBuffer::is_empty");
is_empty(self.handle)
}
pub fn is_full(&self, _context: &TaskContext) -> bool {
assert_task_context("StreamBuffer::is_full");
is_full(self.handle)
}
pub fn reset(&mut self, _context: &TaskContext) -> bool {
assert_task_context("StreamBuffer::reset");
unsafe { xStreamBufferReset(self.handle) == pdPASS }
}
pub unsafe fn reset_from_isr(&mut self) -> bool {
unsafe { xStreamBufferResetFromISR(self.handle) == pdPASS }
}
pub unsafe fn delete(self, _context: &TaskContext) {
unsafe { vStreamBufferDelete(self.handle) };
}
pub unsafe fn raw_handle(&self) -> StreamBufferHandle_t {
self.handle
}
}
impl StreamBufferSender<'_> {
pub const fn capacity(&self) -> usize {
self.capacity
}
pub fn send(&mut self, _context: &TaskContext, data: &[u8]) -> usize {
send_task(self.handle, data, portMAX_DELAY)
}
pub fn try_send(&mut self, _context: &TaskContext, data: &[u8]) -> usize {
send_task(self.handle, data, 0)
}
pub fn send_timeout(
&mut self,
_context: &TaskContext,
data: &[u8],
ticks: TickType_t,
) -> usize {
send_task(self.handle, data, ticks)
}
pub unsafe fn send_from_isr(&mut self, data: &[u8]) -> (usize, bool) {
unsafe { send_isr(self.handle, data) }
}
pub fn spaces(&self, _context: &TaskContext) -> usize {
assert_task_context("StreamBufferSender::spaces");
spaces(self.handle)
}
pub fn is_full(&self, _context: &TaskContext) -> bool {
assert_task_context("StreamBufferSender::is_full");
is_full(self.handle)
}
}
impl StreamBufferReceiver<'_> {
pub const fn capacity(&self) -> usize {
self.capacity
}
pub fn receive(&mut self, _context: &TaskContext, output: &mut [u8]) -> usize {
receive_task(self.handle, output, portMAX_DELAY)
}
pub fn try_receive(&mut self, _context: &TaskContext, output: &mut [u8]) -> usize {
receive_task(self.handle, output, 0)
}
pub fn receive_timeout(
&mut self,
_context: &TaskContext,
output: &mut [u8],
ticks: TickType_t,
) -> usize {
receive_task(self.handle, output, ticks)
}
pub unsafe fn receive_from_isr(&mut self, output: &mut [u8]) -> (usize, bool) {
unsafe { receive_isr(self.handle, output) }
}
pub fn available(&self, _context: &TaskContext) -> usize {
assert_task_context("StreamBufferReceiver::available");
available(self.handle)
}
pub fn is_empty(&self, _context: &TaskContext) -> bool {
assert_task_context("StreamBufferReceiver::is_empty");
is_empty(self.handle)
}
}
fn send_task(handle: StreamBufferHandle_t, data: &[u8], ticks: TickType_t) -> usize {
assert_can_block("StreamBuffer::send", ticks);
if data.is_empty() {
return 0;
}
unsafe { xStreamBufferSend(handle, data.as_ptr().cast::<c_void>(), data.len(), ticks) }
}
fn receive_task(handle: StreamBufferHandle_t, output: &mut [u8], ticks: TickType_t) -> usize {
assert_can_block("StreamBuffer::receive", ticks);
if output.is_empty() {
return 0;
}
unsafe {
xStreamBufferReceive(
handle,
output.as_mut_ptr().cast::<c_void>(),
output.len(),
ticks,
)
}
}
unsafe fn send_isr(handle: StreamBufferHandle_t, data: &[u8]) -> (usize, bool) {
if data.is_empty() {
return (0, false);
}
let mut higher_priority_task_woken = pdFALSE;
let sent = unsafe {
xStreamBufferSendFromISR(
handle,
data.as_ptr().cast::<c_void>(),
data.len(),
&mut higher_priority_task_woken,
)
};
(sent, higher_priority_task_woken != pdFALSE)
}
unsafe fn receive_isr(handle: StreamBufferHandle_t, output: &mut [u8]) -> (usize, bool) {
if output.is_empty() {
return (0, false);
}
let mut higher_priority_task_woken = pdFALSE;
let received = unsafe {
xStreamBufferReceiveFromISR(
handle,
output.as_mut_ptr().cast::<c_void>(),
output.len(),
&mut higher_priority_task_woken,
)
};
(received, higher_priority_task_woken != pdFALSE)
}
fn available(handle: StreamBufferHandle_t) -> usize {
unsafe { xStreamBufferBytesAvailable(handle) }
}
fn spaces(handle: StreamBufferHandle_t) -> usize {
unsafe { xStreamBufferSpacesAvailable(handle) }
}
fn is_empty(handle: StreamBufferHandle_t) -> bool {
unsafe { xStreamBufferIsEmpty(handle) != pdFALSE }
}
fn is_full(handle: StreamBufferHandle_t) -> bool {
unsafe { xStreamBufferIsFull(handle) != pdFALSE }
}
#[cfg(test)]
mod tests {
use super::*;
use std::boxed::Box;
use std::vec;
fn static_stream(
context: &TaskContext,
storage_len: usize,
trigger: usize,
) -> Option<StreamBuffer> {
let storage = Box::leak(vec![0_u8; storage_len].into_boxed_slice());
let control = Box::leak(Box::new(StaticStreamBuffer_t::new()));
StreamBuffer::new_static(context, storage, trigger, control)
}
#[test]
fn rejects_unusable_static_sizes_and_trigger_levels() {
crate::port::test_port_reset();
let context = unsafe { TaskContext::assume() };
assert!(static_stream(&context, 0, 1).is_none());
assert!(static_stream(&context, 1, 1).is_none());
assert!(static_stream(&context, 8, 0).is_none());
assert!(static_stream(&context, 8, 8).is_none());
}
#[test]
fn static_capacity_accounts_for_freertos_sentinel() {
crate::port::test_port_reset();
let context = unsafe { TaskContext::assume() };
let mut stream = static_stream(&context, 8, 7).expect("valid stream");
assert_eq!(stream.capacity(), 7);
assert_eq!(stream.try_send(&context, &[1, 2, 3, 4, 5, 6, 7, 8]), 7);
assert!(stream.is_full(&context));
let mut output = [0_u8; 8];
assert_eq!(stream.try_receive(&context, &mut output), 7);
assert_eq!(&output[..7], &[1, 2, 3, 4, 5, 6, 7]);
}
#[test]
fn split_endpoints_transfer_bytes() {
crate::port::test_port_reset();
let context = unsafe { TaskContext::assume() };
let mut stream = static_stream(&context, 32, 1).expect("valid stream");
let (mut sender, mut receiver) = stream.split();
assert_eq!(sender.try_send(&context, b"byte stream"), 11);
let mut output = [0_u8; 11];
assert_eq!(receiver.try_receive(&context, &mut output), 11);
assert_eq!(output, *b"byte stream");
}
}