From 6b710b0ec0add8fbc0ed97ec75b44854aa50a6fb Mon Sep 17 00:00:00 2001 From: Pierre Avital Date: Wed, 13 Dec 2023 15:53:10 +0100 Subject: [PATCH] attachment API implemented, todo: integration with options and codecs --- include/zenoh-pico/api/primitives.h | 6 +- include/zenoh-pico/api/types.h | 108 ++++++++++++++++++++++++++ include/zenoh-pico/protocol/core.h | 46 +++++++++++ src/api/api.c | 114 +++++++++++++++++++++++++++- src/protocol/core.c | 22 ++++++ zenohpico.pc | 2 +- 6 files changed, 293 insertions(+), 5 deletions(-) diff --git a/include/zenoh-pico/api/primitives.h b/include/zenoh-pico/api/primitives.h index c96937518..893188c2b 100644 --- a/include/zenoh-pico/api/primitives.h +++ b/include/zenoh-pico/api/primitives.h @@ -12,8 +12,8 @@ // ZettaScale Zenoh Team, // -#ifndef ZENOH_PICO_API_PRIMITIVES_H -#define ZENOH_PICO_API_PRIMITIVES_H +#ifndef INCLUDE_ZENOH_PICO_API_PRIMITIVES_H +#define INCLUDE_ZENOH_PICO_API_PRIMITIVES_H #include #include @@ -1400,4 +1400,4 @@ int8_t zp_send_join(z_session_t zs, const zp_send_join_options_t *options); } #endif -#endif /* ZENOH_PICO_API_PRIMITIVES_H */ +#endif /* INCLUDE_ZENOH_PICO_API_PRIMITIVES_H */ diff --git a/include/zenoh-pico/api/types.h b/include/zenoh-pico/api/types.h index 96bd2158d..9b51f29e2 100644 --- a/include/zenoh-pico/api/types.h +++ b/include/zenoh-pico/api/types.h @@ -15,6 +15,9 @@ #ifndef INCLUDE_ZENOH_PICO_API_TYPES_H #define INCLUDE_ZENOH_PICO_API_TYPES_H +#include "zenoh-pico/collections/bytes.h" +#include "zenoh-pico/collections/element.h" +#include "zenoh-pico/collections/list.h" #include "zenoh-pico/net/publish.h" #include "zenoh-pico/net/query.h" #include "zenoh-pico/net/session.h" @@ -289,6 +292,7 @@ typedef struct { z_encoding_t encoding; z_congestion_control_t congestion_control; z_priority_t priority; + z_attachment_t attachment; } z_put_options_t; /** @@ -313,6 +317,7 @@ typedef struct { */ typedef struct { z_encoding_t encoding; + z_attachment_t attachment; } z_publisher_put_options_t; /** @@ -336,6 +341,7 @@ typedef struct { z_value_t value; z_query_consolidation_t consolidation; z_query_target_t target; + z_attachment_t attachment; } z_get_options_t; /** @@ -546,6 +552,108 @@ typedef struct { void z_closure_zid_call(const z_owned_closure_zid_t *closure, const z_id_t *id); +struct _z_bytes_pair_t { + _z_bytes_t key; + _z_bytes_t value; +}; + +void _z_bytes_pair_clear(struct _z_bytes_pair_t *this); + +_Z_ELEM_DEFINE(_z_bytes_pair, struct _z_bytes_pair_t, _z_noop_size, _z_bytes_pair_clear, _z_noop_copy); +_Z_LIST_DEFINE(_z_bytes_pair, struct _z_bytes_pair_t); + +/** + * A map of maybe-owned vector of bytes to maybe-owned vector of bytes. + */ +typedef struct z_owned_bytes_map_t { + _z_bytes_pair_list_t *_inner; +} z_owned_bytes_map_t; + +/** + * Aliases `this` into a generic `z_attachment_t`, allowing it to be passed to corresponding APIs. + */ +z_attachment_t z_bytes_map_as_attachment(const z_owned_bytes_map_t *this_); +/** + * Returns `true` if the map is not in its gravestone state + */ +bool z_bytes_map_check(const 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. + */ +void z_bytes_map_drop(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. + */ +z_owned_bytes_map_t z_bytes_map_from_attachment(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. + */ + +z_owned_bytes_map_t z_bytes_map_from_attachment_aliasing(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` + */ + +z_bytes_t z_bytes_map_get(const z_owned_bytes_map_t *this_, 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. + */ + +void z_bytes_map_insert_by_alias(const z_owned_bytes_map_t *this_, z_bytes_t key, 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. + */ + +void z_bytes_map_insert_by_copy(const z_owned_bytes_map_t *this_, z_bytes_t key, 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. + */ + +int8_t z_bytes_map_iter(const z_owned_bytes_map_t *this_, z_attachment_iter_body_t body, void *ctx); +/** + * Constructs a new map. + */ +z_owned_bytes_map_t z_bytes_map_new(void); +/** + * Constructs the gravestone value for `z_owned_bytes_map_t` + */ +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()` + */ +z_bytes_t z_bytes_new(const char *str); +/** + * Returns the gravestone value for `z_bytes_t` + */ +z_bytes_t z_bytes_null(void); + #ifdef __cplusplus } #endif diff --git a/include/zenoh-pico/protocol/core.h b/include/zenoh-pico/protocol/core.h index 48fb59ac8..9ba46b566 100644 --- a/include/zenoh-pico/protocol/core.h +++ b/include/zenoh-pico/protocol/core.h @@ -16,6 +16,7 @@ #define INCLUDE_ZENOH_PICO_PROTOCOL_CORE_H #include +#include #include #include @@ -71,6 +72,50 @@ typedef struct { uint64_t time; } _z_timestamp_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)(_z_bytes_t key, _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)(const 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; +} 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 { + const void *data; + z_attachment_iter_driver_t iteration_driver; +} z_attachment_t; + +inline z_attachment_t z_attachment_null() { return (z_attachment_t){.data = NULL, .iteration_driver = NULL}; } +inline _Bool z_attachment_check(const z_attachment_t *attachment) { return attachment->iteration_driver != NULL; } +inline int8_t z_attachment_iterate(z_attachment_t this, z_attachment_iter_body_t body, void *ctx) { + return this.iteration_driver(this.data, body, ctx); +} +_z_bytes_t z_attachment_get(z_attachment_t this, _z_bytes_t key); + _z_timestamp_t _z_timestamp_duplicate(const _z_timestamp_t *tstamp); _z_timestamp_t _z_timestamp_null(void); void _z_timestamp_clear(_z_timestamp_t *tstamp); @@ -168,6 +213,7 @@ typedef struct { _z_timestamp_t timestamp; _z_encoding_t encoding; z_sample_kind_t kind; + z_attachment_t attachment; } _z_sample_t; /** diff --git a/src/api/api.c b/src/api/api.c index 9455a6a2e..ffb73e271 100644 --- a/src/api/api.c +++ b/src/api/api.c @@ -1090,4 +1090,116 @@ zp_send_join_options_t zp_send_join_options_default(void) { return (zp_send_join int8_t zp_send_join(z_session_t zs, const zp_send_join_options_t *options) { (void)(options); return _zp_send_join(zs._val); -} \ No newline at end of file +} + +void _z_bytes_pair_clear(struct _z_bytes_pair_t *this) { + _z_bytes_clear(&this->key); + _z_bytes_clear(&this->value); +} + +z_attachment_t z_bytes_map_as_attachment(const z_owned_bytes_map_t *this_) { + if (!z_bytes_map_check(this_)) { + return z_attachment_null(); + } + return (z_attachment_t){.data = this_, .iteration_driver = (z_attachment_iter_driver_t)z_bytes_map_iter}; +} +bool z_bytes_map_check(const z_owned_bytes_map_t *this_) { return this_->_inner != NULL; } +void z_bytes_map_drop(z_owned_bytes_map_t *this_) { _z_bytes_pair_list_free(&this_->_inner); } + +int8_t _z_bytes_map_insert_by_alias(z_bytes_t key, z_bytes_t value, void *this_) { + z_bytes_map_insert_by_alias((z_owned_bytes_map_t *)this_, key, value); + return 0; +} +int8_t _z_bytes_map_insert_by_copy(z_bytes_t key, z_bytes_t value, void *this_) { + z_bytes_map_insert_by_copy((z_owned_bytes_map_t *)this_, key, value); + return 0; +} +z_owned_bytes_map_t z_bytes_map_from_attachment(z_attachment_t this_) { + if (!z_attachment_check(&this_)) { + return z_bytes_map_null(); + } + z_owned_bytes_map_t map = z_bytes_map_new(); + z_attachment_iterate(this_, _z_bytes_map_insert_by_copy, &map); + return map; +} +z_owned_bytes_map_t z_bytes_map_from_attachment_aliasing(z_attachment_t this_) { + if (!z_attachment_check(&this_)) { + return z_bytes_map_null(); + } + z_owned_bytes_map_t map = z_bytes_map_new(); + z_attachment_iterate(this_, _z_bytes_map_insert_by_alias, &map); + return map; +} +z_bytes_t z_bytes_map_get(const z_owned_bytes_map_t *this_, z_bytes_t key) { + _z_bytes_pair_list_t *current = this_->_inner; + while (current) { + struct _z_bytes_pair_t *head = _z_bytes_pair_list_head(current); + if (_z_bytes_eq(&key, &head->key)) { + return _z_bytes_wrap(head->value.start, head->value.len); + } + } + return z_bytes_null(); +} +void z_bytes_map_insert_by_alias(const z_owned_bytes_map_t *this_, z_bytes_t key, z_bytes_t value) { + _z_bytes_pair_list_t *current = this_->_inner; + while (current) { + struct _z_bytes_pair_t *head = _z_bytes_pair_list_head(current); + if (_z_bytes_eq(&key, &head->key)) { + break; + } + current = _z_bytes_pair_list_tail(current); + } + if (current) { + struct _z_bytes_pair_t *head = _z_bytes_pair_list_head(current); + _z_bytes_clear(&head->value); + head->value = _z_bytes_wrap(value.start, value.len); + } else { + struct _z_bytes_pair_t *insert = z_malloc(sizeof(struct _z_bytes_pair_t)); + memset(insert, 0, sizeof(struct _z_bytes_pair_t)); + insert->key = _z_bytes_wrap(key.start, key.len); + insert->value = _z_bytes_wrap(value.start, value.len); + _z_bytes_pair_list_push(this_->_inner, insert); + } +} +void z_bytes_map_insert_by_copy(const z_owned_bytes_map_t *this_, z_bytes_t key, z_bytes_t value) { + _z_bytes_pair_list_t *current = this_->_inner; + while (current) { + struct _z_bytes_pair_t *head = _z_bytes_pair_list_head(current); + if (_z_bytes_eq(&key, &head->key)) { + break; + } + current = _z_bytes_pair_list_tail(current); + } + if (current) { + struct _z_bytes_pair_t *head = _z_bytes_pair_list_head(current); + _z_bytes_clear(&head->value); + _z_bytes_copy(&head->value, &value); + if (!head->key._is_alloc) { + _z_bytes_copy(&head->key, &key); + } + } else { + struct _z_bytes_pair_t *insert = z_malloc(sizeof(struct _z_bytes_pair_t)); + memset(insert, 0, sizeof(struct _z_bytes_pair_t)); + _z_bytes_copy(&insert->key, &key); + _z_bytes_copy(&insert->value, &value); + _z_bytes_pair_list_push(this_->_inner, insert); + } +} +int8_t z_bytes_map_iter(const z_owned_bytes_map_t *this_, z_attachment_iter_body_t body, void *ctx) { + _z_bytes_pair_list_t *current = this_->_inner; + while (current) { + struct _z_bytes_pair_t *head = _z_bytes_pair_list_head(current); + int8_t ret = body(head->key, head->value, ctx); + if (ret) { + return ret; + } + current = _z_bytes_pair_list_tail(current); + } + return 0; +} +z_owned_bytes_map_t z_bytes_map_new(void) { return (z_owned_bytes_map_t){._inner = _z_bytes_pair_list_new()}; } +z_owned_bytes_map_t z_bytes_map_null(void) { return (z_owned_bytes_map_t){._inner = NULL}; } +z_bytes_t z_bytes_new(const char *str) { + return (z_bytes_t){.len = strlen(str), ._is_alloc = false, .start = (unsigned char *)str}; +} +z_bytes_t z_bytes_null(void) { return (z_bytes_t){.len = 0, ._is_alloc = false, .start = NULL}; } \ No newline at end of file diff --git a/src/protocol/core.c b/src/protocol/core.c index b937f1e89..b5d8405f8 100644 --- a/src/protocol/core.c +++ b/src/protocol/core.c @@ -14,7 +14,11 @@ #include "zenoh-pico/protocol/core.h" +#include +#include + #include "zenoh-pico/api/primitives.h" +#include "zenoh-pico/collections/bytes.h" uint8_t _z_id_len(_z_id_t id) { uint8_t len = 16; @@ -64,3 +68,21 @@ _z_value_t _z_value_steal(_z_value_t *value) { *value = _z_value_null(); return ret; } +struct _z_seeker_t { + _z_bytes_t key; + _z_bytes_t value; +}; +int8_t _z_attachment_get_seeker(_z_bytes_t key, _z_bytes_t value, void *ctx) { + struct _z_seeker_t *seeker = (struct _z_seeker_t *)ctx; + _z_bytes_t seeked = seeker->key; + if (key.len == seeked.len && memcmp(key.start, seeked.start, seeked.len)) { + seeker->value = (_z_bytes_t){.start = value.start, .len = value.len, ._is_alloc = false}; + return 1; + } + return 0; +} +_z_bytes_t z_attachment_get(z_attachment_t this, _z_bytes_t key) { + struct _z_seeker_t seeker = {.value = {0}, .key = key}; + z_attachment_iterate(this, _z_attachment_get_seeker, (void *)&seeker); + return seeker.value; +} diff --git a/zenohpico.pc b/zenohpico.pc index e08e8cce2..ec329193d 100644 --- a/zenohpico.pc +++ b/zenohpico.pc @@ -3,6 +3,6 @@ prefix=/usr/local Name: zenohpico Description: URL: -Version: 0.11.20231031dev +Version: 0.11.20231213dev Cflags: -I${prefix}/include Libs: -L${prefix}/lib -lzenohpico