🔨 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:
Niels Lohmann 2017-02-27 22:10:57 +01:00
parent 54ef5f7b47
commit af070744ae
No known key found for this signature in database
GPG key ID: 7F3CEA63AE251B69
2 changed files with 185 additions and 251 deletions

View file

@ -6196,6 +6196,9 @@ class basic_json
/// @{ /// @{
private: private:
/*!
@brief wrapper around the serialization functions
*/
class serializer class serializer
{ {
public: public:
@ -6223,7 +6226,7 @@ class basic_json
void dump(const basic_json& val, void dump(const basic_json& val,
const bool pretty_print, const bool pretty_print,
const unsigned int indent_step, const unsigned int indent_step,
const unsigned int current_indent = 0) const const unsigned int current_indent = 0)
{ {
switch (val.m_type) switch (val.m_type)
{ {
@ -6377,19 +6380,19 @@ class basic_json
case value_t::number_integer: case value_t::number_integer:
{ {
o << numtostr(val.m_value.number_integer).c_str(); x_write(val.m_value.number_integer);
return; return;
} }
case value_t::number_unsigned: case value_t::number_unsigned:
{ {
o << numtostr(val.m_value.number_unsigned).c_str(); x_write(val.m_value.number_unsigned);
return; return;
} }
case value_t::number_float: case value_t::number_float:
{ {
o << numtostr(val.m_value.number_float).c_str(); x_write(val.m_value.number_float);
return; return;
} }
@ -6569,34 +6572,13 @@ class basic_json
return result; return result;
} }
/*!
@brief locale-independent serialization for built-in arithmetic types
*/
struct numtostr
{
public:
template<typename NumberType> template<typename NumberType>
numtostr(NumberType value) void x_write(NumberType x)
{
x_write(value, std::is_integral<NumberType>());
}
const char* c_str() const
{
return m_buf.data();
}
private:
/// a (hopefully) large enough character buffer
std::array < char, 64 > m_buf{{}};
template<typename NumberType>
void x_write(NumberType x, /*is_integral=*/std::true_type)
{ {
// special case for "0" // special case for "0"
if (x == 0) if (x == 0)
{ {
m_buf[0] = '0'; o.put('0');
return; return;
} }
@ -6622,30 +6604,30 @@ class basic_json
} }
std::reverse(m_buf.begin(), m_buf.begin() + i); std::reverse(m_buf.begin(), m_buf.begin() + i);
o.write(m_buf.data(), static_cast<std::streamsize>(i));
} }
template<typename NumberType> void x_write(number_float_t x)
void x_write(NumberType x, /*is_integral=*/std::false_type)
{ {
// special case for 0.0 and -0.0 // special case for 0.0 and -0.0
if (x == 0) if (x == 0)
{ {
size_t i = 0;
if (std::signbit(x)) if (std::signbit(x))
{ {
m_buf[i++] = '-'; o.write("-0.0", 4);
}
else
{
o.write("0.0", 3);
} }
m_buf[i++] = '0';
m_buf[i++] = '.';
m_buf[i] = '0';
return; return;
} }
// get number of digits for a text -> float -> text round-trip // get number of digits for a text -> float -> text round-trip
static constexpr auto d = std::numeric_limits<NumberType>::digits10; static constexpr auto d = std::numeric_limits<number_float_t>::digits10;
// the actual conversion // the actual conversion
const auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x); auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x);
// negative value indicates an error // negative value indicates an error
assert(written_bytes > 0); assert(written_bytes > 0);
@ -6666,6 +6648,7 @@ class basic_json
{ {
const auto end = std::remove(m_buf.begin(), m_buf.begin() + written_bytes, thousands_sep); const auto end = std::remove(m_buf.begin(), m_buf.begin() + written_bytes, thousands_sep);
std::fill(end, m_buf.end(), '\0'); std::fill(end, m_buf.end(), '\0');
written_bytes -= (m_buf.end() - end);
} }
// convert decimal point to '.' // convert decimal point to '.'
@ -6682,41 +6665,27 @@ class basic_json
} }
// determine if need to append ".0" // determine if need to append ".0"
size_t i = 0;
bool value_is_int_like = true; bool value_is_int_like = true;
for (i = 0; i < m_buf.size(); ++i) for (size_t i = 0; i < static_cast<size_t>(written_bytes); ++i)
{ {
// break when end of number is reached
if (m_buf[i] == '\0')
{
break;
}
// check if we find non-int character // check if we find non-int character
value_is_int_like = value_is_int_like and m_buf[i] != '.' and value_is_int_like = value_is_int_like and m_buf[i] != '.' and
m_buf[i] != 'e' and m_buf[i] != 'E'; m_buf[i] != 'e';
} }
o.write(m_buf.data(), static_cast<std::streamsize>(written_bytes));
if (value_is_int_like) if (value_is_int_like)
{ {
// there must be 2 bytes left for ".0" o.write(".0", 2);
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');
} }
} }
};
private: private:
std::ostream& o; std::ostream& o;
/// a (hopefully) large enough character buffer
std::array < char, 64 > m_buf{{}};
}; };
public: public:
@ -6754,7 +6723,6 @@ class basic_json
// do the actual serialization // do the actual serialization
serializer s(o); serializer s(o);
s.dump(j, pretty_print, static_cast<unsigned int>(indentation)); s.dump(j, pretty_print, static_cast<unsigned int>(indentation));
return o; return o;
} }

