arbitrary/
size_hint.rs

1//! Utilities for working with and combining the results of
2//! [`Arbitrary::size_hint`][crate::Arbitrary::size_hint].
3
4pub(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")]
17pub 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]
38pub 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]
51pub 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]
62pub 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]
69pub 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]
82pub 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)]
91mod tests {
92    #[test]
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]
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]
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]
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}