diff --git a/runtime/src/zserio/Types.h b/runtime/src/zserio/Types.h index e93a2b3..a2c8e67 100644 --- a/runtime/src/zserio/Types.h +++ b/runtime/src/zserio/Types.h @@ -13,233 +13,506 @@ namespace zserio /** Typedef for bit size type used in Zserio. */ using BitSize = unsigned int; +template +struct Limits +{}; + namespace detail { -template -class IntegralType +template +class TypeWrapper { +public: + using value_type = VALUE_TYPE; + + constexpr explicit TypeWrapper() noexcept : + m_value() + {} + + constexpr TypeWrapper(value_type value) noexcept : + m_value(value) + {} + + constexpr operator value_type() const + { + return m_value; + } + private: - static_assert((BIT_SIZE + 7) / 8 <= sizeof(VALUE_TYPE), "BitSize doesn't fit the the VALUE_TYPE!"); + value_type m_value; +}; - static constexpr bool IS_FULL_STD_INTEGRAL_TYPE = sizeof(VALUE_TYPE) * 8 == BIT_SIZE && - (BIT_SIZE == 8 || BIT_SIZE == 16 || BIT_SIZE == 32 || BIT_SIZE == 64); +template +class IntWrapper : public TypeWrapper +{ +private: + static_assert((BIT_SIZE + 7) / 8 <= sizeof(VALUE_TYPE), "BitSize doesn't fit to the VALUE_TYPE!"); public: using value_type = VALUE_TYPE; - static constexpr value_type MIN_VALUE = []() { - if constexpr (IS_FULL_STD_INTEGRAL_TYPE) + + using TypeWrapper::TypeWrapper; +}; + +template +class DynIntWrapper : public IntWrapper +{ +public: + using IntWrapper::IntWrapper; +}; + +enum class VarIntType : uint8_t +{ + VAR16, + VAR32, + VAR64, + VAR, + VARSIZE +}; + +template +class VarIntWrapper : public TypeWrapper +{ + static_assert( + VAR_TYPE != VarIntType::VARSIZE || std::is_unsigned_v, "VARSIZE must be unsigned!"); + +public: + using TypeWrapper::TypeWrapper; +}; + +template +struct needs_range_check +{}; + +template +inline constexpr bool needs_range_check_v = needs_range_check::value; + +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 RangeChecker +{ + static constexpr void check(typename TYPE_WRAPPER::value_type value) noexcept( + !detail::needs_range_check_v) + { + if constexpr (std::is_signed_v) { - return std::numeric_limits::min(); + if (value < Limits::min() || value > Limits::max()) + { + throw OutOfRangeException("Value '") + << value << "' out of range '<" << Limits::min() << ", " + << Limits::max() << ">'!"; + } } - else if constexpr (std::is_signed_v) + else { - return -static_cast(1ULL << (BIT_SIZE - 1U)); + if (value > Limits::max()) + { + throw OutOfRangeException("Value '") + << value << "' out of bounds '" << Limits::max() << "'!"; + } + } + } +}; + +template +struct RangeChecker> +{ + static constexpr void check(VALUE_TYPE value, 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 << ">'!"; + } + + if constexpr (std::is_signed_v) + { + if (value < Limits>::min(numBits) || + value > Limits>::max(numBits)) + { + throw OutOfRangeException("Value '") + << value << "' out of range '<" << Limits>::min(numBits) + << ", " << Limits>::max(numBits) << ">'!"; + } } else { - return value_type(); + if (value > Limits>::max(numBits)) + { + throw OutOfRangeException("Value '") + << value << "' out of bounds '" << Limits>::max(numBits) + << "'!"; + } } - }(); + } +}; - static constexpr value_type MAX_VALUE = []() { - if constexpr (IS_FULL_STD_INTEGRAL_TYPE) +} // namespace detail + +template +struct Limits> +{ + static constexpr VALUE_TYPE min() noexcept + { + if constexpr (!detail::needs_range_check_v>) { - return std::numeric_limits::max(); + return std::numeric_limits::min(); } - else if constexpr (std::is_signed_v) + else if constexpr (std::is_signed_v) { - return static_cast((1ULL << (BIT_SIZE - 1U)) - 1U); + return -static_cast(1ULL << (BIT_SIZE - 1U)); } else { - return static_cast((1ULL << BIT_SIZE) - 1U); + return 0; } - }(); + } - constexpr explicit IntegralType() noexcept : - m_value() - {} + static constexpr VALUE_TYPE 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); + } + } +}; - constexpr IntegralType(value_type value) noexcept(IS_FULL_STD_INTEGRAL_TYPE) : - m_value(withRangeCheck(value)) - {} +template +struct Limits> +{ + static constexpr VALUE_TYPE 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; + } + } - constexpr value_type get() const + static constexpr VALUE_TYPE max() noexcept { - return m_value; + 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; + } } - void set(value_type value) noexcept(IS_FULL_STD_INTEGRAL_TYPE) +private: + static constexpr uint64_t FIRST_BYTE_BITS = std::is_signed_v ? 6 : 7; +}; + +template +struct Limits> +{ + static constexpr VALUE_TYPE min(BitSize numBits) { - m_value = withRangeCheck(value); + if constexpr (std::is_signed_v) + { + return minSignedValue(numBits); + } + else + { + return 0; + } } - constexpr operator value_type() const + static constexpr VALUE_TYPE max(BitSize numBits) { - return m_value; + if constexpr (std::is_signed_v) + { + return maxSignedValue(numBits); + } + else + { + return maxUnsignedValue(numBits); + } } private: - value_type withRangeCheck(value_type value) const noexcept(IS_FULL_STD_INTEGRAL_TYPE) + static constexpr VALUE_TYPE minSignedValue(BitSize numBits) { - if constexpr (!IS_FULL_STD_INTEGRAL_TYPE) + if constexpr (std::is_same_v) { - if constexpr (std::is_signed_v) + if (numBits == 64) { - if (value < MIN_VALUE || value > MAX_VALUE) - { - throw OutOfRangeException(); - } + return INT64_MIN; } - else + } + + return -static_cast(1ULL << (numBits - 1U)); + } + + static constexpr VALUE_TYPE maxSignedValue(BitSize numBits) + { + if constexpr (std::is_same_v) + { + if (numBits == 64) { - if (value > MAX_VALUE) - { - throw OutOfRangeException(); - } + return INT64_MAX; } } - return value; + return static_cast((1ULL << (numBits - 1U)) - 1U); } - value_type m_value; + 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); + } }; -} // namespace detail +template +constexpr TYPE_WRAPPER checked_cast(typename TYPE_WRAPPER::value_type value) noexcept( + !detail::needs_range_check_v) +{ + if constexpr (detail::needs_range_check_v) + { + detail::RangeChecker::check(value); + } + + return TYPE_WRAPPER(value); +} + +template +constexpr typename TYPE_WRAPPER::value_type checked_cast(TYPE_WRAPPER wrapper) noexcept( + !detail::needs_range_check_v) +{ + if constexpr (detail::needs_range_check_v) + { + detail::RangeChecker::check(wrapper); + } + + return wrapper; +} + +template +constexpr TYPE_WRAPPER checked_cast(typename TYPE_WRAPPER::value_type value, BitSize numBits) +{ + detail::RangeChecker::check(value, numBits); + + return TYPE_WRAPPER(value); +} + +template +constexpr typename TYPE_WRAPPER::value_type checked_cast(TYPE_WRAPPER wrapper, BitSize numBits) +{ + detail::RangeChecker::check(wrapper, numBits); + + return wrapper; +} + +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 Int8 = detail::IntegralType; -using Int16 = detail::IntegralType; -using Int32 = detail::IntegralType; -using Int64 = detail::IntegralType; - -using UInt8 = detail::IntegralType; -using UInt16 = detail::IntegralType; -using UInt32 = detail::IntegralType; -using UInt64 = detail::IntegralType; - -using Int1 = detail::IntegralType; -using Int2 = detail::IntegralType; -using Int3 = detail::IntegralType; -using Int4 = detail::IntegralType; -using Int5 = detail::IntegralType; -using Int6 = detail::IntegralType; -using Int7 = detail::IntegralType; -using Int9 = detail::IntegralType; -using Int10 = detail::IntegralType; -using Int11 = detail::IntegralType; -using Int12 = detail::IntegralType; -using Int13 = detail::IntegralType; -using Int14 = detail::IntegralType; -using Int15 = detail::IntegralType; -using Int17 = detail::IntegralType; -using Int18 = detail::IntegralType; -using Int19 = detail::IntegralType; -using Int20 = detail::IntegralType; -using Int21 = detail::IntegralType; -using Int22 = detail::IntegralType; -using Int23 = detail::IntegralType; -using Int24 = detail::IntegralType; -using Int25 = detail::IntegralType; -using Int26 = detail::IntegralType; -using Int27 = detail::IntegralType; -using Int28 = detail::IntegralType; -using Int29 = detail::IntegralType; -using Int30 = detail::IntegralType; -using Int31 = detail::IntegralType; -using Int33 = detail::IntegralType; -using Int34 = detail::IntegralType; -using Int35 = detail::IntegralType; -using Int36 = detail::IntegralType; -using Int37 = detail::IntegralType; -using Int38 = detail::IntegralType; -using Int39 = detail::IntegralType; -using Int40 = detail::IntegralType; -using Int41 = detail::IntegralType; -using Int42 = detail::IntegralType; -using Int43 = detail::IntegralType; -using Int44 = detail::IntegralType; -using Int45 = detail::IntegralType; -using Int46 = detail::IntegralType; -using Int47 = detail::IntegralType; -using Int48 = detail::IntegralType; -using Int49 = detail::IntegralType; -using Int50 = detail::IntegralType; -using Int51 = detail::IntegralType; -using Int52 = detail::IntegralType; -using Int53 = detail::IntegralType; -using Int54 = detail::IntegralType; -using Int55 = detail::IntegralType; -using Int56 = detail::IntegralType; -using Int57 = detail::IntegralType; -using Int58 = detail::IntegralType; -using Int59 = detail::IntegralType; -using Int60 = detail::IntegralType; -using Int61 = detail::IntegralType; -using Int62 = detail::IntegralType; -using Int63 = detail::IntegralType; - -using UInt1 = detail::IntegralType; -using UInt2 = detail::IntegralType; -using UInt3 = detail::IntegralType; -using UInt4 = detail::IntegralType; -using UInt5 = detail::IntegralType; -using UInt6 = detail::IntegralType; -using UInt7 = detail::IntegralType; -using UInt9 = detail::IntegralType; -using UInt10 = detail::IntegralType; -using UInt11 = detail::IntegralType; -using UInt12 = detail::IntegralType; -using UInt13 = detail::IntegralType; -using UInt14 = detail::IntegralType; -using UInt15 = detail::IntegralType; -using UInt17 = detail::IntegralType; -using UInt18 = detail::IntegralType; -using UInt19 = detail::IntegralType; -using UInt20 = detail::IntegralType; -using UInt21 = detail::IntegralType; -using UInt22 = detail::IntegralType; -using UInt23 = detail::IntegralType; -using UInt24 = detail::IntegralType; -using UInt25 = detail::IntegralType; -using UInt26 = detail::IntegralType; -using UInt27 = detail::IntegralType; -using UInt28 = detail::IntegralType; -using UInt29 = detail::IntegralType; -using UInt30 = detail::IntegralType; -using UInt31 = detail::IntegralType; -using UInt33 = detail::IntegralType; -using UInt34 = detail::IntegralType; -using UInt35 = detail::IntegralType; -using UInt36 = detail::IntegralType; -using UInt37 = detail::IntegralType; -using UInt38 = detail::IntegralType; -using UInt39 = detail::IntegralType; -using UInt40 = detail::IntegralType; -using UInt41 = detail::IntegralType; -using UInt42 = detail::IntegralType; -using UInt43 = detail::IntegralType; -using UInt44 = detail::IntegralType; -using UInt45 = detail::IntegralType; -using UInt46 = detail::IntegralType; -using UInt47 = detail::IntegralType; -using UInt48 = detail::IntegralType; -using UInt49 = detail::IntegralType; -using UInt50 = detail::IntegralType; -using UInt51 = detail::IntegralType; -using UInt52 = detail::IntegralType; -using UInt53 = detail::IntegralType; -using UInt54 = detail::IntegralType; -using UInt55 = detail::IntegralType; -using UInt56 = detail::IntegralType; -using UInt57 = detail::IntegralType; -using UInt58 = detail::IntegralType; -using UInt59 = detail::IntegralType; -using UInt60 = detail::IntegralType; -using UInt61 = detail::IntegralType; -using UInt62 = detail::IntegralType; -using UInt63 = detail::IntegralType; +using VarSize = detail::VarIntWrapper; } // namespace zserio diff --git a/runtime/test/zserio/TypesTest.cpp b/runtime/test/zserio/TypesTest.cpp index 0c8bc08..fe93c44 100644 --- a/runtime/test/zserio/TypesTest.cpp +++ b/runtime/test/zserio/TypesTest.cpp @@ -4,256 +4,423 @@ namespace zserio { -template +template void testSignedStdInt() { - static_assert(T::MIN_VALUE == std::numeric_limits::min(), "shall be constexpr"); - static_assert(T::MAX_VALUE == std::numeric_limits::max(), "shall be constexpr"); + using value_type = typename T::value_type; - static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); - static_assert( - std::is_nothrow_invocable_v, "shall be noexcept"); + static_assert(zserio::Limits::min() == std::numeric_limits::min(), "shall be constexpr"); + static_assert(zserio::Limits::max() == std::numeric_limits::max(), "shall be constexpr"); - const T minValue(T::MIN_VALUE); - ASSERT_EQ(T::MIN_VALUE, minValue); - ASSERT_EQ(T::MIN_VALUE, minValue.get()); + static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); + static_assert(noexcept(checked_cast(std::declval())), "shall be noexcept"); - const T maxValue(T::MAX_VALUE); - ASSERT_EQ(T::MAX_VALUE, maxValue); + const T minValue(zserio::Limits::min()); + ASSERT_EQ(zserio::Limits::min(), minValue); + + const T maxValue(zserio::Limits::max()); + ASSERT_EQ(zserio::Limits::max(), maxValue); + + T checkedMin = zserio::checked_cast(zserio::Limits::min()); + ASSERT_EQ(zserio::Limits::min(), checkedMin); + + T checkedMax = zserio::checked_cast(zserio::Limits::max()); + ASSERT_EQ(zserio::Limits::max(), checkedMax); T value; value = 0; ASSERT_EQ(0, value); - value.set(1); - ASSERT_EQ(1, value.get()); + value = 1; + ASSERT_EQ(1, value); value = -1; ASSERT_EQ(-1, value); } -template +template void testUnsignedStdInt() { - static_assert(T::MIN_VALUE == std::numeric_limits::min(), "shall be constexpr"); - static_assert(T::MAX_VALUE == std::numeric_limits::max(), "shall be constexpr"); + using value_type = typename T::value_type; + + static_assert(zserio::Limits::min() == std::numeric_limits::min(), "shall be constexpr"); + static_assert(zserio::Limits::max() == std::numeric_limits::max(), "shall be constexpr"); + + static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); + static_assert(noexcept(checked_cast(std::declval())), "shall be noexcept"); + + const T minValue(zserio::Limits::min()); + ASSERT_EQ(zserio::Limits::min(), minValue); - static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); - static_assert( - std::is_nothrow_invocable_v, "shall be noexcept"); + const T maxValue(zserio::Limits::max()); + ASSERT_EQ(zserio::Limits::max(), maxValue); - const T minValue(T::MIN_VALUE); - ASSERT_EQ(T::MIN_VALUE, minValue); - ASSERT_EQ(T::MIN_VALUE, minValue.get()); + T checkedMin = zserio::checked_cast(zserio::Limits::min()); + ASSERT_EQ(zserio::Limits::min(), checkedMin); - const T maxValue(T::MAX_VALUE); - ASSERT_EQ(T::MAX_VALUE, maxValue); + T checkedMax = zserio::checked_cast(zserio::Limits::max()); + ASSERT_EQ(zserio::Limits::max(), checkedMax); T value; value = 0; ASSERT_EQ(0, value); - value.set(1); - ASSERT_EQ(1, value.get()); + value = 1; + ASSERT_EQ(1, value); value = 2; ASSERT_EQ(2, value); } -template -void testSignedBitField() +template +void testSignedInt() { - static_assert(T::MIN_VALUE == MIN_VALUE, "shall be constexpr"); - static_assert(T::MAX_VALUE == MAX_VALUE, "shall be constexpr"); + using value_type = typename T::value_type; + + static_assert(zserio::Limits::min() == MIN_VALUE, "shall be constexpr"); + static_assert(zserio::Limits::max() == MAX_VALUE, "shall be constexpr"); + + static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); - static_assert(!std::is_nothrow_constructible_v, "shall be noexcept"); - static_assert( - !std::is_nothrow_invocable_v, "shall be noexcept"); + static_assert(IS_NOEXCEPT == noexcept(checked_cast(std::declval())), "unexpected noexcept"); - const T minValue(T::MIN_VALUE); - ASSERT_EQ(T::MIN_VALUE, minValue); - ASSERT_EQ(T::MIN_VALUE, minValue.get()); + const T minValue(zserio::Limits::min()); + ASSERT_EQ(zserio::Limits::min(), minValue); - const T maxValue(T::MAX_VALUE); - ASSERT_EQ(T::MAX_VALUE, maxValue); + const T maxValue(zserio::Limits::max()); + ASSERT_EQ(zserio::Limits::max(), maxValue); + + T checkedMin = zserio::checked_cast(zserio::Limits::min()); + ASSERT_EQ(zserio::Limits::min(), checkedMin); + + T checkedMax = zserio::checked_cast(zserio::Limits::max()); + ASSERT_EQ(zserio::Limits::max(), checkedMax); T value; ASSERT_EQ(0, value); - value.set(-1); + value = -1; ASSERT_EQ(-1, value); value = 0; ASSERT_EQ(0, value); - ASSERT_THROW(T{T::MIN_VALUE - 1}, OutOfRangeException); - ASSERT_THROW(value.set(T::MAX_VALUE + 1), OutOfRangeException); + if constexpr (!IS_NOEXCEPT) + { + ASSERT_THROW(zserio::checked_cast(zserio::Limits::min() - 1), OutOfRangeException); + ASSERT_THROW(zserio::checked_cast(zserio::Limits::max() + 1), OutOfRangeException); + const T underMin = minValue - 1; + ASSERT_THROW(zserio::checked_cast(underMin), OutOfRangeException); + const T overMax = maxValue + 1; + ASSERT_THROW(zserio::checked_cast(overMax), OutOfRangeException); + } } -template -void testUnsignedBitField() +template +void testUnsignedInt() { - static_assert(T::MIN_VALUE == 0, "shall be constexpr"); - static_assert(T::MAX_VALUE == MAX_VALUE, "shall be constexpr"); + using value_type = typename T::value_type; - static_assert(!std::is_nothrow_constructible_v, "shall be noexcept"); - static_assert( - !std::is_nothrow_invocable_v, "shall be noexcept"); + static_assert(zserio::Limits::min() == 0, "shall be constexpr"); + static_assert(zserio::Limits::max() == MAX_VALUE, "shall be constexpr"); - const T minValue(T::MIN_VALUE); - ASSERT_EQ(T::MIN_VALUE, minValue); - ASSERT_EQ(T::MIN_VALUE, minValue.get()); + static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); + static_assert(IS_NOEXCEPT == noexcept(checked_cast(std::declval())), "unexpected noexcept"); - const T maxValue(T::MAX_VALUE); - ASSERT_EQ(T::MAX_VALUE, maxValue); + const T minValue(zserio::Limits::min()); + ASSERT_EQ(zserio::Limits::min(), minValue); + + const T maxValue(zserio::Limits::max()); + ASSERT_EQ(zserio::Limits::max(), maxValue); + + T checkedMin = zserio::checked_cast(zserio::Limits::min()); + ASSERT_EQ(zserio::Limits::min(), checkedMin); + + T checkedMax = zserio::checked_cast(zserio::Limits::max()); + ASSERT_EQ(zserio::Limits::max(), checkedMax); T value; ASSERT_EQ(0, value); - value.set(1); + value = 1; ASSERT_EQ(1, value); - value = T::MAX_VALUE - 1; - ASSERT_EQ(T::MAX_VALUE - 1, value); + value = zserio::Limits::max() - 1; + ASSERT_EQ(zserio::Limits::max() - 1, value); + + if constexpr (!IS_NOEXCEPT) + { + ASSERT_THROW(zserio::checked_cast(zserio::Limits::max() + 1), OutOfRangeException); + const T overMax = maxValue + 1; + ASSERT_THROW(zserio::checked_cast(overMax), OutOfRangeException); + } +} + +template +void testSignedDynInt() +{ + using value_type = typename T::value_type; + + static_assert(zserio::Limits::min(8) == -128, "shall be constexpr"); + static_assert(zserio::Limits::max(8) == 127, "shall be constexpr"); + + static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); + static_assert(!noexcept(checked_cast(std::declval(), std::declval())), + "shall not be noexcept"); + + ASSERT_THROW(zserio::checked_cast(0, 0), OutOfRangeException); + ASSERT_THROW(zserio::checked_cast(0, sizeof(value_type) * 8 + 1), OutOfRangeException); + + for (BitSize numBits = 1; numBits <= sizeof(value_type) * 8; ++numBits) + { + const T minValue(zserio::Limits::min(numBits)); + ASSERT_EQ(zserio::Limits::min(numBits), minValue); + + const T maxValue(zserio::Limits::max(numBits)); + ASSERT_EQ(zserio::Limits::max(numBits), maxValue); + + T checkedMin = zserio::checked_cast(zserio::Limits::min(numBits), numBits); + ASSERT_EQ(zserio::Limits::min(numBits), checkedMin); - ASSERT_THROW(T{T::MAX_VALUE + 1}, OutOfRangeException); - ASSERT_THROW(value.set(T::MAX_VALUE + 1), OutOfRangeException); + T checkedMax = zserio::checked_cast(zserio::Limits::max(numBits), numBits); + ASSERT_EQ(zserio::Limits::max(numBits), checkedMax); + + 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( + zserio::checked_cast(zserio::Limits::min(numBits) - 1, numBits), OutOfRangeException) + << numBits; + ASSERT_THROW( + zserio::checked_cast(zserio::Limits::max(numBits) + 1, numBits), OutOfRangeException); + const T underMin = minValue - 1; + ASSERT_THROW(zserio::checked_cast(underMin, numBits), OutOfRangeException); + const T overMax = maxValue + 1; + ASSERT_THROW(zserio::checked_cast(overMax, numBits), OutOfRangeException); + } + } +} + +template +void testUnsignedDynInt() +{ + using value_type = typename T::value_type; + + static_assert(zserio::Limits::min(8) == 0, "shall be constexpr"); + static_assert(zserio::Limits::max(8) == 255, "shall be constexpr"); + + static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); + static_assert(!noexcept(checked_cast(std::declval(), std::declval())), + "shall not be noexcept"); + + ASSERT_THROW(zserio::checked_cast(0, 0), OutOfRangeException); + ASSERT_THROW(zserio::checked_cast(0, sizeof(value_type) * 8 + 1), OutOfRangeException); + + for (BitSize numBits = 1; numBits <= sizeof(value_type) * 8; ++numBits) + { + const T minValue(zserio::Limits::min(numBits)); + ASSERT_EQ(zserio::Limits::min(numBits), minValue); + + const T maxValue(zserio::Limits::max(numBits)); + ASSERT_EQ(zserio::Limits::max(numBits), maxValue); + + T checkedMin = zserio::checked_cast(zserio::Limits::min(numBits), numBits); + ASSERT_EQ(zserio::Limits::min(numBits), checkedMin); + + T checkedMax = zserio::checked_cast(zserio::Limits::max(numBits), numBits); + ASSERT_EQ(zserio::Limits::max(numBits), checkedMax); + + T value; + ASSERT_EQ(0, value); + value = 1; + ASSERT_EQ(1, value); + value = zserio::Limits::max(numBits) - 1; + ASSERT_EQ(zserio::Limits::max(numBits) - 1, value); + + if (numBits < sizeof(value_type) * 8) + { + ASSERT_THROW( + zserio::checked_cast(zserio::Limits::max(numBits) + 1, numBits), OutOfRangeException); + const T overMax = maxValue + 1; + ASSERT_THROW(zserio::checked_cast(overMax, numBits), OutOfRangeException); + } + } } -TEST(TypesTest, signedStdIntegralTypes) +TEST(TypesTest, signedStdInts) { - testSignedStdInt(); - testSignedStdInt(); - testSignedStdInt(); - testSignedStdInt(); + testSignedStdInt(); + testSignedStdInt(); + testSignedStdInt(); + testSignedStdInt(); } -TEST(TypesTest, unsignedStdIntegralTypes) +TEST(TypesTest, unsignedStdInts) { - testUnsignedStdInt(); - testUnsignedStdInt(); - testUnsignedStdInt(); - testUnsignedStdInt(); + testUnsignedStdInt(); + testUnsignedStdInt(); + testUnsignedStdInt(); + testUnsignedStdInt(); } TEST(TypesTest, signedBitFields) { - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); - testSignedBitField(); + 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, unsignedBitFields) { - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); - testUnsignedBitField(); + 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, signedVarInts) +{ + testSignedInt(); + testSignedInt(); + testSignedInt(); + testSignedInt(); +} + +TEST(TypesTest, unsignedVarInts) +{ + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); + testUnsignedInt(); +} + +TEST(TypesTest, signedDynInts) +{ + testSignedDynInt(); + testSignedDynInt(); + testSignedDynInt(); + testSignedDynInt(); +} + +TEST(TypesTest, unsignedDynInts) +{ + testUnsignedDynInt(); + testUnsignedDynInt(); + testUnsignedDynInt(); + testUnsignedDynInt(); } } // namespace zserio