• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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