arbitrary/
size_hint.rs

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]
12pub 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]
26pub 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]
37pub 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]
44pub 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]
57pub 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)]
66mod tests {
67    #[test]
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]
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]
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]
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}