From fe4a3ae2b4941785f6e1ff730f92b8c9403dc1c5 Mon Sep 17 00:00:00 2001 From: Peter Limkilde Svendsen <peter.limkilde@gmail.com> Date: Thu, 23 May 2019 21:33:20 +0200 Subject: [PATCH] Refactor for consistency --- src/header/stdlib/lcg48.rs | 95 +++++++++++++++++++++----------------- src/header/stdlib/mod.rs | 42 ++++++++--------- 2 files changed, 73 insertions(+), 64 deletions(-) diff --git a/src/header/stdlib/lcg48.rs b/src/header/stdlib/lcg48.rs index a847f742b..470d1f37f 100644 --- a/src/header/stdlib/lcg48.rs +++ b/src/header/stdlib/lcg48.rs @@ -2,50 +2,27 @@ use platform::types::*; -/* The current element of the linear congruential generator's sequence. Any - * function that sets this variable must ensure that only the lower 48 bits get - * set. */ -pub static mut XI: u64 = 0; +/* The default element buffer for the linear congruential generator's + * sequence. Implemented using a c_ushort array for consistency between + * the drand48()/lrand48()/mrand48() and erand48()/nrand48()/jrand48() + * functions, and with STASHED_XI (see below). */ +pub static mut DEFAULT_XI: [c_ushort; 3] = [0; 3]; // Used by seed48() (returns a pointer to this array). pub static mut STASHED_XI: [c_ushort; 3] = [0; 3]; -/* Multiplier and addend, which may be set through lcong48(). Default values as - * specified in POSIX. */ -const A_DEFAULT: u64 = 0x5deece66d; -const C_DEFAULT: u16 = 0xb; +/* Multiplier and addend, which may be set through lcong48(). Default + * values as specified in POSIX. */ +const A_DEFAULT_VALUE: u64 = 0x5deece66d; +const C_DEFAULT_VALUE: u16 = 0xb; -pub static mut A: u64 = A_DEFAULT; -pub static mut C: u16 = C_DEFAULT; +pub static mut A: u64 = A_DEFAULT_VALUE; +pub static mut C: u16 = C_DEFAULT_VALUE; -/// Gets the next element in the linear congruential generator's -/// sequence. -pub unsafe fn next_x(x: u64) -> u64 { - /* The recurrence relation of the linear congruential generator, - * X_(n+1) = (a * X_n + c) % m, - * with m = 2**48. The multiplication and addition can overflow a u64, but - * we just let it wrap since we take mod 2**48 anyway. */ - A.wrapping_mul(x).wrapping_add(u64::from(C)) & 0xffff_ffff_ffff -} - -/// Get a C `double` from a 48-bit integer (for `drand48()` and `erand48()`). -pub fn x_to_float64(x: u64) -> c_double { - /* We set the exponent to 0, and the 48-bit integer is copied into the high - * 48 of the 52 significand bits. The value then lies in the range - * [1.0, 2.0), from which we simply subtract 1.0. */ - f64::from_bits(0x3ff0_0000_0000_0000_u64 | (x << 4)) - 1.0f64 -} - -/// Get the high 31 bits of a 48-bit integer (for `lrand48()` and `nrand48()`). -pub fn x_to_uint31(x: u64) -> c_long { - (x >> 17) as c_long -} - -/// Get the high 32 bits, signed, of a 48-bit integer (for `mrand48()` and -/// `jrand48()`). -pub fn x_to_int32(x: u64) -> c_long { - // Cast via i32 to ensure we get the sign correct - (x >> 16) as i32 as c_long +/// Used by `srand48()` and `seed48()`. +pub unsafe fn reset_a_and_c() { + A = A_DEFAULT_VALUE; + C = C_DEFAULT_VALUE; } /// Build a 48-bit integer from a size-3 array of unsigned short. @@ -68,8 +45,42 @@ pub unsafe fn set_ushort_arr3_from_uint48(arr_ptr: *mut c_ushort, value: u64) { *arr_ptr.offset(2) = c_ushort::from((value >> 32) as u16); } -/// Used by `srand48()` and `seed48()`. -pub unsafe fn reset_a_and_c() { - A = A_DEFAULT; - C = C_DEFAULT; +/// Advances the buffer from the input argument to the next element in +/// the linear congruential generator's sequence. +/// +/// Modifies the passed argument in-place and returns the new value as a +/// u64. The input argument must be a size-3 array. +pub unsafe fn generator_step(xi_arr_ptr: *mut c_ushort) -> u64 { + let old_xi: u64 = ushort_arr3_to_uint48(xi_arr_ptr); + + /* The recurrence relation of the linear congruential generator, + * X_(n+1) = (a * X_n + c) % m, + * with m = 2**48. The multiplication and addition can overflow a + * u64, but we just let it wrap since we take mod 2**48 anyway. */ + let new_xi: u64 = A.wrapping_mul(old_xi).wrapping_add(u64::from(C)) & 0xffff_ffff_ffff; + + set_ushort_arr3_from_uint48(xi_arr_ptr, new_xi); + new_xi +} + +/// Get a C `double` from a 48-bit integer (for `drand48()` and +/// `erand48()`). +pub fn x_to_float64(x: u64) -> c_double { + /* We set the exponent to 0, and the 48-bit integer is copied into the high + * 48 of the 52 significand bits. The value then lies in the range + * [1.0, 2.0), from which we simply subtract 1.0. */ + f64::from_bits(0x3ff0_0000_0000_0000_u64 | (x << 4)) - 1.0f64 +} + +/// Get the high 31 bits of a 48-bit integer (for `lrand48()` and +/// `nrand48()`). +pub fn x_to_uint31(x: u64) -> c_long { + (x >> 17) as c_long +} + +/// Get the high 32 bits, signed, of a 48-bit integer (for `mrand48()` +/// and `jrand48()`). +pub fn x_to_int32(x: u64) -> c_long { + // Cast via i32 to ensure we get the sign correct + c_long::from((x >> 16) as i32) } diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs index cb64ae223..995ce5b2a 100644 --- a/src/header/stdlib/mod.rs +++ b/src/header/stdlib/mod.rs @@ -232,8 +232,8 @@ pub extern "C" fn div(numer: c_int, denom: c_int) -> div_t { #[no_mangle] pub unsafe extern "C" fn drand48() -> c_double { - lcg48::XI = lcg48::next_x(lcg48::XI); - lcg48::x_to_float64(lcg48::XI) + let new_xi = lcg48::generator_step(lcg48::DEFAULT_XI.as_mut_ptr()); + lcg48::x_to_float64(new_xi) } // #[no_mangle] @@ -248,9 +248,7 @@ pub extern "C" fn ecvt( #[no_mangle] pub unsafe extern "C" fn erand48(xsubi: *mut c_ushort) -> c_double { - let old_xi = lcg48::ushort_arr3_to_uint48(xsubi); - let new_xi = lcg48::next_x(old_xi); - lcg48::set_ushort_arr3_from_uint48(xsubi, new_xi); + let new_xi = lcg48::generator_step(xsubi); lcg48::x_to_float64(new_xi) } @@ -366,9 +364,7 @@ pub extern "C" fn initstate(seec: c_uint, state: *mut c_char, size: size_t) -> * #[no_mangle] pub unsafe extern "C" fn jrand48(xsubi: *mut c_ushort) -> c_long { - let old_xi = lcg48::ushort_arr3_to_uint48(xsubi); - let new_xi = lcg48::next_x(old_xi); - lcg48::set_ushort_arr3_from_uint48(xsubi, new_xi); + let new_xi = lcg48::generator_step(xsubi); lcg48::x_to_int32(new_xi) } @@ -385,7 +381,11 @@ pub extern "C" fn labs(i: c_long) -> c_long { #[no_mangle] pub unsafe extern "C" fn lcong48(param: *mut c_ushort) { // Input should be a size-7 array. - lcg48::XI = lcg48::ushort_arr3_to_uint48(param.offset(0)); + + /* Go through this ptr -> u64 -> ptr conversion to ensure we only + * get the lower 16 bits of each element. */ + let new_xi = lcg48::ushort_arr3_to_uint48(param.offset(0)); + lcg48::set_ushort_arr3_from_uint48(lcg48::DEFAULT_XI.as_mut_ptr(), new_xi); lcg48::A = lcg48::ushort_arr3_to_uint48(param.offset(3)); lcg48::C = *param.offset(6) as u16; // c_ushort may be more than 16 bits } @@ -425,8 +425,8 @@ pub extern "C" fn lldiv(numer: c_longlong, denom: c_longlong) -> lldiv_t { #[no_mangle] pub unsafe extern "C" fn lrand48() -> c_long { - lcg48::XI = lcg48::next_x(lcg48::XI); - lcg48::x_to_uint31(lcg48::XI) + let new_xi = lcg48::generator_step(lcg48::DEFAULT_XI.as_mut_ptr()); + lcg48::x_to_uint31(new_xi) } #[no_mangle] @@ -582,15 +582,13 @@ pub extern "C" fn mkstemps(name: *mut c_char, suffix_len: c_int) -> c_int { #[no_mangle] pub unsafe extern "C" fn mrand48() -> c_long { - lcg48::XI = lcg48::next_x(lcg48::XI); - lcg48::x_to_int32(lcg48::XI) + let new_xi = lcg48::generator_step(lcg48::DEFAULT_XI.as_mut_ptr()); + lcg48::x_to_int32(new_xi) } #[no_mangle] pub unsafe extern "C" fn nrand48(xsubi: *mut c_ushort) -> c_long { - let old_xi = lcg48::ushort_arr3_to_uint48(xsubi); - let new_xi = lcg48::next_x(old_xi); - lcg48::set_ushort_arr3_from_uint48(xsubi, new_xi); + let new_xi = lcg48::generator_step(xsubi); lcg48::x_to_uint31(new_xi) } @@ -710,12 +708,11 @@ pub unsafe extern "C" fn realpath(pathname: *const c_char, resolved: *mut c_char pub unsafe extern "C" fn seed48(seed16v: *mut c_ushort) -> *mut c_ushort { lcg48::reset_a_and_c(); - //lcg48::STASHED_XI = lcg48::XI; - //lcg48::set_ushort_arr3_from_uint48(&mut lcg48::STASHED_XI[0] as *mut c_ushort, lcg48::XI); - lcg48::set_ushort_arr3_from_uint48(lcg48::STASHED_XI.as_mut_ptr(), lcg48::XI); - lcg48::XI = lcg48::ushort_arr3_to_uint48(seed16v); + lcg48::STASHED_XI = lcg48::DEFAULT_XI; + + let new_xi = lcg48::ushort_arr3_to_uint48(seed16v); + lcg48::set_ushort_arr3_from_uint48(lcg48::DEFAULT_XI.as_mut_ptr(), new_xi); - //&mut lcg48::STASHED_XI[0] lcg48::STASHED_XI.as_mut_ptr() } @@ -808,7 +805,8 @@ pub unsafe extern "C" fn srand48(seedval: c_long) { /* Set the high 32 bits of the 48-bit X_i value to the lower 32 bits * of the input argument, and the lower 16 bits to 0x330e, as * specified in POSIX. */ - lcg48::XI = (((seedval as u32) as u64) << 16) | 0x330e_u64; + let new_xi = (((seedval as u32) as u64) << 16) | 0x330e_u64; + lcg48::set_ushort_arr3_from_uint48(lcg48::DEFAULT_XI.as_mut_ptr(), new_xi); } // #[no_mangle] -- GitLab