Update json from upstream version 3.1.2.

This adds support for ubjson and makes a number of bugfixes.

Binary input and output have switched from strings to uint8_t arrays.
This commit is contained in:
Peter Johnson
2018-05-14 22:53:22 -07:00
parent c274d1790f
commit f8ed48af98
20 changed files with 8915 additions and 6854 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*----------------------------------------------------------------------------*/
/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
/* Modifications Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
@@ -7,11 +7,11 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++
| | |__ | | | | | | version 2.1.1
| | |__ | | | | | | version 3.1.2
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -34,32 +34,50 @@ SOFTWARE.
#define WPI_JSON_IMPLEMENTATION
#include "wpi/json.h"
#include <algorithm>
#include <numeric> // accumulate
using namespace wpi;
#include "wpi/SmallString.h"
std::string json::json_pointer::to_string() const noexcept
namespace wpi {
std::string json_pointer::to_string() const noexcept
{
return std::accumulate(reference_tokens.begin(),
reference_tokens.end(), std::string{},
return std::accumulate(reference_tokens.begin(), reference_tokens.end(),
std::string{},
[](const std::string & a, const std::string & b)
{
return a + "/" + escape(b);
});
}
json::reference json::json_pointer::get_and_create(reference j) const
int json_pointer::array_index(const Twine& s)
{
pointer result = &j;
SmallString<128> buf;
StringRef str = s.toNullTerminatedStringRef(buf);
std::size_t processed_chars = 0;
const int res = std::stoi(str, &processed_chars);
// in case no reference tokens exist, return a reference to the
// JSON value j which will be overwritten by a primitive value
// check if the string was completely read
if (JSON_UNLIKELY(processed_chars != str.size()))
{
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + Twine(s) + "'"));
}
return res;
}
json& json_pointer::get_and_create(json& j) const
{
using size_type = typename json::size_type;
auto result = &j;
// in case no reference tokens exist, return a reference to the JSON value
// j which will be overwritten by a primitive value
for (const auto& reference_token : reference_tokens)
{
switch (result->m_type)
{
case value_t::null:
case detail::value_t::null:
{
if (reference_token == "0")
{
@@ -74,86 +92,80 @@ json::reference json::json_pointer::get_and_create(reference j) const
break;
}
case value_t::object:
case detail::value_t::object:
{
// create an entry in the object
result = &result->operator[](reference_token);
break;
}
case value_t::array:
case detail::value_t::array:
{
// create an entry in the array
JSON_TRY
{
result = &result->operator[](static_cast<size_type>(std::stoi(reference_token)));
result = &result->operator[](static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH (std::invalid_argument&)
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + Twine(reference_token) + "' is not a number"));
}
break;
}
/*
The following code is only reached if there exists a
reference token _and_ the current value is primitive. In
this case, we have an error situation, because primitive
values may only occur as single value; that is, with an
empty list of reference tokens.
The following code is only reached if there exists a reference
token _and_ the current value is primitive. In this case, we have
an error situation, because primitive values may only occur as
single value; that is, with an empty list of reference tokens.
*/
default:
{
JSON_THROW(type_error::create(313, "invalid value to unflatten"));
}
JSON_THROW(detail::type_error::create(313, "invalid value to unflatten"));
}
}
return *result;
}
json::reference json::json_pointer::get_unchecked(pointer ptr) const
json& json_pointer::get_unchecked(json* ptr) const
{
using size_type = typename json::size_type;
for (const auto& reference_token : reference_tokens)
{
// convert null values to arrays or objects before continuing
if (ptr->m_type == value_t::null)
if (ptr->m_type == detail::value_t::null)
{
// check if reference token is a number
const bool nums = std::all_of(reference_token.begin(),
reference_token.end(),
[](const char x)
const bool nums =
std::all_of(reference_token.begin(), reference_token.end(),
[](const char x)
{
return (x >= '0' && x <= '9');
return (x >= '0' and x <= '9');
});
// change value to array for numbers or "-" or to object
// otherwise
if (nums || reference_token == "-")
{
*ptr = value_t::array;
}
else
{
*ptr = value_t::object;
}
// change value to array for numbers or "-" or to object otherwise
*ptr = (nums or reference_token == "-")
? detail::value_t::array
: detail::value_t::object;
}
switch (ptr->m_type)
{
case value_t::object:
case detail::value_t::object:
{
// use unchecked object access
ptr = &ptr->operator[](reference_token);
break;
}
case value_t::array:
case detail::value_t::array:
{
// error condition (cf. RFC 6901, Sect. 4)
if (reference_token.size() > 1 && reference_token[0] == '0')
if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
{
JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
JSON_THROW(detail::parse_error::create(106, 0,
"array index '" + Twine(reference_token) +
"' must not begin with '0'"));
}
if (reference_token == "-")
@@ -166,193 +178,200 @@ json::reference json::json_pointer::get_unchecked(pointer ptr) const
// convert array index to number; unchecked access
JSON_TRY
{
ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
ptr = &ptr->operator[](
static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH (std::invalid_argument&)
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + Twine(reference_token) + "' is not a number"));
}
}
break;
}
default:
{
JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
}
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + Twine(reference_token) + "'"));
}
}
return *ptr;
}
json::reference json::json_pointer::get_checked(pointer ptr) const
json& json_pointer::get_checked(json* ptr) const
{
using size_type = typename json::size_type;
for (const auto& reference_token : reference_tokens)
{
switch (ptr->m_type)
{
case value_t::object:
case detail::value_t::object:
{
// note: at performs range check
ptr = &ptr->at(reference_token);
break;
}
case value_t::array:
case detail::value_t::array:
{
if (reference_token == "-")
if (JSON_UNLIKELY(reference_token == "-"))
{
// "-" always fails the range check
JSON_THROW(out_of_range::create(402, "array index '-' (" +
std::to_string(ptr->m_value.array->size()) +
") is out of range"));
JSON_THROW(detail::out_of_range::create(402,
"array index '-' (" + Twine(ptr->m_value.array->size()) +
") is out of range"));
}
// error condition (cf. RFC 6901, Sect. 4)
if (reference_token.size() > 1 && reference_token[0] == '0')
if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
{
JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
JSON_THROW(detail::parse_error::create(106, 0,
"array index '" + Twine(reference_token) +
"' must not begin with '0'"));
}
// note: at performs range check
JSON_TRY
{
ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token)));
ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH (std::invalid_argument&)
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + Twine(reference_token) + "' is not a number"));
}
break;
}
default:
{
JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
}
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + Twine(reference_token) + "'"));
}
}
return *ptr;
}
json::const_reference json::json_pointer::get_unchecked(const_pointer ptr) const
const json& json_pointer::get_unchecked(const json* ptr) const
{
using size_type = typename json::size_type;
for (const auto& reference_token : reference_tokens)
{
switch (ptr->m_type)
{
case value_t::object:
case detail::value_t::object:
{
// use unchecked object access
ptr = &ptr->operator[](reference_token);
break;
}
case value_t::array:
case detail::value_t::array:
{
if (reference_token == "-")
if (JSON_UNLIKELY(reference_token == "-"))
{
// "-" cannot be used for const access
JSON_THROW(out_of_range::create(402, "array index '-' (" +
std::to_string(ptr->m_value.array->size()) +
") is out of range"));
JSON_THROW(detail::out_of_range::create(402,
"array index '-' (" + Twine(ptr->m_value.array->size()) +
") is out of range"));
}
// error condition (cf. RFC 6901, Sect. 4)
if (reference_token.size() > 1 && reference_token[0] == '0')
if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
{
JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
JSON_THROW(detail::parse_error::create(106, 0,
"array index '" + Twine(reference_token) +
"' must not begin with '0'"));
}
// use unchecked array access
JSON_TRY
{
ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
ptr = &ptr->operator[](
static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH (std::invalid_argument&)
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + Twine(reference_token) + "' is not a number"));
}
break;
}
default:
{
JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
}
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + Twine(reference_token) + "'"));
}
}
return *ptr;
}
json::const_reference json::json_pointer::get_checked(const_pointer ptr) const
const json& json_pointer::get_checked(const json* ptr) const
{
using size_type = typename json::size_type;
for (const auto& reference_token : reference_tokens)
{
switch (ptr->m_type)
{
case value_t::object:
case detail::value_t::object:
{
// note: at performs range check
ptr = &ptr->at(reference_token);
break;
}
case value_t::array:
case detail::value_t::array:
{
if (reference_token == "-")
if (JSON_UNLIKELY(reference_token == "-"))
{
// "-" always fails the range check
JSON_THROW(out_of_range::create(402, "array index '-' (" +
std::to_string(ptr->m_value.array->size()) +
") is out of range"));
JSON_THROW(detail::out_of_range::create(402,
"array index '-' (" + Twine(ptr->m_value.array->size()) +
") is out of range"));
}
// error condition (cf. RFC 6901, Sect. 4)
if (reference_token.size() > 1 && reference_token[0] == '0')
if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
{
JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
JSON_THROW(detail::parse_error::create(106, 0,
"array index '" + Twine(reference_token) +
"' must not begin with '0'"));
}
// note: at performs range check
JSON_TRY
{
ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token)));
ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH (std::invalid_argument&)
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + Twine(reference_token) + "' is not a number"));
}
break;
}
default:
{
JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
}
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + Twine(reference_token) + "'"));
}
}
return *ptr;
}
std::vector<std::string> json::json_pointer::split(const std::string& reference_string)
std::vector<std::string> json_pointer::split(const Twine& reference_string)
{
SmallString<128> ref_str_buf;
StringRef ref_str = reference_string.toStringRef(ref_str_buf);
std::vector<std::string> result;
// special case: empty reference string -> no reference tokens
if (reference_string.empty())
if (ref_str.empty())
{
return result;
}
// check if nonempty reference string begins with slash
if (reference_string[0] != '/')
if (JSON_UNLIKELY(ref_str[0] != '/'))
{
JSON_THROW(parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'"));
JSON_THROW(detail::parse_error::create(107, 1,
"JSON pointer must be empty or begin with '/' - was: '" +
Twine(ref_str) + "'"));
}
// extract the reference tokens:
@@ -360,7 +379,7 @@ std::vector<std::string> json::json_pointer::split(const std::string& reference_
// - start: position after the previous slash
for (
// search for the first slash after the first character
size_t slash = reference_string.find_first_of('/', 1),
std::size_t slash = ref_str.find_first_of('/', 1),
// set the beginning of the first reference token
start = 1;
// we can stop if start == string::npos+1 = 0
@@ -369,120 +388,102 @@ std::vector<std::string> json::json_pointer::split(const std::string& reference_
// (will eventually be 0 if slash == std::string::npos)
start = slash + 1,
// find next slash
slash = reference_string.find_first_of('/', start))
slash = ref_str.find_first_of('/', start))
{
// use the text between the beginning of the reference token
// (start) and the last slash (slash).
auto reference_token = reference_string.substr(start, slash - start);
auto reference_token = ref_str.slice(start, slash);
// check reference tokens are properly escaped
for (size_t pos = reference_token.find_first_of('~');
pos != std::string::npos;
for (std::size_t pos = reference_token.find_first_of('~');
pos != StringRef::npos;
pos = reference_token.find_first_of('~', pos + 1))
{
assert(reference_token[pos] == '~');
// ~ must be followed by 0 or 1
if (pos == reference_token.size() - 1 ||
(reference_token[pos + 1] != '0' &&
reference_token[pos + 1] != '1'))
if (JSON_UNLIKELY(pos == reference_token.size() - 1 or
(reference_token[pos + 1] != '0' and
reference_token[pos + 1] != '1')))
{
JSON_THROW(parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'"));
JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'"));
}
}
// finally, store the reference token
unescape(reference_token);
result.push_back(reference_token);
std::string ref_tok = reference_token;
unescape(ref_tok);
result.emplace_back(std::move(ref_tok));
}
return result;
}
/*!
@brief replace all occurrences of a substring by another string
@param[in,out] s the string to manipulate; changed so that all
occurrences of @a f are replaced with @a t
@param[in] f the substring to replace with @a t
@param[in] t the string to replace @a f
@pre The search string @a f must not be empty. **This precondition is
enforced with an assertion.**
@since version 2.0.0
*/
void json::json_pointer::replace_substring(std::string& s,
const std::string& f,
void json_pointer::replace_substring(std::string& s, const std::string& f,
const std::string& t)
{
assert(!f.empty());
for (
size_t pos = s.find(f); // find first occurrence of f
pos != std::string::npos; // make sure f was found
s.replace(pos, f.size(), t), // replace with t
pos = s.find(f, pos + t.size()) // find next occurrence of f
);
assert(not f.empty());
for (auto pos = s.find(f); // find first occurrence of f
pos != std::string::npos; // make sure f was found
s.replace(pos, f.size(), t), // replace with t, and
pos = s.find(f, pos + t.size())) // find next occurrence of f
{}
}
/// escape tilde and slash
std::string json::json_pointer::escape(std::string s)
std::string json_pointer::escape(std::string s)
{
// escape "~"" to "~0" and "/" to "~1"
replace_substring(s, "~", "~0");
replace_substring(s, "/", "~1");
return s;
}
/// unescape tilde and slash
void json::json_pointer::unescape(std::string& s)
/// unescape "~1" to tilde and "~0" to slash (order is important!)
void json_pointer::unescape(std::string& s)
{
// first transform any occurrence of the sequence '~1' to '/'
replace_substring(s, "~1", "/");
// then transform any occurrence of the sequence '~0' to '~'
replace_substring(s, "~0", "~");
}
void json::json_pointer::flatten(const std::string& reference_string,
void json_pointer::flatten(const Twine& reference_string,
const json& value,
json& result)
{
switch (value.m_type)
{
case value_t::array:
case detail::value_t::array:
{
if (value.m_value.array->empty())
{
// flatten empty array as null
result[reference_string] = nullptr;
SmallString<64> buf;
result[reference_string.toStringRef(buf)] = nullptr;
}
else
{
// iterate array and use index as reference string
for (size_t i = 0; i < value.m_value.array->size(); ++i)
for (std::size_t i = 0; i < value.m_value.array->size(); ++i)
{
flatten(reference_string + "/" + std::to_string(i),
flatten(reference_string + "/" + Twine(i),
value.m_value.array->operator[](i), result);
}
}
break;
}
case value_t::object:
case detail::value_t::object:
{
if (value.m_value.object->empty())
{
// flatten empty object as null
result[reference_string] = nullptr;
SmallString<64> buf;
result[reference_string.toStringRef(buf)] = nullptr;
}
else
{
// iterate object and use keys as reference string
for (const auto& element : *value.m_value.object)
{
flatten(reference_string + "/" + escape(element.first()),
element.second, result);
flatten(reference_string + "/" + Twine(escape(element.first())), element.second, result);
}
}
break;
@@ -491,17 +492,19 @@ void json::json_pointer::flatten(const std::string& reference_string,
default:
{
// add primitive value with its reference string
result[reference_string] = value;
SmallString<64> buf;
result[reference_string.toStringRef(buf)] = value;
break;
}
}
}
json json::json_pointer::unflatten(const json& value)
json
json_pointer::unflatten(const json& value)
{
if (!value.is_object())
if (JSON_UNLIKELY(not value.is_object()))
{
JSON_THROW(type_error::create(314, "only objects can be unflattened"));
JSON_THROW(detail::type_error::create(314, "only objects can be unflattened"));
}
// we need to iterate over the object values in sorted key order
@@ -511,7 +514,7 @@ json json::json_pointer::unflatten(const json& value)
{
if (!i->second.is_primitive())
{
JSON_THROW(type_error::create(315, "values in object must be primitive"));
JSON_THROW(detail::type_error::create(315, "values in object must be primitive"));
}
sorted.push_back(i);
}
@@ -538,3 +541,4 @@ json json::json_pointer::unflatten(const json& value)
return result;
}
} // namespace wpi

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*----------------------------------------------------------------------------*/
/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
/* Modifications Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
@@ -7,11 +7,11 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++
| | |__ | | | | | | version 2.1.1
| | |__ | | | | | | version 3.1.2
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -34,38 +34,41 @@ SOFTWARE.
#include "wpi/json.h"
#include <clocale> // lconv, localeconv
#include <cmath> // labs, isfinite, isnan, signbit, ldexp
#include <locale> // locale
#include "wpi/raw_ostream.h"
namespace wpi {
/*!
@brief wrapper around the serialization functions
*/
class json::serializer
{
public:
serializer(const serializer&) = delete;
serializer& operator=(const serializer&) = delete;
static constexpr uint8_t UTF8_ACCEPT = 0;
static constexpr uint8_t UTF8_REJECT = 1;
public:
/*!
@param[in] s output stream to serialize to
@param[in] ichar indentation character to use
*/
explicit serializer(raw_ostream& s)
serializer(raw_ostream& s, const char ichar)
: o(s), loc(std::localeconv()),
thousands_sep(!loc->thousands_sep ? '\0' : loc->thousands_sep[0]),
decimal_point(!loc->decimal_point ? '\0' : loc->decimal_point[0])
thousands_sep(loc->thousands_sep == nullptr ? '\0' : * (loc->thousands_sep)),
decimal_point(loc->decimal_point == nullptr ? '\0' : * (loc->decimal_point)),
indent_char(ichar), indent_string(512, indent_char)
{}
// delete because of pointer members
serializer(const serializer&) = delete;
serializer& operator=(const serializer&) = delete;
/*!
@brief internal implementation of the serialization function
This function is called by the public member function dump and
organizes the serialization internally. The indentation level is
propagated as additional parameter. In case of arrays and objects, the
function is called recursively.
This function is called by the public member function dump and organizes
the serialization internally. The indentation level is propagated as
additional parameter. In case of arrays and objects, the function is
called recursively.
- strings and object keys are escaped using `escape_string()`
- integer numbers are converted implicitly via `operator<<`
@@ -76,45 +79,128 @@ class json::serializer
@param[in] indent_step the indent level
@param[in] current_indent the current indent level (only used internally)
*/
void dump(const json& val,
const bool pretty_print,
void dump(const json& val, const bool pretty_print,
const bool ensure_ascii,
const unsigned int indent_step,
const unsigned int current_indent = 0);
private:
/*!
@brief dump escaped string
Escape a string by replacing certain special characters by a sequence
of an escape character (backslash) and another character and other
control characters by a sequence of "\u" followed by a four-digit hex
Escape a string by replacing certain special characters by a sequence of an
escape character (backslash) and another character and other control
characters by a sequence of "\u" followed by a four-digit hex
representation. The escaped string is written to output stream @a o.
@param[in] s the string to escape
@param[in] ensure_ascii whether to escape non-ASCII characters with
\uXXXX sequences
@complexity Linear in the length of string @a s.
*/
void dump_escaped(StringRef s) const;
void dump_escaped(StringRef s, const bool ensure_ascii);
/*!
@brief dump an integer
Dump a given integer to output stream @a o. Works internally with
@a number_buffer.
@param[in] x integer number (signed or unsigned) to dump
@tparam NumberType either @a int64_t or @a uint64_t
*/
template<typename NumberType, detail::enable_if_t<
std::is_same<NumberType, uint64_t>::value or
std::is_same<NumberType, int64_t>::value,
int> = 0>
void dump_integer(NumberType x)
{
// special case for "0"
if (x == 0)
{
o << '0';
return;
}
const bool is_negative = (x <= 0) and (x != 0); // see issue #755
std::size_t i = 0;
while (x != 0)
{
// spare 1 byte for '\0'
assert(i < number_buffer.size() - 1);
const auto digit = std::labs(static_cast<long>(x % 10));
number_buffer[i++] = static_cast<char>('0' + digit);
x /= 10;
}
if (is_negative)
{
// make sure there is capacity for the '-'
assert(i < number_buffer.size() - 2);
number_buffer[i++] = '-';
}
std::reverse(number_buffer.begin(), number_buffer.begin() + i);
o.write(number_buffer.data(), i);
}
/*!
@brief dump a floating-point number
Dump a given floating-point number to output stream @a o. Works
internally with @a number_buffer.
Dump a given floating-point number to output stream @a o. Works internally
with @a number_buffer.
@param[in] x floating-point number to dump
*/
void dump_float(double x);
/*!
@brief check whether a string is UTF-8 encoded
The function checks each byte of a string whether it is UTF-8 encoded. The
result of the check is stored in the @a state parameter. The function must
be called initially with state 0 (accept). State 1 means the string must
be rejected, because the current byte is not allowed. If the string is
completely processed, but the state is non-zero, the string ended
prematurely; that is, the last byte indicated more bytes should have
followed.
@param[in,out] state the state of the decoding
@param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT)
@param[in] byte next byte to decode
@return new state
@note The function has been edited: a std::array is used.
@copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
@sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
*/
static uint8_t decode(uint8_t& state, uint32_t& codep, const uint8_t byte) noexcept;
private:
/// the output of the serializer
raw_ostream& o;
/// a (hopefully) large enough character buffer
std::array<char, 64> number_buffer{{}};
/// the locale
const std::lconv* loc = nullptr;
/// the locale's thousand separator character
const char thousands_sep = '\0';
/// the locale's decimal point character
const char decimal_point = '\0';
/// string buffer
std::array<char, 512> string_buffer{{}};
/// the indentation character
const char indent_char;
/// the indentation string
std::string indent_string;
};
} // namespace wpi