Skip to content

Commit

Permalink
introduce split buffers in zenoh-c
Browse files Browse the repository at this point in the history
  • Loading branch information
p-avital committed Mar 5, 2024
1 parent 3eb7236 commit 769d824
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 6 deletions.
55 changes: 54 additions & 1 deletion include/zenoh_commons.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,11 +190,22 @@ typedef struct z_attachment_t {
z_attachment_iter_driver_t iteration_driver;
} z_attachment_t;
/**
* A buffer owned by Zenoh.
* A split buffer that owns all of its data.
*
* To minimize copies and reallocations, Zenoh may provide you data in split buffers.
*
* You can use `z_buffer_contiguous` to obtain a contiguous version of a buffer.
* If the buffer was already contiguous, the reference count will simply be increased.
* Otherwise, the split buffer's entire content will be copied in a newly allocated buffer.
*/
typedef struct z_owned_buffer_t {
size_t _inner[5];
} z_owned_buffer_t;
/**
* A loan of a `z_owned_buffer_t`.
*
* As it is a split buffer, it may contain more than one slice. It's number of slices is returned by `z_buffer_slice_count`.
*/
typedef struct z_buffer_t {
const void *_inner;
} z_buffer_t;
Expand Down Expand Up @@ -1038,12 +1049,54 @@ int8_t z_attachment_iterate(struct z_attachment_t this_,
* Returns the gravestone value for `z_attachment_t`.
*/
ZENOHC_API struct z_attachment_t z_attachment_null(void);
/**
* Returns `true` if the buffer is in a valid state.
*/
ZENOHC_API bool z_buffer_check(const struct z_owned_buffer_t *buffer);
/**
* Increments the buffer's reference count, returning an owned version of the buffer.
*/
ZENOHC_API struct z_owned_buffer_t z_buffer_clone(struct z_buffer_t buffer);
/**
* Returns an owned version of this buffer whose data is guaranteed to be contiguous in memory.
*
* This is achieved by increasing the reference count if the buffer is already contiguous, and by copying its data in a new contiguous buffer if it wasn't.
*/
ZENOHC_API
struct z_owned_buffer_t z_buffer_contiguous(struct z_buffer_t buffer);
/**
* Decrements the buffer's reference counter, destroying it if applicable.
*
* `buffer` will be reset to `z_buffer_null`, preventing UB on double-frees.
*/
ZENOHC_API void z_buffer_drop(struct z_owned_buffer_t *buffer);
/**
* Loans the buffer, allowing you to call functions that only need a loan of it.
*/
ZENOHC_API struct z_buffer_t z_buffer_loan(const struct z_owned_buffer_t *buffer);
/**
* The gravestone value for `z_owned_buffer_t`.
*/
ZENOHC_API struct z_owned_buffer_t z_buffer_null(void);
/**
* Returns the payload of the buffer if it is contiguous, aliasling it.
*
* If the payload was not contiguous in memory, `z_bytes_null` will be returned instead.
*/
ZENOHC_API struct z_bytes_t z_buffer_payload(struct z_buffer_t buffer);
/**
* Returns the `index`th slice of the buffer, aliasing it.
*
* Out of bounds accesses will return `z_bytes_null`.
*/
ZENOHC_API struct z_bytes_t z_buffer_slice_at(struct z_buffer_t buffer, size_t index);
/**
* Returns the number of slices in the buffer.
*
* If the return value is 0 or 1, then the buffer's data is contiguous in memory and `z_buffer_contiguous` will succeed.
*/
ZENOHC_API
size_t z_buffer_slice_count(struct z_buffer_t buffer);
/**
* Returns ``true`` if `b` is initialized.
*/
Expand Down
66 changes: 61 additions & 5 deletions src/collections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,13 @@ impl From<&[u8]> for z_bytes_t {
}
}

/// A buffer owned by Zenoh.
/// A split buffer that owns all of its data.
///
/// To minimize copies and reallocations, Zenoh may provide you data in split buffers.
///
/// You can use `z_buffer_contiguous` to obtain a contiguous version of a buffer.
/// If the buffer was already contiguous, the reference count will simply be increased.
/// Otherwise, the split buffer's entire content will be copied in a newly allocated buffer.
#[repr(C)]
pub struct z_owned_buffer_t {
_inner: [usize; 5],
Expand Down Expand Up @@ -203,24 +209,35 @@ impl core::ops::DerefMut for z_owned_buffer_t {
}
}

