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}