use core::str::from_utf8_unchecked;
use crate::{
decimal::{
dec::{construct::construct_with_clength, ExtraPrecision},
signals::Signals,
Context, Decimal, DecimalError, ParseError, Sign,
},
int::{
intrinsics::{Digit, POWER},
math::overflowing_mul10,
UInt,
},
};
#[inline]
pub const fn from_slice<const N: usize>(
buf: &[u8],
ctx: Context,
) -> Result<Decimal<N>, ParseError> {
if buf.is_empty() {
return Err(ParseError::Empty);
}
let len = buf.len();
let mut sign = Sign::Plus;
let mut i = 0;
match buf[0] {
b'+' => {
i = 1;
}
b'-' => {
sign = Sign::Minus;
i = 1;
}
b'n' | b'N' => {
if bytes_equal_ci(buf.split_at(1).1, b"an") {
return Ok(Decimal::NAN);
}
}
_ => {}
}
if i == len {
return Err(ParseError::Empty);
}
if bytes_equal_ci(buf.split_at(i).1, b"inf") || bytes_equal_ci(buf.split_at(i).1, b"infinity") {
return Ok(Decimal::INFINITY.set_ctx(ctx).set_sign(sign));
}
let mut clength = 0;
let mut decimal_offset: Option<i32> = None;
let mut exponent_value: Option<i32> = None;
let mut dot = None;
let mut ovf;
let mut is_first_digit = true;
let mut value = UInt::ZERO;
while i < len {
let mut digits_count = 0;
let mut n = 0;
while i < len && (digits_count < POWER) {
let b = buf[i];
let d = match b {
b'.' => {
if dot.is_some() {
return Err(ParseError::InvalidLiteral);
} else {
dot = Some(digits_count);
i += 1;
continue;
}
}
b'_' => {
i += 1;
continue;
}
b'E' | b'e' => {
if exponent_value.is_some() {
return Err(ParseError::InvalidLiteral);
} else {
match parse_exp(buf, i + 1) {
Ok(exp) => {
exponent_value = Some(exp);
}
Err(e) => {
return Err(e);
}
}
i = len;
break;
}
}
b'0'..=b'9' => b - b'0',
_ => {
return Err(ParseError::InvalidLiteral);
}
};
n = ((n + (n << 2)) << 1) + d as Digit;
digits_count += 1;
i += 1;
}
clength += digits_count;
if is_first_digit {
if digits_count == 0 {
return Err(ParseError::Empty);
}
value = UInt::from_digit(n);
is_first_digit = false;
} else if digits_count > 0 {
(value, ovf) = overflowing_mul10(value, digits_count);
if ovf {
return Err(overflow(sign));
}
let next = UInt::from_digit(n);
(value, ovf) = value.overflowing_add(next);
if ovf {
return Err(overflow(sign));
}
}
if let Some(current) = decimal_offset {
let Some(dig) = checked_u32_i32(digits_count) else {
return Err(ParseError::ExponentOverflow);
};
let Some(current) = current.checked_add(dig) else {
return Err(ParseError::ExponentOverflow);
};
decimal_offset = Some(current);
} else if let Some(dot_pos) = dot {
let Some(dig) = checked_u32_i32(digits_count) else {
return Err(ParseError::ExponentOverflow);
};
let Some(pos) = checked_u32_i32(dot_pos) else {
return Err(ParseError::ExponentOverflow);
};
let Some(current) = dig.checked_sub(pos) else {
return Err(ParseError::ExponentOverflow);
};
decimal_offset = Some(current);
}
}
let exp = match make_exp(decimal_offset, exponent_value) {
Ok(exp) => exp,
Err(e) => {
return Err(e);
}
};
let dec = construct_with_clength(
value,
exp,
sign,
Signals::empty(),
ctx,
ExtraPrecision::new(),
clength,
);
if dec.is_nan() {
return Err(ParseError::Unknown);
} else if dec.is_infinite() {
return Err(overflow(dec.sign()));
}
match dec.ok_or_err() {
Ok(dec) => Ok(dec),
Err(e) => Err(match e {
DecimalError::Overflow => overflow(dec.sign()),
DecimalError::Underflow => overflow(dec.sign()),
_ => ParseError::Unknown,
}),
}
}
#[inline]
const fn checked_u32_i32(u: u32) -> Option<i32> {
let max = i32::MAX as u32;
if u > max {
None
} else {
Some(u as i32)
}
}
#[inline]
const fn make_exp(
decimal_offset: Option<i32>,
exponent_value: Option<i32>,
) -> Result<i32, ParseError> {
let decimal_offset = if let Some(decimal_offset) = decimal_offset {
decimal_offset
} else {
0
};
let exponent_value = if let Some(exponent_value) = exponent_value {
exponent_value
} else {
0
};
let Some(exp) = exponent_value.checked_sub(decimal_offset) else {
return Err(ParseError::ExponentOverflow);
};
Ok(exp)
}
#[inline]
const fn parse_exp(buf: &[u8], pos: usize) -> Result<i32, ParseError> {
if pos >= buf.len() {
return Err(ParseError::Empty);
}
#[allow(unsafe_code)]
let src = unsafe { from_utf8_unchecked(buf.split_at(pos).1) };
match i32::from_str_radix(src, 10) {
Ok(exp) => Ok(exp),
Err(e) => {
let e = ParseError::from_int_error_kind(e.kind());
Err(match e {
ParseError::PosOverflow => ParseError::ExponentOverflow,
ParseError::NegOverflow => ParseError::ExponentOverflow,
_ => e,
})
}
}
}
#[inline]
const fn bytes_equal_ci(lhs: &[u8], rhs: &[u8]) -> bool {
if lhs.len() != rhs.len() {
return false;
}
let mut i = 0;
while i < lhs.len() {
if !lhs[i].eq_ignore_ascii_case(&rhs[i]) {
return false;
}
i += 1;
}
true
}
#[inline(always)]
const fn overflow(sign: Sign) -> ParseError {
if matches!(sign, Sign::Minus) {
ParseError::NegOverflow
} else {
ParseError::PosOverflow
}
}