diff --git a/examples/z_put.c b/examples/z_put.c index 372fbba57..2db51eb9e 100644 --- a/examples/z_put.c +++ b/examples/z_put.c @@ -23,6 +23,9 @@ int main(int argc, char **argv) { if (argc > 1) keyexpr = argv[1]; if (argc > 2) value = argv[2]; + z_owned_bytes_map_t attachment = z_bytes_map_new(); + z_bytes_map_insert_by_copy(&attachment, z_bytes_new("hello"), z_bytes_new("there")); + z_owned_config_t config = z_config_default(); if (argc > 3) { if (zc_config_insert_json(z_loan(config), Z_CONFIG_CONNECT_KEY, argv[3]) < 0) { @@ -44,11 +47,13 @@ int main(int argc, char **argv) { printf("Putting Data ('%s': '%s')...\n", keyexpr, value); z_put_options_t options = z_put_options_default(); options.encoding = z_encoding(Z_ENCODING_PREFIX_TEXT_PLAIN, NULL); + options.attachment = z_bytes_map_as_attachment(&attachment); int res = z_put(z_loan(s), z_keyexpr(keyexpr), (const uint8_t *)value, strlen(value), &options); if (res < 0) { printf("Put failed...\n"); } z_close(z_move(s)); + z_drop(z_move(attachment)); return 0; } diff --git a/examples/z_sub.c b/examples/z_sub.c index ccfb32825..05bbdea8b 100644 --- a/examples/z_sub.c +++ b/examples/z_sub.c @@ -11,6 +11,7 @@ // Contributors: // ZettaScale Zenoh Team, // +#include #include #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) #include @@ -26,6 +27,10 @@ void data_handler(const z_sample_t *sample, void *arg) { z_owned_str_t keystr = z_keyexpr_to_string(sample->keyexpr); printf(">> [Subscriber] Received %s ('%s': '%.*s')\n", kind_to_str(sample->kind), z_loan(keystr), (int)sample->payload.len, sample->payload.start); + if (z_check(sample->attachment)) { + z_owned_bytes_map_t map = z_bytes_map_from_attachment(sample->attachment); + z_bytes_t there = z_bytes_map_get(&map, z_bytes_new("hello")); + } z_drop(z_move(keystr)); } @@ -91,4 +96,4 @@ const char *kind_to_str(z_sample_kind_t kind) { default: return "UNKNOWN"; } -} \ No newline at end of file +} diff --git a/include/zenoh_commons.h b/include/zenoh_commons.h index 6285f81b8..2907def81 100644 --- a/include/zenoh_commons.h +++ b/include/zenoh_commons.h @@ -149,12 +149,68 @@ typedef enum zcu_reply_keyexpr_t { ZCU_REPLY_KEYEXPR_MATCHING_QUERY = 1, } zcu_reply_keyexpr_t; /** - * An array of bytes. + * A contiguous view of bytes owned by some other entity. + * + * `start` being `null` is considered a gravestone value, + * and empty slices are represented using a possibly dangling pointer for `start`. */ typedef struct z_bytes_t { size_t len; const uint8_t *start; } z_bytes_t; +/** + * The body of a loop over an attachment's key-value pairs. + * + * `key` and `value` are loaned to the body for the duration of a single call. + * `context` is passed transparently through the iteration driver. + * + * Returning `0` is treated as `continue`. + * Returning any other value is treated as `break`. + */ +typedef int8_t (*z_attachment_iter_body_t)(struct z_bytes_t key, + struct z_bytes_t value, + void *context); +/** + * The driver of a loop over an attachment's key-value pairs. + * + * This function is expected to call `loop_body` once for each key-value pair + * within `iterator`, passing `context`, and returning any non-zero value immediately (breaking iteration). + */ +typedef int8_t (*z_attachment_iter_driver_t)(void *iterator, + z_attachment_iter_body_t loop_body, + void *context); +/** + * The v-table for an attachment. + */ +typedef struct z_attachment_vtable_t { + /** + * See `z_attachment_iteration_driver_t`'s documentation. + */ + z_attachment_iter_driver_t iteration_driver; + /** + * Returns the number of key-value pairs within the attachment. + */ + size_t (*len)(const void*); +} z_attachment_vtable_t; +/** + * A v-table based map of byte slice to byte slice. + * + * `vtable == NULL` marks the gravestone value, as this type is often optional. + * Users are encouraged to use `z_attachment_null` and `z_attachment_check` to interact. + */ +typedef struct z_attachment_t { + void *data; + const struct z_attachment_vtable_t *vtable; +} z_attachment_t; +/** + * A map of maybe-owned vector of bytes to owned vector of bytes. + * + * In Zenoh C, this map is backed by Rust's standard HashMap, with a DoS-resistant hasher + */ +typedef struct z_owned_bytes_map_t { + uint64_t _0[2]; + size_t _1[4]; +} z_owned_bytes_map_t; /** * Represents a Zenoh ID. * @@ -340,6 +396,7 @@ typedef struct z_sample_t { const void *_zc_buf; enum z_sample_kind_t kind; struct z_timestamp_t timestamp; + struct z_attachment_t attachment; } z_sample_t; /** * A closure is a structure that contains all the elements for stateful, memory-leak-free callbacks. @@ -629,6 +686,7 @@ typedef struct z_put_options_t { struct z_encoding_t encoding; enum z_congestion_control_t congestion_control; enum z_priority_t priority; + struct z_attachment_t attachment; } z_put_options_t; /** * Represents the set of options that can be applied to a query reply, @@ -812,10 +870,123 @@ ZENOHC_API extern const char *Z_CONFIG_MULTICAST_IPV4_ADDRESS_KEY; ZENOHC_API extern const char *Z_CONFIG_SCOUTING_TIMEOUT_KEY; ZENOHC_API extern const char *Z_CONFIG_SCOUTING_DELAY_KEY; ZENOHC_API extern const char *Z_CONFIG_ADD_TIMESTAMP_KEY; +/** + * Returns the gravestone value for `z_attachment_t`. + */ +ZENOHC_API bool z_attachment_check(const struct z_attachment_t *this_); +/** + * Iterate over `this`'s key-value pairs, breaking if `body` returns a non-zero + * value for a key-value pair, and returning the latest return value. + * + * `context` is passed to `body` to allow stateful closures. + * + * This function takes no ownership whatsoever. + */ +ZENOHC_API +int8_t z_attachment_iterate(struct z_attachment_t this_, + z_attachment_iter_body_t body, + void *context); +/** + * Returns the number of key-value pairs in `this`. + */ +ZENOHC_API size_t z_attachment_len(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 `b` is initialized. */ ZENOHC_API bool z_bytes_check(const struct z_bytes_t *b); +/** + * Aliases `this` into a generic `z_attachment_t`, allowing it to be passed to corresponding APIs. + */ +ZENOHC_API struct z_attachment_t z_bytes_map_as_attachment(const struct z_owned_bytes_map_t *this_); +/** + * Returns `true` if the map is not in its gravestone state + */ +ZENOHC_API bool z_bytes_map_check(const struct z_owned_bytes_map_t *this_); +/** + * Destroys the map, resetting `this` to its gravestone value. + * + * This function is double-free safe, passing a pointer to the gravestone value will have no effect. + */ +ZENOHC_API void z_bytes_map_drop(struct z_owned_bytes_map_t *this_); +/** + * Constructs a map from the provided attachment, copying keys and values. + * + * If `this` is at gravestone value, the returned value will also be at gravestone value. + */ +ZENOHC_API struct z_owned_bytes_map_t z_bytes_map_from_attachment(struct z_attachment_t this_); +/** + * Constructs a map from the provided attachment, aliasing the attachment's keys and values. + * + * If `this` is at gravestone value, the returned value will also be at gravestone value. + */ +ZENOHC_API +struct z_owned_bytes_map_t z_bytes_map_from_attachment_aliasing(struct z_attachment_t this_); +/** + * Returns the value associated with `key`, returning a gravestone value if: + * - `this` or `key` is in gravestone state. + * - `this` has no value associated to `key` + */ +ZENOHC_API +struct z_bytes_t z_bytes_map_get(const struct z_owned_bytes_map_t *this_, + struct z_bytes_t key); +/** + * Associates `value` to `key` in the map, aliasing them. + * + * Note that once `key` is aliased, reinserting at the same key may alias the previous instance, or the new instance of `key`. + * + * Calling this with `NULL` or the gravestone value is undefined behaviour. + */ +ZENOHC_API +void z_bytes_map_insert_by_alias(const struct z_owned_bytes_map_t *this_, + struct z_bytes_t key, + struct z_bytes_t value); +/** + * Associates `value` to `key` in the map, copying them to obtain ownership: `key` and `value` are not aliased past the function's return. + * + * Calling this with `NULL` or the gravestone value is undefined behaviour. + */ +ZENOHC_API +void z_bytes_map_insert_by_copy(const struct z_owned_bytes_map_t *this_, + struct z_bytes_t key, + struct z_bytes_t value); +/** + * Iterates over the key-value pairs in the map. + * + * `body` will be called once per pair, with `ctx` as its last argument. + * If `body` returns a non-zero value, the iteration will stop immediately and the value will be returned. + * Otherwise, this will return 0 once all pairs have been visited. + * `body` is not given ownership of the key nor value, which alias the pairs in the map. + * It is safe to keep these aliases until existing keys are modified/removed, or the map is destroyed. + * Note that this map is unordered. + * + * Calling this with `NULL` or the gravestone value is undefined behaviour. + */ +ZENOHC_API +int8_t z_bytes_map_iter(const struct z_owned_bytes_map_t *this_, + z_attachment_iter_body_t body, + void *ctx); +/** + * Constructs a new map. + */ +ZENOHC_API struct z_owned_bytes_map_t z_bytes_map_new(void); +/** + * Constructs the gravestone value for `z_owned_bytes_map_t` + */ +ZENOHC_API struct z_owned_bytes_map_t z_bytes_map_null(void); +/** + * Returns a view of `str` using `strlen`. + * + * `str == NULL` will cause this to return `z_bytes_null()` + */ +ZENOHC_API struct z_bytes_t z_bytes_new(const char *str); +/** + * Returns the gravestone value for `z_bytes_t` + */ +ZENOHC_API struct z_bytes_t z_bytes_null(void); /** * Closes a zenoh session. This drops and invalidates `session` for double-drop safety. * diff --git a/include/zenoh_macros.h b/include/zenoh_macros.h index 47843d6c8..830e4d271 100644 --- a/include/zenoh_macros.h +++ b/include/zenoh_macros.h @@ -36,6 +36,7 @@ z_owned_closure_zid_t * : z_closure_zid_drop, \ z_owned_reply_channel_closure_t * : z_reply_channel_closure_drop, \ z_owned_reply_channel_t * : z_reply_channel_drop, \ + z_owned_bytes_map_t * : z_bytes_map_drop, \ zc_owned_payload_t * : zc_payload_drop, \ zc_owned_shmbuf_t * : zc_shmbuf_drop, \ zc_owned_shm_manager_t * : zc_shm_manager_drop, \ @@ -44,6 +45,8 @@ ze_owned_querying_subscriber_t * : ze_undeclare_querying_subscriber \ )(x) +// TODO(sashacmc): Check z_attachment_t name + #define z_null(x) (*x = \ _Generic((x), z_owned_session_t * : z_session_null, \ z_owned_publisher_t * : z_publisher_null, \ @@ -64,6 +67,7 @@ z_owned_closure_zid_t * : z_closure_zid_null, \ z_owned_reply_channel_closure_t * : z_reply_channel_closure_null, \ z_owned_reply_channel_t * : z_reply_channel_null, \ + z_attachment_t : z_attachment_null, \ zc_owned_payload_t * : zc_payload_null, \ zc_owned_shmbuf_t * : zc_shmbuf_null, \ zc_owned_shm_manager_t * : zc_shm_manager_null, \ @@ -86,6 +90,8 @@ z_owned_reply_t : z_reply_check, \ z_owned_hello_t : z_hello_check, \ z_owned_str_t : z_str_check, \ + z_attachment_t : z_attachment_check, \ + z_owned_bytes_map_t : z_bytes_map_check, \ zc_owned_payload_t : zc_payload_check, \ zc_owned_shmbuf_t : zc_shmbuf_check, \ zc_owned_shm_manager_t : zc_shm_manager_check, \ diff --git a/src/attachment.rs b/src/attachment.rs new file mode 100644 index 000000000..1d9f127e2 --- /dev/null +++ b/src/attachment.rs @@ -0,0 +1,292 @@ +use std::{borrow::Cow, cell::UnsafeCell, collections::HashMap}; + +use libc::c_void; + +use crate::{z_bytes_null, z_bytes_t}; + +/// The body of a loop over an attachment's key-value pairs. +/// +/// `key` and `value` are loaned to the body for the duration of a single call. +/// `context` is passed transparently through the iteration driver. +/// +/// Returning `0` is treated as `continue`. +/// Returning any other value is treated as `break`. +pub type z_attachment_iter_body_t = + extern "C" fn(key: z_bytes_t, value: z_bytes_t, context: *mut c_void) -> i8; + +/// The driver of a loop over an attachment's key-value pairs. +/// +/// This function is expected to call `loop_body` once for each key-value pair +/// within `iterator`, passing `context`, and returning any non-zero value immediately (breaking iteration). +pub type z_attachment_iter_driver_t = extern "C" fn( + iterator: *mut c_void, + loop_body: z_attachment_iter_body_t, + context: *mut c_void, +) -> i8; + +/// The v-table for an attachment. +#[repr(C)] +pub struct z_attachment_vtable_t { + /// See `z_attachment_iteration_driver_t`'s documentation. + iteration_driver: z_attachment_iter_driver_t, + /// Returns the number of key-value pairs within the attachment. + len: extern "C" fn(*const c_void) -> usize, +} + +/// A v-table based map of byte slice to byte slice. +/// +/// `vtable == NULL` marks the gravestone value, as this type is often optional. +/// Users are encouraged to use `z_attachment_null` and `z_attachment_check` to interact. +#[repr(C)] +pub struct z_attachment_t { + data: *mut c_void, + vtable: Option<&'static z_attachment_vtable_t>, +} + +/// Returns the gravestone value for `z_attachment_t`. +#[no_mangle] +pub extern "C" fn z_attachment_check(this: &z_attachment_t) -> bool { + this.vtable.is_some() +} + +/// Returns the gravestone value for `z_attachment_t`. +#[no_mangle] +pub extern "C" fn z_attachment_null() -> z_attachment_t { + z_attachment_t { + data: core::ptr::null_mut(), + vtable: None, + } +} + +/// Iterate over `this`'s key-value pairs, breaking if `body` returns a non-zero +/// value for a key-value pair, and returning the latest return value. +/// +/// `context` is passed to `body` to allow stateful closures. +/// +/// This function takes no ownership whatsoever. +#[no_mangle] +pub extern "C" fn z_attachment_iterate( + this: z_attachment_t, + body: z_attachment_iter_body_t, + context: *mut c_void, +) -> i8 { + (this.vtable.unwrap().iteration_driver)(this.data, body, context) +} + +/// Returns the number of key-value pairs in `this`. +#[no_mangle] +pub extern "C" fn z_attachment_len(this: z_attachment_t) -> usize { + (this.vtable.unwrap().len)(this.data) +} + +/// A map of maybe-owned vector of bytes to owned vector of bytes. +/// +/// In Zenoh C, this map is backed by Rust's standard HashMap, with a DoS-resistant hasher +#[repr(C)] +pub struct z_owned_bytes_map_t { + _0: [u64; 2], + _1: [usize; 4], +} +impl core::ops::Deref for z_owned_bytes_map_t { + type Target = Option, Cow<'static, [u8]>>>>; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} + +/// Constructs a new map. +#[no_mangle] +pub extern "C" fn z_bytes_map_new() -> z_owned_bytes_map_t { + unsafe { core::mem::transmute(Some(HashMap::, Cow<[u8]>>::new())) } +} + +/// Constructs the gravestone value for `z_owned_bytes_map_t` +#[no_mangle] +pub extern "C" fn z_bytes_map_null() -> z_owned_bytes_map_t { + unsafe { core::mem::transmute(None::, Cow<[u8]>>>) } +} + +/// Returns `true` if the map is not in its gravestone state +#[no_mangle] +pub extern "C" fn z_bytes_map_check(this: &z_owned_bytes_map_t) -> bool { + this.is_some() +} +/// Destroys the map, resetting `this` to its gravestone value. +/// +/// This function is double-free safe, passing a pointer to the gravestone value will have no effect. +#[no_mangle] +pub extern "C" fn z_bytes_map_drop(this: &mut z_owned_bytes_map_t) { + let this = core::mem::replace(this, z_bytes_map_null()); + if z_bytes_map_check(&this) { + core::mem::drop(unsafe { core::mem::transmute::<_, HashMap, Cow<[u8]>>>(this) }) + } +} + +/// Returns the value associated with `key`, returning a gravestone value if: +/// - `this` or `key` is in gravestone state. +/// - `this` has no value associated to `key` +#[no_mangle] +pub extern "C" fn z_bytes_map_get(this: &z_owned_bytes_map_t, key: z_bytes_t) -> z_bytes_t { + let (Some(this), Some(key)) = (this.as_ref(), key.as_slice()) else { + return z_bytes_null(); + }; + if let Some(value) = unsafe { &*this.get() }.get(key) { + value.as_ref().into() + } else { + z_bytes_null() + } +} + +/// Associates `value` to `key` in the map, copying them to obtain ownership: `key` and `value` are not aliased past the function's return. +/// +/// Calling this with `NULL` or the gravestone value is undefined behaviour. +#[no_mangle] +pub extern "C" fn z_bytes_map_insert_by_copy( + this: &z_owned_bytes_map_t, + key: z_bytes_t, + value: z_bytes_t, +) { + if let (Some(this), Some(key), Some(value)) = (this.as_ref(), key.as_slice(), value.as_slice()) + { + unsafe { &mut *this.get() } + .insert(Cow::Owned(key.to_owned()), Cow::Owned(value.to_owned())); + } +} + +/// Associates `value` to `key` in the map, aliasing them. +/// +/// Note that once `key` is aliased, reinserting at the same key may alias the previous instance, or the new instance of `key`. +/// +/// Calling this with `NULL` or the gravestone value is undefined behaviour. +#[no_mangle] +pub extern "C" fn z_bytes_map_insert_by_alias( + this: &z_owned_bytes_map_t, + key: z_bytes_t, + value: z_bytes_t, +) { + if let (Some(this), Some(key), Some(value)) = (this.as_ref(), key.as_slice(), value.as_slice()) + { + unsafe { + (*this.get()).insert( + Cow::Borrowed(core::mem::transmute(key)), + Cow::Borrowed(core::mem::transmute(value)), + ) + }; + } +} + +/// Returns the number of key-value pairs in the map. +/// +/// Calling this with `NULL` or the gravestone value is undefined behaviour. +#[no_mangle] +extern "C" fn z_bytes_map_len(this: &z_owned_bytes_map_t) -> usize { + this.as_ref() + .map_or(0, |this| unsafe { &*this.get() }.len()) +} + +/// Iterates over the key-value pairs in the map. +/// +/// `body` will be called once per pair, with `ctx` as its last argument. +/// If `body` returns a non-zero value, the iteration will stop immediately and the value will be returned. +/// Otherwise, this will return 0 once all pairs have been visited. +/// `body` is not given ownership of the key nor value, which alias the pairs in the map. +/// It is safe to keep these aliases until existing keys are modified/removed, or the map is destroyed. +/// Note that this map is unordered. +/// +/// Calling this with `NULL` or the gravestone value is undefined behaviour. +#[no_mangle] +pub extern "C" fn z_bytes_map_iter( + this: &z_owned_bytes_map_t, + body: z_attachment_iter_body_t, + ctx: *mut c_void, +) -> i8 { + if let Some(this) = this.as_ref() { + for (key, value) in unsafe { &*this.get() }.iter() { + let result = body(key.as_ref().into(), value.as_ref().into(), ctx); + if result != 0 { + return result; + } + } + } + 0 +} + +const Z_BYTES_MAP_VTABLE: z_attachment_vtable_t = z_attachment_vtable_t { + len: unsafe { core::mem::transmute(z_bytes_map_len as extern "C" fn(_) -> usize) }, + iteration_driver: unsafe { + core::mem::transmute(z_bytes_map_iter as extern "C" fn(_, _, _) -> i8) + }, +}; + +/// Aliases `this` into a generic `z_attachment_t`, allowing it to be passed to corresponding APIs. +#[no_mangle] +pub extern "C" fn z_bytes_map_as_attachment(this: &z_owned_bytes_map_t) -> z_attachment_t { + if z_bytes_map_check(this) { + z_attachment_t { + data: this as *const z_owned_bytes_map_t as *mut _, + vtable: Some(&Z_BYTES_MAP_VTABLE), + } + } else { + z_attachment_t { + data: core::ptr::null_mut(), + vtable: None, + } + } +} + +extern "C" fn bytes_map_from_attachment_iterator( + key: z_bytes_t, + value: z_bytes_t, + ctx: *mut c_void, +) -> i8 { + let map = unsafe { &*ctx.cast::() }; + z_bytes_map_insert_by_copy(map, key, value); + 0 +} +extern "C" fn bytes_map_from_attachment_iterator_by_alias( + key: z_bytes_t, + value: z_bytes_t, + ctx: *mut c_void, +) -> i8 { + let map = unsafe { &*ctx.cast::() }; + z_bytes_map_insert_by_alias(map, key, value); + 0 +} + +/// Constructs a map from the provided attachment, copying keys and values. +/// +/// If `this` is at gravestone value, the returned value will also be at gravestone value. +#[no_mangle] +pub extern "C" fn z_bytes_map_from_attachment(this: z_attachment_t) -> z_owned_bytes_map_t { + if z_attachment_check(&this) { + let mut map = z_bytes_map_new(); + z_attachment_iterate( + this, + bytes_map_from_attachment_iterator, + &mut map as *mut _ as *mut _, + ); + map + } else { + z_bytes_map_null() + } +} + +/// Constructs a map from the provided attachment, aliasing the attachment's keys and values. +/// +/// If `this` is at gravestone value, the returned value will also be at gravestone value. +#[no_mangle] +pub extern "C" fn z_bytes_map_from_attachment_aliasing( + this: z_attachment_t, +) -> z_owned_bytes_map_t { + if z_attachment_check(&this) { + let mut map = z_bytes_map_new(); + z_attachment_iterate( + this, + bytes_map_from_attachment_iterator_by_alias, + &mut map as *mut _ as *mut _, + ); + map + } else { + z_bytes_map_null() + } +} diff --git a/src/collections.rs b/src/collections.rs index 76c02d403..77224c2c8 100644 --- a/src/collections.rs +++ b/src/collections.rs @@ -11,10 +11,13 @@ // Contributors: // ZettaScale Zenoh team, // -use libc::size_t; +use libc::{c_char, size_t}; use zenoh::prelude::ZenohId; -/// An array of bytes. +/// A contiguous view of bytes owned by some other entity. +/// +/// `start` being `null` is considered a gravestone value, +/// and empty slices are represented using a possibly dangling pointer for `start`. #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct z_bytes_t { @@ -23,6 +26,12 @@ pub struct z_bytes_t { } impl z_bytes_t { + pub fn as_slice(&self) -> Option<&[u8]> { + if self.start.is_null() { + return None; + } + Some(unsafe { core::slice::from_raw_parts(self.start, self.len) }) + } pub fn empty() -> Self { z_bytes_t { start: std::ptr::null(), @@ -43,6 +52,32 @@ pub extern "C" fn z_bytes_check(b: &z_bytes_t) -> bool { !b.start.is_null() } +/// Returns the gravestone value for `z_bytes_t` +#[no_mangle] +pub extern "C" fn z_bytes_null() -> z_bytes_t { + z_bytes_t { + len: 0, + start: core::ptr::null(), + } +} + +/// Returns a view of `str` using `strlen`. +/// +/// `str == NULL` will cause this to return `z_bytes_null()` +#[no_mangle] +#[allow(clippy::missing_safety_doc)] +pub unsafe extern "C" fn z_bytes_new(str: *const c_char) -> z_bytes_t { + if str.is_null() { + z_bytes_null() + } else { + let len = unsafe { libc::strlen(str) }; + z_bytes_t { + len, + start: str.cast(), + } + } +} + /// Frees `b` and invalidates it for double-drop safety. #[allow(clippy::missing_safety_doc)] pub(crate) unsafe fn z_bytes_drop(b: &mut z_bytes_t) { diff --git a/src/commons.rs b/src/commons.rs index c5fbf3950..7f5a37529 100644 --- a/src/commons.rs +++ b/src/commons.rs @@ -12,6 +12,8 @@ // ZettaScale Zenoh team, // +use crate::attachment::z_attachment_null; +use crate::attachment::z_attachment_t; use crate::collections::*; use crate::keyexpr::*; use crate::z_id_t; @@ -204,6 +206,7 @@ pub struct z_sample_t<'a> { pub _zc_buf: &'a c_void, pub kind: z_sample_kind_t, pub timestamp: z_timestamp_t, + pub attachment: z_attachment_t, } impl<'a> z_sample_t<'a> { @@ -218,6 +221,7 @@ impl<'a> z_sample_t<'a> { _zc_buf: unsafe { std::mem::transmute(owner) }, kind: sample.kind.into(), timestamp: sample.timestamp.as_ref().into(), + attachment: z_attachment_null(), } } } diff --git a/src/lib.rs b/src/lib.rs index 00aea2601..b508855e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,6 +48,7 @@ mod publication_cache; pub use publication_cache::*; mod querying_subscriber; pub use querying_subscriber::*; +pub mod attachment; #[cfg(feature = "shared-memory")] mod shm; diff --git a/src/put.rs b/src/put.rs index 307c9efb4..fed96f5b6 100644 --- a/src/put.rs +++ b/src/put.rs @@ -11,6 +11,8 @@ // Contributors: // ZettaScale Zenoh team, // +use crate::attachment::z_attachment_null; +use crate::attachment::z_attachment_t; use crate::commons::*; use crate::keyexpr::*; use crate::session::*; @@ -112,6 +114,7 @@ pub struct z_put_options_t { pub encoding: z_encoding_t, pub congestion_control: z_congestion_control_t, pub priority: z_priority_t, + pub attachment: z_attachment_t, } /// Constructs the default value for :c:type:`z_put_options_t`. @@ -122,6 +125,7 @@ pub extern "C" fn z_put_options_default() -> z_put_options_t { encoding: z_encoding_default(), congestion_control: CongestionControl::default().into(), priority: Priority::default().into(), + attachment: z_attachment_null(), } }