use core::str::{FromStr, from_utf8};
#[inline]
pub fn try_parse<T: FromStr>(source: &[u8], value: &mut T) -> bool {
if let Ok(s) = from_utf8(source)
&& let Ok(v) = s.parse::<T>()
{
*value = v;
return true;
}
false
}
#[inline]
pub fn try_parse_i64(source: &[u8], value: &mut i64) -> bool {
try_parse(source, value)
}
pub trait StrictFloat: FromStr + Copy {
const INFINITY: Self;
const NEG_INFINITY: Self;
fn is_nan(self) -> bool;
fn is_infinite(self) -> bool;
}
impl StrictFloat for f32 {
const INFINITY: Self = f32::INFINITY;
const NEG_INFINITY: Self = f32::NEG_INFINITY;
fn is_nan(self) -> bool {
f32::is_nan(self)
}
fn is_infinite(self) -> bool {
f32::is_infinite(self)
}
}
impl StrictFloat for f64 {
const INFINITY: Self = f64::INFINITY;
const NEG_INFINITY: Self = f64::NEG_INFINITY;
fn is_nan(self) -> bool {
f64::is_nan(self)
}
fn is_infinite(self) -> bool {
f64::is_infinite(self)
}
}
#[inline]
pub fn strict_parse_float<T: StrictFloat>(raw: &[u8], can_be_infinite: bool) -> Option<T> {
if let Ok(s) = from_utf8(raw)
&& let Ok(v) = s.parse::<T>()
&& !v.is_nan()
&& !(v.is_infinite() && !raw.iter().any(u8::is_ascii_digit))
{
return Some(v);
}
if can_be_infinite {
return match infinity_sign(raw) {
Some(true) => Some(T::INFINITY),
Some(false) => Some(T::NEG_INFINITY),
None => None,
};
}
None
}
#[inline]
pub fn strict_f64(raw: &[u8], can_be_infinite: bool) -> Option<f64> {
strict_parse_float(raw, can_be_infinite)
}
#[inline]
pub fn strict_f32(raw: &[u8], can_be_infinite: bool) -> Option<f32> {
strict_parse_float(raw, can_be_infinite)
}
#[inline]
pub fn strict_i64(raw: &[u8]) -> Option<i64> {
let (digits, negative) = match raw {
[b'+', rest @ ..] => (rest, false),
[b'-', rest @ ..] => (rest, true),
rest => (rest, false),
};
if digits.is_empty() || (digits.len() > 1 && digits[0] == b'0') {
return None;
}
let mut number: u64 = 0;
for &d in digits {
if !d.is_ascii_digit() {
return None;
}
number = number.checked_mul(10)?.checked_add(u64::from(d - b'0'))?;
}
if negative {
if number == i64::MIN.unsigned_abs() {
Some(i64::MIN)
} else {
let positive = i64::try_from(number).ok()?;
Some(-positive)
}
} else {
i64::try_from(number).ok()
}
}
#[inline]
pub fn strict_i32(raw: &[u8]) -> Option<i32> {
i32::try_from(strict_i64(raw)?).ok()
}
#[inline]
pub fn try_parse_f64(source: &[u8], value: &mut f64) -> bool {
strict_f64(source, false).is_some_and(|v| {
*value = v;
true
})
}
#[inline]
pub fn try_parse_with_infinity(source: &[u8], value: &mut f64) -> bool {
strict_f64(source, true).is_some_and(|v| {
*value = v;
true
})
}
#[inline]
fn infinity_sign(raw: &[u8]) -> Option<bool> {
let (positive, body) = match raw {
[s @ (b'+' | b'-'), rest @ ..] => (*s == b'+', rest),
_ => (true, raw),
};
body.eq_ignore_ascii_case(b"inf").then_some(positive)
}
pub fn get_next_offset(value: &mut u64) -> i32 {
let offset = value.trailing_zeros() as i32;
if offset < 64 {
*value &= !(1_u64 << offset);
}
offset
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn strict_i64_strict() {
assert_eq!(strict_i64(b"42"), Some(42));
assert_eq!(strict_i64(b"-7"), Some(-7));
assert_eq!(strict_i64(b"+3"), Some(3));
assert_eq!(strict_i64(b"0"), Some(0));
assert_eq!(strict_i64(b"-0"), Some(0));
assert_eq!(strict_i64(b"+0"), Some(0));
assert_eq!(strict_i64(b"9223372036854775807"), Some(i64::MAX));
assert_eq!(strict_i64(b"-9223372036854775808"), Some(i64::MIN));
assert_eq!(strict_i64(b"007"), None);
assert_eq!(strict_i64(b"-007"), None);
assert_eq!(strict_i64(b"abc"), None);
assert_eq!(strict_i64(b""), None);
assert_eq!(strict_i64(b"1 2"), None);
assert_eq!(strict_i64(b" 1"), None);
assert_eq!(strict_i64(b"5 "), None);
assert_eq!(strict_i64(b"1x"), None);
assert_eq!(strict_i64(b"9223372036854775808"), None);
assert_eq!(strict_i64(b"-9223372036854775809"), None);
}
#[test]
fn strict_i32_strict() {
assert_eq!(strict_i32(b"42"), Some(42));
assert_eq!(strict_i32(b"-7"), Some(-7));
assert_eq!(strict_i32(b"2147483647"), Some(i32::MAX));
assert_eq!(strict_i32(b"-2147483648"), Some(i32::MIN));
assert_eq!(strict_i32(b"2147483648"), None);
assert_eq!(strict_i32(b"-2147483649"), None);
assert_eq!(strict_i32(b"007"), None);
assert_eq!(strict_i32(b"01"), None);
}
#[test]
fn strict_f64_infinity_forms() {
assert_eq!(strict_f64(b"1e999", false), Some(f64::INFINITY));
assert_eq!(strict_f64(b"nan", true), None);
assert_eq!(strict_f64(b"inf", true), Some(f64::INFINITY));
assert_eq!(strict_f64(b"INF", true), Some(f64::INFINITY));
assert_eq!(strict_f64(b"+inf", true), Some(f64::INFINITY));
assert_eq!(strict_f64(b"+Inf", true), Some(f64::INFINITY));
assert_eq!(strict_f64(b"-inf", true), Some(f64::NEG_INFINITY));
assert_eq!(strict_f64(b"-INF", true), Some(f64::NEG_INFINITY));
assert_eq!(strict_f64(b"infinity", true), None);
assert_eq!(strict_f64(b"+infinity", true), None);
assert_eq!(strict_f64(b"-INFINITY", true), None);
assert_eq!(strict_f64(b"inf", false), None);
assert_eq!(strict_f64(b"infinity", false), None);
assert_eq!(strict_f64(b"info", true), None);
assert_eq!(strict_f64(b"", true), None);
}
#[test]
fn strict_f32_infinity_forms() {
assert_eq!(strict_f32(b"1e999", true), Some(f32::INFINITY));
assert_eq!(strict_f32(b"inf", true), Some(f32::INFINITY));
assert_eq!(strict_f32(b"-inf", true), Some(f32::NEG_INFINITY));
assert_eq!(strict_f32(b"-infinity", true), None);
assert_eq!(strict_f32(b"nan", true), None);
assert_eq!(strict_f32(b"inf", false), None);
}
#[test]
fn test_try_parse_integers() {
let mut v = 0i32;
assert!(try_parse(b"123", &mut v));
assert_eq!(v, 123);
assert!(!try_parse(b"1x", &mut v));
}
#[test]
fn test_strict_integers() {
assert_eq!(strict_i64(b"42"), Some(42));
assert_eq!(strict_i64(b"-7"), Some(-7));
assert_eq!(strict_i64(b"+3"), Some(3));
assert_eq!(strict_i64(b"0"), Some(0));
assert_eq!(strict_i64(b"-0"), Some(0));
assert_eq!(strict_i64(b"+0"), Some(0));
assert_eq!(strict_i64(b"9223372036854775807"), Some(i64::MAX));
assert_eq!(strict_i64(b"-9223372036854775808"), Some(i64::MIN));
assert_eq!(strict_i64(b"9223372036854775808"), None);
assert_eq!(strict_i64(b"-9223372036854775809"), None);
assert_eq!(strict_i64(b"007"), None);
assert_eq!(strict_i64(b"-007"), None);
assert_eq!(strict_i64(b"abc"), None);
assert_eq!(strict_i64(b""), None);
assert_eq!(strict_i64(b"1 2"), None);
assert_eq!(strict_i64(b" 1"), None);
assert_eq!(strict_i64(b"5 "), None);
assert_eq!(strict_i64(b"1x"), None);
assert_eq!(strict_i32(b"2147483647"), Some(i32::MAX));
assert_eq!(strict_i32(b"-2147483648"), Some(i32::MIN));
assert_eq!(strict_i32(b"2147483648"), None);
assert_eq!(strict_i32(b"-2147483649"), None);
assert_eq!(strict_i32(b"007"), None);
assert_eq!(strict_i32(b"01"), None);
}
#[test]
fn test_strict_floats() {
assert_eq!(strict_f64(b"3.5", false), Some(3.5));
assert_eq!(strict_f64(b"1e999", false), Some(f64::INFINITY));
assert_eq!(strict_f64(b"1e999", true), Some(f64::INFINITY));
assert_eq!(strict_f64(b"inf", true), Some(f64::INFINITY));
assert_eq!(strict_f64(b"INF", true), Some(f64::INFINITY));
assert_eq!(strict_f64(b"+Inf", true), Some(f64::INFINITY));
assert_eq!(strict_f64(b"-inf", true), Some(f64::NEG_INFINITY));
assert_eq!(strict_f64(b"-INF", true), Some(f64::NEG_INFINITY));
assert_eq!(strict_f64(b"inf", false), None);
assert_eq!(strict_f64(b"+INF", false), None);
assert_eq!(strict_f64(b"Infinity", true), None);
assert_eq!(strict_f64(b"nan", true), None);
assert_eq!(strict_f64(b"NaN", false), None);
assert_eq!(strict_f64(b"abc", true), None);
assert_eq!(strict_f64(b"", true), None);
assert_eq!(strict_f32(b"1e999", false), Some(f32::INFINITY));
assert_eq!(strict_f32(b"inf", true), Some(f32::INFINITY));
assert_eq!(strict_f32(b"inf", false), None);
assert_eq!(strict_f32(b"nan", true), None);
assert_eq!(strict_f32(b"1e3", false), Some(1000.0));
}
#[test]
fn test_try_parse_float_adapters() {
let mut v = 0.0f64;
assert!(try_parse_f64(b"2.5", &mut v));
assert_eq!(v, 2.5);
assert!(!try_parse_f64(b"inf", &mut v));
assert!(!try_parse_f64(b"nan", &mut v));
v = 0.0;
assert!(try_parse_f64(b"1e999", &mut v));
assert!(v.is_infinite());
}
#[test]
fn test_try_parse_with_infinity() {
let mut v = 0.0f64;
assert!(try_parse_with_infinity(b"+inf", &mut v));
assert!(v.is_infinite() && v.is_sign_positive());
assert!(try_parse_with_infinity(b"-inf", &mut v));
assert!(v.is_sign_negative());
assert!(try_parse_with_infinity(b"INF", &mut v));
assert!(!try_parse_with_infinity(b"nan", &mut v));
assert!(try_parse_with_infinity(b"1.25", &mut v));
assert_eq!(v, 1.25);
}
}