diff --git a/docs/api.rst b/docs/api.rst index 6694af4a5..ffd5b0f69 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -38,6 +38,8 @@ Bytes map .. autocfunction:: zenoh_commons.h::z_bytes_map_null .. autocfunction:: zenoh_commons.h::z_bytes_map_drop .. autocfunction:: zenoh_commons.h::z_bytes_map_get +.. autocfunction:: zenoh_commons.h::z_bytes_map_len +.. autocfunction:: zenoh_commons.h::z_bytes_map_is_empty .. autocfunction:: zenoh_commons.h::z_bytes_map_insert_by_alias .. autocfunction:: zenoh_commons.h::z_bytes_map_insert_by_copy .. autocfunction:: zenoh_commons.h::z_bytes_map_iter @@ -157,6 +159,8 @@ Attachment .. autocfunction:: zenoh_commons.h::z_attachment_null .. autocfunction:: zenoh_commons.h::z_attachment_get +.. autocfunction:: zenoh_commons.h::z_attachment_len +.. autocfunction:: zenoh_commons.h::z_attachment_is_empty .. autocfunction:: zenoh_commons.h::z_attachment_check .. autocfunction:: zenoh_commons.h::z_attachment_iterate diff --git a/include/zenoh_commons.h b/include/zenoh_commons.h index cafca6456..ba363f02e 100644 --- a/include/zenoh_commons.h +++ b/include/zenoh_commons.h @@ -1026,6 +1026,10 @@ ZENOHC_API bool z_attachment_check(const struct z_attachment_t *this_); * Returns the value associated with the key. */ ZENOHC_API struct z_bytes_t z_attachment_get(struct z_attachment_t this_, struct z_bytes_t key); +/** + * Returns true if `z_attachment_t` contains no key-value pairs, false otherwise. + */ +ZENOHC_API bool z_attachment_is_empty(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. @@ -1038,6 +1042,12 @@ ZENOHC_API int8_t z_attachment_iterate(struct z_attachment_t this_, z_attachment_iter_body_t body, void *context); +/** + * Returns number of key-value pairs for `z_attachment_t`. + * + * Does so by iterating over all existing key-value pairs. + */ +ZENOHC_API size_t z_attachment_len(struct z_attachment_t this_); /** * Returns the gravestone value for `z_attachment_t`. */ @@ -1107,6 +1117,10 @@ 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); +/** + * Returns true if the map is empty, false otherwise. + */ +ZENOHC_API bool z_bytes_map_is_empty(struct z_owned_bytes_map_t *this_); /** * Iterates over the key-value pairs in the map. * @@ -1123,6 +1137,10 @@ ZENOHC_API int8_t z_bytes_map_iter(const struct z_owned_bytes_map_t *this_, z_attachment_iter_body_t body, void *ctx); +/** + * Returns number of key-value pairs in the map. + */ +ZENOHC_API size_t z_bytes_map_len(struct z_owned_bytes_map_t *this_); /** * Constructs a new map. */ diff --git a/src/attachment.rs b/src/attachment.rs index 5dbbfec5c..e776fa238 100644 --- a/src/attachment.rs +++ b/src/attachment.rs @@ -115,6 +115,58 @@ pub extern "C" fn z_attachment_get(this: z_attachment_t, key: z_bytes_t) -> z_by } } +fn _z_attachment_len(this: z_attachment_t, check_if_non_empty: bool) -> usize { + match this.iteration_driver.as_ref() { + None => 0, + Some(iteration_driver) => { + struct count_context_t { + count: usize, + stop_if_not_empty: bool, + } + + extern "C" fn attachment_count_iterator( + _key: z_bytes_t, + _value: z_bytes_t, + context: *mut c_void, + ) -> i8 { + unsafe { + let context = &mut *(context as *mut count_context_t); + context.count += 1; + if context.stop_if_not_empty { + 1 + } else { + 0 + } + } + } + let mut count_context = count_context_t { + count: 0, + stop_if_not_empty: check_if_non_empty, + }; + (iteration_driver)( + this.data, + attachment_count_iterator, + &mut count_context as *mut _ as *mut c_void, + ); + count_context.count + } + } +} + +/// Returns number of key-value pairs for `z_attachment_t`. +/// +/// Does so by iterating over all existing key-value pairs. +#[no_mangle] +pub extern "C" fn z_attachment_len(this: z_attachment_t) -> usize { + _z_attachment_len(this, false) +} + +/// Returns true if `z_attachment_t` contains no key-value pairs, false otherwise. +#[no_mangle] +pub extern "C" fn z_attachment_is_empty(this: z_attachment_t) -> bool { + _z_attachment_len(this, true) == 0 +} + /// 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 @@ -162,6 +214,20 @@ pub extern "C" fn z_bytes_map_drop(this: &mut z_owned_bytes_map_t) { this.take(); } +/// Returns number of key-value pairs in the map. +#[no_mangle] +pub extern "C" fn z_bytes_map_len(this: &mut z_owned_bytes_map_t) -> usize { + let this = unsafe { &*this.get() }; + this.as_ref().map(|m| m.len()).unwrap_or(0) +} + +/// Returns true if the map is empty, false otherwise. +#[no_mangle] +pub extern "C" fn z_bytes_map_is_empty(this: &mut z_owned_bytes_map_t) -> bool { + let this = unsafe { &*this.get() }; + this.as_ref().map(|m| m.is_empty()).unwrap_or(true) +} + /// 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` diff --git a/tests/z_api_attachment_test.c b/tests/z_api_attachment_test.c index c11abcbfe..737048e9b 100644 --- a/tests/z_api_attachment_test.c +++ b/tests/z_api_attachment_test.c @@ -29,6 +29,11 @@ void writting_through_map_by_alias_read_by_get() { z_attachment_t attachment = z_bytes_map_as_attachment(&map); // Elements check + + assert(z_bytes_map_len(&map) == 2); + assert(z_attachment_len(attachment) == 2); + assert(!z_attachment_is_empty(attachment)); + z_bytes_t a1 = z_attachment_get(attachment, z_bytes_from_str("k1")); ASSERT_STR_BYTES_EQUAL("v1", a1); @@ -61,6 +66,10 @@ void writting_through_map_by_copy_read_by_iter() { z_attachment_t attachment = z_bytes_map_as_attachment(&map); // Elements check + assert(z_bytes_map_len(&map) == 2); + assert(z_attachment_len(attachment) == 2); + assert(!z_attachment_is_empty(attachment)); + int res = z_attachment_iterate(attachment, _attachment_reader, (void*)42); assert(res == 24); @@ -81,6 +90,9 @@ void writting_no_map_read_by_get() { z_attachment_t attachment = {.data = NULL, .iteration_driver = &_iteration_driver}; // Elements check + assert(z_attachment_len(attachment) == 2); + assert(!z_attachment_is_empty(attachment)); + z_bytes_t a1 = z_attachment_get(attachment, z_bytes_from_str("k1")); ASSERT_STR_BYTES_EQUAL("v1", a1); @@ -94,6 +106,8 @@ void writting_no_map_read_by_get() { void invalid_attachment_safety() { z_attachment_t attachment = z_attachment_null(); + assert(z_attachment_is_empty(attachment)); + assert(z_attachment_len(attachment) == 0); z_bytes_t a_non = z_attachment_get(attachment, z_bytes_from_str("k_non")); assert(a_non.start == NULL);