]> git.proxmox.com Git - rustc.git/blob - vendor/minimal-lexical/tests/integration_tests.rs
New upstream version 1.60.0+dfsg1
[rustc.git] / vendor / minimal-lexical / tests / integration_tests.rs
1 /// Find and parse sign and get remaining bytes.
2 #[inline]
3 fn parse_sign<'a>(bytes: &'a [u8]) -> (bool, &'a [u8]) {
4 match bytes.get(0) {
5 Some(&b'+') => (true, &bytes[1..]),
6 Some(&b'-') => (false, &bytes[1..]),
7 _ => (true, bytes),
8 }
9 }
10
11 // Convert u8 to digit.
12 #[inline]
13 fn to_digit(c: u8) -> Option<u32> {
14 (c as char).to_digit(10)
15 }
16
17 // Add digit from exponent.
18 #[inline]
19 fn add_digit_i32(value: i32, digit: u32) -> Option<i32> {
20 return value.checked_mul(10)?.checked_add(digit as i32);
21 }
22
23 // Subtract digit from exponent.
24 #[inline]
25 fn sub_digit_i32(value: i32, digit: u32) -> Option<i32> {
26 return value.checked_mul(10)?.checked_sub(digit as i32);
27 }
28
29 // Convert character to digit.
30 #[inline]
31 fn is_digit(c: u8) -> bool {
32 to_digit(c).is_some()
33 }
34
35 // Split buffer at index.
36 #[inline]
37 fn split_at_index<'a>(digits: &'a [u8], index: usize) -> (&'a [u8], &'a [u8]) {
38 (&digits[..index], &digits[index..])
39 }
40
41 /// Consume until a an invalid digit is found.
42 ///
43 /// - `digits` - Slice containing 0 or more digits.
44 #[inline]
45 fn consume_digits<'a>(digits: &'a [u8]) -> (&'a [u8], &'a [u8]) {
46 // Consume all digits.
47 let mut index = 0;
48 while index < digits.len() && is_digit(digits[index]) {
49 index += 1;
50 }
51 split_at_index(digits, index)
52 }
53
54 // Trim leading 0s.
55 #[inline]
56 fn ltrim_zero<'a>(bytes: &'a [u8]) -> &'a [u8] {
57 let count = bytes.iter().take_while(|&&si| si == b'0').count();
58 &bytes[count..]
59 }
60
61 // Trim trailing 0s.
62 #[inline]
63 fn rtrim_zero<'a>(bytes: &'a [u8]) -> &'a [u8] {
64 let count = bytes.iter().rev().take_while(|&&si| si == b'0').count();
65 let index = bytes.len() - count;
66 &bytes[..index]
67 }
68
69 // PARSERS
70 // -------
71
72 /// Parse the exponent of the float.
73 ///
74 /// * `exponent` - Slice containing the exponent digits.
75 /// * `is_positive` - If the exponent sign is positive.
76 fn parse_exponent(exponent: &[u8], is_positive: bool) -> i32 {
77 // Parse the sign bit or current data.
78 let mut value: i32 = 0;
79 match is_positive {
80 true => {
81 for c in exponent {
82 value = match add_digit_i32(value, to_digit(*c).unwrap()) {
83 Some(v) => v,
84 None => return i32::max_value(),
85 };
86 }
87 },
88 false => {
89 for c in exponent {
90 value = match sub_digit_i32(value, to_digit(*c).unwrap()) {
91 Some(v) => v,
92 None => return i32::min_value(),
93 };
94 }
95 },
96 }
97
98 value
99 }
100
101 pub fn case_insensitive_starts_with<'a, 'b, Iter1, Iter2>(mut x: Iter1, mut y: Iter2) -> bool
102 where
103 Iter1: Iterator<Item = &'a u8>,
104 Iter2: Iterator<Item = &'b u8>,
105 {
106 // We use a faster optimization here for ASCII letters, which NaN
107 // and infinite strings **must** be. [A-Z] is 0x41-0x5A, while
108 // [a-z] is 0x61-0x7A. Therefore, the xor must be 0 or 32 if they
109 // are case-insensitive equal, but only if at least 1 of the inputs
110 // is an ASCII letter.
111 loop {
112 let yi = y.next();
113 if yi.is_none() {
114 return true;
115 }
116 let yi = *yi.unwrap();
117 let is_not_equal = x.next().map_or(true, |&xi| {
118 let xor = xi ^ yi;
119 xor != 0 && xor != 0x20
120 });
121 if is_not_equal {
122 return false;
123 }
124 }
125 }
126
127 /// Parse float from input bytes, returning the float and the remaining bytes.
128 ///
129 /// * `bytes` - Array of bytes leading with float-data.
130 pub fn parse_float<'a, F>(bytes: &'a [u8]) -> (F, &'a [u8])
131 where
132 F: minimal_lexical::Float,
133 {
134 let start = bytes;
135
136 // Parse the sign.
137 let (is_positive, bytes) = parse_sign(bytes);
138
139 // Check NaN, Inf, Infinity
140 if case_insensitive_starts_with(bytes.iter(), b"NaN".iter()) {
141 let mut float = F::from_bits(F::EXPONENT_MASK | (F::HIDDEN_BIT_MASK >> 1));
142 if !is_positive {
143 float = -float;
144 }
145 return (float, &bytes[3..]);
146 } else if case_insensitive_starts_with(bytes.iter(), b"Infinity".iter()) {
147 let mut float = F::from_bits(F::EXPONENT_MASK);
148 if !is_positive {
149 float = -float;
150 }
151 return (float, &bytes[8..]);
152 } else if case_insensitive_starts_with(bytes.iter(), b"inf".iter()) {
153 let mut float = F::from_bits(F::EXPONENT_MASK);
154 if !is_positive {
155 float = -float;
156 }
157 return (float, &bytes[3..]);
158 }
159
160 // Extract and parse the float components:
161 // 1. Integer
162 // 2. Fraction
163 // 3. Exponent
164 let (integer_slc, bytes) = consume_digits(bytes);
165 let (fraction_slc, bytes) = match bytes.first() {
166 Some(&b'.') => consume_digits(&bytes[1..]),
167 _ => (&bytes[..0], bytes),
168 };
169 let (exponent, bytes) = match bytes.first() {
170 Some(&b'e') | Some(&b'E') => {
171 // Extract and parse the exponent.
172 let (is_positive, bytes) = parse_sign(&bytes[1..]);
173 let (exponent, bytes) = consume_digits(bytes);
174 (parse_exponent(exponent, is_positive), bytes)
175 },
176 _ => (0, bytes),
177 };
178
179 if bytes.len() == start.len() {
180 return (F::from_u64(0), bytes);
181 }
182
183 // Note: You may want to check and validate the float data here:
184 // 1). Many floats require integer or fraction digits, if a fraction
185 // is present.
186 // 2). All floats require either integer or fraction digits.
187 // 3). Some floats do not allow a '+' sign before the significant digits.
188 // 4). Many floats require exponent digits after the exponent symbol.
189 // 5). Some floats do not allow a '+' sign before the exponent.
190
191 // We now need to trim leading and trailing 0s from the integer
192 // and fraction, respectively. This is required to make the
193 // fast and moderate paths more efficient, and for the slow
194 // path.
195 let integer_slc = ltrim_zero(integer_slc);
196 let fraction_slc = rtrim_zero(fraction_slc);
197
198 // Create the float and return our data.
199 let mut float: F =
200 minimal_lexical::parse_float(integer_slc.iter(), fraction_slc.iter(), exponent);
201 if !is_positive {
202 float = -float;
203 }
204
205 (float, bytes)
206 }
207
208 macro_rules! b {
209 ($x:literal) => {
210 $x.as_bytes()
211 };
212 }
213
214 #[test]
215 fn f32_test() {
216 assert_eq!(
217 (184467440000000000000.0, b!("\x00\x00006")),
218 parse_float::<f32>(b"000184467440737095516150\x00\x00006")
219 );
220 }
221
222 #[test]
223 fn f64_test() {
224 assert_eq!(
225 (184467440737095500000.0, b!("\x00\x00006")),
226 parse_float::<f64>(b"000184467440737095516150\x00\x00006")
227 );
228 }