mesh_loader/utils/
int.rs

1// Rust port of fast_float's integer parser.
2//
3// Source: https://github.com/fastfloat/fast_float/blob/68b9475585be0839fa0bf3d6bfad3e4a6357d90a/include/fast_float/ascii_number.h#L445
4
5#![allow(
6    clippy::cast_possible_truncation,
7    clippy::cast_possible_wrap,
8    clippy::cast_sign_loss
9)]
10
11use self::integer::RawInteger;
12use crate::utils::float::{common::ByteSlice, parse::try_parse_digits};
13
14#[inline]
15pub fn parse<T: Integer>(bytes: &[u8]) -> Option<T> {
16    T::parse(bytes)
17}
18
19#[inline]
20pub fn parse_partial<T: Integer>(bytes: &[u8]) -> Option<(T, usize)> {
21    T::parse_partial(bytes)
22}
23
24mod integer {
25    pub trait RawInteger: Copy {
26        const MAX_DIGITS: usize;
27        const MIN_SAFE: u64;
28        const MAX: u64;
29        const IS_SIGNED: bool;
30        fn from_u64(v: u64, negative: bool) -> Self;
31    }
32}
33
34pub trait Integer: integer::RawInteger {
35    #[inline]
36    fn parse(bytes: &[u8]) -> Option<Self> {
37        match Self::parse_partial(bytes) {
38            Some((v, n)) if n == bytes.len() => Some(v),
39            _ => None,
40        }
41    }
42    #[inline]
43    fn parse_partial(bytes: &[u8]) -> Option<(Self, usize)> {
44        dec2int(bytes)
45    }
46}
47
48const BASE: u8 = 10;
49
50macro_rules! max_digit_count {
51    ($ty:ident) => {{
52        let mut max = $ty::MAX;
53        let mut count = 0;
54        while max > 0 {
55            count += 1;
56            max /= BASE as $ty;
57        }
58        count
59    }};
60}
61
62macro_rules! uint {
63    ($ty:ident) => {
64        impl RawInteger for $ty {
65            const MAX_DIGITS: usize = max_digit_count!($ty);
66            const MIN_SAFE: u64 = (BASE as u64).pow($ty::MAX_DIGITS as u32 - 1);
67            const MAX: u64 = $ty::MAX as u64;
68            const IS_SIGNED: bool = false;
69            #[inline]
70            fn from_u64(v: u64, negative: bool) -> Self {
71                debug_assert!(!negative);
72                v as $ty
73            }
74        }
75        impl Integer for $ty {}
76    };
77}
78macro_rules! int {
79    ($ty:ident) => {
80        impl RawInteger for $ty {
81            const MAX_DIGITS: usize = max_digit_count!($ty);
82            const MIN_SAFE: u64 = (BASE as u64).pow($ty::MAX_DIGITS as u32 - 1);
83            const MAX: u64 = $ty::MAX as u64;
84            const IS_SIGNED: bool = true;
85            #[inline]
86            fn from_u64(v: u64, negative: bool) -> Self {
87                if negative {
88                    (-$ty::MAX).wrapping_sub((v.wrapping_sub($ty::MAX as u64)) as $ty)
89                } else {
90                    v as $ty
91                }
92            }
93        }
94        impl Integer for $ty {}
95    };
96}
97// uint!(u128);
98uint!(u64);
99uint!(u32);
100uint!(u16);
101uint!(u8);
102// int!(i128);
103int!(i64);
104int!(i32);
105int!(i16);
106int!(i8);
107
108#[inline]
109fn dec2int<I: RawInteger>(mut s: &[u8]) -> Option<(I, usize)> {
110    let start = s;
111    let c = if let Some(&c) = s.first() {
112        c
113    } else {
114        return None;
115    };
116    let negative;
117    if I::IS_SIGNED {
118        negative = c == b'-';
119        if negative || c == b'+' {
120            s = &s[1..];
121            if s.is_empty() {
122                return None;
123            }
124        }
125    } else {
126        negative = false;
127        if c == b'+' {
128            s = &s[1..];
129            if s.is_empty() {
130                return None;
131            }
132        }
133    }
134
135    let (v, len) = parse_partial_number(s, start, negative, I::MAX_DIGITS, I::MIN_SAFE, I::MAX)?;
136    Some((I::from_u64(v, negative), len))
137}
138
139#[inline(always)]
140fn parse_partial_number(
141    mut s: &[u8],
142    full_start: &[u8],
143    negative: bool,
144    max_digits: usize,
145    min_safe: u64,
146    max: u64,
147) -> Option<(u64, usize)> {
148    debug_assert!(!s.is_empty());
149
150    // parse leading zeros
151    let start = s;
152    // FIXME: Can't use s.split_first() here yet,
153    // see https://github.com/rust-lang/rust/issues/109328
154    // (fixed in LLVM 17)
155    while let [b'0', s_next @ ..] = s {
156        s = s_next;
157    }
158
159    // parse digits
160    let mut v = 0_u64;
161    let digits_start = s;
162    if max_digits >= 8 {
163        try_parse_digits(&mut s, &mut v);
164    } else {
165        s = s.parse_digits(|digit| {
166            v = v.wrapping_mul(10).wrapping_add(digit as u64);
167        });
168    }
169    let n_digits = s.offset_from(digits_start) as usize;
170
171    if n_digits == 0 && s.offset_from(start) == 0 {
172        return None;
173    }
174
175    // check overflow
176    if n_digits > max_digits {
177        return None;
178    }
179    if n_digits == max_digits && v < min_safe {
180        return None;
181    }
182    if max != u64::MAX && v > max + negative as u64 {
183        return None;
184    }
185
186    let len = s.offset_from(full_start) as usize;
187    Some((v, len))
188}
189
190#[cfg(test)]
191#[path = "tests/int.rs"]
192mod tests;