use lib::{mem, ptr};
use table::*;
use util::*;
#[inline]
fn digits<Value: Integer>(value: Value, base: u32) -> usize {
match value.is_zero() {
true => 1,
false => {
let v: f64 = as_cast(value);
let b: f64 = as_cast(base);
let digits = ((v.ln() / b.ln()) + 1.0).floor();
digits as usize
}
}
}
macro_rules! check_buffer {
($value:ident, $first:ident, $last:ident, $base:ident) => ({
let has_space = distance($first, $last) >= digits($value, $base);
debug_assert!(has_space, "Need a larger buffer.");
})
}
const MAX_DIGITS: usize = 128;
#[cfg(feature = "table")]
#[inline]
unsafe fn optimized<T>(mut value: T, base: T, table: *const u8, first: *mut u8)
-> *mut u8
where T: UnsignedInteger
{
let base2 = base * base;
let base4 = base2 * base2;
if value == T::ZERO {
*first = b'0';
return first.add(1);
}
let mut buffer: [u8; MAX_DIGITS] = mem::uninitialized();
let mut rem: usize;
let mut curr = buffer.len();
let p: *mut u8 = buffer.as_mut_ptr();
while value >= base4 {
let rem = value % base4;
value /= base4;
let r1: usize = as_cast(T::TWO * (rem / base2));
let r2: usize = as_cast(T::TWO * (rem % base2));
curr -= 4;
ptr::copy_nonoverlapping(table.add(r1), p.add(curr), 2);
ptr::copy_nonoverlapping(table.add(r2), p.add(curr + 2), 2);
}
while value >= base2 {
rem = as_cast(T::TWO * (value % base2));
value /= base2;
curr -= 2;
ptr::copy_nonoverlapping(table.add(rem), p.add(curr), 2);
}
if value < base {
curr -= 1;
*p.add(curr) = digit_to_char(value);
} else {
rem = as_cast(T::TWO * value);
curr -= 2;
ptr::copy_nonoverlapping(table.add(rem), p.add(curr), 2);
}
let len = buffer.len() - curr;
ptr::copy_nonoverlapping(p.add(curr), first, len);
first.add(len)
}
#[cfg(not(feature = "table"))]
#[inline]
unsafe fn naive<T>(mut value: T, base: T, first: *mut u8)
-> *mut u8
where T: UnsignedInteger
{
let mut buffer: [u8; MAX_DIGITS] = mem::uninitialized();
let mut rem: usize;
let mut curr = buffer.len();
let p: *mut u8 = buffer.as_mut_ptr();
while value >= base {
rem = as_cast(value % base);
value /= base;
curr -= 1;
*p.add(curr) = digit_to_char(rem);
}
rem = as_cast(value % base);
curr -= 1;
*p.add(curr) = digit_to_char(rem);
let len = buffer.len() - curr;
ptr::copy_nonoverlapping(p.add(curr), first, len);
first.add(len)
}
#[inline]
pub(crate) unsafe fn forward<T>(value: T, base: u32, first: *mut u8)
-> *mut u8
where T: UnsignedInteger
{
#[cfg(feature = "table")] {
let table = match base {
2 => DIGIT_TO_BASE2_SQUARED.as_ptr(),
3 => DIGIT_TO_BASE3_SQUARED.as_ptr(),
4 => DIGIT_TO_BASE4_SQUARED.as_ptr(),
5 => DIGIT_TO_BASE5_SQUARED.as_ptr(),
6 => DIGIT_TO_BASE6_SQUARED.as_ptr(),
7 => DIGIT_TO_BASE7_SQUARED.as_ptr(),
8 => DIGIT_TO_BASE8_SQUARED.as_ptr(),
9 => DIGIT_TO_BASE9_SQUARED.as_ptr(),
10 => DIGIT_TO_BASE10_SQUARED.as_ptr(),
11 => DIGIT_TO_BASE11_SQUARED.as_ptr(),
12 => DIGIT_TO_BASE12_SQUARED.as_ptr(),
13 => DIGIT_TO_BASE13_SQUARED.as_ptr(),
14 => DIGIT_TO_BASE14_SQUARED.as_ptr(),
15 => DIGIT_TO_BASE15_SQUARED.as_ptr(),
16 => DIGIT_TO_BASE16_SQUARED.as_ptr(),
17 => DIGIT_TO_BASE17_SQUARED.as_ptr(),
18 => DIGIT_TO_BASE18_SQUARED.as_ptr(),
19 => DIGIT_TO_BASE19_SQUARED.as_ptr(),
20 => DIGIT_TO_BASE20_SQUARED.as_ptr(),
21 => DIGIT_TO_BASE21_SQUARED.as_ptr(),
22 => DIGIT_TO_BASE22_SQUARED.as_ptr(),
23 => DIGIT_TO_BASE23_SQUARED.as_ptr(),
24 => DIGIT_TO_BASE24_SQUARED.as_ptr(),
25 => DIGIT_TO_BASE25_SQUARED.as_ptr(),
26 => DIGIT_TO_BASE26_SQUARED.as_ptr(),
27 => DIGIT_TO_BASE27_SQUARED.as_ptr(),
28 => DIGIT_TO_BASE28_SQUARED.as_ptr(),
29 => DIGIT_TO_BASE29_SQUARED.as_ptr(),
30 => DIGIT_TO_BASE30_SQUARED.as_ptr(),
31 => DIGIT_TO_BASE31_SQUARED.as_ptr(),
32 => DIGIT_TO_BASE32_SQUARED.as_ptr(),
33 => DIGIT_TO_BASE33_SQUARED.as_ptr(),
34 => DIGIT_TO_BASE34_SQUARED.as_ptr(),
35 => DIGIT_TO_BASE35_SQUARED.as_ptr(),
36 => DIGIT_TO_BASE36_SQUARED.as_ptr(),
_ => unreachable!(),
};
let base: T = as_cast(base);
optimized(value, base, table, first)
}
#[cfg(not(feature = "table"))] {
let base: T = as_cast(base);
naive(value, base, first)
}
}
#[inline]
pub(crate) unsafe fn unsigned<Value, UWide>(value: Value, base: u32, first: *mut u8, last: *mut u8)
-> *mut u8
where Value: UnsignedInteger,
UWide: UnsignedInteger
{
debug_assert!(first <= last);
check_buffer!(value, first, last, base);
let v: UWide = as_cast(value);
forward(v, base, first)
}
#[inline]
pub(crate) unsafe fn signed<Value, UWide, IWide>(value: Value, base: u32, mut first: *mut u8, last: *mut u8)
-> *mut u8
where Value: SignedInteger,
UWide: UnsignedInteger,
IWide: SignedInteger
{
debug_assert!(first <= last);
check_buffer!(value, first, last, base);
let v: UWide;
if value < Value::ZERO {
*first = b'-';
let wide: IWide = as_cast(value);
v = as_cast(wide.wrapping_neg());
first = first.add(1);
} else {
v = as_cast(value);
}
forward(v, base, first)
}
macro_rules! generate_unsafe_unsigned {
($name:ident, $t:ty, $uwide:ty) => (
#[inline]
pub unsafe extern "C" fn $name(value: $t, base: u8, first: *mut u8, last: *mut u8) -> *mut u8
{
unsigned::<$t, $uwide>(value, base.into(), first, last)
}
)
}
generate_unsafe_unsigned!(u8toa_unsafe, u8, u32);
generate_unsafe_unsigned!(u16toa_unsafe, u16, u32);
generate_unsafe_unsigned!(u32toa_unsafe, u32, u32);
generate_unsafe_unsigned!(u64toa_unsafe, u64, u64);
generate_unsafe_unsigned!(usizetoa_unsafe, usize, usize);
macro_rules! generate_unsafe_signed {
($name:ident, $t:ty, $uwide:ty, $iwide:ty) => (
#[inline]
pub unsafe extern "C" fn $name(value: $t, base: u8, first: *mut u8, last: *mut u8)
-> *mut u8
{
signed::<$t, $uwide, $iwide>(value, base.into(), first, last)
}
)
}
generate_unsafe_signed!(i8toa_unsafe, i8, u32, i32);
generate_unsafe_signed!(i16toa_unsafe, i16, u32, i32);
generate_unsafe_signed!(i32toa_unsafe, i32, u32, i32);
generate_unsafe_signed!(i64toa_unsafe, i64, u64, i64);
generate_unsafe_signed!(isizetoa_unsafe, isize, usize, isize);
generate_to_bytes_local!(u8toa_local, u8, u8toa_unsafe);
generate_to_bytes_local!(u16toa_local, u16, u16toa_unsafe);
generate_to_bytes_local!(u32toa_local, u32, u32toa_unsafe);
generate_to_bytes_local!(u64toa_local, u64, u64toa_unsafe);
generate_to_bytes_local!(usizetoa_local, usize, usizetoa_unsafe);
generate_to_bytes_local!(i8toa_local, i8, i8toa_unsafe);
generate_to_bytes_local!(i16toa_local, i16, i16toa_unsafe);
generate_to_bytes_local!(i32toa_local, i32, i32toa_unsafe);
generate_to_bytes_local!(i64toa_local, i64, i64toa_unsafe);
generate_to_bytes_local!(isizetoa_local, isize, isizetoa_unsafe);
generate_to_bytes_api!(u8toa_bytes, u8, u8toa_local, 16);
generate_to_bytes_api!(u16toa_bytes, u16, u16toa_local, 32);
generate_to_bytes_api!(u32toa_bytes, u32, u32toa_local, 64);
generate_to_bytes_api!(u64toa_bytes, u64, u64toa_local, 128);
generate_to_bytes_api!(usizetoa_bytes, usize, usizetoa_local, 128);
generate_to_bytes_api!(i8toa_bytes, i8, i8toa_local, 16);
generate_to_bytes_api!(i16toa_bytes, i16, i16toa_local, 32);
generate_to_bytes_api!(i32toa_bytes, i32, i32toa_local, 64);
generate_to_bytes_api!(i64toa_bytes, i64, i64toa_local, 128);
generate_to_bytes_api!(isizetoa_bytes, isize, isizetoa_local, 128);
#[cfg(test)]
mod tests {
use atoi::*;
use super::*;
#[test]
fn u8toa_test() {
assert_eq!(b"0".to_vec(), u8toa_bytes(0, 10));
assert_eq!(b"1".to_vec(), u8toa_bytes(1, 10));
assert_eq!(b"127".to_vec(), u8toa_bytes(127, 10));
assert_eq!(b"128".to_vec(), u8toa_bytes(128, 10));
assert_eq!(b"255".to_vec(), u8toa_bytes(255, 10));
assert_eq!(b"255".to_vec(), u8toa_bytes(-1i8 as u8, 10));
}
#[test]
fn i8toa_test() {
assert_eq!(b"0".to_vec(), i8toa_bytes(0, 10));
assert_eq!(b"1".to_vec(), i8toa_bytes(1, 10));
assert_eq!(b"127".to_vec(), i8toa_bytes(127, 10));
assert_eq!(b"-128".to_vec(), i8toa_bytes(128u8 as i8, 10));
assert_eq!(b"-1".to_vec(), i8toa_bytes(255u8 as i8, 10));
assert_eq!(b"-1".to_vec(), i8toa_bytes(-1, 10));
}
#[test]
fn u16toa_test() {
assert_eq!(b"0".to_vec(), u16toa_bytes(0, 10));
assert_eq!(b"1".to_vec(), u16toa_bytes(1, 10));
assert_eq!(b"32767".to_vec(), u16toa_bytes(32767, 10));
assert_eq!(b"32768".to_vec(), u16toa_bytes(32768, 10));
assert_eq!(b"65535".to_vec(), u16toa_bytes(65535, 10));
assert_eq!(b"65535".to_vec(), u16toa_bytes(-1i16 as u16, 10));
}
#[test]
fn i16toa_test() {
assert_eq!(b"0".to_vec(), i16toa_bytes(0, 10));
assert_eq!(b"1".to_vec(), i16toa_bytes(1, 10));
assert_eq!(b"32767".to_vec(), i16toa_bytes(32767, 10));
assert_eq!(b"-32768".to_vec(), i16toa_bytes(32768u16 as i16, 10));
assert_eq!(b"-1".to_vec(), i16toa_bytes(65535u16 as i16, 10));
assert_eq!(b"-1".to_vec(), i16toa_bytes(-1, 10));
}
#[test]
fn u32toa_test() {
assert_eq!(b"0".to_vec(), u32toa_bytes(0, 10));
assert_eq!(b"1".to_vec(), u32toa_bytes(1, 10));
assert_eq!(b"2147483647".to_vec(), u32toa_bytes(2147483647, 10));
assert_eq!(b"2147483648".to_vec(), u32toa_bytes(2147483648, 10));
assert_eq!(b"4294967295".to_vec(), u32toa_bytes(4294967295, 10));
assert_eq!(b"4294967295".to_vec(), u32toa_bytes(-1i32 as u32, 10));
}
#[test]
fn i32toa_test() {
assert_eq!(b"0".to_vec(), i32toa_bytes(0, 10));
assert_eq!(b"1".to_vec(), i32toa_bytes(1, 10));
assert_eq!(b"2147483647".to_vec(), i32toa_bytes(2147483647, 10));
assert_eq!(b"-2147483648".to_vec(), i32toa_bytes(2147483648u32 as i32, 10));
assert_eq!(b"-1".to_vec(), i32toa_bytes(4294967295u32 as i32, 10));
assert_eq!(b"-1".to_vec(), i32toa_bytes(-1, 10));
}
#[test]
fn u64toa_test() {
assert_eq!(b"0".to_vec(), u64toa_bytes(0, 10));
assert_eq!(b"1".to_vec(), u64toa_bytes(1, 10));
assert_eq!(b"9223372036854775807".to_vec(), u64toa_bytes(9223372036854775807, 10));
assert_eq!(b"9223372036854775808".to_vec(), u64toa_bytes(9223372036854775808, 10));
assert_eq!(b"18446744073709551615".to_vec(), u64toa_bytes(18446744073709551615, 10));
assert_eq!(b"18446744073709551615".to_vec(), u64toa_bytes(-1i64 as u64, 10));
}
#[test]
fn i64toa_test() {
assert_eq!(b"0".to_vec(), i64toa_bytes(0, 10));
assert_eq!(b"1".to_vec(), i64toa_bytes(1, 10));
assert_eq!(b"9223372036854775807".to_vec(), i64toa_bytes(9223372036854775807, 10));
assert_eq!(b"-9223372036854775808".to_vec(), i64toa_bytes(9223372036854775808u64 as i64, 10));
assert_eq!(b"-1".to_vec(), i64toa_bytes(18446744073709551615u64 as i64, 10));
assert_eq!(b"-1".to_vec(), i64toa_bytes(-1, 10));
}
#[test]
fn basen_test() {
let data = [
(2, "100101"),
(3, "1101"),
(4, "211"),
(5, "122"),
(6, "101"),
(7, "52"),
(8, "45"),
(9, "41"),
(10, "37"),
(11, "34"),
(12, "31"),
(13, "2B"),
(14, "29"),
(15, "27"),
(16, "25"),
(17, "23"),
(18, "21"),
(19, "1I"),
(20, "1H"),
(21, "1G"),
(22, "1F"),
(23, "1E"),
(24, "1D"),
(25, "1C"),
(26, "1B"),
(27, "1A"),
(28, "19"),
(29, "18"),
(30, "17"),
(31, "16"),
(32, "15"),
(33, "14"),
(34, "13"),
(35, "12"),
(36, "11"),
];
for (base, expected) in data.iter() {
assert_eq!(expected.as_bytes().to_vec(), i8toa_bytes(37, *base));
}
}
quickcheck! {
fn u8_quickcheck(i: u8) -> bool {
i == atou8_bytes(10, u8toa_bytes(i, 10).as_slice())
}
fn u16_quickcheck(i: u16) -> bool {
i == atou16_bytes(10, u16toa_bytes(i, 10).as_slice())
}
fn u32_quickcheck(i: u32) -> bool {
i == atou32_bytes(10, u32toa_bytes(i, 10).as_slice())
}
fn u64_quickcheck(i: u64) -> bool {
i == atou64_bytes(10, u64toa_bytes(i, 10).as_slice())
}
fn usize_quickcheck(i: usize) -> bool {
i == atousize_bytes(10, usizetoa_bytes(i, 10).as_slice())
}
fn i8_quickcheck(i: i8) -> bool {
i == atoi8_bytes(10, i8toa_bytes(i, 10).as_slice())
}
fn i16_quickcheck(i: i16) -> bool {
i == atoi16_bytes(10, i16toa_bytes(i, 10).as_slice())
}
fn i32_quickcheck(i: i32) -> bool {
i == atoi32_bytes(10, i32toa_bytes(i, 10).as_slice())
}
fn i64_quickcheck(i: i64) -> bool {
i == atoi64_bytes(10, i64toa_bytes(i, 10).as_slice())
}
fn isize_quickcheck(i: isize) -> bool {
i == atoisize_bytes(10, isizetoa_bytes(i, 10).as_slice())
}
}
}