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