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
//! Random source.
use std::cell::RefCell;

use rand_chacha::{
    rand_core::{OsRng, RngCore, SeedableRng},
    ChaCha12Rng,
};

use super::{memory::ConfigSourceBuilder, ConfigSource};
use crate::{value::RandValue, ConfigError, ConfigValue};

/// Random source.
#[allow(missing_debug_implementations, missing_copy_implementations)]
pub(crate) struct Random;

impl ConfigSource for Random {
    fn name(&self) -> &str {
        "random_generator"
    }

    fn load(&self, source: &mut ConfigSourceBuilder<'_>) -> Result<(), ConfigError> {
        source.set("random.u8", RandValue::U8);
        source.set("random.u16", RandValue::U16);
        source.set("random.u32", RandValue::U32);
        source.set("random.u64", RandValue::U64);
        source.set("random.u128", RandValue::U128);
        source.set("random.usize", RandValue::Usize);
        source.set("random.i8", RandValue::I8);
        source.set("random.i16", RandValue::I16);
        source.set("random.i32", RandValue::I32);
        source.set("random.i64", RandValue::I64);
        source.set("random.i128", RandValue::I128);
        source.set("random.isize", RandValue::Isize);
        Ok(())
    }
}

thread_local! {
    static RND: RefCell<ChaCha12Rng> = RefCell::new( ChaCha12Rng::from_rng(OsRng).unwrap());
}

#[inline]
fn get_rand<R, F: Fn(&mut ChaCha12Rng) -> R>(f: F) -> R {
    RND.with(move |x| (f)(&mut x.borrow_mut()))
}

macro_rules! get_val {
    ($($f:ident.$n:literal),+) => {$(
        #[inline]
        fn $f<R, F: Fn(&[u8; $n]) -> R>(f: F) -> R {
            get_rand(|c| {
                let mut x = [0; $n];
                c.fill_bytes(&mut x);
                (f)(&x)
            })
        }
        )+};
}

get_val!(get_1.1, get_2.2, get_4.4, get_8.8, get_16.16);

impl RandValue {
    pub(crate) fn normalize(self) -> ConfigValue<'static> {
        match self {
            RandValue::U8 => get_rand(|f| f.next_u32() as u8).into(),
            RandValue::U16 => get_rand(|f| f.next_u32() as u16).into(),
            RandValue::U32 => get_rand(|f| f.next_u32()).into(),
            RandValue::U64 => get_rand(|f| f.next_u64()).into(),
            RandValue::U128 => get_16(|f| u128::from_le_bytes(*f)).into(),
            RandValue::Usize => get_8(|f| usize::from_le_bytes(*f)).into(),
            RandValue::I8 => get_1(|f| i8::from_le_bytes(*f)).into(),
            RandValue::I16 => get_2(|f| i16::from_le_bytes(*f)).into(),
            RandValue::I32 => get_4(|f| i32::from_le_bytes(*f)).into(),
            RandValue::I64 => get_8(|f| i64::from_le_bytes(*f)).into(),
            RandValue::I128 => get_16(|f| i128::from_le_bytes(*f)).into(),
            RandValue::Isize => get_8(|f| isize::from_le_bytes(*f)).into(),
        }
    }
}

#[cfg(test)]
mod test {

    use crate::test::TestConfigExt;

    use super::Random;

    #[test]
    fn env_test() {
        let config = Random.new_config();
        let a = config.get::<u128>("random.u128").unwrap();
        let b = config.get::<u128>("random.u128").unwrap();
        assert_ne!(a, b);
    }

    #[test]
    fn value_test() {
        let config = Random.new_config();
        assert_eq!(true, config.get::<u8>("random.u8").is_ok());
        assert_eq!(true, config.get::<u16>("random.u16").is_ok());
        assert_eq!(true, config.get::<u32>("random.u32").is_ok());
        assert_eq!(true, config.get::<u64>("random.u64").is_ok());
        assert_eq!(true, config.get::<u128>("random.u128").is_ok());
        assert_eq!(true, config.get::<usize>("random.usize").is_ok());
        assert_eq!(true, config.get::<i8>("random.i8").is_ok());
        assert_eq!(true, config.get::<i16>("random.i16").is_ok());
        assert_eq!(true, config.get::<i32>("random.i32").is_ok());
        assert_eq!(true, config.get::<i64>("random.i64").is_ok());
        assert_eq!(true, config.get::<i128>("random.i128").is_ok());
        assert_eq!(true, config.get::<isize>("random.isize").is_ok());
    }
}