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