swift_check/
lib.rs

1//! ```
2//! use swift_check::{search, range, one_of, any, all, eq, not};
3//!
4//! let input = b"
5//!     swift-check is a high performance library for searching or validating data based on \
6//!     expressive conditions
7//! ";
8//!
9//! let cond = all!(
10//!     all!(any!(range!(b'a'..=b'z'), range!(b'A'..=b'Z'), eq(b' ')), not(range!(b'0'..=b'9'))),
11//!     one_of!(eq(b' '), range!(b'a'..=b'z'), range!(b'a'..=b'z'))
12//! );
13//!
14//! let Some(first_space) = search(input, cond) else {
15//!     unreachable!("There's a space!")
16//! };
17//!
18//! assert_eq!(input[first_space], b' ');
19//!
20//! // or, more simply
21//!
22//! let Some(first_space2) = search(input, eq(b' ')) else {
23//!     unreachable!("There's a space!")
24//! };
25//!
26//! assert_eq!(input[first_space2], b' ');
27//! assert_eq!(first_space2, first_space);
28//! ```
29#![allow(unused_unsafe, unused_parens)] // fallback
30#![cfg_attr(not(any(test, mirai, feature = "verify", feature = "std")), no_std)]
31#![cfg_attr(not(any(test, mirai, feature = "verify")), no_builtins)]
32#![cfg_attr(docsrs, feature(doc_auto_cfg))]
33
34pub mod arch;
35
36#[cfg(feature = "require")]
37pub mod require;
38
39use arch::Vector;
40
41/// Check that the condition holds for all bytes
42///
43/// # Arguments
44///
45/// * `data` - The `Vector` to check each element of
46/// * `cond` - The condition to check
47///
48/// # Returns
49///
50/// `true` if the `cond` was satisfied for each byte, `false` otherwise.
51///
52/// # Example
53///
54/// ```
55/// use swift_check::{ensure, any, eq, arch::load};
56///
57/// let input = b"2112111211211211";
58/// let data = load(input);
59///
60/// let two_or_one = ensure(data, any!(eq(b'1'), eq(b'2')));
61/// assert!(two_or_one);
62///
63/// let should_fail = ensure(data, eq(b'1'));
64/// assert!(!should_fail);
65/// ```
66///
67/// **Note**: This is part of the lower-level api, for better ergonomics see [`for_all_ensure`] or
68/// [`for_all_ensure_ct`] if branching on the data's contents is unacceptable for security reasons.
69#[inline(always)] #[must_use]
70pub fn ensure(data: Vector, cond: impl Fn(Vector) -> Vector) -> bool {
71    unsafe { arch::MoveMask::new(cond(data)).all_bits_set() }
72}
73
74#[macro_export] #[doc(hidden)]
75macro_rules! ensure {
76    ($data:expr, $cond:expr) => {
77        unsafe { $crate::arch::MoveMask::new($cond($data)).all_bits_set() }
78    };
79}
80
81/// Find the first circumstance of the condition being met
82///
83/// # Arguments
84///
85/// * `data` - The `Vector` to search
86/// * `cond` - The condition to find
87///
88/// # Returns
89///
90/// - `Some(position)`: The first position of the condition being met.
91/// - `None`: The condition was never met.
92///
93/// # Example
94///
95/// ```
96/// use swift_check::{arch::load, find, eq};
97///
98/// let input = b"aaaaaaaaaaaaaaaB";
99/// let data = load(input);
100///
101/// if let Some(position) = find(data, eq(b'B')) {
102///     assert_eq!(position as usize, input.len() - 1);
103/// } else {
104///     unreachable!("B is within the data");
105/// }
106/// ```
107///
108/// **Note**: This is part of the lower-level api, for better ergonomics see [`search`].
109#[inline(always)]
110pub fn find(data: Vector, cond: impl Fn(Vector) -> Vector) -> Option<u32> {
111    let len = unsafe { arch::MoveMask::new(cond(data)).trailing_zeros() };
112    if len >= arch::MoveMask::MAX_TRAIL { None } else { Some(len) }
113}
114
115#[doc(hidden)] #[macro_export]
116macro_rules! __is_found {
117    ($cond_eval:expr, |$len:ident| $then:expr, || $otherwise:expr) => { unsafe {
118        let $len = $crate::arch::MoveMask::new($cond_eval).trailing_zeros();
119        if $len == $crate::arch::MoveMask::MAX_TRAIL { $otherwise } else { $then }
120    }};
121}
122
123#[doc(hidden)] #[macro_export]
124macro_rules! find {
125    ($data:expr, $cond:expr) => {
126        $crate::__is_found!($cond($data), |__len| Some(__len), || None)
127    };
128}
129
130/// For all the bytes, ensure that the condition holds
131///
132/// # Security
133///
134/// Unlike [`for_all_ensure`] this will continue scanning even if the condition has failed, this
135/// is to reduce information leakage. While this doesn't branch on the data, we cannot assure it is
136/// perfect constant time for all architectures, so if you're using this on secrets please use tools
137/// such as `dudect` to verify that said usage is acceptable.
138///
139/// # Arguments
140///
141/// * `data` - The data to validate
142/// * `cond` - The condition to validate with, this should be some composition of the conditions
143///            exposed within this crate.
144///
145/// # Returns
146///
147/// `true` if every byte in `data` met the `cond`, `false` otherwise.
148///
149/// # Example
150///
151/// ```
152/// use swift_check::{for_all_ensure_ct, any, range, eq};
153///
154/// let input = b"hello world I am valid input";
155/// // everything must be a lowercase character outside I and space
156/// let res = for_all_ensure_ct(input, any!(range!(b'a'..=b'z'), eq(b'I'), eq(b' ')));
157/// assert!(res);
158///
159/// let input = b"Hello world I am invalid input";
160/// // has capital H, will fail
161/// let should_fail = for_all_ensure_ct(input, any!(range!(b'a'..=b'z'), eq(b'I'), eq(b' ')));
162/// assert!(!should_fail);
163/// ```
164#[inline] #[must_use]
165pub fn for_all_ensure_ct(data: &[u8], cond: impl Fn(Vector) -> Vector) -> bool {
166    let mut valid = true;
167    if data.len() >= arch::WIDTH {
168        unsafe { arch::scan::for_all_ensure_ct(data, cond, &mut valid) }
169    } else {
170        // This is a temporary solution, in the future conditions will support both simd and byte
171        // by byte checks
172        valid &= unsafe {
173            arch::MoveMask::new(cond(arch::load_partial(data, data.len())))
174                .trailing_ones() >= data.len() as u32
175        };
176    }
177
178    valid
179}
180
181/// For all the bytes, ensure that the condition holds
182///
183/// # Arguments
184///
185/// * `data` - The data to validate
186/// * `cond` - The condition to validate with, this should be some composition of the conditions
187///            exposed within this crate.
188///
189/// # Returns
190///
191/// `true` if every byte in `data` met the `cond`, `false` otherwise.
192///
193/// # Performance
194///
195/// For anything less than 16 bytes you will not benefit from using this, in fact for simple
196/// conditions a branchless search will outperform this by a good amount.
197///
198/// ```
199/// use swift_check::{for_all_ensure, any, range, eq};
200///
201/// let input = b"hello world I am valid input";
202/// // everything must be a lowercase character outside I and space
203/// let res = for_all_ensure(input, any!(range!(b'a'..=b'z'), eq(b'I'), eq(b' ')));
204/// assert!(res);
205///
206/// let input = b"Hello world I am invalid input";
207/// // has capital H, will fail
208/// let should_fail = for_all_ensure(input, any!(range!(b'a'..=b'z'), eq(b'I'), eq(b' ')));
209/// assert!(!should_fail);
210/// ```
211#[inline] #[must_use]
212pub fn for_all_ensure(data: &[u8], cond: impl Fn(Vector) -> Vector) -> bool {
213    if data.len() >= arch::WIDTH {
214        unsafe { arch::scan::for_all_ensure(data, cond) }
215    } else {
216        unsafe {
217            arch::MoveMask::new(cond(arch::load_partial(data, data.len())))
218                .trailing_ones() >= data.len() as u32
219        }
220    }
221}
222
223/// Find the first byte that meets the `cond`
224///
225/// # Arguments
226///
227/// * `data` - The haystack to search
228/// * `cond` - The condition to find the first occurrence of
229///
230/// # Returns
231///
232/// - `Some(position)` - The first position where the condition was met, this position will always
233///   be less than `data.len()` ensuring that indexing it will never panic. If you're interested in
234///   further investigating this claim see `arch/simd_scan.rs`.
235/// - `None` - There was no circumstance of the condition being met.
236///
237/// # Example
238///
239/// ```
240/// use swift_check::{search, eq};
241///
242/// let input = b"some data with a 5 burger 383294 hello world blah blah blah";
243/// if let Some(pos) = search(input, eq(b'5')) {
244///     assert_eq!(input[pos], b'5');
245/// } else {
246///     unreachable!("input contained a 5");
247/// }
248/// ```
249#[inline]
250pub fn search(data: &[u8], cond: impl Fn(Vector) -> Vector) -> Option<usize> {
251    if data.len() >= arch::WIDTH {
252        unsafe { arch::scan::search(data, cond) }
253    } else {
254        match unsafe { arch::MoveMask::new(cond(arch::load_partial(data, data.len()))).trailing_zeros() } {
255            offset if offset < data.len() as u32 => Some(offset as usize),
256            _ => None
257        }
258    }
259}
260
261/// Ensure min is less than max at compile time
262#[doc(hidden)] #[macro_export]
263macro_rules! comp_check_rng {
264    ($min:literal, $max:literal, $do:expr) => {
265        match ($min, $max) {
266            ($min..=$max, $min..=$max) | _ => $do
267        }
268    };
269}
270
271/// Check that the value is within the specified range
272///
273/// # Between
274///
275/// Inclusive
276/// ```no
277/// range!(5..=20)
278/// ```
279/// Exclusive
280/// ```no
281/// range!(5..20)
282/// ```
283///
284/// ### Example
285///
286/// ```
287/// use swift_check::{ensure, range, arch::load};
288///
289/// let input = b"abcdefghijklmnop";
290/// let data = load(input);
291///
292/// let is_lowercase_alphabet = ensure!(data, range!(b'a'..=b'z'));
293/// assert!(is_lowercase_alphabet);
294///
295/// let input = b"Abcdefghijklmnop";
296/// let data = load(input);
297///
298/// let is_lowercase_alphabet = ensure!(data, range!(b'a'..=b'z'));
299/// assert!(!is_lowercase_alphabet);
300///
301/// let data = load(b"bbcdefghijklmnop");
302/// // exclusive range
303/// let is_not_a_or_z = ensure!(data, range!(b'a'..b'z'));
304/// assert!(is_not_a_or_z);
305///
306/// let data = load(b"abbbbbbbbbbbbbbz");
307/// let should_fail = ensure!(data, range!(b'a'..b'z'));
308///
309/// assert!(!should_fail);
310/// ```
311///
312/// # Less or Greater Than
313///
314/// Less than
315/// ```no
316/// range!(< 20)
317/// ```
318/// Less than or eq
319/// ```no
320/// range!(<= 20)
321/// ```
322/// Greater than
323/// ```no
324/// range!(> 20)
325/// ```
326/// Greater than or eq
327/// ```no
328/// range!(>= 20)
329/// ```
330#[macro_export]
331macro_rules! range {
332    ($min:literal..=$max:literal) => {
333        $crate::comp_check_rng!($min, $max, $crate::arch::range::<$min, $max>())
334    };
335    ($min:literal..$max:literal) => {
336        $crate::comp_check_rng!($min, $max, $crate::arch::exclusive_range::<$min, $max>())
337    };
338    (<= $max:literal) => {
339        $crate::arch::less_than_or_eq::<$max>()
340    };
341    (< $max:literal) => {
342        $crate::arch::less_than::<$max>()
343    };
344    (>= $min:literal) => {
345        $crate::arch::greater_than_or_eq::<$min>()
346    };
347    (> $min:literal) => {
348        $crate::arch::greater_than::<$min>()
349    };
350}
351
352/// Check if the bytes are equal to `expected`
353///
354/// # Arguments
355///
356/// * `expected` - The value you expect
357///
358/// # Example
359///
360/// ```
361/// use swift_check::{ensure, any, eq, arch::load};
362///
363/// let input = b"1111111111111111";
364/// let data = load(input);
365///
366/// let has_one = ensure!(data, eq(b'1'));
367/// assert!(has_one);
368///
369/// let input = b"2112111211211211";
370/// let data = load(input);
371///
372/// let has_one_or_two = ensure!(data, any!(eq(b'1'), eq(b'2')));
373/// assert!(has_one_or_two);
374///
375/// let input = b"3452111211211211";
376/// let data = load(input);
377///
378/// let should_fail = ensure!(data, any!(eq(b'1'), eq(b'2')));
379/// assert!(!should_fail);
380/// ```
381#[inline(always)]
382pub const fn eq(expected: u8) -> impl Fn(Vector) -> Vector {
383    move |data| unsafe { arch::eq(data, arch::splat(expected)) }
384}
385
386/// Negate a condition
387///
388/// # Arguments
389///
390/// * `cond` - The condition to negate
391///
392/// # Example
393///
394/// ```
395/// use swift_check::{ensure, range, not, arch::load};
396///
397/// let input = b"abcdefghijklmnop";
398/// let data = load(input);
399///
400/// let no_numbers = ensure!(data, not(range!(b'0'..=b'9')));
401/// assert!(no_numbers);
402///
403/// let input = b"abcdefghijklmno1";
404/// let data = load(input);
405///
406/// let should_fail = ensure!(data, not(range!(b'0'..=b'9')));
407/// assert!(!should_fail);
408/// ```
409#[inline(always)]
410pub const fn not(cond: impl Fn(Vector) -> Vector) -> impl Fn(Vector) -> Vector {
411    move |data| unsafe { arch::not(cond(data)) }
412}
413
414/// Combine two conditions
415///
416/// # Arguments
417///
418/// * `a` - The lhs condition
419/// * `b` - The rhs condition
420///
421/// # Example
422///
423/// ```
424/// use swift_check::{ensure, range, eq, not, and, arch::load};
425///
426/// let input = b"1112221111111111";
427/// let data = load(input);
428///
429/// let is_num_but_not_5 = ensure!(data, and(range!(b'0'..=b'9'), not(eq(b'5'))));
430/// assert!(is_num_but_not_5);
431///
432/// let input = b"5112221111111111";
433/// let data = load(input);
434///
435/// let should_fail = ensure!(data, and(range!(b'0'..=b'9'), not(eq(b'5'))));
436/// assert!(!should_fail);
437/// ```
438#[inline(always)]
439pub const fn and(
440    a: impl Fn(Vector) -> Vector,
441    b: impl Fn(Vector) -> Vector
442) -> impl Fn(Vector) -> Vector {
443    move |data| unsafe { arch::and(a(data), b(data)) }
444}
445
446/// Check that either condition is met
447///
448/// # Arguments
449///
450/// * `a` - The lhs condition
451/// * `b` - The rhs condition
452///
453/// # Example
454///
455/// ```
456/// use swift_check::{ensure, or, eq, arch::load};
457///
458/// let input = b"1113331111111111";
459/// let data = load(input);
460///
461/// let is_1_or_3 = ensure!(data, or(eq(b'1'), eq(b'3')));
462/// assert!(is_1_or_3);
463///
464/// let input = b"!113331111111111";
465/// let data = load(input);
466///
467/// let should_fail = ensure!(data, or(eq(b'1'), eq(b'3')));
468/// assert!(!should_fail);
469/// ```
470#[inline(always)]
471pub const fn or(
472    a: impl Fn(Vector) -> Vector,
473    b: impl Fn(Vector) -> Vector
474) -> impl Fn(Vector) -> Vector {
475    move |data| unsafe { arch::or(a(data), b(data)) }
476}
477
478/// Check that only one of the conditions are met
479///
480/// # Arguments
481///
482/// * `a` - The lhs condition
483/// * `b` - The rhs condition
484///
485/// # Example
486///
487/// ```
488/// use swift_check::{ensure, xor, range, arch::load};
489///
490/// let input = b"3333333377777777";
491/// let data = load(input);
492///
493/// let not_five = ensure!(data, xor(range!(b'0'..=b'5'), range!(b'5'..=b'9')));
494/// assert!(not_five);
495///
496/// let input = b"3333333557777777";
497/// let data = load(input);
498///
499/// let should_fail = ensure!(data, xor(range!(b'0'..=b'5'), range!(b'5'..=b'9')));
500/// assert!(!should_fail);
501/// ```
502#[inline(always)]
503pub const fn xor(
504    a: impl Fn(Vector) -> Vector,
505    b: impl Fn(Vector) -> Vector
506) -> impl Fn(Vector) -> Vector {
507    move |data| unsafe { arch::xor(a(data), b(data)) }
508}
509
510#[macro_export]
511#[doc(hidden)]
512macro_rules! __all {
513    ($left:expr $(,)?) => {
514        $left
515    };
516    ($left:expr, $right:expr $(,)?) => {
517        $crate::arch::and($left, $right)
518    };
519    ($left:expr, $right:expr, $($rest:expr),+ $(,)?) => {
520        $crate::arch::and(
521            $crate::__all!($left, $right),
522            $crate::__all!($($rest),+)
523        )
524    };
525}
526
527/// Check that all the conditions hold
528///
529/// # Arguments
530///
531/// * `conditions` - The conditions to ensure all hold
532///
533/// # Example
534///
535/// ```
536/// use swift_check::{ensure, all, range, not, eq, arch::load};
537///
538/// let input = b"3333333377777777";
539/// let data = load(input);
540///
541/// let not_five = ensure!(data, all!(range!(b'0'..=b'9'), not(eq(b'5'))));
542/// assert!(not_five);
543///
544/// let input = b"3333333557777777";
545/// let data = load(input);
546///
547/// let should_fail = ensure!(data, all!(range!(b'0'..=b'9'), not(eq(b'5'))));
548/// assert!(!should_fail);
549/// ```
550#[macro_export]
551macro_rules! all {
552    // Base case: if only one argument is provided, simply return it
553    ($left:expr $(,)? ) => {
554        $left
555    };
556    // Two arguments: directly apply AND between them
557    ($left:expr, $right:expr $(,)?) => {
558        |data: $crate::arch::Vector| -> $crate::arch::Vector {
559            #[allow(unused_unsafe)]
560            unsafe { $crate::__all!($left(data), $right(data)) }
561        }
562    };
563    ($left:expr, $right:expr, $($rest:expr),+ $(,)?) => {
564        |data: $crate::arch::Vector| -> $crate::arch::Vector {
565            #[allow(unused_unsafe)]
566            unsafe { $crate::__all!($left(data), $right(data), $($rest(data)),+) }
567        }
568    };
569}
570
571#[doc(hidden)] #[macro_export]
572macro_rules! __or {
573    ($left:expr) => {
574        // or against nothing, so just return left
575        $left
576    };
577    ($left:expr, $right:expr $(,)?) => {
578        $crate::arch::or($left, $right)
579    };
580    ($left:expr, $right:expr, $($rest:expr),+ $(,)?) => {
581        $crate::arch::or(
582            $crate::__or!($left, $right),
583            $crate::__or!($($rest),+)
584        )
585    };
586}
587
588/// Check that any of the conditions hold
589///
590/// This is practically [`or`], just can handle any number of conditions
591///
592/// # Arguments
593///
594/// * `conditions` - The conditions where at least one must hold
595///
596/// # Example
597///
598/// ```
599/// use swift_check::{ensure, any, eq, arch::load};
600///
601/// let input = b"3333333377777777";
602/// let data = load(input);
603///
604/// let three_or_seven = ensure!(data, any!(eq(b'3'), eq(b'7')));
605/// assert!(three_or_seven);
606///
607/// let input = b"3333333557777777";
608/// let data = load(input);
609///
610/// let should_fail = ensure!(data, any!(eq(b'3'), eq(b'7')));
611/// assert!(!should_fail);
612/// ```
613#[macro_export]
614macro_rules! any {
615    ($left:expr $(,)?) => {
616        // any, all, etc are not leaf conditions, therefore we can just return left as it should
617        // already be a higher order function
618        $left
619    };
620    ($left:expr, $right:expr $(,)?) => {
621        |data: $crate::arch::Vector| -> $crate::arch::Vector {
622            #[allow(unused_unsafe)]
623            unsafe { $crate::__or!($left(data), $right(data)) }
624        }
625    };
626    ($left:expr, $right:expr, $($rest:expr),+ $(,)?) => {
627        |data: $crate::arch::Vector| -> $crate::arch::Vector {
628            #[allow(unused_unsafe)]
629            unsafe { $crate::__or!($left(data), $right(data), $($rest(data)),+) }
630        }
631    }
632}
633
634#[doc(hidden)] #[macro_export]
635macro_rules! __xor {
636    ($left:expr $(,)?) => {
637        $left
638    };
639    ($left:expr, $right:expr $(,)?) => {
640        $crate::arch::xor($left, $right)
641    };
642    ($left:expr, $right:expr, $($rest:expr),+ $(,)?) => {
643        $crate::arch::xor(
644            $crate::__xor!($left, $right),
645            $crate::__xor!($($rest),+)
646        )
647    };
648}
649
650#[macro_export] #[doc(hidden)]
651macro_rules! __one_of {
652    ($l_i:ident: $left:expr, $r_i:ident: $right:expr, $($rest:ident: $cond:expr),* $(,)?) => {
653        |data: $crate::arch::Vector| -> $crate::arch::Vector {
654            #[allow(unused_unsafe)]
655            unsafe {
656                let ($l_i, $r_i, $($rest),+) = ($left(data), $right(data), $($cond(data)),+);
657                // combine xor and nand to ensure only one cond held, this property is ensured for
658                // up to 4 conditions.
659                $crate::arch::and(
660                    $crate::__xor!($l_i, $r_i, $($rest),+),
661                    $crate::arch::not($crate::__all!($l_i, $r_i, $($rest),+))
662                )
663            }
664        }
665    };
666}
667
668/// Ensure only one of the conditions are true
669///
670/// # Arguments
671///
672/// * `condition`, ... - The conditions to check, only allowing one to hold (up to 4)
673///
674/// # Example
675///
676/// ```
677/// use swift_check::{one_of, for_all_ensure, eq, range};
678///
679/// let input = b"123456789";
680/// let char_or_num = for_all_ensure(
681///     input, one_of!(range!(b'0'..=b'9'), range!(b'a'..=b'z'), range!(b'A'..=b'Z'))
682/// );
683/// assert!(char_or_num);
684///
685/// let should_fail = for_all_ensure(
686///     input,
687///     one_of!(range!(b'0'..=b'9'), range!(b'0'..=b'9'), range!(b'0'..=b'9'))
688/// );
689/// assert!(!should_fail)
690/// ```
691#[macro_export]
692macro_rules! one_of {
693    ($first:expr $(,)?) => {
694        $first
695    };
696    ($first:expr, $second:expr $(,)?) => {
697        |data: $crate::arch::Vector| -> $crate::arch::Vector {
698            #[allow(unused_unsafe)]
699            unsafe { $crate::__xor!($first(data), $second(data)) }
700        }
701    };
702    ($first:expr, $second:expr, $third:expr $(,)?) => {
703        $crate::__one_of!(first: $first, second: $second, third: $third)
704    };
705    ($first:expr, $second:expr, $third:expr, $fourth:expr $(,)?) => {
706        $crate::__one_of!(first: $first, second: $second, third: $third, fourth: $fourth)
707    };
708}
709
710#[cfg(all(test, not(mirai)))]
711mod tests {
712    use super::*;
713    use quickcheck::quickcheck;
714
715    extern crate alloc;
716    use alloc::string::String;
717    use alloc::vec::Vec;
718
719    #[test]
720    fn for_all_ensure_range_is_inclusive() {
721        let input = b"hello world";
722        let res = for_all_ensure(input, any!(range!(0..=127), eq(b' ')));
723        assert!(res);
724    }
725
726    macro_rules! one_of_eq {
727        ($($lit:literal),* $(,)?) => {
728            one_of!($(eq($lit)),*)
729        };
730    }
731
732    macro_rules! ensure_one_of {
733        ($input:ident, $($lit:literal),* $(,)?) => {
734            ensure!($input, one_of_eq!($($lit),*))
735        };
736    }
737
738    macro_rules! failure_perms {
739        ($input:ident, $f_val:literal, $a_val:literal) => {{
740            assert!(!ensure_one_of!($input, $f_val, $f_val));
741            assert!(!ensure_one_of!($input, $f_val, $f_val, $f_val));
742            assert!(!ensure_one_of!($input, $f_val, $f_val, $f_val, $f_val));
743
744            assert!(!ensure_one_of!($input, $f_val, $f_val, $a_val, $a_val));
745            assert!(!ensure_one_of!($input, $a_val, $a_val, $f_val, $f_val));
746
747            assert!(!ensure_one_of!($input, $a_val, $f_val, $a_val, $f_val));
748            assert!(!ensure_one_of!($input, $f_val, $a_val, $f_val, $a_val));
749
750            assert!(!ensure_one_of!($input, $f_val, $a_val, $a_val, $f_val));
751            assert!(!ensure_one_of!($input, $a_val, $f_val, $f_val, $a_val));
752        }};
753    }
754
755    #[test]
756    fn one_of_permutations() {
757        let input = arch::load(&[0u8; 16]);
758        failure_perms!(input, 0, 0);
759        failure_perms!(input, 0, 1);
760        failure_perms!(input, 1, 0);
761
762        assert!(ensure_one_of!(input, 0, 1, 1, 1));
763        assert!(ensure_one_of!(input, 1, 0, 1, 1));
764        assert!(ensure_one_of!(input, 1, 1, 0, 1));
765        assert!(ensure_one_of!(input, 1, 1, 1, 0));
766        assert!(ensure_one_of!(input, 0, 1, 1));
767        assert!(ensure_one_of!(input, 1, 0, 1));
768        assert!(ensure_one_of!(input, 1, 1, 0));
769        assert!(ensure_one_of!(input, 0, 1));
770        assert!(ensure_one_of!(input, 1, 0));
771        assert!(ensure_one_of!(input, 0));
772        assert!(!ensure_one_of!(input, 1));
773
774        // none true
775
776        assert!(!ensure_one_of!(input, 1, 1, 1, 1));
777        assert!(!ensure_one_of!(input, 1, 1, 1));
778        assert!(!ensure_one_of!(input, 1, 1));
779        assert!(!ensure_one_of!(input, 1));
780    }
781
782    macro_rules! one_of_nest {
783        ($($($f_lit:literal),*);* $(;)?) => {
784            one_of!(
785                $(one_of_eq!($($f_lit),*)),+
786            )
787        };
788    }
789
790    #[test]
791    fn one_of_nesting() {
792        let input = arch::load(&[1u8; 16]);
793
794        assert!(ensure!(input, one_of_nest!(
795            1, 0, 0, 0;
796            0, 0, 0, 0;
797            0, 0, 0, 0;
798            0, 0, 0, 0
799        )));
800
801        assert!(!ensure!(input, one_of_nest!(1; 1)));
802
803        assert!(ensure!(input, one_of_nest!(
804            0, 0, 0, 0; // fails
805            1, 1, 1, 1; // fails
806            1, 0, 0, 0  // succeeds therefore true
807        )));
808    }
809
810    macro_rules! ensure_test {
811        (
812            $input:expr, $condition:expr,
813            |$success_byte:ident| $validate_positive:expr,
814            |$failure_byte:ident| $validate_negative:expr
815        ) => {
816            if for_all_ensure($input, $condition) {
817                let mut true_success = true;
818                for $success_byte in $input {
819                    true_success &= $validate_positive;
820                }
821                true_success
822            } else {
823                let mut should_have_failed = false;
824                for $failure_byte in $input {
825                    should_have_failed |= $validate_negative
826                }
827                should_have_failed
828            }
829        };
830    }
831
832    macro_rules! search_test {
833        (
834            $input:expr,
835            $condition:expr, |$pos:ident| $cond_met_assertion:expr,
836            |$byte:ident| $cond_failed_assertion:expr
837        ) => {{
838            if let Some($pos) = search($input, $condition) {
839                if $pos >= $input.len() {
840                    panic!(
841                        "search should never return greater than or eq len. \n{:?}\t{:?} >= {}\t{}",
842                        $input, $pos, $input.len(), stringify!($condition)
843                    );
844                }
845                $cond_met_assertion
846            } else {
847                let mut actually_failed = true;
848                for $byte in $input {
849                    actually_failed &= $cond_failed_assertion;
850                }
851                actually_failed
852            }
853        }};
854    }
855
856    macro_rules! cmp_test {
857        ($cmp:expr, $assert:pat, $input:ident) => {
858            search_test!(
859                $input.as_slice(),
860                $cmp, |pos| matches!($input[pos], $assert),
861                |byte| !matches!(byte, $assert)
862            )
863        };
864    }
865
866    macro_rules! range_test {
867        ($min:literal..=$max:literal, $input:ident) => {
868            cmp_test!(
869                range!($min..=$max), $min..=$max, $input
870            )
871        };
872    }
873
874    macro_rules! excl_r_test {
875        ($min:literal..$max:literal, $input:ident) => {
876            search_test!(
877                $input.as_slice(),
878                range!($min..$max), |pos| $input[pos] > $min && $input[pos] < $max,
879                |byte| !(byte > &$min && byte < &$max)
880            )
881        };
882    }
883
884    macro_rules! check {
885        ($test:expr, $message:expr) => {
886            if !$test {
887                println!("[{}:{}] Test failed: {}", file!(), line!(), $message);
888                return false;
889            }
890        };
891        ($test:expr $(,)?) => {
892            check!($test, stringify!($test))
893        };
894    }
895
896    macro_rules! checks {
897        ($($test:expr),+ $(,)?) => {{
898            $(check!($test);)*
899            true
900        }};
901    }
902
903    quickcheck! {
904        fn search_for_needle(s: String) -> bool {
905            if let Some(pos) = search(s.as_bytes(), eq(b'a')) {
906                s.as_bytes()[pos] == b'a'
907            } else {
908                !s.contains("a")
909            }
910        }
911        fn search_for_number(s: String) -> bool {
912            if let Some(pos) = search(s.as_bytes(), range!(b'0'..=b'9')) {
913                s.as_bytes()[pos].is_ascii_digit()
914            } else {
915                let mut has_digits = false;
916                for byte in s.as_bytes() {
917                    has_digits |= byte.is_ascii_digit()
918                }
919                !has_digits
920            }
921        }
922        fn always_holds(s: String) -> bool {
923            for_all_ensure(s.as_bytes(), range!(0..=255))
924        }
925        fn range_large(s: Vec<u8>) -> bool {
926            checks!(
927                range_test!(10..=244, s),
928                range_test!(1..=254, s),
929                range_test!(54..=200, s),
930                range_test!(100..=200, s),
931                range_test!(0..=128, s),
932                range_test!(0..=129, s)
933            )
934        }
935        fn range_mid(s: Vec<u8>) -> bool {
936            checks!(
937                range_test!(100..=150, s),
938                range_test!(127..=129, s),
939                range_test!(127..=128, s),
940                range_test!(128..=129, s),
941                range_test!(120..=130, s)
942            )
943        }
944        fn range_small(s: Vec<u8>) -> bool {
945            checks!(
946                range_test!(200..=255, s),
947                range_test!(240..=255, s),
948                range_test!(255..=255, s),
949                range_test!(254..=255, s),
950                range_test!(130..=255, s),
951                range_test!(160..=255, s),
952                range_test!(1..=5, s),
953                range_test!(0..=1, s),
954                range_test!(0..=13, s),
955                range_test!(3..=60, s),
956                range_test!(30..=31, s)
957            )
958        }
959        fn excl_range_large(s: Vec<u8>) -> bool {
960            checks!(
961                excl_r_test!(10..245, s),
962                excl_r_test!(0..255, s),
963                excl_r_test!(200..254, s),
964                excl_r_test!(100..200, s),
965                excl_r_test!(60..160, s),
966                excl_r_test!(1..254, s)
967            )
968        }
969        fn excl_range_mid(s: Vec<u8>) -> bool {
970            checks!(
971                excl_r_test!(125..135, s),
972                excl_r_test!(110..140, s),
973                excl_r_test!(127..128, s),
974                excl_r_test!(128..129, s),
975                excl_r_test!(127..129, s),
976                excl_r_test!(126..129, s),
977                excl_r_test!(126..128, s),
978                excl_r_test!(128..130, s)
979            )
980        }
981        fn excl_range_small(s: Vec<u8>) -> bool {
982            checks!(
983                excl_r_test!(0..1, s),
984                excl_r_test!(0..2, s),
985                excl_r_test!(0..5, s),
986                excl_r_test!(254..255, s),
987                excl_r_test!(253..255, s),
988                excl_r_test!(250..255, s),
989                excl_r_test!(1..5, s),
990                excl_r_test!(2..5, s),
991                excl_r_test!(4..5, s),
992            )
993        }
994        fn less_than_always_false(s: Vec<u8>) -> bool {
995            search_test!(
996                s.as_slice(),
997                range!(< 0), |_cannot_happen| false,
998                |_na| true
999            )
1000        }
1001        fn less_than_large(s: Vec<u8>) -> bool {
1002            checks!(
1003                cmp_test!(range!(< 255), 0..=254, s),
1004                cmp_test!(range!(< 240), 0..=239, s),
1005                cmp_test!(range!(< 254), 0..=253, s),
1006                cmp_test!(range!(< 200), 0..=199, s),
1007                cmp_test!(range!(< 140), 0..=139, s)
1008            )
1009        }
1010        fn less_than_mid(s: Vec<u8>) -> bool {
1011            checks!(
1012                cmp_test!(range!(< 128), 0..=127, s),
1013                cmp_test!(range!(< 129), 0..=128, s),
1014                cmp_test!(range!(< 128), 0..=127, s),
1015                cmp_test!(range!(< 130), 0..=129, s)
1016            )
1017        }
1018        fn less_than_small(s: Vec<u8>) -> bool {
1019            checks!(
1020                cmp_test!(range!(< 30), 0..=29, s),
1021                cmp_test!(range!(< 1), 0, s),
1022                cmp_test!(range!(< 64), 0..=63, s),
1023                cmp_test!(range!(< 120), 0..=119, s)
1024            )
1025        }
1026        fn less_than_or_eq_small(s: Vec<u8>) -> bool {
1027            checks!(
1028                cmp_test!(range!(<= 30), 0..=30, s),
1029                cmp_test!(range!(<= 0), 0, s),
1030                cmp_test!(range!(<= 1), 0..=1, s),
1031                cmp_test!(range!(<= 2), 0..=2, s)
1032            )
1033        }
1034        fn less_than_or_eq_mid(s: Vec<u8>) -> bool {
1035            checks!(
1036                cmp_test!(range!(<= 128), 0..=128, s),
1037                cmp_test!(range!(<= 129), 0..=129, s),
1038                cmp_test!(range!(<= 127), 0..=127, s),
1039                cmp_test!(range!(<= 130), 0..=130, s)
1040            )
1041        }
1042        fn less_than_or_eq_large(s: Vec<u8>) -> bool {
1043            checks!(
1044                cmp_test!(range!(<= 254), 0..=254, s),
1045                cmp_test!(range!(<= 250), 0..=250, s),
1046                cmp_test!(range!(<= 200), 0..=200, s),
1047                cmp_test!(range!(<= 160), 0..=160, s)
1048            )
1049        }
1050        fn less_than_or_eq_always_true(s: Vec<u8>) -> bool {
1051            search_test!(
1052                s.as_slice(),
1053                range!(<= 255), |_always| true,
1054                |_never| false
1055            )
1056        }
1057        fn greater_than_always_false(s: Vec<u8>) -> bool {
1058            search_test!(
1059                s.as_slice(),
1060                range!(> 255), |_never| false,
1061                |_always| true
1062            )
1063        }
1064        fn greater_than_small(s: Vec<u8>) -> bool {
1065            checks!(
1066                cmp_test!(range!(> 254), 255, s),
1067                cmp_test!(range!(> 250), 251..=255, s),
1068                cmp_test!(range!(> 253), 254..=255, s),
1069                cmp_test!(range!(> 230), 231..=255, s),
1070                cmp_test!(range!(> 190), 191..=255, s)
1071            )
1072        }
1073        fn greater_than_mid(s: Vec<u8>) -> bool {
1074            checks!(
1075                cmp_test!(range!(> 128), 129..=255, s),
1076                cmp_test!(range!(> 127), 128..=255, s),
1077                cmp_test!(range!(> 129), 130..=255, s),
1078                cmp_test!(range!(> 125), 126..=255, s)
1079            )
1080        }
1081        fn greater_than_large(s: Vec<u8>) -> bool {
1082            checks!(
1083                cmp_test!(range!(> 0), 1..=255, s),
1084                cmp_test!(range!(> 50), 51..=255, s),
1085                cmp_test!(range!(> 90), 91..=255, s),
1086                cmp_test!(range!(> 1), 2..=255, s)
1087            )
1088        }
1089        fn greater_than_or_eq_always_true(s: Vec<u8>) -> bool {
1090            search_test!(
1091                s.as_slice(),
1092                range!(>= 0), |_always| true,
1093                |_never| false
1094            )
1095        }
1096        fn greater_than_or_eq_small(s: Vec<u8>) -> bool {
1097            checks!(
1098                cmp_test!(range!(>= 255), 255, s),
1099                cmp_test!(range!(>= 254), 254..=255, s),
1100                cmp_test!(range!(>= 250), 250..=255, s),
1101                cmp_test!(range!(>= 230), 230..=255, s),
1102                cmp_test!(range!(>= 200), 200..=255, s)
1103            )
1104        }
1105        fn greater_than_or_eq_mid(s: Vec<u8>) -> bool {
1106            checks!(
1107                cmp_test!(range!(>= 128), 128..=255, s),
1108                cmp_test!(range!(>= 127), 127..=255, s),
1109                cmp_test!(range!(>= 129), 129..=255, s),
1110                cmp_test!(range!(>= 126), 126..=255, s),
1111                cmp_test!(range!(>= 130), 130..=255, s)
1112            )
1113        }
1114        fn greater_than_or_eq_large(s: Vec<u8>) -> bool {
1115            checks!(
1116                cmp_test!(range!(>= 1), 1..=255, s),
1117                cmp_test!(range!(>= 3), 3..=255, s),
1118                cmp_test!(range!(>= 64), 64..=255, s),
1119                cmp_test!(range!(>= 90), 90..=255, s),
1120                cmp_test!(range!(>= 120), 120..=255, s)
1121            )
1122        }
1123
1124        fn basic_for_all_ensure(s: Vec<u8>) -> bool {
1125            checks!(
1126                ensure_test!(
1127                    s.as_slice(), eq(0),
1128                    |succ| succ == &0,
1129                    |fail| fail != &0
1130                ),
1131                ensure_test!(
1132                    s.as_slice(), range!(0..=250),
1133                    |succ| matches!(succ, 0..=250),
1134                    |fail| matches!(fail, 251..=255)
1135                ),
1136                ensure_test!(
1137                    s.as_slice(), not(eq(0)),
1138                    |succ| succ != &0,
1139                    |fail| fail == &0
1140                ),
1141                ensure_test!(
1142                    s.as_slice(), any!(eq(0), eq(1)),
1143                    |succ| matches!(succ, 0..=1),
1144                    |fail| matches!(fail, 2..=255)
1145                ),
1146                ensure_test!(
1147                    s.as_slice(), all!(eq(0), eq(0)),
1148                    |succ| succ == &0,
1149                    |fail| fail != &0
1150                ),
1151                ensure_test!(
1152                    s.as_slice(), all!(eq(0), eq(1)),
1153                    |_succ| false,
1154                    |_fail| true
1155                ),
1156                ensure_test!(
1157                    s.as_slice(), any!(eq(0), not(eq(0))),
1158                    |_succ| true,
1159                    |_fail| false
1160                )
1161            )
1162        }
1163    }
1164}
1165
1166#[cfg(test)]
1167mod mirai_tests {
1168    use crate::{eq, for_all_ensure, for_all_ensure_ct, search};
1169
1170    #[test]
1171    fn simple_search() {
1172        let input = b"I am a simple input to evaluate the correctness of the search adjflkasdfl;kjasdfl;kjasdl;kjasl;kdjf";
1173
1174        let res = search(input, eq(b'h')).unwrap();
1175        assert_eq!(input[res], b'h');
1176
1177        let res = search(input, eq(input[16])).unwrap();
1178        assert_eq!(input[res], input[16]);
1179
1180        let res = for_all_ensure_ct(input, eq(b' '));
1181        assert!(!res);
1182
1183        let res = for_all_ensure(input, eq(b' '));
1184        assert!(!res);
1185    }
1186}