🔨 integrating numtostr into serializer class
By merging numtostr into serializer, we can write directly to the output stream. As a consequence, all stream calls are now unformatted.
This commit is contained in:
parent
54ef5f7b47
commit
af070744ae
2 changed files with 185 additions and 251 deletions
220
src/json.hpp
220
src/json.hpp
|
@ -6196,6 +6196,9 @@ class basic_json
|
|||
/// @{
|
||||
|
||||
private:
|
||||
/*!
|
||||
@brief wrapper around the serialization functions
|
||||
*/
|
||||
class serializer
|
||||
{
|
||||
public:
|
||||
|
@ -6223,7 +6226,7 @@ class basic_json
|
|||
void dump(const basic_json& val,
|
||||
const bool pretty_print,
|
||||
const unsigned int indent_step,
|
||||
const unsigned int current_indent = 0) const
|
||||
const unsigned int current_indent = 0)
|
||||
{
|
||||
switch (val.m_type)
|
||||
{
|
||||
|
@ -6377,19 +6380,19 @@ class basic_json
|
|||
|
||||
case value_t::number_integer:
|
||||
{
|
||||
o << numtostr(val.m_value.number_integer).c_str();
|
||||
x_write(val.m_value.number_integer);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::number_unsigned:
|
||||
{
|
||||
o << numtostr(val.m_value.number_unsigned).c_str();
|
||||
x_write(val.m_value.number_unsigned);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::number_float:
|
||||
{
|
||||
o << numtostr(val.m_value.number_float).c_str();
|
||||
x_write(val.m_value.number_float);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -6569,154 +6572,120 @@ class basic_json
|
|||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief locale-independent serialization for built-in arithmetic types
|
||||
*/
|
||||
struct numtostr
|
||||
template<typename NumberType>
|
||||
void x_write(NumberType x)
|
||||
{
|
||||
public:
|
||||
template<typename NumberType>
|
||||
numtostr(NumberType value)
|
||||
// special case for "0"
|
||||
if (x == 0)
|
||||
{
|
||||
x_write(value, std::is_integral<NumberType>());
|
||||
o.put('0');
|
||||
return;
|
||||
}
|
||||
|
||||
const char* c_str() const
|
||||
const bool is_negative = x < 0;
|
||||
size_t i = 0;
|
||||
|
||||
// spare 1 byte for '\0'
|
||||
while (x != 0 and i < m_buf.size() - 1)
|
||||
{
|
||||
return m_buf.data();
|
||||
const auto digit = std::labs(static_cast<long>(x % 10));
|
||||
m_buf[i++] = static_cast<char>('0' + digit);
|
||||
x /= 10;
|
||||
}
|
||||
|
||||
private:
|
||||
/// a (hopefully) large enough character buffer
|
||||
std::array < char, 64 > m_buf{{}};
|
||||
// make sure the number has been processed completely
|
||||
assert(x == 0);
|
||||
|
||||
template<typename NumberType>
|
||||
void x_write(NumberType x, /*is_integral=*/std::true_type)
|
||||
if (is_negative)
|
||||
{
|
||||
// special case for "0"
|
||||
if (x == 0)
|
||||
{
|
||||
m_buf[0] = '0';
|
||||
return;
|
||||
}
|
||||
|
||||
const bool is_negative = x < 0;
|
||||
size_t i = 0;
|
||||
|
||||
// spare 1 byte for '\0'
|
||||
while (x != 0 and i < m_buf.size() - 1)
|
||||
{
|
||||
const auto digit = std::labs(static_cast<long>(x % 10));
|
||||
m_buf[i++] = static_cast<char>('0' + digit);
|
||||
x /= 10;
|
||||
}
|
||||
|
||||
// make sure the number has been processed completely
|
||||
assert(x == 0);
|
||||
|
||||
if (is_negative)
|
||||
{
|
||||
// make sure there is capacity for the '-'
|
||||
assert(i < m_buf.size() - 2);
|
||||
m_buf[i++] = '-';
|
||||
}
|
||||
|
||||
std::reverse(m_buf.begin(), m_buf.begin() + i);
|
||||
// make sure there is capacity for the '-'
|
||||
assert(i < m_buf.size() - 2);
|
||||
m_buf[i++] = '-';
|
||||
}
|
||||
|
||||
template<typename NumberType>
|
||||
void x_write(NumberType x, /*is_integral=*/std::false_type)
|
||||
std::reverse(m_buf.begin(), m_buf.begin() + i);
|
||||
o.write(m_buf.data(), static_cast<std::streamsize>(i));
|
||||
}
|
||||
|
||||
void x_write(number_float_t x)
|
||||
{
|
||||
// special case for 0.0 and -0.0
|
||||
if (x == 0)
|
||||
{
|
||||
// special case for 0.0 and -0.0
|
||||
if (x == 0)
|
||||
if (std::signbit(x))
|
||||
{
|
||||
size_t i = 0;
|
||||
if (std::signbit(x))
|
||||
{
|
||||
m_buf[i++] = '-';
|
||||
}
|
||||
m_buf[i++] = '0';
|
||||
m_buf[i++] = '.';
|
||||
m_buf[i] = '0';
|
||||
return;
|
||||
o.write("-0.0", 4);
|
||||
}
|
||||
|
||||
// get number of digits for a text -> float -> text round-trip
|
||||
static constexpr auto d = std::numeric_limits<NumberType>::digits10;
|
||||
|
||||
// the actual conversion
|
||||
const auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x);
|
||||
|
||||
// negative value indicates an error
|
||||
assert(written_bytes > 0);
|
||||
// check if buffer was large enough
|
||||
assert(static_cast<size_t>(written_bytes) < m_buf.size());
|
||||
|
||||
// read information from locale
|
||||
const auto loc = localeconv();
|
||||
assert(loc != nullptr);
|
||||
const char thousands_sep = !loc->thousands_sep ? '\0'
|
||||
: loc->thousands_sep[0];
|
||||
|
||||
const char decimal_point = !loc->decimal_point ? '\0'
|
||||
: loc->decimal_point[0];
|
||||
|
||||
// erase thousands separator
|
||||
if (thousands_sep != '\0')
|
||||
else
|
||||
{
|
||||
const auto end = std::remove(m_buf.begin(), m_buf.begin() + written_bytes, thousands_sep);
|
||||
std::fill(end, m_buf.end(), '\0');
|
||||
o.write("0.0", 3);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// convert decimal point to '.'
|
||||
if (decimal_point != '\0' and decimal_point != '.')
|
||||
{
|
||||
for (auto& c : m_buf)
|
||||
{
|
||||
if (c == decimal_point)
|
||||
{
|
||||
c = '.';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// get number of digits for a text -> float -> text round-trip
|
||||
static constexpr auto d = std::numeric_limits<number_float_t>::digits10;
|
||||
|
||||
// determine if need to append ".0"
|
||||
size_t i = 0;
|
||||
bool value_is_int_like = true;
|
||||
for (i = 0; i < m_buf.size(); ++i)
|
||||
// the actual conversion
|
||||
auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x);
|
||||
|
||||
// negative value indicates an error
|
||||
assert(written_bytes > 0);
|
||||
// check if buffer was large enough
|
||||
assert(static_cast<size_t>(written_bytes) < m_buf.size());
|
||||
|
||||
// read information from locale
|
||||
const auto loc = localeconv();
|
||||
assert(loc != nullptr);
|
||||
const char thousands_sep = !loc->thousands_sep ? '\0'
|
||||
: loc->thousands_sep[0];
|
||||
|
||||
const char decimal_point = !loc->decimal_point ? '\0'
|
||||
: loc->decimal_point[0];
|
||||
|
||||
// erase thousands separator
|
||||
if (thousands_sep != '\0')
|
||||
{
|
||||
const auto end = std::remove(m_buf.begin(), m_buf.begin() + written_bytes, thousands_sep);
|
||||
std::fill(end, m_buf.end(), '\0');
|
||||
written_bytes -= (m_buf.end() - end);
|
||||
}
|
||||
|
||||
// convert decimal point to '.'
|
||||
if (decimal_point != '\0' and decimal_point != '.')
|
||||
{
|
||||
for (auto& c : m_buf)
|
||||
{
|
||||
// break when end of number is reached
|
||||
if (m_buf[i] == '\0')
|
||||
if (c == decimal_point)
|
||||
{
|
||||
c = '.';
|
||||
break;
|
||||
}
|
||||
|
||||
// check if we find non-int character
|
||||
value_is_int_like = value_is_int_like and m_buf[i] != '.' and
|
||||
m_buf[i] != 'e' and m_buf[i] != 'E';
|
||||
}
|
||||
|
||||
if (value_is_int_like)
|
||||
{
|
||||
// there must be 2 bytes left for ".0"
|
||||
assert((i + 2) < m_buf.size());
|
||||
// we write to the end of the number
|
||||
assert(m_buf[i] == '\0');
|
||||
assert(m_buf[i - 1] != '\0');
|
||||
|
||||
// add ".0"
|
||||
m_buf[i] = '.';
|
||||
m_buf[i + 1] = '0';
|
||||
|
||||
// the resulting string is properly terminated
|
||||
assert(m_buf[i + 2] == '\0');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// determine if need to append ".0"
|
||||
bool value_is_int_like = true;
|
||||
for (size_t i = 0; i < static_cast<size_t>(written_bytes); ++i)
|
||||
{
|
||||
// check if we find non-int character
|
||||
value_is_int_like = value_is_int_like and m_buf[i] != '.' and
|
||||
m_buf[i] != 'e';
|
||||
}
|
||||
|
||||
o.write(m_buf.data(), static_cast<std::streamsize>(written_bytes));
|
||||
|
||||
if (value_is_int_like)
|
||||
{
|
||||
o.write(".0", 2);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostream& o;
|
||||
|
||||
/// a (hopefully) large enough character buffer
|
||||
std::array < char, 64 > m_buf{{}};
|
||||
};
|
||||
|
||||
public:
|
||||
|
@ -6754,7 +6723,6 @@ class basic_json
|
|||
// do the actual serialization
|
||||
serializer s(o);
|
||||
s.dump(j, pretty_print, static_cast<unsigned int>(indentation));
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
|
|
|
@ -6226,7 +6226,7 @@ class basic_json
|
|||
void dump(const basic_json& val,
|
||||
const bool pretty_print,
|
||||
const unsigned int indent_step,
|
||||
const unsigned int current_indent = 0) const
|
||||
const unsigned int current_indent = 0)
|
||||
{
|
||||
switch (val.m_type)
|
||||
{
|
||||
|
@ -6380,19 +6380,19 @@ class basic_json
|
|||
|
||||
case value_t::number_integer:
|
||||
{
|
||||
o << numtostr(val.m_value.number_integer).c_str();
|
||||
x_write(val.m_value.number_integer);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::number_unsigned:
|
||||
{
|
||||
o << numtostr(val.m_value.number_unsigned).c_str();
|
||||
x_write(val.m_value.number_unsigned);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::number_float:
|
||||
{
|
||||
o << numtostr(val.m_value.number_float).c_str();
|
||||
x_write(val.m_value.number_float);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -6572,154 +6572,120 @@ class basic_json
|
|||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief locale-independent serialization for built-in arithmetic types
|
||||
*/
|
||||
struct numtostr
|
||||
template<typename NumberType>
|
||||
void x_write(NumberType x)
|
||||
{
|
||||
public:
|
||||
template<typename NumberType>
|
||||
numtostr(NumberType value)
|
||||
// special case for "0"
|
||||
if (x == 0)
|
||||
{
|
||||
x_write(value, std::is_integral<NumberType>());
|
||||
o.put('0');
|
||||
return;
|
||||
}
|
||||
|
||||
const char* c_str() const
|
||||
const bool is_negative = x < 0;
|
||||
size_t i = 0;
|
||||
|
||||
// spare 1 byte for '\0'
|
||||
while (x != 0 and i < m_buf.size() - 1)
|
||||
{
|
||||
return m_buf.data();
|
||||
const auto digit = std::labs(static_cast<long>(x % 10));
|
||||
m_buf[i++] = static_cast<char>('0' + digit);
|
||||
x /= 10;
|
||||
}
|
||||
|
||||
private:
|
||||
/// a (hopefully) large enough character buffer
|
||||
std::array < char, 64 > m_buf{{}};
|
||||
// make sure the number has been processed completely
|
||||
assert(x == 0);
|
||||
|
||||
template<typename NumberType>
|
||||
void x_write(NumberType x, /*is_integral=*/std::true_type)
|
||||
if (is_negative)
|
||||
{
|
||||
// special case for "0"
|
||||
if (x == 0)
|
||||
{
|
||||
m_buf[0] = '0';
|
||||
return;
|
||||
}
|
||||
|
||||
const bool is_negative = x < 0;
|
||||
size_t i = 0;
|
||||
|
||||
// spare 1 byte for '\0'
|
||||
while (x != 0 and i < m_buf.size() - 1)
|
||||
{
|
||||
const auto digit = std::labs(static_cast<long>(x % 10));
|
||||
m_buf[i++] = static_cast<char>('0' + digit);
|
||||
x /= 10;
|
||||
}
|
||||
|
||||
// make sure the number has been processed completely
|
||||
assert(x == 0);
|
||||
|
||||
if (is_negative)
|
||||
{
|
||||
// make sure there is capacity for the '-'
|
||||
assert(i < m_buf.size() - 2);
|
||||
m_buf[i++] = '-';
|
||||
}
|
||||
|
||||
std::reverse(m_buf.begin(), m_buf.begin() + i);
|
||||
// make sure there is capacity for the '-'
|
||||
assert(i < m_buf.size() - 2);
|
||||
m_buf[i++] = '-';
|
||||
}
|
||||
|
||||
template<typename NumberType>
|
||||
void x_write(NumberType x, /*is_integral=*/std::false_type)
|
||||
std::reverse(m_buf.begin(), m_buf.begin() + i);
|
||||
o.write(m_buf.data(), static_cast<std::streamsize>(i));
|
||||
}
|
||||
|
||||
void x_write(number_float_t x)
|
||||
{
|
||||
// special case for 0.0 and -0.0
|
||||
if (x == 0)
|
||||
{
|
||||
// special case for 0.0 and -0.0
|
||||
if (x == 0)
|
||||
if (std::signbit(x))
|
||||
{
|
||||
size_t i = 0;
|
||||
if (std::signbit(x))
|
||||
{
|
||||
m_buf[i++] = '-';
|
||||
}
|
||||
m_buf[i++] = '0';
|
||||
m_buf[i++] = '.';
|
||||
m_buf[i] = '0';
|
||||
return;
|
||||
o.write("-0.0", 4);
|
||||
}
|
||||
|
||||
// get number of digits for a text -> float -> text round-trip
|
||||
static constexpr auto d = std::numeric_limits<NumberType>::digits10;
|
||||
|
||||
// the actual conversion
|
||||
const auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x);
|
||||
|
||||
// negative value indicates an error
|
||||
assert(written_bytes > 0);
|
||||
// check if buffer was large enough
|
||||
assert(static_cast<size_t>(written_bytes) < m_buf.size());
|
||||
|
||||
// read information from locale
|
||||
const auto loc = localeconv();
|
||||
assert(loc != nullptr);
|
||||
const char thousands_sep = !loc->thousands_sep ? '\0'
|
||||
: loc->thousands_sep[0];
|
||||
|
||||
const char decimal_point = !loc->decimal_point ? '\0'
|
||||
: loc->decimal_point[0];
|
||||
|
||||
// erase thousands separator
|
||||
if (thousands_sep != '\0')
|
||||
else
|
||||
{
|
||||
const auto end = std::remove(m_buf.begin(), m_buf.begin() + written_bytes, thousands_sep);
|
||||
std::fill(end, m_buf.end(), '\0');
|
||||
o.write("0.0", 3);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// convert decimal point to '.'
|
||||
if (decimal_point != '\0' and decimal_point != '.')
|
||||
{
|
||||
for (auto& c : m_buf)
|
||||
{
|
||||
if (c == decimal_point)
|
||||
{
|
||||
c = '.';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// get number of digits for a text -> float -> text round-trip
|
||||
static constexpr auto d = std::numeric_limits<number_float_t>::digits10;
|
||||
|
||||
// determine if need to append ".0"
|
||||
size_t i = 0;
|
||||
bool value_is_int_like = true;
|
||||
for (i = 0; i < m_buf.size(); ++i)
|
||||
// the actual conversion
|
||||
auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x);
|
||||
|
||||
// negative value indicates an error
|
||||
assert(written_bytes > 0);
|
||||
// check if buffer was large enough
|
||||
assert(static_cast<size_t>(written_bytes) < m_buf.size());
|
||||
|
||||
// read information from locale
|
||||
const auto loc = localeconv();
|
||||
assert(loc != nullptr);
|
||||
const char thousands_sep = !loc->thousands_sep ? '\0'
|
||||
: loc->thousands_sep[0];
|
||||
|
||||
const char decimal_point = !loc->decimal_point ? '\0'
|
||||
: loc->decimal_point[0];
|
||||
|
||||
// erase thousands separator
|
||||
if (thousands_sep != '\0')
|
||||
{
|
||||
const auto end = std::remove(m_buf.begin(), m_buf.begin() + written_bytes, thousands_sep);
|
||||
std::fill(end, m_buf.end(), '\0');
|
||||
written_bytes -= (m_buf.end() - end);
|
||||
}
|
||||
|
||||
// convert decimal point to '.'
|
||||
if (decimal_point != '\0' and decimal_point != '.')
|
||||
{
|
||||
for (auto& c : m_buf)
|
||||
{
|
||||
// break when end of number is reached
|
||||
if (m_buf[i] == '\0')
|
||||
if (c == decimal_point)
|
||||
{
|
||||
c = '.';
|
||||
break;
|
||||
}
|
||||
|
||||
// check if we find non-int character
|
||||
value_is_int_like = value_is_int_like and m_buf[i] != '.' and
|
||||
m_buf[i] != 'e' and m_buf[i] != 'E';
|
||||
}
|
||||
|
||||
if (value_is_int_like)
|
||||
{
|
||||
// there must be 2 bytes left for ".0"
|
||||
assert((i + 2) < m_buf.size());
|
||||
// we write to the end of the number
|
||||
assert(m_buf[i] == '\0');
|
||||
assert(m_buf[i - 1] != '\0');
|
||||
|
||||
// add ".0"
|
||||
m_buf[i] = '.';
|
||||
m_buf[i + 1] = '0';
|
||||
|
||||
// the resulting string is properly terminated
|
||||
assert(m_buf[i + 2] == '\0');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// determine if need to append ".0"
|
||||
bool value_is_int_like = true;
|
||||
for (size_t i = 0; i < static_cast<size_t>(written_bytes); ++i)
|
||||
{
|
||||
// check if we find non-int character
|
||||
value_is_int_like = value_is_int_like and m_buf[i] != '.' and
|
||||
m_buf[i] != 'e';
|
||||
}
|
||||
|
||||
o.write(m_buf.data(), static_cast<std::streamsize>(written_bytes));
|
||||
|
||||
if (value_is_int_like)
|
||||
{
|
||||
o.write(".0", 2);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostream& o;
|
||||
|
||||
/// a (hopefully) large enough character buffer
|
||||
std::array < char, 64 > m_buf{{}};
|
||||
};
|
||||
|
||||
public:
|
||||
|
|
Loading…
Reference in a new issue