/// The gravestone value for `z_owned_buffer_t`.
#[no_mangle]
pub extern "C" fn z_buffer_null() -> z_owned_buffer_t {
unsafe { core::mem::transmute(None::<ZBuf>) }
}

/// Decrements the buffer's reference counter, destroying it if applicable.
///
/// `buffer` will be reset to `z_buffer_null`, preventing UB on double-frees.
#[no_mangle]
pub extern "C" fn z_buffer_drop(buffer: &mut z_owned_buffer_t) {
core::mem::drop(buffer.take())
}

/// Returns `true` if the buffer is in a valid state.
#[no_mangle]
pub extern "C" fn z_buffer_check(buffer: &z_owned_buffer_t) -> bool {
buffer.is_some()
}

/// Loans the buffer, allowing you to call functions that only need a loan of it.
#[no_mangle]
pub extern "C" fn z_buffer_loan(buffer: &z_owned_buffer_t) -> z_buffer_t {
buffer.as_ref().into()
}

/// A loan of a `z_owned_buffer_t`.
///
/// As it is a split buffer, it may contain more than one slice. It's number of slices is returned by `z_buffer_slice_count`.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct z_buffer_t<'a> {
Expand All @@ -233,21 +250,60 @@ impl<'a> From<z_buffer_t<'a>> for Option<&'a ZBuf> {
}
}

/// Increments the buffer's reference count, returning an owned version of the buffer.
#[no_mangle]
pub extern "C" fn z_buffer_clone(buffer: z_buffer_t) -> z_owned_buffer_t {
unsafe { Some(core::mem::transmute::<_, &ZBuf>(buffer).clone()).into() }
}

/// Returns the payload of the buffer if it is contiguous, aliasling it.
///
/// If the payload was not contiguous in memory, `z_bytes_null` will be returned instead.
#[no_mangle]
pub extern "C" fn z_buffer_payload(buffer: z_buffer_t) -> z_bytes_t {
let Some(buffer): Option<&ZBuf> = buffer.into() else {
return z_bytes_null();
};
match buffer.contiguous() {
std::borrow::Cow::Borrowed(buffer) => buffer.into(),
std::borrow::Cow::Owned(_) => {
log::error!("A non-contiguous buffer reached user code, this is definitely a bug, please inform us at https://github.com/eclipse-zenoh/zenoh-c/issues/new");
z_bytes_null()
}
std::borrow::Cow::Owned(_) => z_bytes_null(),
}
}

/// Returns an owned version of this buffer whose data is guaranteed to be contiguous in memory.
///
/// This is achieved by increasing the reference count if the buffer is already contiguous, and by copying its data in a new contiguous buffer if it wasn't.
#[no_mangle]
pub extern "C" fn z_buffer_contiguous(buffer: z_buffer_t) -> z_owned_buffer_t {
let Some(buf): Option<&ZBuf> = buffer.into() else {
return z_buffer_null();
};
match buf.contiguous() {
std::borrow::Cow::Borrowed(_) => buf.clone().into(),
std::borrow::Cow::Owned(buf) => ZBuf::from(buf).into(),
}
}

/// Returns the number of slices in the buffer.
///
/// If the return value is 0 or 1, then the buffer's data is contiguous in memory and `z_buffer_contiguous` will succeed.
#[no_mangle]
pub extern "C" fn z_buffer_slice_count(buffer: z_buffer_t) -> usize {
match buffer.into() {
None => 0,
Some(buf) => ZBuf::slices(buf).len(),
}
}

/// Returns the `index`th slice of the buffer, aliasing it.
///
/// Out of bounds accesses will return `z_bytes_null`.
#[no_mangle]
pub extern "C" fn z_buffer_slice_at(buffer: z_buffer_t, index: usize) -> z_bytes_t {
match buffer.into() {
None => z_bytes_null(),
Some(buf) => ZBuf::slices(buf)
.nth(index)
.map_or(z_bytes_null(), |slice| slice.into()),
}
}

0 comments on commit 769d824

Please sign in to comment.