💄 cleanup

This commit is contained in:
Niels Lohmann 2017-02-11 15:55:53 +01:00
parent 926af6d50b
commit 9f5dccbab5
No known key found for this signature in database
GPG key ID: 7F3CEA63AE251B69
2 changed files with 128 additions and 164 deletions

View file

@ -10840,13 +10840,13 @@ basic_json_parser_66:
/*! /*!
@brief parse string into a built-in arithmetic type as if @brief parse string into a built-in arithmetic type as if the current
the current locale is POSIX. locale is POSIX.
note: in floating-point case strtod may parse @note in floating-point case strtod may parse past the token's end -
past the token's end - this is not an error. this is not an error
any leading blanks are not handled. @note any leading blanks are not handled
*/ */
struct strtonum struct strtonum
{ {
@ -10855,36 +10855,37 @@ basic_json_parser_66:
: m_start(start), m_end(end) : m_start(start), m_end(end)
{} {}
/// return true iff parsed successfully as /*!
/// number of type T. @return true iff parsed successfully as number of type T
///
/// @val shall contain parsed value, or @param[in,out] val shall contain parsed value, or undefined value
/// undefined value if could not parse. if could not parse
template<typename T, */
typename = typename std::enable_if< template<typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value>::type>
std::is_arithmetic<T>::value>::type >
bool to(T& val) const bool to(T& val) const
{ {
return parse(val, std::is_integral<T>()); return parse(val, std::is_integral<T>());
} }
/// return true iff token matches ^[+-]\d+$ /*!
/// This is a helper to determine whether to parse the token into
/// this is a helper to determine whether to floating-point or integral type.
/// parse the token into floating-point or
/// integral type. We wouldn't need it if @note We wouldn't need it if we had separate token types for
/// we had separate token types for integral integral and floating-point cases.
/// and floating-point cases.
@return true iff token matches `^[+-]\d+$`
*/
bool is_integral() const bool is_integral() const
{ {
const char* p = m_start; const char* p = m_start;
if (!p) if (p == nullptr)
{ {
return false; // LCOV_EXCL_LINE return false; // LCOV_EXCL_LINE
} }
if (*p == '-' or * p == '+') if ((*p == '-') or (*p == '+'))
{ {
++p; ++p;
} }
@ -10894,39 +10895,31 @@ basic_json_parser_66:
return false; // LCOV_EXCL_LINE return false; // LCOV_EXCL_LINE
} }
while (p < m_end and* p >= '0' while ((p < m_end) and (*p >= '0') and (*p <= '9'))
and * p <= '9')
{ {
++p; ++p;
} }
return p == m_end; return (p == m_end);
} }
private: private:
const char* const m_start = nullptr; const char* const m_start = nullptr;
const char* const m_end = nullptr; const char* const m_end = nullptr;
// overloaded wrappers for strtod/strtof/strtold // overloaded wrappers for strtod/strtof/strtold
// that will be called from parse<floating_point_t> // that will be called from parse<floating_point_t>
static void strtof(float& f, const char* str, char** endptr)
static void strtof(float& f,
const char* str,
char** endptr)
{ {
f = std::strtof(str, endptr); f = std::strtof(str, endptr);
} }
static void strtof(double& f, static void strtof(double& f, const char* str, char** endptr)
const char* str,
char** endptr)
{ {
f = std::strtod(str, endptr); f = std::strtod(str, endptr);
} }
static void strtof(long double& f, static void strtof(long double& f, const char* str, char** endptr)
const char* str,
char** endptr)
{ {
f = std::strtold(str, endptr); f = std::strtold(str, endptr);
} }
@ -10934,37 +10927,32 @@ basic_json_parser_66:
template<typename T> template<typename T>
bool parse(T& value, /*is_integral=*/std::false_type) const bool parse(T& value, /*is_integral=*/std::false_type) const
{ {
// replace decimal separator with locale-specific // replace decimal separator with locale-specific version,
// version, when necessary; data will point to // when necessary; data will point to either the original
// either the original string, or buf, or tempstr // string, or buf, or tempstr containing the fixed string.
// containing the fixed string.
std::string tempstr; std::string tempstr;
std::array<char, 64> buf; std::array<char, 64> buf;
const size_t len = static_cast<size_t>(m_end - m_start); const size_t len = static_cast<size_t>(m_end - m_start);
// Since dealing with strtod family of functions, // since dealing with strtod family of functions, we're
// we're getting the decimal point char from the // getting the decimal point char from the C locale facilities
// C locale facilities instead of C++'s numpunct // instead of C++'s numpunct facet of the current std::locale
// facet of the current std::locale;
const auto loc = localeconv(); const auto loc = localeconv();
assert(loc != nullptr); assert(loc != nullptr);
const char decimal_point_char = !loc->decimal_point ? '.' const char decimal_point_char = (loc->decimal_point == nullptr) ? '.' : loc->decimal_point[0];
: loc->decimal_point[0];
const char* data = m_start; const char* data = m_start;
if (decimal_point_char != '.') if (decimal_point_char != '.')
{ {
const size_t ds_pos = static_cast<size_t>( const size_t ds_pos = static_cast<size_t>(std::find(m_start, m_end, '.') - m_start);
std::find(m_start, m_end, '.') - m_start );
if (ds_pos != len) if (ds_pos != len)
{ {
// copy the data into the local buffer or // copy the data into the local buffer or tempstr, if
// tempstr, if buffer is too small; // buffer is too small; replace decimal separator, and
// replace decimal separator, and update // update data to point to the modified bytes
// data to point to the modified bytes if ((len + 1) < buf.size())
if (len + 1 < buf.size())
{ {
std::copy(m_start, m_end, buf.data()); std::copy(m_start, m_end, buf.data());
buf[len] = 0; buf[len] = 0;
@ -10985,13 +10973,12 @@ basic_json_parser_66:
// this calls appropriate overload depending on T // this calls appropriate overload depending on T
strtof(value, data, &endptr); strtof(value, data, &endptr);
// note that reading past the end is OK, the data may be, // note that reading past the end is OK, the data may be, for
// for example, "123.", where the parsed token only // example, "123.", where the parsed token only contains
// contains "123", but strtod will read the dot as well. // "123", but strtod will read the dot as well.
const bool ok = endptr >= data + len const bool ok = (endptr >= (data + len)) and (len > 0);
and len > 0;
if (ok and value == 0.0 and * data == '-') if (ok and (value == 0.0) and (*data == '-'))
{ {
// some implementations forget to negate the zero // some implementations forget to negate the zero
value = -0.0; value = -0.0;
@ -11000,16 +10987,12 @@ basic_json_parser_66:
return ok; return ok;
} }
signed long long parse_integral( signed long long parse_integral(char** endptr, /*is_signed*/std::true_type) const
char** endptr,
/*is_signed*/std::true_type) const
{ {
return std::strtoll(m_start, endptr, 10); return std::strtoll(m_start, endptr, 10);
} }
unsigned long long parse_integral( unsigned long long parse_integral(char** endptr, /*is_signed*/std::false_type) const
char** endptr,
/*is_signed*/std::false_type) const
{ {
return std::strtoull(m_start, endptr, 10); return std::strtoull(m_start, endptr, 10);
} }
@ -11018,20 +11001,20 @@ basic_json_parser_66:
bool parse(T& value, /*is_integral=*/std::true_type) const bool parse(T& value, /*is_integral=*/std::true_type) const
{ {
char* endptr = nullptr; char* endptr = nullptr;
errno = 0; // these are thread-local errno = 0; // these are thread-local
const auto x = parse_integral(&endptr, std::is_signed<T>()); const auto x = parse_integral(&endptr, std::is_signed<T>());
static_assert(std::is_signed<T>() // called right overload? // called right overload?
== std::is_signed<decltype(x)>(), ""); static_assert(std::is_signed<T>() == std::is_signed<decltype(x)>(), "");
value = static_cast<T>(x); value = static_cast<T>(x);
return x == static_cast<decltype(x)>(value) // x fits into destination T return (x == static_cast<decltype(x)>(value)) // x fits into destination T
and (x < 0) == (value < 0) // preserved sign and (x < 0) == (value < 0) // preserved sign
and (x != 0 or is_integral()) // strto[u]ll did nto fail and ((x != 0) or is_integral()) // strto[u]ll did nto fail
and errno == 0 // strto[u]ll did not overflow and (errno == 0) // strto[u]ll did not overflow
and m_start < m_end // token was not empty and (m_start < m_end) // token was not empty
and endptr == m_end; // parsed entire token exactly and (endptr == m_end); // parsed entire token exactly
} }
}; };
@ -11061,20 +11044,20 @@ basic_json_parser_66:
strtonum num(reinterpret_cast<const char*>(m_start), strtonum num(reinterpret_cast<const char*>(m_start),
reinterpret_cast<const char*>(m_cursor)); reinterpret_cast<const char*>(m_cursor));
const bool is_negative = *m_start == '-'; const bool is_negative = (*m_start == '-');
result.m_type = value_t::discarded; result.m_type = value_t::discarded;
if (not num.is_integral()) if (not num.is_integral())
{ {
; // will parse as float below // will parse as float below
} }
else if (is_negative) else if (is_negative)
{ {
number_integer_t val{0}; number_integer_t val{0};
if (num.to(val)) if (num.to(val))
{ {
result.m_type = value_t::number_integer; result.m_type = value_t::number_integer;
result.m_value = val; result.m_value = val;
} }
} }
@ -11089,14 +11072,13 @@ basic_json_parser_66:
} }
number_float_t val{0}; number_float_t val{0};
if (result.m_type != value_t::discarded if (result.m_type != value_t::discarded or (not num.to(val)))
or !num.to(val))
{ {
return; // already have a value from above // already have a value from above or couldn't parse as float_t
// or couldn't parse as float_t return;
} }
result.m_type = value_t::number_float; result.m_type = value_t::number_float;
result.m_value = val; result.m_value = val;
// replace infinity and NAN by null // replace infinity and NAN by null

View file

@ -9990,13 +9990,13 @@ class basic_json
/*! /*!
@brief parse string into a built-in arithmetic type as if @brief parse string into a built-in arithmetic type as if the current
the current locale is POSIX. locale is POSIX.
note: in floating-point case strtod may parse @note in floating-point case strtod may parse past the token's end -
past the token's end - this is not an error. this is not an error
any leading blanks are not handled. @note any leading blanks are not handled
*/ */
struct strtonum struct strtonum
{ {
@ -10005,36 +10005,37 @@ class basic_json
: m_start(start), m_end(end) : m_start(start), m_end(end)
{} {}
/// return true iff parsed successfully as /*!
/// number of type T. @return true iff parsed successfully as number of type T
///
/// @val shall contain parsed value, or @param[in,out] val shall contain parsed value, or undefined value
/// undefined value if could not parse. if could not parse
template<typename T, */
typename = typename std::enable_if< template<typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value>::type>
std::is_arithmetic<T>::value>::type >
bool to(T& val) const bool to(T& val) const
{ {
return parse(val, std::is_integral<T>()); return parse(val, std::is_integral<T>());
} }
/// return true iff token matches ^[+-]\d+$ /*!
/// This is a helper to determine whether to parse the token into
/// this is a helper to determine whether to floating-point or integral type.
/// parse the token into floating-point or
/// integral type. We wouldn't need it if @note We wouldn't need it if we had separate token types for
/// we had separate token types for integral integral and floating-point cases.
/// and floating-point cases.
@return true iff token matches `^[+-]\d+$`
*/
bool is_integral() const bool is_integral() const
{ {
const char* p = m_start; const char* p = m_start;
if (!p) if (p == nullptr)
{ {
return false; // LCOV_EXCL_LINE return false; // LCOV_EXCL_LINE
} }
if (*p == '-' or * p == '+') if ((*p == '-') or (*p == '+'))
{ {
++p; ++p;
} }
@ -10044,39 +10045,31 @@ class basic_json
return false; // LCOV_EXCL_LINE return false; // LCOV_EXCL_LINE
} }
while (p < m_end and* p >= '0' while ((p < m_end) and (*p >= '0') and (*p <= '9'))
and * p <= '9')
{ {
++p; ++p;
} }
return p == m_end; return (p == m_end);
} }
private: private:
const char* const m_start = nullptr; const char* const m_start = nullptr;
const char* const m_end = nullptr; const char* const m_end = nullptr;
// overloaded wrappers for strtod/strtof/strtold // overloaded wrappers for strtod/strtof/strtold
// that will be called from parse<floating_point_t> // that will be called from parse<floating_point_t>
static void strtof(float& f, const char* str, char** endptr)
static void strtof(float& f,
const char* str,
char** endptr)
{ {
f = std::strtof(str, endptr); f = std::strtof(str, endptr);
} }
static void strtof(double& f, static void strtof(double& f, const char* str, char** endptr)
const char* str,
char** endptr)
{ {
f = std::strtod(str, endptr); f = std::strtod(str, endptr);
} }
static void strtof(long double& f, static void strtof(long double& f, const char* str, char** endptr)
const char* str,
char** endptr)
{ {
f = std::strtold(str, endptr); f = std::strtold(str, endptr);
} }
@ -10084,37 +10077,32 @@ class basic_json
template<typename T> template<typename T>
bool parse(T& value, /*is_integral=*/std::false_type) const bool parse(T& value, /*is_integral=*/std::false_type) const
{ {
// replace decimal separator with locale-specific // replace decimal separator with locale-specific version,
// version, when necessary; data will point to // when necessary; data will point to either the original
// either the original string, or buf, or tempstr // string, or buf, or tempstr containing the fixed string.
// containing the fixed string.
std::string tempstr; std::string tempstr;
std::array<char, 64> buf; std::array<char, 64> buf;
const size_t len = static_cast<size_t>(m_end - m_start); const size_t len = static_cast<size_t>(m_end - m_start);
// Since dealing with strtod family of functions, // since dealing with strtod family of functions, we're
// we're getting the decimal point char from the // getting the decimal point char from the C locale facilities
// C locale facilities instead of C++'s numpunct // instead of C++'s numpunct facet of the current std::locale
// facet of the current std::locale;
const auto loc = localeconv(); const auto loc = localeconv();
assert(loc != nullptr); assert(loc != nullptr);
const char decimal_point_char = !loc->decimal_point ? '.' const char decimal_point_char = (loc->decimal_point == nullptr) ? '.' : loc->decimal_point[0];
: loc->decimal_point[0];
const char* data = m_start; const char* data = m_start;
if (decimal_point_char != '.') if (decimal_point_char != '.')
{ {
const size_t ds_pos = static_cast<size_t>( const size_t ds_pos = static_cast<size_t>(std::find(m_start, m_end, '.') - m_start);
std::find(m_start, m_end, '.') - m_start );
if (ds_pos != len) if (ds_pos != len)
{ {
// copy the data into the local buffer or // copy the data into the local buffer or tempstr, if
// tempstr, if buffer is too small; // buffer is too small; replace decimal separator, and
// replace decimal separator, and update // update data to point to the modified bytes
// data to point to the modified bytes if ((len + 1) < buf.size())
if (len + 1 < buf.size())
{ {
std::copy(m_start, m_end, buf.data()); std::copy(m_start, m_end, buf.data());
buf[len] = 0; buf[len] = 0;
@ -10135,13 +10123,12 @@ class basic_json
// this calls appropriate overload depending on T // this calls appropriate overload depending on T
strtof(value, data, &endptr); strtof(value, data, &endptr);
// note that reading past the end is OK, the data may be, // note that reading past the end is OK, the data may be, for
// for example, "123.", where the parsed token only // example, "123.", where the parsed token only contains
// contains "123", but strtod will read the dot as well. // "123", but strtod will read the dot as well.
const bool ok = endptr >= data + len const bool ok = (endptr >= (data + len)) and (len > 0);
and len > 0;
if (ok and value == 0.0 and * data == '-') if (ok and (value == 0.0) and (*data == '-'))
{ {
// some implementations forget to negate the zero // some implementations forget to negate the zero
value = -0.0; value = -0.0;
@ -10150,16 +10137,12 @@ class basic_json
return ok; return ok;
} }
signed long long parse_integral( signed long long parse_integral(char** endptr, /*is_signed*/std::true_type) const
char** endptr,
/*is_signed*/std::true_type) const
{ {
return std::strtoll(m_start, endptr, 10); return std::strtoll(m_start, endptr, 10);
} }
unsigned long long parse_integral( unsigned long long parse_integral(char** endptr, /*is_signed*/std::false_type) const
char** endptr,
/*is_signed*/std::false_type) const
{ {
return std::strtoull(m_start, endptr, 10); return std::strtoull(m_start, endptr, 10);
} }
@ -10168,20 +10151,20 @@ class basic_json
bool parse(T& value, /*is_integral=*/std::true_type) const bool parse(T& value, /*is_integral=*/std::true_type) const
{ {
char* endptr = nullptr; char* endptr = nullptr;
errno = 0; // these are thread-local errno = 0; // these are thread-local
const auto x = parse_integral(&endptr, std::is_signed<T>()); const auto x = parse_integral(&endptr, std::is_signed<T>());
static_assert(std::is_signed<T>() // called right overload? // called right overload?
== std::is_signed<decltype(x)>(), ""); static_assert(std::is_signed<T>() == std::is_signed<decltype(x)>(), "");
value = static_cast<T>(x); value = static_cast<T>(x);
return x == static_cast<decltype(x)>(value) // x fits into destination T return (x == static_cast<decltype(x)>(value)) // x fits into destination T
and (x < 0) == (value < 0) // preserved sign and (x < 0) == (value < 0) // preserved sign
and (x != 0 or is_integral()) // strto[u]ll did nto fail and ((x != 0) or is_integral()) // strto[u]ll did nto fail
and errno == 0 // strto[u]ll did not overflow and (errno == 0) // strto[u]ll did not overflow
and m_start < m_end // token was not empty and (m_start < m_end) // token was not empty
and endptr == m_end; // parsed entire token exactly and (endptr == m_end); // parsed entire token exactly
} }
}; };
@ -10211,20 +10194,20 @@ class basic_json
strtonum num(reinterpret_cast<const char*>(m_start), strtonum num(reinterpret_cast<const char*>(m_start),
reinterpret_cast<const char*>(m_cursor)); reinterpret_cast<const char*>(m_cursor));
const bool is_negative = *m_start == '-'; const bool is_negative = (*m_start == '-');
result.m_type = value_t::discarded; result.m_type = value_t::discarded;
if (not num.is_integral()) if (not num.is_integral())
{ {
; // will parse as float below // will parse as float below
} }
else if (is_negative) else if (is_negative)
{ {
number_integer_t val{0}; number_integer_t val{0};
if (num.to(val)) if (num.to(val))
{ {
result.m_type = value_t::number_integer; result.m_type = value_t::number_integer;
result.m_value = val; result.m_value = val;
} }
} }
@ -10239,14 +10222,13 @@ class basic_json
} }
number_float_t val{0}; number_float_t val{0};
if (result.m_type != value_t::discarded if (result.m_type != value_t::discarded or (not num.to(val)))
or !num.to(val))
{ {
return; // already have a value from above // already have a value from above or couldn't parse as float_t
// or couldn't parse as float_t return;
} }
result.m_type = value_t::number_float; result.m_type = value_t::number_float;
result.m_value = val; result.m_value = val;
// replace infinity and NAN by null // replace infinity and NAN by null