arbitrary/
size_hint.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//! Utilities for working with and combining the results of
//! [`Arbitrary::size_hint`][crate::Arbitrary::size_hint].

pub(crate) const MAX_DEPTH: usize = 20;

/// Protects against potential infinite recursion when calculating size hints
/// due to indirect type recursion.
///
/// When the depth is not too deep, calls `f` with `depth + 1` to calculate the
/// size hint.
///
/// Otherwise, returns the default size hint: `(0, None)`.
///
/// <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>
#[inline]
#[deprecated(note = "use `try_recursion_guard` instead")]
pub fn recursion_guard(
    depth: usize,
    f: impl FnOnce(usize) -> (usize, Option<usize>),
) -> (usize, Option<usize>) {
    if depth > MAX_DEPTH {
        (0, None)
    } else {
        f(depth + 1)
    }
}

/// Protects against potential infinite recursion when calculating size hints
/// due to indirect type recursion.
///
/// When the depth is not too deep, calls `f` with `depth + 1` to calculate the
/// size hint.
///
/// Otherwise, returns an error.
///
/// This should be used when implementing [`try_size_hint`](crate::Arbitrary::try_size_hint)
#[inline]
pub fn try_recursion_guard(
    depth: usize,
    f: impl FnOnce(usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached>,
) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
    if depth > MAX_DEPTH {
        Err(crate::MaxRecursionReached {})
    } else {
        f(depth + 1)
    }
}

/// Take the sum of the `lhs` and `rhs` size hints.
#[inline]
pub fn and(lhs: (usize, Option<usize>), rhs: (usize, Option<usize>)) -> (usize, Option<usize>) {
    let lower = lhs.0 + rhs.0;
    let upper = lhs.1.and_then(|lhs| rhs.1.map(|rhs| lhs + rhs));
    (lower, upper)
}

/// Take the sum of all of the given size hints.
///
/// If `hints` is empty, returns `(0, Some(0))`, aka the size of consuming
/// nothing.
#[inline]
pub fn and_all(hints: &[(usize, Option<usize>)]) -> (usize, Option<usize>) {
    hints.iter().copied().fold((0, Some(0)), and)
}

/// Take the minimum of the lower bounds and maximum of the upper bounds in the
/// `lhs` and `rhs` size hints.
#[inline]
pub fn or(lhs: (usize, Option<usize>), rhs: (usize, Option<usize>)) -> (usize, Option<usize>) {
    let lower = std::cmp::min(lhs.0, rhs.0);
    let upper = lhs
        .1
        .and_then(|lhs| rhs.1.map(|rhs| std::cmp::max(lhs, rhs)));
    (lower, upper)
}

/// Take the maximum of the `lhs` and `rhs` size hints.
///
/// If `hints` is empty, returns `(0, Some(0))`, aka the size of consuming
/// nothing.
#[inline]
pub fn or_all(hints: &[(usize, Option<usize>)]) -> (usize, Option<usize>) {
    if let Some(head) = hints.first().copied() {
        hints[1..].iter().copied().fold(head, or)
    } else {
        (0, Some(0))
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn and() {
        assert_eq!((5, Some(5)), super::and((2, Some(2)), (3, Some(3))));
        assert_eq!((5, None), super::and((2, Some(2)), (3, None)));
        assert_eq!((5, None), super::and((2, None), (3, Some(3))));
        assert_eq!((5, None), super::and((2, None), (3, None)));
    }

    #[test]
    fn or() {
        assert_eq!((2, Some(3)), super::or((2, Some(2)), (3, Some(3))));
        assert_eq!((2, None), super::or((2, Some(2)), (3, None)));
        assert_eq!((2, None), super::or((2, None), (3, Some(3))));
        assert_eq!((2, None), super::or((2, None), (3, None)));
    }

    #[test]
    fn and_all() {
        assert_eq!((0, Some(0)), super::and_all(&[]));
        assert_eq!(
            (7, Some(7)),
            super::and_all(&[(1, Some(1)), (2, Some(2)), (4, Some(4))])
        );
        assert_eq!(
            (7, None),
            super::and_all(&[(1, Some(1)), (2, Some(2)), (4, None)])
        );
        assert_eq!(
            (7, None),
            super::and_all(&[(1, Some(1)), (2, None), (4, Some(4))])
        );
        assert_eq!(
            (7, None),
            super::and_all(&[(1, None), (2, Some(2)), (4, Some(4))])
        );
    }

    #[test]
    fn or_all() {
        assert_eq!((0, Some(0)), super::or_all(&[]));
        assert_eq!(
            (1, Some(4)),
            super::or_all(&[(1, Some(1)), (2, Some(2)), (4, Some(4))])
        );
        assert_eq!(
            (1, None),
            super::or_all(&[(1, Some(1)), (2, Some(2)), (4, None)])
        );
        assert_eq!(
            (1, None),
            super::or_all(&[(1, Some(1)), (2, None), (4, Some(4))])
        );
        assert_eq!(
            (1, None),
            super::or_all(&[(1, None), (2, Some(2)), (4, Some(4))])
        );
    }
}