zenoh_keyexpr/key_expr/
fuzzer.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//
14use super::OwnedKeyExpr;
15
16fn random_chunk(rng: &'_ mut impl rand::Rng) -> impl Iterator<Item = u8> + '_ {
17    let n = rng.gen_range(1..3);
18    rng.gen_bool(0.05)
19        .then_some(b'@')
20        .into_iter()
21        .chain((0..n).map(move |_| rng.sample(rand::distributions::Uniform::from(b'a'..b'c'))))
22}
23
24fn make(ke: &mut Vec<u8>, rng: &mut impl rand::Rng) {
25    let mut iters = 0;
26    loop {
27        let n = rng.sample(rand::distributions::Uniform::<u8>::from(0..=255));
28        match n {
29            0..=15 => ke.extend(b"/**"),
30            16..=31 => ke.extend(b"/*"),
31            32..=47 => {
32                if !ke.is_empty() && !ke.ends_with(b"*") {
33                    ke.extend(b"$*")
34                } else {
35                    continue;
36                }
37            }
38            48.. => {
39                if n >= 128 || ke.ends_with(b"**") || ke.ends_with(b"/*") {
40                    ke.push(b'/')
41                }
42                ke.extend(random_chunk(rng))
43            }
44        }
45        if n % (10 - iters) == 0 {
46            return;
47        }
48        iters += 1;
49    }
50}
51
52pub struct KeyExprFuzzer<Rng: rand::Rng>(pub Rng);
53impl<Rng: rand::Rng> Iterator for KeyExprFuzzer<Rng> {
54    type Item = OwnedKeyExpr;
55    fn next(&mut self) -> Option<Self::Item> {
56        let mut next = Vec::new();
57        make(&mut next, &mut self.0);
58        let mut next = String::from_utf8(next).unwrap();
59        if let Some(n) = next.strip_prefix('/').map(ToOwned::to_owned) {
60            next = n
61        }
62        Some(OwnedKeyExpr::autocanonize(next).unwrap())
63    }
64}