@@ -234,6 +234,25 @@ void loop_parse_if_eight_digits(const char*& p, const char* const pend, uint64_t
234
234
}
235
235
}
236
236
237
+ enum class parse_error {
238
+ no_error,
239
+ // [JSON-only] The minus sign must be followed by an integer.
240
+ missing_integer_after_sign,
241
+ // A sign must be followed by an integer or dot.
242
+ missing_integer_or_dot_after_sign,
243
+ // [JSON-only] The integer part must not have leading zeros.
244
+ leading_zeros_in_integer_part,
245
+ // [JSON-only] The integer part must have at least one digit.
246
+ no_digits_in_integer_part,
247
+ // [JSON-only] If there is a decimal point, there must be digits in the
248
+ // fractional part.
249
+ no_digits_in_fractional_part,
250
+ // The mantissa must have at least one digit.
251
+ no_digits_in_mantissa,
252
+ // Scientific notation requires an exponential part.
253
+ missing_exponential_part,
254
+ };
255
+
237
256
template <typename UC>
238
257
struct parsed_number_string_t {
239
258
int64_t exponent{0 };
@@ -245,11 +264,22 @@ struct parsed_number_string_t {
245
264
// contains the range of the significant digits
246
265
span<const UC> integer{}; // non-nullable
247
266
span<const UC> fraction{}; // nullable
267
+ parse_error error{parse_error::no_error};
248
268
};
249
269
250
270
using byte_span = span<const char >;
251
271
using parsed_number_string = parsed_number_string_t <char >;
252
272
273
+ template <typename UC>
274
+ fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t <UC>
275
+ report_parse_error (UC const * p, parse_error error) {
276
+ parsed_number_string_t <UC> answer;
277
+ answer.valid = false ;
278
+ answer.lastmatch = p;
279
+ answer.error = error;
280
+ return answer;
281
+ }
282
+
253
283
// Assuming that you use no more than 19 digits, this will
254
284
// parse an ASCII string.
255
285
template <typename UC>
@@ -269,15 +299,16 @@ parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, par
269
299
#endif
270
300
++p;
271
301
if (p == pend) {
272
- return answer;
302
+ return report_parse_error<UC>(
303
+ p, parse_error::missing_integer_or_dot_after_sign);
273
304
}
274
305
if (fmt & FASTFLOAT_JSONFMT) {
275
306
if (!is_integer (*p)) { // a sign must be followed by an integer
276
- return answer ;
307
+ return report_parse_error<UC>(p, parse_error::missing_integer_after_sign) ;
277
308
}
278
309
} else {
279
310
if (!is_integer (*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot
280
- return answer ;
311
+ return report_parse_error<UC>(p, parse_error::missing_integer_or_dot_after_sign) ;
281
312
}
282
313
}
283
314
}
@@ -297,8 +328,12 @@ parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, par
297
328
answer.integer = span<const UC>(start_digits, size_t (digit_count));
298
329
if (fmt & FASTFLOAT_JSONFMT) {
299
330
// at least 1 digit in integer part, without leading zeros
300
- if (digit_count == 0 || (start_digits[0 ] == UC (' 0' ) && digit_count > 1 )) {
301
- return answer;
331
+ if (digit_count == 0 ) {
332
+ return report_parse_error<UC>(p, parse_error::no_digits_in_integer_part);
333
+ }
334
+ if ((start_digits[0 ] == UC (' 0' ) && digit_count > 1 )) {
335
+ return report_parse_error<UC>(start_digits,
336
+ parse_error::leading_zeros_in_integer_part);
302
337
}
303
338
}
304
339
@@ -323,11 +358,10 @@ parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, par
323
358
if (fmt & FASTFLOAT_JSONFMT) {
324
359
// at least 1 digit in fractional part
325
360
if (has_decimal_point && exponent == 0 ) {
326
- return answer ;
361
+ return report_parse_error<UC>(p, parse_error::no_digits_in_fractional_part) ;
327
362
}
328
- }
329
- else if (digit_count == 0 ) { // we must have encountered at least one integer!
330
- return answer;
363
+ } else if (digit_count == 0 ) { // we must have encountered at least one integer!
364
+ return report_parse_error<UC>(p, parse_error::no_digits_in_mantissa);
331
365
}
332
366
int64_t exp_number = 0 ; // explicit exponential part
333
367
if ( ((fmt & chars_format::scientific) &&
@@ -350,8 +384,10 @@ parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, par
350
384
}
351
385
if ((p == pend) || !is_integer (*p)) {
352
386
if (!(fmt & chars_format::fixed)) {
353
- // We are in error.
354
- return answer;
387
+ // The exponential part is invalid for scientific notation, so it must
388
+ // be a trailing token for fixed notation. However, fixed notation is
389
+ // disabled, so report a scientific notation error.
390
+ return report_parse_error<UC>(p, parse_error::missing_exponential_part);
355
391
}
356
392
// Otherwise, we will be ignoring the 'e'.
357
393
p = location_of_e;
@@ -368,7 +404,9 @@ parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, par
368
404
}
369
405
} else {
370
406
// If it scientific and not fixed, we have to bail out.
371
- if ((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) { return answer; }
407
+ if ((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) {
408
+ return report_parse_error<UC>(p, parse_error::missing_exponential_part);
409
+ }
372
410
}
373
411
answer.lastmatch = p;
374
412
answer.valid = true ;
0 commit comments