Unverified Commit 9e5d901f authored by Niels Lohmann's avatar 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}
......
...@@ -90,6 +90,27 @@ class binary_reader ...@@ -90,6 +90,27 @@ class binary_reader
return res; return res;
} }
/*!
@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
...@@ -751,6 +772,16 @@ class binary_reader ...@@ -751,6 +772,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
...@@ -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
...@@ -1051,6 +1096,230 @@ class binary_reader ...@@ -1051,6 +1096,230 @@ class binary_reader
return result; return result;
} }
/*!
@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
......
This diff is collapsed.
...@@ -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);
}
/// @} /// @}
////////////////////////// //////////////////////////
......
This diff is collapsed.
...@@ -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)
......
This diff is collapsed.
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