blob: 8a88dd38c90004af5349744688afb928aacef649 [file] [log] [blame]
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: BSD-3-Clause
//! Miscellaneous functions related to getting (pseudo) random numbers and
//! strings.
//!
//! NOTE! This should not be used when you do need __real__ random numbers such
//! as for encryption but will probably be suitable when you want locally
//! unique ID's that will not be shared over the network.
use std::ffi::OsString;
use std::str;
/// Gets an ever increasing u64 (at least for this process).
///
/// The number retrieved will be based upon the time of the last reboot (x86_64)
/// and something undefined for other architectures.
pub fn timestamp_cycles() -> u64 {
#[cfg(target_arch = "x86_64")]
// SAFETY: Safe because there's nothing that can go wrong with this call.
unsafe {
std::arch::x86_64::_rdtsc()
}
#[cfg(not(target_arch = "x86_64"))]
{
const MONOTONIC_CLOCK_MULTPIPLIER: u64 = 1_000_000_000;
let mut ts = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
// SAFETY: We initialized the parameters correctly and we trust the function.
unsafe {
libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts);
}
(ts.tv_sec as u64) * MONOTONIC_CLOCK_MULTPIPLIER + (ts.tv_nsec as u64)
}
}
/// Generate pseudo random u32 numbers based on the current timestamp.
pub fn xor_pseudo_rng_u32() -> u32 {
let mut t: u32 = timestamp_cycles() as u32;
// Taken from https://en.wikipedia.org/wiki/Xorshift
t ^= t << 13;
t ^= t >> 17;
t ^ (t << 5)
}
// This will get an array of numbers that can safely be converted to strings
// because they will be in the range [a-zA-Z0-9]. The return vector could be any
// size between 0 and 4.
fn xor_pseudo_rng_u8_alphanumerics(rand_fn: &dyn Fn() -> u32) -> Vec<u8> {
rand_fn()
.to_ne_bytes()
.to_vec()
.drain(..)
.filter(|val| {
(48..=57).contains(val) || (65..=90).contains(val) || (97..=122).contains(val)
})
.collect()
}
fn xor_pseudo_rng_u8_bytes(rand_fn: &dyn Fn() -> u32) -> Vec<u8> {
rand_fn().to_ne_bytes().to_vec()
}
fn rand_alphanumerics_impl(rand_fn: &dyn Fn() -> u32, len: usize) -> OsString {
let mut buf = OsString::new();
let mut done = 0;
loop {
for n in xor_pseudo_rng_u8_alphanumerics(rand_fn) {
done += 1;
buf.push(str::from_utf8(&[n]).unwrap_or("_"));
if done >= len {
return buf;
}
}
}
}
fn rand_bytes_impl(rand_fn: &dyn Fn() -> u32, len: usize) -> Vec<u8> {
let mut buf: Vec<Vec<u8>> = Vec::new();
let mut num = if len % 4 == 0 { len / 4 } else { len / 4 + 1 };
while num > 0 {
buf.push(xor_pseudo_rng_u8_bytes(rand_fn));
num -= 1;
}
buf.into_iter().flatten().take(len).collect()
}
/// Gets a pseudo random OsString of length `len` with characters in the
/// range [a-zA-Z0-9].
pub fn rand_alphanumerics(len: usize) -> OsString {
rand_alphanumerics_impl(&xor_pseudo_rng_u32, len)
}
/// Get a pseudo random vector of `len` bytes.
pub fn rand_bytes(len: usize) -> Vec<u8> {
rand_bytes_impl(&xor_pseudo_rng_u32, len)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_timestamp_cycles() {
for _ in 0..1000 {
assert!(timestamp_cycles() < timestamp_cycles());
}
}
#[test]
fn test_xor_pseudo_rng_u32() {
for _ in 0..1000 {
assert_ne!(xor_pseudo_rng_u32(), xor_pseudo_rng_u32());
}
}
#[test]
fn test_xor_pseudo_rng_u8_alphas() {
let i = 3612982; // 55 (shifted 16 places), 33 (shifted 8 places), 54...
// The 33 will be discarded as it is not a valid letter
// (upper or lower) or number.
let s = xor_pseudo_rng_u8_alphanumerics(&|| i);
if cfg!(target_endian = "big") {
assert_eq!(vec![55, 54], s);
} else {
assert_eq!(vec![54, 55], s);
}
}
#[test]
fn test_rand_alphanumerics_impl() {
let s = rand_alphanumerics_impl(&|| 14134, 5);
if cfg!(target_endian = "big") {
assert_eq!("76767", s);
} else {
assert_eq!("67676", s);
}
}
#[test]
fn test_rand_alphanumerics() {
let s = rand_alphanumerics(5);
assert_eq!(5, s.len());
}
#[test]
fn test_xor_pseudo_rng_u8_bytes() {
let i = 3612982; // 55 (shifted 16 places), 33 (shifted 8 places), 54...
// The 33 will be discarded as it is not a valid letter
// (upper or lower) or number.
let s = xor_pseudo_rng_u8_bytes(&|| i);
if cfg!(target_endian = "big") {
assert_eq!(vec![0, 55, 33, 54], s);
} else {
assert_eq!(vec![54, 33, 55, 0], s);
}
}
#[test]
fn test_rand_bytes_impl() {
let s = rand_bytes_impl(&|| 1234567, 4);
if cfg!(target_endian = "big") {
assert_eq!(vec![0, 18, 214, 135], s);
} else {
assert_eq!(vec![135, 214, 18, 0], s);
}
}
#[test]
fn test_rand_bytes() {
for i in 0..8 {
assert_eq!(i, rand_bytes(i).len());
}
}
}