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
// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

// Copyright 2021 Conflux Foundation. All rights reserved.
// Conflux is free software and distributed under GNU General Public License.
// See http://www.gnu.org/licenses/

#![forbid(unsafe_code)]

#[cfg(test)]
mod unit_tests;

mod growing_subset;
mod repeat_vec;
mod value_generator;

pub use crate::{
    growing_subset::GrowingSubset, repeat_vec::RepeatVec,
    value_generator::ValueGenerator,
};

use crossbeam::thread;
use proptest::sample::Index as PropIndex;
use proptest_derive::Arbitrary;
use std::{
    any::Any,
    collections::BTreeSet,
    ops::{Deref, Index as OpsIndex},
};

/// Creates a new thread with a larger stack size.
///
/// Generating some proptest values can overflow the stack. This allows test
/// authors to work around this limitation.
///
/// This is expected to be used with closure-style proptest invocations:
///
/// ```
/// use proptest::prelude::*;
/// use diem_proptest_helpers::with_stack_size;
///
/// with_stack_size(4 * 1024 * 1024, || proptest!(|(x in 0usize..128)| {
///     // assertions go here
///     prop_assert!(x >= 0 && x < 128);
/// }));
/// ```
pub fn with_stack_size<'a, F, T>(
    size: usize, f: F,
) -> Result<T, Box<dyn Any + 'static + Send>>
where
    F: FnOnce() -> T + Send + 'a,
    T: Send + 'a,
{
    thread::scope(|s| {
        let handle =
            s.builder().stack_size(size).spawn(|_| f()).map_err(|err| {
                let any: Box<dyn Any + 'static + Send> = Box::new(err);
                any
            })?;
        handle.join()
    })?
}

/// Given a maximum value `max` and a list of [`Index`](proptest::sample::Index)
/// instances, picks integers in the range `[0, max)` uniformly randomly and
/// without duplication.
///
/// If `indexes_len` is greater than `max`, all indexes will be returned.
///
/// This function implements [Robert Floyd's F2
/// algorithm](https://blog.acolyer.org/2018/01/30/a-sample-of-brilliance/) for sampling without
/// replacement.
pub fn pick_idxs<T, P>(
    max: usize, indexes: &T, indexes_len: usize,
) -> Vec<usize>
where
    T: OpsIndex<usize, Output = P> + ?Sized,
    P: AsRef<PropIndex>,
{
    // See https://blog.acolyer.org/2018/01/30/a-sample-of-brilliance/ (the F2 algorithm)
    // for a longer explanation. This is a variant that works with
    // zero-indexing.
    let mut selected = BTreeSet::new();
    let to_select = indexes_len.min(max);
    for (iter_idx, choice) in ((max - to_select)..max).enumerate() {
        // "RandInt(1, J)" in the original algorithm means a number between 1
        // and choice, both inclusive. `PropIndex::index` picks a number between
        // 0 and whatever's passed in, with the latter exclusive. Pass
        // in "+1" to ensure the same range of values is picked from.
        // (This also ensures that if choice is 0 then `index`
        // doesn't panic.
        let idx = indexes[iter_idx].as_ref().index(choice + 1);
        if !selected.insert(idx) {
            selected.insert(choice);
        }
    }
    selected.into_iter().collect()
}

/// Given a maximum value `max` and a slice of
/// [`Index`](proptest::sample::Index) instances, picks integers in the range
/// `[0, max)` uniformly randomly and without duplication.
///
/// If the number of `Index` instances is greater than `max`, all indexes will
/// be returned.
///
/// This function implements [Robert Floyd's F2
/// algorithm](https://blog.acolyer.org/2018/01/30/a-sample-of-brilliance/) for sampling without
/// replacement.
#[inline]
pub fn pick_slice_idxs(
    max: usize, indexes: &[impl AsRef<PropIndex>],
) -> Vec<usize> {
    pick_idxs(max, indexes, indexes.len())
}

/// Wrapper for `proptest`'s [`Index`][proptest::sample::Index] that allows
/// `AsRef` to work.
///
/// There is no blanket `impl<T> AsRef<T> for T`, so `&[PropIndex]` doesn't work
/// with `&[impl AsRef<PropIndex>]` (unless an impl gets added upstream).
/// `Index` does.
#[derive(Arbitrary, Clone, Copy, Debug)]
pub struct Index(PropIndex);

impl AsRef<PropIndex> for Index {
    fn as_ref(&self) -> &PropIndex { &self.0 }
}

impl Deref for Index {
    type Target = PropIndex;

    fn deref(&self) -> &PropIndex { &self.0 }
}