1 //! Utilities for working with and combining the results of
2 //! [`Arbitrary::size_hint`][crate::Arbitrary::size_hint].
3 
4 /// Protects against potential infinite recursion when calculating size hints
5 /// due to indirect type recursion.
6 ///
7 /// When the depth is not too deep, calls `f` with `depth + 1` to calculate the
8 /// size hint.
9 ///
10 /// Otherwise, returns the default size hint: `(0, None)`.
11 #[inline]
recursion_guard( depth: usize, f: impl FnOnce(usize) -> (usize, Option<usize>), ) -> (usize, Option<usize>)12 pub fn recursion_guard(
13     depth: usize,
14     f: impl FnOnce(usize) -> (usize, Option<usize>),
15 ) -> (usize, Option<usize>) {
16     const MAX_DEPTH: usize = 20;
17     if depth > MAX_DEPTH {
18         (0, None)
19     } else {
20         f(depth + 1)
21     }
22 }
23 
24 /// Take the sum of the `lhs` and `rhs` size hints.
25 #[inline]
and(lhs: (usize, Option<usize>), rhs: (usize, Option<usize>)) -> (usize, Option<usize>)26 pub fn and(lhs: (usize, Option<usize>), rhs: (usize, Option<usize>)) -> (usize, Option<usize>) {
27     let lower = lhs.0 + rhs.0;
28     let upper = lhs.1.and_then(|lhs| rhs.1.map(|rhs| lhs + rhs));
29     (lower, upper)
30 }
31 
32 /// Take the sum of all of the given size hints.
33 ///
34 /// If `hints` is empty, returns `(0, Some(0))`, aka the size of consuming
35 /// nothing.
36 #[inline]
and_all(hints: &[(usize, Option<usize>)]) -> (usize, Option<usize>)37 pub fn and_all(hints: &[(usize, Option<usize>)]) -> (usize, Option<usize>) {
38     hints.iter().copied().fold((0, Some(0)), and)
39 }
40 
41 /// Take the minimum of the lower bounds and maximum of the upper bounds in the
42 /// `lhs` and `rhs` size hints.
43 #[inline]
or(lhs: (usize, Option<usize>), rhs: (usize, Option<usize>)) -> (usize, Option<usize>)44 pub fn or(lhs: (usize, Option<usize>), rhs: (usize, Option<usize>)) -> (usize, Option<usize>) {
45     let lower = std::cmp::min(lhs.0, rhs.0);
46     let upper = lhs
47         .1
48         .and_then(|lhs| rhs.1.map(|rhs| std::cmp::max(lhs, rhs)));
49     (lower, upper)
50 }
51 
52 /// Take the maximum of the `lhs` and `rhs` size hints.
53 ///
54 /// If `hints` is empty, returns `(0, Some(0))`, aka the size of consuming
55 /// nothing.
56 #[inline]
or_all(hints: &[(usize, Option<usize>)]) -> (usize, Option<usize>)57 pub fn or_all(hints: &[(usize, Option<usize>)]) -> (usize, Option<usize>) {
58     if let Some(head) = hints.first().copied() {
59         hints[1..].iter().copied().fold(head, or)
60     } else {
61         (0, Some(0))
62     }
63 }
64 
65 #[cfg(test)]
66 mod tests {
67     #[test]
and()68     fn and() {
69         assert_eq!((5, Some(5)), super::and((2, Some(2)), (3, Some(3))));
70         assert_eq!((5, None), super::and((2, Some(2)), (3, None)));
71         assert_eq!((5, None), super::and((2, None), (3, Some(3))));
72         assert_eq!((5, None), super::and((2, None), (3, None)));
73     }
74 
75     #[test]
or()76     fn or() {
77         assert_eq!((2, Some(3)), super::or((2, Some(2)), (3, Some(3))));
78         assert_eq!((2, None), super::or((2, Some(2)), (3, None)));
79         assert_eq!((2, None), super::or((2, None), (3, Some(3))));
80         assert_eq!((2, None), super::or((2, None), (3, None)));
81     }
82 
83     #[test]
and_all()84     fn and_all() {
85         assert_eq!((0, Some(0)), super::and_all(&[]));
86         assert_eq!(
87             (7, Some(7)),
88             super::and_all(&[(1, Some(1)), (2, Some(2)), (4, Some(4))])
89         );
90         assert_eq!(
91             (7, None),
92             super::and_all(&[(1, Some(1)), (2, Some(2)), (4, None)])
93         );
94         assert_eq!(
95             (7, None),
96             super::and_all(&[(1, Some(1)), (2, None), (4, Some(4))])
97         );
98         assert_eq!(
99             (7, None),
100             super::and_all(&[(1, None), (2, Some(2)), (4, Some(4))])
101         );
102     }
103 
104     #[test]
or_all()105     fn or_all() {
106         assert_eq!((0, Some(0)), super::or_all(&[]));
107         assert_eq!(
108             (1, Some(4)),
109             super::or_all(&[(1, Some(1)), (2, Some(2)), (4, Some(4))])
110         );
111         assert_eq!(
112             (1, None),
113             super::or_all(&[(1, Some(1)), (2, Some(2)), (4, None)])
114         );
115         assert_eq!(
116             (1, None),
117             super::or_all(&[(1, Some(1)), (2, None), (4, Some(4))])
118         );
119         assert_eq!(
120             (1, None),
121             super::or_all(&[(1, None), (2, Some(2)), (4, Some(4))])
122         );
123     }
124 }
125