Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Sample API rework #251

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open

Proposal: Sample API rework #251

wants to merge 8 commits into from

Conversation

p-avital
Copy link
Contributor

This PR proposes to obfuscate z_sample_t, allowing future API additions and layout changes

@eclipse-zenoh-bot
Copy link
Contributor

@p-avital If this pull request contains a bugfix or a new feature, then please consider using Closes #ISSUE-NUMBER syntax to link it to an issue.

@p-avital p-avital requested a review from milyin March 1, 2024 10:34
Cargo.toml Outdated
@@ -61,7 +61,7 @@ serde_yaml = "0.9.19"

[lib]
path="src/lib.rs"
name = "zenohc"
name = "zenohcd"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

undo this automatic change

@@ -623,7 +625,7 @@ typedef struct z_delete_options_t {
* To check if `val` is still valid, you may use `z_X_check(&val)` (or `z_check(val)` if your compiler supports `_Generic`), which will return `true` if `val` is valid.
*/
typedef struct z_owned_encoding_t {
enum z_encoding_prefix_t prefix;
uint64_t prefix;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change should come in a separate PR.

src/commons.rs Outdated
pub fn new(sample: &'a Sample, owner: &'a ZBuf) -> Self {
let std::borrow::Cow::Borrowed(payload) = owner.contiguous() else {
pub fn new(sample: &'a Sample) -> Self {
if !sample.value.payload.zslices().count() <= 1 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if ! zslices.count <= 1 is exactly if zslices.count >1, but less readable
And should we also check for emply payload: zslices.count == 0 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the exposure of ZBuf, we actually don't want to check it at all anymore, because samples with non-contiguous payloads are now allowed

src/commons.rs Show resolved Hide resolved
@@ -436,21 +428,24 @@ impl From<zenoh_protocol::core::KnownEncoding> for z_encoding_prefix_t {
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct z_encoding_t {
pub prefix: z_encoding_prefix_t,
pub prefix: u64,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As @Mallets mentioned, this change and changes below are for separate PR

src/lib.rs Show resolved Hide resolved
@milyin milyin linked an issue Mar 12, 2024 that may be closed by this pull request
@Mallets Mallets added the api sync Synchronize API with other bindings label Mar 14, 2024
* 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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is similar situation to mutable API discussed previously with @Mallets . Implicit copy API is bad for the following reasons:

  • masks the potential operation overhead (for user code reviewers)
  • isolates user code from having knowledge on the side effects
  • forces user code to have only one behavior (what if user wants to drop non-contiguous buffers instead of copying them? or user wants to process non-contiguous buffers in separate code branch? or user doesn't need to create z_owned_buffer_t wrapper around contiguous data?)

Instead, I just suggest adding bool is_contiguous(struct z_buffer_t); convenience method and give user all the freedom.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed at the meeting today, I support the idea of removing z_buffer_contiguous both here and in rust.
For simple cases (number, string, etc., which IMHO will cover most of the needs of ordinary users), simple operations for obtaining them should be provided, for complex ones (working directly with buffers, which will most likely be used by experienced users) we should not hide the internal structure to avoid side effects.

///
/// 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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the behavior of this method is counterintuitive for most users, since it might be unexpected that z_buffer_contiguous needs to be called in certain cases (which ones btw? i.e. can we reliable predict when the buffer contents is actually guaranteed to be contagious ?) to receive non-null z_bytes_t. I would rather suggest to modify the output of this method into vector of z_bytes_t, or return a struct containing a union z_bytes_t/vector of z_bytes_t to more closely resemble what we have in rust.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the logic I suggest here, this method won't be needed anymore

};
match buffer.contiguous() {
std::borrow::Cow::Borrowed(buffer) => buffer.into(),
std::borrow::Cow::Owned(_) => z_bytes_null(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we get the Owned enum member, it means we already allocated and copied element into a vector. It seems to be a pity not to return the data in this case.

Copy link
Contributor Author

@p-avital p-avital Mar 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The core problem here is that z_bytes_t is a borrowed type according to our convention: it may never be owned by the user. Hence, the user must never call its destructor, so giving the vector tp the user would cause a memory leak, or break the ownership conventions.

};
let sample = z_sample_t::new(&sample, &owner);
.callback(move |mut sample| {
if let std::borrow::Cow::Owned(v) = sample.payload.contiguous() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose since we have api for working with non-contiguous ZBuf, we no longer need to convert payload to contiguous.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct :)

pub struct zc_owned_sample_t {
_0: z_owned_keyexpr_t,
_1: z_owned_buffer_t,
_2: z_owned_buffer_t,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Todo: investigate if this size padding should be made with _3

*
* 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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Todo: let's remove this

Comment on lines +2058 to +2064
ZENOHC_API struct z_owned_buffer_t z_sample_owned_payload(const struct z_sample_t *sample);
/**
* The sample's data, the return value aliases the sample.
*
* If you need ownership of the buffer, you may use `z_sample_owned_payload`.
*/
ZENOHC_API struct z_bytes_t z_sample_payload(const struct z_sample_t *sample);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Todo: replace this with one method
ZENOHC_API struct z_payload_t z_sample_payload(const struct z_sample_t *sample);

Copy link
Contributor

@yellowhatter yellowhatter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes requested: @sashacmc @DenisBiryukov91 @milyin @yellowhatter
Also we would like to implement read method in separate PR
Also we have to be able to send z_owned_buffer_t instead of (or with) z_bytes_t

@@ -21,8 +27,8 @@ use zenoh::prelude::ZenohId;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct z_bytes_t {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo: replace with ZSlice API everywhere

std::borrow::Cow::Borrowed(_) => value,
std::borrow::Cow::Owned(value) => value.into(),
};
unsafe { core::mem::transmute(Some(value)) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

core::mem::transmute should be used ONLY in impl_guarded_transmute macro, so no such code should present

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api sync Synchronize API with other bindings
Projects
Status: Backlog
Development

Successfully merging this pull request may close these issues.

zc_payload_rcinc and zc_attachment_rcinc
7 participants