macro_toolset/
random.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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//! Random number / string generation utilities

#[macro_export]
/// Generate random `String`.
///
/// For [`PushAnyT::push_any`](crate::string_v2::PushAnyT::push_any),
/// [`random_str`](crate::random_str) is recommended.
///
/// # Example
///
/// Just add `rand = "0.8.5"` to your crate deps then try:
///
/// ```rust
/// # use macro_toolset::random_string;
/// #
/// // Use default charset `b"0123456789abcdef"` **NOT RECOMMEND, use RandHexStr instead**
/// let rs_1 = random_string!(32);
/// # assert_eq!(rs_1.len(), 32);
/// // Use custom charset
/// let rs_2 = random_string!(32, b"0123456789abcdefABCDEF");
/// # assert_eq!(rs_2.len(), 32);
/// // Provide your own string and the randon string will be appended to it
/// # let mut your_own_string = "test".to_string();
/// random_string!(32 => your_own_string);
/// # assert_eq!(&your_own_string[0..4], "test");
/// # assert_eq!(your_own_string.len(), 36);
/// // Of course, custom charset is supported
/// # let mut your_own_string = "test".to_string();
/// random_string!(32, b"0123456789abcdefABCDEF" => your_own_string);
/// # assert_eq!(&your_own_string[0..4], "test");
/// # assert_eq!(your_own_string.len(), 36);
/// ```
macro_rules! random_string {
    ($range:expr, $charset:expr => $string:expr) => {{
        use ::rand::{distributions::Slice, Rng};

        $string.extend(
            ::rand::thread_rng()
                .sample_iter(Slice::new($charset).unwrap())
                .take($range)
                .map(|&c| c as char)
        );
    }};
    ($range:expr, $charset:expr) => {{
        use ::rand::{distributions::Slice, Rng};

        let mut string = String::with_capacity($range);
        string.extend(
            ::rand::thread_rng()
                .sample_iter(Slice::new($charset).unwrap())
                .take($range)
                .map(|&c| c as char)
        );
        string
    }};
    ($range:expr => $string:expr) => {
        $crate::random_string!($range, b"0123456789abcdef" => $string)
    };
    ($range:expr) => {
        $crate::random_string!($range, b"0123456789abcdef")
    };
}

#[deprecated(since = "0.7.12", note = "Use `RandHexStr` instead")]
#[cfg(feature = "feat-string")]
#[macro_export]
/// Generate random string base on xor-shift algorithm.
///
/// Notice: Length of string should be always <= 16 (u64)
macro_rules! random_string_fast {
    ($b:expr, $l:expr) => {{
        use $crate::string::StringExtT;
        $crate::string::NumStr::hex_default($crate::random::fast_random())
            .set_uppercase::<$b>()
            .to_string_ext()
    }};
}

#[inline]
/// [xorshift*] is a fast pseudorandom number generator which will
/// even tolerate weak seeding, as long as it's not zero.
///
/// [xorshift*]: https://en.wikipedia.org/wiki/Xorshift#xorshift*
pub fn fast_random() -> u64 {
    #[cfg(not(feature = "feat-random-fast"))]
    use std::hash::RandomState;
    use std::{
        cell::Cell,
        hash::{BuildHasher, Hasher},
        num::Wrapping,
    };

    #[cfg(feature = "feat-random-fast")]
    use ::foldhash::fast::RandomState;

    thread_local! {
        static RNG: Cell<Wrapping<u64>> = Cell::new(Wrapping(seed()));
    }

    fn seed() -> u64 {
        let seed = RandomState::default();

        let mut out = 0;
        let mut cnt = 0;
        while out == 0 {
            cnt += 1;
            let mut hasher = seed.build_hasher();
            hasher.write_usize(cnt);
            out = hasher.finish();
        }
        out
    }

    RNG.with(|rng| {
        let mut n = rng.get();
        debug_assert_ne!(n.0, 0);
        n ^= n >> 12;
        n ^= n << 25;
        n ^= n >> 27;
        rng.set(n);
        n.0.wrapping_mul(0x2545_f491_4f6c_dd1d)
    })
}

#[macro_export]
/// Generate a random string by choosing ones from given candidates.
///
/// Candidates should be `Vec<&str>` or `[&'a str]`.
///
/// # Examples
///
/// Here's an example rewritten from the original JavaScript code.
///
/// ```
/// # use macro_toolset::random_choice;
/// #
/// static DIGHT_MAP: [&'static str; 17] = [
/// "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "10",
/// ];
///
/// let rc_1 = random_choice!(32, DIGHT_MAP);
/// let rc_2 = random_choice!(8, 4, 4, 4, 12; "-"; DIGHT_MAP); // like `8310B0E0A-40105-9EC3-8298-36C75D10FEA59`
/// ```
macro_rules! random_choice {
    ($range:expr, $choice_set:expr) => {{
        let mut rng = ::rand::thread_rng();
        let mut result = String::with_capacity(32);
        (0..$range).for_each(|_| {
            result.push_str($choice_set[::rand::Rng::gen_range(&mut rng, 0..$choice_set.len())]);
        });
        result
    }};
    ($($range:expr),+; $split:expr; $choice_set:expr) => {{
        let mut rng = ::rand::thread_rng();
        let mut result = String::with_capacity(32);
        $(
            (0..$range).for_each(|_| {
                result.push_str($choice_set[::rand::Rng::gen_range(&mut rng, 0..$choice_set.len())]);
            });
            result.push_str($split);
        )+
        result.truncate(result.len() - $split.len());
        result
    }};
}