swc_ecma_lexer/common/lexer/
number.rs

1use swc_common::BytePos;
2
3pub struct LazyInteger {
4    pub(super) start: BytePos,
5    pub(super) end: BytePos,
6    /// `true` if there was `8` or `9``
7    pub(super) not_octal: bool,
8    pub(super) has_underscore: bool,
9}
10
11const MAX_SAFE_INT: u64 = 9007199254740991;
12
13pub(super) fn parse_integer<const RADIX: u8>(s: &str) -> f64 {
14    debug_assert!(matches!(RADIX, 2 | 8 | 10 | 16));
15    debug_assert!(!s.is_empty());
16    debug_assert!(!s.contains('_'));
17
18    if RADIX == 10 {
19        parse_integer_from_dec(s)
20    } else if RADIX == 16 {
21        parse_integer_from_hex(s)
22    } else if RADIX == 2 {
23        parse_integer_from_bin(s)
24    } else if RADIX == 8 {
25        parse_integer_from_oct(s)
26    } else {
27        unreachable!()
28    }
29}
30
31fn parse_integer_from_hex(s: &str) -> f64 {
32    debug_assert!(s.chars().all(|c| c.is_ascii_hexdigit()));
33    const MAX_FAST_INT_LEN: usize = MAX_SAFE_INT.ilog(16) as usize;
34    if s.len() > MAX_FAST_INT_LEN {
35        // Hex digit character representations:
36        //   b'0'==0x30, b'1'==0x31 ... b'9'==0x39  → low nibble: 0x0-0x9
37        //   b'A'==0x41, b'B'==0x42 ... b'F'==0x46  → low nibble: 0x1-0x6
38        //   b'a'==0x61, b'b'==0x62 ... b'f'==0x66  → low nibble: 0x1-0x6
39        //
40        // Conversion requires only the low 4 bits:
41        //   digit_char & 0x0F gives base value:
42        //     - For '0'-'9': direct value (0-9)
43        //     - For 'A'-'F'/'a'-'f': offset base (1-6)
44        //   Add 9 for alphabetic chars: (low_nibble + 9) → 0xA-0xF
45        //
46        // Example: (b'A' & 0x0f) + 9 == 0x1 + 9 == 0xA
47        s.as_bytes().iter().fold(0f64, |res, &cur| {
48            res.mul_add(
49                16.,
50                if cur < b'A' {
51                    cur & 0xf
52                } else {
53                    (cur & 0xf) + 9
54                } as f64,
55            )
56        })
57    } else {
58        u64::from_str_radix(s, 16).unwrap() as f64
59    }
60}
61
62fn parse_integer_from_bin(s: &str) -> f64 {
63    debug_assert!(s.chars().all(|c| c == '0' || c == '1'));
64    const MAX_FAST_INT_LEN: usize = MAX_SAFE_INT.ilog2() as usize;
65    if s.len() > MAX_FAST_INT_LEN {
66        s.as_bytes().iter().fold(0f64, |res, &cur| {
67            res.mul_add(2., if cur == b'0' { 0. } else { 1. })
68        })
69    } else {
70        u64::from_str_radix(s, 2).unwrap() as f64
71    }
72}
73
74fn parse_integer_from_oct(s: &str) -> f64 {
75    debug_assert!(s.chars().all(|c| matches!(c, '0'..='7')));
76    const MAX_FAST_INT_LEN: usize = MAX_SAFE_INT.ilog(8) as usize;
77    if s.len() > MAX_FAST_INT_LEN {
78        s.as_bytes()
79            .iter()
80            .fold(0f64, |res, &cur| res.mul_add(8., (cur - b'0') as f64))
81    } else {
82        u64::from_str_radix(s, 8).unwrap() as f64
83    }
84}
85
86fn parse_integer_from_dec(s: &str) -> f64 {
87    debug_assert!(s.chars().all(|c| c.is_ascii_digit()));
88    const MAX_FAST_INT_LEN: usize = MAX_SAFE_INT.ilog10() as usize;
89    if s.len() > MAX_FAST_INT_LEN {
90        s.parse::<f64>().unwrap()
91    } else {
92        s.parse::<u64>().unwrap() as f64
93    }
94}