diff --git a/data/include/join/cbor.hpp b/data/include/join/cbor.hpp index 3575be5c..1e6c4baf 100644 --- a/data/include/join/cbor.hpp +++ b/data/include/join/cbor.hpp @@ -154,6 +154,7 @@ namespace join */ explicit BasicCborWriter (std::ostream& document) : _out (document.rdbuf ()) + , _document (&document) { } @@ -281,8 +282,17 @@ namespace join float f = static_cast (value); if (std::bit_cast (static_cast (f)) == std::bit_cast (value)) { - append (0xfa); - pack (f); + uint16_t half; + if (toHalf (f, half)) + { + append (0xf9); + pack (half); + } + else + { + append (0xfa); + pack (f); + } } else { @@ -547,7 +557,10 @@ namespace join { if (_size) { - _out->sputn (_buffer, _size); + if (JOIN_UNLIKELY (_out->sputn (_buffer, _size) != static_cast (_size))) + { + _document->setstate (std::ios_base::badbit); + } _size = 0; } } @@ -582,6 +595,10 @@ namespace join */ inline void append (const char* data, size_t size) { + if (_size > sizeof (_buffer)) + { + __builtin_unreachable (); + } if (JOIN_UNLIKELY (size == 0)) { return; @@ -591,7 +608,10 @@ namespace join flush (); if (size >= sizeof (_buffer)) { - _out->sputn (data, size); + if (JOIN_UNLIKELY (_out->sputn (data, size) != static_cast (size))) + { + _document->setstate (std::ios_base::badbit); + } return; } } @@ -609,6 +629,60 @@ namespace join append (reinterpret_cast (&swap (value)), sizeof (value)); } + /** + * @brief convert a float to half precision when no information is lost. + * @param value float value to convert. + * @param half half precision bits. + * @return true if the value is exactly representable in half precision, false otherwise. + */ + static bool toHalf (float value, uint16_t& half) noexcept + { + const uint32_t bits = std::bit_cast (value); + const uint16_t sign = static_cast ((bits >> 16) & 0x8000); + const uint32_t exponent = (bits >> 23) & 0xff; + const uint32_t mantissa = bits & 0x7fffff; + + if (exponent == 0xff) + { + if ((mantissa != 0) && (mantissa != 0x400000)) + { + return false; + } + half = sign | 0x7c00 | static_cast (mantissa >> 13); + return true; + } + + if ((exponent == 0) && (mantissa == 0)) + { + half = sign; + return true; + } + + if ((exponent >= 113) && (exponent <= 142)) + { + if ((mantissa & 0x1fff) != 0) + { + return false; + } + half = sign | static_cast (((exponent - 112) << 10) | (mantissa >> 13)); + return true; + } + + if ((exponent >= 103) && (exponent <= 112)) + { + const uint32_t significand = 0x800000 | mantissa; + const uint32_t shift = 126 - exponent; + if ((significand & ((1u << shift) - 1)) != 0) + { + return false; + } + half = sign | static_cast (significand >> shift); + return true; + } + + return false; + } + /** * @brief write a data item head with the shortest argument encoding. * @param major major type, already shifted. @@ -656,6 +730,9 @@ namespace join /// underlying output stream. std::streambuf* _out; + /// output stream receiving the error state. + std::ostream* _document; + /// output buffer. char _buffer[1024] = {}; @@ -1126,7 +1203,7 @@ namespace join return -1; } - if (JOIN_UNLIKELY (len > std::numeric_limits::max ())) + if (JOIN_UNLIKELY (len > std::numeric_limits::max () - output.size ())) { join::lastError = make_error_code (SaxErrc::InvalidValue); return -1; diff --git a/data/include/join/json.hpp b/data/include/join/json.hpp index 55bd79e3..f7566a33 100644 --- a/data/include/join/json.hpp +++ b/data/include/join/json.hpp @@ -209,6 +209,7 @@ namespace join */ explicit BasicJsonWriter (std::ostream& document, size_t indentation = 0) : _out (document.rdbuf ()) + , _document (&document) , _indentation ((WriteMode == JsonWriteMode::Canonical) ? 0 : indentation) { } @@ -636,7 +637,10 @@ namespace join { if (_size) { - _out->sputn (_buffer, _size); + if (JOIN_UNLIKELY (_out->sputn (_buffer, _size) != static_cast (_size))) + { + _document->setstate (std::ios_base::badbit); + } _size = 0; } } @@ -773,6 +777,10 @@ namespace join { char buf[25]; char* end = dconv::dtoa (buf, value); + if ((end - buf > 2) && (end[-2] == '.') && (end[-1] == '0')) + { + end -= 2; + } for (char* pos = buf; pos < end; ++pos) { append (*pos); @@ -880,7 +888,10 @@ namespace join flush (); if (size >= sizeof (_buffer)) { - _out->sputn (data, size); + if (JOIN_UNLIKELY (_out->sputn (data, size) != static_cast (size))) + { + _document->setstate (std::ios_base::badbit); + } return; } } @@ -992,6 +1003,9 @@ namespace join /// underlying output stream. std::streambuf* _out; + /// output stream receiving the error state. + std::ostream* _document; + /// output buffer. char _buffer[1024] = {}; @@ -1284,6 +1298,9 @@ namespace join case 'f': document.get (); return readFalse (document, handler); + case std::char_traits::eof (): + join::lastError = make_error_code (SaxErrc::Truncated); + return -1; default: return readNumber (document, handler); } @@ -1581,7 +1598,14 @@ namespace join if (ch < 0x20) { - join::lastError = make_error_code (JsonErrc::IllegalCharacter); + if (ch == std::char_traits::eof ()) + { + join::lastError = make_error_code (SaxErrc::Truncated); + } + else + { + join::lastError = make_error_code (JsonErrc::IllegalCharacter); + } return -1; } } @@ -1654,7 +1678,14 @@ namespace join break; } - join::lastError = make_error_code (JsonErrc::MissingComma); + if (ch == std::char_traits::eof ()) + { + join::lastError = make_error_code (SaxErrc::Truncated); + } + else + { + join::lastError = make_error_code (JsonErrc::MissingComma); + } return -1; } @@ -1694,9 +1725,17 @@ namespace join for (;;) { - if (JOIN_UNLIKELY (document.get () != '"')) + int quote = document.get (); + if (JOIN_UNLIKELY (quote != '"')) { - join::lastError = make_error_code (JsonErrc::MissingQuote); + if (quote == std::char_traits::eof ()) + { + join::lastError = make_error_code (SaxErrc::Truncated); + } + else + { + join::lastError = make_error_code (JsonErrc::MissingQuote); + } return -1; } @@ -1713,9 +1752,17 @@ namespace join } } - if (JOIN_UNLIKELY (document.get () != ':')) + int colon = document.get (); + if (JOIN_UNLIKELY (colon != ':')) { - join::lastError = make_error_code (JsonErrc::MissingColon); + if (colon == std::char_traits::eof ()) + { + join::lastError = make_error_code (SaxErrc::Truncated); + } + else + { + join::lastError = make_error_code (JsonErrc::MissingColon); + } return -1; } @@ -1754,7 +1801,14 @@ namespace join break; } - join::lastError = make_error_code (JsonErrc::MissingComma); + if (ch == std::char_traits::eof ()) + { + join::lastError = make_error_code (SaxErrc::Truncated); + } + else + { + join::lastError = make_error_code (JsonErrc::MissingComma); + } return -1; } diff --git a/data/include/join/pack.hpp b/data/include/join/pack.hpp index 6eaf647c..cdaee2fe 100644 --- a/data/include/join/pack.hpp +++ b/data/include/join/pack.hpp @@ -74,6 +74,7 @@ namespace join */ explicit BasicPackWriter (std::ostream& document) : _out (document.rdbuf ()) + , _document (&document) { } @@ -649,7 +650,10 @@ namespace join { if (_size) { - _out->sputn (_buffer, _size); + if (JOIN_UNLIKELY (_out->sputn (_buffer, _size) != static_cast (_size))) + { + _document->setstate (std::ios_base::badbit); + } _size = 0; } } @@ -693,7 +697,10 @@ namespace join flush (); if (size >= sizeof (_buffer)) { - _out->sputn (data, size); + if (JOIN_UNLIKELY (_out->sputn (data, size) != static_cast (size))) + { + _document->setstate (std::ios_base::badbit); + } return; } } @@ -725,6 +732,9 @@ namespace join /// underlying output stream. std::streambuf* _out; + /// output stream receiving the error state. + std::ostream* _document; + /// output buffer. char _buffer[1024] = {}; diff --git a/data/tests/cbor_reader_test.cpp b/data/tests/cbor_reader_test.cpp index 205d03b8..d1849d81 100644 --- a/data/tests/cbor_reader_test.cpp +++ b/data/tests/cbor_reader_test.cpp @@ -498,6 +498,7 @@ TEST (CborReader, fail) ASSERT_EQ (error (hex ("5f5cff")), SaxErrc::InvalidValue); ASSERT_EQ (error (hex ("7f7bffffffffffffffff")), SaxErrc::InvalidValue); ASSERT_EQ (error (hex ("5f5b0000000100000000")), SaxErrc::InvalidValue); + ASSERT_EQ (error (hex ("5f41005affffffff")), SaxErrc::InvalidValue); ASSERT_EQ (error (hex ("bf6161ff")), SaxErrc::InvalidKey); ASSERT_EQ (error (hex ("0000")), SaxErrc::ExtraData); diff --git a/data/tests/cbor_roundtrip_test.cpp b/data/tests/cbor_roundtrip_test.cpp index 5084a45d..f97534ea 100644 --- a/data/tests/cbor_roundtrip_test.cpp +++ b/data/tests/cbor_roundtrip_test.cpp @@ -224,20 +224,20 @@ TEST (Cbor, roundtrip) EXPECT_EQ (stream.str (), hex ("811bffffffffffffffff")); stream.clear (); - stream.str (hex ("81fa00000000")); + stream.str (hex ("81f90000")); ASSERT_NE (reader.deserialize (stream, value), -1) << join::lastError.message (); stream.str (""); stream.clear (); ASSERT_NE (value.serialize (writer), -1) << join::lastError.message (); - EXPECT_EQ (stream.str (), hex ("81fa00000000")); + EXPECT_EQ (stream.str (), hex ("81f90000")); stream.clear (); - stream.str (hex ("81fa80000000")); + stream.str (hex ("81f98000")); ASSERT_NE (reader.deserialize (stream, value), -1) << join::lastError.message (); stream.str (""); stream.clear (); ASSERT_NE (value.serialize (writer), -1) << join::lastError.message (); - EXPECT_EQ (stream.str (), hex ("81fa80000000")); + EXPECT_EQ (stream.str (), hex ("81f98000")); stream.clear (); stream.str (hex ("81fb3ff3c083126e978d")); @@ -304,36 +304,36 @@ TEST (Cbor, roundtrip) EXPECT_EQ (stream.str (), hex ("a10102")); stream.clear (); - stream.str (hex ("81fa7f800000")); + stream.str (hex ("81f97c00")); ASSERT_NE (reader.deserialize (stream, value), -1) << join::lastError.message (); stream.str (""); stream.clear (); ASSERT_NE (value.serialize (writer), -1) << join::lastError.message (); - EXPECT_EQ (stream.str (), hex ("81fa7f800000")); + EXPECT_EQ (stream.str (), hex ("81f97c00")); stream.clear (); - stream.str (hex ("81faff800000")); + stream.str (hex ("81f9fc00")); ASSERT_NE (reader.deserialize (stream, value), -1) << join::lastError.message (); stream.str (""); stream.clear (); ASSERT_NE (value.serialize (writer), -1) << join::lastError.message (); - EXPECT_EQ (stream.str (), hex ("81faff800000")); + EXPECT_EQ (stream.str (), hex ("81f9fc00")); stream.clear (); - stream.str (hex ("81fa7fc00000")); + stream.str (hex ("81f97e00")); ASSERT_NE (reader.deserialize (stream, value), -1) << join::lastError.message (); stream.str (""); stream.clear (); ASSERT_NE (value.serialize (writer), -1) << join::lastError.message (); - EXPECT_EQ (stream.str (), hex ("81fa7fc00000")); + EXPECT_EQ (stream.str (), hex ("81f97e00")); stream.clear (); - stream.str (hex ("81faffc00000")); + stream.str (hex ("81f9fe00")); ASSERT_NE (reader.deserialize (stream, value), -1) << join::lastError.message (); stream.str (""); stream.clear (); ASSERT_NE (value.serialize (writer), -1) << join::lastError.message (); - EXPECT_EQ (stream.str (), hex ("81faffc00000")); + EXPECT_EQ (stream.str (), hex ("81f9fe00")); } /** diff --git a/data/tests/cbor_stream_roundtrip_test.cpp b/data/tests/cbor_stream_roundtrip_test.cpp index d3c33b92..db565a2e 100644 --- a/data/tests/cbor_stream_roundtrip_test.cpp +++ b/data/tests/cbor_stream_roundtrip_test.cpp @@ -224,20 +224,20 @@ TEST (CborStream, roundtrip) EXPECT_EQ (stream.str (), hex ("9f1bffffffffffffffffff")); stream.clear (); - stream.str (hex ("9ffa00000000ff")); + stream.str (hex ("9ff90000ff")); ASSERT_NE (reader.deserialize (stream, value), -1) << join::lastError.message (); stream.str (""); stream.clear (); ASSERT_NE (value.serialize (writer), -1) << join::lastError.message (); - EXPECT_EQ (stream.str (), hex ("9ffa00000000ff")); + EXPECT_EQ (stream.str (), hex ("9ff90000ff")); stream.clear (); - stream.str (hex ("9ffa80000000ff")); + stream.str (hex ("9ff98000ff")); ASSERT_NE (reader.deserialize (stream, value), -1) << join::lastError.message (); stream.str (""); stream.clear (); ASSERT_NE (value.serialize (writer), -1) << join::lastError.message (); - EXPECT_EQ (stream.str (), hex ("9ffa80000000ff")); + EXPECT_EQ (stream.str (), hex ("9ff98000ff")); stream.clear (); stream.str (hex ("9ffb3ff3c083126e978dff")); @@ -304,36 +304,36 @@ TEST (CborStream, roundtrip) EXPECT_EQ (stream.str (), hex ("bf0102ff")); stream.clear (); - stream.str (hex ("9ffa7f800000ff")); + stream.str (hex ("9ff97c00ff")); ASSERT_NE (reader.deserialize (stream, value), -1) << join::lastError.message (); stream.str (""); stream.clear (); ASSERT_NE (value.serialize (writer), -1) << join::lastError.message (); - EXPECT_EQ (stream.str (), hex ("9ffa7f800000ff")); + EXPECT_EQ (stream.str (), hex ("9ff97c00ff")); stream.clear (); - stream.str (hex ("9ffaff800000ff")); + stream.str (hex ("9ff9fc00ff")); ASSERT_NE (reader.deserialize (stream, value), -1) << join::lastError.message (); stream.str (""); stream.clear (); ASSERT_NE (value.serialize (writer), -1) << join::lastError.message (); - EXPECT_EQ (stream.str (), hex ("9ffaff800000ff")); + EXPECT_EQ (stream.str (), hex ("9ff9fc00ff")); stream.clear (); - stream.str (hex ("9ffa7fc00000ff")); + stream.str (hex ("9ff97e00ff")); ASSERT_NE (reader.deserialize (stream, value), -1) << join::lastError.message (); stream.str (""); stream.clear (); ASSERT_NE (value.serialize (writer), -1) << join::lastError.message (); - EXPECT_EQ (stream.str (), hex ("9ffa7fc00000ff")); + EXPECT_EQ (stream.str (), hex ("9ff97e00ff")); stream.clear (); - stream.str (hex ("9ffaffc00000ff")); + stream.str (hex ("9ff9fe00ff")); ASSERT_NE (reader.deserialize (stream, value), -1) << join::lastError.message (); stream.str (""); stream.clear (); ASSERT_NE (value.serialize (writer), -1) << join::lastError.message (); - EXPECT_EQ (stream.str (), hex ("9ffaffc00000ff")); + EXPECT_EQ (stream.str (), hex ("9ff9fe00ff")); } /** diff --git a/data/tests/cbor_stream_writer_test.cpp b/data/tests/cbor_stream_writer_test.cpp index d0219970..3e6fd5bb 100644 --- a/data/tests/cbor_stream_writer_test.cpp +++ b/data/tests/cbor_stream_writer_test.cpp @@ -44,6 +44,22 @@ using join::Value; using join::Object; using join::SaxErrc; +/** + * @brief stream buffer refusing every write. + */ +struct RefusingBuffer : public std::streambuf +{ + std::streamsize xsputn (const char*, std::streamsize) override + { + return 0; + } + + int overflow (int) override + { + return traits_type::eof (); + } +}; + /** * @brief Test setNull method. */ @@ -189,16 +205,13 @@ TEST (CborStreamWriter, setDouble) return stream.str (); }; - EXPECT_EQ (write (0.0), std::string ({'\xFA', '\x00', '\x00', '\x00', '\x00'})); - EXPECT_EQ (write (-0.0), std::string ({'\xFA', '\x80', '\x00', '\x00', '\x00'})); - EXPECT_EQ (write (1.5), std::string ({'\xFA', '\x3F', '\xC0', '\x00', '\x00'})); + EXPECT_EQ (write (0.0), std::string ({'\xF9', '\x00', '\x00'})); + EXPECT_EQ (write (-0.0), std::string ({'\xF9', '\x80', '\x00'})); + EXPECT_EQ (write (1.5), std::string ({'\xF9', '\x3E', '\x00'})); EXPECT_EQ (write (1.1), std::string ({'\xFB', '\x3F', '\xF1', '\x99', '\x99', '\x99', '\x99', '\x99', '\x9A'})); - EXPECT_EQ (write (std::numeric_limits::infinity ()), - std::string ({'\xFA', '\x7F', '\x80', '\x00', '\x00'})); - EXPECT_EQ (write (-std::numeric_limits::infinity ()), - std::string ({'\xFA', '\xFF', '\x80', '\x00', '\x00'})); - EXPECT_EQ (write (std::numeric_limits::quiet_NaN ()), - std::string ({'\xFA', '\x7F', '\xC0', '\x00', '\x00'})); + EXPECT_EQ (write (std::numeric_limits::infinity ()), std::string ({'\xF9', '\x7C', '\x00'})); + EXPECT_EQ (write (-std::numeric_limits::infinity ()), std::string ({'\xF9', '\xFC', '\x00'})); + EXPECT_EQ (write (std::numeric_limits::quiet_NaN ()), std::string ({'\xF9', '\x7E', '\x00'})); EXPECT_EQ (write (std::bit_cast (uint64_t (0x7ff8000000000001))), std::string ({'\xFB', '\x7F', '\xF8', '\x00', '\x00', '\x00', '\x00', '\x00', '\x01'})); EXPECT_EQ (write (std::numeric_limits::max ()), std::string ({'\xFA', '\x7F', '\x7F', '\xFF', '\xFF'})); @@ -209,10 +222,17 @@ TEST (CborStreamWriter, setDouble) std::string ({'\xFA', '\x00', '\x00', '\x00', '\x01'})); EXPECT_EQ (write (std::numeric_limits::denorm_min ()), std::string ({'\xFB', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x01'})); - EXPECT_EQ (write (-std::numeric_limits::quiet_NaN ()), - std::string ({'\xFA', '\xFF', '\xC0', '\x00', '\x00'})); + EXPECT_EQ (write (-std::numeric_limits::quiet_NaN ()), std::string ({'\xF9', '\xFE', '\x00'})); EXPECT_EQ (write (std::bit_cast (uint64_t (0x7ff4000000000000))), std::string ({'\xFB', '\x7F', '\xF4', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00'})); + EXPECT_EQ (write (65504.0), std::string ({'\xF9', '\x7B', '\xFF'})); + EXPECT_EQ (write (65520.0), std::string ({'\xFA', '\x47', '\x7F', '\xF0', '\x00'})); + EXPECT_EQ (write (std::ldexp (1.0, -14)), std::string ({'\xF9', '\x04', '\x00'})); + EXPECT_EQ (write (std::ldexp (1.0, -24)), std::string ({'\xF9', '\x00', '\x01'})); + EXPECT_EQ (write (std::ldexp (1.0, -25)), std::string ({'\xFA', '\x33', '\x00', '\x00', '\x00'})); + EXPECT_EQ (write (std::ldexp (1.5, -24)), std::string ({'\xFA', '\x33', '\xC0', '\x00', '\x00'})); + EXPECT_EQ (write (std::bit_cast (uint64_t (0x7ff8000020000000))), + std::string ({'\xFA', '\x7F', '\xC0', '\x00', '\x01'})); } /** @@ -446,7 +466,7 @@ TEST (CborStreamWriter, setKey) EXPECT_EQ (stream.str ().substr (1), std::string ({'\x18', '\x2A'})); EXPECT_EQ (key (1.5), 0); - EXPECT_EQ (stream.str ().substr (1), std::string ({'\xFA', '\x3F', '\xC0', '\x00', '\x00'})); + EXPECT_EQ (stream.str ().substr (1), std::string ({'\xF9', '\x3E', '\x00'})); EXPECT_EQ (key ("a"), 0); EXPECT_EQ (stream.str ().substr (1), std::string ({'\x61', 'a'})); @@ -530,6 +550,16 @@ TEST (CborStreamWriter, flush) EXPECT_EQ (stream.str ().size (), 1024u); EXPECT_EQ (cborStreamWriter.stopArray (), 0); EXPECT_EQ (stream.str ().size (), 1102u); + + RefusingBuffer refusing; + std::ostream refused (&refusing); + CborStreamWriter refusedWriter (refused); + EXPECT_EQ (refusedWriter.setInt (1), 0); + EXPECT_TRUE (refused.bad ()); + + refused.clear (); + EXPECT_EQ (refusedWriter.setString (std::string (2048, 'x')), 0); + EXPECT_TRUE (refused.bad ()); } /** diff --git a/data/tests/cbor_writer_test.cpp b/data/tests/cbor_writer_test.cpp index f697951b..166d8161 100644 --- a/data/tests/cbor_writer_test.cpp +++ b/data/tests/cbor_writer_test.cpp @@ -44,6 +44,22 @@ using join::Value; using join::Object; using join::SaxErrc; +/** + * @brief stream buffer refusing every write. + */ +struct RefusingBuffer : public std::streambuf +{ + std::streamsize xsputn (const char*, std::streamsize) override + { + return 0; + } + + int overflow (int) override + { + return traits_type::eof (); + } +}; + /** * @brief Test setNull method. */ @@ -189,16 +205,13 @@ TEST (CborWriter, setDouble) return stream.str (); }; - EXPECT_EQ (write (0.0), std::string ({'\xFA', '\x00', '\x00', '\x00', '\x00'})); - EXPECT_EQ (write (-0.0), std::string ({'\xFA', '\x80', '\x00', '\x00', '\x00'})); - EXPECT_EQ (write (1.5), std::string ({'\xFA', '\x3F', '\xC0', '\x00', '\x00'})); + EXPECT_EQ (write (0.0), std::string ({'\xF9', '\x00', '\x00'})); + EXPECT_EQ (write (-0.0), std::string ({'\xF9', '\x80', '\x00'})); + EXPECT_EQ (write (1.5), std::string ({'\xF9', '\x3E', '\x00'})); EXPECT_EQ (write (1.1), std::string ({'\xFB', '\x3F', '\xF1', '\x99', '\x99', '\x99', '\x99', '\x99', '\x9A'})); - EXPECT_EQ (write (std::numeric_limits::infinity ()), - std::string ({'\xFA', '\x7F', '\x80', '\x00', '\x00'})); - EXPECT_EQ (write (-std::numeric_limits::infinity ()), - std::string ({'\xFA', '\xFF', '\x80', '\x00', '\x00'})); - EXPECT_EQ (write (std::numeric_limits::quiet_NaN ()), - std::string ({'\xFA', '\x7F', '\xC0', '\x00', '\x00'})); + EXPECT_EQ (write (std::numeric_limits::infinity ()), std::string ({'\xF9', '\x7C', '\x00'})); + EXPECT_EQ (write (-std::numeric_limits::infinity ()), std::string ({'\xF9', '\xFC', '\x00'})); + EXPECT_EQ (write (std::numeric_limits::quiet_NaN ()), std::string ({'\xF9', '\x7E', '\x00'})); EXPECT_EQ (write (std::bit_cast (uint64_t (0x7ff8000000000001))), std::string ({'\xFB', '\x7F', '\xF8', '\x00', '\x00', '\x00', '\x00', '\x00', '\x01'})); EXPECT_EQ (write (std::numeric_limits::max ()), std::string ({'\xFA', '\x7F', '\x7F', '\xFF', '\xFF'})); @@ -209,10 +222,17 @@ TEST (CborWriter, setDouble) std::string ({'\xFA', '\x00', '\x00', '\x00', '\x01'})); EXPECT_EQ (write (std::numeric_limits::denorm_min ()), std::string ({'\xFB', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x01'})); - EXPECT_EQ (write (-std::numeric_limits::quiet_NaN ()), - std::string ({'\xFA', '\xFF', '\xC0', '\x00', '\x00'})); + EXPECT_EQ (write (-std::numeric_limits::quiet_NaN ()), std::string ({'\xF9', '\xFE', '\x00'})); EXPECT_EQ (write (std::bit_cast (uint64_t (0x7ff4000000000000))), std::string ({'\xFB', '\x7F', '\xF4', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00'})); + EXPECT_EQ (write (65504.0), std::string ({'\xF9', '\x7B', '\xFF'})); + EXPECT_EQ (write (65520.0), std::string ({'\xFA', '\x47', '\x7F', '\xF0', '\x00'})); + EXPECT_EQ (write (std::ldexp (1.0, -14)), std::string ({'\xF9', '\x04', '\x00'})); + EXPECT_EQ (write (std::ldexp (1.0, -24)), std::string ({'\xF9', '\x00', '\x01'})); + EXPECT_EQ (write (std::ldexp (1.0, -25)), std::string ({'\xFA', '\x33', '\x00', '\x00', '\x00'})); + EXPECT_EQ (write (std::ldexp (1.5, -24)), std::string ({'\xFA', '\x33', '\xC0', '\x00', '\x00'})); + EXPECT_EQ (write (std::bit_cast (uint64_t (0x7ff8000020000000))), + std::string ({'\xFA', '\x7F', '\xC0', '\x00', '\x01'})); } /** @@ -436,7 +456,7 @@ TEST (CborWriter, setKey) EXPECT_EQ (stream.str ().substr (1), std::string ({'\x18', '\x2A'})); EXPECT_EQ (key (1.5), 0); - EXPECT_EQ (stream.str ().substr (1), std::string ({'\xFA', '\x3F', '\xC0', '\x00', '\x00'})); + EXPECT_EQ (stream.str ().substr (1), std::string ({'\xF9', '\x3E', '\x00'})); EXPECT_EQ (key ("a"), 0); EXPECT_EQ (stream.str ().substr (1), std::string ({'\x61', 'a'})); @@ -518,6 +538,16 @@ TEST (CborWriter, flush) EXPECT_EQ (stream.str ().size (), 1024u); EXPECT_EQ (cborWriter.stopArray (), 0); EXPECT_EQ (stream.str ().size (), 1103u); + + RefusingBuffer refusing; + std::ostream refused (&refusing); + CborWriter refusedWriter (refused); + EXPECT_EQ (refusedWriter.setInt (1), 0); + EXPECT_TRUE (refused.bad ()); + + refused.clear (); + EXPECT_EQ (refusedWriter.setString (std::string (2048, 'x')), 0); + EXPECT_TRUE (refused.bad ()); } /** diff --git a/data/tests/json_canonicalizer_test.cpp b/data/tests/json_canonicalizer_test.cpp index d3af3aa5..29c26e8d 100644 --- a/data/tests/json_canonicalizer_test.cpp +++ b/data/tests/json_canonicalizer_test.cpp @@ -249,6 +249,26 @@ TEST (JsonCanonicalizer, values) EXPECT_EQ (out.str (), "{\"literals\":[null,true,false],\"numbers\":[333333333.3333333,1e+30,4.5,0.002,1e-27],\"string\":\"€$" "\\u000f\\nA'B\\\"\\\\\\\\\\\"/\"}"); + + out.str (""); + EXPECT_EQ (writer.setDouble (18446744073709551616.0), 0); + EXPECT_EQ (out.str (), "18446744073709552000"); + + out.str (""); + EXPECT_EQ (writer.setDouble (-18446744073709551616.0), 0); + EXPECT_EQ (out.str (), "-18446744073709552000"); + + out.str (""); + EXPECT_EQ (writer.setDouble (295147905179352825856.0), 0); + EXPECT_EQ (out.str (), "295147905179352830000"); + + out.str (""); + EXPECT_EQ (writer.setDouble (999999999999999700000.0), 0); + EXPECT_EQ (out.str (), "999999999999999700000"); + + out.str (""); + EXPECT_EQ (writer.setDouble (1e21), 0); + EXPECT_EQ (out.str (), "1e+21"); } /** diff --git a/data/tests/json_reader_test.cpp b/data/tests/json_reader_test.cpp index b2f6b05a..1d30e8d6 100644 --- a/data/tests/json_reader_test.cpp +++ b/data/tests/json_reader_test.cpp @@ -796,6 +796,51 @@ TEST (JsonReader, fail) stream.clear (); stream.str ("{} extra"); ASSERT_EQ (JsonReader ().deserialize (stream, value), -1); + + stream.clear (); + stream.str (""); + ASSERT_EQ (JsonReader ().deserialize (stream, value), -1); + EXPECT_EQ (join::lastError, SaxErrc::Truncated); + + stream.clear (); + stream.str ("["); + ASSERT_EQ (JsonReader ().deserialize (stream, value), -1); + EXPECT_EQ (join::lastError, SaxErrc::Truncated); + + stream.clear (); + stream.str ("[1"); + ASSERT_EQ (JsonReader ().deserialize (stream, value), -1); + EXPECT_EQ (join::lastError, SaxErrc::Truncated); + + stream.clear (); + stream.str ("[1,"); + ASSERT_EQ (JsonReader ().deserialize (stream, value), -1); + EXPECT_EQ (join::lastError, SaxErrc::Truncated); + + stream.clear (); + stream.str ("{"); + ASSERT_EQ (JsonReader ().deserialize (stream, value), -1); + EXPECT_EQ (join::lastError, SaxErrc::Truncated); + + stream.clear (); + stream.str ("{\"a\""); + ASSERT_EQ (JsonReader ().deserialize (stream, value), -1); + EXPECT_EQ (join::lastError, SaxErrc::Truncated); + + stream.clear (); + stream.str ("{\"a\":"); + ASSERT_EQ (JsonReader ().deserialize (stream, value), -1); + EXPECT_EQ (join::lastError, SaxErrc::Truncated); + + stream.clear (); + stream.str ("{\"a\":1"); + ASSERT_EQ (JsonReader ().deserialize (stream, value), -1); + EXPECT_EQ (join::lastError, SaxErrc::Truncated); + + stream.clear (); + stream.str ("[\"Hello"); + ASSERT_EQ (JsonReader ().deserialize (stream, value), -1); + EXPECT_EQ (join::lastError, SaxErrc::Truncated); } /** diff --git a/data/tests/json_writer_test.cpp b/data/tests/json_writer_test.cpp index 5a2708c7..461550e2 100644 --- a/data/tests/json_writer_test.cpp +++ b/data/tests/json_writer_test.cpp @@ -39,6 +39,22 @@ using join::Array; using join::Object; using join::SaxErrc; +/** + * @brief stream buffer refusing every write. + */ +struct RefusingBuffer : public std::streambuf +{ + std::streamsize xsputn (const char*, std::streamsize) override + { + return 0; + } + + int overflow (int) override + { + return traits_type::eof (); + } +}; + /** * @brief stream buffer failing on its first write. */ @@ -481,6 +497,16 @@ TEST (JsonWriter, flush) EXPECT_EQ (stream.str (), "[1"); EXPECT_EQ (jsonWriter.stopArray (), 0); EXPECT_EQ (stream.str (), "[1]"); + + RefusingBuffer refusing; + std::ostream refused (&refusing); + JsonWriter refusedWriter (refused); + EXPECT_EQ (refusedWriter.setInt (1), 0); + EXPECT_TRUE (refused.bad ()); + + refused.clear (); + EXPECT_EQ (refusedWriter.setString (std::string (2048, 'x')), 0); + EXPECT_TRUE (refused.bad ()); } /** diff --git a/data/tests/pack_writer_test.cpp b/data/tests/pack_writer_test.cpp index 19d83bcb..4cded5b9 100644 --- a/data/tests/pack_writer_test.cpp +++ b/data/tests/pack_writer_test.cpp @@ -45,6 +45,22 @@ using join::Array; using join::Object; using join::SaxErrc; +/** + * @brief stream buffer refusing every write. + */ +struct RefusingBuffer : public std::streambuf +{ + std::streamsize xsputn (const char*, std::streamsize) override + { + return 0; + } + + int overflow (int) override + { + return traits_type::eof (); + } +}; + /** * @brief Test setNull method. */ @@ -766,6 +782,16 @@ TEST (PackWriter, flush) EXPECT_EQ (packWriter.setNull (), 0); EXPECT_EQ (packWriter.stopArray (), 0); EXPECT_EQ (stream.str (), std::string ({'\x92', '\xC0', '\xC0'})); + + RefusingBuffer refusing; + std::ostream refused (&refusing); + PackWriter refusedWriter (refused); + EXPECT_EQ (refusedWriter.setInt (1), 0); + EXPECT_TRUE (refused.bad ()); + + refused.clear (); + EXPECT_EQ (refusedWriter.setString (std::string (2048, 'x')), 0); + EXPECT_TRUE (refused.bad ()); } /**