From cfa4a00a940ae3224e286d1393cf035dc4fe3d81 Mon Sep 17 00:00:00 2001 From: Pierre Avital Date: Mon, 22 Jan 2024 15:58:12 +0100 Subject: [PATCH 1/4] handle verbatims in intersections --- include/zenoh-pico/utils/string.h | 2 + src/protocol/keyexpr.c | 319 ++++++++++++++++-------------- src/utils/string.c | 5 + tests/z_keyexpr_test.c | 29 +++ zenohpico.pc | 2 +- 5 files changed, 208 insertions(+), 149 deletions(-) diff --git a/include/zenoh-pico/utils/string.h b/include/zenoh-pico/utils/string.h index 43cd2d63f..b4680ee1c 100644 --- a/include/zenoh-pico/utils/string.h +++ b/include/zenoh-pico/utils/string.h @@ -47,7 +47,9 @@ char const *_z_strstr(char const *haystack_start, char const *haystack_end, cons char const *_z_strstr_skipneedle(char const *haystack_start, char const *haystack_end, const char *needle_start); char const *_z_bstrstr_skipneedle(_z_str_se_t haystack, _z_str_se_t needle); +_Bool _z_splitstr_is_empty(const _z_splitstr_t *src); _z_str_se_t _z_splitstr_next(_z_splitstr_t *str); +_z_str_se_t _z_splitstr_split_once(_z_splitstr_t src, _z_str_se_t *next); _z_str_se_t _z_splitstr_nextback(_z_splitstr_t *str); size_t _z_strcnt(char const *haystack_start, const char *harstack_end, const char *needle_start); diff --git a/src/protocol/keyexpr.c b/src/protocol/keyexpr.c index a33e6645a..27528bf98 100644 --- a/src/protocol/keyexpr.c +++ b/src/protocol/keyexpr.c @@ -246,7 +246,7 @@ void __zp_ke_write_chunk(char **writer, const char *chunk, size_t len, const cha typedef _Bool (*_z_ke_chunk_matcher)(_z_str_se_t l, _z_str_se_t r); enum _zp_wildness_t { _ZP_WILDNESS_ANY = 1, _ZP_WILDNESS_SUPERCHUNKS = 2, _ZP_WILDNESS_SUBCHUNK_DSL = 4 }; -int8_t _zp_ke_wildness(_z_str_se_t ke, size_t *n_segments) { +int8_t _zp_ke_wildness(_z_str_se_t ke, size_t *n_segments, size_t *n_verbatims) { const char *start = ke.start; const char *end = ke.end; int8_t result = 0; @@ -267,7 +267,9 @@ int8_t _zp_ke_wildness(_z_str_se_t ke, size_t *n_segments) { case '/': { *n_segments = *n_segments + (size_t)1; } break; - + case '@': { + *n_verbatims = *n_verbatims + (size_t)1; + } default: { // Do nothing } break; @@ -282,6 +284,7 @@ int8_t _zp_ke_wildness(_z_str_se_t ke, size_t *n_segments) { const char *_Z_DELIMITER = "/"; const char *_Z_DOUBLE_STAR = "**"; const char *_Z_DOLLAR_STAR = "$*"; +char _Z_VERBATIM = '@'; _Bool _z_ke_isdoublestar(_z_str_se_t s) { return ((_z_ptr_char_diff(s.end, s.start) == 2) && (s.start[0] == '*') && (s.start[1] == '*')); } @@ -337,10 +340,115 @@ _Bool _z_ke_chunk_includes_stardsl(_z_str_se_t l1, _z_str_se_t r) { return result; } +/*------------------ Zenoh-Core helpers ------------------*/ +_Bool _z_keyexpr_includes(const char *lstart, const size_t llen, const char *rstart, const size_t rlen) { + _Bool result = ((llen == rlen) && (strncmp(lstart, rstart, llen) == 0)); + if (result == false) { + _z_str_se_t l = {.start = lstart, .end = _z_cptr_char_offset(lstart, llen)}; + _z_str_se_t r = {.start = rstart, .end = _z_cptr_char_offset(rstart, rlen)}; + size_t ln_chunks = 0, ln_verbatim = 0; + size_t rn_chunks = 0, rn_verbatim = 0; + int8_t lwildness = _zp_ke_wildness(l, &ln_chunks, &ln_verbatim); + int8_t rwildness = _zp_ke_wildness(r, &rn_chunks, &rn_verbatim); + int8_t wildness = lwildness | rwildness; + _z_ke_chunk_matcher chunk_intersector = + ((wildness & (int8_t)_ZP_WILDNESS_SUBCHUNK_DSL) == (int8_t)_ZP_WILDNESS_SUBCHUNK_DSL) + ? _z_ke_chunk_includes_stardsl + : _z_ke_chunk_includes_nodsl; + if ((lwildness & (int8_t)_ZP_WILDNESS_SUPERCHUNKS) == (int8_t)_ZP_WILDNESS_SUPERCHUNKS) { + result = true; + _z_splitstr_t rchunks = {.s = r, .delimiter = _Z_DELIMITER}; + _z_splitstr_t lsplitatsuper = {.s = l, .delimiter = _Z_DOUBLE_STAR}; + _z_str_se_t lnosuper = _z_splitstr_next(&lsplitatsuper); + _z_str_se_t rchunk; + if (lnosuper.start != lnosuper.end) { + _z_splitstr_t pchunks = {.s = {.start = lnosuper.start, .end = _z_cptr_char_offset(lnosuper.end, -1)}, + .delimiter = _Z_DELIMITER}; + _z_str_se_t lchunk = _z_splitstr_next(&pchunks); + while ((result == true) && (lchunk.start != NULL)) { + rchunk = _z_splitstr_next(&rchunks); + result = ((rchunk.start != NULL) && (chunk_intersector(lchunk, rchunk) == true)); + lchunk = _z_splitstr_next(&pchunks); + } + } + if (result == true) { + lnosuper = _z_splitstr_nextback(&lsplitatsuper); + if (lnosuper.start != lnosuper.end) { + _z_splitstr_t schunks = { + .s = {.start = _z_cptr_char_offset(lnosuper.start, 1), .end = lnosuper.end}, + .delimiter = _Z_DELIMITER}; + _z_str_se_t lchunk = _z_splitstr_nextback(&schunks); + while ((result == true) && (lchunk.start != NULL)) { + rchunk = _z_splitstr_nextback(&rchunks); + result = rchunk.start && chunk_intersector(lchunk, rchunk); + lchunk = _z_splitstr_nextback(&schunks); + } + } + } + while (result == true) { + lnosuper = _z_splitstr_next(&lsplitatsuper); + if (lnosuper.start == NULL) { + break; + } + _z_splitstr_t needle = {.s = {.start = _z_cptr_char_offset(lnosuper.start, 1), + .end = _z_cptr_char_offset(lnosuper.end, -1)}, + .delimiter = _Z_DELIMITER}; + _z_splitstr_t haystack = rchunks; + _z_str_se_t needle_start = _z_splitstr_next(&needle); + _Bool needle_found = false; + _z_str_se_t h = _z_splitstr_next(&haystack); + while ((needle_found == false) && (h.start != NULL)) { + if (chunk_intersector(needle_start, h) == true) { + needle_found = true; + _z_splitstr_t needlecp = needle; + _z_str_se_t n = _z_splitstr_next(&needlecp); + _z_splitstr_t haystackcp = haystack; + while ((needle_found == true) && (n.start != NULL)) { + h = _z_splitstr_next(&haystackcp); + if (h.start != NULL) { + if (chunk_intersector(n, h) == false) { + needle_found = false; + } else { + n = _z_splitstr_next(&needlecp); + } + } else { + needle_found = false; + haystack.s = (_z_str_se_t){.start = NULL, .end = NULL}; + } + } + if (needle_found == true) { + rchunks = haystackcp; + } + } else { + needle_found = false; + } + h = _z_splitstr_next(&haystack); + } + } + } else if (((rwildness & (int8_t)_ZP_WILDNESS_SUPERCHUNKS) == 0) && (ln_chunks == rn_chunks)) { + _z_splitstr_t lchunks = {.s = l, .delimiter = _Z_DELIMITER}; + _z_splitstr_t rchunks = {.s = r, .delimiter = _Z_DELIMITER}; + _z_str_se_t lchunk = _z_splitstr_next(&lchunks); + _z_str_se_t rchunk = _z_splitstr_next(&rchunks); + result = true; + while ((result == true) && (lchunk.start != NULL)) { + result = chunk_intersector(lchunk, rchunk); + lchunk = _z_splitstr_next(&lchunks); + rchunk = _z_splitstr_next(&rchunks); + } + } else { + // If l doesn't have superchunks, but r does, or they have different chunk counts, non-inclusion is + // guaranteed + } + } + + return result; +} /*------------------ Intersection helpers ------------------*/ _Bool _z_ke_chunk_intersect_nodsl(_z_str_se_t l, _z_str_se_t r) { - _Bool result = ((l.start[0] == '*') || (r.start[0] == '*')); + _Bool result = + ((l.start[0] == '*' && r.start[0] != _Z_VERBATIM) || (r.start[0] == '*' && l.start[0] != _Z_VERBATIM)); if (result == false) { size_t lclen = _z_ptr_char_diff(l.end, l.start); result = ((lclen == _z_ptr_char_diff(r.end, r.start)) && (strncmp(l.start, r.start, lclen) == 0)); @@ -385,7 +493,7 @@ _Bool _z_ke_chunk_intersect_rhasstardsl(_z_str_se_t l, _z_str_se_t r) { } _Bool _z_ke_chunk_intersect_stardsl(_z_str_se_t l, _z_str_se_t r) { _Bool result = _z_ke_chunk_intersect_nodsl(l, r); - if (result == false) { + if (result == false && !(l.start[0] == '@' || r.start[0] == '@')) { result = true; _Bool l_has_stardsl = (_z_strstr(l.start, l.end, _Z_DOLLAR_STAR) != NULL); if (l_has_stardsl == true) { @@ -466,8 +574,8 @@ _Bool _z_ke_intersect_rhassuperchunks(_z_str_se_t l, _z_str_se_t r, _z_ke_chunk_ _z_str_se_t h = _z_splitstr_next(&haystack); while ((needle_found == false) && (h.start != NULL)) { - if (chunk_intersector(needle_start, h) == true) { - needle_found = true; + needle_found = chunk_intersector(needle_start, h); + if (needle_found == true) { _z_splitstr_t needlecp = needle; _z_str_se_t n = _z_splitstr_next(&needlecp); _z_splitstr_t haystackcp = haystack; @@ -487,118 +595,67 @@ _Bool _z_ke_intersect_rhassuperchunks(_z_str_se_t l, _z_str_se_t r, _z_ke_chunk_ if (needle_found == true) { lchunks = haystackcp; } - } else { - needle_found = false; + } else if (h.start[0] == _Z_VERBATIM) { + return false; } h = _z_splitstr_next(&haystack); } } + for (_z_str_se_t chunk = _z_splitstr_next(&lchunks); chunk.start != NULL && result == true; + chunk = _z_splitstr_next(&lchunks)) { + if (chunk.start[0] == _Z_VERBATIM) { + return false; + } + } return result; } -/*------------------ Zenoh-Core helpers ------------------*/ -_Bool _z_keyexpr_includes(const char *lstart, const size_t llen, const char *rstart, const size_t rlen) { - _Bool result = ((llen == rlen) && (strncmp(lstart, rstart, llen) == 0)); - if (result == false) { - _z_str_se_t l = {.start = lstart, .end = _z_cptr_char_offset(lstart, llen)}; - _z_str_se_t r = {.start = rstart, .end = _z_cptr_char_offset(rstart, rlen)}; - size_t ln_chunks = (size_t)0; - size_t rn_chunks = (size_t)0; - int8_t lwildness = _zp_ke_wildness(l, &ln_chunks); - int8_t rwildness = _zp_ke_wildness(r, &rn_chunks); - int8_t wildness = lwildness | rwildness; - _z_ke_chunk_matcher chunk_intersector = - ((wildness & (int8_t)_ZP_WILDNESS_SUBCHUNK_DSL) == (int8_t)_ZP_WILDNESS_SUBCHUNK_DSL) - ? _z_ke_chunk_includes_stardsl - : _z_ke_chunk_includes_nodsl; - if ((lwildness & (int8_t)_ZP_WILDNESS_SUPERCHUNKS) == (int8_t)_ZP_WILDNESS_SUPERCHUNKS) { - result = true; - _z_splitstr_t rchunks = {.s = r, .delimiter = _Z_DELIMITER}; - _z_splitstr_t lsplitatsuper = {.s = l, .delimiter = _Z_DOUBLE_STAR}; - _z_str_se_t lnosuper = _z_splitstr_next(&lsplitatsuper); - _z_str_se_t rchunk; - if (lnosuper.start != lnosuper.end) { - _z_splitstr_t pchunks = {.s = {.start = lnosuper.start, .end = _z_cptr_char_offset(lnosuper.end, -1)}, - .delimiter = _Z_DELIMITER}; - _z_str_se_t lchunk = _z_splitstr_next(&pchunks); - while ((result == true) && (lchunk.start != NULL)) { - rchunk = _z_splitstr_next(&rchunks); - result = ((rchunk.start != NULL) && (chunk_intersector(lchunk, rchunk) == true)); - lchunk = _z_splitstr_next(&pchunks); - } - } - if (result == true) { - lnosuper = _z_splitstr_nextback(&lsplitatsuper); - if (lnosuper.start != lnosuper.end) { - _z_splitstr_t schunks = { - .s = {.start = _z_cptr_char_offset(lnosuper.start, 1), .end = lnosuper.end}, - .delimiter = _Z_DELIMITER}; - _z_str_se_t lchunk = _z_splitstr_nextback(&schunks); - while ((result == true) && (lchunk.start != NULL)) { - rchunk = _z_splitstr_nextback(&rchunks); - result = rchunk.start && chunk_intersector(lchunk, rchunk); - lchunk = _z_splitstr_nextback(&schunks); - } - } - } - while (result == true) { - lnosuper = _z_splitstr_next(&lsplitatsuper); - if (lnosuper.start == NULL) { - break; - } - _z_splitstr_t needle = {.s = {.start = _z_cptr_char_offset(lnosuper.start, 1), - .end = _z_cptr_char_offset(lnosuper.end, -1)}, - .delimiter = _Z_DELIMITER}; - _z_splitstr_t haystack = rchunks; - _z_str_se_t needle_start = _z_splitstr_next(&needle); - _Bool needle_found = false; - _z_str_se_t h = _z_splitstr_next(&haystack); - while ((needle_found == false) && (h.start != NULL)) { - if (chunk_intersector(needle_start, h) == true) { - needle_found = true; - _z_splitstr_t needlecp = needle; - _z_str_se_t n = _z_splitstr_next(&needlecp); - _z_splitstr_t haystackcp = haystack; - while ((needle_found == true) && (n.start != NULL)) { - h = _z_splitstr_next(&haystackcp); - if (h.start != NULL) { - if (chunk_intersector(n, h) == false) { - needle_found = false; - } else { - n = _z_splitstr_next(&needlecp); - } - } else { - needle_found = false; - haystack.s = (_z_str_se_t){.start = NULL, .end = NULL}; - } - } - if (needle_found == true) { - rchunks = haystackcp; - } - } else { - needle_found = false; - } - h = _z_splitstr_next(&haystack); - } +_Bool _z_keyexpr_is_superwild_chunk(_z_str_se_t s) { + return _z_ptr_char_diff(s.end, s.start) == 2 && s.start[0] == '*'; +} + +_Bool _z_keyexpr_has_verbatim(_z_str_se_t s) { + _z_splitstr_t it = {.s = s, .delimiter = _Z_DELIMITER}; + _z_str_se_t chunk = _z_splitstr_next(&it); + while (chunk.start != NULL) { + if (chunk.start[0] == _Z_VERBATIM) { + return true; + } + } + return false; +} + +_Bool _z_keyexpr_intersect_bothsuper(_z_str_se_t l, _z_str_se_t r, _z_ke_chunk_matcher chunk_intersector) { + _z_splitstr_t it1 = {.s = l, .delimiter = _Z_DELIMITER}; + _z_splitstr_t it2 = {.s = r, .delimiter = _Z_DELIMITER}; + _z_str_se_t current1 = {0}; + _z_str_se_t current2 = {0}; + while (!_z_splitstr_is_empty(&it1) && !_z_splitstr_is_empty(&it2)) { + _z_str_se_t advanced1 = _z_splitstr_split_once(it1, ¤t1); + _z_str_se_t advanced2 = _z_splitstr_split_once(it2, ¤t2); + if (_z_keyexpr_is_superwild_chunk(current1)) { + if (advanced1.start == NULL) { + return !_z_keyexpr_has_verbatim(it2.s); } - } else if (((rwildness & (int8_t)_ZP_WILDNESS_SUPERCHUNKS) == 0) && (ln_chunks == rn_chunks)) { - _z_splitstr_t lchunks = {.s = l, .delimiter = _Z_DELIMITER}; - _z_splitstr_t rchunks = {.s = r, .delimiter = _Z_DELIMITER}; - _z_str_se_t lchunk = _z_splitstr_next(&lchunks); - _z_str_se_t rchunk = _z_splitstr_next(&rchunks); - result = true; - while ((result == true) && (lchunk.start != NULL)) { - result = chunk_intersector(lchunk, rchunk); - lchunk = _z_splitstr_next(&lchunks); - rchunk = _z_splitstr_next(&rchunks); + return (current2.start[0] != _Z_VERBATIM && + _z_keyexpr_intersect_bothsuper(it1.s, advanced2, chunk_intersector)) || + _z_keyexpr_intersect_bothsuper(advanced1, it2.s, chunk_intersector); + } else if (_z_keyexpr_is_superwild_chunk(current2)) { + if (advanced2.start == NULL) { + return !_z_keyexpr_has_verbatim(it1.s); } + return (current1.start[0] != _Z_VERBATIM && + _z_keyexpr_intersect_bothsuper(it2.s, advanced1, chunk_intersector)) || + _z_keyexpr_intersect_bothsuper(advanced2, it1.s, chunk_intersector); + } else if (chunk_intersector(current1, current2)) { + it1.s = advanced1; + it2.s = advanced2; } else { - // If l doesn't have superchunks, but r does, or they have different chunk counts, non-inclusion is - // guaranteed + return false; } } - - return result; + return (_z_splitstr_is_empty(&it1) || _z_keyexpr_is_superwild_chunk(it1.s)) && + (_z_splitstr_is_empty(&it2) || _z_keyexpr_is_superwild_chunk(it2.s)); } _Bool _z_keyexpr_intersects(const char *lstart, const size_t llen, const char *rstart, const size_t rlen) { @@ -606,53 +663,18 @@ _Bool _z_keyexpr_intersects(const char *lstart, const size_t llen, const char *r if (result == false) { _z_str_se_t l = {.start = lstart, .end = _z_cptr_char_offset(lstart, llen)}; _z_str_se_t r = {.start = rstart, .end = _z_cptr_char_offset(rstart, rlen)}; - size_t ln_chunks = 0; - size_t rn_chunks = 0; - int8_t lwildness = _zp_ke_wildness(l, &ln_chunks); - int8_t rwildness = _zp_ke_wildness(r, &rn_chunks); + size_t ln_chunks = 0, ln_verbatim = 0; + size_t rn_chunks = 0, rn_verbatim = 0; + int8_t lwildness = _zp_ke_wildness(l, &ln_chunks, &ln_verbatim); + int8_t rwildness = _zp_ke_wildness(r, &rn_chunks, &rn_verbatim); int8_t wildness = lwildness | rwildness; _z_ke_chunk_matcher chunk_intersector = ((wildness & (int8_t)_ZP_WILDNESS_SUBCHUNK_DSL) == (int8_t)_ZP_WILDNESS_SUBCHUNK_DSL) ? _z_ke_chunk_intersect_stardsl : _z_ke_chunk_intersect_nodsl; - if (wildness != (int8_t)0) { + if (wildness != (int8_t)0 && rn_verbatim == ln_verbatim) { if ((lwildness & rwildness & (int8_t)_ZP_WILDNESS_SUPERCHUNKS) == (int8_t)_ZP_WILDNESS_SUPERCHUNKS) { - // TODO: both expressions contain superchunks - _z_splitstr_t lchunks = {.s = l, .delimiter = _Z_DELIMITER}; - _z_splitstr_t rchunks = {.s = r, .delimiter = _Z_DELIMITER}; - result = true; - while (result == true) { - _z_str_se_t lchunk = _z_splitstr_next(&lchunks); - _z_str_se_t rchunk = _z_splitstr_next(&rchunks); - if ((lchunk.start != NULL) || (rchunk.start != NULL)) { - if ((_z_ke_isdoublestar(lchunk) == true) || (_z_ke_isdoublestar(rchunk) == true)) { - break; - } else if ((lchunk.start != NULL) && (rchunk.start != NULL)) { - result = chunk_intersector(lchunk, rchunk); - } else { - // Guaranteed not to happen, as both iterators contain a `**` which will break - // iteration. - } - } else { - // Guaranteed not to happen, as both iterators contain a `**` which will break iteration. - } - } - while (result == true) { - _z_str_se_t lchunk = _z_splitstr_nextback(&lchunks); - _z_str_se_t rchunk = _z_splitstr_nextback(&rchunks); - if ((lchunk.start != NULL) || (rchunk.start != NULL)) { - if ((_z_ke_isdoublestar(lchunk) == true) || (_z_ke_isdoublestar(rchunk) == true)) { - break; - } else if ((lchunk.start != NULL) && (rchunk.start != NULL)) { - result = chunk_intersector(lchunk, rchunk); - } else { - // Happens in cases such as `a / ** / b` with `a / ** / c / b` - break; - } - } else { - break; - } - } + result = _z_keyexpr_intersect_bothsuper(l, r, chunk_intersector); } else if (((lwildness & (int8_t)_ZP_WILDNESS_SUPERCHUNKS) == (int8_t)_ZP_WILDNESS_SUPERCHUNKS) && (ln_chunks <= (rn_chunks * (size_t)2 + (size_t)1))) { result = _z_ke_intersect_rhassuperchunks(r, l, chunk_intersector); @@ -675,7 +697,8 @@ _Bool _z_keyexpr_intersects(const char *lstart, const size_t llen, const char *r // No superchunks detected, and number of chunks differ: no intersection guaranteed. } } else { - // No string equality and no wildness detected: no intersection guaranteed. + // No string equality and no wildness detected, or different count of verbatim chunks: no intersection + // guaranteed. } } else { // String equality guarantees intersection, no further process needed. diff --git a/src/utils/string.c b/src/utils/string.c index 74e847981..e95e3ab07 100644 --- a/src/utils/string.c +++ b/src/utils/string.c @@ -91,6 +91,7 @@ char const *_z_bstrstr_skipneedle(_z_str_se_t haystack, _z_str_se_t needle) { return result; } +_Bool _z_splitstr_is_empty(const _z_splitstr_t *src) { return src->s.start == NULL; } _z_str_se_t _z_splitstr_next(_z_splitstr_t *str) { _z_str_se_t result = str->s; if (str->s.start != NULL) { @@ -106,6 +107,10 @@ _z_str_se_t _z_splitstr_next(_z_splitstr_t *str) { } return result; } +_z_str_se_t _z_splitstr_split_once(_z_splitstr_t src, _z_str_se_t *next) { + *next = _z_splitstr_next(&src); + return src.s; +} _z_str_se_t _z_splitstr_nextback(_z_splitstr_t *str) { _z_str_se_t result = str->s; diff --git a/tests/z_keyexpr_test.c b/tests/z_keyexpr_test.c index d4cf921ef..6b49f106a 100644 --- a/tests/z_keyexpr_test.c +++ b/tests/z_keyexpr_test.c @@ -96,6 +96,12 @@ int main(void) { assert(_z_keyexpr_intersects("a/**/b$*", strlen("a/**/b$*"), "a/bc", strlen("a/bc"))); assert(_z_keyexpr_intersects("a/**/$*b$*", strlen("a/**/$*b$*"), "a/ebc", strlen("a/ebc"))); assert(_z_keyexpr_intersects("a/**/$*b", strlen("a/**/$*b"), "a/cb", strlen("a/cb"))); + assert(_z_keyexpr_intersects("a/**/b/c/**/d", strlen("a/**/b/c/**/d"), "a/b/b/b/c/d", strlen("a/b/b/b/c/d"))); + assert( + !_z_keyexpr_intersects("a/**/b/c/**/d", strlen("a/**/b/c/**/d"), "a/b/b/b/c/@c/d", strlen("a/b/b/b/c/@c/d"))); + assert(!_z_keyexpr_intersects("a/**/b/c/**/d", strlen("a/**/b/c/**/d"), "a/b/@b/b/c/d", strlen("a/b/@b/b/c/d"))); + assert(_z_keyexpr_intersects("a/**/b/@b/**/b/c/**/d", strlen("a/**/b/@b/**/b/c/**/d"), "a/b/@b/b/c/d", + strlen("a/b/@b/b/c/d"))); assert(!_z_keyexpr_intersects("a/**/b$*", strlen("a/**/b$*"), "a/ebc", strlen("a/ebc"))); assert(!_z_keyexpr_intersects("a/**/$*b", strlen("a/**/$*b"), "a/cbc", strlen("a/cbc"))); @@ -171,6 +177,29 @@ int main(void) { assert(zp_keyexpr_intersect_null_terminated("a/**/b$*", "a/ebc") == -1); assert(zp_keyexpr_intersect_null_terminated("a/**/$*b", "a/cbc") == -1); + assert((zp_keyexpr_intersect_null_terminated("@a", "@a") == 0)); + assert(!(zp_keyexpr_intersect_null_terminated("@a", "@ab") == 0)); + assert(!(zp_keyexpr_intersect_null_terminated("@a", "@a/b") == 0)); + assert(!(zp_keyexpr_intersect_null_terminated("@a", "@a/*") == 0)); + assert(!(zp_keyexpr_intersect_null_terminated("@a", "@a/*/**") == 0)); + assert(!(zp_keyexpr_intersect_null_terminated("@a", "@a$*/**") == 0)); + assert((zp_keyexpr_intersect_null_terminated("@a", "@a/**") == 0)); + assert(!(zp_keyexpr_intersect_null_terminated("**/xyz$*xyz", "@a/b/xyzdefxyz") == 0)); + assert((zp_keyexpr_intersect_null_terminated("@a/**/c/**/e", "@a/b/b/b/c/d/d/d/e") == 0)); + assert(!(zp_keyexpr_intersect_null_terminated("@a/**/c/**/e", "@a/@b/b/b/c/d/d/d/e") == 0)); + assert((zp_keyexpr_intersect_null_terminated("@a/**/@c/**/e", "@a/b/b/b/@c/d/d/d/e") == 0)); + assert((zp_keyexpr_intersect_null_terminated("@a/**/e", "@a/b/b/d/d/d/e") == 0)); + assert((zp_keyexpr_intersect_null_terminated("@a/**/e", "@a/b/b/b/d/d/d/e") == 0)); + assert((zp_keyexpr_intersect_null_terminated("@a/**/e", "@a/b/b/c/d/d/d/e") == 0)); + assert(!(zp_keyexpr_intersect_null_terminated("@a/**/e", "@a/b/b/@c/b/d/d/d/e") == 0)); + assert(!(zp_keyexpr_intersect_null_terminated("@a/*", "@a/@b") == 0)); + assert(!(zp_keyexpr_intersect_null_terminated("@a/**", "@a/@b") == 0)); + assert((zp_keyexpr_intersect_null_terminated("@a/**/@b", "@a/@b") == 0)); + assert(!(zp_keyexpr_intersect_null_terminated("@a/**/@b", "@a/**/@c/**/@b") == 0)); + assert((zp_keyexpr_intersect_null_terminated("@a/@b/**", "@a/@b") == 0)); + assert((zp_keyexpr_intersect_null_terminated("@a/**/@c/@b", "@a/**/@c/**/@b") == 0)); + assert((zp_keyexpr_intersect_null_terminated("@a/**/@c/**/@b", "@a/**/@c/@b") == 0)); + assert(_z_keyexpr_includes("a", strlen("a"), "a", strlen("a"))); assert(_z_keyexpr_includes("a/b", strlen("a/b"), "a/b", strlen("a/b"))); assert(_z_keyexpr_includes("*", strlen("*"), "a", strlen("a"))); diff --git a/zenohpico.pc b/zenohpico.pc index 13a28765e..ae9a01519 100644 --- a/zenohpico.pc +++ b/zenohpico.pc @@ -3,6 +3,6 @@ prefix=/usr/local Name: zenohpico Description: URL: -Version: 0.11.20240118dev +Version: 0.11.20240122dev Cflags: -I${prefix}/include Libs: -L${prefix}/lib -lzenohpico From a3d0a7fcc450776f2ac3126d6f1d7d8cc4577295 Mon Sep 17 00:00:00 2001 From: Pierre Avital Date: Mon, 22 Jan 2024 18:18:17 +0100 Subject: [PATCH 2/4] handle verbatims in inclusions --- src/protocol/keyexpr.c | 148 ++++++++++++++++------------------------- tests/z_keyexpr_test.c | 15 +++++ 2 files changed, 74 insertions(+), 89 deletions(-) diff --git a/src/protocol/keyexpr.c b/src/protocol/keyexpr.c index 27528bf98..4a845d867 100644 --- a/src/protocol/keyexpr.c +++ b/src/protocol/keyexpr.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "zenoh-pico/protocol/core.h" @@ -292,8 +293,9 @@ _Bool _z_ke_isdoublestar(_z_str_se_t s) { /*------------------ Inclusion helpers ------------------*/ _Bool _z_ke_chunk_includes_nodsl(_z_str_se_t l, _z_str_se_t r) { size_t llen = l.end - l.start; - _Bool result = ((llen == (size_t)1) && (l.start[0] == '*') && - (((_z_ptr_char_diff(r.end, r.start) == 2) && (r.start[0] == '*') && (r.start[1] == '*')) == false)); + _Bool result = + !(r.start[0] == _Z_VERBATIM) && ((llen == (size_t)1) && (l.start[0] == '*') && + (((_z_ptr_char_diff(r.end, r.start) == 2) && (r.start[0] == '*')) == false)); if ((result == false) && (llen == _z_ptr_char_diff(r.end, r.start))) { result = strncmp(l.start, r.start, llen) == 0; } @@ -303,7 +305,7 @@ _Bool _z_ke_chunk_includes_nodsl(_z_str_se_t l, _z_str_se_t r) { _Bool _z_ke_chunk_includes_stardsl(_z_str_se_t l1, _z_str_se_t r) { _Bool result = _z_ke_chunk_includes_nodsl(l1, r); - if (result == false) { + if (result == false && l1.start[0] != _Z_VERBATIM && r.start[0] != _Z_VERBATIM) { _z_splitstr_t lcs = {.s = l1, .delimiter = _Z_DOLLAR_STAR}; _z_str_se_t split_l = _z_splitstr_next(&lcs); result = (_z_ptr_char_diff(r.end, r.start) >= _z_ptr_char_diff(split_l.end, split_l.start)); @@ -340,6 +342,57 @@ _Bool _z_ke_chunk_includes_stardsl(_z_str_se_t l1, _z_str_se_t r) { return result; } + +_Bool _z_keyexpr_is_wild_chunk(_z_str_se_t s) { return _z_ptr_char_diff(s.end, s.start) == 1 && s.start[0] == '*'; } + +_Bool _z_keyexpr_is_superwild_chunk(_z_str_se_t s) { + return _z_ptr_char_diff(s.end, s.start) == 2 && s.start[0] == '*'; +} + +_Bool _z_keyexpr_has_verbatim(_z_str_se_t s) { + _z_splitstr_t it = {.s = s, .delimiter = _Z_DELIMITER}; + _z_str_se_t chunk = _z_splitstr_next(&it); + while (chunk.start != NULL) { + if (chunk.start[0] == _Z_VERBATIM) { + return true; + } + chunk = _z_splitstr_next(&it); + } + return false; +} + +_Bool _z_keyexpr_includes_superwild(_z_str_se_t left, _z_str_se_t right, _z_ke_chunk_matcher chunk_includer) { + for (;;) { + _z_str_se_t lchunk = {0}; + _z_str_se_t lrest = _z_splitstr_split_once((_z_splitstr_t){.s = left, .delimiter = _Z_DELIMITER}, &lchunk); + _Bool lempty = lrest.start == NULL; + if (_z_keyexpr_is_superwild_chunk(lchunk)) { + if (lempty ? !_z_keyexpr_has_verbatim(right) + : _z_keyexpr_includes_superwild(lrest, right, chunk_includer)) { + return true; + } + if (right.start[0] == _Z_VERBATIM) { + return false; + } + right = _z_splitstr_split_once((_z_splitstr_t){.s = right, .delimiter = _Z_DELIMITER}, &lrest); + if (right.start == NULL) { + return false; + } + } else { + _z_str_se_t rchunk = {0}; + _z_str_se_t rrest = _z_splitstr_split_once((_z_splitstr_t){.s = right, .delimiter = _Z_DELIMITER}, &rchunk); + if (rchunk.start == NULL || _z_keyexpr_is_superwild_chunk(rchunk) || !chunk_includer(lchunk, rchunk)) { + return false; + } + if (lempty) { + return rrest.start == NULL; + } + left = lrest; + right = rrest; + } + } +} + /*------------------ Zenoh-Core helpers ------------------*/ _Bool _z_keyexpr_includes(const char *lstart, const size_t llen, const char *rstart, const size_t rlen) { _Bool result = ((llen == rlen) && (strncmp(lstart, rstart, llen) == 0)); @@ -351,80 +404,12 @@ _Bool _z_keyexpr_includes(const char *lstart, const size_t llen, const char *rst int8_t lwildness = _zp_ke_wildness(l, &ln_chunks, &ln_verbatim); int8_t rwildness = _zp_ke_wildness(r, &rn_chunks, &rn_verbatim); int8_t wildness = lwildness | rwildness; - _z_ke_chunk_matcher chunk_intersector = + _z_ke_chunk_matcher chunk_includer = ((wildness & (int8_t)_ZP_WILDNESS_SUBCHUNK_DSL) == (int8_t)_ZP_WILDNESS_SUBCHUNK_DSL) ? _z_ke_chunk_includes_stardsl : _z_ke_chunk_includes_nodsl; if ((lwildness & (int8_t)_ZP_WILDNESS_SUPERCHUNKS) == (int8_t)_ZP_WILDNESS_SUPERCHUNKS) { - result = true; - _z_splitstr_t rchunks = {.s = r, .delimiter = _Z_DELIMITER}; - _z_splitstr_t lsplitatsuper = {.s = l, .delimiter = _Z_DOUBLE_STAR}; - _z_str_se_t lnosuper = _z_splitstr_next(&lsplitatsuper); - _z_str_se_t rchunk; - if (lnosuper.start != lnosuper.end) { - _z_splitstr_t pchunks = {.s = {.start = lnosuper.start, .end = _z_cptr_char_offset(lnosuper.end, -1)}, - .delimiter = _Z_DELIMITER}; - _z_str_se_t lchunk = _z_splitstr_next(&pchunks); - while ((result == true) && (lchunk.start != NULL)) { - rchunk = _z_splitstr_next(&rchunks); - result = ((rchunk.start != NULL) && (chunk_intersector(lchunk, rchunk) == true)); - lchunk = _z_splitstr_next(&pchunks); - } - } - if (result == true) { - lnosuper = _z_splitstr_nextback(&lsplitatsuper); - if (lnosuper.start != lnosuper.end) { - _z_splitstr_t schunks = { - .s = {.start = _z_cptr_char_offset(lnosuper.start, 1), .end = lnosuper.end}, - .delimiter = _Z_DELIMITER}; - _z_str_se_t lchunk = _z_splitstr_nextback(&schunks); - while ((result == true) && (lchunk.start != NULL)) { - rchunk = _z_splitstr_nextback(&rchunks); - result = rchunk.start && chunk_intersector(lchunk, rchunk); - lchunk = _z_splitstr_nextback(&schunks); - } - } - } - while (result == true) { - lnosuper = _z_splitstr_next(&lsplitatsuper); - if (lnosuper.start == NULL) { - break; - } - _z_splitstr_t needle = {.s = {.start = _z_cptr_char_offset(lnosuper.start, 1), - .end = _z_cptr_char_offset(lnosuper.end, -1)}, - .delimiter = _Z_DELIMITER}; - _z_splitstr_t haystack = rchunks; - _z_str_se_t needle_start = _z_splitstr_next(&needle); - _Bool needle_found = false; - _z_str_se_t h = _z_splitstr_next(&haystack); - while ((needle_found == false) && (h.start != NULL)) { - if (chunk_intersector(needle_start, h) == true) { - needle_found = true; - _z_splitstr_t needlecp = needle; - _z_str_se_t n = _z_splitstr_next(&needlecp); - _z_splitstr_t haystackcp = haystack; - while ((needle_found == true) && (n.start != NULL)) { - h = _z_splitstr_next(&haystackcp); - if (h.start != NULL) { - if (chunk_intersector(n, h) == false) { - needle_found = false; - } else { - n = _z_splitstr_next(&needlecp); - } - } else { - needle_found = false; - haystack.s = (_z_str_se_t){.start = NULL, .end = NULL}; - } - } - if (needle_found == true) { - rchunks = haystackcp; - } - } else { - needle_found = false; - } - h = _z_splitstr_next(&haystack); - } - } + return _z_keyexpr_includes_superwild(l, r, chunk_includer); } else if (((rwildness & (int8_t)_ZP_WILDNESS_SUPERCHUNKS) == 0) && (ln_chunks == rn_chunks)) { _z_splitstr_t lchunks = {.s = l, .delimiter = _Z_DELIMITER}; _z_splitstr_t rchunks = {.s = r, .delimiter = _Z_DELIMITER}; @@ -432,7 +417,7 @@ _Bool _z_keyexpr_includes(const char *lstart, const size_t llen, const char *rst _z_str_se_t rchunk = _z_splitstr_next(&rchunks); result = true; while ((result == true) && (lchunk.start != NULL)) { - result = chunk_intersector(lchunk, rchunk); + result = chunk_includer(lchunk, rchunk); lchunk = _z_splitstr_next(&lchunks); rchunk = _z_splitstr_next(&rchunks); } @@ -610,21 +595,6 @@ _Bool _z_ke_intersect_rhassuperchunks(_z_str_se_t l, _z_str_se_t r, _z_ke_chunk_ return result; } -_Bool _z_keyexpr_is_superwild_chunk(_z_str_se_t s) { - return _z_ptr_char_diff(s.end, s.start) == 2 && s.start[0] == '*'; -} - -_Bool _z_keyexpr_has_verbatim(_z_str_se_t s) { - _z_splitstr_t it = {.s = s, .delimiter = _Z_DELIMITER}; - _z_str_se_t chunk = _z_splitstr_next(&it); - while (chunk.start != NULL) { - if (chunk.start[0] == _Z_VERBATIM) { - return true; - } - } - return false; -} - _Bool _z_keyexpr_intersect_bothsuper(_z_str_se_t l, _z_str_se_t r, _z_ke_chunk_matcher chunk_intersector) { _z_splitstr_t it1 = {.s = l, .delimiter = _Z_DELIMITER}; _z_splitstr_t it2 = {.s = r, .delimiter = _Z_DELIMITER}; diff --git a/tests/z_keyexpr_test.c b/tests/z_keyexpr_test.c index 6b49f106a..1539b75a5 100644 --- a/tests/z_keyexpr_test.c +++ b/tests/z_keyexpr_test.c @@ -268,6 +268,21 @@ int main(void) { assert(zp_keyexpr_includes_null_terminated("a/**/b$*", "a/ebc") == -1); assert(zp_keyexpr_includes_null_terminated("a/**/$*b", "a/cbc") == -1); + assert((zp_keyexpr_includes_null_terminated("@a", "@a") == 0)); + assert(!(zp_keyexpr_includes_null_terminated("@a", "@ab") == 0)); + assert(!(zp_keyexpr_includes_null_terminated("@a", "@a/b") == 0)); + assert(!(zp_keyexpr_includes_null_terminated("@a", "@a/*") == 0)); + assert(!(zp_keyexpr_includes_null_terminated("@a", "@a/*/**") == 0)); + assert(!(zp_keyexpr_includes_null_terminated("@a$*/**", "@a") == 0)); + assert(!(zp_keyexpr_includes_null_terminated("@a", "@a/**") == 0)); + assert((zp_keyexpr_includes_null_terminated("@a/**", "@a") == 0)); + assert(!(zp_keyexpr_includes_null_terminated("**/xyz$*xyz", "@a/b/xyzdefxyz") == 0)); + assert((zp_keyexpr_includes_null_terminated("@a/**/c/**/e", "@a/b/b/b/c/d/d/d/e") == 0)); + assert(!(zp_keyexpr_includes_null_terminated("@a/*", "@a/@b") == 0)); + assert(!(zp_keyexpr_includes_null_terminated("@a/**", "@a/@b") == 0)); + assert((zp_keyexpr_includes_null_terminated("@a/**/@b", "@a/@b") == 0)); + assert((zp_keyexpr_includes_null_terminated("@a/@b/**", "@a/@b") == 0)); + // clang-format off #define N 31 From be797e4938dfe765c3c64ed0615190d5b72572ef Mon Sep 17 00:00:00 2001 From: Pierre Avital Date: Mon, 22 Jan 2024 18:22:27 +0100 Subject: [PATCH 3/4] forgot to remove header after debuging with prints --- src/protocol/keyexpr.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/protocol/keyexpr.c b/src/protocol/keyexpr.c index 4a845d867..28910e29e 100644 --- a/src/protocol/keyexpr.c +++ b/src/protocol/keyexpr.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include "zenoh-pico/protocol/core.h" From 4d41a171d6e2a5a73846c3d8024b8528cefc2f26 Mon Sep 17 00:00:00 2001 From: Pierre Avital Date: Tue, 23 Jan 2024 12:28:58 +0100 Subject: [PATCH 4/4] make intersect try to consume superwilds ASAP --- src/protocol/keyexpr.c | 12 ++++++------ zenohpico.pc | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/protocol/keyexpr.c b/src/protocol/keyexpr.c index 28910e29e..4f1b10a60 100644 --- a/src/protocol/keyexpr.c +++ b/src/protocol/keyexpr.c @@ -606,16 +606,16 @@ _Bool _z_keyexpr_intersect_bothsuper(_z_str_se_t l, _z_str_se_t r, _z_ke_chunk_m if (advanced1.start == NULL) { return !_z_keyexpr_has_verbatim(it2.s); } - return (current2.start[0] != _Z_VERBATIM && - _z_keyexpr_intersect_bothsuper(it1.s, advanced2, chunk_intersector)) || - _z_keyexpr_intersect_bothsuper(advanced1, it2.s, chunk_intersector); + return _z_keyexpr_intersect_bothsuper(advanced1, it2.s, chunk_intersector) || + (current2.start[0] != _Z_VERBATIM && + _z_keyexpr_intersect_bothsuper(it1.s, advanced2, chunk_intersector)); } else if (_z_keyexpr_is_superwild_chunk(current2)) { if (advanced2.start == NULL) { return !_z_keyexpr_has_verbatim(it1.s); } - return (current1.start[0] != _Z_VERBATIM && - _z_keyexpr_intersect_bothsuper(it2.s, advanced1, chunk_intersector)) || - _z_keyexpr_intersect_bothsuper(advanced2, it1.s, chunk_intersector); + return _z_keyexpr_intersect_bothsuper(advanced2, it1.s, chunk_intersector) || + (current1.start[0] != _Z_VERBATIM && + _z_keyexpr_intersect_bothsuper(it2.s, advanced1, chunk_intersector)); } else if (chunk_intersector(current1, current2)) { it1.s = advanced1; it2.s = advanced2; diff --git a/zenohpico.pc b/zenohpico.pc index ae9a01519..915f9cc6e 100644 --- a/zenohpico.pc +++ b/zenohpico.pc @@ -3,6 +3,6 @@ prefix=/usr/local Name: zenohpico Description: URL: -Version: 0.11.20240122dev +Version: 0.11.20240123dev Cflags: -I${prefix}/include Libs: -L${prefix}/lib -lzenohpico