Skip to content

Commit

Permalink
preliminary work on attachements API, some additional docs and protoc…
Browse files Browse the repository at this point in the history
…ol support are needed
  • Loading branch information
p-avital committed Nov 14, 2023
1 parent cf215fd commit 53322c6
Show file tree
Hide file tree
Showing 6 changed files with 327 additions and 0 deletions.
111 changes: 111 additions & 0 deletions include/zenoh_commons.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,59 @@ 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_attachement_iter_body_t)(struct z_bytes_t key,
struct z_bytes_t value,
void *context);
/**
* The driver of a loop over an attachement'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_attachement_iter_driver_t)(void *iterator,
z_attachement_iter_body_t loop_body,
void *context);
/**
* The v-table for an attachement.
*/
typedef struct z_attachement_vtable_t {
/**
* See `z_attachement_iteration_driver_t`'s documentation.
*/
z_attachement_iter_driver_t iteration_driver;
/**
* Returns the number of key-value pairs within the attachement.
*/
uintptr_t (*len)(const void*);
} z_attachement_vtable_t;
/**
* A v-table based map of vector of bool to vector of bool.
*
* `vtable == NULL` marks the gravestone value, as this type is often optional.
* Users are encouraged to use `z_attachement_null` and `z_attachement_check` to interact.
*/
typedef struct z_attachement_t {
void *data;
const struct z_attachement_vtable_t *vtable;
} z_attachement_t;
/**
* A map of 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];
uintptr_t _1[4];
} z_owned_bytes_map_t;
/**
* Represents a Zenoh ID.
*
Expand Down Expand Up @@ -331,6 +384,7 @@ typedef struct z_sample_t {
const void *_zc_buf;
enum z_sample_kind_t kind;
struct z_timestamp_t timestamp;
struct z_attachement_t attachements;
} z_sample_t;
/**
* A closure is a structure that contains all the elements for stateful, memory-leak-free callbacks.
Expand Down Expand Up @@ -620,6 +674,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_attachement_t attachements;
} z_put_options_t;
/**
* Represents the set of options that can be applied to a query reply,
Expand Down Expand Up @@ -731,10 +786,66 @@ 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_attachement_t`.
*/
ZENOHC_API bool z_attachement_check(const struct z_attachement_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_attachement_iterate(struct z_attachement_t this_,
z_attachement_iter_body_t body,
void *context);
/**
* Returns the number of key-value pairs in `this`.
*/
ZENOHC_API uintptr_t z_attachement_len(struct z_attachement_t this_);
/**
* Returns the gravestone value for `z_attachement_t`.
*/
ZENOHC_API struct z_attachement_t z_attachement_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_attachement_t`, allowing it to be passed to corresponding APIs.
*/
ZENOHC_API
struct z_attachement_t z_bytes_map_as_attachement(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_);
/**
* 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);
/**
* 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);
/**
* Closes a zenoh session. This drops and invalidates `session` for double-drop safety.
*
Expand Down
201 changes: 201 additions & 0 deletions src/attachements.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
use std::collections::HashMap;

use libc::c_void;

use crate::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_attachement_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 attachement'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_attachement_iter_driver_t = extern "C" fn(
iterator: *mut c_void,
loop_body: z_attachement_iter_body_t,
context: *mut c_void,
) -> i8;

/// The v-table for an attachement.
#[repr(C)]
pub struct z_attachement_vtable_t {
/// See `z_attachement_iteration_driver_t`'s documentation.
iteration_driver: z_attachement_iter_driver_t,
/// Returns the number of key-value pairs within the attachement.
len: extern "C" fn(*const c_void) -> usize,
}

/// A v-table based map of vector of bool to vector of bool.
///
/// `vtable == NULL` marks the gravestone value, as this type is often optional.
/// Users are encouraged to use `z_attachement_null` and `z_attachement_check` to interact.
#[repr(C)]
pub struct z_attachement_t {
data: *mut c_void,
vtable: Option<&'static z_attachement_vtable_t>,
}

/// Returns the gravestone value for `z_attachement_t`.
#[no_mangle]
pub extern "C" fn z_attachement_check(this: &z_attachement_t) -> bool {
this.vtable.is_some()
}

/// Returns the gravestone value for `z_attachement_t`.
#[no_mangle]
pub extern "C" fn z_attachement_null() -> z_attachement_t {
z_attachement_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_attachement_iterate(
this: z_attachement_t,
body: z_attachement_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_attachement_len(this: z_attachement_t) -> usize {
(this.vtable.unwrap().len)(this.data)
}

/// A map of 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 = core::cell::UnsafeCell<HashMap<Vec<u8>, Vec<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(HashMap::<Vec<u8>, Vec<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 {
z_owned_bytes_map_t {
_0: [0; 2],
_1: [0; 4],
}
}

/// 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._0 != [0; 2] && this._1 != [0; 4]
}
/// 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<Vec<u8>, Vec<u8>>>(this) })
}
}

/// 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,
) {
match (z_bytes_map_check(this), key.as_slice(), value.as_slice()) {
(true, Some(key), Some(value)) => {
unsafe { &mut *this.get() }.insert(key.to_owned(), value.to_owned());
}
_ => {
todo!()
}
}
}

/// 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 {
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]
extern "C" fn z_bytes_map_iter(
this: &z_owned_bytes_map_t,
body: z_attachement_iter_body_t,
ctx: *mut c_void,
) -> i8 {
for (key, value) in unsafe { &*this.get() }.iter() {
let result = body(key.as_slice().into(), value.as_slice().into(), ctx);
if result != 0 {
return result;
}
}
0
}

const Z_BYTES_MAP_VTABLE: z_attachement_vtable_t = z_attachement_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_attachement_t`, allowing it to be passed to corresponding APIs.
#[no_mangle]
pub extern "C" fn z_bytes_map_as_attachement(this: &z_owned_bytes_map_t) -> z_attachement_t {
if z_bytes_map_check(this) {
z_attachement_t {
data: this as *const z_owned_bytes_map_t as *mut _,
vtable: Some(&Z_BYTES_MAP_VTABLE),
}
} else {
z_attachement_t {
data: core::ptr::null_mut(),
vtable: None,
}
}
}
6 changes: 6 additions & 0 deletions src/collections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,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(),
Expand Down
4 changes: 4 additions & 0 deletions src/commons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
// ZettaScale Zenoh team, <[email protected]>
//

use crate::attachements::z_attachement_null;
use crate::attachements::z_attachement_t;
use crate::collections::*;
use crate::keyexpr::*;
use crate::z_id_t;
Expand Down Expand Up @@ -202,6 +204,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 attachements: z_attachement_t,
}

impl<'a> z_sample_t<'a> {
Expand All @@ -216,6 +219,7 @@ impl<'a> z_sample_t<'a> {
_zc_buf: unsafe { std::mem::transmute(owner) },
kind: sample.kind.into(),
timestamp: sample.timestamp.as_ref().into(),
attachements: z_attachement_null(),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ mod closures;
pub use closures::*;
mod liveliness;
pub use liveliness::*;
pub mod attachements;
#[cfg(feature = "shared-memory")]
mod shm;

Expand Down
Loading

0 comments on commit 53322c6

Please sign in to comment.