1 //
2 // Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5
6 #pragma once
7
8 #include "Assert.hpp"
9
10 #include <type_traits>
11 #include <limits>
12
13 namespace arm
14 {
15
16 namespace pipe
17 {
18
19 #if !defined(NDEBUG) || defined(ARM_PIPE_NUMERIC_CAST_TESTABLE)
20 #define ENABLE_NUMERIC_CAST_CHECKS 1
21 #else
22 #define ENABLE_NUMERIC_CAST_CHECKS 0
23 #endif
24
25 #if defined(ARM_PIPE_NUMERIC_CAST_TESTABLE)
26 # define ARM_PIPE_NUMERIC_CAST_CHECK(cond, msg) ConditionalThrow<std::bad_cast>(cond)
27 #else
28 # define ARM_PIPE_NUMERIC_CAST_CHECK(cond, msg) ARM_PIPE_ASSERT_MSG(cond, msg)
29 #endif
30
31 template<typename Dest, typename Source>
32 typename std::enable_if_t<
33 std::is_unsigned<Source>::value &&
34 std::is_unsigned<Dest>::value
35 , Dest>
numeric_cast(Source source)36 numeric_cast(Source source)
37 {
38 #if ENABLE_NUMERIC_CAST_CHECKS
39 if (source > std::numeric_limits<Dest>::max())
40 {
41 ARM_PIPE_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting unsigned type to "
42 "narrower unsigned type. Overflow detected.");
43 }
44 #endif // ENABLE_NUMERIC_CAST_CHECKS
45
46 return static_cast<Dest>(source);
47 }
48
49 template<typename Dest, typename Source>
50 typename std::enable_if_t<
51 std::is_signed<Source>::value &&
52 std::is_signed<Dest>::value
53 , Dest>
numeric_cast(Source source)54 numeric_cast(Source source)
55 {
56 static_assert(!std::is_floating_point<Source>::value && !std::is_floating_point<Dest>::value,
57 "numeric_cast doesn't cast float.");
58
59 #if ENABLE_NUMERIC_CAST_CHECKS
60 if (source > std::numeric_limits<Dest>::max())
61 {
62 ARM_PIPE_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting signed type to narrower signed type. "
63 "Overflow detected.");
64 }
65
66 if (source < std::numeric_limits<Dest>::lowest())
67 {
68 ARM_PIPE_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting signed type to narrower signed type. "
69 "Underflow detected.");
70 }
71 #endif // ENABLE_NUMERIC_CAST_CHECKS
72
73 return static_cast<Dest>(source);
74 }
75
76 // numeric cast from unsigned to signed checked for narrowing overflows
77 template<typename Dest, typename Source>
78 typename std::enable_if_t<
79 std::is_signed<Dest>::value &&
80 std::is_unsigned<Source>::value
81 , Dest>
numeric_cast(Source sValue)82 numeric_cast(Source sValue)
83 {
84 static_assert(!std::is_floating_point<Dest>::value, "numeric_cast doesn't cast to float.");
85
86 #if ENABLE_NUMERIC_CAST_CHECKS
87 if (sValue > static_cast< typename std::make_unsigned<Dest>::type >(std::numeric_limits<Dest>::max()))
88 {
89 ARM_PIPE_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting unsigned type to signed type. "
90 "Overflow detected.");
91 }
92 #endif // ENABLE_NUMERIC_CAST_CHECKS
93
94 return static_cast<Dest>(sValue);
95 }
96
97 // numeric cast from signed to unsigned checked for underflows and narrowing overflows
98 template<typename Dest, typename Source>
99 typename std::enable_if_t<
100 std::is_unsigned<Dest>::value &&
101 std::is_signed<Source>::value
102 , Dest>
numeric_cast(Source sValue)103 numeric_cast(Source sValue)
104 {
105 static_assert(!std::is_floating_point<Source>::value && !std::is_floating_point<Dest>::value,
106 "numeric_cast doesn't cast floats.");
107
108 #if ENABLE_NUMERIC_CAST_CHECKS
109 if (sValue < 0)
110 {
111 ARM_PIPE_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting negative value to unsigned type. "
112 "Underflow detected.");
113 }
114
115 if (static_cast< typename std::make_unsigned<Source>::type >(sValue) > std::numeric_limits<Dest>::max())
116 {
117 ARM_PIPE_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting signed type to unsigned type. "
118 "Overflow detected.");
119 }
120
121 #endif // ENABLE_NUMERIC_CAST_CHECKS
122 return static_cast<Dest>(sValue);
123 }
124
125 #undef ENABLE_NUMERIC_CAST_CHECKS
126
127 } // namespace pipe
128 } // namespace arm
129