2018-01-10 12:18:31 +03:00
|
|
|
#pragma once
|
2017-08-14 19:04:51 +03:00
|
|
|
|
2017-08-14 20:28:01 +03:00
|
|
|
#include <algorithm> // reverse, remove, fill, find, none_of
|
|
|
|
|
#include <array> // array
|
|
|
|
|
#include <clocale> // localeconv, lconv
|
|
|
|
|
#include <cmath> // labs, isfinite, isnan, signbit
|
|
|
|
|
#include <cstddef> // size_t, ptrdiff_t
|
|
|
|
|
#include <cstdint> // uint8_t
|
|
|
|
|
#include <cstdio> // snprintf
|
|
|
|
|
#include <limits> // numeric_limits
|
2020-06-22 23:32:21 +03:00
|
|
|
#include <string> // string, char_traits
|
2021-10-29 22:27:34 +03:00
|
|
|
#include <iomanip> // setfill, setw
|
|
|
|
|
#include <sstream> // stringstream
|
2017-08-14 20:28:01 +03:00
|
|
|
#include <type_traits> // is_same
|
2019-03-15 16:55:13 +03:00
|
|
|
#include <utility> // move
|
2017-08-14 19:04:51 +03:00
|
|
|
|
2019-03-17 14:01:49 +03:00
|
|
|
#include <nlohmann/detail/exceptions.hpp>
|
2018-01-29 13:21:11 +03:00
|
|
|
#include <nlohmann/detail/macro_scope.hpp>
|
2018-10-28 16:20:20 +03:00
|
|
|
#include <nlohmann/detail/output/binary_writer.hpp>
|
2018-01-29 13:21:11 +03:00
|
|
|
#include <nlohmann/detail/output/output_adapters.hpp>
|
|
|
|
|
#include <nlohmann/detail/value_t.hpp>
|
2021-12-07 23:28:19 +03:00
|
|
|
#include <nlohmann/detail/output/denumerizer.hpp>
|
2017-08-14 19:04:51 +03:00
|
|
|
|
|
|
|
|
namespace nlohmann
|
|
|
|
|
{
|
|
|
|
|
namespace detail
|
|
|
|
|
{
|
|
|
|
|
///////////////////
|
|
|
|
|
// serialization //
|
|
|
|
|
///////////////////
|
|
|
|
|
|
2018-10-21 12:49:37 +03:00
|
|
|
/// how to treat decoding errors
|
|
|
|
|
enum class error_handler_t
|
|
|
|
|
{
|
|
|
|
|
strict, ///< throw a type_error exception in case of invalid UTF-8
|
|
|
|
|
replace, ///< replace invalid UTF-8 sequences with U+FFFD
|
|
|
|
|
ignore ///< ignore invalid UTF-8 sequences
|
|
|
|
|
};
|
|
|
|
|
|
2021-12-07 23:28:19 +03:00
|
|
|
template<typename BasicJsonType, typename DenumerizerType = denumerizer>
|
2017-08-14 19:04:51 +03:00
|
|
|
class serializer
|
|
|
|
|
{
|
|
|
|
|
using string_t = typename BasicJsonType::string_t;
|
|
|
|
|
using number_float_t = typename BasicJsonType::number_float_t;
|
|
|
|
|
using number_integer_t = typename BasicJsonType::number_integer_t;
|
|
|
|
|
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
2020-05-17 23:50:27 +03:00
|
|
|
using binary_char_t = typename BasicJsonType::binary_t::value_type;
|
2019-03-17 02:27:44 +03:00
|
|
|
static constexpr std::uint8_t UTF8_ACCEPT = 0;
|
|
|
|
|
static constexpr std::uint8_t UTF8_REJECT = 1;
|
2018-01-16 22:42:00 +03:00
|
|
|
|
2017-08-14 19:04:51 +03:00
|
|
|
public:
|
|
|
|
|
/*!
|
|
|
|
|
@param[in] s output stream to serialize to
|
|
|
|
|
@param[in] ichar indentation character to use
|
2018-10-21 12:49:37 +03:00
|
|
|
@param[in] error_handler_ how to react on decoding errors
|
2017-08-14 19:04:51 +03:00
|
|
|
*/
|
2018-10-21 12:49:37 +03:00
|
|
|
serializer(output_adapter_t<char> s, const char ichar,
|
|
|
|
|
error_handler_t error_handler_ = error_handler_t::strict)
|
|
|
|
|
: o(std::move(s))
|
|
|
|
|
, indent_char(ichar)
|
|
|
|
|
, indent_string(512, indent_char)
|
|
|
|
|
, error_handler(error_handler_)
|
2018-01-16 22:42:00 +03:00
|
|
|
{}
|
2017-08-14 19:04:51 +03:00
|
|
|
|
|
|
|
|
// delete because of pointer members
|
|
|
|
|
serializer(const serializer&) = delete;
|
|
|
|
|
serializer& operator=(const serializer&) = delete;
|
2018-10-07 22:34:40 +03:00
|
|
|
serializer(serializer&&) = delete;
|
|
|
|
|
serializer& operator=(serializer&&) = delete;
|
2018-10-07 19:39:18 +03:00
|
|
|
~serializer() = default;
|
2017-08-14 19:04:51 +03:00
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
@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.
|
|
|
|
|
|
|
|
|
|
- strings and object keys are escaped using `escape_string()`
|
|
|
|
|
- integer numbers are converted implicitly via `operator<<`
|
|
|
|
|
- floating-point numbers are converted to a string using `"%g"` format
|
2020-05-18 14:53:20 +03:00
|
|
|
- binary values are serialized as objects containing the subtype and the
|
|
|
|
|
byte array
|
2017-08-14 19:04:51 +03:00
|
|
|
|
2019-07-05 07:13:25 +03:00
|
|
|
@param[in] val value to serialize
|
|
|
|
|
@param[in] pretty_print whether the output shall be pretty-printed
|
2020-05-18 14:53:20 +03:00
|
|
|
@param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters
|
|
|
|
|
in the output are escaped with `\uXXXX` sequences, and the result consists
|
|
|
|
|
of ASCII characters only.
|
2019-07-05 07:13:25 +03:00
|
|
|
@param[in] indent_step the indent level
|
|
|
|
|
@param[in] current_indent the current indent level (only used internally)
|
2017-08-14 19:04:51 +03:00
|
|
|
*/
|
2020-05-18 14:53:20 +03:00
|
|
|
void dump(const BasicJsonType& val,
|
|
|
|
|
const bool pretty_print,
|
2017-08-14 19:04:51 +03:00
|
|
|
const bool ensure_ascii,
|
|
|
|
|
const unsigned int indent_step,
|
2020-05-18 14:53:20 +03:00
|
|
|
const unsigned int current_indent = 0)
|
2017-08-14 19:04:51 +03:00
|
|
|
{
|
|
|
|
|
switch (val.m_type)
|
|
|
|
|
{
|
|
|
|
|
case value_t::object:
|
|
|
|
|
{
|
|
|
|
|
if (val.m_value.object->empty())
|
|
|
|
|
{
|
|
|
|
|
o->write_characters("{}", 2);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pretty_print)
|
|
|
|
|
{
|
|
|
|
|
o->write_characters("{\n", 2);
|
|
|
|
|
|
|
|
|
|
// variable to hold indentation for recursive calls
|
|
|
|
|
const auto new_indent = current_indent + indent_step;
|
2019-07-01 23:37:30 +03:00
|
|
|
if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
|
2017-08-14 19:04:51 +03:00
|
|
|
{
|
|
|
|
|
indent_string.resize(indent_string.size() * 2, ' ');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// first n-1 elements
|
|
|
|
|
auto i = val.m_value.object->cbegin();
|
|
|
|
|
for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
|
|
|
|
|
{
|
|
|
|
|
o->write_characters(indent_string.c_str(), new_indent);
|
|
|
|
|
o->write_character('\"');
|
|
|
|
|
dump_escaped(i->first, ensure_ascii);
|
|
|
|
|
o->write_characters("\": ", 3);
|
2020-05-18 14:53:20 +03:00
|
|
|
dump(i->second, true, ensure_ascii, indent_step, new_indent);
|
2017-08-14 19:04:51 +03:00
|
|
|
o->write_characters(",\n", 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// last element
|
2020-07-06 13:22:31 +03:00
|
|
|
JSON_ASSERT(i != val.m_value.object->cend());
|
|
|
|
|
JSON_ASSERT(std::next(i) == val.m_value.object->cend());
|
2017-08-14 19:04:51 +03:00
|
|
|
o->write_characters(indent_string.c_str(), new_indent);
|
|
|
|
|
o->write_character('\"');
|
|
|
|
|
dump_escaped(i->first, ensure_ascii);
|
|
|
|
|
o->write_characters("\": ", 3);
|
2020-05-18 14:53:20 +03:00
|
|
|
dump(i->second, true, ensure_ascii, indent_step, new_indent);
|
2017-08-14 19:04:51 +03:00
|
|
|
|
|
|
|
|
o->write_character('\n');
|
|
|
|
|
o->write_characters(indent_string.c_str(), current_indent);
|
|
|
|
|
o->write_character('}');
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
o->write_character('{');
|
|
|
|
|
|
|
|
|
|
// first n-1 elements
|
|
|
|
|
auto i = val.m_value.object->cbegin();
|
|
|
|
|
for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
|
|
|
|
|
{
|
|
|
|
|
o->write_character('\"');
|
|
|
|
|
dump_escaped(i->first, ensure_ascii);
|
|
|
|
|
o->write_characters("\":", 2);
|
2020-05-18 14:53:20 +03:00
|
|
|
dump(i->second, false, ensure_ascii, indent_step, current_indent);
|
2017-08-14 19:04:51 +03:00
|
|
|
o->write_character(',');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// last element
|
2020-07-06 13:22:31 +03:00
|
|
|
JSON_ASSERT(i != val.m_value.object->cend());
|
|
|
|
|
JSON_ASSERT(std::next(i) == val.m_value.object->cend());
|
2017-08-14 19:04:51 +03:00
|
|
|
o->write_character('\"');
|
|
|
|
|
dump_escaped(i->first, ensure_ascii);
|
|
|
|
|
o->write_characters("\":", 2);
|
2020-05-18 14:53:20 +03:00
|
|
|
dump(i->second, false, ensure_ascii, indent_step, current_indent);
|
2017-08-14 19:04:51 +03:00
|
|
|
|
|
|
|
|
o->write_character('}');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case value_t::array:
|
|
|
|
|
{
|
|
|
|
|
if (val.m_value.array->empty())
|
|
|
|
|
{
|
|
|
|
|
o->write_characters("[]", 2);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pretty_print)
|
|
|
|
|
{
|
|
|
|
|
o->write_characters("[\n", 2);
|
|
|
|
|
|
|
|
|
|
// variable to hold indentation for recursive calls
|
|
|
|
|
const auto new_indent = current_indent + indent_step;
|
2019-07-01 23:37:30 +03:00
|
|
|
if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
|
2017-08-14 19:04:51 +03:00
|
|
|
{
|
|
|
|
|
indent_string.resize(indent_string.size() * 2, ' ');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// first n-1 elements
|
|
|
|
|
for (auto i = val.m_value.array->cbegin();
|
|
|
|
|
i != val.m_value.array->cend() - 1; ++i)
|
|
|
|
|
{
|
|
|
|
|
o->write_characters(indent_string.c_str(), new_indent);
|
2020-05-18 14:53:20 +03:00
|
|
|
dump(*i, true, ensure_ascii, indent_step, new_indent);
|
2017-08-14 19:04:51 +03:00
|
|
|
o->write_characters(",\n", 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// last element
|
2020-07-11 15:04:40 +03:00
|
|
|
JSON_ASSERT(!val.m_value.array->empty());
|
2017-08-14 19:04:51 +03:00
|
|
|
o->write_characters(indent_string.c_str(), new_indent);
|
2020-05-18 14:53:20 +03:00
|
|
|
dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
|
2017-08-14 19:04:51 +03:00
|
|
|
|
|
|
|
|
o->write_character('\n');
|
|
|
|
|
o->write_characters(indent_string.c_str(), current_indent);
|
|
|
|
|
o->write_character(']');
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
o->write_character('[');
|
|
|
|
|
|
|
|
|
|
// first n-1 elements
|
|
|
|
|
for (auto i = val.m_value.array->cbegin();
|
|
|
|
|
i != val.m_value.array->cend() - 1; ++i)
|
|
|
|
|
{
|
2020-05-18 14:53:20 +03:00
|
|
|
dump(*i, false, ensure_ascii, indent_step, current_indent);
|
2017-08-14 19:04:51 +03:00
|
|
|
o->write_character(',');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// last element
|
2020-07-11 15:04:40 +03:00
|
|
|
JSON_ASSERT(!val.m_value.array->empty());
|
2020-05-18 14:53:20 +03:00
|
|
|
dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
|
2017-08-14 19:04:51 +03:00
|
|
|
|
|
|
|
|
o->write_character(']');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case value_t::string:
|
|
|
|
|
{
|
|
|
|
|
o->write_character('\"');
|
|
|
|
|
dump_escaped(*val.m_value.string, ensure_ascii);
|
|
|
|
|
o->write_character('\"');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-05 07:13:25 +03:00
|
|
|
case value_t::binary:
|
|
|
|
|
{
|
2020-05-18 14:53:20 +03:00
|
|
|
if (pretty_print)
|
2019-07-05 07:13:25 +03:00
|
|
|
{
|
2020-05-18 14:53:20 +03:00
|
|
|
o->write_characters("{\n", 2);
|
2019-07-05 07:13:25 +03:00
|
|
|
|
2020-05-18 14:53:20 +03:00
|
|
|
// variable to hold indentation for recursive calls
|
|
|
|
|
const auto new_indent = current_indent + indent_step;
|
|
|
|
|
if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
|
|
|
|
|
{
|
|
|
|
|
indent_string.resize(indent_string.size() * 2, ' ');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
o->write_characters(indent_string.c_str(), new_indent);
|
|
|
|
|
|
|
|
|
|
o->write_characters("\"bytes\": [", 10);
|
|
|
|
|
|
2020-06-03 15:20:36 +03:00
|
|
|
if (!val.m_value.binary->empty())
|
2020-05-18 14:53:20 +03:00
|
|
|
{
|
|
|
|
|
for (auto i = val.m_value.binary->cbegin();
|
|
|
|
|
i != val.m_value.binary->cend() - 1; ++i)
|
|
|
|
|
{
|
2021-12-07 23:28:19 +03:00
|
|
|
DenumerizerType::dump_integer(o, *i);
|
2020-05-18 14:53:20 +03:00
|
|
|
o->write_characters(", ", 2);
|
|
|
|
|
}
|
2021-12-07 23:28:19 +03:00
|
|
|
DenumerizerType::dump_integer(o, val.m_value.binary->back());
|
2020-05-18 14:53:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
o->write_characters("],\n", 3);
|
|
|
|
|
o->write_characters(indent_string.c_str(), new_indent);
|
|
|
|
|
|
|
|
|
|
o->write_characters("\"subtype\": ", 11);
|
|
|
|
|
if (val.m_value.binary->has_subtype())
|
|
|
|
|
{
|
2021-12-07 23:28:19 +03:00
|
|
|
DenumerizerType::dump_integer(o, val.m_value.binary->subtype());
|
2020-05-18 14:53:20 +03:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
o->write_characters("null", 4);
|
|
|
|
|
}
|
|
|
|
|
o->write_character('\n');
|
|
|
|
|
o->write_characters(indent_string.c_str(), current_indent);
|
|
|
|
|
o->write_character('}');
|
2019-07-05 07:13:25 +03:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-05-18 14:53:20 +03:00
|
|
|
o->write_characters("{\"bytes\":[", 10);
|
|
|
|
|
|
2020-06-03 15:20:36 +03:00
|
|
|
if (!val.m_value.binary->empty())
|
2019-07-05 07:13:25 +03:00
|
|
|
{
|
2020-05-18 14:53:20 +03:00
|
|
|
for (auto i = val.m_value.binary->cbegin();
|
|
|
|
|
i != val.m_value.binary->cend() - 1; ++i)
|
|
|
|
|
{
|
2021-12-07 23:28:19 +03:00
|
|
|
DenumerizerType::dump_integer(o, *i);
|
2020-05-18 14:53:20 +03:00
|
|
|
o->write_character(',');
|
|
|
|
|
}
|
2021-12-07 23:28:19 +03:00
|
|
|
DenumerizerType::dump_integer(o, val.m_value.binary->back());
|
2019-07-05 07:13:25 +03:00
|
|
|
}
|
|
|
|
|
|
2020-05-18 14:53:20 +03:00
|
|
|
o->write_characters("],\"subtype\":", 12);
|
|
|
|
|
if (val.m_value.binary->has_subtype())
|
|
|
|
|
{
|
2021-12-07 23:28:19 +03:00
|
|
|
DenumerizerType::dump_integer(o, val.m_value.binary->subtype());
|
2020-05-18 14:53:20 +03:00
|
|
|
o->write_character('}');
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
o->write_characters("null}", 5);
|
|
|
|
|
}
|
2019-07-05 07:13:25 +03:00
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-14 19:04:51 +03:00
|
|
|
case value_t::boolean:
|
|
|
|
|
{
|
|
|
|
|
if (val.m_value.boolean)
|
|
|
|
|
{
|
|
|
|
|
o->write_characters("true", 4);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
o->write_characters("false", 5);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case value_t::number_integer:
|
|
|
|
|
{
|
2021-12-07 23:28:19 +03:00
|
|
|
DenumerizerType::dump_integer(o, val.m_value.number_integer);
|
2017-08-14 19:04:51 +03:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case value_t::number_unsigned:
|
|
|
|
|
{
|
2021-12-07 23:28:19 +03:00
|
|
|
DenumerizerType::dump_integer(o, val.m_value.number_unsigned);
|
2017-08-14 19:04:51 +03:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case value_t::number_float:
|
|
|
|
|
{
|
2021-12-07 23:28:19 +03:00
|
|
|
DenumerizerType::dump_float(o, val.m_value.number_float);
|
2017-08-14 19:04:51 +03:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case value_t::discarded:
|
|
|
|
|
{
|
|
|
|
|
o->write_characters("<discarded>", 11);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case value_t::null:
|
|
|
|
|
{
|
|
|
|
|
o->write_characters("null", 4);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-03-17 14:01:49 +03:00
|
|
|
|
2019-03-18 15:53:48 +03:00
|
|
|
default: // LCOV_EXCL_LINE
|
2021-03-24 09:15:18 +03:00
|
|
|
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
2017-08-14 19:04:51 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-12 14:30:06 +03:00
|
|
|
JSON_PRIVATE_UNLESS_TESTED:
|
2017-08-14 19:04:51 +03:00
|
|
|
/*!
|
|
|
|
|
@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
|
|
|
|
|
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.
|
|
|
|
|
*/
|
2018-10-21 12:49:37 +03:00
|
|
|
void dump_escaped(const string_t& s, const bool ensure_ascii)
|
2017-08-14 19:04:51 +03:00
|
|
|
{
|
2021-03-24 09:15:18 +03:00
|
|
|
std::uint32_t codepoint{};
|
2019-03-17 02:27:44 +03:00
|
|
|
std::uint8_t state = UTF8_ACCEPT;
|
2018-01-16 22:42:00 +03:00
|
|
|
std::size_t bytes = 0; // number of bytes written to string_buffer
|
2017-08-14 19:04:51 +03:00
|
|
|
|
2018-10-21 12:49:37 +03:00
|
|
|
// number of bytes written at the point of the last valid byte
|
|
|
|
|
std::size_t bytes_after_last_accept = 0;
|
2018-10-22 16:53:36 +03:00
|
|
|
std::size_t undumped_chars = 0;
|
2018-10-21 12:49:37 +03:00
|
|
|
|
2017-08-14 19:04:51 +03:00
|
|
|
for (std::size_t i = 0; i < s.size(); ++i)
|
|
|
|
|
{
|
2021-08-08 14:24:17 +03:00
|
|
|
const auto byte = static_cast<std::uint8_t>(s[i]);
|
2017-08-14 19:04:51 +03:00
|
|
|
|
2018-01-16 22:42:00 +03:00
|
|
|
switch (decode(state, codepoint, byte))
|
|
|
|
|
{
|
|
|
|
|
case UTF8_ACCEPT: // decode found a new code point
|
2017-08-14 19:04:51 +03:00
|
|
|
{
|
2018-01-16 22:42:00 +03:00
|
|
|
switch (codepoint)
|
|
|
|
|
{
|
|
|
|
|
case 0x08: // backspace
|
|
|
|
|
{
|
|
|
|
|
string_buffer[bytes++] = '\\';
|
|
|
|
|
string_buffer[bytes++] = 'b';
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-08-14 19:04:51 +03:00
|
|
|
|
2018-01-16 22:42:00 +03:00
|
|
|
case 0x09: // horizontal tab
|
|
|
|
|
{
|
|
|
|
|
string_buffer[bytes++] = '\\';
|
|
|
|
|
string_buffer[bytes++] = 't';
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-08-14 19:04:51 +03:00
|
|
|
|
2018-01-16 22:42:00 +03:00
|
|
|
case 0x0A: // newline
|
|
|
|
|
{
|
|
|
|
|
string_buffer[bytes++] = '\\';
|
|
|
|
|
string_buffer[bytes++] = 'n';
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-08-14 19:04:51 +03:00
|
|
|
|
2018-01-16 22:42:00 +03:00
|
|
|
case 0x0C: // formfeed
|
|
|
|
|
{
|
|
|
|
|
string_buffer[bytes++] = '\\';
|
|
|
|
|
string_buffer[bytes++] = 'f';
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-08-14 19:04:51 +03:00
|
|
|
|
2018-01-16 22:42:00 +03:00
|
|
|
case 0x0D: // carriage return
|
|
|
|
|
{
|
|
|
|
|
string_buffer[bytes++] = '\\';
|
|
|
|
|
string_buffer[bytes++] = 'r';
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-08-14 19:04:51 +03:00
|
|
|
|
2018-01-16 22:42:00 +03:00
|
|
|
case 0x22: // quotation mark
|
2017-08-14 19:04:51 +03:00
|
|
|
{
|
2018-01-16 22:42:00 +03:00
|
|
|
string_buffer[bytes++] = '\\';
|
|
|
|
|
string_buffer[bytes++] = '\"';
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-08-14 19:04:51 +03:00
|
|
|
|
2018-01-16 22:42:00 +03:00
|
|
|
case 0x5C: // reverse solidus
|
|
|
|
|
{
|
|
|
|
|
string_buffer[bytes++] = '\\';
|
|
|
|
|
string_buffer[bytes++] = '\\';
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-08-14 19:04:51 +03:00
|
|
|
|
2018-01-16 22:42:00 +03:00
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
// escape control characters (0x00..0x1F) or, if
|
|
|
|
|
// ensure_ascii parameter is used, non-ASCII characters
|
2020-06-03 15:20:36 +03:00
|
|
|
if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F)))
|
2017-08-14 19:04:51 +03:00
|
|
|
{
|
2018-01-16 22:42:00 +03:00
|
|
|
if (codepoint <= 0xFFFF)
|
|
|
|
|
{
|
2021-03-24 09:15:18 +03:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
2018-11-09 23:18:02 +03:00
|
|
|
(std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x",
|
2019-03-17 02:27:44 +03:00
|
|
|
static_cast<std::uint16_t>(codepoint));
|
2018-01-16 22:42:00 +03:00
|
|
|
bytes += 6;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-03-24 09:15:18 +03:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
2018-11-09 23:18:02 +03:00
|
|
|
(std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x",
|
2019-03-17 02:27:44 +03:00
|
|
|
static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),
|
|
|
|
|
static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu)));
|
2018-01-16 22:42:00 +03:00
|
|
|
bytes += 12;
|
|
|
|
|
}
|
2017-08-14 19:04:51 +03:00
|
|
|
}
|
2018-01-16 22:42:00 +03:00
|
|
|
else
|
2017-08-14 19:04:51 +03:00
|
|
|
{
|
2018-01-16 22:42:00 +03:00
|
|
|
// copy byte to buffer (all previous bytes
|
|
|
|
|
// been copied have in default case above)
|
|
|
|
|
string_buffer[bytes++] = s[i];
|
2017-08-14 19:04:51 +03:00
|
|
|
}
|
2018-01-16 22:42:00 +03:00
|
|
|
break;
|
2017-08-14 19:04:51 +03:00
|
|
|
}
|
2018-01-16 22:42:00 +03:00
|
|
|
}
|
2017-08-14 19:04:51 +03:00
|
|
|
|
2018-01-16 22:42:00 +03:00
|
|
|
// write buffer and reset index; there must be 13 bytes
|
|
|
|
|
// left, as this is the maximal number of bytes to be
|
|
|
|
|
// written ("\uxxxx\uxxxx\0") for one code point
|
|
|
|
|
if (string_buffer.size() - bytes < 13)
|
|
|
|
|
{
|
|
|
|
|
o->write_characters(string_buffer.data(), bytes);
|
|
|
|
|
bytes = 0;
|
2017-08-14 19:04:51 +03:00
|
|
|
}
|
2018-10-21 12:49:37 +03:00
|
|
|
|
|
|
|
|
// remember the byte position of this accept
|
|
|
|
|
bytes_after_last_accept = bytes;
|
2018-10-22 16:53:36 +03:00
|
|
|
undumped_chars = 0;
|
2018-01-16 22:42:00 +03:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case UTF8_REJECT: // decode found invalid UTF-8 byte
|
|
|
|
|
{
|
2018-10-16 21:38:50 +03:00
|
|
|
switch (error_handler)
|
|
|
|
|
{
|
|
|
|
|
case error_handler_t::strict:
|
|
|
|
|
{
|
2021-10-29 22:27:34 +03:00
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << (byte | 0);
|
|
|
|
|
JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + ss.str(), BasicJsonType()));
|
2018-10-16 21:38:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case error_handler_t::ignore:
|
|
|
|
|
case error_handler_t::replace:
|
|
|
|
|
{
|
2018-10-23 23:56:10 +03:00
|
|
|
// in case we saw this character the first time, we
|
2018-10-21 12:49:37 +03:00
|
|
|
// would like to read it again, because the byte
|
|
|
|
|
// may be OK for itself, but just not OK for the
|
|
|
|
|
// previous sequence
|
2018-10-22 16:53:36 +03:00
|
|
|
if (undumped_chars > 0)
|
2018-10-21 12:49:37 +03:00
|
|
|
{
|
|
|
|
|
--i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// reset length buffer to the last accepted index;
|
|
|
|
|
// thus removing/ignoring the invalid characters
|
|
|
|
|
bytes = bytes_after_last_accept;
|
|
|
|
|
|
|
|
|
|
if (error_handler == error_handler_t::replace)
|
|
|
|
|
{
|
|
|
|
|
// add a replacement character
|
2018-10-22 10:18:16 +03:00
|
|
|
if (ensure_ascii)
|
|
|
|
|
{
|
|
|
|
|
string_buffer[bytes++] = '\\';
|
|
|
|
|
string_buffer[bytes++] = 'u';
|
|
|
|
|
string_buffer[bytes++] = 'f';
|
|
|
|
|
string_buffer[bytes++] = 'f';
|
|
|
|
|
string_buffer[bytes++] = 'f';
|
|
|
|
|
string_buffer[bytes++] = 'd';
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-10-28 16:20:20 +03:00
|
|
|
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xEF');
|
|
|
|
|
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBF');
|
|
|
|
|
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBD');
|
2018-10-22 10:18:16 +03:00
|
|
|
}
|
2019-01-19 04:27:49 +03:00
|
|
|
|
|
|
|
|
// write buffer and reset index; there must be 13 bytes
|
|
|
|
|
// left, as this is the maximal number of bytes to be
|
|
|
|
|
// written ("\uxxxx\uxxxx\0") for one code point
|
|
|
|
|
if (string_buffer.size() - bytes < 13)
|
|
|
|
|
{
|
|
|
|
|
o->write_characters(string_buffer.data(), bytes);
|
|
|
|
|
bytes = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-22 00:26:25 +03:00
|
|
|
bytes_after_last_accept = bytes;
|
2018-10-21 12:49:37 +03:00
|
|
|
}
|
|
|
|
|
|
2018-10-22 16:53:36 +03:00
|
|
|
undumped_chars = 0;
|
|
|
|
|
|
2018-10-21 12:49:37 +03:00
|
|
|
// continue processing the string
|
2018-10-16 21:38:50 +03:00
|
|
|
state = UTF8_ACCEPT;
|
2018-10-26 12:10:49 +03:00
|
|
|
break;
|
2018-10-16 21:38:50 +03:00
|
|
|
}
|
2019-03-17 14:01:49 +03:00
|
|
|
|
2019-03-18 15:53:48 +03:00
|
|
|
default: // LCOV_EXCL_LINE
|
2021-03-24 09:15:18 +03:00
|
|
|
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
2018-10-16 21:38:50 +03:00
|
|
|
}
|
2018-10-26 12:10:49 +03:00
|
|
|
break;
|
2018-01-16 22:42:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default: // decode found yet incomplete multi-byte code point
|
|
|
|
|
{
|
2020-06-03 15:20:36 +03:00
|
|
|
if (!ensure_ascii)
|
2017-08-14 19:04:51 +03:00
|
|
|
{
|
2018-01-16 22:42:00 +03:00
|
|
|
// code point will not be escaped - copy byte to buffer
|
|
|
|
|
string_buffer[bytes++] = s[i];
|
2017-08-14 19:04:51 +03:00
|
|
|
}
|
2018-10-22 16:53:36 +03:00
|
|
|
++undumped_chars;
|
2017-08-14 19:04:51 +03:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-21 12:49:37 +03:00
|
|
|
// we finished processing the string
|
2019-07-01 23:37:30 +03:00
|
|
|
if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT))
|
2018-01-16 22:42:00 +03:00
|
|
|
{
|
|
|
|
|
// write buffer
|
|
|
|
|
if (bytes > 0)
|
|
|
|
|
{
|
|
|
|
|
o->write_characters(string_buffer.data(), bytes);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// we finish reading, but do not accept: string was incomplete
|
2018-10-16 21:38:50 +03:00
|
|
|
switch (error_handler)
|
|
|
|
|
{
|
|
|
|
|
case error_handler_t::strict:
|
|
|
|
|
{
|
2021-10-29 22:27:34 +03:00
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << (static_cast<std::uint8_t>(s.back()) | 0);
|
|
|
|
|
JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + ss.str(), BasicJsonType()));
|
2018-10-16 21:38:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case error_handler_t::ignore:
|
|
|
|
|
{
|
2018-10-21 12:49:37 +03:00
|
|
|
// write all accepted bytes
|
|
|
|
|
o->write_characters(string_buffer.data(), bytes_after_last_accept);
|
2018-10-16 21:38:50 +03:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case error_handler_t::replace:
|
|
|
|
|
{
|
2018-10-21 12:49:37 +03:00
|
|
|
// write all accepted bytes
|
|
|
|
|
o->write_characters(string_buffer.data(), bytes_after_last_accept);
|
|
|
|
|
// add a replacement character
|
2018-10-22 10:18:16 +03:00
|
|
|
if (ensure_ascii)
|
|
|
|
|
{
|
|
|
|
|
o->write_characters("\\ufffd", 6);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
o->write_characters("\xEF\xBF\xBD", 3);
|
|
|
|
|
}
|
2018-10-16 21:38:50 +03:00
|
|
|
break;
|
|
|
|
|
}
|
2019-03-17 14:01:49 +03:00
|
|
|
|
2019-03-18 15:53:48 +03:00
|
|
|
default: // LCOV_EXCL_LINE
|
2021-03-24 09:15:18 +03:00
|
|
|
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
2018-10-16 21:38:50 +03:00
|
|
|
}
|
2018-01-16 22:42:00 +03:00
|
|
|
}
|
2017-08-14 19:04:51 +03:00
|
|
|
}
|
|
|
|
|
|
2020-08-12 14:30:06 +03:00
|
|
|
private:
|
2017-08-14 19:04:51 +03:00
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
@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
|
2018-01-16 22:42:00 +03:00
|
|
|
@param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT)
|
2017-08-14 19:04:51 +03:00
|
|
|
@param[in] byte next byte to decode
|
2018-01-16 22:42:00 +03:00
|
|
|
@return new state
|
2017-08-14 19:04:51 +03:00
|
|
|
|
2018-01-16 22:42:00 +03:00
|
|
|
@note The function has been edited: a std::array is used.
|
2017-08-14 19:04:51 +03:00
|
|
|
|
|
|
|
|
@copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
|
|
|
|
|
@sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
|
|
|
|
|
*/
|
2019-03-17 02:27:44 +03:00
|
|
|
static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept
|
2017-08-14 19:04:51 +03:00
|
|
|
{
|
2019-03-17 02:27:44 +03:00
|
|
|
static const std::array<std::uint8_t, 400> utf8d =
|
2017-08-14 19:04:51 +03:00
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F
|
|
|
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F
|
|
|
|
|
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF
|
|
|
|
|
8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF
|
|
|
|
|
0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
|
|
|
|
|
0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
|
|
|
|
|
0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
|
|
|
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
|
|
|
|
|
1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
|
|
|
|
|
1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
|
|
|
|
|
1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-11-24 22:02:58 +03:00
|
|
|
JSON_ASSERT(byte < utf8d.size());
|
2019-03-17 02:27:44 +03:00
|
|
|
const std::uint8_t type = utf8d[byte];
|
2017-08-14 19:04:51 +03:00
|
|
|
|
2018-01-16 22:42:00 +03:00
|
|
|
codep = (state != UTF8_ACCEPT)
|
2019-03-17 02:27:44 +03:00
|
|
|
? (byte & 0x3fu) | (codep << 6u)
|
|
|
|
|
: (0xFFu >> type) & (byte);
|
2017-08-14 19:04:51 +03:00
|
|
|
|
2020-03-23 11:08:47 +03:00
|
|
|
std::size_t index = 256u + static_cast<size_t>(state) * 16u + static_cast<size_t>(type);
|
2020-07-06 13:22:31 +03:00
|
|
|
JSON_ASSERT(index < 400);
|
2020-03-23 11:08:47 +03:00
|
|
|
state = utf8d[index];
|
2018-01-16 22:42:00 +03:00
|
|
|
return state;
|
2017-08-14 19:04:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
/// the output of the serializer
|
|
|
|
|
output_adapter_t<char> o = nullptr;
|
|
|
|
|
|
2018-01-16 22:42:00 +03:00
|
|
|
/// string buffer
|
|
|
|
|
std::array<char, 512> string_buffer{{}};
|
|
|
|
|
|
2017-08-14 19:04:51 +03:00
|
|
|
/// the indentation character
|
|
|
|
|
const char indent_char;
|
|
|
|
|
/// the indentation string
|
|
|
|
|
string_t indent_string;
|
2018-10-21 12:49:37 +03:00
|
|
|
|
|
|
|
|
/// error_handler how to react on decoding errors
|
|
|
|
|
const error_handler_t error_handler;
|
2017-08-14 19:04:51 +03:00
|
|
|
};
|
2018-10-07 19:39:18 +03:00
|
|
|
} // namespace detail
|
|
|
|
|
} // namespace nlohmann
|