macro_toolset/
random.rs

1//! Random number / string generation utilities
2
3#[macro_export]
4/// Generate random `String`.
5///
6/// For [`PushAnyT::push_any`](crate::string::PushAnyT::push_any),
7/// [`random_str`](crate::random_str) is recommended.
8///
9/// # Example
10///
11/// Just add `rand = "0.8.5"` to your crate deps then try:
12///
13/// ```rust
14/// # use macro_toolset::random_string;
15/// #
16/// // Use default charset `b"0123456789abcdef"` **NOT RECOMMEND, use RandHexStr instead**
17/// let rs_1 = random_string!(32);
18/// # assert_eq!(rs_1.len(), 32);
19/// // Use custom charset
20/// let rs_2 = random_string!(32, b"0123456789abcdefABCDEF");
21/// # assert_eq!(rs_2.len(), 32);
22/// // Provide your own string and the randon string will be appended to it
23/// # let mut your_own_string = "test".to_string();
24/// random_string!(32 => your_own_string);
25/// # assert_eq!(&your_own_string[0..4], "test");
26/// # assert_eq!(your_own_string.len(), 36);
27/// // Of course, custom charset is supported
28/// # let mut your_own_string = "test".to_string();
29/// random_string!(32, b"0123456789abcdefABCDEF" => your_own_string);
30/// # assert_eq!(&your_own_string[0..4], "test");
31/// # assert_eq!(your_own_string.len(), 36);
32/// ```
33macro_rules! random_string {
34    ($range:expr, $charset:expr => $string:expr) => {{
35        use ::rand::{distributions::Slice, Rng};
36
37        $string.extend(
38            ::rand::thread_rng()
39                .sample_iter(Slice::new($charset).unwrap())
40                .take($range)
41                .map(|&c| c as char)
42        );
43    }};
44    ($range:expr, $charset:expr) => {{
45        use ::rand::{distributions::Slice, Rng};
46
47        let mut string = String::with_capacity($range);
48        string.extend(
49            ::rand::thread_rng()
50                .sample_iter(Slice::new($charset).unwrap())
51                .take($range)
52                .map(|&c| c as char)
53        );
54        string
55    }};
56    ($range:expr => $string:expr) => {
57        $crate::random_string!($range, b"0123456789abcdef" => $string)
58    };
59    ($range:expr) => {
60        $crate::random_string!($range, b"0123456789abcdef")
61    };
62}
63
64#[deprecated(since = "0.7.12", note = "Use `RandHexStr` instead")]
65#[cfg(feature = "feat-string")]
66#[macro_export]
67/// Generate random string base on xor-shift algorithm.
68///
69/// Notice: Length of string should be always <= 16 (u64)
70macro_rules! random_string_fast {
71    ($b:expr, $l:expr) => {{
72        use $crate::string::StringExtT;
73        $crate::string::NumStr::hex_default($crate::random::fast_random())
74            .set_uppercase::<$b>()
75            .to_string_ext()
76    }};
77}
78
79#[inline]
80/// [xorshift*] is a fast pseudorandom number generator which will
81/// even tolerate weak seeding, as long as it's not zero.
82///
83/// [xorshift*]: https://en.wikipedia.org/wiki/Xorshift#xorshift*
84pub fn fast_random() -> u64 {
85    #[cfg(not(feature = "feat-random-fast"))]
86    use std::hash::RandomState;
87    use std::{
88        cell::Cell,
89        hash::{BuildHasher, Hasher},
90        num::Wrapping,
91    };
92
93    #[cfg(feature = "feat-random-fast")]
94    use ::foldhash::fast::RandomState;
95
96    thread_local! {
97        static RNG: Cell<Wrapping<u64>> = Cell::new(Wrapping(seed()));
98    }
99
100    fn seed() -> u64 {
101        let seed = RandomState::default();
102
103        let mut out = 0;
104        let mut cnt = 0;
105        while out == 0 {
106            cnt += 1;
107            let mut hasher = seed.build_hasher();
108            hasher.write_usize(cnt);
109            out = hasher.finish();
110        }
111        out
112    }
113
114    RNG.with(|rng| {
115        let mut n = rng.get();
116        debug_assert_ne!(n.0, 0);
117        n ^= n >> 12;
118        n ^= n << 25;
119        n ^= n >> 27;
120        rng.set(n);
121        n.0.wrapping_mul(0x2545_f491_4f6c_dd1d)
122    })
123}
124
125#[macro_export]
126/// Generate a random string by choosing ones from given candidates.
127///
128/// Candidates should be `Vec<&str>` or `[&'a str]`.
129///
130/// # Examples
131///
132/// Here's an example rewritten from the original JavaScript code.
133///
134/// ```
135/// # use macro_toolset::random_choice;
136/// #
137/// static DIGHT_MAP: [&'static str; 17] = [
138/// "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "10",
139/// ];
140///
141/// let rc_1 = random_choice!(32, DIGHT_MAP);
142/// let rc_2 = random_choice!(8, 4, 4, 4, 12; "-"; DIGHT_MAP); // like `8310B0E0A-40105-9EC3-8298-36C75D10FEA59`
143/// ```
144macro_rules! random_choice {
145    ($range:expr, $choice_set:expr) => {{
146        let mut rng = ::rand::thread_rng();
147        let mut result = String::with_capacity(32);
148        (0..$range).for_each(|_| {
149            result.push_str($choice_set[::rand::Rng::gen_range(&mut rng, 0..$choice_set.len())]);
150        });
151        result
152    }};
153    ($($range:expr),+; $split:expr; $choice_set:expr) => {{
154        let mut rng = ::rand::thread_rng();
155        let mut result = String::with_capacity(32);
156        $(
157            (0..$range).for_each(|_| {
158                result.push_str($choice_set[::rand::Rng::gen_range(&mut rng, 0..$choice_set.len())]);
159            });
160            result.push_str($split);
161        )+
162        result.truncate(result.len() - $split.len());
163        result
164    }};
165}