Created
May 6, 2024 19:10
-
-
Save athre0z/ba4267518b0191b6c02b8301ea707cce to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
impl<V: Value> core::str::FromStr for NonNormalizingDec<V> { | |
type Err = &'static str; | |
#[inline(never)] | |
#[rustfmt::skip] | |
fn from_str(s: &str) -> Result<Self, Self::Err> { | |
/// Converts an ASCII decimal digit to an int. | |
/// | |
/// In release builds, no range checks are performed and passing a | |
/// non-digit character will result is undefined (yet safe) behavior. | |
fn digit2num(x: char) -> i8 { | |
debug_assert!(x >= '0' && x <= '9'); | |
let out = x as i8 - '0' as i8; | |
debug_assert!(out >= 0 && out <= 9); | |
out | |
} | |
fn push<V: Value>(nn: &mut NonNormalizingDec<V>, digit: char) { | |
let digit = V::from_i8(digit2num(digit)).unwrap(); | |
nn.v = nn.v.clone() * V::ten() + digit; | |
} | |
fn push_fract<V: Value>(nn: &mut NonNormalizingDec<V>, digit: char) { | |
push(nn, digit); | |
nn.s += 1; | |
} | |
enum ReadStatus { | |
Enough, | |
NeedMore, | |
} | |
enum PosNeg { | |
Pos, | |
Neg, | |
} | |
enum ParserState { | |
Sign, | |
Int(ReadStatus), | |
Frac, | |
ExpSign, | |
Exp(PosNeg, Scale), | |
} | |
use ParserState::*; | |
use PosNeg::*; | |
use ReadStatus::*; | |
let mut iter = s.chars(); | |
let mut state = ParserState::Sign; | |
let mut sign = V::one(); | |
let mut out = Self::zero(); | |
loop { match (&mut state, iter.next()) { | |
(Sign , Some('+' )) => state = Int(NeedMore), | |
(Sign , Some('-' )) => { sign = -V::one(); state = Int(NeedMore) } | |
(Sign , Some(x @ '0'..='9')) => { push(&mut out, x); state = Int(Enough) } | |
(Sign , Some(_ )) => return Err("unexpected char (sign)"), | |
(Sign , None ) => return Err("empty string"), | |
(Int(status ), Some(x @ '0'..='9')) => { push(&mut out, x); *status = Enough; } | |
(Int(Enough ), Some('e' | 'E') ) => state = ExpSign, | |
(Int(Enough ), Some('.' )) => state = Frac, | |
(Int(Enough ), None ) => break, | |
(Int(NeedMore), _ ) => return Err("expected digits"), | |
(Int(_ ), Some(_ )) => return Err("unexpected char (int)"), | |
(Frac , Some(x @ '0'..='9')) => push_fract(&mut out, x), | |
(Frac , Some('e' | 'E' )) => state = ExpSign, | |
(Frac , Some(_ )) => return Err("unexpected char (frac)"), | |
(Frac , None ) => break, | |
(ExpSign , Some('+' )) => state = Exp(Pos, 0), | |
(ExpSign , Some('-' )) => state = Exp(Neg, 0), | |
(ExpSign , Some(x @ '0'..='9')) => state = Exp(Pos, digit2num(x) as Scale), | |
(ExpSign , Some(_ )) => return Err("unexpected char (exp sign)"), | |
(ExpSign , None ) => return Err("unexpected end (exp sign)"), | |
(Exp(_ , exp), Some(x @ '0'..='9')) => *exp = *exp * 10 + digit2num(x) as Scale, | |
(Exp(_ , _ ), Some(_ )) => return Err("unexpected char (exp)"), | |
(Exp(Pos, exp), None ) => { out *= Self::pow10(*exp); break } | |
(Exp(Neg, exp), None ) => { out *= Self::pow10(-*exp); break } | |
}} | |
out.v *= sign; | |
Ok(out) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment