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
}};
}