|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 /* Cast operations to supplement the built-in casting operations. */ |
|
8 |
|
9 #ifndef mozilla_Casting_h |
|
10 #define mozilla_Casting_h |
|
11 |
|
12 #include "mozilla/Assertions.h" |
|
13 #include "mozilla/TypeTraits.h" |
|
14 |
|
15 #include <limits.h> |
|
16 |
|
17 namespace mozilla { |
|
18 |
|
19 /** |
|
20 * Return a value of type |To|, containing the underlying bit pattern of |from|. |
|
21 * |
|
22 * |To| and |From| must be types of the same size; be careful of cross-platform |
|
23 * size differences, or this might fail to compile on some but not all |
|
24 * platforms. |
|
25 */ |
|
26 template<typename To, typename From> |
|
27 inline To |
|
28 BitwiseCast(const From from) |
|
29 { |
|
30 static_assert(sizeof(From) == sizeof(To), |
|
31 "To and From must have the same size"); |
|
32 union { |
|
33 From from; |
|
34 To to; |
|
35 } u; |
|
36 u.from = from; |
|
37 return u.to; |
|
38 } |
|
39 |
|
40 namespace detail { |
|
41 |
|
42 enum ToSignedness { ToIsSigned, ToIsUnsigned }; |
|
43 enum FromSignedness { FromIsSigned, FromIsUnsigned }; |
|
44 |
|
45 template<typename From, |
|
46 typename To, |
|
47 FromSignedness = IsSigned<From>::value ? FromIsSigned : FromIsUnsigned, |
|
48 ToSignedness = IsSigned<To>::value ? ToIsSigned : ToIsUnsigned> |
|
49 struct BoundsCheckImpl; |
|
50 |
|
51 // Implicit conversions on operands to binary operations make this all a bit |
|
52 // hard to verify. Attempt to ease the pain below by *only* comparing values |
|
53 // that are obviously the same type (and will undergo no further conversions), |
|
54 // even when it's not strictly necessary, for explicitness. |
|
55 |
|
56 enum UUComparison { FromIsBigger, FromIsNotBigger }; |
|
57 |
|
58 // Unsigned-to-unsigned range check |
|
59 |
|
60 template<typename From, typename To, |
|
61 UUComparison = (sizeof(From) > sizeof(To)) ? FromIsBigger : FromIsNotBigger> |
|
62 struct UnsignedUnsignedCheck; |
|
63 |
|
64 template<typename From, typename To> |
|
65 struct UnsignedUnsignedCheck<From, To, FromIsBigger> |
|
66 { |
|
67 public: |
|
68 static bool checkBounds(const From from) { |
|
69 return from <= From(To(-1)); |
|
70 } |
|
71 }; |
|
72 |
|
73 template<typename From, typename To> |
|
74 struct UnsignedUnsignedCheck<From, To, FromIsNotBigger> |
|
75 { |
|
76 public: |
|
77 static bool checkBounds(const From from) { |
|
78 return true; |
|
79 } |
|
80 }; |
|
81 |
|
82 template<typename From, typename To> |
|
83 struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsUnsigned> |
|
84 { |
|
85 public: |
|
86 static bool checkBounds(const From from) { |
|
87 return UnsignedUnsignedCheck<From, To>::checkBounds(from); |
|
88 } |
|
89 }; |
|
90 |
|
91 // Signed-to-unsigned range check |
|
92 |
|
93 template<typename From, typename To> |
|
94 struct BoundsCheckImpl<From, To, FromIsSigned, ToIsUnsigned> |
|
95 { |
|
96 public: |
|
97 static bool checkBounds(const From from) { |
|
98 if (from < 0) |
|
99 return false; |
|
100 if (sizeof(To) >= sizeof(From)) |
|
101 return true; |
|
102 return from <= From(To(-1)); |
|
103 } |
|
104 }; |
|
105 |
|
106 // Unsigned-to-signed range check |
|
107 |
|
108 enum USComparison { FromIsSmaller, FromIsNotSmaller }; |
|
109 |
|
110 template<typename From, typename To, |
|
111 USComparison = (sizeof(From) < sizeof(To)) ? FromIsSmaller : FromIsNotSmaller> |
|
112 struct UnsignedSignedCheck; |
|
113 |
|
114 template<typename From, typename To> |
|
115 struct UnsignedSignedCheck<From, To, FromIsSmaller> |
|
116 { |
|
117 public: |
|
118 static bool checkBounds(const From from) { |
|
119 return true; |
|
120 } |
|
121 }; |
|
122 |
|
123 template<typename From, typename To> |
|
124 struct UnsignedSignedCheck<From, To, FromIsNotSmaller> |
|
125 { |
|
126 public: |
|
127 static bool checkBounds(const From from) { |
|
128 const To MaxValue = To((1ULL << (CHAR_BIT * sizeof(To) - 1)) - 1); |
|
129 return from <= From(MaxValue); |
|
130 } |
|
131 }; |
|
132 |
|
133 template<typename From, typename To> |
|
134 struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsSigned> |
|
135 { |
|
136 public: |
|
137 static bool checkBounds(const From from) { |
|
138 return UnsignedSignedCheck<From, To>::checkBounds(from); |
|
139 } |
|
140 }; |
|
141 |
|
142 // Signed-to-signed range check |
|
143 |
|
144 template<typename From, typename To> |
|
145 struct BoundsCheckImpl<From, To, FromIsSigned, ToIsSigned> |
|
146 { |
|
147 public: |
|
148 static bool checkBounds(const From from) { |
|
149 if (sizeof(From) <= sizeof(To)) |
|
150 return true; |
|
151 const To MaxValue = To((1ULL << (CHAR_BIT * sizeof(To) - 1)) - 1); |
|
152 const To MinValue = -MaxValue - To(1); |
|
153 return From(MinValue) <= from && |
|
154 From(from) <= From(MaxValue); |
|
155 } |
|
156 }; |
|
157 |
|
158 template<typename From, typename To, |
|
159 bool TypesAreIntegral = IsIntegral<From>::value && IsIntegral<To>::value> |
|
160 class BoundsChecker; |
|
161 |
|
162 template<typename From> |
|
163 class BoundsChecker<From, From, true> |
|
164 { |
|
165 public: |
|
166 static bool checkBounds(const From from) { return true; } |
|
167 }; |
|
168 |
|
169 template<typename From, typename To> |
|
170 class BoundsChecker<From, To, true> |
|
171 { |
|
172 public: |
|
173 static bool checkBounds(const From from) { |
|
174 return BoundsCheckImpl<From, To>::checkBounds(from); |
|
175 } |
|
176 }; |
|
177 |
|
178 template<typename From, typename To> |
|
179 inline bool |
|
180 IsInBounds(const From from) |
|
181 { |
|
182 return BoundsChecker<From, To>::checkBounds(from); |
|
183 } |
|
184 |
|
185 } // namespace detail |
|
186 |
|
187 /** |
|
188 * Cast a value of integral type |From| to a value of integral type |To|, |
|
189 * asserting that the cast will be a safe cast per C++ (that is, that |to| is in |
|
190 * the range of values permitted for the type |From|). |
|
191 */ |
|
192 template<typename To, typename From> |
|
193 inline To |
|
194 SafeCast(const From from) |
|
195 { |
|
196 MOZ_ASSERT((detail::IsInBounds<From, To>(from))); |
|
197 return static_cast<To>(from); |
|
198 } |
|
199 |
|
200 } // namespace mozilla |
|
201 |
|
202 #endif /* mozilla_Casting_h */ |