Skip to content

Commit

Permalink
Raw pointer API for publisher and subscriber
Browse files Browse the repository at this point in the history
  • Loading branch information
elBoberido committed Jul 29, 2023
1 parent f04a339 commit e91c1b0
Show file tree
Hide file tree
Showing 10 changed files with 396 additions and 166 deletions.
5 changes: 3 additions & 2 deletions iceoryx-sys/src/chunk_header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ 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;
pub(super) unsafe fn from_user_payload_unchecked<'a>(
payload: *const c_void,
) -> Option<&'a Self> {
unsafe {
let chunk_header = cpp!([payload as "void*"] -> *const c_void as "const void*" {
return iox::mepoo::ChunkHeader::fromUserPayload(payload);
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
187 changes: 187 additions & 0 deletions iceoryx-sys/src/sample.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
// 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;

#[repr(transparent)]
pub struct RawSample<T: ?Sized> {
payload: *const T,
}

impl<T: ?Sized> RawSample<T> {
#[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 }
}

#[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
}
}

#[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>()) }
}

#[must_use]
#[inline(always)]
pub fn as_payload_ptr(self) -> *const T {
self.payload
}

#[must_use]
#[inline]
pub fn chunk_header(&self) -> &ChunkHeader {
unsafe {
ChunkHeader::from_user_payload_unchecked(self.as_payload_ptr() as *const c_void)
.expect("Valid chunk header from payload!")
}
}
}

impl<T> RawSample<[T]> {
#[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)) }
}

#[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)
}
}

#[repr(transparent)]
pub struct RawSampleMut<T: ?Sized> {
payload: *mut T,
}

impl<T: ?Sized> RawSampleMut<T> {
#[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 }
}

#[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
}
}

#[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>()) }
}

#[must_use]
#[inline(always)]
pub fn as_payload_ptr(self) -> *const T {
self.as_payload_mut_ptr()
}

#[must_use]
#[inline(always)]
pub fn as_payload_mut_ptr(self) -> *mut T {
self.payload
}

#[must_use]
#[inline]
pub fn chunk_header(&self) -> &ChunkHeader {
unsafe {
ChunkHeader::from_user_payload_unchecked(self.as_payload_ptr() as *const c_void)
.expect("Valid chunk header from payload!")
}
}
}

impl<T> RawSampleMut<[T]> {
#[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)) }
}

#[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)
}
}
54 changes: 40 additions & 14 deletions iceoryx-sys/src/subscriber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// SPDX-FileContributor: Mathias Kraus
// SPDX-FileContributor: Apex.AI

use crate::SubscriberOptions;
use crate::{RawSample, SubscriberOptions};

use std::ffi::{c_void, CString};
use std::fmt;
Expand Down Expand Up @@ -244,29 +244,55 @@ impl Subscriber {
}
}

pub fn get_chunk<T>(&self) -> Option<*const T> {
pub fn try_take<T>(&self) -> Option<RawSample<T>> {
unsafe { self.try_get_chunk().map(|payload| payload.cast::<T>()) }
}

pub fn try_take_slice<T>(&self) -> Option<RawSample<[T]>> {
unsafe {
let payload = cpp!([self as "SubscriberPortUser*"] -> *const std::ffi::c_void as "const void*" {
auto getChunkResult = self->tryGetChunk();
let payload = self.try_get_chunk()?;

if (getChunkResult.has_error()) {
return nullptr;
}
let chunk_header = payload.chunk_header();
let payload_size = chunk_header.get_user_payload_size();
let payload_alignment = chunk_header.get_user_payload_alignment();
let len = payload_size as usize / std::mem::size_of::<T>();

return getChunkResult.value()->userPayload();
});

if !payload.is_null() {
Some(payload as *const T)
if payload_size as usize % std::mem::size_of::<T>() == 0
&& payload_alignment as usize >= std::mem::align_of::<T>()
{
Some(RawSample::slice_from_raw_parts(
payload.cast::<T>(),
len as usize,
))
} else {
// TODO return Result<Option<T>>
self.release(payload);
None
}
}
}

pub fn release_chunk<T: ?Sized>(&self, payload: *const T) {
unsafe fn try_get_chunk(&self) -> Option<RawSample<c_void>> {
let payload = cpp!([self as "SubscriberPortUser*"] -> *const std::ffi::c_void as "const void*" {
auto getChunkResult = self->tryGetChunk();

if (getChunkResult.has_error()) {
return nullptr;
}

return getChunkResult.value()->userPayload();
});

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

pub fn release<T: ?Sized>(&self, sample: RawSample<T>) {
unsafe {
let payload = payload as *const c_void;
let payload = sample.cast::<c_void>().as_payload_ptr();
cpp!([self as "SubscriberPortUser*", payload as "void*"] {
auto header = iox::mepoo::ChunkHeader::fromUserPayload(payload);
self->releaseChunk(header);
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ pub mod mt {
// re-exports from iceoryx-sys
pub use ffi::ConsumerTooSlowPolicy;
pub use ffi::QueueFullPolicy;
pub use ffi::RawSample;
pub use ffi::RawSampleMut;
pub use ffi::Runtime;
pub use ffi::SubscribeState;

Expand Down
Loading

0 comments on commit e91c1b0

Please sign in to comment.