zenoh_keyexpr/key_expr/intersect/
mod.rs

1//
2// Copyright (c) 2023 ZettaScale Technology
3//
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7// which is available at https://www.apache.org/licenses/LICENSE-2.0.
8//
9// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10//
11// Contributors:
12//   ZettaScale Zenoh Team, <zenoh@zettascale.tech>
13//
14
15use super::keyexpr;
16use crate::DELIMITER;
17
18mod classical;
19pub use classical::ClassicIntersector;
20
21pub const DEFAULT_INTERSECTOR: ClassicIntersector = ClassicIntersector;
22
23/// The trait used to implement key expression intersectors.
24///
25/// Note that `Intersector<&keyexpr, &keyexpr>` is auto-implemented with quickchecks (`streq->true`, `strne&nowild->false`)
26/// for any `Intersector<&[u8], &[u8]>`. Implementing `Intersector<&[u8], &[u8]>` is the recommended way to implement intersectors.
27pub trait Intersector<Left, Right> {
28    fn intersect(&self, left: Left, right: Right) -> bool;
29}
30
31pub(crate) mod restriction {
32    #[repr(transparent)]
33    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
34    pub struct NoSubWilds<T>(pub T);
35}
36#[repr(u8)]
37enum MatchComplexity {
38    NoWilds = 0,
39    ChunkWildsOnly = 1,
40    Dsl = 2,
41}
42trait KeyExprHelpers {
43    fn match_complexity(&self) -> MatchComplexity;
44}
45impl KeyExprHelpers for keyexpr {
46    fn match_complexity(&self) -> MatchComplexity {
47        let mut has_wilds = false;
48        for &c in self.as_bytes() {
49            match c {
50                b'*' => has_wilds = true,
51                b'$' => return MatchComplexity::Dsl,
52                _ => {}
53            }
54        }
55        if has_wilds {
56            MatchComplexity::ChunkWildsOnly
57        } else {
58            MatchComplexity::NoWilds
59        }
60    }
61}
62
63use restriction::NoSubWilds;
64impl<
65        T: for<'a> Intersector<&'a [u8], &'a [u8]>
66            + for<'a> Intersector<NoSubWilds<&'a [u8]>, NoSubWilds<&'a [u8]>>,
67    > Intersector<&keyexpr, &keyexpr> for T
68{
69    fn intersect(&self, left: &keyexpr, right: &keyexpr) -> bool {
70        let left_bytes = left.as_bytes();
71        let right_bytes = right.as_bytes();
72        if left_bytes == right_bytes {
73            return true;
74        }
75        match left.match_complexity() as u8 | right.match_complexity() as u8 {
76            0 => false,
77            1 => self.intersect(NoSubWilds(left_bytes), NoSubWilds(right_bytes)),
78            _ => self.intersect(left_bytes, right_bytes),
79        }
80    }
81}
82
83pub(crate) trait MayHaveVerbatim {
84    fn has_verbatim(&self) -> bool;
85    fn has_direct_verbatim(&self) -> bool;
86    unsafe fn has_direct_verbatim_non_empty(&self) -> bool {
87        self.has_direct_verbatim()
88    }
89}
90
91impl MayHaveVerbatim for [u8] {
92    fn has_direct_verbatim(&self) -> bool {
93        matches!(self, [b'@', ..])
94    }
95    fn has_verbatim(&self) -> bool {
96        self.split(|c| *c == DELIMITER)
97            .any(MayHaveVerbatim::has_direct_verbatim)
98    }
99    unsafe fn has_direct_verbatim_non_empty(&self) -> bool {
100        unsafe { *self.get_unchecked(0) == b'@' }
101    }
102}