Skip to content

Commit

Permalink
iox-#64 Raw pointer API for publisher and subscriber
Browse files Browse the repository at this point in the history
  • Loading branch information
elBoberido committed Jul 30, 2023
1 parent 9f898c8 commit 0f14f04
Show file tree
Hide file tree
Showing 11 changed files with 506 additions and 171 deletions.
18 changes: 11 additions & 7 deletions iceoryx-sys/src/chunk_header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,21 @@ cpp! {{
cpp_class!(pub unsafe struct ChunkHeader as "ChunkHeader");

impl ChunkHeader {
pub fn from_user_payload<T>(payload: &T) -> Option<&Self> {
let payload = payload as *const T as *const c_void;
/// Get a reference to a ChunkHeader
///
/// # Safety
///
/// The caller must ensure that `payload` is non-null
pub(super) unsafe fn from_user_payload_unchecked<'a>(payload: *const c_void) -> &'a Self {
unsafe {
let chunk_header = cpp!([payload as "void*"] -> *const c_void as "const void*" {
return iox::mepoo::ChunkHeader::fromUserPayload(payload);
});
if chunk_header.is_null() {
None
} else {
Some(&*(chunk_header as *const Self))
}
debug_assert!(
!chunk_header.is_null(),
"The ChunkHeader ptr should always be non-null when the payload ptr was non-null!"
);
&*(chunk_header as *const Self)
}
}

Expand Down
4 changes: 4 additions & 0 deletions iceoryx-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ pub use queue_policy::QueueFullPolicy;
mod runtime;
pub use runtime::Runtime;

mod sample;
pub use sample::RawSample;
pub use sample::RawSampleMut;

mod subscriber;
pub use subscriber::ConditionVariable;
pub use subscriber::SubscribeState;
Expand Down
47 changes: 24 additions & 23 deletions iceoryx-sys/src/publisher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
// SPDX-FileCopyrightText: © Contributors to the iceoryx-rs project
// SPDX-FileContributor: Mathias Kraus

use crate::PublisherOptions;
use crate::{PublisherOptions, RawSampleMut};

use std::ffi::{c_void, CString};
use std::mem::MaybeUninit;

cpp! {{
#include "iceoryx_posh/internal/popo/ports/publisher_port_user.hpp"
Expand Down Expand Up @@ -104,38 +105,33 @@ impl Publisher {
}
}

pub fn try_allocate<T>(&self) -> Option<*mut T> {
pub fn try_allocate<T>(&self) -> Option<RawSampleMut<MaybeUninit<T>>> {
let size = std::mem::size_of::<T>() as u32;
let align = std::mem::align_of::<T>() as u32;
unsafe {
let payload = self.try_allocate_chunk(size, align);

if !payload.is_null() {
Some(payload as *mut T)
} else {
None
}
self.try_allocate_chunk(size, align)
.map(|payload| payload.cast::<MaybeUninit<T>>())
}
}

pub fn try_allocate_slice<T>(&self, len: u32, align: u32) -> Option<*mut T> {
pub fn try_allocate_slice<T>(
&self,
len: u32,
align: u32,
) -> Option<RawSampleMut<[MaybeUninit<T>]>> {
unsafe {
if align < std::mem::align_of::<T>() as u32 {
return None;
}

let size = len * std::mem::size_of::<T>() as u32;
let payload = self.try_allocate_chunk(size, align);

if !payload.is_null() {
Some(payload as *mut T)
} else {
None
}
self.try_allocate_chunk(size, align).map(|payload| {
RawSampleMut::slice_from_raw_parts(payload.cast::<MaybeUninit<T>>(), len as usize)
})
}
}

unsafe fn try_allocate_chunk(&self, size: u32, align: u32) -> *mut c_void {
unsafe fn try_allocate_chunk(&self, size: u32, align: u32) -> Option<RawSampleMut<c_void>> {
let payload = cpp!([self as "PublisherPortUser*", size as "uint32_t", align as "uint32_t"] -> *mut std::ffi::c_void as "void*" {
auto allocResult = self->tryAllocateChunk(size,
align,
Expand All @@ -147,21 +143,26 @@ impl Publisher {
return allocResult.value()->userPayload();
}
});
payload

if !payload.is_null() {
Some(RawSampleMut::new_unchecked(payload))
} else {
None
}
}

pub fn release<T: ?Sized>(&self, payload: *mut T) {
pub fn release<T: ?Sized>(&self, sample: RawSampleMut<T>) {
unsafe {
let payload = payload as *const c_void;
let payload = sample.cast::<c_void>().as_payload_ptr();
cpp!([self as "PublisherPortUser*", payload as "void*"] {
auto header = iox::mepoo::ChunkHeader::fromUserPayload(payload);
self->releaseChunk(header);
});
}
}

pub fn send<T: ?Sized>(&self, payload: *mut T) {
let payload = payload as *const c_void;
pub fn send<T: ?Sized>(&self, sample: RawSampleMut<T>) {
let payload = sample.cast::<c_void>().as_payload_ptr();
unsafe {
cpp!([self as "PublisherPortUser*", payload as "void*"] {
auto header = iox::mepoo::ChunkHeader::fromUserPayload(payload);
Expand Down
218 changes: 218 additions & 0 deletions iceoryx-sys/src/sample.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: © Contributors to the iceoryx-rs project
// SPDX-FileContributor: Mathias Kraus

use crate::ChunkHeader;

use std::ffi::c_void;
use std::fmt;
use std::slice;

/// A `*const T` non-zero sample pointer to the user payload.
#[repr(transparent)]
pub struct RawSample<T: ?Sized> {
payload: *const T,
}

impl<T: ?Sized> RawSample<T> {
/// Creates a new `RawSample`.
///
/// # Safety
///
/// `payload` must be non-null.
#[inline]
pub unsafe fn new_unchecked(payload: *const T) -> Self {
debug_assert!(
!payload.is_null(),
"RawSample::new_unchecked requires that the payload pointer is non-null"
);
Self { payload }
}

/// Creates a new `RawSample`.
#[allow(clippy::not_unsafe_ptr_arg_deref)] // false positive
#[inline]
pub fn new(payload: *const T) -> Option<Self> {
if !payload.is_null() {
// SAFETY: `payload` pointer is checked to be non-null
Some(unsafe { Self::new_unchecked(payload) })
} else {
None
}
}

/// Casts to a `RawSample` of another type.
#[must_use]
#[inline]
pub fn cast<U>(self) -> RawSample<U> {
// SAFETY: `self.as_payload_ptr` returns a non-null ptr
unsafe { RawSample::new_unchecked(self.as_payload_ptr().cast::<U>()) }
}

/// Acquires the underlying payload pointer as `*const` pointer.
#[must_use]
#[inline(always)]
pub fn as_payload_ptr(self) -> *const T {
self.payload
}

/// Returns a reference to the `ChunkHeader`.
#[must_use]
#[inline]
pub fn chunk_header(&self) -> &ChunkHeader {
// SAFTEY: `self.as_payload_ptr` returns a non-null ptr
unsafe { ChunkHeader::from_user_payload_unchecked(self.as_payload_ptr() as *const c_void) }
}
}

impl<T> RawSample<[T]> {
/// Creates a non-null raw slice from a thin payload pointer and a length.
///
/// The `len` argument is the number of **elements**, not the number of bytes.
///
/// This function is safe, but dereferencing the return value is unsafe.
/// See the documentation of [`slice::from_raw_parts`] for slice safety requirements.
#[must_use]
#[inline]
pub fn slice_from_raw_parts(sample: RawSample<T>, len: usize) -> RawSample<[T]> {
// SAFETY: `self.as_payload_ptr` returns a non-null ptr
unsafe { Self::new_unchecked(slice::from_raw_parts(sample.as_payload_ptr(), len)) }
}

/// Returns the length of a non-null raw slice.
#[must_use]
#[inline]
pub fn len(self) -> usize {
// SAFETY: `self.as_payload_ptr` returns a non-null ptr
unsafe { (*self.as_payload_ptr()).len() }
}
}

impl<T: ?Sized> Clone for RawSample<T> {
#[inline(always)]
fn clone(&self) -> Self {
*self
}
}

impl<T: ?Sized> Copy for RawSample<T> {}

impl<T: ?Sized> fmt::Debug for RawSample<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.as_payload_ptr(), f)
}
}

impl<T: ?Sized> fmt::Pointer for RawSample<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.as_payload_ptr(), f)
}
}

/// A `*mut T` non-zero sample pointer to the user payload.
#[repr(transparent)]
pub struct RawSampleMut<T: ?Sized> {
payload: *mut T,
}

impl<T: ?Sized> RawSampleMut<T> {
/// Creates a new `RawSampleMut`.
///
/// # Safety
///
/// `payload` must be non-null.
#[inline]
pub unsafe fn new_unchecked(payload: *mut T) -> Self {
debug_assert!(
!payload.is_null(),
"RawSampleMut::new_unchecked requires that the payload pointer is non-null"
);
Self { payload }
}

/// Creates a new `RawSampleMut`.
#[allow(clippy::not_unsafe_ptr_arg_deref)] // false positive
#[inline]
pub fn new(payload: *mut T) -> Option<Self> {
if !payload.is_null() {
// SAFETY: `payload` pointer is checked to be non-null
Some(unsafe { Self::new_unchecked(payload) })
} else {
None
}
}

/// Casts to a `RawSampleMut` of another type.
#[must_use]
#[inline]
pub fn cast<U>(self) -> RawSampleMut<U> {
// SAFETY: `self.as_payload_mut_ptr` returns a non-null ptr
unsafe { RawSampleMut::new_unchecked(self.as_payload_mut_ptr().cast::<U>()) }
}

/// Acquires the underlying payload pointer as `*const` pointer.
#[must_use]
#[inline(always)]
pub fn as_payload_ptr(self) -> *const T {
self.as_payload_mut_ptr()
}

/// Acquires the underlying payload pointer as `*mut` pointer.
#[must_use]
#[inline(always)]
pub fn as_payload_mut_ptr(self) -> *mut T {
self.payload
}

/// Returns a reference to the `ChunkHeader`.
#[must_use]
#[inline]
pub fn chunk_header(&self) -> &ChunkHeader {
// SAFTEY: `self.as_payload_ptr` returns a non-null ptr
unsafe { ChunkHeader::from_user_payload_unchecked(self.as_payload_ptr() as *const c_void) }
}
}

impl<T> RawSampleMut<[T]> {
/// Creates a non-null raw slice from a thin payload pointer and a length.
///
/// The `len` argument is the number of **elements**, not the number of bytes.
///
/// This function is safe, but dereferencing the return value is unsafe.
/// See the documentation of [`slice::from_raw_parts_mut`] for slice safety requirements.
#[must_use]
#[inline]
pub fn slice_from_raw_parts(sample: RawSampleMut<T>, len: usize) -> RawSampleMut<[T]> {
// SAFETY: `self.as_payload_mut_ptr` returns a non-null ptr
unsafe { Self::new_unchecked(slice::from_raw_parts_mut(sample.as_payload_mut_ptr(), len)) }
}

/// Returns the length of a non-null raw slice.
#[must_use]
#[inline]
pub fn len(self) -> usize {
// SAFETY: `self.as_payload_ptr` returns a non-null ptr
unsafe { (*self.as_payload_ptr()).len() }
}
}

impl<T: ?Sized> Clone for RawSampleMut<T> {
#[inline(always)]
fn clone(&self) -> Self {
*self
}
}

impl<T: ?Sized> Copy for RawSampleMut<T> {}

impl<T: ?Sized> fmt::Debug for RawSampleMut<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.as_payload_ptr(), f)
}
}

impl<T: ?Sized> fmt::Pointer for RawSampleMut<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.as_payload_ptr(), f)
}
}
Loading

0 comments on commit 0f14f04

Please sign in to comment.