From c5711f30729c05d4f7f0b99de9db1bc41f173679 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 16 Mar 2017 18:39:33 +0100 Subject: [PATCH] :construction: a lot of minor changes - Removed unused headers. - Added override where needed. - Added description for parse_error.113 exception. - Fixed some conversion warnings. - Integrated cbor_expect_string function for CBOR maps. - Added documentation on the supported CBOR/MessagePack features. - Added test to check all initial bytes for CBOR input. --- src/json.hpp | 137 +++++++++++++++++++++++++++--- src/json.hpp.re2c | 137 +++++++++++++++++++++++++++--- test/src/unit-cbor.cpp | 109 ++++++++++++++++++++---- test/src/unit-class_lexer.cpp | 2 +- test/src/unit-class_parser.cpp | 10 +-- test/src/unit-concepts.cpp | 4 +- test/src/unit-element_access1.cpp | 8 +- test/src/unit-element_access2.cpp | 16 ++-- test/src/unit-msgpack.cpp | 40 ++++----- test/src/unit-regression.cpp | 12 +-- 10 files changed, 385 insertions(+), 90 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index cbf12dc5d..339cd095a 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -32,7 +32,6 @@ SOFTWARE. #include // all_of, copy, fill, find, for_each, none_of, remove, reverse, transform #include // array #include // assert -#include // isdigit #include // and, not, or #include // lconv, localeconv #include // isfinite, labs, ldexp, signbit @@ -43,7 +42,6 @@ SOFTWARE. #include // forward_list #include // function, hash, less #include // initializer_list -#include // setw #include // istream, ostream #include // advance, begin, back_inserter, bidirectional_iterator_tag, distance, end, inserter, iterator, iterator_traits, next, random_access_iterator_tag, reverse_iterator #include // numeric_limits @@ -130,7 +128,7 @@ class exception : public std::exception {} /// returns the explanatory string - virtual const char* what() const noexcept + virtual const char* what() const noexcept override { return what_arg.c_str(); } @@ -174,7 +172,7 @@ json.exception.parse_error.109 | parse error: array index 'one' is not a number json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. json.exception.parse_error.111 | parse error: bad input stream | Parsing CBOR or MessagePack from an input stream where the [`badbit` or `failbit`](http://en.cppreference.com/w/cpp/io/ios_base/iostate) is set. json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xf8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. -json.exception.parse_error.113 | | While parsing a map key, a value that is not a string has been read. +json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read. @since version 3.0.0 */ @@ -6468,7 +6466,7 @@ class basic_json auto i = val.m_value.object->cbegin(); for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) { - o.write(indent_string.c_str(), new_indent); + o.write(indent_string.c_str(), static_cast(new_indent)); o.put('\"'); dump_escaped(i->first); o.write("\": ", 3); @@ -6478,14 +6476,14 @@ class basic_json // last element assert(i != val.m_value.object->cend()); - o.write(indent_string.c_str(), new_indent); + o.write(indent_string.c_str(), static_cast(new_indent)); o.put('\"'); dump_escaped(i->first); o.write("\": ", 3); dump(i->second, true, indent_step, new_indent); o.put('\n'); - o.write(indent_string.c_str(), current_indent); + o.write(indent_string.c_str(), static_cast(current_indent)); o.put('}'); } else @@ -6538,18 +6536,18 @@ class basic_json // first n-1 elements for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) { - o.write(indent_string.c_str(), new_indent); + o.write(indent_string.c_str(), static_cast(new_indent)); dump(*i, true, indent_step, new_indent); o.write(",\n", 2); } // last element assert(not val.m_value.array->empty()); - o.write(indent_string.c_str(), new_indent); + o.write(indent_string.c_str(), static_cast(new_indent)); dump(val.m_value.array->back(), true, indent_step, new_indent); o.put('\n'); - o.write(indent_string.c_str(), current_indent); + o.write(indent_string.c_str(), static_cast(current_indent)); o.put(']'); } else @@ -7848,7 +7846,7 @@ class basic_json const auto N = j.m_value.string->size(); if (N <= 0x17) { - v.push_back(0x60 + static_cast(N)); // 1 byte for string + size + v.push_back(static_cast(0x60 + N)); // 1 byte for string + size } else if (N <= 0xff) { @@ -7884,7 +7882,7 @@ class basic_json const auto N = j.m_value.array->size(); if (N <= 0x17) { - v.push_back(0x80 + static_cast(N)); // 1 byte for array + size + v.push_back(static_cast(0x80 + N)); // 1 byte for array + size } else if (N <= 0xff) { @@ -7922,7 +7920,7 @@ class basic_json const auto N = j.m_value.object->size(); if (N <= 0x17) { - v.push_back(0xa0 + static_cast(N)); // 1 byte for object + size + v.push_back(static_cast(0xa0 + N)); // 1 byte for object + size } else if (N <= 0xff) { @@ -8653,6 +8651,7 @@ class basic_json const auto len = static_cast(v[current_idx] - 0xa0); for (size_t i = 0; i < len; ++i) { + cbor_expect_string(v, idx); std::string key = from_cbor_internal(v, idx); result[key] = from_cbor_internal(v, idx); } @@ -8666,6 +8665,7 @@ class basic_json idx += 1; // skip 1 size byte for (size_t i = 0; i < len; ++i) { + cbor_expect_string(v, idx); std::string key = from_cbor_internal(v, idx); result[key] = from_cbor_internal(v, idx); } @@ -8679,6 +8679,7 @@ class basic_json idx += 2; // skip 2 size bytes for (size_t i = 0; i < len; ++i) { + cbor_expect_string(v, idx); std::string key = from_cbor_internal(v, idx); result[key] = from_cbor_internal(v, idx); } @@ -8692,6 +8693,7 @@ class basic_json idx += 4; // skip 4 size bytes for (size_t i = 0; i < len; ++i) { + cbor_expect_string(v, idx); std::string key = from_cbor_internal(v, idx); result[key] = from_cbor_internal(v, idx); } @@ -8705,6 +8707,7 @@ class basic_json idx += 8; // skip 8 size bytes for (size_t i = 0; i < len; ++i) { + cbor_expect_string(v, idx); std::string key = from_cbor_internal(v, idx); result[key] = from_cbor_internal(v, idx); } @@ -8716,6 +8719,7 @@ class basic_json basic_json result = value_t::object; while (check_length(v.size(), 1, idx), v[idx] != 0xff) { + cbor_expect_string(v, idx); std::string key = from_cbor_internal(v, idx); result[key] = from_cbor_internal(v, idx); } @@ -8864,6 +8868,9 @@ class basic_json - float 32 (0xca) - fixext 1 - fixext 16 (0xd4..0xd8) + @note Any MessagePack output created @ref to_msgpack can be successfully + parsed by @ref from_msgpack. + @param[in] j JSON value to serialize @return MessagePack serialization as byte vector @@ -8892,6 +8899,46 @@ class basic_json Deserializes a given byte vector @a v to a JSON value using the MessagePack serialization format. + The library maps MessagePack types to JSON value types as follows: + + MessagePack type | JSON value type | first byte + ---------------- | --------------- | ---------- + positive fixint | number_unsigned | 0x00..0x7f + fixmap | object | 0x80..0x8f + fixarray | array | 0x90..0x9f + fixstr | string | 0xa0..0xbf + nil | `null` | 0xc0 + false | `false` | 0xc2 + true | `true` | 0xc3 + float 32 | number_float | 0xca + float 64 | number_float | 0xcb + uint 8 | number_unsigned | 0xcc + uint 16 | number_unsigned | 0xcd + uint 32 | number_unsigned | 0xce + uint 64 | number_unsigned | 0xcf + int 8 | number_integer | 0xd0 + int 16 | number_integer | 0xd1 + int 32 | number_integer | 0xd2 + int 64 | number_integer | 0xd3 + str 8 | string | 0xd9 + str 16 | string | 0xda + str 32 | string | 0xdb + array 16 | array | 0xdc + array 32 | array | 0xdd + map 16 | object | 0xde + map 32 | object | 0xdf + negative fixint | number_integer | 0xe0-0xff + + @warning The mapping is **incomplete** in the sense that not all + MessagePack types can be converted to a JSON value. The following + MessagePack types are not supported and will yield parse errors: + - bin 8 - bin 32 (0xc4..0xc6) + - ext 8 - ext 32 (0xc7..0xc9) + - fixext 1 - fixext 16 (0xd4..0xd8) + + @note Any MessagePack output created @ref to_msgpack can be successfully + parsed by @ref from_msgpack. + @param[in] v a byte vector in MessagePack format @param[in] start_index the index to start reading from @a v (0 by default) @return deserialized JSON value @@ -8899,6 +8946,7 @@ class basic_json @throw parse_error.110 if the given vector ends prematurely @throw parse_error.112 if unsupported features from MessagePack were used in the given vector @a v or if the input is not valid MessagePack + @throw parse_error.113 if a string was expected as map key, but not found @complexity Linear in the size of the byte vector @a v. @@ -9014,6 +9062,66 @@ class basic_json Deserializes a given byte vector @a v to a JSON value using the CBOR (Concise Binary Object Representation) serialization format. + The library maps CBOR types to JSON value types as follows: + + CBOR type | JSON value type | first byte + ---------------------- | --------------- | ---------- + Integer | number_unsigned | 0x00..0x17 + Unsigned integer | number_unsigned | 0x18 + Unsigned integer | number_unsigned | 0x19 + Unsigned integer | number_unsigned | 0x1a + Unsigned integer | number_unsigned | 0x1b + Negative integer | number_integer | 0x20..0x37 + Negative integer | number_integer | 0x38 + Negative integer | number_integer | 0x39 + Negative integer | number_integer | 0x3a + Negative integer | number_integer | 0x3b + Negative integer | number_integer | 0x40..0x57 + UTF-8 string | string | 0x60..0x77 + UTF-8 string | string | 0x78 + UTF-8 string | string | 0x79 + UTF-8 string | string | 0x7a + UTF-8 string | string | 0x7b + UTF-8 string | string | 0x7f + array | array | 0x80..0x97 + array | array | 0x98 + array | array | 0x99 + array | array | 0x9a + array | array | 0x9b + array | array | 0x9f + map | object | 0xa0..0xb7 + map | object | 0xb8 + map | object | 0xb9 + map | object | 0xba + map | object | 0xbb + map | object | 0xbf + False | `false` | 0xf4 + True | `true` | 0xf5 + Nill | `null` | 0xf6 + Half-Precision Float | number_float | 0xf9 + Single-Precision Float | number_float | 0xfa + Double-Precision Float | number_float | 0xfb + + @warning The mapping is **incomplete** in the sense that not all CBOR + types can be converted to a JSON value. The following CBOR types + are not supported and will yield parse errors (parse_error.112): + - byte strings (0x40..0x5f) + - date/time (0xc0..0xc1) + - bignum (0xc2..0xc3) + - decimal fraction (0xc4) + - bigfloat (0xc5) + - tagged items (0xc6..0xd4, 0xd8..0xdb) + - expected conversions (0xd5..0xd7) + - simple values (0xe0..0xf3, 0xf8) + - undefined (0xf7) + + @warning CBOR allows map keys of any type, whereas JSON only allows + strings as keys in object values. Therefore, CBOR maps with keys + other than UTF-8 strings are rejected (parse_error.113). + + @note Any CBOR output created @ref to_cbor can be successfully parsed by + @ref from_cbor. + @param[in] v a byte vector in CBOR format @param[in] start_index the index to start reading from @a v (0 by default) @return deserialized JSON value @@ -9021,6 +9129,7 @@ class basic_json @throw parse_error.110 if the given vector ends prematurely @throw parse_error.112 if unsupported features from CBOR were used in the given vector @a v or if the input is not valid CBOR + @throw parse_error.113 if a string was expected as map key, but not found @complexity Linear in the size of the byte vector @a v. @@ -12423,7 +12532,7 @@ basic_json_parser_74: reference_token.end(), [](const char x) { - return std::isdigit(x); + return (x >= '0' and x <= '9'); }); // change value to array for numbers or "-" or to object diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index c4d142b7d..5203fb8be 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -32,7 +32,6 @@ SOFTWARE. #include // all_of, copy, fill, find, for_each, none_of, remove, reverse, transform #include // array #include // assert -#include // isdigit #include // and, not, or #include // lconv, localeconv #include // isfinite, labs, ldexp, signbit @@ -43,7 +42,6 @@ SOFTWARE. #include // forward_list #include // function, hash, less #include // initializer_list -#include // setw #include // istream, ostream #include // advance, begin, back_inserter, bidirectional_iterator_tag, distance, end, inserter, iterator, iterator_traits, next, random_access_iterator_tag, reverse_iterator #include // numeric_limits @@ -130,7 +128,7 @@ class exception : public std::exception {} /// returns the explanatory string - virtual const char* what() const noexcept + virtual const char* what() const noexcept override { return what_arg.c_str(); } @@ -174,7 +172,7 @@ json.exception.parse_error.109 | parse error: array index 'one' is not a number json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. json.exception.parse_error.111 | parse error: bad input stream | Parsing CBOR or MessagePack from an input stream where the [`badbit` or `failbit`](http://en.cppreference.com/w/cpp/io/ios_base/iostate) is set. json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xf8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. -json.exception.parse_error.113 | | While parsing a map key, a value that is not a string has been read. +json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read. @since version 3.0.0 */ @@ -6468,7 +6466,7 @@ class basic_json auto i = val.m_value.object->cbegin(); for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) { - o.write(indent_string.c_str(), new_indent); + o.write(indent_string.c_str(), static_cast(new_indent)); o.put('\"'); dump_escaped(i->first); o.write("\": ", 3); @@ -6478,14 +6476,14 @@ class basic_json // last element assert(i != val.m_value.object->cend()); - o.write(indent_string.c_str(), new_indent); + o.write(indent_string.c_str(), static_cast(new_indent)); o.put('\"'); dump_escaped(i->first); o.write("\": ", 3); dump(i->second, true, indent_step, new_indent); o.put('\n'); - o.write(indent_string.c_str(), current_indent); + o.write(indent_string.c_str(), static_cast(current_indent)); o.put('}'); } else @@ -6538,18 +6536,18 @@ class basic_json // first n-1 elements for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) { - o.write(indent_string.c_str(), new_indent); + o.write(indent_string.c_str(), static_cast(new_indent)); dump(*i, true, indent_step, new_indent); o.write(",\n", 2); } // last element assert(not val.m_value.array->empty()); - o.write(indent_string.c_str(), new_indent); + o.write(indent_string.c_str(), static_cast(new_indent)); dump(val.m_value.array->back(), true, indent_step, new_indent); o.put('\n'); - o.write(indent_string.c_str(), current_indent); + o.write(indent_string.c_str(), static_cast(current_indent)); o.put(']'); } else @@ -7848,7 +7846,7 @@ class basic_json const auto N = j.m_value.string->size(); if (N <= 0x17) { - v.push_back(0x60 + static_cast(N)); // 1 byte for string + size + v.push_back(static_cast(0x60 + N)); // 1 byte for string + size } else if (N <= 0xff) { @@ -7884,7 +7882,7 @@ class basic_json const auto N = j.m_value.array->size(); if (N <= 0x17) { - v.push_back(0x80 + static_cast(N)); // 1 byte for array + size + v.push_back(static_cast(0x80 + N)); // 1 byte for array + size } else if (N <= 0xff) { @@ -7922,7 +7920,7 @@ class basic_json const auto N = j.m_value.object->size(); if (N <= 0x17) { - v.push_back(0xa0 + static_cast(N)); // 1 byte for object + size + v.push_back(static_cast(0xa0 + N)); // 1 byte for object + size } else if (N <= 0xff) { @@ -8653,6 +8651,7 @@ class basic_json const auto len = static_cast(v[current_idx] - 0xa0); for (size_t i = 0; i < len; ++i) { + cbor_expect_string(v, idx); std::string key = from_cbor_internal(v, idx); result[key] = from_cbor_internal(v, idx); } @@ -8666,6 +8665,7 @@ class basic_json idx += 1; // skip 1 size byte for (size_t i = 0; i < len; ++i) { + cbor_expect_string(v, idx); std::string key = from_cbor_internal(v, idx); result[key] = from_cbor_internal(v, idx); } @@ -8679,6 +8679,7 @@ class basic_json idx += 2; // skip 2 size bytes for (size_t i = 0; i < len; ++i) { + cbor_expect_string(v, idx); std::string key = from_cbor_internal(v, idx); result[key] = from_cbor_internal(v, idx); } @@ -8692,6 +8693,7 @@ class basic_json idx += 4; // skip 4 size bytes for (size_t i = 0; i < len; ++i) { + cbor_expect_string(v, idx); std::string key = from_cbor_internal(v, idx); result[key] = from_cbor_internal(v, idx); } @@ -8705,6 +8707,7 @@ class basic_json idx += 8; // skip 8 size bytes for (size_t i = 0; i < len; ++i) { + cbor_expect_string(v, idx); std::string key = from_cbor_internal(v, idx); result[key] = from_cbor_internal(v, idx); } @@ -8716,6 +8719,7 @@ class basic_json basic_json result = value_t::object; while (check_length(v.size(), 1, idx), v[idx] != 0xff) { + cbor_expect_string(v, idx); std::string key = from_cbor_internal(v, idx); result[key] = from_cbor_internal(v, idx); } @@ -8864,6 +8868,9 @@ class basic_json - float 32 (0xca) - fixext 1 - fixext 16 (0xd4..0xd8) + @note Any MessagePack output created @ref to_msgpack can be successfully + parsed by @ref from_msgpack. + @param[in] j JSON value to serialize @return MessagePack serialization as byte vector @@ -8892,6 +8899,46 @@ class basic_json Deserializes a given byte vector @a v to a JSON value using the MessagePack serialization format. + The library maps MessagePack types to JSON value types as follows: + + MessagePack type | JSON value type | first byte + ---------------- | --------------- | ---------- + positive fixint | number_unsigned | 0x00..0x7f + fixmap | object | 0x80..0x8f + fixarray | array | 0x90..0x9f + fixstr | string | 0xa0..0xbf + nil | `null` | 0xc0 + false | `false` | 0xc2 + true | `true` | 0xc3 + float 32 | number_float | 0xca + float 64 | number_float | 0xcb + uint 8 | number_unsigned | 0xcc + uint 16 | number_unsigned | 0xcd + uint 32 | number_unsigned | 0xce + uint 64 | number_unsigned | 0xcf + int 8 | number_integer | 0xd0 + int 16 | number_integer | 0xd1 + int 32 | number_integer | 0xd2 + int 64 | number_integer | 0xd3 + str 8 | string | 0xd9 + str 16 | string | 0xda + str 32 | string | 0xdb + array 16 | array | 0xdc + array 32 | array | 0xdd + map 16 | object | 0xde + map 32 | object | 0xdf + negative fixint | number_integer | 0xe0-0xff + + @warning The mapping is **incomplete** in the sense that not all + MessagePack types can be converted to a JSON value. The following + MessagePack types are not supported and will yield parse errors: + - bin 8 - bin 32 (0xc4..0xc6) + - ext 8 - ext 32 (0xc7..0xc9) + - fixext 1 - fixext 16 (0xd4..0xd8) + + @note Any MessagePack output created @ref to_msgpack can be successfully + parsed by @ref from_msgpack. + @param[in] v a byte vector in MessagePack format @param[in] start_index the index to start reading from @a v (0 by default) @return deserialized JSON value @@ -8899,6 +8946,7 @@ class basic_json @throw parse_error.110 if the given vector ends prematurely @throw parse_error.112 if unsupported features from MessagePack were used in the given vector @a v or if the input is not valid MessagePack + @throw parse_error.113 if a string was expected as map key, but not found @complexity Linear in the size of the byte vector @a v. @@ -9014,6 +9062,66 @@ class basic_json Deserializes a given byte vector @a v to a JSON value using the CBOR (Concise Binary Object Representation) serialization format. + The library maps CBOR types to JSON value types as follows: + + CBOR type | JSON value type | first byte + ---------------------- | --------------- | ---------- + Integer | number_unsigned | 0x00..0x17 + Unsigned integer | number_unsigned | 0x18 + Unsigned integer | number_unsigned | 0x19 + Unsigned integer | number_unsigned | 0x1a + Unsigned integer | number_unsigned | 0x1b + Negative integer | number_integer | 0x20..0x37 + Negative integer | number_integer | 0x38 + Negative integer | number_integer | 0x39 + Negative integer | number_integer | 0x3a + Negative integer | number_integer | 0x3b + Negative integer | number_integer | 0x40..0x57 + UTF-8 string | string | 0x60..0x77 + UTF-8 string | string | 0x78 + UTF-8 string | string | 0x79 + UTF-8 string | string | 0x7a + UTF-8 string | string | 0x7b + UTF-8 string | string | 0x7f + array | array | 0x80..0x97 + array | array | 0x98 + array | array | 0x99 + array | array | 0x9a + array | array | 0x9b + array | array | 0x9f + map | object | 0xa0..0xb7 + map | object | 0xb8 + map | object | 0xb9 + map | object | 0xba + map | object | 0xbb + map | object | 0xbf + False | `false` | 0xf4 + True | `true` | 0xf5 + Nill | `null` | 0xf6 + Half-Precision Float | number_float | 0xf9 + Single-Precision Float | number_float | 0xfa + Double-Precision Float | number_float | 0xfb + + @warning The mapping is **incomplete** in the sense that not all CBOR + types can be converted to a JSON value. The following CBOR types + are not supported and will yield parse errors (parse_error.112): + - byte strings (0x40..0x5f) + - date/time (0xc0..0xc1) + - bignum (0xc2..0xc3) + - decimal fraction (0xc4) + - bigfloat (0xc5) + - tagged items (0xc6..0xd4, 0xd8..0xdb) + - expected conversions (0xd5..0xd7) + - simple values (0xe0..0xf3, 0xf8) + - undefined (0xf7) + + @warning CBOR allows map keys of any type, whereas JSON only allows + strings as keys in object values. Therefore, CBOR maps with keys + other than UTF-8 strings are rejected (parse_error.113). + + @note Any CBOR output created @ref to_cbor can be successfully parsed by + @ref from_cbor. + @param[in] v a byte vector in CBOR format @param[in] start_index the index to start reading from @a v (0 by default) @return deserialized JSON value @@ -9021,6 +9129,7 @@ class basic_json @throw parse_error.110 if the given vector ends prematurely @throw parse_error.112 if unsupported features from CBOR were used in the given vector @a v or if the input is not valid CBOR + @throw parse_error.113 if a string was expected as map key, but not found @complexity Linear in the size of the byte vector @a v. @@ -11456,7 +11565,7 @@ class basic_json reference_token.end(), [](const char x) { - return std::isdigit(x); + return (x >= '0' and x <= '9'); }); // change value to array for numbers or "-" or to object diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp index b4b539754..f1609f4f7 100644 --- a/test/src/unit-cbor.cpp +++ b/test/src/unit-cbor.cpp @@ -130,14 +130,14 @@ TEST_CASE("CBOR") // check individual bytes CHECK(result[0] == 0x3b); - uint64_t restored = static_cast((static_cast(result[1]) << 070) + + uint64_t restored = (static_cast(result[1]) << 070) + (static_cast(result[2]) << 060) + (static_cast(result[3]) << 050) + (static_cast(result[4]) << 040) + (static_cast(result[5]) << 030) + (static_cast(result[6]) << 020) + (static_cast(result[7]) << 010) + - static_cast(result[8])); + static_cast(result[8]); CHECK(restored == positive); CHECK(-1 - static_cast(restored) == i); @@ -182,10 +182,10 @@ TEST_CASE("CBOR") // check individual bytes CHECK(result[0] == 0x3a); - uint32_t restored = static_cast((static_cast(result[1]) << 030) + + uint32_t restored = (static_cast(result[1]) << 030) + (static_cast(result[2]) << 020) + (static_cast(result[3]) << 010) + - static_cast(result[4])); + static_cast(result[4]); CHECK(restored == positive); CHECK(-1ll - restored == i); @@ -220,7 +220,7 @@ TEST_CASE("CBOR") // check individual bytes CHECK(result[0] == 0x39); - uint16_t restored = static_cast(result[1]) * 256 + static_cast(result[2]); + uint16_t restored = static_cast(static_cast(result[1]) * 256 + static_cast(result[2])); CHECK(restored == positive); CHECK(-1 - restored == i); @@ -289,7 +289,7 @@ TEST_CASE("CBOR") // create expected byte vector std::vector expected; - expected.push_back(0x20 - 1 - static_cast(i)); + expected.push_back(static_cast(0x20 - 1 - static_cast(i))); // compare result + size const auto result = json::to_cbor(j); @@ -392,7 +392,7 @@ TEST_CASE("CBOR") // check individual bytes CHECK(result[0] == 0x19); - uint16_t restored = static_cast(result[1]) * 256 + static_cast(result[2]); + uint16_t restored = static_cast(static_cast(result[1]) * 256 + static_cast(result[2])); CHECK(restored == i); // roundtrip @@ -431,10 +431,10 @@ TEST_CASE("CBOR") // check individual bytes CHECK(result[0] == 0x1a); - uint32_t restored = static_cast((static_cast(result[1]) << 030) + + uint32_t restored = (static_cast(result[1]) << 030) + (static_cast(result[2]) << 020) + (static_cast(result[3]) << 010) + - static_cast(result[4])); + static_cast(result[4]); CHECK(restored == i); // roundtrip @@ -477,14 +477,14 @@ TEST_CASE("CBOR") // check individual bytes CHECK(result[0] == 0x1b); - uint64_t restored = static_cast((static_cast(result[1]) << 070) + + uint64_t restored = (static_cast(result[1]) << 070) + (static_cast(result[2]) << 060) + (static_cast(result[3]) << 050) + (static_cast(result[4]) << 040) + (static_cast(result[5]) << 030) + (static_cast(result[6]) << 020) + (static_cast(result[7]) << 010) + - static_cast(result[8])); + static_cast(result[8]); CHECK(restored == i); // roundtrip @@ -616,7 +616,7 @@ TEST_CASE("CBOR") // check individual bytes CHECK(result[0] == 0x19); - uint16_t restored = static_cast(result[1]) * 256 + static_cast(result[2]); + uint16_t restored = static_cast(static_cast(result[1]) * 256 + static_cast(result[2])); CHECK(restored == i); // roundtrip @@ -654,10 +654,10 @@ TEST_CASE("CBOR") // check individual bytes CHECK(result[0] == 0x1a); - uint32_t restored = static_cast((static_cast(result[1]) << 030) + + uint32_t restored = (static_cast(result[1]) << 030) + (static_cast(result[2]) << 020) + (static_cast(result[3]) << 010) + - static_cast(result[4])); + static_cast(result[4]); CHECK(restored == i); // roundtrip @@ -699,14 +699,14 @@ TEST_CASE("CBOR") // check individual bytes CHECK(result[0] == 0x1b); - uint64_t restored = static_cast((static_cast(result[1]) << 070) + + uint64_t restored = (static_cast(result[1]) << 070) + (static_cast(result[2]) << 060) + (static_cast(result[3]) << 050) + (static_cast(result[4]) << 040) + (static_cast(result[5]) << 030) + (static_cast(result[6]) << 020) + (static_cast(result[7]) << 010) + - static_cast(result[8])); + static_cast(result[8]); CHECK(restored == i); // roundtrip @@ -1538,6 +1538,83 @@ TEST_CASE("CBOR roundtrips", "[hide]") } } +TEST_CASE("all first bytes", "[!throws]") +{ + // these bytes will fail immediately with exception parse_error.112 + std::set unsupported = + { + //// types not supported by this library + + // byte strings + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + // byte strings + 0x58, 0x59, 0x5a, 0x5b, 0x5f, + // date/time + 0xc0, 0xc1, + // bignum + 0xc2, 0xc3, + // decimal fracion + 0xc4, + // bigfloat + 0xc5, + // tagged item + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd8, + 0xd9, 0xda, 0xdb, + // expected conversion + 0xd5, 0xd6, 0xd7, + // simple value + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xef, 0xf0, + 0xf1, 0xf2, 0xf3, + 0xf8, + // undefined + 0xf7, + + //// bytes not specified by CBOR + + 0x1c, 0x1d, 0x1e, 0x1f, + 0x3c, 0x3d, 0x3e, 0x3f, + 0x5c, 0x5d, 0x5e, + 0x7c, 0x7d, 0x7e, + 0x9c, 0x9d, 0x9e, + 0xbc, 0xbd, 0xbe, + 0xdc, 0xdd, 0xde, 0xdf, + 0xee, + 0xfc, 0xfe, 0xfd, + + /// break cannot be the first byte + + 0xff + }; + + for (auto i = 0; i < 256; ++i) + { + const auto byte = static_cast(i); + + try + { + json::from_cbor({byte}); + } + catch (const json::parse_error& e) + { + // check that parse_error.112 is only thrown if the + // first byte is in the unsupported set + CAPTURE(e.what()); + if (std::find(unsupported.begin(), unsupported.end(), byte) != unsupported.end()) + { + CHECK(e.id == 112); + } + else + { + CHECK(e.id != 112); + } + } + } +} + TEST_CASE("examples from RFC 7049 Appendix A") { SECTION("numbers") diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp index b9e602b7b..b88a5291c 100644 --- a/test/src/unit-class_lexer.cpp +++ b/test/src/unit-class_lexer.cpp @@ -139,7 +139,7 @@ TEST_CASE("lexer class") for (int c = 1; c < 128; ++c) { // create string from the ASCII code - const auto s = std::string(1, c); + const auto s = std::string(1, static_cast(c)); // store scan() result const auto res = json::lexer(reinterpret_cast(s.c_str()), 1).scan(); diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index 7f2c9a952..2a5c07cfb 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -494,7 +494,7 @@ TEST_CASE("parser class") // invalid escapes for (int c = 1; c < 128; ++c) { - auto s = std::string("\"\\") + std::string(1, c) + "\""; + auto s = std::string("\"\\") + std::string(1, static_cast(c)) + "\""; switch (c) { @@ -574,10 +574,10 @@ TEST_CASE("parser class") std::string s = "\"\\u"; // create a string with the iterated character at each position - auto s1 = s + "000" + std::string(1, c) + "\""; - auto s2 = s + "00" + std::string(1, c) + "0\""; - auto s3 = s + "0" + std::string(1, c) + "00\""; - auto s4 = s + std::string(1, c) + "000\""; + auto s1 = s + "000" + std::string(1, static_cast(c)) + "\""; + auto s2 = s + "00" + std::string(1, static_cast(c)) + "0\""; + auto s3 = s + "0" + std::string(1, static_cast(c)) + "00\""; + auto s4 = s + std::string(1, static_cast(c)) + "000\""; if (valid(c)) { diff --git a/test/src/unit-concepts.cpp b/test/src/unit-concepts.cpp index 79be78cd4..d2c24866a 100644 --- a/test/src/unit-concepts.cpp +++ b/test/src/unit-concepts.cpp @@ -72,8 +72,8 @@ TEST_CASE("concepts") // X::size_type must return an unsigned integer CHECK((std::is_unsigned::value)); // X::size_type can represent any non-negative value of X::difference_type - CHECK(static_cast(std::numeric_limits::max()) <= - static_cast(std::numeric_limits::max())); + CHECK(static_cast(std::numeric_limits::max()) <= + std::numeric_limits::max()); // the expression "X u" has the post-condition "u.empty()" { diff --git a/test/src/unit-element_access1.cpp b/test/src/unit-element_access1.cpp index b8a57af43..eb4a92cbd 100644 --- a/test/src/unit-element_access1.cpp +++ b/test/src/unit-element_access1.cpp @@ -48,7 +48,7 @@ TEST_CASE("element access 1") CHECK(j.at(3) == json(nullptr)); CHECK(j.at(4) == json("string")); CHECK(j.at(5) == json(42.23)); - CHECK(j.at(6) == json(json::object())); + CHECK(j.at(6) == json::object()); CHECK(j.at(7) == json({1, 2, 3})); CHECK(j_const.at(0) == json(1)); @@ -57,7 +57,7 @@ TEST_CASE("element access 1") CHECK(j_const.at(3) == json(nullptr)); CHECK(j_const.at(4) == json("string")); CHECK(j_const.at(5) == json(42.23)); - CHECK(j_const.at(6) == json(json::object())); + CHECK(j_const.at(6) == json::object()); CHECK(j_const.at(7) == json({1, 2, 3})); } @@ -171,7 +171,7 @@ TEST_CASE("element access 1") CHECK(j[3] == json(nullptr)); CHECK(j[4] == json("string")); CHECK(j[5] == json(42.23)); - CHECK(j[6] == json(json::object())); + CHECK(j[6] == json::object()); CHECK(j[7] == json({1, 2, 3})); CHECK(j_const[0] == json(1)); @@ -180,7 +180,7 @@ TEST_CASE("element access 1") CHECK(j_const[3] == json(nullptr)); CHECK(j_const[4] == json("string")); CHECK(j_const[5] == json(42.23)); - CHECK(j_const[6] == json(json::object())); + CHECK(j_const[6] == json::object()); CHECK(j_const[7] == json({1, 2, 3})); } diff --git a/test/src/unit-element_access2.cpp b/test/src/unit-element_access2.cpp index 3533bdcd9..ec03507e5 100644 --- a/test/src/unit-element_access2.cpp +++ b/test/src/unit-element_access2.cpp @@ -48,7 +48,7 @@ TEST_CASE("element access 2") CHECK(j.at("null") == json(nullptr)); CHECK(j.at("string") == json("hello world")); CHECK(j.at("floating") == json(42.23)); - CHECK(j.at("object") == json(json::object())); + CHECK(j.at("object") == json::object()); CHECK(j.at("array") == json({1, 2, 3})); CHECK(j_const.at("integer") == json(1)); @@ -57,7 +57,7 @@ TEST_CASE("element access 2") CHECK(j_const.at("null") == json(nullptr)); CHECK(j_const.at("string") == json("hello world")); CHECK(j_const.at("floating") == json(42.23)); - CHECK(j_const.at("object") == json(json::object())); + CHECK(j_const.at("object") == json::object()); CHECK(j_const.at("array") == json({1, 2, 3})); } @@ -161,7 +161,7 @@ TEST_CASE("element access 2") CHECK(j.value("string", std::string("bar")) == "hello world"); CHECK(j.value("floating", 12.34) == Approx(42.23)); CHECK(j.value("floating", 12) == 42); - CHECK(j.value("object", json({{"foo", "bar"}})) == json(json::object())); + CHECK(j.value("object", json({{"foo", "bar"}})) == json::object()); CHECK(j.value("array", json({10, 100})) == json({1, 2, 3})); CHECK(j_const.value("integer", 2) == 1); @@ -173,7 +173,7 @@ TEST_CASE("element access 2") CHECK(j_const.value("string", std::string("bar")) == "hello world"); CHECK(j_const.value("floating", 12.34) == Approx(42.23)); CHECK(j_const.value("floating", 12) == 42); - CHECK(j_const.value("object", json({{"foo", "bar"}})) == json(json::object())); + CHECK(j_const.value("object", json({{"foo", "bar"}})) == json::object()); CHECK(j_const.value("array", json({10, 100})) == json({1, 2, 3})); } @@ -298,7 +298,7 @@ TEST_CASE("element access 2") CHECK(j.value("/string"_json_pointer, std::string("bar")) == "hello world"); CHECK(j.value("/floating"_json_pointer, 12.34) == Approx(42.23)); CHECK(j.value("/floating"_json_pointer, 12) == 42); - CHECK(j.value("/object"_json_pointer, json({{"foo", "bar"}})) == json(json::object())); + CHECK(j.value("/object"_json_pointer, json({{"foo", "bar"}})) == json::object()); CHECK(j.value("/array"_json_pointer, json({10, 100})) == json({1, 2, 3})); CHECK(j_const.value("/integer"_json_pointer, 2) == 1); @@ -310,7 +310,7 @@ TEST_CASE("element access 2") CHECK(j_const.value("/string"_json_pointer, std::string("bar")) == "hello world"); CHECK(j_const.value("/floating"_json_pointer, 12.34) == Approx(42.23)); CHECK(j_const.value("/floating"_json_pointer, 12) == 42); - CHECK(j_const.value("/object"_json_pointer, json({{"foo", "bar"}})) == json(json::object())); + CHECK(j_const.value("/object"_json_pointer, json({{"foo", "bar"}})) == json::object()); CHECK(j_const.value("/array"_json_pointer, json({10, 100})) == json({1, 2, 3})); } @@ -435,7 +435,7 @@ TEST_CASE("element access 2") CHECK(j["floating"] == json(42.23)); CHECK(j[json::object_t::key_type("floating")] == j["floating"]); - CHECK(j["object"] == json(json::object())); + CHECK(j["object"] == json::object()); CHECK(j[json::object_t::key_type("object")] == j["object"]); CHECK(j["array"] == json({1, 2, 3})); @@ -456,7 +456,7 @@ TEST_CASE("element access 2") CHECK(j_const["floating"] == json(42.23)); CHECK(j_const[json::object_t::key_type("floating")] == j["floating"]); - CHECK(j_const["object"] == json(json::object())); + CHECK(j_const["object"] == json::object()); CHECK(j_const[json::object_t::key_type("object")] == j["object"]); CHECK(j_const["array"] == json({1, 2, 3})); diff --git a/test/src/unit-msgpack.cpp b/test/src/unit-msgpack.cpp index 1da33153b..5d1578127 100644 --- a/test/src/unit-msgpack.cpp +++ b/test/src/unit-msgpack.cpp @@ -203,7 +203,7 @@ TEST_CASE("MessagePack") // check individual bytes CHECK(result[0] == 0xcd); - uint16_t restored = static_cast(result[1]) * 256 + static_cast(result[2]); + uint16_t restored = static_cast(static_cast(result[1]) * 256 + static_cast(result[2])); CHECK(restored == i); // roundtrip @@ -242,10 +242,10 @@ TEST_CASE("MessagePack") // check individual bytes CHECK(result[0] == 0xce); - uint32_t restored = static_cast((static_cast(result[1]) << 030) + + uint32_t restored = (static_cast(result[1]) << 030) + (static_cast(result[2]) << 020) + (static_cast(result[3]) << 010) + - static_cast(result[4])); + static_cast(result[4]); CHECK(restored == i); // roundtrip @@ -288,14 +288,14 @@ TEST_CASE("MessagePack") // check individual bytes CHECK(result[0] == 0xcf); - uint64_t restored = static_cast((static_cast(result[1]) << 070) + + uint64_t restored = (static_cast(result[1]) << 070) + (static_cast(result[2]) << 060) + (static_cast(result[3]) << 050) + (static_cast(result[4]) << 040) + (static_cast(result[5]) << 030) + (static_cast(result[6]) << 020) + (static_cast(result[7]) << 010) + - static_cast(result[8])); + static_cast(result[8]); CHECK(restored == i); // roundtrip @@ -415,10 +415,10 @@ TEST_CASE("MessagePack") // check individual bytes CHECK(result[0] == 0xd2); - uint32_t restored = static_cast((static_cast(result[1]) << 030) + + uint32_t restored = (static_cast(result[1]) << 030) + (static_cast(result[2]) << 020) + (static_cast(result[3]) << 010) + - static_cast(result[4])); + static_cast(result[4]); CHECK(restored == i); // roundtrip @@ -460,14 +460,14 @@ TEST_CASE("MessagePack") // check individual bytes CHECK(result[0] == 0xd3); - int64_t restored = static_cast((static_cast(result[1]) << 070) + - (static_cast(result[2]) << 060) + - (static_cast(result[3]) << 050) + - (static_cast(result[4]) << 040) + - (static_cast(result[5]) << 030) + - (static_cast(result[6]) << 020) + - (static_cast(result[7]) << 010) + - static_cast(result[8])); + int64_t restored = (static_cast(result[1]) << 070) + + (static_cast(result[2]) << 060) + + (static_cast(result[3]) << 050) + + (static_cast(result[4]) << 040) + + (static_cast(result[5]) << 030) + + (static_cast(result[6]) << 020) + + (static_cast(result[7]) << 010) + + static_cast(result[8]); CHECK(restored == i); // roundtrip @@ -564,7 +564,7 @@ TEST_CASE("MessagePack") // check individual bytes CHECK(result[0] == 0xcd); - uint16_t restored = static_cast(result[1]) * 256 + static_cast(result[2]); + uint16_t restored = static_cast(static_cast(result[1]) * 256 + static_cast(result[2])); CHECK(restored == i); // roundtrip @@ -602,10 +602,10 @@ TEST_CASE("MessagePack") // check individual bytes CHECK(result[0] == 0xce); - uint32_t restored = static_cast((static_cast(result[1]) << 030) + + uint32_t restored = (static_cast(result[1]) << 030) + (static_cast(result[2]) << 020) + (static_cast(result[3]) << 010) + - static_cast(result[4])); + static_cast(result[4]); CHECK(restored == i); // roundtrip @@ -647,14 +647,14 @@ TEST_CASE("MessagePack") // check individual bytes CHECK(result[0] == 0xcf); - uint64_t restored = static_cast((static_cast(result[1]) << 070) + + uint64_t restored = (static_cast(result[1]) << 070) + (static_cast(result[2]) << 060) + (static_cast(result[3]) << 050) + (static_cast(result[4]) << 040) + (static_cast(result[5]) << 030) + (static_cast(result[6]) << 020) + (static_cast(result[7]) << 010) + - static_cast(result[8])); + static_cast(result[8]); CHECK(restored == i); // roundtrip diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 3f17a917f..f7f4c350e 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -412,17 +412,17 @@ TEST_CASE("regression tests") class CommaDecimalSeparator : public std::numpunct { protected: - char do_decimal_point() const + char do_decimal_point() const override { return ','; } - char do_thousands_sep() const + char do_thousands_sep() const override { return '.'; } - std::string do_grouping() const + std::string do_grouping() const override { return "\03"; } @@ -755,7 +755,7 @@ TEST_CASE("regression tests") }; CHECK_THROWS_AS(json::from_cbor(vec), json::parse_error); CHECK_THROWS_WITH(json::from_cbor(vec), - "[json.exception.parse_error.110] parse error at 137: cannot read 1 bytes from vector"); + "[json.exception.parse_error.113] parse error at 2: expected a CBOR string; last byte: 0x98"); // related test case: nonempty UTF-8 string (indefinite length) std::vector vec1 {0x7f, 0x61, 0x61}; @@ -808,7 +808,7 @@ TEST_CASE("regression tests") }; CHECK_THROWS_AS(json::from_cbor(vec1), json::parse_error); CHECK_THROWS_WITH(json::from_cbor(vec1), - "[json.exception.parse_error.110] parse error at 49: cannot read 4 bytes from vector"); + "[json.exception.parse_error.113] parse error at 13: expected a CBOR string; last byte: 0xb4"); // related test case: double-precision std::vector vec2 @@ -822,7 +822,7 @@ TEST_CASE("regression tests") }; CHECK_THROWS_AS(json::from_cbor(vec2), json::parse_error); CHECK_THROWS_WITH(json::from_cbor(vec2), - "[json.exception.parse_error.110] parse error at 49: cannot read 8 bytes from vector"); + "[json.exception.parse_error.113] parse error at 13: expected a CBOR string; last byte: 0xb4"); } SECTION("issue #452 - Heap-buffer-overflow (OSS-Fuzz issue 585)")