use core::{
mem,
num,
ptr,
};
use super::traits::IntoRepr;
use super::Repr;
use crate::{
ToCompactStringError,
UnwrapWithMsg,
};
const DEC_DIGITS_LUT: &[u8] = b"\
0001020304050607080910111213141516171819\
2021222324252627282930313233343536373839\
4041424344454647484950515253545556575859\
6061626364656667686970717273747576777879\
8081828384858687888990919293949596979899";
macro_rules! impl_IntoRepr {
($t:ident, $conv_ty:ident) => {
impl IntoRepr for $t {
fn into_repr(self) -> Result<Repr, ToCompactStringError> {
let num_digits = NumChars::num_chars(self);
let mut repr = Repr::with_capacity(num_digits).unwrap_with_msg();
#[allow(unused_comparisons)]
let is_nonnegative = self >= 0;
let mut n = if is_nonnegative {
self as $conv_ty
} else {
(!(self as $conv_ty)).wrapping_add(1)
};
let mut curr = num_digits as isize;
unsafe { repr.set_len(num_digits) };
let buf_ptr = unsafe { repr.as_mut_buf().as_mut_ptr() };
let lut_ptr = DEC_DIGITS_LUT.as_ptr();
unsafe {
if mem::size_of::<$t>() >= 2 {
while n >= 10000 {
let rem = (n % 10000) as isize;
n /= 10000;
let d1 = (rem / 100) << 1;
let d2 = (rem % 100) << 1;
curr -= 4;
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
ptr::copy_nonoverlapping(
lut_ptr.offset(d2),
buf_ptr.offset(curr + 2),
2,
);
}
}
let mut n = n as isize; if n >= 100 {
let d1 = (n % 100) << 1;
n /= 100;
curr -= 2;
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
}
if n < 10 {
curr -= 1;
*buf_ptr.offset(curr) = (n as u8) + b'0';
} else {
let d1 = n << 1;
curr -= 2;
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
}
if !is_nonnegative {
curr -= 1;
*buf_ptr.offset(curr) = b'-';
}
}
debug_assert_eq!(curr, 0);
Ok(repr)
}
}
};
}
impl_IntoRepr!(u8, u32);
impl_IntoRepr!(i8, u32);
impl_IntoRepr!(u16, u32);
impl_IntoRepr!(i16, u32);
impl_IntoRepr!(u32, u32);
impl_IntoRepr!(i32, u32);
impl_IntoRepr!(u64, u64);
impl_IntoRepr!(i64, u64);
#[cfg(target_pointer_width = "32")]
impl_IntoRepr!(usize, u32);
#[cfg(target_pointer_width = "32")]
impl_IntoRepr!(isize, u32);
#[cfg(target_pointer_width = "64")]
impl_IntoRepr!(usize, u64);
#[cfg(target_pointer_width = "64")]
impl_IntoRepr!(isize, u64);
impl IntoRepr for u128 {
#[inline]
fn into_repr(self) -> Result<Repr, ToCompactStringError> {
let mut buffer = itoa::Buffer::new();
Ok(Repr::new(buffer.format(self))?)
}
}
impl IntoRepr for i128 {
#[inline]
fn into_repr(self) -> Result<Repr, ToCompactStringError> {
let mut buffer = itoa::Buffer::new();
Ok(Repr::new(buffer.format(self))?)
}
}
macro_rules! impl_NonZero_IntoRepr {
($t:path) => {
impl IntoRepr for $t {
#[inline]
fn into_repr(self) -> Result<Repr, ToCompactStringError> {
self.get().into_repr()
}
}
};
}
impl_NonZero_IntoRepr!(num::NonZeroU8);
impl_NonZero_IntoRepr!(num::NonZeroI8);
impl_NonZero_IntoRepr!(num::NonZeroU16);
impl_NonZero_IntoRepr!(num::NonZeroI16);
impl_NonZero_IntoRepr!(num::NonZeroU32);
impl_NonZero_IntoRepr!(num::NonZeroI32);
impl_NonZero_IntoRepr!(num::NonZeroU64);
impl_NonZero_IntoRepr!(num::NonZeroI64);
impl_NonZero_IntoRepr!(num::NonZeroUsize);
impl_NonZero_IntoRepr!(num::NonZeroIsize);
impl_NonZero_IntoRepr!(num::NonZeroU128);
impl_NonZero_IntoRepr!(num::NonZeroI128);
trait NumChars {
fn num_chars(val: Self) -> usize;
}
impl NumChars for u8 {
#[inline(always)]
fn num_chars(val: u8) -> usize {
match val {
u8::MIN..=9 => 1,
10..=99 => 2,
100..=u8::MAX => 3,
}
}
}
impl NumChars for i8 {
#[inline(always)]
fn num_chars(val: i8) -> usize {
match val {
i8::MIN..=-100 => 4,
-99..=-10 => 3,
-9..=-1 => 2,
0..=9 => 1,
10..=99 => 2,
100..=i8::MAX => 3,
}
}
}
impl NumChars for u16 {
#[inline(always)]
fn num_chars(val: u16) -> usize {
match val {
u16::MIN..=9 => 1,
10..=99 => 2,
100..=999 => 3,
1000..=9999 => 4,
10000..=u16::MAX => 5,
}
}
}
impl NumChars for i16 {
#[inline(always)]
fn num_chars(val: i16) -> usize {
match val {
i16::MIN..=-10000 => 6,
-9999..=-1000 => 5,
-999..=-100 => 4,
-99..=-10 => 3,
-9..=-1 => 2,
0..=9 => 1,
10..=99 => 2,
100..=999 => 3,
1000..=9999 => 4,
10000..=i16::MAX => 5,
}
}
}
impl NumChars for u32 {
#[inline(always)]
fn num_chars(val: u32) -> usize {
match val {
u32::MIN..=9 => 1,
10..=99 => 2,
100..=999 => 3,
1000..=9999 => 4,
10000..=99999 => 5,
100000..=999999 => 6,
1000000..=9999999 => 7,
10000000..=99999999 => 8,
100000000..=999999999 => 9,
1000000000..=u32::MAX => 10,
}
}
}
impl NumChars for i32 {
#[inline(always)]
fn num_chars(val: i32) -> usize {
match val {
i32::MIN..=-1000000000 => 11,
-999999999..=-100000000 => 10,
-99999999..=-10000000 => 9,
-9999999..=-1000000 => 8,
-999999..=-100000 => 7,
-99999..=-10000 => 6,
-9999..=-1000 => 5,
-999..=-100 => 4,
-99..=-10 => 3,
-9..=-1 => 2,
0..=9 => 1,
10..=99 => 2,
100..=999 => 3,
1000..=9999 => 4,
10000..=99999 => 5,
100000..=999999 => 6,
1000000..=9999999 => 7,
10000000..=99999999 => 8,
100000000..=999999999 => 9,
1000000000..=i32::MAX => 10,
}
}
}
impl NumChars for u64 {
#[inline(always)]
fn num_chars(val: u64) -> usize {
match val {
u64::MIN..=9 => 1,
10..=99 => 2,
100..=999 => 3,
1000..=9999 => 4,
10000..=99999 => 5,
100000..=999999 => 6,
1000000..=9999999 => 7,
10000000..=99999999 => 8,
100000000..=999999999 => 9,
1000000000..=9999999999 => 10,
10000000000..=99999999999 => 11,
100000000000..=999999999999 => 12,
1000000000000..=9999999999999 => 13,
10000000000000..=99999999999999 => 14,
100000000000000..=999999999999999 => 15,
1000000000000000..=9999999999999999 => 16,
10000000000000000..=99999999999999999 => 17,
100000000000000000..=999999999999999999 => 18,
1000000000000000000..=9999999999999999999 => 19,
10000000000000000000..=u64::MAX => 20,
}
}
}
impl NumChars for i64 {
#[inline(always)]
fn num_chars(val: i64) -> usize {
match val {
i64::MIN..=-1000000000000000000 => 20,
-999999999999999999..=-100000000000000000 => 19,
-99999999999999999..=-10000000000000000 => 18,
-9999999999999999..=-1000000000000000 => 17,
-999999999999999..=-100000000000000 => 16,
-99999999999999..=-10000000000000 => 15,
-9999999999999..=-1000000000000 => 14,
-999999999999..=-100000000000 => 13,
-99999999999..=-10000000000 => 12,
-9999999999..=-1000000000 => 11,
-999999999..=-100000000 => 10,
-99999999..=-10000000 => 9,
-9999999..=-1000000 => 8,
-999999..=-100000 => 7,
-99999..=-10000 => 6,
-9999..=-1000 => 5,
-999..=-100 => 4,
-99..=-10 => 3,
-9..=-1 => 2,
0..=9 => 1,
10..=99 => 2,
100..=999 => 3,
1000..=9999 => 4,
10000..=99999 => 5,
100000..=999999 => 6,
1000000..=9999999 => 7,
10000000..=99999999 => 8,
100000000..=999999999 => 9,
1000000000..=9999999999 => 10,
10000000000..=99999999999 => 11,
100000000000..=999999999999 => 12,
1000000000000..=9999999999999 => 13,
10000000000000..=99999999999999 => 14,
100000000000000..=999999999999999 => 15,
1000000000000000..=9999999999999999 => 16,
10000000000000000..=99999999999999999 => 17,
100000000000000000..=999999999999999999 => 18,
1000000000000000000..=i64::MAX => 19,
}
}
}
impl NumChars for usize {
fn num_chars(val: usize) -> usize {
#[cfg(target_pointer_width = "32")]
{
u32::num_chars(val as u32)
}
#[cfg(target_pointer_width = "64")]
{
u64::num_chars(val as u64)
}
}
}
impl NumChars for isize {
fn num_chars(val: isize) -> usize {
#[cfg(target_pointer_width = "32")]
{
i32::num_chars(val as i32)
}
#[cfg(target_pointer_width = "64")]
{
i64::num_chars(val as i64)
}
}
}
#[cfg(test)]
mod tests {
use alloc::string::ToString;
use super::IntoRepr;
#[test]
fn test_from_u8_sanity() {
let vals = [u8::MIN, u8::MIN + 1, 0, 42, u8::MAX - 1, u8::MAX];
for x in &vals {
let repr = u8::into_repr(*x).unwrap();
assert_eq!(repr.as_str(), x.to_string());
}
}
#[test]
fn test_from_i8_sanity() {
let vals = [i8::MIN, i8::MIN + 1, 0, 42, i8::MAX - 1, i8::MAX];
for x in &vals {
let repr = i8::into_repr(*x).unwrap();
assert_eq!(repr.as_str(), x.to_string());
}
}
#[test]
fn test_from_u16_sanity() {
let vals = [u16::MIN, u16::MIN + 1, 0, 42, u16::MAX - 1, u16::MAX];
for x in &vals {
let repr = u16::into_repr(*x).unwrap();
assert_eq!(repr.as_str(), x.to_string());
}
}
#[test]
fn test_from_i16_sanity() {
let vals = [i16::MIN, i16::MIN + 1, 0, 42, i16::MAX - 1, i16::MAX];
for x in &vals {
let repr = i16::into_repr(*x).unwrap();
assert_eq!(repr.as_str(), x.to_string());
}
}
#[test]
fn test_from_u32_sanity() {
let vals = [u32::MIN, u32::MIN + 1, 0, 42, u32::MAX - 1, u32::MAX];
for x in &vals {
let repr = u32::into_repr(*x).unwrap();
assert_eq!(repr.as_str(), x.to_string());
}
}
#[test]
fn test_from_i32_sanity() {
let vals = [i32::MIN, i32::MIN + 1, 0, 42, i32::MAX - 1, i32::MAX];
for x in &vals {
let repr = i32::into_repr(*x).unwrap();
assert_eq!(repr.as_str(), x.to_string());
}
}
#[test]
fn test_from_u64_sanity() {
let vals = [u64::MIN, u64::MIN + 1, 0, 42, u64::MAX - 1, u64::MAX];
for x in &vals {
let repr = u64::into_repr(*x).unwrap();
assert_eq!(repr.as_str(), x.to_string());
}
}
#[test]
fn test_from_i64_sanity() {
let vals = [i64::MIN, i64::MIN + 1, 0, 42, i64::MAX - 1, i64::MAX];
for x in &vals {
let repr = i64::into_repr(*x).unwrap();
assert_eq!(repr.as_str(), x.to_string());
}
}
#[test]
fn test_from_usize_sanity() {
let vals = [
usize::MIN,
usize::MIN + 1,
0,
42,
usize::MAX - 1,
usize::MAX,
];
for x in &vals {
let repr = usize::into_repr(*x).unwrap();
assert_eq!(repr.as_str(), x.to_string());
}
}
#[test]
fn test_from_isize_sanity() {
let vals = [
isize::MIN,
isize::MIN + 1,
0,
42,
isize::MAX - 1,
isize::MAX,
];
for x in &vals {
let repr = isize::into_repr(*x).unwrap();
assert_eq!(repr.as_str(), x.to_string());
}
}
#[test]
fn test_from_u128_sanity() {
let vals = [u128::MIN, u128::MIN + 1, 0, 42, u128::MAX - 1, u128::MAX];
for x in &vals {
let repr = u128::into_repr(*x).unwrap();
assert_eq!(repr.as_str(), x.to_string());
}
}
#[test]
fn test_from_i128_sanity() {
let vals = [i128::MIN, i128::MIN + 1, 0, 42, i128::MAX - 1, i128::MAX];
for x in &vals {
let repr = i128::into_repr(*x).unwrap();
assert_eq!(repr.as_str(), x.to_string());
}
}
}