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
//! Glue between mruby FFI and `ENV` Rust implementation.

use spinoso_string::String;

use super::{Random, Rng, Seed};
use crate::convert::implicitly_convert_to_int;
use crate::extn::prelude::*;

pub fn initialize(interp: &mut Artichoke, seed: Option<Value>, into: Value) -> Result<Value, Error> {
    let seed: Seed = interp.try_convert_mut(seed)?;
    let random = if let Some(seed) = seed.to_mt_seed() {
        Random::with_array_seed(seed)
    } else {
        Random::new()?
    };
    let random = Rng::Instance(Box::new(random));
    let random = Rng::box_into_value(random, into, interp)?;
    Ok(random)
}

pub fn equal(interp: &mut Artichoke, mut rand: Value, mut other: Value) -> Result<Value, Error> {
    let random = unsafe { Rng::unbox_from_value(&mut rand, interp)? };
    let other = unsafe { Rng::unbox_from_value(&mut other, interp)? };
    let eql = *random == *other;
    Ok(interp.convert(eql))
}

pub fn bytes(interp: &mut Artichoke, mut rand: Value, size: Value) -> Result<Value, Error> {
    let mut random = unsafe { Rng::unbox_from_value(&mut rand, interp)? };
    let size = implicitly_convert_to_int(interp, size)?;
    let mut buf = match usize::try_from(size) {
        Ok(0) => {
            let s = String::binary(vec![]);
            return String::alloc_value(s, interp);
        }
        Ok(len) => vec![0; len],
        Err(_) => return Err(ArgumentError::with_message("negative string size (or size too big)").into()),
    };
    match &mut *random {
        Rng::Global => interp.prng_mut()?.fill_bytes(&mut buf),
        Rng::Instance(random) => random.fill_bytes(&mut buf),
    }
    let s = String::binary(buf);
    String::alloc_value(s, interp)
}

pub fn rand(interp: &mut Artichoke, mut rand: Value, max: Option<Value>) -> Result<Value, Error> {
    let mut random = unsafe { Rng::unbox_from_value(&mut rand, interp)? };
    let max = interp.try_convert_mut(max)?;
    let num = match &mut *random {
        Rng::Global => spinoso_random::rand(interp.prng_mut()?, max)?,
        Rng::Instance(random) => spinoso_random::rand(random, max)?,
    };
    Ok(interp.convert_mut(num))
}

pub fn seed(interp: &mut Artichoke, mut rand: Value) -> Result<Value, Error> {
    let random = unsafe { Rng::unbox_from_value(&mut rand, interp)? };
    let seed = match &*random {
        Rng::Global => interp.prng()?.seed(),
        Rng::Instance(random) => random.seed(),
    };
    let seed = seed
        .try_into()
        .map_err(|_| Fatal::with_message("Got random seed larger than i128"))?;
    interp.try_convert(Seed::from_mt_seed_lossy(seed))
}

pub fn new_seed(interp: &mut Artichoke) -> Result<Value, Error> {
    let seed = spinoso_random::new_seed()?;
    let seed = Seed::from_mt_seed_lossy(seed);
    interp.try_convert(seed)
}

pub fn srand(interp: &mut Artichoke, seed: Option<Value>) -> Result<Value, Error> {
    let seed: Seed = interp.try_convert_mut(seed)?;
    let old_seed = interp
        .prng()?
        .seed()
        .try_into()
        .map_err(|_| Fatal::with_message("Got random seed larger than i128"))?;
    let old_seed = Seed::from_mt_seed_lossy(old_seed);

    let new_random = if let Some(seed) = seed.to_mt_seed() {
        Random::with_array_seed(seed)
    } else {
        Random::new()?
    };
    // "Reseed" by replacing the RNG with a newly seeded one.
    let prng = interp.prng_mut()?;
    *prng = new_random;

    interp.try_convert(old_seed)
}

pub fn urandom(interp: &mut Artichoke, size: Value) -> Result<Value, Error> {
    let size = implicitly_convert_to_int(interp, size)?;
    let mut buf = match usize::try_from(size) {
        Ok(0) => {
            let s = String::binary(vec![]);
            return String::alloc_value(s, interp);
        }
        Ok(len) => vec![0; len],
        Err(_) => return Err(ArgumentError::with_message("negative string size (or size too big)").into()),
    };
    spinoso_random::urandom(&mut buf)?;
    let s = String::binary(buf);
    String::alloc_value(s, interp)
}