View file

@ -6226,7 +6226,7 @@ class basic_json
void dump(const basic_json& val, void dump(const basic_json& val,
const bool pretty_print, const bool pretty_print,
const unsigned int indent_step, const unsigned int indent_step,
const unsigned int current_indent = 0) const const unsigned int current_indent = 0)
{ {
switch (val.m_type) switch (val.m_type)
{ {
@ -6380,19 +6380,19 @@ class basic_json
case value_t::number_integer: case value_t::number_integer:
{ {
o << numtostr(val.m_value.number_integer).c_str(); x_write(val.m_value.number_integer);
return; return;
} }
case value_t::number_unsigned: case value_t::number_unsigned:
{ {
o << numtostr(val.m_value.number_unsigned).c_str(); x_write(val.m_value.number_unsigned);
return; return;
} }
case value_t::number_float: case value_t::number_float:
{ {
o << numtostr(val.m_value.number_float).c_str(); x_write(val.m_value.number_float);
return; return;
} }
@ -6572,34 +6572,13 @@ class basic_json
return result; return result;
} }
/*!
@brief locale-independent serialization for built-in arithmetic types
*/
struct numtostr
{
public:
template<typename NumberType> template<typename NumberType>
numtostr(NumberType value) void x_write(NumberType x)
{
x_write(value, std::is_integral<NumberType>());
}
const char* c_str() const
{
return m_buf.data();
}
private:
/// a (hopefully) large enough character buffer
std::array < char, 64 > m_buf{{}};
template<typename NumberType>
void x_write(NumberType x, /*is_integral=*/std::true_type)
{ {
// special case for "0" // special case for "0"
if (x == 0) if (x == 0)
{ {
m_buf[0] = '0'; o.put('0');
return; return;
} }
@ -6625,30 +6604,30 @@ class basic_json
} }
std::reverse(m_buf.begin(), m_buf.begin() + i); std::reverse(m_buf.begin(), m_buf.begin() + i);
o.write(m_buf.data(), static_cast<std::streamsize>(i));
} }
template<typename NumberType> void x_write(number_float_t x)
void x_write(NumberType x, /*is_integral=*/std::false_type)
{ {
// special case for 0.0 and -0.0 // special case for 0.0 and -0.0
if (x == 0) if (x == 0)
{ {
size_t i = 0;
if (std::signbit(x)) if (std::signbit(x))
{ {
m_buf[i++] = '-'; o.write("-0.0", 4);
}
else
{
o.write("0.0", 3);
} }
m_buf[i++] = '0';
m_buf[i++] = '.';
m_buf[i] = '0';
return; return;
} }
// get number of digits for a text -> float -> text round-trip // get number of digits for a text -> float -> text round-trip
static constexpr auto d = std::numeric_limits<NumberType>::digits10; static constexpr auto d = std::numeric_limits<number_float_t>::digits10;
// the actual conversion // the actual conversion
const auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x); auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x);
// negative value indicates an error // negative value indicates an error
assert(written_bytes > 0); assert(written_bytes > 0);
@ -6669,6 +6648,7 @@ class basic_json
{ {
const auto end = std::remove(m_buf.begin(), m_buf.begin() + written_bytes, thousands_sep); const auto end = std::remove(m_buf.begin(), m_buf.begin() + written_bytes, thousands_sep);
std::fill(end, m_buf.end(), '\0'); std::fill(end, m_buf.end(), '\0');
written_bytes -= (m_buf.end() - end);
} }
// convert decimal point to '.' // convert decimal point to '.'
@ -6685,41 +6665,27 @@ class basic_json
} }
// determine if need to append ".0" // determine if need to append ".0"
size_t i = 0;
bool value_is_int_like = true; bool value_is_int_like = true;
for (i = 0; i < m_buf.size(); ++i) for (size_t i = 0; i < static_cast<size_t>(written_bytes); ++i)
{ {
// break when end of number is reached
if (m_buf[i] == '\0')
{
break;
}
// check if we find non-int character // check if we find non-int character
value_is_int_like = value_is_int_like and m_buf[i] != '.' and value_is_int_like = value_is_int_like and m_buf[i] != '.' and
m_buf[i] != 'e' and m_buf[i] != 'E'; m_buf[i] != 'e';
} }
o.write(m_buf.data(), static_cast<std::streamsize>(written_bytes));
if (value_is_int_like) if (value_is_int_like)
{ {
// there must be 2 bytes left for ".0" o.write(".0", 2);
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');
} }
} }
};
private: private:
std::ostream& o; std::ostream& o;
/// a (hopefully) large enough character buffer
std::array < char, 64 > m_buf{{}};
}; };
public: public: