Skip to main content

zenoh_keyexpr/keyexpr_tree/iters/
intersection.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 alloc::vec::Vec;
16
17use zenoh_result::unlikely;
18
19use crate::keyexpr_tree::*;
20
21struct StackFrame<'a, Children: IChildrenProvider<Node>, Node: UIKeyExprTreeNode<Weight>, Weight>
22where
23    Children::Assoc: IChildren<Node> + 'a,
24    <Children::Assoc as IChildren<Node>>::Node: 'a,
25{
26    iterator: <Children::Assoc as IChildren<Node>>::Iter<'a>,
27    start: usize,
28    end: usize,
29    _marker: core::marker::PhantomData<Weight>,
30}
31pub struct Intersection<
32    'a,
33    Children: IChildrenProvider<Node>,
34    Node: UIKeyExprTreeNode<Weight>,
35    Weight,
36> where
37    Children::Assoc: IChildren<Node> + 'a,
38{
39    key: &'a keyexpr,
40    ke_indices: Vec<usize>,
41    iterators: Vec<StackFrame<'a, Children, Node, Weight>>,
42}
43
44impl<'a, Children: IChildrenProvider<Node>, Node: UIKeyExprTreeNode<Weight>, Weight>
45    core::fmt::Debug for Intersection<'a, Children, Node, Weight>
46where
47    Children::Assoc: IChildren<Node> + 'a,
48{
49    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
50        f.debug_struct("Intersection")
51            .field("key", &self.key)
52            .field("ke_indices_len", &self.ke_indices.len())
53            .field("depth", &self.iterators.len())
54            .finish()
55    }
56}
57
58impl<'a, Children: IChildrenProvider<Node>, Node: UIKeyExprTreeNode<Weight>, Weight>
59    Intersection<'a, Children, Node, Weight>
60where
61    Children::Assoc: IChildren<Node> + 'a,
62{
63    pub(crate) fn new(children: &'a Children::Assoc, key: &'a keyexpr) -> Self {
64        let mut ke_indices = Vec::with_capacity(32);
65        ke_indices.push(0);
66        let mut iterators = Vec::with_capacity(16);
67        iterators.push(StackFrame {
68            iterator: children.children(),
69            start: 0,
70            end: 1,
71            _marker: Default::default(),
72        });
73        Self {
74            key,
75            ke_indices,
76            iterators,
77        }
78    }
79}
80
81impl<
82        'a,
83        Children: IChildrenProvider<Node>,
84        Node: UIKeyExprTreeNode<Weight, Children = Children::Assoc> + 'a,
85        Weight,
86    > Iterator for Intersection<'a, Children, Node, Weight>
87where
88    Children::Assoc: IChildren<Node> + 'a,
89{
90    type Item = &'a Node;
91    fn next(&mut self) -> Option<Self::Item> {
92        loop {
93            let StackFrame {
94                iterator,
95                start,
96                end,
97                _marker,
98            } = self.iterators.last_mut()?;
99            match iterator.next() {
100                Some(node) => {
101                    let mut node_matches = false;
102                    let new_start = *end;
103                    let mut new_end = *end;
104                    macro_rules! push {
105                        ($index: expr) => {
106                            let index = $index;
107                            if new_end == new_start || self.ke_indices[new_end - 1] < index {
108                                self.ke_indices.push(index);
109                                new_end += 1;
110                            }
111                        };
112                    }
113                    let chunk = node.chunk();
114                    let chunk_is_verbatim = chunk.first_byte() == b'@';
115                    if unlikely(chunk.as_bytes() == b"**") {
116                        // If the current node is `**`, it is guaranteed to match...
117                        node_matches = true;
118                        // and may consume any number of chunks from the KE
119                        push!(self.ke_indices[*start]);
120                        if self.key.len() != self.ke_indices[*start] {
121                            if self.key.as_bytes()[self.ke_indices[*start]] != b'@' {
122                                for i in self.ke_indices[*start]..self.key.len() {
123                                    if self.key.as_bytes()[i] == b'/' {
124                                        push!(i + 1);
125                                        if self.key.as_bytes()[i + 1] == b'@' {
126                                            node_matches = false; // ...unless the KE contains a verbatim chunk.
127                                            break;
128                                        }
129                                    }
130                                }
131                            } else {
132                                node_matches = false;
133                            }
134                        }
135                    } else {
136                        // The current node is not `**`
137                        // For all candidate chunks of the KE
138                        for i in *start..*end {
139                            // construct that chunk, while checking whether or not it's the last one
140                            let kec_start = self.ke_indices[i];
141                            if unlikely(kec_start == self.key.len()) {
142                                break;
143                            }
144                            let key = &self.key.as_bytes()[kec_start..];
145                            match key.iter().position(|&c| c == b'/') {
146                                Some(kec_end) => {
147                                    // If we aren't in the last chunk
148                                    let subkey =
149                                        // SAFETY: upheld by the surrounding invariants and prior validation.
150                                        unsafe { keyexpr::from_slice_unchecked(&key[..kec_end]) };
151                                    if unlikely(subkey.as_bytes() == b"**") {
152                                        if !chunk_is_verbatim {
153                                            // If the query chunk is `**`:
154                                            // children will have to process it again
155                                            push!(kec_start);
156                                        }
157                                        // and we need to process this chunk as if the `**` wasn't there,
158                                        // but with the knowledge that the next chunk won't be `**`.
159                                        let post_key = &key[kec_end + 1..];
160                                        match post_key.iter().position(|&c| c == b'/') {
161                                            Some(sec_end) => {
162                                                // SAFETY: upheld by the surrounding invariants and prior validation.
163                                                let post_key = unsafe {
164                                                    keyexpr::from_slice_unchecked(
165                                                        &post_key[..sec_end],
166                                                    )
167                                                };
168                                                if post_key.intersects(chunk) {
169                                                    push!(kec_start + kec_end + sec_end + 2);
170                                                }
171                                            }
172                                            None => {
173                                                // SAFETY: upheld by the surrounding invariants and prior validation.
174                                                if unsafe {
175                                                    keyexpr::from_slice_unchecked(post_key)
176                                                }
177                                                .intersects(chunk)
178                                                {
179                                                    push!(self.key.len());
180                                                    node_matches = true;
181                                                }
182                                            }
183                                        }
184                                    } else if chunk.intersects(subkey) {
185                                        push!(kec_start + kec_end + 1);
186                                    }
187                                }
188                                None => {
189                                    // If it's the last chunk of the query, check whether it's `**`
190                                    // SAFETY: upheld by the surrounding invariants and prior validation.
191                                    let key = unsafe { keyexpr::from_slice_unchecked(key) };
192                                    if unlikely(key.as_bytes() == b"**") && !chunk_is_verbatim {
193                                        // If yes, it automatically matches, and must be reused from now on for iteration.
194                                        push!(kec_start);
195                                        node_matches = true;
196                                    } else if chunk.intersects(key) {
197                                        // else, if it intersects with the chunk, make sure the children of the node
198                                        // are searched for `**`
199                                        push!(self.key.len());
200                                        node_matches = true;
201                                    }
202                                }
203                            }
204                        }
205                    }
206                    // If new progress points have been set
207                    if new_end != new_start {
208                        // Check if any of them is `**`, as this would mean a match
209                        for &i in &self.ke_indices[new_start..new_end] {
210                            if &self.key.as_bytes()[i..] == b"**" {
211                                node_matches = true;
212                                break;
213                            }
214                        }
215                        // Prepare the next children
216                        // SAFETY: upheld by the surrounding invariants and prior validation.
217                        let iterator = unsafe { node.as_node().__children() }.children();
218                        self.iterators.push(StackFrame {
219                            iterator,
220                            start: new_start,
221                            end: new_end,
222                            _marker: Default::default(),
223                        })
224                    }
225                    // yield the node if a match was found
226                    if node_matches {
227                        return Some(node.as_node());
228                    }
229                }
230                None => {
231                    if let Some(StackFrame { start, .. }) = self.iterators.pop() {
232                        self.ke_indices.truncate(start);
233                    }
234                }
235            }
236        }
237    }
238}
239struct StackFrameMut<'a, Children: IChildrenProvider<Node>, Node: IKeyExprTreeNode<Weight>, Weight>
240where
241    Children::Assoc: IChildren<Node> + 'a,
242    <Children::Assoc as IChildren<Node>>::Node: 'a,
243{
244    iterator: <Children::Assoc as IChildren<Node>>::IterMut<'a>,
245    start: usize,
246    end: usize,
247    _marker: core::marker::PhantomData<Weight>,
248}
249
250pub struct IntersectionMut<
251    'a,
252    Children: IChildrenProvider<Node>,
253    Node: IKeyExprTreeNode<Weight>,
254    Weight,
255> where
256    Children::Assoc: IChildren<Node> + 'a,
257{
258    key: &'a keyexpr,
259    ke_indices: Vec<usize>,
260    iterators: Vec<StackFrameMut<'a, Children, Node, Weight>>,
261}
262
263impl<'a, Children: IChildrenProvider<Node>, Node: IKeyExprTreeNode<Weight>, Weight> core::fmt::Debug
264    for IntersectionMut<'a, Children, Node, Weight>
265where
266    Children::Assoc: IChildren<Node> + 'a,
267{
268    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
269        f.debug_struct("IntersectionMut")
270            .field("key", &self.key)
271            .field("ke_indices_len", &self.ke_indices.len())
272            .field("depth", &self.iterators.len())
273            .finish()
274    }
275}
276
277impl<'a, Children: IChildrenProvider<Node>, Node: IKeyExprTreeNode<Weight>, Weight>
278    IntersectionMut<'a, Children, Node, Weight>
279where
280    Children::Assoc: IChildren<Node> + 'a,
281{
282    pub(crate) fn new(children: &'a mut Children::Assoc, key: &'a keyexpr) -> Self {
283        let mut ke_indices = Vec::with_capacity(32);
284        ke_indices.push(0);
285        let mut iterators = Vec::with_capacity(16);
286        iterators.push(StackFrameMut {
287            iterator: children.children_mut(),
288            start: 0,
289            end: 1,
290            _marker: Default::default(),
291        });
292        Self {
293            key,
294            ke_indices,
295            iterators,
296        }
297    }
298}
299
300impl<
301        'a,
302        Children: IChildrenProvider<Node>,
303        Node: IKeyExprTreeNodeMut<Weight, Children = Children::Assoc> + 'a,
304        Weight,
305    > Iterator for IntersectionMut<'a, Children, Node, Weight>
306where
307    Children::Assoc: IChildren<Node> + 'a,
308{
309    type Item = &'a mut <Children::Assoc as IChildren<Node>>::Node;
310    fn next(&mut self) -> Option<Self::Item> {
311        loop {
312            let StackFrameMut {
313                iterator,
314                start,
315                end,
316                _marker,
317            } = self.iterators.last_mut()?;
318            match iterator.next() {
319                Some(node) => {
320                    let mut node_matches = false;
321                    let new_start = *end;
322                    let mut new_end = *end;
323                    macro_rules! push {
324                        ($index: expr) => {
325                            let index = $index;
326                            if new_end == new_start || self.ke_indices[new_end - 1] < index {
327                                self.ke_indices.push(index);
328                                new_end += 1;
329                            }
330                        };
331                    }
332                    let chunk = node.chunk();
333                    let chunk_is_verbatim = chunk.first_byte() == b'@';
334                    if unlikely(chunk.as_bytes() == b"**") {
335                        // If the current node is `**`, it is guaranteed to match...
336                        node_matches = true;
337                        // and may consume any number of chunks from the KE
338                        push!(self.ke_indices[*start]);
339                        if self.key.len() != self.ke_indices[*start] {
340                            if self.key.as_bytes()[self.ke_indices[*start]] != b'@' {
341                                for i in self.ke_indices[*start]..self.key.len() {
342                                    if self.key.as_bytes()[i] == b'/' {
343                                        push!(i + 1);
344                                        if self.key.as_bytes()[i + 1] == b'@' {
345                                            node_matches = false; // ...unless the KE contains a verbatim chunk.
346                                            break;
347                                        }
348                                    }
349                                }
350                            } else {
351                                node_matches = false;
352                            }
353                        }
354                    } else {
355                        // The current node is not `**`
356                        // For all candidate chunks of the KE
357                        for i in *start..*end {
358                            // construct that chunk, while checking whether or not it's the last one
359                            let kec_start = self.ke_indices[i];
360                            if unlikely(kec_start == self.key.len()) {
361                                break;
362                            }
363                            let key = &self.key.as_bytes()[kec_start..];
364                            match key.iter().position(|&c| c == b'/') {
365                                Some(kec_end) => {
366                                    // If we aren't in the last chunk
367                                    let subkey =
368                                        // SAFETY: upheld by the surrounding invariants and prior validation.
369                                        unsafe { keyexpr::from_slice_unchecked(&key[..kec_end]) };
370                                    if unlikely(subkey.as_bytes() == b"**") {
371                                        if !chunk_is_verbatim {
372                                            // If the query chunk is `**`:
373                                            // children will have to process it again
374                                            push!(kec_start);
375                                        }
376                                        // and we need to process this chunk as if the `**` wasn't there,
377                                        // but with the knowledge that the next chunk won't be `**`.
378                                        let post_key = &key[kec_end + 1..];
379                                        match post_key.iter().position(|&c| c == b'/') {
380                                            Some(sec_end) => {
381                                                // SAFETY: upheld by the surrounding invariants and prior validation.
382                                                let post_key = unsafe {
383                                                    keyexpr::from_slice_unchecked(
384                                                        &post_key[..sec_end],
385                                                    )
386                                                };
387                                                if post_key.intersects(chunk) {
388                                                    push!(kec_start + kec_end + sec_end + 2);
389                                                }
390                                            }
391                                            None => {
392                                                // SAFETY: upheld by the surrounding invariants and prior validation.
393                                                if unsafe {
394                                                    keyexpr::from_slice_unchecked(post_key)
395                                                }
396                                                .intersects(chunk)
397                                                {
398                                                    push!(self.key.len());
399                                                    node_matches = true;
400                                                }
401                                            }
402                                        }
403                                    } else if chunk.intersects(subkey) {
404                                        push!(kec_start + kec_end + 1);
405                                    }
406                                }
407                                None => {
408                                    // If it's the last chunk of the query, check whether it's `**`
409                                    // SAFETY: upheld by the surrounding invariants and prior validation.
410                                    let key = unsafe { keyexpr::from_slice_unchecked(key) };
411                                    if unlikely(key.as_bytes() == b"**") && !chunk_is_verbatim {
412                                        // If yes, it automatically matches, and must be reused from now on for iteration.
413                                        push!(kec_start);
414                                        node_matches = true;
415                                    } else if chunk.intersects(key) {
416                                        // else, if it intersects with the chunk, make sure the children of the node
417                                        // are searched for `**`
418                                        push!(self.key.len());
419                                        node_matches = true;
420                                    }
421                                }
422                            }
423                        }
424                    }
425                    if new_end != new_start {
426                        for &i in &self.ke_indices[new_start..new_end] {
427                            if &self.key.as_bytes()[i..] == b"**" {
428                                node_matches = true;
429                                break;
430                            }
431                        }
432                        // SAFETY: upheld by the surrounding invariants and prior validation.
433                        let iterator = unsafe { &mut *(node.as_node_mut() as *mut Node) }
434                            .children_mut()
435                            .children_mut();
436                        self.iterators.push(StackFrameMut {
437                            iterator,
438                            start: new_start,
439                            end: new_end,
440                            _marker: Default::default(),
441                        })
442                    }
443                    if node_matches {
444                        return Some(node);
445                    }
446                }
447                None => {
448                    if let Some(StackFrameMut { start, .. }) = self.iterators.pop() {
449                        self.ke_indices.truncate(start);
450                    }
451                }
452            }
453        }
454    }
455}