diff --git a/docs/api.rst b/docs/api.rst index 6694af4a5..eeff59e4f 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -106,6 +106,7 @@ Key expression .. autocstruct:: zenoh_commons.h::z_owned_keyexpr_t .. autocfunction:: zenoh_commons.h::z_keyexpr +.. autocfunction:: zenoh_commons.h::z_keyexpr_autocanonize .. autocfunction:: zenoh_commons.h::z_keyexpr_unchecked .. autocfunction:: zenoh_commons.h::z_keyexpr_to_string .. autocfunction:: zenoh_commons.h::z_keyexpr_as_bytes @@ -120,6 +121,7 @@ Key expression .. autocfunction:: zenoh_commons.h::z_keyexpr_intersects .. autocfunction:: zenoh_commons.h::z_keyexpr_new +.. autocfunction:: zenoh_commons.h::z_keyexpr_new_autocanonize .. autocfunction:: zenoh_commons.h::z_keyexpr_loan .. autocfunction:: zenoh_commons.h::z_keyexpr_check .. autocfunction:: zenoh_commons.h::z_keyexpr_drop diff --git a/include/zenoh_commons.h b/include/zenoh_commons.h index cafca6456..f19e2ba8f 100644 --- a/include/zenoh_commons.h +++ b/include/zenoh_commons.h @@ -1551,6 +1551,14 @@ ZENOHC_API struct z_keyexpr_t z_keyexpr(const char *name); * Currently exclusive to zenoh-c */ ZENOHC_API struct z_bytes_t z_keyexpr_as_bytes(struct z_keyexpr_t keyexpr); +/** + * Constructs a :c:type:`z_keyexpr_t` departing from a string. + * It is a loaned key expression that aliases `name`. + * The string is canonized in-place before being passed to keyexpr. + * May SEGFAULT if `start` is NULL or lies in read-only memory (as values initialized with string litterals do). + */ +ZENOHC_API +struct z_keyexpr_t z_keyexpr_autocanonize(char *name); /** * Canonizes the passed string in place, possibly shortening it by modifying `len`. * @@ -1639,6 +1647,11 @@ ZENOHC_API struct z_keyexpr_t z_keyexpr_loan(const struct z_owned_keyexpr_t *key * Constructs a :c:type:`z_keyexpr_t` departing from a string, copying the passed string. */ ZENOHC_API struct z_owned_keyexpr_t z_keyexpr_new(const char *name); +/** + * Constructs a :c:type:`z_keyexpr_t` departing from a string, copying the passed string. The copied string is canonized. + */ +ZENOHC_API +struct z_owned_keyexpr_t z_keyexpr_new_autocanonize(const char *name); /** * Constructs a null safe-to-drop value of 'z_owned_keyexpr_t' type */ @@ -2174,6 +2187,15 @@ ZENOHC_API void zc_init_logger(void); * It is a loaned key expression that aliases `name`. */ ZENOHC_API struct z_keyexpr_t zc_keyexpr_from_slice(const char *name, size_t len); +/** + * Constructs a :c:type:`z_keyexpr_t` departing from a string. + * It is a loaned key expression that aliases `name`. + * The string is canonized in-place before being passed to keyexpr. + * May SEGFAULT if `start` is NULL or lies in read-only memory (as values initialized with string litterals do). + */ +ZENOHC_API +struct z_keyexpr_t zc_keyexpr_from_slice_autocanonize(char *name, + size_t *len); /** * Constructs a :c:type:`z_keyexpr_t` departing from a string without checking any of `z_keyexpr_t`'s assertions: * - `name` MUST be valid UTF8. diff --git a/src/keyexpr.rs b/src/keyexpr.rs index 04efe0c99..88fc7e4af 100644 --- a/src/keyexpr.rs +++ b/src/keyexpr.rs @@ -119,6 +119,32 @@ pub unsafe extern "C" fn z_keyexpr_new(name: *const c_char) -> z_owned_keyexpr_t } } +/// Constructs a :c:type:`z_keyexpr_t` departing from a string, copying the passed string. The copied string is canonized. +#[allow(clippy::missing_safety_doc)] +#[no_mangle] +pub unsafe extern "C" fn z_keyexpr_new_autocanonize(name: *const c_char) -> z_owned_keyexpr_t { + if name.is_null() { + return z_owned_keyexpr_t::null(); + } + let name = std::slice::from_raw_parts(name as _, libc::strlen(name)); + match std::str::from_utf8(name) { + Ok(name) => { + let name_owned = name.to_owned(); + match KeyExpr::autocanonize(name_owned) { + Ok(v) => v.into_owned().into(), + Err(e) => { + log::error!("Couldn't construct a keyexpr from {:02x?}: {}", name, e); + z_owned_keyexpr_t::null() + } + } + } + Err(e) => { + log::error!("{}", e); + z_owned_keyexpr_t::null() + } + } +} + /// Returns a :c:type:`z_keyexpr_t` loaned from :c:type:`z_owned_keyexpr_t`. #[no_mangle] pub extern "C" fn z_keyexpr_loan(keyexpr: &z_owned_keyexpr_t) -> z_keyexpr_t { @@ -309,6 +335,22 @@ pub unsafe extern "C" fn zc_keyexpr_from_slice(name: *const c_char, len: usize) } } +/// Constructs a :c:type:`z_keyexpr_t` departing from a string. +/// It is a loaned key expression that aliases `name`. +/// The string is canonized in-place before being passed to keyexpr. +/// May SEGFAULT if `start` is NULL or lies in read-only memory (as values initialized with string litterals do). +#[allow(clippy::missing_safety_doc)] +#[no_mangle] +pub unsafe extern "C" fn zc_keyexpr_from_slice_autocanonize( + name: *mut c_char, + len: &mut usize, +) -> z_keyexpr_t { + if z_keyexpr_canonize(name, len) < 0 { + return z_keyexpr_t::null(); + } + zc_keyexpr_from_slice(name, *len) +} + /// Constructs a :c:type:`z_keyexpr_t` departing from a string. /// It is a loaned key expression that aliases `name`. #[allow(clippy::missing_safety_doc)] @@ -321,6 +363,20 @@ pub unsafe extern "C" fn z_keyexpr(name: *const c_char) -> z_keyexpr_t { } } +/// Constructs a :c:type:`z_keyexpr_t` departing from a string. +/// It is a loaned key expression that aliases `name`. +/// The string is canonized in-place before being passed to keyexpr. +/// May SEGFAULT if `start` is NULL or lies in read-only memory (as values initialized with string litterals do). +#[allow(clippy::missing_safety_doc)] +#[no_mangle] +pub unsafe extern "C" fn z_keyexpr_autocanonize(name: *mut c_char) -> z_keyexpr_t { + if name.is_null() || z_keyexpr_canonize_null_terminated(name) < 0 { + z_keyexpr_t::null() + } else { + z_keyexpr(name) + } +} + /// Constructs a :c:type:`z_keyexpr_t` departing from a string without checking any of `z_keyexpr_t`'s assertions: /// - `name` MUST be valid UTF8. /// - `name` MUST follow the Key Expression specification, ie: diff --git a/tests/z_api_keyexpr_test.c b/tests/z_api_keyexpr_test.c index b5cdeaad1..007c43b4b 100644 --- a/tests/z_api_keyexpr_test.c +++ b/tests/z_api_keyexpr_test.c @@ -41,6 +41,22 @@ void canonize() { printf("'%s', err = %d\n", keyexpr, err); assert(err == 0); assert(strcmp(keyexpr, "a/**/c") == 0); + + strcpy(keyexpr, "a/**/**/c"); + z_keyexpr_t key_expr_canonized = z_keyexpr_autocanonize(keyexpr); + assert(z_keyexpr_check(keyexpr) == true); + assert(strcmp(keyexpr, "a/**/c") == 0); + assert(z_keyexpr_as_bytes(key_expr_canonized).len == len_new); + assert(strncmp(z_keyexpr_as_bytes(key_expr_canonized).start, "a/**/c", len_new) == 0); + + strcpy(keyexpr, "a/**/**/c"); + len_new = len_old; + key_expr_canonized = zc_keyexpr_from_slice_autocanonize(keyexpr, &len_new); + assert(z_keyexpr_check(keyexpr) == true); + assert(len_new == len_old - 3); + assert(strncmp(keyexpr, "a/**/c", len_new) == 0); + assert(z_keyexpr_as_bytes(key_expr_canonized).len == len_new); + assert(strncmp(z_keyexpr_as_bytes(key_expr_canonized).start, "a/**/c", len_new) == 0); } void includes() {