use crate::math::{powi_f64, trunc_f64};
use crate::object::macros::object;
use crate::object::{Object, ObjectLike};
use crate::reader::Reader;
use crate::reader::{Readable, ReaderContext, ReaderExt, Skippable};
use crate::trivia::{is_regular_character, is_white_space_character};
use core::fmt::{Debug, Display, Formatter};
#[rustfmt::skip]
static POWERS_OF_10: [f64; 20] = [
1.0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
];
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Number(pub(crate) InternalNumber);
impl Number {
pub const ZERO: Self = Self::from_i32(0);
pub const ONE: Self = Self::from_i32(1);
pub fn as_f64(&self) -> f64 {
match self.0 {
InternalNumber::Real(r) => r,
InternalNumber::Integer(i) => i as f64,
}
}
pub fn as_f32(&self) -> f32 {
match self.0 {
InternalNumber::Real(r) => r as f32,
InternalNumber::Integer(i) => i as f32,
}
}
pub fn as_i64(&self) -> i64 {
match self.0 {
InternalNumber::Real(r) => {
let res = r as i64;
if !(trunc_f64(r) == r) {
debug!("float {r} was truncated to {res}");
}
res
}
InternalNumber::Integer(i) => i,
}
}
pub const fn from_f32(num: f32) -> Self {
Self(InternalNumber::Real(num as f64))
}
pub const fn from_i32(num: i32) -> Self {
Self(InternalNumber::Integer(num as i64))
}
}
impl Display for Number {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self.0 {
InternalNumber::Real(value) => write!(f, "{value:?}"),
InternalNumber::Integer(value) => write!(f, "{value}"),
}
}
}
impl Skippable for Number {
fn skip(r: &mut Reader<'_>, _: bool) -> Option<()> {
let has_sign = r.forward_if(|b| b == b'+' || b == b'-').is_some();
match r.peek_byte()? {
b'.' => {
r.read_byte()?;
r.forward_while(is_digit_or_minus);
}
b'0'..=b'9' | b'-' => {
r.forward_while_1(is_digit_or_minus)?;
if let Some(()) = r.forward_tag(b".") {
r.forward_while(is_digit_or_minus);
}
}
b if has_sign && (is_white_space_character(b) || matches!(b, b'(' | b'<')) => {}
_ => return None,
}
if r.peek_byte().is_some_and(is_regular_character) {
return None;
}
Some(())
}
}
impl Readable<'_> for Number {
#[inline]
fn read(r: &mut Reader<'_>, _: &ReaderContext<'_>) -> Option<Self> {
let old_offset = r.offset();
read_inner(r).or_else(|| {
r.jump(old_offset);
None
})
}
}
#[inline(always)]
fn read_inner(r: &mut Reader<'_>) -> Option<Number> {
let start_offset = r.offset();
let negative = match r.peek_byte()? {
b'-' => {
r.forward();
true
}
b'+' => {
r.forward();
false
}
_ => false,
};
let mut mantissa: u64 = 0;
let mut has_dot = false;
let mut decimal_shift: u32 = 0;
let mut has_digits = false;
let mut overflowed = false;
let mut number_end = None;
loop {
match r.peek_byte() {
Some(b'0'..=b'9') => {
let d = r.read_byte().unwrap();
if !overflowed {
if let Some(value) = mantissa
.checked_mul(10)
.and_then(|v| v.checked_add((d - b'0') as u64))
{
mantissa = value;
} else {
overflowed = true;
}
}
has_digits = true;
if has_dot {
decimal_shift += 1;
}
}
Some(b'.') if !has_dot => {
r.forward();
has_dot = true;
}
Some(b'-') if has_digits => {
number_end = Some(r.offset());
r.forward();
r.forward_while(is_digit_or_minus);
break;
}
_ => break,
}
}
if !has_digits {
if negative || has_dot {
return Some(Number(InternalNumber::Integer(0)));
}
return None;
}
if r.peek_byte().is_some_and(is_regular_character) {
return None;
}
let integer_overflow = !has_dot && mantissa > i64::MAX as u64;
if overflowed || integer_overflow {
let end_offset = number_end.unwrap_or_else(|| r.offset());
let number = core::str::from_utf8(r.range(start_offset..end_offset)?).ok()?;
if !has_dot && let Ok(value) = number.parse::<i64>() {
return Some(Number(InternalNumber::Integer(value)));
}
let value = number.parse::<f64>().ok()?;
return Some(Number(InternalNumber::Real(value)));
}
if !has_dot {
let value = if negative {
(mantissa as i64).wrapping_neg()
} else {
mantissa as i64
};
Some(Number(InternalNumber::Integer(value)))
} else {
let mut value = mantissa as f64;
if decimal_shift > 0 {
if decimal_shift < POWERS_OF_10.len() as u32 {
value /= POWERS_OF_10[decimal_shift as usize];
} else {
value /= powi_f64(10.0, decimal_shift);
}
}
if negative {
value = -value;
}
Some(Number(InternalNumber::Real(value)))
}
}
object!(Number, Number);
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) enum InternalNumber {
Real(f64),
Integer(i64),
}
macro_rules! int_num {
($i:ident) => {
impl Skippable for $i {
fn skip(r: &mut Reader<'_>, _: bool) -> Option<()> {
r.forward_if(|b| b == b'+' || b == b'-');
r.forward_while_1(is_digit)?;
if r.peek_byte() == Some(b'.') {
return None;
}
if r.peek_byte().is_some_and(is_regular_character) {
return None;
}
Some(())
}
}
impl<'a> Readable<'a> for $i {
fn read(r: &mut Reader<'a>, ctx: &ReaderContext<'a>) -> Option<$i> {
r.read::<Number>(ctx)
.map(|n| n.as_i64())
.and_then(|n| n.try_into().ok())
}
}
impl TryFrom<Object<'_>> for $i {
type Error = ();
fn try_from(value: Object<'_>) -> core::result::Result<Self, Self::Error> {
match value {
Object::Number(n) => n.as_i64().try_into().ok().ok_or(()),
_ => Err(()),
}
}
}
impl<'a> ObjectLike<'a> for $i {}
};
}
int_num!(i32);
int_num!(i64);
int_num!(u32);
int_num!(u16);
int_num!(usize);
int_num!(u8);
impl Skippable for f32 {
fn skip(r: &mut Reader<'_>, is_content_stream: bool) -> Option<()> {
r.skip::<Number>(is_content_stream).map(|_| {})
}
}
impl Readable<'_> for f32 {
fn read(r: &mut Reader<'_>, _: &ReaderContext<'_>) -> Option<Self> {
r.read_without_context::<Number>()
.map(|n| n.as_f64() as Self)
}
}
impl TryFrom<Object<'_>> for f32 {
type Error = ();
fn try_from(value: Object<'_>) -> Result<Self, Self::Error> {
match value {
Object::Number(n) => Ok(n.as_f64() as Self),
_ => Err(()),
}
}
}
impl ObjectLike<'_> for f32 {}
impl Skippable for f64 {
fn skip(r: &mut Reader<'_>, is_content_stream: bool) -> Option<()> {
r.skip::<Number>(is_content_stream).map(|_| {})
}
}
impl Readable<'_> for f64 {
fn read(r: &mut Reader<'_>, _: &ReaderContext<'_>) -> Option<Self> {
r.read_without_context::<Number>().map(|n| n.as_f64())
}
}
impl TryFrom<Object<'_>> for f64 {
type Error = ();
fn try_from(value: Object<'_>) -> Result<Self, Self::Error> {
match value {
Object::Number(n) => Ok(n.as_f64()),
_ => Err(()),
}
}
}
impl ObjectLike<'_> for f64 {}
pub(crate) fn is_digit(byte: u8) -> bool {
byte.is_ascii_digit()
}
pub(crate) fn is_digit_or_minus(byte: u8) -> bool {
is_digit(byte) || byte == b'-'
}
#[cfg(test)]
mod tests {
use crate::object::Number;
use crate::reader::Reader;
use crate::reader::ReaderExt;
#[test]
fn display() {
assert_eq!(format!("{}", Number::from_i32(10)), "10");
assert_eq!(format!("{}", Number::from_f32(10.0)), "10.0");
assert_eq!(format!("{}", Number::from_f32(10.5)), "10.5");
}
#[test]
fn int_1() {
assert_eq!(
Reader::new("0".as_bytes())
.read_without_context::<i32>()
.unwrap(),
0
);
}
#[test]
fn int_3() {
assert_eq!(
Reader::new("+32".as_bytes())
.read_without_context::<i32>()
.unwrap(),
32
);
}
#[test]
fn int_4() {
assert_eq!(
Reader::new("-32".as_bytes())
.read_without_context::<i32>()
.unwrap(),
-32
);
}
#[test]
fn int_6() {
assert_eq!(
Reader::new("98349".as_bytes())
.read_without_context::<i32>()
.unwrap(),
98349
);
}
#[test]
fn int_7() {
assert_eq!(
Reader::new("003245".as_bytes())
.read_without_context::<i32>()
.unwrap(),
3245
);
}
#[test]
fn int_min_stays_integer() {
let number = Reader::new("-9223372036854775808".as_bytes())
.read_without_context::<Number>()
.unwrap();
assert_eq!(number.0, super::InternalNumber::Integer(i64::MIN));
assert_eq!(number.as_i64(), i64::MIN);
}
#[test]
fn real_1() {
assert_eq!(
Reader::new("3".as_bytes())
.read_without_context::<f32>()
.unwrap(),
3.0
);
}
#[test]
fn real_3() {
assert_eq!(
Reader::new("+32".as_bytes())
.read_without_context::<f32>()
.unwrap(),
32.0
);
}
#[test]
fn real_4() {
assert_eq!(
Reader::new("-32".as_bytes())
.read_without_context::<f32>()
.unwrap(),
-32.0
);
}
#[test]
fn real_5() {
assert_eq!(
Reader::new("-32.01".as_bytes())
.read_without_context::<f32>()
.unwrap(),
-32.01
);
}
#[test]
fn real_6() {
assert_eq!(
Reader::new("-.345".as_bytes())
.read_without_context::<f32>()
.unwrap(),
-0.345
);
}
#[test]
fn real_7() {
assert_eq!(
Reader::new("-.00143".as_bytes())
.read_without_context::<f32>()
.unwrap(),
-0.00143
);
}
#[test]
fn real_8() {
assert_eq!(
Reader::new("-12.0013".as_bytes())
.read_without_context::<f32>()
.unwrap(),
-12.0013
);
}
#[test]
fn real_9() {
assert_eq!(
Reader::new("98349.432534".as_bytes())
.read_without_context::<f32>()
.unwrap(),
98_349.43
);
}
#[test]
fn real_10() {
assert_eq!(
Reader::new("-34534656.34".as_bytes())
.read_without_context::<f32>()
.unwrap(),
-34534656.34
);
}
#[test]
fn real_failing() {
assert!(
Reader::new("+abc".as_bytes())
.read_without_context::<f32>()
.is_none()
);
}
#[test]
fn number_1() {
assert_eq!(
Reader::new("+32".as_bytes())
.read_without_context::<Number>()
.unwrap()
.as_f64() as f32,
32.0
);
}
#[test]
fn number_2() {
assert_eq!(
Reader::new("-32.01".as_bytes())
.read_without_context::<Number>()
.unwrap()
.as_f64() as f32,
-32.01
);
}
#[test]
fn number_3() {
assert_eq!(
Reader::new("-.345".as_bytes())
.read_without_context::<Number>()
.unwrap()
.as_f64() as f32,
-0.345
);
}
#[test]
fn large_number() {
assert_eq!(
Reader::new("38359922".as_bytes())
.read_without_context::<Number>()
.unwrap()
.as_i64(),
38359922
);
}
#[test]
fn large_number_2() {
assert_eq!(
Reader::new("4294966260".as_bytes())
.read_without_context::<u32>()
.unwrap(),
4294966260
);
}
#[test]
fn twenty_digit_real_does_not_wrap() {
let got = Reader::new("190.50000762939453225".as_bytes())
.read_without_context::<Number>()
.unwrap()
.as_f64();
assert_eq!(got, 190.500_007_629_394_53);
}
#[test]
fn nineteen_digit_real_stays_exact() {
let got = Reader::new("190.5000076293945313".as_bytes())
.read_without_context::<Number>()
.unwrap()
.as_f64();
assert_eq!(got, 190.500_007_629_394_53);
}
#[test]
fn oversized_integer_uses_float_fallback() {
let n = Reader::new("9999999999999999999".as_bytes())
.read_without_context::<Number>()
.unwrap();
assert_eq!(n.as_f64(), 9_999_999_999_999_999_999_u64 as f64);
assert_eq!(n.as_i64(), i64::MAX);
}
#[test]
fn oversized_negative_integer_uses_float_fallback() {
let n = Reader::new("-1234567890123456789012345".as_bytes())
.read_without_context::<Number>()
.unwrap();
assert_eq!(n.as_f64(), -1.234_567_890_123_456_8e24);
assert_eq!(n.as_i64(), i64::MIN);
}
#[test]
fn integer_overflow_then_fraction_uses_float_fallback() {
let got = Reader::new("12345678901234567890123.456".as_bytes())
.read_without_context::<Number>()
.unwrap()
.as_f64();
assert_eq!(got, 1.234_567_890_123_456_8e22);
}
#[test]
fn f32_max_uses_float_fallback() {
let got = Reader::new("340282346638528859811704183484516925440".as_bytes())
.read_without_context::<f32>()
.unwrap();
assert_eq!(got, f32::MAX);
}
#[test]
fn float_fallback_ignores_trailing_minus() {
let input = "1234567890123456789012345-123".as_bytes();
let mut reader = Reader::new(input);
let got = reader.read_without_context::<Number>().unwrap().as_f64();
assert_eq!(got, 1.234_567_890_123_456_8e24);
assert_eq!(reader.offset(), input.len());
}
}