Experimental support for char_t types

This commit is contained in:
Maya Warrier 2023-03-30 04:48:18 -04:00
parent a699476fd2
commit 2b118c843a
5 changed files with 144 additions and 81 deletions

View File

@ -5,15 +5,24 @@
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include <iterator> #include <iterator>
#include <type_traits>
#include "float_common.h" #include "float_common.h"
#define FASTFLOAT_SSE2 1
#if FASTFLOAT_SSE2
#include <emmintrin.h>
#endif
namespace fast_float { namespace fast_float {
// Next function can be micro-optimized, but compilers are entirely // Next function can be micro-optimized, but compilers are entirely
// able to optimize it well. // able to optimize it well.
fastfloat_really_inline constexpr bool is_integer(char c) noexcept { template <typename CharT>
return c >= '0' && c <= '9'; fastfloat_really_inline constexpr bool is_integer(CharT c) noexcept {
return c >= static_cast<CharT>('0') && c <= static_cast<CharT>('9');
} }
fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) { fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
@ -28,7 +37,46 @@ fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20
uint64_t read_u64(const char *chars) { uint64_t fast_read_u64(const char* chars)
{
uint64_t val;
::memcpy(&val, chars, sizeof(uint64_t));
return val;
}
fastfloat_really_inline
uint64_t fast_read_u64(const char16_t* chars)
{
#if FASTFLOAT_SSE2
const void* const p = chars;
static const char16_t masks[] = {0xff, 0xff, 0xff, 0xff};
const __m128i m_masks = _mm_loadu_si128(reinterpret_cast<const __m128i*>(masks));
// mask hi bytes
__m128i i1 = _mm_and_si128(_mm_loadu_si64(p), m_masks);
__m128i i2 = _mm_and_si128(_mm_loadu_si64(p + 8), m_masks);
// pack into chars
__m128i packed = _mm_packus_epi16(i1, i2);
// extract
uint64_t val;
_mm_storeu_epi64(&val, _mm_shuffle_epi32(packed, 0x8));
return val;
#else
alignas(8) unsigned char bytes[8];
for (int i = 0; i < 8; ++i)
bytes[i] = (unsigned char)chars[i];
uint64_t val;
::memcpy(&val, bytes, sizeof(uint64_t));
return val;
#endif
}
template <typename CharT>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
uint64_t read_u64(const CharT *chars) {
if (cpp20_and_in_constexpr()) { if (cpp20_and_in_constexpr()) {
uint64_t val = 0; uint64_t val = 0;
for(int i = 0; i < 8; ++i) { for(int i = 0; i < 8; ++i) {
@ -37,8 +85,7 @@ uint64_t read_u64(const char *chars) {
} }
return val; return val;
} }
uint64_t val; uint64_t val = fast_read_u64(chars);
::memcpy(&val, chars, sizeof(uint64_t));
#if FASTFLOAT_IS_BIG_ENDIAN == 1 #if FASTFLOAT_IS_BIG_ENDIAN == 1
// Need to read as-if the number was in little-endian order. // Need to read as-if the number was in little-endian order.
val = byteswap(val); val = byteswap(val);
@ -46,6 +93,7 @@ uint64_t read_u64(const char *chars) {
return val; return val;
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20
void write_u64(uint8_t *chars, uint64_t val) { void write_u64(uint8_t *chars, uint64_t val) {
if (cpp20_and_in_constexpr()) { if (cpp20_and_in_constexpr()) {
@ -75,8 +123,9 @@ uint32_t parse_eight_digits_unrolled(uint64_t val) {
return uint32_t(val); return uint32_t(val);
} }
template <typename CharT>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20
uint32_t parse_eight_digits_unrolled(const char *chars) noexcept { uint32_t parse_eight_digits_unrolled(const CharT *chars) noexcept {
return parse_eight_digits_unrolled(read_u64(chars)); return parse_eight_digits_unrolled(read_u64(chars));
} }
@ -86,43 +135,46 @@ fastfloat_really_inline constexpr bool is_made_of_eight_digits_fast(uint64_t val
0x8080808080808080)); 0x8080808080808080));
} }
template <typename CharT>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20
bool is_made_of_eight_digits_fast(const char *chars) noexcept { bool is_made_of_eight_digits_fast(const CharT *chars) noexcept {
return is_made_of_eight_digits_fast(read_u64(chars)); return is_made_of_eight_digits_fast(read_u64(chars));
} }
typedef span<const char> byte_span; typedef span<const char> byte_span;
template <typename CharT = char>
struct parsed_number_string { struct parsed_number_string {
int64_t exponent{0}; int64_t exponent{0};
uint64_t mantissa{0}; uint64_t mantissa{0};
const char *lastmatch{nullptr}; const CharT *lastmatch{nullptr};
bool negative{false}; bool negative{false};
bool valid{false}; bool valid{false};
bool is_64bit_int{false}; bool is_64bit_int{false};
bool too_many_digits{false}; bool too_many_digits{false};
// contains the range of the significant digits // contains the range of the significant digits
byte_span integer{}; // non-nullable span<const CharT> integer{}; // non-nullable
byte_span fraction{}; // nullable span<const CharT> fraction{}; // nullable
}; };
// Assuming that you use no more than 19 digits, this will // Assuming that you use no more than 19 digits, this will
// parse an ASCII string. // parse an ASCII string.
template <typename CharT>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20
parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept { parsed_number_string<CharT> parse_number_string(const CharT *p, const CharT *pend, parse_options options) noexcept {
const chars_format fmt = options.format; const chars_format fmt = options.format;
const parse_rules rules = options.rules; const parse_rules rules = options.rules;
const bool parse_ints = options.parse_ints; const bool parse_ints = options.parse_ints;
const char decimal_point = options.decimal_point; const CharT decimal_point = static_cast<CharT>(options.decimal_point);
parsed_number_string answer; parsed_number_string<CharT> answer;
answer.valid = false; answer.valid = false;
answer.too_many_digits = false; answer.too_many_digits = false;
answer.negative = (*p == '-'); answer.negative = (*p == static_cast<CharT>('-'));
#if FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default #if FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
if ((*p == '-') || (*p == '+')) { if ((*p == static_cast<CharT>('-')) || (*p == static_cast<CharT>('+'))) {
#else #else
if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here if (*p == static_cast<CharT>('-')) { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
#endif #endif
++p; ++p;
if (p == pend) { if (p == pend) {
@ -132,7 +184,7 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
if (!is_integer(*p) && (rules == parse_rules::json_rules || *p != decimal_point)) if (!is_integer(*p) && (rules == parse_rules::json_rules || *p != decimal_point))
return answer; return answer;
} }
const char *const start_digits = p; const CharT *const start_digits = p;
uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad) uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
@ -140,17 +192,17 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
// a multiplication by 10 is cheaper than an arbitrary integer // a multiplication by 10 is cheaper than an arbitrary integer
// multiplication // multiplication
i = 10 * i + i = 10 * i +
uint64_t(*p - '0'); // might overflow, we will handle the overflow later uint64_t(*p - static_cast<CharT>('0')); // might overflow, we will handle the overflow later
++p; ++p;
} }
const char *const end_of_integer_part = p; const CharT *const end_of_integer_part = p;
int64_t digit_count = int64_t(end_of_integer_part - start_digits); int64_t digit_count = int64_t(end_of_integer_part - start_digits);
answer.integer = byte_span(start_digits, size_t(digit_count)); answer.integer = span<const CharT>(start_digits, size_t(digit_count));
int64_t exponent = 0; int64_t exponent = 0;
const bool has_decimal_point = (p != pend) && (*p == decimal_point); const bool has_decimal_point = (p != pend) && (*p == decimal_point);
if (has_decimal_point) { if (has_decimal_point) {
++p; ++p;
const char* before = p; const CharT* before = p;
// can occur at most twice without overflowing, but let it occur more, since // can occur at most twice without overflowing, but let it occur more, since
// for integers with many digits, digit parsing is the primary bottleneck. // for integers with many digits, digit parsing is the primary bottleneck.
while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) { while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) {
@ -158,12 +210,11 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
p += 8; p += 8;
} }
while ((p != pend) && is_integer(*p)) { while ((p != pend) && is_integer(*p)) {
uint8_t digit = uint8_t(*p - '0'); i = i * 10 + uint64_t(*p - static_cast<CharT>('0')); // in rare cases, this will overflow, but that's ok
++p; ++p;
i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
} }
exponent = before - p; exponent = before - p;
answer.fraction = byte_span(before, size_t(p - before)); answer.fraction = span<const CharT>(before, size_t(p - before));
digit_count -= exponent; digit_count -= exponent;
} }
// we must have encountered at least one integer (or two if a decimal point exists, with json rules). // we must have encountered at least one integer (or two if a decimal point exists, with json rules).
@ -171,14 +222,14 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
return answer; return answer;
} }
int64_t exp_number = 0; // explicit exponential part int64_t exp_number = 0; // explicit exponential part
if ((fmt & chars_format::scientific) && (p != pend) && (('e' == *p) || ('E' == *p))) { if ((fmt & chars_format::scientific) && (p != pend) && ((static_cast<CharT>('e') == *p) || (static_cast<CharT>('E') == *p))) {
const char * location_of_e = p; const CharT * location_of_e = p;
++p; ++p;
bool neg_exp = false; bool neg_exp = false;
if ((p != pend) && ('-' == *p)) { if ((p != pend) && (static_cast<CharT>('-') == *p)) {
neg_exp = true; neg_exp = true;
++p; ++p;
} else if ((p != pend) && ('+' == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1) } else if ((p != pend) && (static_cast<CharT>('+') == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
++p; ++p;
} }
if ((p == pend) || !is_integer(*p)) { if ((p == pend) || !is_integer(*p)) {
@ -190,7 +241,7 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
p = location_of_e; p = location_of_e;
} else { } else {
while ((p != pend) && is_integer(*p)) { while ((p != pend) && is_integer(*p)) {
uint8_t digit = uint8_t(*p - '0'); uint8_t digit = uint8_t(*p - static_cast<CharT>('0'));
if (exp_number < 0x10000000) { if (exp_number < 0x10000000) {
exp_number = 10 * exp_number + digit; exp_number = 10 * exp_number + digit;
} }
@ -205,7 +256,7 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
} }
// disallow leading zeros before the decimal point // disallow leading zeros before the decimal point
if (rules == parse_rules::json_rules && start_digits[0] == '0' && digit_count >= 2 && is_integer(start_digits[1])) if (rules == parse_rules::json_rules && start_digits[0] == static_cast<CharT>('0') && digit_count >= 2 && is_integer(start_digits[1]))
return answer; return answer;
answer.lastmatch = p; answer.lastmatch = p;
@ -222,9 +273,9 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
// We have to handle the case where we have 0.0000somenumber. // We have to handle the case where we have 0.0000somenumber.
// We need to be mindful of the case where we only have zeroes... // We need to be mindful of the case where we only have zeroes...
// E.g., 0.000000000...000. // E.g., 0.000000000...000.
const char *start = start_digits; const CharT *start = start_digits;
while ((start != pend) && (*start == '0' || *start == decimal_point)) { while ((start != pend) && (*start == static_cast<CharT>('0') || *start == decimal_point)) {
if(*start == '0') { digit_count --; } if(*start == static_cast<CharT>('0')) { digit_count --; }
start++; start++;
} }
constexpr uint64_t minimal_twenty_digit_integer{10000000000000000000ULL}; constexpr uint64_t minimal_twenty_digit_integer{10000000000000000000ULL};
@ -241,19 +292,19 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
// pre-tokenized spans from above. // pre-tokenized spans from above.
i = 0; i = 0;
p = answer.integer.ptr; p = answer.integer.ptr;
const char* int_end = p + answer.integer.len(); const CharT* int_end = p + answer.integer.len();
const uint64_t minimal_nineteen_digit_integer{1000000000000000000}; const uint64_t minimal_nineteen_digit_integer{1000000000000000000};
while((i < minimal_nineteen_digit_integer) && (p != int_end)) { while((i < minimal_nineteen_digit_integer) && (p != int_end)) {
i = i * 10 + uint64_t(*p - '0'); i = i * 10 + uint64_t(*p - static_cast<CharT>('0'));
++p; ++p;
} }
if (i >= minimal_nineteen_digit_integer) { // We have a big integers if (i >= minimal_nineteen_digit_integer) { // We have a big integers
exponent = end_of_integer_part - p + exp_number; exponent = end_of_integer_part - p + exp_number;
} else { // We have a value with a fractional component. } else { // We have a value with a fractional component.
p = answer.fraction.ptr; p = answer.fraction.ptr;
const char* frac_end = p + answer.fraction.len(); const CharT* frac_end = p + answer.fraction.len();
while((i < minimal_nineteen_digit_integer) && (p != frac_end)) { while((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
i = i * 10 + uint64_t(*p - '0'); i = i * 10 + uint64_t(*p - static_cast<CharT>('0'));
++p; ++p;
} }
exponent = answer.fraction.ptr - p + exp_number; exponent = answer.fraction.ptr - p + exp_number;

View File

@ -23,8 +23,9 @@ constexpr static uint64_t powers_of_ten_uint64[] = {
// this algorithm is not even close to optimized, but it has no practical // this algorithm is not even close to optimized, but it has no practical
// effect on performance: in order to have a faster algorithm, we'd need // effect on performance: in order to have a faster algorithm, we'd need
// to slow down performance for faster algorithms, and this is still fast. // to slow down performance for faster algorithms, and this is still fast.
template <typename CharT>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 fastfloat_really_inline FASTFLOAT_CONSTEXPR14
int32_t scientific_exponent(parsed_number_string& num) noexcept { int32_t scientific_exponent(parsed_number_string<CharT>& num) noexcept {
uint64_t mantissa = num.mantissa; uint64_t mantissa = num.mantissa;
int32_t exponent = int32_t(num.exponent); int32_t exponent = int32_t(num.exponent);
while (mantissa >= 10000) { while (mantissa >= 10000) {
@ -154,18 +155,19 @@ void round_down(adjusted_mantissa& am, int32_t shift) noexcept {
am.power2 += shift; am.power2 += shift;
} }
template <typename CharT>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20
void skip_zeros(const char*& first, const char* last) noexcept { void skip_zeros(const CharT*& first, const CharT* last) noexcept {
uint64_t val; uint64_t val;
while (!cpp20_and_in_constexpr() && std::distance(first, last) >= 8) { while (!cpp20_and_in_constexpr() && std::distance(first, last) >= 8) {
::memcpy(&val, first, sizeof(uint64_t)); val = fast_read_u64(first);
if (val != 0x3030303030303030) { if (val != 0x3030303030303030) {
break; break;
} }
first += 8; first += 8;
} }
while (first != last) { while (first != last) {
if (*first != '0') { if (*first != static_cast<CharT>('0')) {
break; break;
} }
first++; first++;
@ -174,19 +176,20 @@ void skip_zeros(const char*& first, const char* last) noexcept {
// determine if any non-zero digits were truncated. // determine if any non-zero digits were truncated.
// all characters must be valid digits. // all characters must be valid digits.
template <typename CharT>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20
bool is_truncated(const char* first, const char* last) noexcept { bool is_truncated(const CharT* first, const CharT* last) noexcept {
// do 8-bit optimizations, can just compare to 8 literal 0s. // do 8-bit optimizations, can just compare to 8 literal 0s.
uint64_t val; uint64_t val;
while (!cpp20_and_in_constexpr() && std::distance(first, last) >= 8) { while (!cpp20_and_in_constexpr() && std::distance(first, last) >= 8) {
::memcpy(&val, first, sizeof(uint64_t)); val = fast_read_u64(first);
if (val != 0x3030303030303030) { if (val != 0x3030303030303030) {
return true; return true;
} }
first += 8; first += 8;
} }
while (first != last) { while (first != last) {
if (*first != '0') { if (*first != static_cast<CharT>('0')) {
return true; return true;
} }
first++; first++;
@ -194,22 +197,25 @@ bool is_truncated(const char* first, const char* last) noexcept {
return false; return false;
} }
template <typename CharT>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20
bool is_truncated(byte_span s) noexcept { bool is_truncated(span<const CharT> s) noexcept {
return is_truncated(s.ptr, s.ptr + s.len()); return is_truncated(s.ptr, s.ptr + s.len());
} }
template <typename CharT>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20
void parse_eight_digits(const char*& p, limb& value, size_t& counter, size_t& count) noexcept { void parse_eight_digits(const CharT*& p, limb& value, size_t& counter, size_t& count) noexcept {
value = value * 100000000 + parse_eight_digits_unrolled(p); value = value * 100000000 + parse_eight_digits_unrolled(p);
p += 8; p += 8;
counter += 8; counter += 8;
count += 8; count += 8;
} }
template <typename CharT>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 fastfloat_really_inline FASTFLOAT_CONSTEXPR14
void parse_one_digit(const char*& p, limb& value, size_t& counter, size_t& count) noexcept { void parse_one_digit(const CharT*& p, limb& value, size_t& counter, size_t& count) noexcept {
value = value * 10 + limb(*p - '0'); value = value * 10 + limb(*p - static_cast<CharT>('0'));
p++; p++;
counter++; counter++;
count++; count++;
@ -230,8 +236,9 @@ void round_up_bigint(bigint& big, size_t& count) noexcept {
} }
// parse the significant digits into a big integer // parse the significant digits into a big integer
template <typename CharT>
inline FASTFLOAT_CONSTEXPR20 inline FASTFLOAT_CONSTEXPR20
void parse_mantissa(bigint& result, parsed_number_string& num, size_t max_digits, size_t& digits) noexcept { void parse_mantissa(bigint& result, parsed_number_string<CharT>& num, size_t max_digits, size_t& digits) noexcept {
// try to minimize the number of big integer and scalar multiplication. // try to minimize the number of big integer and scalar multiplication.
// therefore, try to parse 8 digits at a time, and multiply by the largest // therefore, try to parse 8 digits at a time, and multiply by the largest
// scalar value (9 or 19 digits) for each step. // scalar value (9 or 19 digits) for each step.
@ -245,8 +252,8 @@ void parse_mantissa(bigint& result, parsed_number_string& num, size_t max_digits
#endif #endif
// process all integer digits. // process all integer digits.
const char* p = num.integer.ptr; const CharT* p = num.integer.ptr;
const char* pend = p + num.integer.len(); const CharT* pend = p + num.integer.len();
skip_zeros(p, pend); skip_zeros(p, pend);
// process all digits, in increments of step per loop // process all digits, in increments of step per loop
while (p != pend) { while (p != pend) {
@ -395,9 +402,9 @@ adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa am, int
// `b` as a big-integer type, scaled to the same binary exponent as // `b` as a big-integer type, scaled to the same binary exponent as
// the actual digits. we then compare the big integer representations // the actual digits. we then compare the big integer representations
// of both, and use that to direct rounding. // of both, and use that to direct rounding.
template <typename T> template <typename T, typename CharT>
inline FASTFLOAT_CONSTEXPR20 inline FASTFLOAT_CONSTEXPR20
adjusted_mantissa digit_comp(parsed_number_string& num, adjusted_mantissa am) noexcept { adjusted_mantissa digit_comp(parsed_number_string<CharT>& num, adjusted_mantissa am) noexcept {
// remove the invalid exponent bias // remove the invalid exponent bias
am.power2 -= invalid_am_bias; am.power2 -= invalid_am_bias;

View File

@ -18,8 +18,9 @@ enum parse_rules {
json_rules, json_rules,
}; };
template <typename CharT>
struct from_chars_result { struct from_chars_result {
const char *ptr; const CharT *ptr;
std::errc ec; std::errc ec;
}; };
@ -59,17 +60,17 @@ struct parse_options {
* to determine whether we allow the fixed point and scientific notation respectively. * to determine whether we allow the fixed point and scientific notation respectively.
* The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`. * The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
*/ */
template<typename T> template<typename T, typename CharT>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20
from_chars_result from_chars(const char *first, const char *last, from_chars_result<CharT> from_chars(const CharT *first, const CharT *last,
T &value, chars_format fmt = chars_format::general) noexcept; T &value, chars_format fmt = chars_format::general) noexcept;
/** /**
* Like from_chars, but accepts an `options` argument to govern number parsing. * Like from_chars, but accepts an `options` argument to govern number parsing.
*/ */
template<typename T> template<typename T, typename CharT>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20
from_chars_result from_chars_advanced(const char *first, const char *last, from_chars_result<CharT> from_chars_advanced(const CharT *first, const CharT *last,
T &value, parse_options options) noexcept; T &value, parse_options options) noexcept;
} }
@ -77,10 +78,10 @@ from_chars_result from_chars_advanced(const char *first, const char *last,
#include "ascii_number.h" // parsed_number_string #include "ascii_number.h" // parsed_number_string
namespace fast_float { namespace fast_float {
template <typename T> template <typename T, typename CharT>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20
from_chars_result from_chars_preparsed(parsed_number_string parsed, from_chars_result<CharT> from_chars_preparsed(parsed_number_string<CharT> parsed,
const char* first, const char* last, T& value) noexcept; const CharT* first, const CharT* last, T& value) noexcept;
} }
// namespace fast_float // namespace fast_float

View File

@ -106,11 +106,13 @@ fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() {
} }
// Compares two ASCII strings in a case insensitive manner. // Compares two ASCII strings in a case insensitive manner.
// maya: for now, keep input2 ASCII only
template <typename CharT>
inline FASTFLOAT_CONSTEXPR14 bool inline FASTFLOAT_CONSTEXPR14 bool
fastfloat_strncasecmp(const char *input1, const char *input2, size_t length) { fastfloat_strncasecmp(const CharT *input1, const char *input2, size_t length) {
char running_diff{0}; char running_diff{0};
for (size_t i = 0; i < length; i++) { for (size_t i = 0; i < length; i++) {
running_diff |= (input1[i] ^ input2[i]); running_diff |= (static_cast<char>(input1[i]) ^ input2[i]);
} }
return (running_diff == 0) || (running_diff == 32); return (running_diff == 0) || (running_diff == 32);
} }

View File

@ -19,19 +19,19 @@ namespace detail {
* The case comparisons could be made much faster given that we know that the * The case comparisons could be made much faster given that we know that the
* strings a null-free and fixed. * strings a null-free and fixed.
**/ **/
template <typename T> template <typename T, typename CharT>
from_chars_result FASTFLOAT_CONSTEXPR14 from_chars_result<CharT> FASTFLOAT_CONSTEXPR14
parse_infnan(const char *first, const char *last, T &value) noexcept { parse_infnan(const CharT *first, const CharT *last, T &value) noexcept {
from_chars_result answer{}; from_chars_result<CharT> answer{};
answer.ptr = first; answer.ptr = first;
answer.ec = std::errc(); // be optimistic answer.ec = std::errc(); // be optimistic
bool minusSign = false; bool minusSign = false;
if (*first == '-') { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here if (*first == static_cast<CharT>('-')) { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here
minusSign = true; minusSign = true;
++first; ++first;
} }
#if FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default #if FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
if (*first == '+') { if (*first == static_cast<CharT>('+')) {
++first; ++first;
} }
#endif #endif
@ -40,13 +40,15 @@ parse_infnan(const char *first, const char *last, T &value) noexcept {
answer.ptr = (first += 3); answer.ptr = (first += 3);
value = minusSign ? -std::numeric_limits<T>::quiet_NaN() : std::numeric_limits<T>::quiet_NaN(); value = minusSign ? -std::numeric_limits<T>::quiet_NaN() : std::numeric_limits<T>::quiet_NaN();
// Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan). // Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan).
if(first != last && *first == '(') { if(first != last && *first == static_cast<CharT>('(')) {
for(const char* ptr = first + 1; ptr != last; ++ptr) { for(const CharT* ptr = first + 1; ptr != last; ++ptr) {
if (*ptr == ')') { if (*ptr == static_cast<CharT>(')')) {
answer.ptr = ptr + 1; // valid nan(n-char-seq-opt) answer.ptr = ptr + 1; // valid nan(n-char-seq-opt)
break; break;
} }
else if(!(('a' <= *ptr && *ptr <= 'z') || ('A' <= *ptr && *ptr <= 'Z') || ('0' <= *ptr && *ptr <= '9') || *ptr == '_')) else if(!((static_cast<CharT>('a') <= *ptr && *ptr <= static_cast<CharT>('z')) ||
(static_cast<CharT>('A') <= *ptr && *ptr <= static_cast<CharT>('Z')) ||
(static_cast<CharT>('0') <= *ptr && *ptr <= static_cast<CharT>('9')) || *ptr == static_cast<CharT>('_')))
break; // forbidden char, not nan(n-char-seq-opt) break; // forbidden char, not nan(n-char-seq-opt)
} }
} }
@ -132,21 +134,21 @@ fastfloat_really_inline bool rounds_to_nearest() noexcept {
} // namespace detail } // namespace detail
template<typename T> template<typename T, typename CharT>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20
from_chars_result from_chars(const char *first, const char *last, from_chars_result<CharT> from_chars(const CharT *first, const CharT *last,
T &value, chars_format fmt /*= chars_format::general*/) noexcept { T &value, chars_format fmt /*= chars_format::general*/) noexcept {
return from_chars_advanced(first, last, value, parse_options{fmt}); return from_chars_advanced(first, last, value, parse_options{fmt});
} }
template<typename T> template<typename T, typename CharT>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20
from_chars_result from_chars_preparsed(parsed_number_string pns, const char* first, const char* last, T& value) noexcept from_chars_result<CharT> from_chars_preparsed(parsed_number_string<CharT> pns, const CharT* first, const CharT* last, T& value) noexcept
{ {
static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported"); static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported");
from_chars_result answer; from_chars_result<CharT> answer;
if (!pns.valid) { if (!pns.valid) {
return detail::parse_infnan(first, last, value); return detail::parse_infnan(first, last, value);
} }
@ -205,12 +207,12 @@ from_chars_result from_chars_preparsed(parsed_number_string pns, const char* fir
return answer; return answer;
} }
template<typename T> template<typename T, typename CharT>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20
from_chars_result from_chars_advanced(const char *first, const char *last, from_chars_result<CharT> from_chars_advanced(const CharT *first, const CharT *last,
T &value, parse_options options) noexcept { T &value, parse_options options) noexcept {
from_chars_result answer; from_chars_result<CharT> answer;
#if FASTFLOAT_SKIP_WHITE_SPACE // disabled by default #if FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
while ((first != last) && fast_float::is_space(uint8_t(*first))) { while ((first != last) && fast_float::is_space(uint8_t(*first))) {
first++; first++;