diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index d00afdd..bf1e865 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -16,8 +16,12 @@ jobs: - { os: "windows-2022", msvc-toolset: "v142", cxx-standard: "17", build-type: "Release" } - { os: "windows-2022", msvc-toolset: "v142", cxx-standard: "17", build-type: "Debug" } - - { os: "windows-2022", mingw-version: "9.4.0", cxx-standard: "17", build-type: "Release" } - - { os: "windows-2022", mingw-version: "9.4.0", cxx-standard: "17", build-type: "Debug" } + # fires -Werror=conversion on IntWrapper default template argument + #- { os: "windows-2022", mingw-version: "9.4.0", cxx-standard: "17", build-type: "Release" } + #- { os: "windows-2022", mingw-version: "9.4.0", cxx-standard: "17", build-type: "Debug" } + + - { os: "windows-2022", mingw-version: "11.2.0", cxx-standard: "17", build-type: "Release" } + - { os: "windows-2022", mingw-version: "11.2.0", cxx-standard: "17", build-type: "Debug" } runs-on: ${{matrix.os}} name: "\ diff --git a/runtime/ClangTidySuppressions.txt b/runtime/ClangTidySuppressions.txt index dba13ab..80251cd 100644 --- a/runtime/ClangTidySuppressions.txt +++ b/runtime/ClangTidySuppressions.txt @@ -51,6 +51,9 @@ google-explicit-constructor:src/zserio/Span.h:138 google-explicit-constructor:src/zserio/Span.h:150 google-explicit-constructor:src/zserio/Span.h:163 google-explicit-constructor:src/zserio/Span.h:176 +# This is requirement on type wrappers in Zserio C++17 extension +google-explicit-constructor:src/zserio/Types.h:59 +google-explicit-constructor:src/zserio/Types.h:66 # False positive, this is a template method. modernize-use-equals-default:src/zserio/Span.h:80 diff --git a/runtime/src/CMakeLists.txt b/runtime/src/CMakeLists.txt index e6576d0..229ac0b 100644 --- a/runtime/src/CMakeLists.txt +++ b/runtime/src/CMakeLists.txt @@ -39,6 +39,7 @@ set(ZSERIO_CPP17_RUNTIME_LIB_SRCS zserio/FloatUtil.cpp zserio/FloatUtil.h zserio/HashCodeUtil.h + zserio/OutOfRangeException.h zserio/RebindAlloc.h zserio/RuntimeArch.h zserio/SizeConvertUtil.cpp diff --git a/runtime/src/zserio/BitSizeOfCalculator.cpp b/runtime/src/zserio/BitSizeOfCalculator.cpp index a6781bf..c377eb9 100644 --- a/runtime/src/zserio/BitSizeOfCalculator.cpp +++ b/runtime/src/zserio/BitSizeOfCalculator.cpp @@ -7,7 +7,7 @@ namespace zserio { -static const std::array VARIN16_MAX_VALUES = { +static const std::array VARINT16_MAX_VALUES = { (UINT64_C(1) << (6)) - 1, (UINT64_C(1) << (6 + 8)) - 1, }; @@ -116,7 +116,7 @@ static uint64_t convertToAbsValue(T value) size_t bitSizeOfVarInt16(int16_t value) { - return bitSizeOfVarIntImpl(convertToAbsValue(value), VARIN16_MAX_VALUES, "varint16"); + return bitSizeOfVarIntImpl(convertToAbsValue(value), VARINT16_MAX_VALUES, "varint16"); } size_t bitSizeOfVarInt32(int32_t value) diff --git a/runtime/src/zserio/OutOfRangeException.h b/runtime/src/zserio/OutOfRangeException.h new file mode 100644 index 0000000..8f8c434 --- /dev/null +++ b/runtime/src/zserio/OutOfRangeException.h @@ -0,0 +1,20 @@ +#ifndef ZSERIO_OUT_OF_RANGE_EXCEPTION_H_INC +#define ZSERIO_OUT_OF_RANGE_EXCEPTION_H_INC + +#include "zserio/CppRuntimeException.h" + +namespace zserio +{ + +/** + * Exception thrown when a value is out of range. + */ +class OutOfRangeException : public CppRuntimeException +{ +public: + using CppRuntimeException::CppRuntimeException; +}; + +} // namespace zserio + +#endif // ifndef ZSERIO_OUT_OF_RANGE_EXCEPTION_H_INC diff --git a/runtime/src/zserio/Types.h b/runtime/src/zserio/Types.h index 928fb57..4660026 100644 --- a/runtime/src/zserio/Types.h +++ b/runtime/src/zserio/Types.h @@ -2,5 +2,844 @@ #define ZSERIO_TYPES_H_INC #include +#include +#include + +#include "zserio/OutOfRangeException.h" + +namespace zserio +{ + +/** Typedef for bit size type used in Zserio. */ +using BitSize = unsigned int; + +/** + * NumericLimits template provides a standardized way to query various properties of + * zserio numerical type wrappers. Inspired by std::numeric_limits. + * + * This information is provided via specializations of the NumericLimits template. All types provide information + * about their minimum and maximum value. + */ +template +struct NumericLimits +{}; + +namespace detail +{ + +/** + * Base type wrapper for all numeric types. + * + * Implements all necessary conversions and operators needed to allow to use particular type wrappers + * in all expressions as if they were built-in types. + * + * Limitations: + * + * * Implicit assignment between different wrapper types is not allowed. + */ +template +class NumericTypeWrapper +{ +public: + /** Typedef for the underlying value type. */ + using value_type = VALUE_TYPE; + + /** + * Empty constructor. + */ + constexpr explicit NumericTypeWrapper() noexcept : + m_value() + {} + + /** + * Implicit constructor from underlying value type. + * + * \param value Value to construct from. + */ + constexpr NumericTypeWrapper(value_type value) noexcept : + m_value(value) + {} + + /** + * Implicit conversion to underlying value type. + */ + constexpr operator value_type() const noexcept + { + return m_value; + } + + /** + * Operator overload needed to allow to use particular type wrappers in all expressions as if they + * were built-in types. + * + * Note that overload of operator value_type&() could be used instead, but it fires a warning on gcc. + */ + /** \{ */ + + constexpr NumericTypeWrapper& operator++() noexcept + { + ++m_value; + return *this; + } + + constexpr NumericTypeWrapper operator++(int) noexcept + { + NumericTypeWrapper old; + operator++(); + return old; + } + + constexpr NumericTypeWrapper& operator--() noexcept + { + --m_value; + return *this; + } + + constexpr NumericTypeWrapper operator--(int) noexcept + { + NumericTypeWrapper old; + operator--(); + return old; + } + + constexpr NumericTypeWrapper& operator+=(value_type value) noexcept + { + m_value += value; + return *this; + } + + constexpr NumericTypeWrapper& operator-=(value_type value) noexcept + { + m_value -= value; + return *this; + } + + constexpr NumericTypeWrapper& operator*=(value_type value) noexcept + { + m_value *= value; + return *this; + } + + constexpr NumericTypeWrapper& operator/=(value_type value) noexcept + { + m_value /= value; + return *this; + } + + constexpr NumericTypeWrapper& operator%=(value_type value) noexcept + { + m_value %= value; + return *this; + } + + constexpr NumericTypeWrapper& operator&=(value_type value) noexcept + { + m_value &= value; + return *this; + } + + constexpr NumericTypeWrapper& operator|=(value_type value) noexcept + { + m_value |= value; + return *this; + } + + constexpr NumericTypeWrapper& operator^=(value_type value) noexcept + { + m_value ^= value; + return *this; + } + + constexpr NumericTypeWrapper& operator<<=(value_type value) noexcept + { + m_value <<= value; + return *this; + } + + constexpr NumericTypeWrapper& operator>>=(value_type value) noexcept + { + m_value >>= value; + return *this; + } + + /** \} */ + +private: + value_type m_value; +}; + +class BoolWrapper : public NumericTypeWrapper +{ +public: + using NumericTypeWrapper::NumericTypeWrapper; +}; + +template +class IntWrapper : public NumericTypeWrapper +{ +private: + static_assert((BIT_SIZE + 7) / 8 <= sizeof(VALUE_TYPE), "BitSize doesn't fit to the VALUE_TYPE!"); + +public: + using NumericTypeWrapper::NumericTypeWrapper; +}; + +template +class DynIntWrapper : public IntWrapper +{ +public: + using IntWrapper::IntWrapper; +}; + +enum class VarIntType : uint8_t +{ + VAR16, + VAR32, + VAR64, + VAR, + VARSIZE +}; + +template +class VarIntWrapper : public NumericTypeWrapper +{ + static_assert( + VAR_TYPE != VarIntType::VARSIZE || std::is_unsigned_v, "VARSIZE must be unsigned!"); + +public: + using NumericTypeWrapper::NumericTypeWrapper; +}; + +enum class FloatType : uint8_t +{ + FLOAT16, + FLOAT32, + FLOAT64 +}; + +template +class FloatWrapper : public NumericTypeWrapper +{ +public: + using NumericTypeWrapper::NumericTypeWrapper; +}; + +// helper traits to check if range checking is needed (used in checkedCast) +template +struct needs_range_check +{}; + +template +inline constexpr bool needs_range_check_v = needs_range_check::value; + +template <> +struct needs_range_check : std::false_type +{}; + +template +struct needs_range_check> +{ + static constexpr bool value = !(sizeof(VALUE_TYPE) * 8 == BIT_SIZE && + (BIT_SIZE == 8 || BIT_SIZE == 16 || BIT_SIZE == 32 || BIT_SIZE == 64)); +}; + +template +struct needs_range_check> : std::true_type +{}; + +template +struct needs_range_check> +{ + static constexpr bool value = VAR_TYPE != VarIntType::VAR; +}; + +template +struct needs_range_check> : std::false_type +{ + // TODO[mikir]: Does it have sense to have range checking for Float16? +}; + +// range checker used by checkedCast, specialized for particular types when needed +template +struct RangeChecker +{ + static constexpr void check(typename T::value_type value) + { + if constexpr (std::is_signed_v) + { + if (value < NumericLimits::min() || value > NumericLimits::max()) + { + throw OutOfRangeException("Value '") + << value << "' out of range '<" << NumericLimits::min() << ", " + << NumericLimits::max() << ">'!"; + } + } + else + { + if (value > NumericLimits::max()) + { + throw OutOfRangeException("Value '") + << value << "' out of bounds '" << NumericLimits::max() << "'!"; + } + } + } +}; + +template +struct RangeChecker> +{ + static constexpr void check(VALUE_TYPE value, BitSize numBits) + { + if constexpr (std::is_signed_v) + { + if (value < NumericLimits>::min(numBits) || + value > NumericLimits>::max(numBits)) + { + throw OutOfRangeException("Value '") + << value << "' out of range '<" + << NumericLimits>::min(numBits) << ", " + << NumericLimits>::max(numBits) << ">'!"; + } + } + else + { + if (value > NumericLimits>::max(numBits)) + { + throw OutOfRangeException("Value '") + << value << "' out of bounds '" + << NumericLimits>::max(numBits) << "'!"; + } + } + } +}; + +} // namespace detail + +/** + * Specialization of NumericLimits template for bool type wrapper. + */ +template <> +struct NumericLimits +{ + static constexpr detail::BoolWrapper min() noexcept + { + return false; + } + + static constexpr detail::BoolWrapper max() noexcept + { + return true; + } +}; + +/** + * Specialization of NumericLimits template for integral types wrapper. + */ +template +struct NumericLimits> +{ + static constexpr detail::IntWrapper min() noexcept + { + if constexpr (!detail::needs_range_check_v>) + { + return std::numeric_limits::min(); + } + else if constexpr (std::is_signed_v) + { + return static_cast(-static_cast(1ULL << (BIT_SIZE - 1U))); + } + else + { + return 0; + } + } + + static constexpr detail::IntWrapper max() noexcept + { + if constexpr (!detail::needs_range_check_v>) + { + return std::numeric_limits::max(); + } + else if constexpr (std::is_signed_v) + { + return static_cast((1ULL << (BIT_SIZE - 1U)) - 1U); + } + else + { + return static_cast((1ULL << BIT_SIZE) - 1U); + } + } +}; + +/** + * Specialization of NumericLimits template for variable length integral types wrapper. + */ +template +struct NumericLimits> +{ + static constexpr detail::VarIntWrapper min() noexcept + { + if constexpr (std::is_signed_v) + { + if constexpr (VAR_TYPE == detail::VarIntType::VAR) + { + return std::numeric_limits::min(); + } + else + { + return -max(); + } + } + else + { + return 0; + } + } + + static constexpr detail::VarIntWrapper max() noexcept + { + if constexpr (VAR_TYPE == detail::VarIntType::VAR16) + { + return static_cast((UINT64_C(1) << (FIRST_BYTE_BITS + 8)) - 1); + } + else if constexpr (VAR_TYPE == detail::VarIntType::VAR32) + { + return static_cast((UINT64_C(1) << (FIRST_BYTE_BITS + 7 + 7 + 8)) - 1); + } + else if constexpr (VAR_TYPE == detail::VarIntType::VAR64) + { + return static_cast((UINT64_C(1) << (FIRST_BYTE_BITS + 7 + 7 + 7 + 7 + 7 + 7 + 8)) - 1); + } + else if constexpr (VAR_TYPE == detail::VarIntType::VAR) + { + return std::numeric_limits::max(); + } + else // VARSIZE + { + return (UINT64_C(1) << (2 + 7 + 7 + 7 + 8)) - 1; + } + } + +private: + static constexpr uint64_t FIRST_BYTE_BITS = std::is_signed_v ? 6 : 7; +}; + +/** + * Specialization of NumericLimits template for dynamic length integral types wrapper. + */ +template +struct NumericLimits> +{ + static constexpr detail::DynIntWrapper min(BitSize numBits) + { + if constexpr (std::is_signed_v) + { + checkNumBits(numBits); + + return minSignedValue(numBits); + } + else + { + return 0; + } + } + + static constexpr detail::DynIntWrapper max(BitSize numBits) + { + checkNumBits(numBits); + + if constexpr (std::is_signed_v) + { + return maxSignedValue(numBits); + } + else + { + return maxUnsignedValue(numBits); + } + } + +private: + static constexpr void checkNumBits(BitSize numBits) + { + if (numBits < 1 || numBits > sizeof(VALUE_TYPE) * 8) + { + throw OutOfRangeException("Dynamic bit field numBits '") + << numBits << "' out of range '<1, " << sizeof(VALUE_TYPE) * 8 << ">'!"; + } + } + + static constexpr VALUE_TYPE minSignedValue(BitSize numBits) + { + if constexpr (std::is_same_v) + { + if (numBits == 64) + { + return INT64_MIN; + } + } + + return static_cast(-static_cast(1ULL << (numBits - 1U))); + } + + static constexpr VALUE_TYPE maxSignedValue(BitSize numBits) + { + if constexpr (std::is_same_v) + { + if (numBits == 64) + { + return INT64_MAX; + } + } + + return static_cast((1ULL << (numBits - 1U)) - 1U); + } + + static constexpr VALUE_TYPE maxUnsignedValue(BitSize numBits) + { + if constexpr (std::is_same_v) + { + if (numBits == 64) + { + return UINT64_MAX; + } + } + + return static_cast((1ULL << numBits) - 1U); + } +}; + +/** + * Specialization of NumericLimits template for float16 type wrapper. + * + * Hard-coded constants are used to allow the limits to be constexpr. + */ +template <> +struct NumericLimits> +{ + static constexpr detail::FloatWrapper min() noexcept + { + return 6.103515625e-05F; + } + + static constexpr detail::FloatWrapper max() noexcept + { + return 65504.F; + } +}; + +/** + * Specialization of NumericLimits template for float32 type wrapper. + */ +template <> +struct NumericLimits> +{ + static constexpr detail::FloatWrapper min() noexcept + { + return std::numeric_limits::min(); + } + + static constexpr detail::FloatWrapper max() noexcept + { + return std::numeric_limits::max(); + } +}; + +/** + * Specialization of NumericLimits template for float64 type wrapper. + */ +template <> +struct NumericLimits> +{ + static constexpr detail::FloatWrapper min() noexcept + { + return std::numeric_limits::min(); + } + + static constexpr detail::FloatWrapper max() noexcept + { + return std::numeric_limits::max(); + } +}; + +/** + * Utility to safely construct zserio numeric type wrapper from its underlying value with range checking. + * + * \param value Underlying value to convert to the type wrapper. + * + * \return Numeric type wrapper constructed after range checking (if needed). + * + * \throw OutOfRangeException when the underlying value is out of range of the zserio numeric type. + */ +template , T>, int> = 0> +constexpr T fromCheckedValue(typename T::value_type value) noexcept(!detail::needs_range_check_v) +{ + if constexpr (detail::needs_range_check_v) + { + detail::RangeChecker::check(value); + } + + return T(value); +} + +/** + * Utility to safely construct zserio numeric type wrapper from its underlying value with range checking. + * + * Overload for dynamic length types wrappers. + * + * \param value Underlying value to convert to the type wrapper. + * \param numBits Number of bits as a length of a dynamic length numeric type. + * + * \return Numeric type wrapper constructed after range checking (if needed). + * + * \throw OutOfRangeException when the underlying value is out of range of the zserio numeric type. + */ +template , T>, int> = 0> +constexpr T fromCheckedValue(typename T::value_type value, BitSize numBits) +{ + detail::RangeChecker::check(value, numBits); + + return T(value); +} + +/** + * Utility to get checked underlying value from zserio numeric type wrapper. + * + * \param wrapper Numeric type wrapper to use. + * + * \return Built-in numeric type casted from numeric type wrapper after range checking (if needed). + * + * \throw OutOfRangeException when the underlying value is out of range of the zserio numeric type. + */ +template , T>, int> = 0> +constexpr typename T::value_type toCheckedValue(T wrapper) noexcept(!detail::needs_range_check_v) +{ + if constexpr (detail::needs_range_check_v) + { + detail::RangeChecker::check(wrapper); + } + + return wrapper; +} + +/** + * Utility to get checked underlying value from zserio numeric type wrapper. + * + * Overload for dynamic length types wrappers. + * + * \param wrapper Numeric type wrapper to use. + * \param numBits Number of bits as a length of a dynamic length numeric type. + * + * \return Built-in numeric type casted from numeric type wrapper after range checking (if needed). + * + * \throw OutOfRangeException when the underlying value is out of range of the zserio numeric type. + */ +template , T>, int> = 0> +constexpr typename T::value_type toCheckedValue(T wrapper, BitSize numBits) +{ + detail::RangeChecker::check(wrapper, numBits); + + return wrapper; +} + +/** + * Utility function which ensures that static_cast is called on underlying types during casting of wrappers. + * + * \param wrapper Numeric type wrapper of type T to be converted. + * + * \return Numeric type wrapper of type R. + */ +template , T> && + std::is_base_of_v, R>, + int> = 0> +R typeCast(T wrapper) +{ + return static_cast(wrapper); +} + +/** Typedef for a zserio type. */ +/** \{ */ + +using Bool = detail::BoolWrapper; + +using Int8 = detail::IntWrapper; +using Int16 = detail::IntWrapper; +using Int32 = detail::IntWrapper; +using Int64 = detail::IntWrapper; + +using UInt8 = detail::IntWrapper; +using UInt16 = detail::IntWrapper; +using UInt32 = detail::IntWrapper; +using UInt64 = detail::IntWrapper; + +using Int1 = detail::IntWrapper; +using Int2 = detail::IntWrapper; +using Int3 = detail::IntWrapper; +using Int4 = detail::IntWrapper; +using Int5 = detail::IntWrapper; +using Int6 = detail::IntWrapper; +using Int7 = detail::IntWrapper; +using Int9 = detail::IntWrapper; +using Int10 = detail::IntWrapper; +using Int11 = detail::IntWrapper; +using Int12 = detail::IntWrapper; +using Int13 = detail::IntWrapper; +using Int14 = detail::IntWrapper; +using Int15 = detail::IntWrapper; +using Int17 = detail::IntWrapper; +using Int18 = detail::IntWrapper; +using Int19 = detail::IntWrapper; +using Int20 = detail::IntWrapper; +using Int21 = detail::IntWrapper; +using Int22 = detail::IntWrapper; +using Int23 = detail::IntWrapper; +using Int24 = detail::IntWrapper; +using Int25 = detail::IntWrapper; +using Int26 = detail::IntWrapper; +using Int27 = detail::IntWrapper; +using Int28 = detail::IntWrapper; +using Int29 = detail::IntWrapper; +using Int30 = detail::IntWrapper; +using Int31 = detail::IntWrapper; +using Int33 = detail::IntWrapper; +using Int34 = detail::IntWrapper; +using Int35 = detail::IntWrapper; +using Int36 = detail::IntWrapper; +using Int37 = detail::IntWrapper; +using Int38 = detail::IntWrapper; +using Int39 = detail::IntWrapper; +using Int40 = detail::IntWrapper; +using Int41 = detail::IntWrapper; +using Int42 = detail::IntWrapper; +using Int43 = detail::IntWrapper; +using Int44 = detail::IntWrapper; +using Int45 = detail::IntWrapper; +using Int46 = detail::IntWrapper; +using Int47 = detail::IntWrapper; +using Int48 = detail::IntWrapper; +using Int49 = detail::IntWrapper; +using Int50 = detail::IntWrapper; +using Int51 = detail::IntWrapper; +using Int52 = detail::IntWrapper; +using Int53 = detail::IntWrapper; +using Int54 = detail::IntWrapper; +using Int55 = detail::IntWrapper; +using Int56 = detail::IntWrapper; +using Int57 = detail::IntWrapper; +using Int58 = detail::IntWrapper; +using Int59 = detail::IntWrapper; +using Int60 = detail::IntWrapper; +using Int61 = detail::IntWrapper; +using Int62 = detail::IntWrapper; +using Int63 = detail::IntWrapper; + +using UInt1 = detail::IntWrapper; +using UInt2 = detail::IntWrapper; +using UInt3 = detail::IntWrapper; +using UInt4 = detail::IntWrapper; +using UInt5 = detail::IntWrapper; +using UInt6 = detail::IntWrapper; +using UInt7 = detail::IntWrapper; +using UInt9 = detail::IntWrapper; +using UInt10 = detail::IntWrapper; +using UInt11 = detail::IntWrapper; +using UInt12 = detail::IntWrapper; +using UInt13 = detail::IntWrapper; +using UInt14 = detail::IntWrapper; +using UInt15 = detail::IntWrapper; +using UInt17 = detail::IntWrapper; +using UInt18 = detail::IntWrapper; +using UInt19 = detail::IntWrapper; +using UInt20 = detail::IntWrapper; +using UInt21 = detail::IntWrapper; +using UInt22 = detail::IntWrapper; +using UInt23 = detail::IntWrapper; +using UInt24 = detail::IntWrapper; +using UInt25 = detail::IntWrapper; +using UInt26 = detail::IntWrapper; +using UInt27 = detail::IntWrapper; +using UInt28 = detail::IntWrapper; +using UInt29 = detail::IntWrapper; +using UInt30 = detail::IntWrapper; +using UInt31 = detail::IntWrapper; +using UInt33 = detail::IntWrapper; +using UInt34 = detail::IntWrapper; +using UInt35 = detail::IntWrapper; +using UInt36 = detail::IntWrapper; +using UInt37 = detail::IntWrapper; +using UInt38 = detail::IntWrapper; +using UInt39 = detail::IntWrapper; +using UInt40 = detail::IntWrapper; +using UInt41 = detail::IntWrapper; +using UInt42 = detail::IntWrapper; +using UInt43 = detail::IntWrapper; +using UInt44 = detail::IntWrapper; +using UInt45 = detail::IntWrapper; +using UInt46 = detail::IntWrapper; +using UInt47 = detail::IntWrapper; +using UInt48 = detail::IntWrapper; +using UInt49 = detail::IntWrapper; +using UInt50 = detail::IntWrapper; +using UInt51 = detail::IntWrapper; +using UInt52 = detail::IntWrapper; +using UInt53 = detail::IntWrapper; +using UInt54 = detail::IntWrapper; +using UInt55 = detail::IntWrapper; +using UInt56 = detail::IntWrapper; +using UInt57 = detail::IntWrapper; +using UInt58 = detail::IntWrapper; +using UInt59 = detail::IntWrapper; +using UInt60 = detail::IntWrapper; +using UInt61 = detail::IntWrapper; +using UInt62 = detail::IntWrapper; +using UInt63 = detail::IntWrapper; + +using DynInt8 = detail::DynIntWrapper; +using DynInt16 = detail::DynIntWrapper; +using DynInt32 = detail::DynIntWrapper; +using DynInt64 = detail::DynIntWrapper; + +using DynUInt8 = detail::DynIntWrapper; +using DynUInt16 = detail::DynIntWrapper; +using DynUInt32 = detail::DynIntWrapper; +using DynUInt64 = detail::DynIntWrapper; + +using VarInt16 = detail::VarIntWrapper; +using VarInt32 = detail::VarIntWrapper; +using VarInt64 = detail::VarIntWrapper; +using VarInt = detail::VarIntWrapper; + +using VarUInt16 = detail::VarIntWrapper; +using VarUInt32 = detail::VarIntWrapper; +using VarUInt64 = detail::VarIntWrapper; +using VarUInt = detail::VarIntWrapper; + +using VarSize = detail::VarIntWrapper; + +using Float16 = detail::FloatWrapper; +using Float32 = detail::FloatWrapper; +using Float64 = detail::FloatWrapper; + +/** \} */ + +/** + * Allows to append NumericTypeWrapper to CppRuntimeException. + * + * \param exception Exception to modify. + * \param bitBuffer Bit buffer value. + * + * \return Reference to the exception to allow operator chaining. + */ +template +CppRuntimeException& operator<<(CppRuntimeException& exception, detail::NumericTypeWrapper value) +{ + return exception << static_cast(value); +} + +} // namespace zserio #endif // ifndef ZSERIO_TYPES_H_INC diff --git a/runtime/test/CMakeLists.txt b/runtime/test/CMakeLists.txt index 24fde0d..9c5cb57 100644 --- a/runtime/test/CMakeLists.txt +++ b/runtime/test/CMakeLists.txt @@ -11,6 +11,12 @@ project(ZserioCpp17RuntimeTest) include(gtest_utils) gtest_add_library("${ZSERIO_CPP17_PROJECT_ROOT}/3rdparty/cpp/googletest") +compiler_set_warnings() +compiler_set_warnings_as_errors() +if (SANITIZERS_ENABLED) + compiler_set_undefined_sanitizer() +endif () + set(ZSERIO_CPP17_RUNTIME_TEST_SRCS zserio/BitBufferTest.cpp zserio/BitFieldUtilTest.cpp @@ -26,10 +32,12 @@ set(ZSERIO_CPP17_RUNTIME_TEST_SRCS zserio/FileUtilTest.cpp zserio/FloatUtilTest.cpp zserio/HashCodeUtilTest.cpp + zserio/OutOfRangeExceptionTest.cpp zserio/SizeConvertUtilTest.cpp zserio/SpanTest.cpp zserio/StringConvertUtilTest.cpp zserio/TrackingAllocator.h + zserio/TypesTest.cpp ) add_executable(${PROJECT_NAME} ${ZSERIO_CPP17_RUNTIME_TEST_SRCS}) diff --git a/runtime/test/zserio/OutOfRangeExceptionTest.cpp b/runtime/test/zserio/OutOfRangeExceptionTest.cpp new file mode 100644 index 0000000..759b09f --- /dev/null +++ b/runtime/test/zserio/OutOfRangeExceptionTest.cpp @@ -0,0 +1,17 @@ +#include "gtest/gtest.h" +#include "zserio/OutOfRangeException.h" + +namespace zserio +{ + +TEST(OutOfRangeExceptionTest, correctTypeAfterAppend) +{ + ASSERT_THROW( + { + throw OutOfRangeException() + << "Test that appending using operator<< persists the exception type!"; + }, + OutOfRangeException); +} + +} // namespace zserio diff --git a/runtime/test/zserio/TypesTest.cpp b/runtime/test/zserio/TypesTest.cpp new file mode 100644 index 0000000..7abf91e --- /dev/null +++ b/runtime/test/zserio/TypesTest.cpp @@ -0,0 +1,786 @@ +#include "gtest/gtest.h" +#include "zserio/Types.h" + +namespace zserio +{ + +static void testBoolOperators() +{ + Bool valueMin = NumericLimits::min(); + Bool valueMax = NumericLimits::max(); + + // constructor + Bool copy(valueMin); + Bool copy2 = valueMin; + (void)copy; + (void)copy2; + + // logical + ASSERT_FALSE(!valueMax); + ASSERT_FALSE(valueMin && valueMax); + ASSERT_TRUE(valueMin || valueMax); + + // comparison + ASSERT_FALSE(valueMin == valueMax); + ASSERT_TRUE(valueMin != valueMax); + ASSERT_TRUE(valueMin < valueMax); + ASSERT_FALSE(valueMin > valueMax); + ASSERT_TRUE(valueMin <= valueMax); + ASSERT_FALSE(valueMin >= valueMax); + + // ternary + ASSERT_TRUE(valueMin ? valueMin : valueMax); +} + +template +void testIntOperators(typename T::value_type minValue = NumericLimits::min()) +{ + T value0; + T valueMin = minValue; + Int64 valueMax = NumericLimits::max(); + + // constructor + Int64 copy(valueMin); + Int64 copy2 = Int64(valueMin); + (void)copy; + (void)copy2; + + // arithmetic + Int64 value = +valueMin; + value = -valueMin; + value = valueMin + valueMax; + value = valueMin - valueMax; + value = valueMin * valueMax; + if (valueMax != 0) // == 0 for Int1 + { + value = valueMin / valueMax; + value = valueMin % valueMax; + } + + // increment / decrement + ++value; + --value; + value++; + value--; + + // logical + ASSERT_FALSE(!valueMin); + ASSERT_TRUE(valueMin && valueMax); + ASSERT_TRUE(valueMin || valueMax); + + // comparison + ASSERT_FALSE(valueMin == valueMax); + ASSERT_TRUE(valueMin != valueMax); + ASSERT_TRUE(valueMin < valueMax); + ASSERT_FALSE(valueMin > valueMax); + ASSERT_TRUE(valueMin <= valueMax); + ASSERT_FALSE(valueMin >= valueMax); + + // assignment + value = Int64(valueMin); + value += valueMin; + value -= valueMin; + value *= valueMin; + if (valueMax != 0) // == 0 for Int1 + { + value /= valueMax; + value %= valueMax; + } + + // ternary + value = Int64(valueMin ? valueMin : value0); + value = (valueMin ? +(valueMin) : +(valueMax)); + value = (valueMin ? +(valueMin + value0) : +(valueMax + value0)); + value = (valueMin ? valueMin + value0 : valueMax + value0); + value = (valueMin ? valueMin : valueMax); + + (void)value; +} + +template +void testUIntOperators(typename T::value_type minValue = NumericLimits::min()) +{ + T value0; + T valueMin = minValue; + ASSERT_EQ(value0, valueMin); + UInt64 valueMax = NumericLimits::max(); + + // constructor + UInt64 copy(valueMin); + UInt64 copy2 = UInt64(valueMin); + (void)copy; + (void)copy2; + + // arithmetic + UInt64 value = +valueMin; + value = -valueMax; + value = valueMin + valueMax; + value = valueMin - valueMax; + value = valueMin * valueMax; + value = valueMin / valueMax; + value = valueMin % valueMax; + value = ~valueMin; + value = valueMin & valueMax; + value = valueMin | valueMax; + value = valueMin ^ valueMax; + if constexpr (sizeof(T) < 4) // bypass integral promotion + { + value = valueMax << value0; + value = valueMax >> value0; + } + else + { + value = valueMin << value0; + value = valueMin >> value0; + } + + // increment / decrement + ++value; + --value; + value++; + value--; + + // logical + ASSERT_FALSE(!valueMax); + ASSERT_FALSE(valueMin && valueMax); + ASSERT_TRUE(valueMin || valueMax); + + // comparison + ASSERT_FALSE(valueMin == valueMax); + ASSERT_TRUE(valueMin != valueMax); + ASSERT_TRUE(valueMin < valueMax); + ASSERT_FALSE(valueMin > valueMax); + ASSERT_TRUE(valueMin <= valueMax); + ASSERT_FALSE(valueMin >= valueMax); + + // assignment + value = UInt64(valueMin); + value += valueMin; + value -= valueMin; + value *= valueMin; + value /= valueMax; + value %= valueMax; + value &= valueMin; + value |= valueMin; + value ^= valueMin; + value <<= value0; + value >>= value0; + + // ternary + value = UInt64(valueMin ? valueMin : value0); + value = (valueMin ? +(valueMin) : +(valueMax)); + value = (valueMin ? +(valueMin + value0) : +(valueMax + value0)); + value = (valueMin ? valueMin + value0 : valueMax + value0); + value = (valueMin ? valueMin : valueMax); + + (void)value; +} + +template +struct FloatTypeHelper +{ + using type = Float32; +}; + +template <> +struct FloatTypeHelper +{ + using type = Float64; +}; + +template +void testFloatOperators() +{ + using float_wrapper = typename FloatTypeHelper::type; + + T value0; + T valueMin = NumericLimits::min(); + float_wrapper valueMax = NumericLimits::max(); + + // constructor + float_wrapper copy(valueMin); + float_wrapper copy2 = float_wrapper(valueMin); + (void)copy; + (void)copy2; + + // arithmetic + float_wrapper value = +valueMin; + value = -valueMin; + value = valueMin + valueMax; + value = valueMin - valueMax; + value = valueMin * valueMax; + value = valueMin / value0; + + // increment / decrement + ++value; + --value; + value++; + value--; + + // comparison + ASSERT_TRUE(valueMin < valueMax); + ASSERT_FALSE(valueMin > valueMax); + ASSERT_TRUE(valueMin <= valueMax); + ASSERT_FALSE(valueMin >= valueMax); + + // assignment + value = float_wrapper(valueMin); + value += valueMin; + value -= valueMin; + value *= valueMin; + value /= valueMax; + + // ternary + value = float_wrapper(valueMin < 0 ? valueMin : value0); + value = (valueMin < 0 ? +(valueMin) : +(valueMax)); + value = (valueMin < 0 ? +(valueMin + value0) : +(valueMax + value0)); + value = (valueMin < 0 ? valueMin + value0 : valueMax + value0); + value = (valueMin < 0 ? valueMin : valueMax); + + (void)value; +} + +template +void testSignedStdInt() +{ + using value_type = typename T::value_type; + + static_assert(NumericLimits::min() == std::numeric_limits::min(), "shall be constexpr"); + static_assert(NumericLimits::max() == std::numeric_limits::max(), "shall be constexpr"); + + static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); + static_assert(noexcept(fromCheckedValue(std::declval())), "shall be noexcept"); + static_assert(noexcept(toCheckedValue(std::declval())), "shall be noexcept"); + + const T minValue(NumericLimits::min()); + ASSERT_EQ(NumericLimits::min(), minValue); + + const T maxValue(NumericLimits::max()); + ASSERT_EQ(NumericLimits::max(), maxValue); + + T checkedMin = fromCheckedValue(NumericLimits::min()); + ASSERT_EQ(NumericLimits::min(), toCheckedValue(checkedMin)); + + T checkedMax = fromCheckedValue(NumericLimits::max()); + ASSERT_EQ(NumericLimits::max(), toCheckedValue(checkedMax)); + + T value; + value = static_cast(0); + ASSERT_EQ(0, value); + value = static_cast(1); + ASSERT_EQ(1, value); + value = static_cast(-1); + ASSERT_EQ(-1, value); + + testIntOperators(); +} + +template +void testUnsignedStdInt() +{ + using value_type = typename T::value_type; + + static_assert(NumericLimits::min() == std::numeric_limits::min(), "shall be constexpr"); + static_assert(NumericLimits::max() == std::numeric_limits::max(), "shall be constexpr"); + + static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); + static_assert(noexcept(fromCheckedValue(std::declval())), "shall be noexcept"); + static_assert(noexcept(toCheckedValue(std::declval())), "shall be noexcept"); + + const T minValue(NumericLimits::min()); + ASSERT_EQ(NumericLimits::min(), minValue); + + const T maxValue(NumericLimits::max()); + ASSERT_EQ(NumericLimits::max(), maxValue); + + T checkedMin = fromCheckedValue(NumericLimits::min()); + ASSERT_EQ(NumericLimits::min(), toCheckedValue(checkedMin)); + + T checkedMax = fromCheckedValue(NumericLimits::max()); + ASSERT_EQ(NumericLimits::max(), toCheckedValue(checkedMax)); + + T value; + value = static_cast(0); + ASSERT_EQ(0, value); + value = static_cast(1); + ASSERT_EQ(1, value); + value = static_cast(2); + ASSERT_EQ(2, value); + + testUIntOperators(); +} + +template +void testSignedInt() +{ + using value_type = typename T::value_type; + + static_assert(NumericLimits::min() == MIN_VALUE, "shall be constexpr"); + static_assert(NumericLimits::max() == MAX_VALUE, "shall be constexpr"); + + static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); + static_assert( + IS_NOEXCEPT == noexcept(fromCheckedValue(std::declval())), "unexpected noexcept"); + static_assert(IS_NOEXCEPT == noexcept(toCheckedValue(std::declval())), "unexpected noexcept"); + + const T minValue(NumericLimits::min()); + ASSERT_EQ(NumericLimits::min(), minValue); + + const T maxValue(NumericLimits::max()); + ASSERT_EQ(NumericLimits::max(), maxValue); + + T checkedMin = fromCheckedValue(NumericLimits::min()); + ASSERT_EQ(NumericLimits::min(), toCheckedValue(checkedMin)); + + T checkedMax = fromCheckedValue(NumericLimits::max()); + ASSERT_EQ(NumericLimits::max(), toCheckedValue(checkedMax)); + + T value; + ASSERT_EQ(0, value); + value = -1; + ASSERT_EQ(-1, value); + value = 0; + ASSERT_EQ(0, value); + + if constexpr (!IS_NOEXCEPT) + { + ASSERT_THROW(fromCheckedValue(NumericLimits::min() - 1), OutOfRangeException); + ASSERT_THROW(fromCheckedValue(NumericLimits::max() + 1), OutOfRangeException); + const T underMin = minValue - 1; + ASSERT_THROW(toCheckedValue(underMin), OutOfRangeException); + const T overMax = maxValue + 1; + ASSERT_THROW(toCheckedValue(overMax), OutOfRangeException); + } + + testIntOperators(); +} + +template +void testUnsignedInt() +{ + using value_type = typename T::value_type; + + static_assert(NumericLimits::min() == 0, "shall be constexpr"); + static_assert(NumericLimits::max() == MAX_VALUE, "shall be constexpr"); + + static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); + static_assert( + IS_NOEXCEPT == noexcept(fromCheckedValue(std::declval())), "unexpected noexcept"); + static_assert(IS_NOEXCEPT == noexcept(toCheckedValue(std::declval())), "unexpected noexcept"); + + const T minValue(NumericLimits::min()); + ASSERT_EQ(NumericLimits::min(), minValue); + + const T maxValue(NumericLimits::max()); + ASSERT_EQ(NumericLimits::max(), maxValue); + + T checkedMin = fromCheckedValue(NumericLimits::min()); + ASSERT_EQ(NumericLimits::min(), toCheckedValue(checkedMin)); + + T checkedMax = fromCheckedValue(NumericLimits::max()); + ASSERT_EQ(NumericLimits::max(), toCheckedValue(checkedMax)); + + T value; + ASSERT_EQ(0, value); + value = 1; + ASSERT_EQ(1, value); + value = NumericLimits::max() - 1; + ASSERT_EQ(NumericLimits::max() - 1, value); + + if constexpr (!IS_NOEXCEPT) + { + ASSERT_THROW(fromCheckedValue(NumericLimits::max() + 1), OutOfRangeException); + const T overMax = maxValue + 1; + ASSERT_THROW(toCheckedValue(overMax), OutOfRangeException); + } + + testUIntOperators(); +} + +template +void testSignedDynInt() +{ + using value_type = typename T::value_type; + + static_assert(NumericLimits::min(8) == -128, "shall be constexpr"); + static_assert(NumericLimits::max(8) == 127, "shall be constexpr"); + + static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); + static_assert(!noexcept(fromCheckedValue(std::declval(), std::declval())), + "shall not be noexcept"); + static_assert( + !noexcept(toCheckedValue(std::declval(), std::declval())), "shall not be noexcept"); + + ASSERT_THROW(fromCheckedValue(0, 0), OutOfRangeException); + ASSERT_THROW(fromCheckedValue(0, sizeof(value_type) * 8 + 1), OutOfRangeException); + + for (BitSize numBits = 1; numBits <= sizeof(value_type) * 8; ++numBits) + { + const T minValue(NumericLimits::min(numBits)); + ASSERT_EQ(NumericLimits::min(numBits), minValue); + + const T maxValue(NumericLimits::max(numBits)); + ASSERT_EQ(NumericLimits::max(numBits), maxValue); + + T checkedMin = fromCheckedValue(NumericLimits::min(numBits), numBits); + ASSERT_EQ(NumericLimits::min(numBits), toCheckedValue(checkedMin, numBits)); + + T checkedMax = fromCheckedValue(NumericLimits::max(numBits), numBits); + ASSERT_EQ(NumericLimits::max(numBits), toCheckedValue(checkedMax, numBits)); + + T value; + ASSERT_EQ(0, value); + value = -1; + ASSERT_EQ(-1, value); + value = 0; + ASSERT_EQ(0, value); + + if (numBits < sizeof(value_type) * 8) + { + ASSERT_THROW(fromCheckedValue(NumericLimits::min(numBits) - 1, numBits), OutOfRangeException) + << numBits; + ASSERT_THROW(fromCheckedValue(NumericLimits::max(numBits) + 1, numBits), OutOfRangeException); + const T underMin = minValue - 1; + ASSERT_THROW(toCheckedValue(underMin, numBits), OutOfRangeException); + const T overMax = maxValue + 1; + ASSERT_THROW(toCheckedValue(overMax, numBits), OutOfRangeException); + } + + testIntOperators(NumericLimits::min(numBits)); + } +} + +template +void testUnsignedDynInt() +{ + using value_type = typename T::value_type; + + static_assert(NumericLimits::min(8) == 0, "shall be constexpr"); + static_assert(NumericLimits::max(8) == 255, "shall be constexpr"); + + static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); + static_assert(!noexcept(fromCheckedValue(std::declval(), std::declval())), + "shall not be noexcept"); + static_assert( + !noexcept(toCheckedValue(std::declval(), std::declval())), "shall not be noexcept"); + + ASSERT_THROW(fromCheckedValue(0, 0), OutOfRangeException); + ASSERT_THROW(fromCheckedValue(0, sizeof(value_type) * 8 + 1), OutOfRangeException); + + for (BitSize numBits = 1; numBits <= sizeof(value_type) * 8; ++numBits) + { + const T minValue(NumericLimits::min(numBits)); + ASSERT_EQ(NumericLimits::min(numBits), minValue); + + const T maxValue(NumericLimits::max(numBits)); + ASSERT_EQ(NumericLimits::max(numBits), maxValue); + + T checkedMin = fromCheckedValue(NumericLimits::min(numBits), numBits); + ASSERT_EQ(NumericLimits::min(numBits), toCheckedValue(checkedMin, numBits)); + + T checkedMax = fromCheckedValue(NumericLimits::max(numBits), numBits); + ASSERT_EQ(NumericLimits::max(numBits), toCheckedValue(checkedMax, numBits)); + + T value; + ASSERT_EQ(0, value); + value = 1; + ASSERT_EQ(1, value); + value = NumericLimits::max(numBits) - 1; + ASSERT_EQ(NumericLimits::max(numBits) - 1, value); + + if (numBits < sizeof(value_type) * 8) + { + ASSERT_THROW(fromCheckedValue(NumericLimits::max(numBits) + 1, numBits), OutOfRangeException); + const T overMax = maxValue + 1; + ASSERT_THROW(toCheckedValue(overMax, numBits), OutOfRangeException); + } + + testUIntOperators(NumericLimits::min(numBits)); + } +} + +template +constexpr void testFloat() +{ + using value_type = typename T::value_type; + + if constexpr (std::is_same_v) + { + // cannot compare floating point numbers directly + static_assert(NumericLimits::min() > 0.F, "shall be constexpr"); + static_assert(NumericLimits::max() > 0.F, "shall be constexpr"); + + ASSERT_FLOAT_EQ(NumericLimits::min(), 6.103515625e-05F); + ASSERT_FLOAT_EQ(NumericLimits::max(), 65504.F); + } + else + { + // cannot compare floating point numbers directly + static_assert(NumericLimits::min() > static_cast(0.), "shall be constexpr"); + static_assert(NumericLimits::max() > static_cast(0.), "shall be constexpr"); + + ASSERT_DOUBLE_EQ(NumericLimits::min(), std::numeric_limits::min()); + } + + static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); + static_assert(noexcept(fromCheckedValue(std::declval())), "shall be noexcept"); + static_assert(noexcept(toCheckedValue(std::declval())), "shall be noexcept"); + + const T minValue(NumericLimits::min()); + ASSERT_DOUBLE_EQ(NumericLimits::min(), minValue); + + const T maxValue(NumericLimits::max()); + ASSERT_DOUBLE_EQ(NumericLimits::max(), maxValue); + + T checkedMin = fromCheckedValue(NumericLimits::min()); + ASSERT_DOUBLE_EQ(NumericLimits::min(), toCheckedValue(checkedMin)); + + T checkedMax = fromCheckedValue(NumericLimits::max()); + ASSERT_DOUBLE_EQ(NumericLimits::max(), toCheckedValue(checkedMax)); + + T value; + ASSERT_DOUBLE_EQ(static_cast(0.0), value); + value = static_cast(1.0); + ASSERT_DOUBLE_EQ(static_cast(1.0), value); + value = static_cast(0.0); + ASSERT_DOUBLE_EQ(static_cast(0.0), value); + + T checked_value = fromCheckedValue(static_cast(1.0)); + ASSERT_DOUBLE_EQ(static_cast(1.0), toCheckedValue(checked_value)); + + testFloatOperators(); +} + +TEST(TypesTest, boolType) +{ + static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); + static_assert(noexcept(fromCheckedValue(std::declval())), "shall be noexcept"); + static_assert(noexcept(toCheckedValue(std::declval())), "shall be noexcept"); + + const Bool minValue(NumericLimits::min()); + ASSERT_EQ(NumericLimits::min(), minValue); + + const Bool maxValue(NumericLimits::max()); + ASSERT_EQ(NumericLimits::max(), maxValue); + + Bool checkedMin = fromCheckedValue(NumericLimits::min()); + ASSERT_EQ(NumericLimits::min(), toCheckedValue(checkedMin)); + + Bool checkedMax = fromCheckedValue(NumericLimits::max()); + ASSERT_EQ(NumericLimits::max(), toCheckedValue(checkedMax)); + + Bool value; + ASSERT_EQ(false, value); + value = true; + ASSERT_EQ(true, value); + value = false; + ASSERT_EQ(false, value); + + testBoolOperators(); +} + +TEST(TypesTest, signedStdIntTypes) +{ + testSignedStdInt(); + testSignedStdInt(); + testSignedStdInt(); + testSignedStdInt(); +} + +TEST(TypesTest, unsignedStdIntTypes) +{ + testUnsignedStdInt(); + testUnsignedStdInt(); + testUnsignedStdInt(); + testUnsignedStdInt(); +} + +TEST(TypesTest, signedBitFieldTypes) +{ + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); +} + +TEST(TypesTest, unsignedBitFieldTypes) +{ + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); +} + +TEST(TypesTest, signedDynIntTypes) +{ + testSignedDynInt(); + testSignedDynInt(); + testSignedDynInt(); + testSignedDynInt(); +} + +TEST(TypesTest, unsignedDynIntTypes) +{ + testUnsignedDynInt(); + testUnsignedDynInt(); + testUnsignedDynInt(); + testUnsignedDynInt(); +} + +TEST(TypesTest, signedVarIntTypes) +{ + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); +} + +TEST(TypesTest, unsignedVarIntTypes) +{ + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); +} + +TEST(TypesTest, floatTypes) +{ + testFloat(); + testFloat(); + testFloat(); +} + +TEST(TypesTest, typeCast) +{ + { + Int32 i32; + Int16 i16 = typeCast(i32); + i32 = Int32(i16); + } + { + Int16 i16; + DynInt8 dynI8 = typeCast(i16); + i16 = Int16(dynI8); + } + { + Float16 f16; + Int16 i16 = typeCast(f16); + f16 = typeCast(i16); + } +} + +} // namespace zserio