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;
#[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 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 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;
loop {
match r.peek_byte() {
Some(b'0'..=b'9') => {
let d = r.read_byte().unwrap();
mantissa = mantissa
.wrapping_mul(10)
.wrapping_add((d - b'0') as u64);
has_digits = true;
if has_dot {
decimal_shift += 1;
}
}
Some(b'.') if !has_dot => {
r.forward();
has_dot = true;
}
Some(b'-') if has_digits => {
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;
}
if !has_dot {
let value = if negative {
-(mantissa as i64)
} 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;
}
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 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 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
);
}
}