1#![cfg_attr(not(test), no_std)]
6
7use core::ffi::CStr;
8use core::str;
9
10#[doc(hidden)]
11pub mod wide;
12
13#[doc(hidden)]
14pub mod cfo;
15
16mod murmur3;
17pub use self::murmur3::murmur3;
18
19mod pos;
20pub use self::pos::position;
21
22#[doc(hidden)]
23pub mod xref;
24
25#[macro_export]
83macro_rules! random {
84 ($ty:ident $(, $seeds:expr)* $(,)?) => {{
85 const _RANDOM: $ty = $crate::__random_cast!($ty,
86 $crate::entropy(concat!(file!(), ":", line!(), ":", column!() $(, ":", $seeds)*)));
87 _RANDOM
88 }};
89}
90
91#[doc(hidden)]
92#[macro_export]
93macro_rules! __random_cast {
94 (u8, $seed:expr) => {
95 $seed as u8
96 };
97 (u16, $seed:expr) => {
98 $seed as u16
99 };
100 (u32, $seed:expr) => {
101 $seed as u32
102 };
103 (u64, $seed:expr) => {
104 $seed
105 };
106 (usize, $seed:expr) => {
107 $seed as usize
108 };
109 (i8, $seed:expr) => {
110 $seed as i8
111 };
112 (i16, $seed:expr) => {
113 $seed as i16
114 };
115 (i32, $seed:expr) => {
116 $seed as i32
117 };
118 (i64, $seed:expr) => {
119 $seed as i64
120 };
121 (isize, $seed:expr) => {
122 $seed as isize
123 };
124 (bool, $seed:expr) => {
125 $seed as i64 >= 0
126 };
127
128 (f32, $seed:expr) => {
130 f32::from_bits(0b0_01111111 << (f32::MANTISSA_DIGITS - 1) | ($seed as u32 >> 9))
131 };
132 (f64, $seed:expr) => {
133 f64::from_bits(0b0_01111111111 << (f64::MANTISSA_DIGITS - 1) | ($seed >> 12))
134 };
135
136 ($ty:ident, $seed:expr) => {
137 compile_error!(concat!("unsupported type: ", stringify!($ty)))
138 };
139}
140
141#[test]
142fn test_random_f32() {
143 fn t(v: f32) {
144 assert!(v >= 1.0 && v < 2.0, "{}", v);
145 }
146 t(random!(f32, "a"));
148 t(random!(f32, "b"));
149 t(random!(f32, "c"));
150 t(random!(f32));
152}
153
154#[test]
155fn test_random_f64() {
156 fn t(v: f64) {
157 assert!(v >= 1.0 && v < 2.0, "{}", v);
158 }
159 t(random!(f64, "a"));
160 t(random!(f64, "b"));
161 t(random!(f64, "c"));
162 t(random!(f64));
163}
164
165#[test]
166fn test_random_int_types() {
167 let _: u8 = random!(u8, "u8");
169 let _: u16 = random!(u16, "u16");
170 let _: u32 = random!(u32, "u32");
171 let _: u64 = random!(u64, "u64");
172 let _: usize = random!(usize, "usize");
173 let _: i8 = random!(i8, "i8");
174 let _: i16 = random!(i16, "i16");
175 let _: i32 = random!(i32, "i32");
176 let _: i64 = random!(i64, "i64");
177 let _: isize = random!(isize, "isize");
178 let _: bool = random!(bool, "bool");
179 assert_ne!(random!(u64, "x"), random!(u64, "y"));
181}
182
183#[inline(always)]
188pub const fn splitmix(seed: u64) -> u64 {
189 let next = seed.wrapping_add(0x9e3779b97f4a7c15);
190 let mut z = next;
191 z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
192 z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
193 return z ^ (z >> 31);
194}
195
196#[test]
197fn test_splitmix() {
198 assert_ne!(splitmix(0), 0);
200 assert_eq!(splitmix(42), splitmix(42));
202 assert_ne!(splitmix(1), splitmix(2));
204}
205
206#[inline(always)]
210pub const fn hash(s: &str) -> u32 {
211 let s = s.as_bytes();
212 let mut result = 3581u32;
213 let mut i = 0usize;
214 while i < s.len() {
215 result = result.wrapping_mul(33) ^ s[i] as u32;
216 i += 1;
217 }
218 return result;
219}
220
221#[test]
222fn test_hash() {
223 assert_eq!(hash!("Hello World"), 0x6E4A573D);
225 let _ = hash!("");
227 let _ = hash!("🦀");
229 assert_eq!(hash!("abc"), hash!("abc"));
231 assert_ne!(hash!("abc"), hash!("abd"));
233}
234
235#[macro_export]
244macro_rules! hash {
245 ($s:expr) => {{
246 const _DJB2_HASH: u32 = $crate::hash($s);
247 _DJB2_HASH
248 }};
249}
250
251#[doc(hidden)]
253#[inline(always)]
254pub const fn entropy(string: &str) -> u64 {
255 splitmix(SEED ^ splitmix(hash(string) as u64))
256}
257
258pub const SEED: u64 = splitmix(hash(match option_env!("OBFANY_SEED") {
263 Some(seed) => seed,
264 None => "FIXED",
265}) as u64);
266
267#[doc(hidden)]
270pub mod bytes;
271
272#[doc(hidden)]
273pub mod words;
274
275#[doc(hidden)]
276#[inline(always)]
277pub const fn unsafe_as_str(bytes: &[u8]) -> &str {
278 #[cfg(debug_assertions)]
280 return match str::from_utf8(bytes) {
281 Ok(s) => s,
282 Err(_) => panic!("invalid str"),
283 };
284 #[cfg(not(debug_assertions))]
285 return unsafe { str::from_utf8_unchecked(bytes) };
286}
287
288#[doc(hidden)]
289#[inline(always)]
290pub const fn unsafe_as_cstr(bytes: &[u8]) -> &CStr {
291 #[cfg(debug_assertions)]
293 return match CStr::from_bytes_with_nul(bytes) {
294 Ok(cstr) => cstr,
295 Err(_) => panic!("invalid cstr"),
296 };
297 #[cfg(not(debug_assertions))]
298 return unsafe { CStr::from_bytes_with_nul_unchecked(bytes) };
299}