-
Notifications
You must be signed in to change notification settings - Fork 58
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
Conversation
Cargo.toml
Outdated
@@ -61,7 +61,7 @@ serde_yaml = "0.9.19" | |||
|
|||
[lib] | |||
path="src/lib.rs" | |||
name = "zenohc" | |||
name = "zenohcd" |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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
@@ -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, |
There was a problem hiding this comment.
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
* 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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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(), |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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() { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
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); |
There was a problem hiding this comment.
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);
There was a problem hiding this 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 { |
There was a problem hiding this comment.
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)) } |
There was a problem hiding this comment.
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
sample is reworked in dev/1.0.0, this PR is outtated |
This PR proposes to obfuscate
z_sample_t
, allowing future API additions and layout changes