Skip to content

Instantly share code, notes, and snippets.

@athre0z
Created May 6, 2024 19:10
Show Gist options
  • Save athre0z/ba4267518b0191b6c02b8301ea707cce to your computer and use it in GitHub Desktop.
Save athre0z/ba4267518b0191b6c02b8301ea707cce to your computer and use it in GitHub Desktop.
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