use crate::{AsU256, U256};
use core::fmt;
use core::mem::{self, MaybeUninit};
use core::num::ParseIntError;
use core::ptr;
use core::slice;
use core::str::{self, FromStr};
pub(crate) fn from_str_radix(src: &str, radix: u32) -> Result<U256, ParseIntError> {
assert!(
radix >= 2 && radix <= 36,
"from_str_radix_int: must lie in the range `[2, 36]` - found {}",
radix
);
if src.is_empty() {
return Err(Pie::Empty.into());
}
let mut result = U256::ZERO;
for &c in src.as_bytes() {
let x = match (c as char).to_digit(radix) {
Some(x) => x,
None => return Err(Pie::InvalidDigit.into()),
};
result = match result.checked_mul(radix.as_u256()) {
Some(result) => result,
None => return Err(Pie::Overflow.into()),
};
result = match result.checked_add(x.as_u256()) {
Some(result) => result,
None => return Err(Pie::Overflow.into()),
};
}
Ok(result)
}
enum Pie {
Empty,
InvalidDigit,
Overflow,
}
impl Into<ParseIntError> for Pie {
#[inline]
fn into(self) -> ParseIntError {
unsafe { mem::transmute(self) }
}
}
impl FromStr for U256 {
type Err = ParseIntError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
from_str_radix(s, 10)
}
}
impl fmt::Debug for U256 {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
const DEBUG_LOWER_HEX: u32 = 1 << 4;
const DEBUG_UPPER_HEX: u32 = 1 << 5;
#[allow(deprecated)]
let flags = f.flags();
if flags & DEBUG_LOWER_HEX != 0 {
fmt::LowerHex::fmt(self, f)
} else if flags & DEBUG_UPPER_HEX != 0 {
fmt::UpperHex::fmt(self, f)
} else {
fmt::Display::fmt(self, f)
}
}
}
const DEC_DIGITS_LUT: &[u8; 200] = b"\
0001020304050607080910111213141516171819\
2021222324252627282930313233343536373839\
4041424344454647484950515253545556575859\
6061626364656667686970717273747576777879\
8081828384858687888990919293949596979899";
impl fmt::Display for U256 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut n = *self;
let mut buf = [MaybeUninit::<u8>::uninit(); 79];
let mut curr = buf.len() as isize;
let buf_ptr = &mut buf[0] as *mut _ as *mut u8;
let lut_ptr = DEC_DIGITS_LUT.as_ptr();
unsafe {
while n >= 10000 {
let rem = *(n % 10000).low() 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.low() 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);
}
}
let buf_slice = unsafe {
str::from_utf8_unchecked(slice::from_raw_parts(
buf_ptr.offset(curr),
buf.len() - curr as usize,
))
};
f.pad_integral(true, "", buf_slice)
}
}
pub(crate) fn fmt_radix(
mut x: U256,
base: usize,
prefix: &str,
digits: &[u8],
f: &mut fmt::Formatter,
) -> fmt::Result {
let mut buf = [MaybeUninit::<u8>::uninit(); 256];
let mut curr = buf.len();
for byte in buf.iter_mut().rev() {
let n = (*x.low() as usize) % base;
x /= base.as_u256();
unsafe {
#[cfg(debug_assertions)]
let digit = digits[n];
#[cfg(not(debug_assertions))]
let digit = *digits.get_unchecked(n);
byte.as_mut_ptr().write(digit);
};
curr -= 1;
if x == 0 {
break;
};
}
let buf = &buf[curr..];
let buf = unsafe { str::from_utf8_unchecked(&*(buf as *const _ as *const [u8])) };
f.pad_integral(true, prefix, buf)
}
impl fmt::Binary for U256 {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_radix(*self, 2, "0b", b"01", f)
}
}
impl fmt::Octal for U256 {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_radix(*self, 8, "0o", b"01234567", f)
}
}
impl fmt::LowerHex for U256 {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_radix(*self, 16, "0x", b"0123456789abcdef", f)
}
}
impl fmt::UpperHex for U256 {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_radix(*self, 16, "0x", b"0123456789ABCDEF", f)
}
}
impl fmt::LowerExp for U256 {
#[allow(unused_comparisons)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::LowerExp::fmt(&self.as_f64(), f)
}
}
impl fmt::UpperExp for U256 {
#[allow(unused_comparisons)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::UpperExp::fmt(&self.as_f64(), f)
}
}
#[cfg(test)]
mod test {
use super::*;
use alloc::format;
#[test]
fn parse_int_error() {
assert_eq!(U256::from_str_radix("", 2), Err(Pie::Empty.into()));
assert_eq!(U256::from_str_radix("?", 2), Err(Pie::InvalidDigit.into()));
assert_eq!(
U256::from_str_radix("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", 36),
Err(Pie::Overflow.into())
);
}
#[test]
fn debug() {
assert_eq!(
format!("{:?}", U256::MAX),
"115792089237316195423570985008687907853269984665640564039457584007913129639935",
);
assert_eq!(
format!("{:x?}", U256::MAX),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
);
assert_eq!(
format!("{:#X?}", U256::MAX),
"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
);
}
#[test]
fn display() {
assert_eq!(
format!("{}", U256::MAX),
"115792089237316195423570985008687907853269984665640564039457584007913129639935",
);
}
#[test]
fn radix() {
assert_eq!(format!("{:b}", U256::new(42)), "101010");
assert_eq!(format!("{:o}", U256::new(42)), "52");
assert_eq!(format!("{:x}", U256::new(42)), "2a");
}
#[test]
fn exp() {
assert_eq!(format!("{:e}", U256::new(42)), "4.2e1");
assert_eq!(format!("{:e}", U256::new(10).pow(77)), "1e77");
assert_eq!(format!("{:E}", U256::new(10).pow(39) * 1337), "1.337E42");
}
}