2022-03-24 10:53:25 +00:00
|
|
|
use core::fmt;
|
|
|
|
use core::num::Wrapping;
|
2021-09-28 15:48:14 +00:00
|
|
|
|
|
|
|
use super::WStr;
|
|
|
|
|
2021-11-05 22:32:43 +00:00
|
|
|
/// Analog of [`std::str::FromStr`], but for Ruffle's [`&WStr`].
|
2021-09-28 15:48:14 +00:00
|
|
|
pub trait FromWStr: Sized {
|
|
|
|
type Err;
|
|
|
|
|
2021-11-05 22:32:43 +00:00
|
|
|
fn from_wstr(s: &WStr) -> Result<Self, Self::Err>;
|
2021-09-28 15:48:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Error returned by [`Integer::from_str_radix`].
|
2022-03-23 14:52:55 +00:00
|
|
|
#[derive(Debug)]
|
2021-10-06 17:30:43 +00:00
|
|
|
pub struct ParseNumError(());
|
2021-09-28 15:48:14 +00:00
|
|
|
|
2022-03-23 14:52:55 +00:00
|
|
|
impl fmt::Display for ParseNumError {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "failed to parse integer")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-05 22:32:43 +00:00
|
|
|
/// Trait implemented for all integer types that can be parsed from a [`WStr`].
|
2021-10-06 17:30:43 +00:00
|
|
|
pub trait Integer: FromWStr<Err = ParseNumError> {
|
2021-11-05 22:32:43 +00:00
|
|
|
fn from_wstr_radix(s: &WStr, radix: u32) -> Result<Self, Self::Err>;
|
2021-09-28 15:48:14 +00:00
|
|
|
}
|
|
|
|
|
2021-11-05 22:32:43 +00:00
|
|
|
fn parse_special_floats(s: &WStr) -> Option<f64> {
|
2021-10-06 17:30:43 +00:00
|
|
|
let nan = WStr::from_units(b"NaN");
|
|
|
|
let inf = WStr::from_units(b"inf");
|
|
|
|
|
|
|
|
let slice = match s.len() {
|
|
|
|
3 if s == nan => return Some(f64::NAN),
|
|
|
|
3 if s == inf => return Some(f64::INFINITY),
|
2021-11-05 22:32:43 +00:00
|
|
|
4 => &s[1..],
|
2021-10-06 17:30:43 +00:00
|
|
|
_ => return None,
|
|
|
|
};
|
|
|
|
|
|
|
|
let is_nan = if slice == nan {
|
|
|
|
true
|
|
|
|
} else if slice == inf {
|
|
|
|
false
|
|
|
|
} else {
|
|
|
|
return None;
|
|
|
|
};
|
2021-11-05 22:32:43 +00:00
|
|
|
let is_neg = match u8::try_from(s.at(0)) {
|
2021-10-06 17:30:43 +00:00
|
|
|
Ok(b'+') => false,
|
|
|
|
Ok(b'-') => true,
|
|
|
|
_ => return None,
|
|
|
|
};
|
|
|
|
|
|
|
|
Some(match (is_nan, is_neg) {
|
|
|
|
(false, false) => f64::INFINITY,
|
|
|
|
(false, true) => f64::NEG_INFINITY,
|
|
|
|
(true, _) => f64::NAN,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-09-28 15:48:14 +00:00
|
|
|
impl FromWStr for f64 {
|
2021-10-06 17:30:43 +00:00
|
|
|
type Err = ParseNumError;
|
2021-09-28 15:48:14 +00:00
|
|
|
|
2021-11-05 22:32:43 +00:00
|
|
|
fn from_wstr(s: &WStr) -> Result<Self, Self::Err> {
|
2021-10-06 17:30:43 +00:00
|
|
|
if let Some(f) = parse_special_floats(s) {
|
|
|
|
return Ok(f);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Early-reject strings with non-float chars to avoid the utf8 conversion.
|
|
|
|
let is_valid = s.iter().all(|c| {
|
|
|
|
if let Ok(c) = u8::try_from(c) {
|
|
|
|
matches!(c, b'0'..=b'9' | b'.' | b'+' | b'-' | b'e' | b'E')
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if is_valid {
|
|
|
|
s.to_utf8_lossy().parse().map_err(|_| ParseNumError(()))
|
|
|
|
} else {
|
|
|
|
Err(ParseNumError(()))
|
|
|
|
}
|
2021-09-28 15:48:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mod int_parse {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
pub trait IntParse: Sized {
|
|
|
|
const SIGNED: bool;
|
|
|
|
|
|
|
|
fn from_digit(n: u32) -> Self;
|
|
|
|
fn checked_add(self, n: u32) -> Option<Self>;
|
|
|
|
fn checked_sub(self, n: u32) -> Option<Self>;
|
|
|
|
fn checked_mul(self, n: u32) -> Option<Self>;
|
|
|
|
}
|
|
|
|
|
2021-11-05 22:32:43 +00:00
|
|
|
pub fn from_wstr_radix<T: IntParse>(s: &WStr, radix: u32) -> Option<T> {
|
2021-09-28 15:48:14 +00:00
|
|
|
assert!(
|
|
|
|
radix >= 2 && radix <= 36,
|
|
|
|
"from_str_radix: radix must be between 2 and 36, got {}",
|
|
|
|
radix
|
|
|
|
);
|
|
|
|
|
2021-11-05 22:32:43 +00:00
|
|
|
let (is_neg, digits) = match s.get(0).map(u8::try_from) {
|
|
|
|
Some(Ok(b'-')) => (true, &s[1..]),
|
|
|
|
Some(Ok(b'+')) => (false, &s[1..]),
|
2021-09-28 15:48:14 +00:00
|
|
|
Some(_) => (false, s),
|
|
|
|
None => return None,
|
|
|
|
};
|
|
|
|
|
|
|
|
if is_neg && !T::SIGNED {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
digits.iter().try_fold(T::from_digit(0), |num, c| {
|
|
|
|
let byte = u8::try_from(c).ok()?;
|
|
|
|
let digit = (byte as char).to_digit(radix)?;
|
|
|
|
let num = num.checked_mul(radix)?;
|
|
|
|
if is_neg {
|
|
|
|
num.checked_sub(digit)
|
|
|
|
} else {
|
|
|
|
num.checked_add(digit)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! impl_int_parse {
|
|
|
|
($($ty:ty)*) => { $(
|
|
|
|
impl int_parse::IntParse for $ty {
|
|
|
|
#[allow(unused_comparisons)]
|
|
|
|
const SIGNED: bool = <$ty>::MIN < 0;
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn from_digit(n: u32) -> Self {
|
|
|
|
n as $ty
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn checked_add(self, n: u32) -> Option<Self> {
|
|
|
|
<$ty>::checked_add(self, n as $ty)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn checked_sub(self, n: u32) -> Option<Self> {
|
|
|
|
<$ty>::checked_sub(self, n as $ty)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn checked_mul(self, n: u32) -> Option<Self> {
|
|
|
|
<$ty>::checked_mul(self, n as $ty)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)* }
|
|
|
|
}
|
|
|
|
|
2021-09-30 23:13:06 +00:00
|
|
|
impl_int_parse! { u8 u32 i32 usize }
|
2021-09-28 15:48:14 +00:00
|
|
|
|
|
|
|
macro_rules! impl_wrapping_int_parse {
|
|
|
|
($($ty:ty)*) => { $(
|
|
|
|
impl int_parse::IntParse for Wrapping<$ty> {
|
|
|
|
#[allow(unused_comparisons)]
|
|
|
|
const SIGNED: bool = <$ty>::MIN < 0;
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn from_digit(n: u32) -> Self {
|
|
|
|
Self(n as $ty)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn checked_add(self, n: u32) -> Option<Self> {
|
|
|
|
Some(self + Self::from_digit(n))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn checked_sub(self, n: u32) -> Option<Self> {
|
|
|
|
Some(self - Self::from_digit(n))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn checked_mul(self, n: u32) -> Option<Self> {
|
|
|
|
Some(self * Self::from_digit(n))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)* }
|
|
|
|
}
|
|
|
|
|
2021-09-30 23:13:06 +00:00
|
|
|
impl_wrapping_int_parse! { u8 u32 i32 usize }
|
2021-09-28 15:48:14 +00:00
|
|
|
|
|
|
|
macro_rules! impl_from_str_int {
|
|
|
|
($($ty:ty)*) => { $(
|
|
|
|
impl Integer for $ty {
|
|
|
|
#[inline]
|
2021-11-05 22:32:43 +00:00
|
|
|
fn from_wstr_radix(s: &WStr, radix: u32) -> Result<Self, ParseNumError> {
|
2021-10-06 17:30:43 +00:00
|
|
|
int_parse::from_wstr_radix(s, radix).ok_or(ParseNumError(()))
|
2021-09-28 15:48:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Integer for Wrapping<$ty> {
|
|
|
|
#[inline]
|
2021-11-05 22:32:43 +00:00
|
|
|
fn from_wstr_radix(s: &WStr, radix: u32) -> Result<Self, ParseNumError> {
|
2021-10-06 17:30:43 +00:00
|
|
|
int_parse::from_wstr_radix(s, radix).ok_or(ParseNumError(()))
|
2021-09-28 15:48:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FromWStr for $ty {
|
2021-10-06 17:30:43 +00:00
|
|
|
type Err = ParseNumError;
|
2021-09-28 15:48:14 +00:00
|
|
|
#[inline]
|
2021-11-05 22:32:43 +00:00
|
|
|
fn from_wstr(s: &WStr) -> Result<Self, Self::Err> {
|
2021-10-06 17:30:43 +00:00
|
|
|
int_parse::from_wstr_radix(s, 10).ok_or(ParseNumError(()))
|
2021-09-28 15:48:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FromWStr for Wrapping<$ty> {
|
2021-10-06 17:30:43 +00:00
|
|
|
type Err = ParseNumError;
|
2021-09-28 15:48:14 +00:00
|
|
|
#[inline]
|
2021-11-05 22:32:43 +00:00
|
|
|
fn from_wstr(s: &WStr) -> Result<Self, Self::Err> {
|
2021-10-06 17:30:43 +00:00
|
|
|
int_parse::from_wstr_radix(s, 10).ok_or(ParseNumError(()))
|
2021-09-28 15:48:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
)* }
|
|
|
|
}
|
|
|
|
|
2021-09-30 23:13:06 +00:00
|
|
|
impl_from_str_int! { u8 u32 i32 usize }
|