Skip to content

Commit b9661b4

Browse files
authored
Merge pull request #280 from dalle/issue275-deprecate-feature-macros
Add allow_leading_plus and skip_white_space in chars_format
2 parents 724834f + 4ed0177 commit b9661b4

11 files changed

+491
-331
lines changed

README.md

Lines changed: 234 additions & 186 deletions
Large diffs are not rendered by default.

include/fast_float/ascii_number.h

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -283,19 +283,18 @@ template <typename UC>
283283
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
284284
parse_number_string(UC const *p, UC const *pend,
285285
parse_options_t<UC> options) noexcept {
286-
chars_format const fmt = options.format;
286+
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
287287
UC const decimal_point = options.decimal_point;
288288

289289
parsed_number_string_t<UC> answer;
290290
answer.valid = false;
291291
answer.too_many_digits = false;
292+
// assume p < pend, so dereference without checks;
292293
answer.negative = (*p == UC('-'));
293-
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
294+
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here
294295
if ((*p == UC('-')) ||
295-
(!uint64_t(fmt & detail::basic_json_fmt) && *p == UC('+'))) {
296-
#else
297-
if (*p == UC('-')) { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
298-
#endif
296+
(uint64_t(fmt & chars_format::allow_leading_plus) &&
297+
!uint64_t(fmt & detail::basic_json_fmt) && *p == UC('+'))) {
299298
++p;
300299
if (p == pend) {
301300
return report_parse_error<UC>(
@@ -473,7 +472,11 @@ parse_number_string(UC const *p, UC const *pend,
473472

474473
template <typename T, typename UC>
475474
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
476-
parse_int_string(UC const *p, UC const *pend, T &value, int base) {
475+
parse_int_string(UC const *p, UC const *pend, T &value,
476+
parse_options_t<UC> options) {
477+
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
478+
int const base = options.base;
479+
477480
from_chars_result_t<UC> answer;
478481

479482
UC const *const first = p;
@@ -484,11 +487,8 @@ parse_int_string(UC const *p, UC const *pend, T &value, int base) {
484487
answer.ptr = first;
485488
return answer;
486489
}
487-
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
488-
if ((*p == UC('-')) || (*p == UC('+'))) {
489-
#else
490-
if (*p == UC('-')) {
491-
#endif
490+
if ((*p == UC('-')) ||
491+
(uint64_t(fmt & chars_format::allow_leading_plus) && (*p == UC('+')))) {
492492
++p;
493493
}
494494

include/fast_float/fast_float.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,13 @@ from_chars(UC const *first, UC const *last, T &value,
3838

3939
/**
4040
* Like from_chars, but accepts an `options` argument to govern number parsing.
41+
* Both for floating-point types and integer types.
4142
*/
4243
template <typename T, typename UC = char>
4344
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
4445
from_chars_advanced(UC const *first, UC const *last, T &value,
4546
parse_options_t<UC> options) noexcept;
47+
4648
/**
4749
* from_chars for integer types.
4850
*/

include/fast_float/float_common.h

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ enum class chars_format : uint64_t {
3434
json_or_infnan = uint64_t(detail::basic_json_fmt) | fixed | scientific,
3535
fortran = uint64_t(detail::basic_fortran_fmt) | fixed | scientific,
3636
general = fixed | scientific,
37+
allow_leading_plus = 1 << 7,
38+
skip_white_space = 1 << 8,
3739
};
3840

3941
template <typename UC> struct from_chars_result_t {
@@ -44,13 +46,15 @@ using from_chars_result = from_chars_result_t<char>;
4446

4547
template <typename UC> struct parse_options_t {
4648
constexpr explicit parse_options_t(chars_format fmt = chars_format::general,
47-
UC dot = UC('.'))
48-
: format(fmt), decimal_point(dot) {}
49+
UC dot = UC('.'), int b = 10)
50+
: format(fmt), decimal_point(dot), base(b) {}
4951

5052
/** Which number formats are accepted */
5153
chars_format format;
5254
/** The character used as decimal point */
5355
UC decimal_point;
56+
/** The base used for integers */
57+
int base;
5458
};
5559
using parse_options = parse_options_t<char>;
5660

@@ -218,12 +222,15 @@ fastfloat_really_inline constexpr bool is_supported_char_type() {
218222
// Compares two ASCII strings in a case insensitive manner.
219223
template <typename UC>
220224
inline FASTFLOAT_CONSTEXPR14 bool
221-
fastfloat_strncasecmp(UC const *input1, UC const *input2, size_t length) {
222-
char running_diff{0};
225+
fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase,
226+
size_t length) {
223227
for (size_t i = 0; i < length; ++i) {
224-
running_diff |= (char(input1[i]) ^ char(input2[i]));
228+
UC const actual = actual_mixedcase[i];
229+
if ((actual < 256 ? actual | 32 : actual) != expected_lowercase[i]) {
230+
return false;
231+
}
225232
}
226-
return (running_diff == 0) || (running_diff == 32);
233+
return true;
227234
}
228235

229236
#ifndef FLT_EVAL_METHOD
@@ -674,7 +681,6 @@ to_float(bool negative, adjusted_mantissa am, T &value) {
674681
#endif
675682
}
676683

677-
#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
678684
template <typename = void> struct space_lut {
679685
static constexpr bool value[] = {
680686
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -696,8 +702,9 @@ template <typename T> constexpr bool space_lut<T>::value[];
696702

697703
#endif
698704

699-
inline constexpr bool is_space(uint8_t c) { return space_lut<>::value[c]; }
700-
#endif
705+
template <typename UC> constexpr bool is_space(UC c) {
706+
return c < 256 && space_lut<>::value[uint8_t(c)];
707+
}
701708

702709
template <typename UC> static constexpr uint64_t int_cmp_zeros() {
703710
static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4),
@@ -839,6 +846,20 @@ operator^=(chars_format &lhs, chars_format rhs) noexcept {
839846
return lhs = (lhs ^ rhs);
840847
}
841848

849+
namespace detail {
850+
// adjust for deprecated feature macros
851+
constexpr chars_format adjust_for_feature_macros(chars_format fmt) {
852+
return fmt
853+
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS
854+
| chars_format::allow_leading_plus
855+
#endif
856+
#ifdef FASTFLOAT_SKIP_WHITE_SPACE
857+
| chars_format::skip_white_space
858+
#endif
859+
;
860+
}
861+
} // namespace detail
862+
842863
} // namespace fast_float
843864

844865
#endif

include/fast_float/parse_number.h

Lines changed: 65 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,18 @@ namespace detail {
1919
* strings a null-free and fixed.
2020
**/
2121
template <typename T, typename UC>
22-
from_chars_result_t<UC> FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first,
23-
UC const *last,
24-
T &value) noexcept {
22+
from_chars_result_t<UC>
23+
FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first, UC const *last,
24+
T &value, chars_format fmt) noexcept {
2525
from_chars_result_t<UC> answer{};
2626
answer.ptr = first;
2727
answer.ec = std::errc(); // be optimistic
2828
// assume first < last, so dereference without checks;
2929
bool const minusSign = (*first == UC('-'));
30-
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
31-
if ((*first == UC('-')) || (*first == UC('+'))) {
32-
#else
3330
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here
34-
if (*first == UC('-')) {
35-
#endif
31+
if ((*first == UC('-')) ||
32+
(uint64_t(fmt & chars_format::allow_leading_plus) &&
33+
(*first == UC('+')))) {
3634
++first;
3735
}
3836
if (last - first >= 3) {
@@ -284,22 +282,22 @@ from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
284282

285283
template <typename T, typename UC>
286284
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
287-
from_chars_advanced(UC const *first, UC const *last, T &value,
288-
parse_options_t<UC> options) noexcept {
285+
from_chars_float_advanced(UC const *first, UC const *last, T &value,
286+
parse_options_t<UC> options) noexcept {
289287

290288
static_assert(is_supported_float_type<T>(),
291289
"only some floating-point types are supported");
292290
static_assert(is_supported_char_type<UC>(),
293291
"only char, wchar_t, char16_t and char32_t are supported");
294292

295-
chars_format const fmt = options.format;
293+
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
296294

297295
from_chars_result_t<UC> answer;
298-
#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
299-
while ((first != last) && fast_float::is_space(uint8_t(*first))) {
300-
first++;
296+
if (uint64_t(fmt & chars_format::skip_white_space)) {
297+
while ((first != last) && fast_float::is_space(*first)) {
298+
first++;
299+
}
301300
}
302-
#endif
303301
if (first == last) {
304302
answer.ec = std::errc::invalid_argument;
305303
answer.ptr = first;
@@ -313,7 +311,7 @@ from_chars_advanced(UC const *first, UC const *last, T &value,
313311
answer.ptr = first;
314312
return answer;
315313
} else {
316-
return detail::parse_infnan(first, last, value);
314+
return detail::parse_infnan(first, last, value, fmt);
317315
}
318316
}
319317

@@ -324,21 +322,67 @@ from_chars_advanced(UC const *first, UC const *last, T &value,
324322
template <typename T, typename UC, typename>
325323
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
326324
from_chars(UC const *first, UC const *last, T &value, int base) noexcept {
325+
326+
static_assert(std::is_integral<T>::value, "only integer types are supported");
327+
static_assert(is_supported_char_type<UC>(),
328+
"only char, wchar_t, char16_t and char32_t are supported");
329+
330+
parse_options_t<UC> options;
331+
options.base = base;
332+
return from_chars_advanced(first, last, value, options);
333+
}
334+
335+
template <typename T, typename UC>
336+
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
337+
from_chars_int_advanced(UC const *first, UC const *last, T &value,
338+
parse_options_t<UC> options) noexcept {
339+
340+
static_assert(std::is_integral<T>::value, "only integer types are supported");
327341
static_assert(is_supported_char_type<UC>(),
328342
"only char, wchar_t, char16_t and char32_t are supported");
329343

344+
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
345+
int const base = options.base;
346+
330347
from_chars_result_t<UC> answer;
331-
#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
332-
while ((first != last) && fast_float::is_space(uint8_t(*first))) {
333-
first++;
348+
if (uint64_t(fmt & chars_format::skip_white_space)) {
349+
while ((first != last) && fast_float::is_space(*first)) {
350+
first++;
351+
}
334352
}
335-
#endif
336353
if (first == last || base < 2 || base > 36) {
337354
answer.ec = std::errc::invalid_argument;
338355
answer.ptr = first;
339356
return answer;
340357
}
341-
return parse_int_string(first, last, value, base);
358+
359+
return parse_int_string(first, last, value, options);
360+
}
361+
362+
template <bool> struct from_chars_advanced_caller {
363+
template <typename T, typename UC>
364+
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
365+
call(UC const *first, UC const *last, T &value,
366+
parse_options_t<UC> options) noexcept {
367+
return from_chars_float_advanced(first, last, value, options);
368+
}
369+
};
370+
371+
template <> struct from_chars_advanced_caller<false> {
372+
template <typename T, typename UC>
373+
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
374+
call(UC const *first, UC const *last, T &value,
375+
parse_options_t<UC> options) noexcept {
376+
return from_chars_int_advanced(first, last, value, options);
377+
}
378+
};
379+
380+
template <typename T, typename UC>
381+
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
382+
from_chars_advanced(UC const *first, UC const *last, T &value,
383+
parse_options_t<UC> options) noexcept {
384+
return from_chars_advanced_caller<is_supported_float_type<T>()>::call(
385+
first, last, value, options);
342386
}
343387

344388
} // namespace fast_float

tests/BUILD.bazel

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,15 @@ cc_test(
8888
],
8989
)
9090

91+
cc_test(
92+
name = "wide_char_test",
93+
srcs = ["wide_char_test.cpp"],
94+
deps = [
95+
"//:fast_float",
96+
"@doctest//doctest",
97+
],
98+
)
99+
91100
cc_test(
92101
name = "string_test",
93102
srcs = ["string_test.cpp"],

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ function(fast_float_add_cpp_test TEST_NAME)
6767
endfunction(fast_float_add_cpp_test)
6868

6969
fast_float_add_cpp_test(rcppfastfloat_test)
70+
fast_float_add_cpp_test(wide_char_test)
7071
fast_float_add_cpp_test(example_test)
7172
fast_float_add_cpp_test(example_comma_test)
7273
fast_float_add_cpp_test(basictest)

tests/fortran.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,14 @@
44
#include <cstdlib>
55
#include <iostream>
66
#include <vector>
7-
8-
#define FASTFLOAT_ALLOWS_LEADING_PLUS
9-
107
#include "fast_float/fast_float.h"
118

129
int main_readme() {
1310
const std::string input = "1d+4";
1411
double result;
15-
fast_float::parse_options options{fast_float::chars_format::fortran};
12+
fast_float::parse_options options{
13+
fast_float::chars_format::fortran |
14+
fast_float::chars_format::allow_leading_plus};
1615
auto answer = fast_float::from_chars_advanced(
1716
input.data(), input.data() + input.size(), result, options);
1817
if ((answer.ec != std::errc()) || ((result != 10000))) {
@@ -32,7 +31,9 @@ int main() {
3231
"1d-1", "1d-2", "1d-3", "1d-4"};
3332
const std::vector<std::string> fmt3{"+1+4", "+1+3", "+1+2", "+1+1", "+1+0",
3433
"+1-1", "+1-2", "+1-3", "+1-4"};
35-
const fast_float::parse_options options{fast_float::chars_format::fortran};
34+
const fast_float::parse_options options{
35+
fast_float::chars_format::fortran |
36+
fast_float::chars_format::allow_leading_plus};
3637

3738
for (auto const &f : fmt1) {
3839
auto d{std::distance(&fmt1[0], &f)};

0 commit comments

Comments
 (0)