blob: 069f9514fe38db82089474441b6b1efb2ffba7a3 [file] [log] [blame]
Jim Flynnbbfe6032020-07-20 16:57:44 +01001//
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
13namespace arm
14{
15
16namespace 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
31template<typename Dest, typename Source>
32typename std::enable_if_t<
33 std::is_unsigned<Source>::value &&
34 std::is_unsigned<Dest>::value
35 , Dest>
36numeric_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
49template<typename Dest, typename Source>
50typename std::enable_if_t<
51 std::is_signed<Source>::value &&
52 std::is_signed<Dest>::value
53 , Dest>
54numeric_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
77template<typename Dest, typename Source>
78typename std::enable_if_t<
79 std::is_signed<Dest>::value &&
80 std::is_unsigned<Source>::value
81 , Dest>
82numeric_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
98template<typename Dest, typename Source>
99typename std::enable_if_t<
100 std::is_unsigned<Dest>::value &&
101 std::is_signed<Source>::value
102 , Dest>
103numeric_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