use sealed::mem;
use sealed::ptr;
#[cfg(all(feature = "alloc", not(feature = "std")))]
pub use alloc::string::String;
#[cfg(all(feature = "alloc", not(feature = "std")))]
pub use alloc::vec::Vec;
use table::*;
use util::{distance, floor, ln};
macro_rules! digits {
($value:ident, $base:ident) => ({
match $value {
0 => 1,
_ => {
let v = $value as f64;
let b = $base as f64;
let digits = floor((ln(v) / ln(b)) + 1.0);
digits as usize
}
}
})
}
macro_rules! check_digits {
($value:ident, $first:ident, $last:ident, $base:ident) => ({
debug_assert!(distance($first, $last) >= digits!($value, $base), "Need a larger buffer.");
})
}
const MAX_DIGITS: usize = 128;
#[cfg(feature = "table")]
#[inline]
unsafe extern "C" fn itoa_optimized(
mut value: u64,
first: *mut u8,
base: u64,
table: *const u8
)
-> *mut u8
{
let base2 = base * base;
let base4 = base2 * base2;
if value == 0 {
*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 = (2 * (rem / base2)) as usize;
let r2 = (2 * (rem % base2)) as usize;
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 = (2 * (value % base2)) as usize;
value /= base2;
curr -= 2;
ptr::copy_nonoverlapping(table.add(rem), p.add(curr), 2);
}
if value < base {
curr -= 1;
*p.add(curr) = *BASEN.get_unchecked(value as usize);
} else {
let rem = 2 * value as usize;
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 extern "C" fn itoa_naive(
mut value: u64,
first: *mut u8,
base: u64
)
-> *mut u8
{
debug_assert!(base >= 2 && base <= 36,"Numerical base must be from 2-36");
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 = (value % base) as usize;
value /= base;
curr -= 1;
*p.add(curr) = *BASEN.get_unchecked(rem);
}
rem = (value % base) as usize;
curr -= 1;
*p.add(curr) = *BASEN.get_unchecked(rem);
let len = buffer.len() - curr;
ptr::copy_nonoverlapping(p.add(curr), first, len);
first.add(len)
}
#[inline]
pub(crate) unsafe extern "C" fn itoa_forward(
value: u64,
first: *mut u8,
base: u64
) -> *mut u8
{
#[cfg(feature = "table")]
match base {
2 => itoa_optimized(value, first, base, BASE2.as_ptr()),
3 => itoa_optimized(value, first, base, BASE3.as_ptr()),
4 => itoa_optimized(value, first, base, BASE4.as_ptr()),
5 => itoa_optimized(value, first, base, BASE5.as_ptr()),
6 => itoa_optimized(value, first, base, BASE6.as_ptr()),
7 => itoa_optimized(value, first, base, BASE7.as_ptr()),
8 => itoa_optimized(value, first, base, BASE8.as_ptr()),
9 => itoa_optimized(value, first, base, BASE9.as_ptr()),
10 => itoa_optimized(value, first, base, BASE10.as_ptr()),
11 => itoa_optimized(value, first, base, BASE11.as_ptr()),
12 => itoa_optimized(value, first, base, BASE12.as_ptr()),
13 => itoa_optimized(value, first, base, BASE13.as_ptr()),
14 => itoa_optimized(value, first, base, BASE14.as_ptr()),
15 => itoa_optimized(value, first, base, BASE15.as_ptr()),
16 => itoa_optimized(value, first, base, BASE16.as_ptr()),
17 => itoa_optimized(value, first, base, BASE17.as_ptr()),
18 => itoa_optimized(value, first, base, BASE18.as_ptr()),
19 => itoa_optimized(value, first, base, BASE19.as_ptr()),
20 => itoa_optimized(value, first, base, BASE20.as_ptr()),
21 => itoa_optimized(value, first, base, BASE21.as_ptr()),
22 => itoa_optimized(value, first, base, BASE22.as_ptr()),
23 => itoa_optimized(value, first, base, BASE23.as_ptr()),
24 => itoa_optimized(value, first, base, BASE24.as_ptr()),
25 => itoa_optimized(value, first, base, BASE25.as_ptr()),
26 => itoa_optimized(value, first, base, BASE26.as_ptr()),
27 => itoa_optimized(value, first, base, BASE27.as_ptr()),
28 => itoa_optimized(value, first, base, BASE28.as_ptr()),
29 => itoa_optimized(value, first, base, BASE29.as_ptr()),
30 => itoa_optimized(value, first, base, BASE30.as_ptr()),
31 => itoa_optimized(value, first, base, BASE31.as_ptr()),
32 => itoa_optimized(value, first, base, BASE32.as_ptr()),
33 => itoa_optimized(value, first, base, BASE33.as_ptr()),
34 => itoa_optimized(value, first, base, BASE34.as_ptr()),
35 => itoa_optimized(value, first, base, BASE35.as_ptr()),
36 => itoa_optimized(value, first, base, BASE36.as_ptr()),
_ => unreachable!(),
}
#[cfg(not(feature = "table"))]
itoa_naive(value, first, base)
}
macro_rules! itoa_unsigned {
($value:ident, $first:ident, $last:ident, $base:ident) => ({
debug_assert!($first <= $last);
check_digits!($value, $first, $last, $base);
let v = $value as u64;
let b = $base as u64;
itoa_forward(v, $first, b)
})
}
macro_rules! itoa_signed {
($value:ident, $first:ident, $last:ident, $base:ident) => ({
debug_assert!($first <= $last);
check_digits!($value, $first, $last, $base);
let v: u64;
if $value < 0 {
*$first = b'-';
v = ($value as i64).wrapping_neg() as u64;
$first = $first.add(1);
} else {
v = $value as u64;
}
let b = $base as u64;
itoa_forward(v, $first, b)
})
}
macro_rules! unsigned_unsafe_impl {
($func:ident, $t:ty) => (
#[inline]
pub unsafe extern "C" fn $func(
value: $t,
first: *mut u8,
last: *mut u8,
base: u8
)
-> *mut u8
{
itoa_unsigned!(value, first, last, base)
}
)
}
unsigned_unsafe_impl!(u8toa_unsafe, u8);
unsigned_unsafe_impl!(u16toa_unsafe, u16);
unsigned_unsafe_impl!(u32toa_unsafe, u32);
unsigned_unsafe_impl!(u64toa_unsafe, u64);
unsigned_unsafe_impl!(usizetoa_unsafe, usize);
macro_rules! signed_unsafe_impl {
($func:ident, $t:ty) => (
#[inline]
pub unsafe extern "C" fn $func(
value: $t,
mut first: *mut u8,
last: *mut u8,
base: u8
)
-> *mut u8
{
itoa_signed!(value, first, last, base)
}
)
}
signed_unsafe_impl!(i8toa_unsafe, i8);
signed_unsafe_impl!(i16toa_unsafe, i16);
signed_unsafe_impl!(i32toa_unsafe, i32);
signed_unsafe_impl!(i64toa_unsafe, i64);
signed_unsafe_impl!(isizetoa_unsafe, isize);
#[cfg(any(feature = "std", feature = "alloc"))]
string_impl!(u8toa_string, u8, u8toa_unsafe, 16);
#[cfg(any(feature = "std", feature = "alloc"))]
string_impl!(u16toa_string, u16, u16toa_unsafe, 32);
#[cfg(any(feature = "std", feature = "alloc"))]
string_impl!(u32toa_string, u32, u32toa_unsafe, 64);
#[cfg(any(feature = "std", feature = "alloc"))]
string_impl!(u64toa_string, u64, u64toa_unsafe, 128);
#[cfg(any(feature = "std", feature = "alloc"))]
string_impl!(usizetoa_string, usize, usizetoa_unsafe, 128);
#[cfg(any(feature = "std", feature = "alloc"))]
string_impl!(i8toa_string, i8, i8toa_unsafe, 16);
#[cfg(any(feature = "std", feature = "alloc"))]
string_impl!(i16toa_string, i16, i16toa_unsafe, 32);
#[cfg(any(feature = "std", feature = "alloc"))]
string_impl!(i32toa_string, i32, i32toa_unsafe, 64);
#[cfg(any(feature = "std", feature = "alloc"))]
string_impl!(i64toa_string, i64, i64toa_unsafe, 128);
#[cfg(any(feature = "std", feature = "alloc"))]
string_impl!(isizetoa_string, isize, isizetoa_unsafe, 128);
#[cfg(any(feature = "std", feature = "alloc"))]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn u8toa_test() {
assert_eq!("0", u8toa_string(0, 10));
assert_eq!("1", u8toa_string(1, 10));
assert_eq!("127", u8toa_string(127, 10));
assert_eq!("128", u8toa_string(128, 10));
assert_eq!("255", u8toa_string(255, 10));
assert_eq!("255", u8toa_string(-1i8 as u8, 10));
}
#[test]
fn i8toa_test() {
assert_eq!("0", i8toa_string(0, 10));
assert_eq!("1", i8toa_string(1, 10));
assert_eq!("127", i8toa_string(127, 10));
assert_eq!("-128", i8toa_string(128u8 as i8, 10));
assert_eq!("-1", i8toa_string(255u8 as i8, 10));
assert_eq!("-1", i8toa_string(-1, 10));
}
#[test]
fn u16toa_test() {
assert_eq!("0", u16toa_string(0, 10));
assert_eq!("1", u16toa_string(1, 10));
assert_eq!("32767", u16toa_string(32767, 10));
assert_eq!("32768", u16toa_string(32768, 10));
assert_eq!("65535", u16toa_string(65535, 10));
assert_eq!("65535", u16toa_string(-1i16 as u16, 10));
}
#[test]
fn i16toa_test() {
assert_eq!("0", i16toa_string(0, 10));
assert_eq!("1", i16toa_string(1, 10));
assert_eq!("32767", i16toa_string(32767, 10));
assert_eq!("-32768", i16toa_string(32768u16 as i16, 10));
assert_eq!("-1", i16toa_string(65535u16 as i16, 10));
assert_eq!("-1", i16toa_string(-1, 10));
}
#[test]
fn u32toa_test() {
assert_eq!("0", u32toa_string(0, 10));
assert_eq!("1", u32toa_string(1, 10));
assert_eq!("2147483647", u32toa_string(2147483647, 10));
assert_eq!("2147483648", u32toa_string(2147483648, 10));
assert_eq!("4294967295", u32toa_string(4294967295, 10));
assert_eq!("4294967295", u32toa_string(-1i32 as u32, 10));
}
#[test]
fn i32toa_test() {
assert_eq!("0", i32toa_string(0, 10));
assert_eq!("1", i32toa_string(1, 10));
assert_eq!("2147483647", i32toa_string(2147483647, 10));
assert_eq!("-2147483648", i32toa_string(2147483648u32 as i32, 10));
assert_eq!("-1", i32toa_string(4294967295u32 as i32, 10));
assert_eq!("-1", i32toa_string(-1, 10));
}
#[test]
fn u64toa_test() {
assert_eq!("0", u64toa_string(0, 10));
assert_eq!("1", u64toa_string(1, 10));
assert_eq!("9223372036854775807", u64toa_string(9223372036854775807, 10));
assert_eq!("9223372036854775808", u64toa_string(9223372036854775808, 10));
assert_eq!("18446744073709551615", u64toa_string(18446744073709551615, 10));
assert_eq!("18446744073709551615", u64toa_string(-1i64 as u64, 10));
}
#[test]
fn i64toa_test() {
assert_eq!("0", i64toa_string(0, 10));
assert_eq!("1", i64toa_string(1, 10));
assert_eq!("9223372036854775807", i64toa_string(9223372036854775807, 10));
assert_eq!("-9223372036854775808", i64toa_string(9223372036854775808u64 as i64, 10));
assert_eq!("-1", i64toa_string(18446744073709551615u64 as i64, 10));
assert_eq!("-1", i64toa_string(-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, i8toa_string(37, *base));
}
}
}