From a8b063d196c5daf66ce2813237705f069aeb47ce Mon Sep 17 00:00:00 2001 From: Fabian Meyer Date: Fri, 8 Nov 2024 12:28:04 +0100 Subject: [PATCH 1/4] Fix missing length checks in decoding traits Signed-off-by: Fabian Meyer --- .../Headers/ember/ber/detail/MultiByte.hpp | 6 +++- libember/Headers/ember/ber/traits/Boolean.hpp | 4 +++ .../Headers/ember/ber/traits/Integral.hpp | 4 +++ libember/Headers/ember/ber/traits/Length.hpp | 9 ++++++ .../ember/ber/traits/ObjectIdentifier.hpp | 1 + libember/Headers/ember/ber/traits/Real.hpp | 30 ++++++++++++++----- libember/Headers/ember/ber/traits/Tag.hpp | 5 ++++ 7 files changed, 51 insertions(+), 8 deletions(-) diff --git a/libember/Headers/ember/ber/detail/MultiByte.hpp b/libember/Headers/ember/ber/detail/MultiByte.hpp index 6e2ea14b..7af16ac1 100644 --- a/libember/Headers/ember/ber/detail/MultiByte.hpp +++ b/libember/Headers/ember/ber/detail/MultiByte.hpp @@ -97,7 +97,11 @@ namespace libember { namespace ber { namespace detail util::OctetStream::value_type byte = 0; do { - byte = input.front(); + if (input.empty()) + { + throw std::runtime_error("Not enough data"); + } + byte = input.front(); input.consume(); ++byteCount; result = (result << 7) | (byte & ~0x80); diff --git a/libember/Headers/ember/ber/traits/Boolean.hpp b/libember/Headers/ember/ber/traits/Boolean.hpp index f21d69d0..e5f17b6f 100644 --- a/libember/Headers/ember/ber/traits/Boolean.hpp +++ b/libember/Headers/ember/ber/traits/Boolean.hpp @@ -68,6 +68,10 @@ namespace libember { namespace ber static value_type decode(util::OctetStream& input, std::size_t) { + if (input.empty()) + { + throw std::runtime_error("Not enough data"); + } util::OctetStream::value_type const byte = input.front(); input.consume(); return (byte != 0); diff --git a/libember/Headers/ember/ber/traits/Integral.hpp b/libember/Headers/ember/ber/traits/Integral.hpp index 3503611f..2fe22511 100644 --- a/libember/Headers/ember/ber/traits/Integral.hpp +++ b/libember/Headers/ember/ber/traits/Integral.hpp @@ -118,6 +118,10 @@ namespace libember { namespace ber static value_type decode(util::OctetStream& input, std::size_t encodedLength) { + if (input.size() < encodedLength) + { + throw std::runtime_error("Not enough data"); + } typedef typename meta::MakeUnsigned::type unsigned_type; value_type value = 0; for (std::size_t index = 0; index < encodedLength; ++index) diff --git a/libember/Headers/ember/ber/traits/Length.hpp b/libember/Headers/ember/ber/traits/Length.hpp index c50c4f53..c3d6f47e 100644 --- a/libember/Headers/ember/ber/traits/Length.hpp +++ b/libember/Headers/ember/ber/traits/Length.hpp @@ -74,6 +74,11 @@ namespace libember { namespace ber static Length decode(util::OctetStream& input) { + if (input.empty()) + { + throw std::runtime_error("Not enough data"); + } + underlying_type length = input.front(); input.consume(); @@ -86,6 +91,10 @@ namespace libember { namespace ber } else { + if (input.size() < bytes) + { + throw std::runtime_error("Not enough data"); + } length = 0U; for (/* Nothing */; bytes > 0U; bytes -= 1U) { diff --git a/libember/Headers/ember/ber/traits/ObjectIdentifier.hpp b/libember/Headers/ember/ber/traits/ObjectIdentifier.hpp index f79172d3..7eb1c7c0 100644 --- a/libember/Headers/ember/ber/traits/ObjectIdentifier.hpp +++ b/libember/Headers/ember/ber/traits/ObjectIdentifier.hpp @@ -82,6 +82,7 @@ namespace libember { namespace ber static value_type decode(util::OctetStream& input, std::size_t size) { + // Note: Multibyte decoding already verifies validity of the given size. typedef ObjectIdentifier::value_type item_type; std::vector items; while(size > 0) diff --git a/libember/Headers/ember/ber/traits/Real.hpp b/libember/Headers/ember/ber/traits/Real.hpp index bcdebcf7..b10d5b9b 100644 --- a/libember/Headers/ember/ber/traits/Real.hpp +++ b/libember/Headers/ember/ber/traits/Real.hpp @@ -152,23 +152,34 @@ namespace libember { namespace ber static value_type decode(util::OctetStream& input, std::size_t encodedLength) { if (encodedLength == 0) + { return static_cast(0.0); + } + + if (encodedLength > input.size()) + { + throw std::runtime_error("Not enough data"); + } unsigned char const preamble = input.front(); input.consume(); - if (encodedLength == 1 && preamble == 0x40) + if ((encodedLength == 1) && (preamble == 0x40)) { return +std::numeric_limits::infinity(); } - else if (encodedLength == 1 && preamble == 0x41) + else if ((encodedLength == 1) && (preamble == 0x41)) { return -std::numeric_limits::infinity(); } - else if (encodedLength == 1 && preamble == 0x42) + else if ((encodedLength == 1) && (preamble == 0x42)) { return std::numeric_limits::quiet_NaN(); } + else if ((encodedLength == 1) && (preamble == 0x43)) + { + return static_cast(-0.0); + } else { unsigned long long bits = 0; @@ -176,14 +187,19 @@ namespace libember { namespace ber unsigned int const exponentLength = 1 + (preamble & 3); unsigned int const mantissaShift = ((preamble >> 2) & 3); + // Note: If (exponentLength > encodedLength - 1), the following call to decode will throw, + // so there is no need to check this separately. long long exponent = ber::decode(input, exponentLength); unsigned long long mantissa = ber::decode(input, encodedLength - exponentLength - 1) << mantissaShift; - while((mantissa & 0x7FFFF00000000000ULL) == 0x00) - mantissa <<= 8; + if (mantissa != 0) + { + while((mantissa & 0x7FFFF00000000000ULL) == 0x00) + mantissa <<= 8; - while((mantissa & 0x7FF0000000000000ULL) == 0x00) - mantissa <<= 1; + while((mantissa & 0x7FF0000000000000ULL) == 0x00) + mantissa <<= 1; + } mantissa &= 0x0FFFFFFFFFFFFFULL; bits = (static_cast(exponent + 1023) << 52) | mantissa; diff --git a/libember/Headers/ember/ber/traits/Tag.hpp b/libember/Headers/ember/ber/traits/Tag.hpp index 6441b641..852ced1b 100644 --- a/libember/Headers/ember/ber/traits/Tag.hpp +++ b/libember/Headers/ember/ber/traits/Tag.hpp @@ -65,6 +65,11 @@ namespace libember { namespace ber static value_type decode(util::OctetStream& input) { + if (input.empty()) + { + throw std::runtime_error("Not enough data"); + } + util::OctetStream::value_type byte = input.front(); input.consume(); From 4f54cc4fee0994dd972249f7db9969f7b5ac20ba Mon Sep 17 00:00:00 2001 From: Fabian Meyer Date: Fri, 8 Nov 2024 12:28:57 +0100 Subject: [PATCH 2/4] Fix incorrect encoding of -0.0 Signed-off-by: Fabian Meyer --- libember/Headers/ember/ber/traits/Real.hpp | 8 +++++++ libember/Headers/ember/util/SignBit.hpp | 28 ++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 libember/Headers/ember/util/SignBit.hpp diff --git a/libember/Headers/ember/ber/traits/Real.hpp b/libember/Headers/ember/ber/traits/Real.hpp index b10d5b9b..80c2edf1 100644 --- a/libember/Headers/ember/ber/traits/Real.hpp +++ b/libember/Headers/ember/ber/traits/Real.hpp @@ -15,6 +15,7 @@ #include "Integral.hpp" #include "../../meta/FunctionTraits.hpp" #include "../../util/TypePun.hpp" +#include "../../util/SignBit.hpp" //SimianIgnore @@ -62,6 +63,13 @@ namespace libember { namespace ber // 0x42 Indicates NaN output.append(0x42); } + else if ( + (value == static_cast(0.0)) && + util::signbit(value)) + { + // 0x43 Indicates -0.0 + output.append(0x43); + } else { double const real = value; diff --git a/libember/Headers/ember/util/SignBit.hpp b/libember/Headers/ember/util/SignBit.hpp new file mode 100644 index 00000000..6ae35bc2 --- /dev/null +++ b/libember/Headers/ember/util/SignBit.hpp @@ -0,0 +1,28 @@ +/* + libember -- C++ 03 implementation of the Ember+ Protocol + + Copyright (C) 2024 Lawo AG (http://www.lawo.com). + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef __LIBEMBER_UTIL_SIGNBIT_HPP +#define __LIBEMBER_UTIL_SIGNBIT_HPP + +#include "TypePun.hpp" + +namespace libember { namespace util +{ + inline bool signbit(float value) + { + return (type_pun(value) & 0x80000000U) != 0U; + } + + inline bool signbit(double value) + { + return (type_pun(value) & 0x8000000000000000LLU) != 0LLU; + } +} +} + +#endif // __LIBEMBER_UTIL_SIGNBIT_HPP From 55bdf71fb9323f80d0245d4261760583f84ccb4d Mon Sep 17 00:00:00 2001 From: Fabian Meyer Date: Fri, 8 Nov 2024 12:30:20 +0100 Subject: [PATCH 3/4] Fix whitespace and test error messages Signed-off-by: Fabian Meyer --- libember/Headers/ember/ber/detail/MultiByte.hpp | 1 - libember/Headers/ember/ber/traits/Boolean.hpp | 1 - libember/Headers/ember/ber/traits/Integral.hpp | 1 - libember/Headers/ember/ber/traits/Length.hpp | 1 - .../Headers/ember/ber/traits/ObjectIdentifier.hpp | 3 +-- libember/Headers/ember/ber/traits/Real.hpp | 9 ++++----- libember/Headers/ember/ber/traits/Tag.hpp | 1 - libember/Tests/ber/DynamicEncodeDecode.cpp | 14 +++++--------- libember/Tests/ber/StaticEncodeDecode.cpp | 8 ++++---- 9 files changed, 14 insertions(+), 25 deletions(-) diff --git a/libember/Headers/ember/ber/detail/MultiByte.hpp b/libember/Headers/ember/ber/detail/MultiByte.hpp index 7af16ac1..d266c49c 100644 --- a/libember/Headers/ember/ber/detail/MultiByte.hpp +++ b/libember/Headers/ember/ber/detail/MultiByte.hpp @@ -114,4 +114,3 @@ namespace libember { namespace ber { namespace detail } #endif // __LIBEMBER_BER_DETAIL_MULTIBYTE_HPP - diff --git a/libember/Headers/ember/ber/traits/Boolean.hpp b/libember/Headers/ember/ber/traits/Boolean.hpp index e5f17b6f..6e3de5f3 100644 --- a/libember/Headers/ember/ber/traits/Boolean.hpp +++ b/libember/Headers/ember/ber/traits/Boolean.hpp @@ -87,4 +87,3 @@ namespace libember { namespace ber } #endif // __LIBEMBER_BER_TRAITS_BOOLEAN_HPP - diff --git a/libember/Headers/ember/ber/traits/Integral.hpp b/libember/Headers/ember/ber/traits/Integral.hpp index 2fe22511..2e7b3b86 100644 --- a/libember/Headers/ember/ber/traits/Integral.hpp +++ b/libember/Headers/ember/ber/traits/Integral.hpp @@ -351,4 +351,3 @@ namespace libember { namespace ber } #endif // __LIBEMBER_BER_TRAITS_INTEGRAL_HPP - diff --git a/libember/Headers/ember/ber/traits/Length.hpp b/libember/Headers/ember/ber/traits/Length.hpp index c3d6f47e..7ce53b8b 100644 --- a/libember/Headers/ember/ber/traits/Length.hpp +++ b/libember/Headers/ember/ber/traits/Length.hpp @@ -110,4 +110,3 @@ namespace libember { namespace ber } #endif // __LIBEMBER_BER_TRAITS_LENGTH_HPP - diff --git a/libember/Headers/ember/ber/traits/ObjectIdentifier.hpp b/libember/Headers/ember/ber/traits/ObjectIdentifier.hpp index 7eb1c7c0..92b2d86c 100644 --- a/libember/Headers/ember/ber/traits/ObjectIdentifier.hpp +++ b/libember/Headers/ember/ber/traits/ObjectIdentifier.hpp @@ -44,7 +44,7 @@ namespace libember { namespace ber std::size_t length = 0; value_type::const_iterator first = value.begin(); value_type::const_iterator const last = value.end(); - + for (/* Nothing */; first != last; ++first) { length += detail::getMultiByteEncodedLength(*first); @@ -99,4 +99,3 @@ namespace libember { namespace ber } #endif // __LIBEMBER_BER_TRAITS_OBJECTIDENTIFIER_HPP - diff --git a/libember/Headers/ember/ber/traits/Real.hpp b/libember/Headers/ember/ber/traits/Real.hpp index 80c2edf1..c6530b0f 100644 --- a/libember/Headers/ember/ber/traits/Real.hpp +++ b/libember/Headers/ember/ber/traits/Real.hpp @@ -77,8 +77,8 @@ namespace libember { namespace ber if (bits != 0) { - long long const exponent = ((0x7FF0000000000000LL & bits) >> 52) - 1023; - unsigned long long mantissa = 0x000FFFFFFFFFFFFFULL & bits; + long long const exponent = ((0x7FF0000000000000LL & bits) >> 52) - 1023; + unsigned long long mantissa = 0x000FFFFFFFFFFFFFULL & bits; mantissa |= 0x0010000000000000ULL; while((mantissa & 0xFF) == 0x00) @@ -117,8 +117,8 @@ namespace libember { namespace ber if (bits != 0) { - long long const exponent = ((0x7FF0000000000000LL & bits) >> 52) - 1023; - unsigned long long mantissa = 0x000FFFFFFFFFFFFFULL & bits; + long long const exponent = ((0x7FF0000000000000LL & bits) >> 52) - 1023; + unsigned long long mantissa = 0x000FFFFFFFFFFFFFULL & bits; mantissa |= 0x0010000000000000ULL; while((mantissa & 0xFF) == 0x00) @@ -270,4 +270,3 @@ namespace libember { namespace ber //EndSimianIgnore #endif // __LIBEMBER_BER_TRAITS_REAL_HPP - diff --git a/libember/Headers/ember/ber/traits/Tag.hpp b/libember/Headers/ember/ber/traits/Tag.hpp index 852ced1b..4e750830 100644 --- a/libember/Headers/ember/ber/traits/Tag.hpp +++ b/libember/Headers/ember/ber/traits/Tag.hpp @@ -87,4 +87,3 @@ namespace libember { namespace ber } #endif // __LIBEMBER_BER_TRAITS_TAG_HPP - diff --git a/libember/Tests/ber/DynamicEncodeDecode.cpp b/libember/Tests/ber/DynamicEncodeDecode.cpp index ba191bdf..f82f395f 100644 --- a/libember/Tests/ber/DynamicEncodeDecode.cpp +++ b/libember/Tests/ber/DynamicEncodeDecode.cpp @@ -30,7 +30,7 @@ namespace TYPE_FLOAT, TYPE_BOOLEAN, TYPE_STRING, - + TYPE_COUNT }; @@ -161,7 +161,7 @@ int main(int, char const* const*) for (unsigned int i = 0; i < TEST_ITERATIONS; ++i) { Tag const appTag = generateApplicationTag(i); - Value const value = generateDynamicValue(i); + Value const value = generateDynamicValue(i); encodeExplicitlyTaggedFrame(testStream, appTag, value); } @@ -179,20 +179,17 @@ int main(int, char const* const*) Value const expectedValue = generateDynamicValue(i); decodeAndAssertTag(testStream, expectedTag); - + // Decode the outer frame length (and ignore it) libember::ber::decode(testStream); decodeAndAssertValue(testStream, expectedValue); } - /* - * Decode the previously encoded sequence of explicitly tagged value - * frames. - */ + // Make sure the previously encoded byte stream was consumed in its entirety. if (!testStream.empty()) { - throw std::runtime_error("Stream not empty after all values have been encoded."); + throw std::runtime_error("Stream not empty after all values have been decoded."); } } catch (std::exception const& e) @@ -209,4 +206,3 @@ int main(int, char const* const*) } //EndSimianIgnore - diff --git a/libember/Tests/ber/StaticEncodeDecode.cpp b/libember/Tests/ber/StaticEncodeDecode.cpp index 724a5f59..e3e0044f 100644 --- a/libember/Tests/ber/StaticEncodeDecode.cpp +++ b/libember/Tests/ber/StaticEncodeDecode.cpp @@ -29,7 +29,7 @@ namespace TYPE_FLOAT, TYPE_BOOLEAN, TYPE_STRING, - + TYPE_COUNT }; @@ -193,10 +193,11 @@ int main(int, char const* const*) break; } } - // Make the previously encoded byte stream was consumed in its entirety. + + // Make sure the previously encoded byte stream was consumed in its entirety. if (!testStream.empty()) { - throw std::runtime_error("Stream not empty after all values have been encoded."); + throw std::runtime_error("Stream not empty after all values have been decoded."); } } catch (std::exception const& e) @@ -213,4 +214,3 @@ int main(int, char const* const*) } //EndSimianIgnore - From d4a3b8fdc15ed31b554df2deda5cab29b28b1443 Mon Sep 17 00:00:00 2001 From: Fabian Meyer Date: Fri, 8 Nov 2024 12:31:20 +0100 Subject: [PATCH 4/4] Add tests for decoding of invalid lengths Signed-off-by: Fabian Meyer --- libember/Tests/CMakeLists.txt | 32 +++++ libember/Tests/ber/DecodeLengthCheck.cpp | 173 +++++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 libember/Tests/ber/DecodeLengthCheck.cpp diff --git a/libember/Tests/CMakeLists.txt b/libember/Tests/CMakeLists.txt index e5b3dac5..1873039f 100644 --- a/libember/Tests/CMakeLists.txt +++ b/libember/Tests/CMakeLists.txt @@ -43,6 +43,20 @@ target_link_libraries(libember-test-dynamic_encode_decode PRIVATE ember-headeron enable_warnings_on_target(libember-test-dynamic_encode_decode) +add_executable(libember-test-decode_length_check ber/DecodeLengthCheck.cpp) +set_target_properties(libember-test-decode_length_check + PROPERTIES + POSITION_INDEPENDENT_CODE ON + VISIBILITY_INLINES_HIDDEN ON + C_VISIBILITY_PRESET hidden + CXX_VISIBILITY_PRESET hidden + C_EXTENSIONS OFF + CXX_EXTENSIONS OFF + ) +target_link_libraries(libember-test-decode_length_check PRIVATE ember-headeronly) +enable_warnings_on_target(libember-test-decode_length_check) + + add_executable(libember-test-glow_value glow/GlowValue.cpp) set_target_properties(libember-test-glow_value PROPERTIES @@ -69,7 +83,25 @@ if (NOT CMAKE_BUILD_TYPE MATCHES "Debug") set_target_properties(libember-test-streambuffer PROPERTIES INTERPROCEDURAL_OPTIMIZATION ON) set_target_properties(libember-test-static_encode_decode PROPERTIES INTERPROCEDURAL_OPTIMIZATION ON) set_target_properties(libember-test-dynamic_encode_decode PROPERTIES INTERPROCEDURAL_OPTIMIZATION ON) + set_target_properties(libember-test-decode_length_check PROPERTIES INTERPROCEDURAL_OPTIMIZATION ON) set_target_properties(libember-test-glow_value PROPERTIES INTERPROCEDURAL_OPTIMIZATION ON) endif() endif() + +include(CTest) + +add_test(NAME length-negative_zero COMMAND libember-test-decode_length_check negative_zero) +add_test(NAME length-encoded_length COMMAND libember-test-decode_length_check encoded_length) +add_test(NAME length-exponent_length COMMAND libember-test-decode_length_check exponent_length) +add_test(NAME length-zero_mantissa COMMAND libember-test-decode_length_check zero_mantissa) +add_test(NAME length-preamble_length_not_one COMMAND libember-test-decode_length_check preamble_length_not_one) +add_test(NAME length-integral COMMAND libember-test-decode_length_check integral) +add_test(NAME length-boolean COMMAND libember-test-decode_length_check boolean) +add_test(NAME length-length_first_byte COMMAND libember-test-decode_length_check length_first_byte) +add_test(NAME length-length_second_byte COMMAND libember-test-decode_length_check length_second_byte) +add_test(NAME length-object_identifier_empty COMMAND libember-test-decode_length_check object_identifier_empty) +add_test(NAME length-object_identifier_too_short COMMAND libember-test-decode_length_check object_identifier_too_short) +add_test(NAME length-tag COMMAND libember-test-decode_length_check tag) +add_test(NAME length-tag_multibyte COMMAND libember-test-decode_length_check tag_multibyte) +add_test(NAME length-tag_multibyte_too_short COMMAND libember-test-decode_length_check tag_multibyte_too_short) diff --git a/libember/Tests/ber/DecodeLengthCheck.cpp b/libember/Tests/ber/DecodeLengthCheck.cpp new file mode 100644 index 00000000..e58525cd --- /dev/null +++ b/libember/Tests/ber/DecodeLengthCheck.cpp @@ -0,0 +1,173 @@ +/* + libember -- C++ 03 implementation of the Ember+ Protocol + + Copyright (C) 2024 Lawo AG (http://www.lawo.com). + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +*/ + +#include +#include +#include +#include "ember/ber/Ber.hpp" + +//SimianIgnore + +namespace +{ + /** + * Global definition of the length type used by this example. + */ + typedef libember::ber::Length LengthType; + + template + void assertDecodeThrows(libember::util::OctetStream& input) + { + try + { + libember::ber::decode(input); + } + catch (std::exception const&) + { + return; + } + throw std::logic_error("Decode did not throw."); + } + + template + void assertDecodeThrows(libember::util::OctetStream& input, std::size_t length) + { + try + { + libember::ber::decode(input, length); + } + catch (std::exception const&) + { + return; + } + throw std::logic_error("Decode did not throw."); + } +} + +int main(int argc, char const* const* argv) +{ + if (argc != 2) + { + std::cerr << "Test name required" << std::endl; + return 1; + } + + std::string test_name = argv[1]; + + try + { + libember::util::OctetStream testStream; + + if (test_name == "negative_zero") + { + // issue #120 + testStream.append(0x43); + float decoded = libember::ber::decode(testStream, testStream.size()); + if ((decoded != 0.0) || !libember::util::signbit(decoded)) + { + throw std::runtime_error("Encountered unexpected value in input stream."); + } + } + else if (test_name == "encoded_length") + { + assertDecodeThrows(testStream, testStream.size() + 1); + } + else if (test_name == "exponent_length") + { + testStream.append(0x03); + assertDecodeThrows(testStream, testStream.size()); + } + else if (test_name == "zero_mantissa") + { + testStream.append(0x00); + testStream.append(0x00); + float decoded = libember::ber::decode(testStream, testStream.size()); + if (decoded != 1.0f) + { + throw std::runtime_error("Encountered unexpected value in input stream."); + } + } + else if (test_name == "preamble_length_not_one") + { + // preamble 0x40 should be decoded as +Infinity, but due to length 2, is decoded as -1 instead (non-conforming to X.690) + testStream.append(0x40); + testStream.append(0x00); + float decoded = libember::ber::decode(testStream, testStream.size()); + if (decoded != -1.0f) + { + throw std::runtime_error("Encountered unexpected value in input stream."); + } + } + else if (test_name == "integral") + { + assertDecodeThrows(testStream, testStream.size() + 1); + } + else if (test_name == "boolean") + { + assertDecodeThrows(testStream, testStream.size() + 1); + } + else if (test_name == "length_first_byte") + { + assertDecodeThrows(testStream); + } + else if (test_name == "length_second_byte") + { + testStream.append(0x81U); + assertDecodeThrows(testStream); + } + else if (test_name == "object_identifier_empty") + { + assertDecodeThrows(testStream, testStream.size() + 1); + } + else if (test_name == "object_identifier_too_short") + { + testStream.append(0x80); + assertDecodeThrows(testStream, testStream.size()); + } + else if (test_name == "tag") + { + assertDecodeThrows(testStream); + } + else if (test_name == "tag_multibyte") + { + testStream.append(0x1F); + assertDecodeThrows(testStream); + } + else if (test_name == "tag_multibyte_too_short") + { + testStream.append(0x1F); + testStream.append(0x80); + assertDecodeThrows(testStream); + } + else + { + std::cerr << "Invalid test name" << std::endl; + return 1; + } + + // Make sure the previously encoded byte stream was consumed in its entirety. + if (!testStream.empty()) + { + throw std::runtime_error("Stream not empty after all values have been decoded."); + } + } + catch (std::exception const& e) + { + std::cerr << "ERROR: " << e.what() << std::endl; + return 1; + } + catch (...) + { + std::cerr << "ERROR: " << "An unknown error occurred." << std::endl; + return 1; + } + + return 0; +} + +//EndSimianIgnore