Unverified Commit 9e5d901f by Niels Lohmann

Merge branch 'feature/ubjson' into develop

parents 327b8bb0 92f72950
...@@ -262,6 +262,7 @@ json.exception.out_of_range.403 | key 'foo' not found | The provided key was not ...@@ -262,6 +262,7 @@ json.exception.out_of_range.403 | key 'foo' not found | The provided key was not
json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved.
json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value.
json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF.
json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON only supports integers numbers up to 9223372036854775807. |
@liveexample{The following code shows how an `out_of_range` exception can be @liveexample{The following code shows how an `out_of_range` exception can be
caught.,out_of_range} caught.,out_of_range}
......
...@@ -91,6 +91,27 @@ class binary_reader ...@@ -91,6 +91,27 @@ class binary_reader
} }
/*! /*!
@brief create a JSON value from UBJSON input
@param[in] strict whether to expect the input to be consumed completed
@return JSON value created from UBJSON input
@throw parse_error.110 if input ended unexpectedly or the end of file was
not reached when @a strict was set to true
@throw parse_error.112 if unsupported byte was read
*/
BasicJsonType parse_ubjson(const bool strict)
{
const auto res = parse_ubjson_internal();
if (strict)
{
get_ignore_noop();
check_eof(true);
}
return res;
}
/*!
@brief determine system byte order @brief determine system byte order
@return true if and only if system's byte order is little endian @return true if and only if system's byte order is little endian
...@@ -752,6 +773,16 @@ class binary_reader ...@@ -752,6 +773,16 @@ class binary_reader
} }
/*! /*!
@param[in] get_char whether a new character should be retrieved from the
input (true, default) or whether the last read
character should be considered instead
*/
BasicJsonType parse_ubjson_internal(const bool get_char = true)
{
return get_ubjson_value(get_char ? get_ignore_noop() : current);
}
/*!
@brief get next character from the input @brief get next character from the input
This function provides the interface to the used input adapter. It does This function provides the interface to the used input adapter. It does
...@@ -766,6 +797,20 @@ class binary_reader ...@@ -766,6 +797,20 @@ class binary_reader
return (current = ia->get_character()); return (current = ia->get_character());
} }
/*!
@return character read from the input after ignoring all 'N' entries
*/
int get_ignore_noop()
{
do
{
get();
}
while (current == 'N');
return current;
}
/* /*
@brief read a number from the input @brief read a number from the input
...@@ -1052,6 +1097,230 @@ class binary_reader ...@@ -1052,6 +1097,230 @@ class binary_reader
} }
/*! /*!
@brief reads a UBJSON string
This function is either called after reading the 'S' byte explicitly
indicating a string, or in case of an object key where the 'S' byte can be
left out.
@param[in] get_char whether a new character should be retrieved from the
input (true, default) or whether the last read
character should be considered instead
@return string
@throw parse_error.110 if input ended
@throw parse_error.113 if an unexpected byte is read
*/
std::string get_ubjson_string(const bool get_char = true)
{
if (get_char)
{
get(); // TODO: may we ignore N here?
}
check_eof();
switch (current)
{
case 'U':
return get_string(get_number<uint8_t>());
case 'i':
return get_string(get_number<int8_t>());
case 'I':
return get_string(get_number<int16_t>());
case 'l':
return get_string(get_number<int32_t>());
case 'L':
return get_string(get_number<int64_t>());
default:
std::stringstream ss;
ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current;
JSON_THROW(parse_error::create(113, chars_read,
"expected a UBJSON string; last byte: 0x" + ss.str()));
}
}
/*!
@brief determine the type and size for a container
In the optimized UBJSON format, a type and a size can be provided to allow
for a more compact representation.
@return pair of the size and the type
*/
std::pair<std::size_t, int> get_ubjson_size_type()
{
std::size_t sz = std::string::npos;
int tc = 0;
get_ignore_noop();
if (current == '$')
{
tc = get(); // must not ignore 'N', because 'N' maybe the type
check_eof();
get_ignore_noop();
if (current != '#')
{
std::stringstream ss;
ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current;
JSON_THROW(parse_error::create(112, chars_read,
"expected '#' after UBJSON type information; last byte: 0x" + ss.str()));
}
sz = parse_ubjson_internal();
}
else if (current == '#')
{
sz = parse_ubjson_internal();
}
return std::make_pair(sz, tc);
}
BasicJsonType get_ubjson_value(const int prefix)
{
switch (prefix)
{
case std::char_traits<char>::eof(): // EOF
JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input"));
case 'T': // true
return true;
case 'F': // false
return false;
case 'Z': // null
return nullptr;
case 'U':
return get_number<uint8_t>();
case 'i':
return get_number<int8_t>();
case 'I':
return get_number<int16_t>();
case 'l':
return get_number<int32_t>();
case 'L':
return get_number<int64_t>();
case 'd':
return get_number<float>();
case 'D':
return get_number<double>();
case 'C': // char
{
get();
check_eof();
if (JSON_UNLIKELY(current > 127))
{
std::stringstream ss;
ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current;
JSON_THROW(parse_error::create(113, chars_read,
"byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + ss.str()));
}
return std::string(1, static_cast<char>(current));
}
case 'S': // string
return get_ubjson_string();
case '[': // array
return get_ubjson_array();
case '{': // object
return get_ubjson_object();
default: // anything else
std::stringstream ss;
ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current;
JSON_THROW(parse_error::create(112, chars_read,
"error reading UBJSON; last byte: 0x" + ss.str()));
}
}
BasicJsonType get_ubjson_array()
{
BasicJsonType result = value_t::array;
const auto size_and_type = get_ubjson_size_type();
if (size_and_type.first != std::string::npos)
{
if (size_and_type.second != 0)
{
if (size_and_type.second != 'N')
std::generate_n(std::back_inserter(*result.m_value.array),
size_and_type.first, [this, size_and_type]()
{
return get_ubjson_value(size_and_type.second);
});
}
else
{
std::generate_n(std::back_inserter(*result.m_value.array),
size_and_type.first, [this]()
{
return parse_ubjson_internal();
});
}
}
else
{
while (current != ']')
{
result.push_back(parse_ubjson_internal(false));
get_ignore_noop();
}
}
return result;
}
BasicJsonType get_ubjson_object()
{
BasicJsonType result = value_t::object;
const auto size_and_type = get_ubjson_size_type();
if (size_and_type.first != std::string::npos)
{
if (size_and_type.second != 0)
{
std::generate_n(std::inserter(*result.m_value.object,
result.m_value.object->end()),
size_and_type.first, [this, size_and_type]()
{
auto key = get_ubjson_string();
auto val = get_ubjson_value(size_and_type.second);
return std::make_pair(std::move(key), std::move(val));
});
}
else
{
std::generate_n(std::inserter(*result.m_value.object,
result.m_value.object->end()),
size_and_type.first, [this]()
{
auto key = get_ubjson_string();
auto val = parse_ubjson_internal();
return std::make_pair(std::move(key), std::move(val));
});
}
}
else
{
while (current != '}')
{
auto key = get_ubjson_string(false);
result[std::move(key)] = parse_ubjson_internal();
get_ignore_noop();
}
}
return result;
}
/*!
@brief check if input ended @brief check if input ended
@throw parse_error.110 if input ended @throw parse_error.110 if input ended
*/ */
......
...@@ -6633,6 +6633,26 @@ class basic_json ...@@ -6633,6 +6633,26 @@ class basic_json
binary_writer<char>(o).write_msgpack(j); binary_writer<char>(o).write_msgpack(j);
} }
static std::vector<uint8_t> to_ubjson(const basic_json& j,
const bool use_size = false, const bool use_type = false)
{
std::vector<uint8_t> result;
to_ubjson(j, result, use_size, use_type);
return result;
}
static void to_ubjson(const basic_json& j, detail::output_adapter<uint8_t> o,
const bool use_size = false, const bool use_type = false)
{
binary_writer<uint8_t>(o).write_ubjson(j, use_size, use_type);
}
static void to_ubjson(const basic_json& j, detail::output_adapter<char> o,
const bool use_size = false, const bool use_type = false)
{
binary_writer<char>(o).write_ubjson(j, use_size, use_type);
}
/*! /*!
@brief create a JSON value from an input in CBOR format @brief create a JSON value from an input in CBOR format
...@@ -6827,6 +6847,19 @@ class basic_json ...@@ -6827,6 +6847,19 @@ class basic_json
return binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).parse_msgpack(strict); return binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).parse_msgpack(strict);
} }
static basic_json from_ubjson(detail::input_adapter i,
const bool strict = true)
{
return binary_reader(i).parse_ubjson(strict);
}
template<typename A1, typename A2,
detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>
static basic_json from_ubjson(A1 && a1, A2 && a2, const bool strict = true)
{
return binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).parse_ubjson(strict);
}
/// @} /// @}
////////////////////////// //////////////////////////
......
...@@ -40,6 +40,7 @@ SOURCES = src/unit.cpp \ ...@@ -40,6 +40,7 @@ SOURCES = src/unit.cpp \
src/unit-regression.cpp \ src/unit-regression.cpp \
src/unit-serialization.cpp \ src/unit-serialization.cpp \
src/unit-testsuites.cpp \ src/unit-testsuites.cpp \
src/unit-ubjson.cpp \
src/unit-unicode.cpp src/unit-unicode.cpp
OBJECTS = $(SOURCES:.cpp=.o) OBJECTS = $(SOURCES:.cpp=.o)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment