artichoke_backend/extn/core/random/
mod.rs1use spinoso_random::{InitializeError, NewSeedError, UrandomError};
23#[doc(inline)]
24pub use spinoso_random::{Max, Rand, Random};
25
26use crate::convert::{HeapAllocatedData, implicitly_convert_to_int};
27use crate::extn::prelude::*;
28
29pub(in crate::extn) mod mruby;
30pub(super) mod trampoline;
31
32#[derive(Debug, Clone, Hash, PartialEq, Eq)]
33enum Rng {
34 Global,
35 Instance(Box<Random>),
36}
37
38impl HeapAllocatedData for Rng {
39 const RUBY_TYPE: &'static str = "Random";
40}
41
42#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
43pub enum Seed {
44 New(i64),
45 None,
46}
47
48impl Default for Seed {
49 fn default() -> Self {
50 Self::new()
51 }
52}
53
54impl From<i64> for Seed {
55 fn from(seed: i64) -> Seed {
56 Seed::New(seed)
57 }
58}
59
60impl Seed {
61 #[must_use]
63 pub const fn new() -> Self {
64 Self::None
65 }
66
67 #[must_use]
68 pub fn from_mt_seed_lossy(seed: [u32; 4]) -> Self {
69 let seed = {
71 let [hi, lo, _, _] = seed;
72 ((i64::from(hi)) << 32) | i64::from(lo)
73 };
74
75 Self::New(seed)
76 }
77
78 #[must_use]
79 pub fn to_mt_seed(self) -> Option<[u32; 4]> {
80 if let Self::New(seed) = self {
81 let seed = i128::from(seed);
82 let seed = seed.to_le_bytes();
83 let seed = spinoso_random::seed_to_key(seed);
84 Some(seed)
85 } else {
86 None
87 }
88 }
89}
90
91impl TryConvert<Seed, Value> for Artichoke {
92 type Error = Error;
93
94 fn try_convert(&self, seed: Seed) -> Result<Value, Self::Error> {
95 match seed {
96 Seed::None => Ok(Value::nil()),
97 Seed::New(seed) => self.try_convert(seed),
98 }
99 }
100}
101
102impl TryConvertMut<Value, Seed> for Artichoke {
103 type Error = Error;
104
105 fn try_convert_mut(&mut self, value: Value) -> Result<Seed, Self::Error> {
106 let seed = implicitly_convert_to_int(self, value)?;
107 Ok(Seed::New(seed))
108 }
109}
110
111impl TryConvertMut<Option<Value>, Seed> for Artichoke {
112 type Error = Error;
113
114 fn try_convert_mut(&mut self, value: Option<Value>) -> Result<Seed, Self::Error> {
115 if let Some(value) = value {
116 let seed = self.try_convert_mut(value)?;
117 Ok(seed)
118 } else {
119 Ok(Seed::None)
120 }
121 }
122}
123
124impl TryConvertMut<Value, Max> for Artichoke {
125 type Error = Error;
126
127 fn try_convert_mut(&mut self, max: Value) -> Result<Max, Self::Error> {
128 let optional: Option<Value> = self.try_convert(max)?;
129 self.try_convert_mut(optional)
130 }
131}
132
133impl TryConvertMut<Option<Value>, Max> for Artichoke {
134 type Error = Error;
135
136 fn try_convert_mut(&mut self, max: Option<Value>) -> Result<Max, Self::Error> {
137 if let Some(max) = max {
138 match max.ruby_type() {
139 Ruby::Fixnum => {
140 let max = max.try_convert_into(self)?;
141 Ok(Max::Integer(max))
142 }
143 Ruby::Float => {
144 let max = max.try_convert_into(self)?;
145 Ok(Max::Float(max))
146 }
147 _ => {
148 let max = implicitly_convert_to_int(self, max).map_err(|_| {
149 let mut message = b"invalid argument - ".to_vec();
150 message.extend(max.inspect(self));
151 ArgumentError::from(message)
152 })?;
153 Ok(Max::Integer(max))
154 }
155 }
156 } else {
157 Ok(Max::None)
158 }
159 }
160}
161
162impl ConvertMut<Rand, Value> for Artichoke {
163 fn convert_mut(&mut self, from: Rand) -> Value {
164 match from {
165 Rand::Integer(num) => self.convert(num),
166 Rand::Float(num) => self.convert_mut(num),
167 }
168 }
169}
170
171impl From<spinoso_random::ArgumentError> for Error {
172 fn from(err: spinoso_random::ArgumentError) -> Self {
173 let err = RuntimeError::from(err.to_string());
175 err.into()
176 }
177}
178
179impl From<InitializeError> for Error {
180 fn from(err: InitializeError) -> Self {
181 let err = RuntimeError::from(err.message());
182 err.into()
183 }
184}
185
186impl From<NewSeedError> for Error {
187 fn from(err: NewSeedError) -> Self {
188 let err = RuntimeError::from(err.message());
189 err.into()
190 }
191}
192
193impl From<UrandomError> for Error {
194 fn from(err: UrandomError) -> Self {
195 let err = RuntimeError::from(err.message());
196 err.into()
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::Seed;
203
204 #[test]
205 fn test_seed_new() {
206 let seed = Seed::new();
207 assert_eq!(seed, Seed::None);
208 }
209
210 #[test]
211 fn test_from_mt_seed_lossy_basic() {
212 let input = [0x1234_5678, 0x9ABC_DEF0, 0x0, 0x0];
213 let seed = Seed::from_mt_seed_lossy(input);
214 assert_eq!(seed, Seed::New((0x123_45678_i64 << 32) | 0x9ABC_DEF0));
216 }
217
218 #[test]
219 fn test_from_mt_seed_lossy_max_values() {
220 let input = [u32::MAX, u32::MAX, 0x0, 0x0];
221 let seed = Seed::from_mt_seed_lossy(input);
222 assert_eq!(seed, Seed::New((i64::from(u32::MAX) << 32) | i64::from(u32::MAX)));
224 }
225
226 #[test]
227 fn test_from_mt_seed_lossy_discarded_values() {
228 let input = [0x1234_5678, 0x9AB_CDEF0, 0xDEAD_BEEF, 0xFEED_FACE];
229 let seed = Seed::from_mt_seed_lossy(input);
230 assert_eq!(seed, Seed::New((0x1234_5678_i64 << 32) | 0x9ABC_DEF0));
233 }
234
235 #[test]
236 fn test_from_mt_seed_lossy_zero_values() {
237 let input = [0x0, 0x0, 0x0, 0x0];
238 let seed = Seed::from_mt_seed_lossy(input);
239 assert_eq!(seed, Seed::New(0));
241 }
242
243 #[test]
244 fn test_from_mt_seed_lossy_high_only() {
245 let input = [0x1234_5678, 0x0, 0x0, 0x0];
246 let seed = Seed::from_mt_seed_lossy(input);
247 assert_eq!(seed, Seed::New(0x1234_5678 << 32));
249 }
250
251 #[test]
252 fn test_from_mt_seed_lossy_low_only() {
253 let input = [0x0, 0x9ABC_DEF0, 0x0, 0x0];
254 let seed = Seed::from_mt_seed_lossy(input);
255 assert_eq!(seed, Seed::New(0x9ABC_DEF0));
257 }
258}