diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs index 380bca304290fc447555f5d0691e1ff8b885d853..220579ec331a267b0df686796df923fbbaae0759 100644 --- a/src/header/stdlib/mod.rs +++ b/src/header/stdlib/mod.rs @@ -35,6 +35,7 @@ pub const MB_CUR_MAX: c_int = 4; pub const MB_LEN_MAX: c_int = 4; static mut ATEXIT_FUNCS: [Option<extern "C" fn()>; 32] = [None; 32]; +static mut L64A_BUFFER: [c_char; 7] = [0; 7]; // up to 6 digits plus null terminator static mut RNG: Option<XorShiftRng> = None; lazy_static! { @@ -51,26 +52,27 @@ pub unsafe extern "C" fn a64l(s: *const c_char) -> c_long { if s.is_null() { return 0; } - let mut l: c_long = 0; + // POSIX says only the low-order 32 bits are used. + let mut l: i32 = 0; // a64l does not support more than 6 characters at once for x in 0..6 { let c = *s.offset(x); if c == 0 { // string is null terminated - return l; + return c_long::from(l); } // ASCII to base64 conversion: - let mut bits: c_long = if c < 58 { - (c - 46) as c_long // ./0123456789 + let mut bits: i32 = if c < 58 { + (c - 46) as i32 // ./0123456789 } else if c < 91 { - (c - 53) as c_long // A-Z + (c - 53) as i32 // A-Z } else { - (c - 59) as c_long // a-z + (c - 59) as i32 // a-z }; bits <<= 6 * x; l |= bits; } - l + c_long::from(l) } #[no_mangle] @@ -368,9 +370,37 @@ pub unsafe extern "C" fn jrand48(xsubi: *mut c_ushort) -> c_long { lcg48::int32_from_x(new_xi) } -// #[no_mangle] -pub extern "C" fn l64a(value: c_long) -> *mut c_char { - unimplemented!(); +#[no_mangle] +pub unsafe extern "C" fn l64a(value: c_long) -> *mut c_char { + // POSIX says we should only consider the lower 32 bits of value. + let value_as_i32 = value as i32; + + /* If we pretend to extend the 32-bit value with 4 binary zeros, we + * would get a 36-bit integer. The number of base-64 digits to be + * left unused can then be found by taking the number of leading + * zeros, dividing by 6 and rounding down (i.e. using integer + * division). */ + let num_output_digits = 6 - (value_as_i32.leading_zeros() + 4) / 6; + + // Reset buffer (and have null terminator in place for any result) + L64A_BUFFER = [0; 7]; + + for i in 0..num_output_digits as usize { + let digit_value = ((value_as_i32 >> 6 * i) & 63) as c_char; + + if digit_value < 12 { + // ./0123456789 for values 0 to 11. b'.' == 46 + L64A_BUFFER[i] = 46 + digit_value; + } else if digit_value < 38 { + // A-Z for values 12 to 37. b'A' == 65, 65-12 == 53 + L64A_BUFFER[i] = 53 + digit_value; + } else { + // a-z for values 38 to 63. b'a' == 97, 97-38 == 59 + L64A_BUFFER[i] = 59 + digit_value; + } + } + + L64A_BUFFER.as_mut_ptr() } #[no_mangle] diff --git a/tests/expected/stdlib/a64l.stdout b/tests/expected/stdlib/a64l.stdout index 18c83b1ed91bcb0e0f21533435e84e0299fca763..d22f10d9732c62ea499aa853809b09f70591648b 100644 --- a/tests/expected/stdlib/a64l.stdout +++ b/tests/expected/stdlib/a64l.stdout @@ -1,2 +1,42 @@ Correct a64l: azAZ9. = 194301926 Correct a64l: azA = 53222 +l64a(0): +l64a(1): / +l64a(2): 0 +l64a(11): 9 +l64a(12): A +l64a(37): Z +l64a(38): a +l64a(63): z +l64a(64): ./ +l64a(65): // +l64a(4095): zz +l64a(4096): ../ +l64a(262143): zzz +l64a(262144): .../ +l64a(16777215): zzzz +l64a(16777216): ..../ +l64a(1073741823): zzzzz +l64a(1073741824): ...../ +l64a(2147483647): zzzzz/ +a64l(l64a(0)): 0 +a64l(l64a(1)): 1 +a64l(l64a(2)): 2 +a64l(l64a(11)): 11 +a64l(l64a(12)): 12 +a64l(l64a(37)): 37 +a64l(l64a(38)): 38 +a64l(l64a(63)): 63 +a64l(l64a(64)): 64 +a64l(l64a(65)): 65 +a64l(l64a(4095)): 4095 +a64l(l64a(4096)): 4096 +a64l(l64a(262143)): 262143 +a64l(l64a(262144)): 262144 +a64l(l64a(16777215)): 16777215 +a64l(l64a(16777216)): 16777216 +a64l(l64a(1073741823)): 1073741823 +a64l(l64a(1073741824)): 1073741824 +a64l(l64a(2147483647)): 2147483647 +l64a(x) (lower 32 bits of x are 1985229328): E61Jq/ +a64l(l64a(x)) (lower 32 bits of x are 1985229328): 1985229328 diff --git a/tests/stdlib/a64l.c b/tests/stdlib/a64l.c index 24937f52061f9bc6a79b0d6de04090b4c05fa252..2696c34664db32a3504bab059e382a0ad0a956b6 100644 --- a/tests/stdlib/a64l.c +++ b/tests/stdlib/a64l.c @@ -20,4 +20,34 @@ int main(void) { exit(EXIT_FAILURE); } printf("Correct a64l: %s = %ld\n", s, l); + + /* Test near boundaries of digit character mapping, and near + * boundaries for number of digits */ + long l64a_test_values[] = {0, 1, 2, 11, 12, 37, \ + 38, 63, \ + 64, 65, 4095, \ + 4096, 262143, \ + 262144, 16777215, \ + 16777216, 1073741823, \ + 1073741824, 2147483647}; + + // l64a tests + for (size_t i = 0; i < sizeof(l64a_test_values)/sizeof(long); i++) { + printf("l64a(%ld): %s\n", l64a_test_values[i], l64a(l64a_test_values[i])); + } + + // a64l(l64a(x)) round-trip tests + for (size_t i = 0; i < sizeof(l64a_test_values)/sizeof(long); i++) { + printf("a64l(l64a(%ld)): %ld\n", l64a_test_values[i], a64l(l64a(l64a_test_values[i]))); + } + + /* For testing 32-bit truncation behavior (for platforms where long + * is larger than 32 bits). Note that the behavior for a64l() and + * l64a() is unspecified for negative values. */ + int64_t test_value_64bit = 0x7edcba9876543210; + printf("l64a(x) (lower 32 bits of x are %ld): %s\n", ((long)test_value_64bit) & 0xffffffff, l64a((long)test_value_64bit)); + + /* Test for trunctation in l64a(a64(x)) round trip (POSIX says the + * result of that is "x in the low-order 32-bits". */ + printf("a64l(l64a(x)) (lower 32 bits of x are %ld): %ld\n", ((long)test_value_64bit) & 0xffffffff, a64l(l64a((long)test_value_64bit))); }