nautilus_common/msgbus/matching.rs
1// -------------------------------------------------------------------------------------------------
2// Copyright (C) 2015-2025 2Posei Systems Pty Ltd. All rights reserved.
3// https://poseitrader.io
4//
5// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6// You may not use this file except in compliance with the License.
7// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use super::core::{MStr, Pattern, Topic};
17
18/// Match a topic and a string pattern using iterative backtracking algorithm
19/// pattern can contains -
20/// '*' - match 0 or more characters after this
21/// '?' - match any character once
22/// 'a-z' - match the specific character
23pub fn is_matching_backtracking(topic: MStr<Topic>, pattern: MStr<Pattern>) -> bool {
24 let topic_bytes = topic.as_bytes();
25 let pattern_bytes = pattern.as_bytes();
26
27 is_matching(topic_bytes, pattern_bytes)
28}
29
30#[must_use]
31pub fn is_matching(topic: &[u8], pattern: &[u8]) -> bool {
32 // Stack to store states for backtracking (topic_idx, pattern_idx)
33 let mut stack = vec![(0, 0)];
34
35 while let Some((mut i, mut j)) = stack.pop() {
36 loop {
37 // Found a match if we've consumed both strings
38 if i == topic.len() && j == pattern.len() {
39 return true;
40 }
41
42 // If we've reached the end of the pattern, break to try other paths
43 if j == pattern.len() {
44 break;
45 }
46
47 // Handle '*' wildcard
48 if pattern[j] == b'*' {
49 // Try skipping '*' entirely first
50 stack.push((i, j + 1));
51
52 // Continue with matching current character and keeping '*'
53 if i < topic.len() {
54 i += 1;
55 continue;
56 }
57 break;
58 }
59 // Handle '?' or exact character match
60 else if i < topic.len() && (pattern[j] == b'?' || topic[i] == pattern[j]) {
61 // Continue matching linearly without stack operations
62 i += 1;
63 j += 1;
64 continue;
65 }
66
67 // No match found in current path
68 break;
69 }
70 }
71
72 false
73}