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  * Sets the outparam value of type |To| with the same underlying bit pattern of
21  * |aFrom|.
22  *
23  * |To| and |From| must be types of the same size; be careful of cross-platform
24  * size differences, or this might fail to compile on some but not all
25  * platforms.
26  *
27  * There is also a variant that returns the value directly.  In most cases, the
28  * two variants should be identical.  However, in the specific case of x86
29  * chips, the behavior differs: returning floating-point values directly is done
30  * through the x87 stack, and x87 loads and stores turn signaling NaNs into
31  * quiet NaNs... silently.  Returning floating-point values via outparam,
32  * however, is done entirely within the SSE registers when SSE2 floating-point
33  * is enabled in the compiler, which has semantics-preserving behavior you would
34  * expect.
35  *
36  * If preserving the distinction between signaling NaNs and quiet NaNs is
37  * important to you, you should use the outparam version.  In all other cases,
38  * you should use the direct return version.
39  */
40 template <typename To, typename From>
BitwiseCast(const From aFrom,To * aResult)41 inline void BitwiseCast(const From aFrom, To* aResult) {
42   static_assert(sizeof(From) == sizeof(To),
43                 "To and From must have the same size");
44   union {
45     From mFrom;
46     To mTo;
47   } u;
48   u.mFrom = aFrom;
49   *aResult = u.mTo;
50 }
51 
52 template <typename To, typename From>
BitwiseCast(const From aFrom)53 inline To BitwiseCast(const From aFrom) {
54   To temp;
55   BitwiseCast<To, From>(aFrom, &temp);
56   return temp;
57 }
58 
59 namespace detail {
60 
61 enum ToSignedness { ToIsSigned, ToIsUnsigned };
62 enum FromSignedness { FromIsSigned, FromIsUnsigned };
63 
64 template <typename From, typename To,
65           FromSignedness =
66               IsSigned<From>::value ? FromIsSigned : FromIsUnsigned,
67           ToSignedness = IsSigned<To>::value ? ToIsSigned : ToIsUnsigned>
68 struct BoundsCheckImpl;
69 
70 // Implicit conversions on operands to binary operations make this all a bit
71 // hard to verify.  Attempt to ease the pain below by *only* comparing values
72 // that are obviously the same type (and will undergo no further conversions),
73 // even when it's not strictly necessary, for explicitness.
74 
75 enum UUComparison { FromIsBigger, FromIsNotBigger };
76 
77 // Unsigned-to-unsigned range check
78 
79 template <typename From, typename To,
80           UUComparison =
81               (sizeof(From) > sizeof(To)) ? FromIsBigger : FromIsNotBigger>
82 struct UnsignedUnsignedCheck;
83 
84 template <typename From, typename To>
85 struct UnsignedUnsignedCheck<From, To, FromIsBigger> {
86  public:
87   static bool checkBounds(const From aFrom) { return aFrom <= From(To(-1)); }
88 };
89 
90 template <typename From, typename To>
91 struct UnsignedUnsignedCheck<From, To, FromIsNotBigger> {
92  public:
93   static bool checkBounds(const From aFrom) { return true; }
94 };
95 
96 template <typename From, typename To>
97 struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsUnsigned> {
98  public:
99   static bool checkBounds(const From aFrom) {
100     return UnsignedUnsignedCheck<From, To>::checkBounds(aFrom);
101   }
102 };
103 
104 // Signed-to-unsigned range check
105 
106 template <typename From, typename To>
107 struct BoundsCheckImpl<From, To, FromIsSigned, ToIsUnsigned> {
108  public:
109   static bool checkBounds(const From aFrom) {
110     if (aFrom < 0) {
111       return false;
112     }
113     if (sizeof(To) >= sizeof(From)) {
114       return true;
115     }
116     return aFrom <= From(To(-1));
117   }
118 };
119 
120 // Unsigned-to-signed range check
121 
122 enum USComparison { FromIsSmaller, FromIsNotSmaller };
123 
124 template <typename From, typename To,
125           USComparison =
126               (sizeof(From) < sizeof(To)) ? FromIsSmaller : FromIsNotSmaller>
127 struct UnsignedSignedCheck;
128 
129 template <typename From, typename To>
130 struct UnsignedSignedCheck<From, To, FromIsSmaller> {
131  public:
132   static bool checkBounds(const From aFrom) { return true; }
133 };
134 
135 template <typename From, typename To>
136 struct UnsignedSignedCheck<From, To, FromIsNotSmaller> {
137  public:
138   static bool checkBounds(const From aFrom) {
139     const To MaxValue = To((1ULL << (CHAR_BIT * sizeof(To) - 1)) - 1);
140     return aFrom <= From(MaxValue);
141   }
142 };
143 
144 template <typename From, typename To>
145 struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsSigned> {
146  public:
147   static bool checkBounds(const From aFrom) {
148     return UnsignedSignedCheck<From, To>::checkBounds(aFrom);
149   }
150 };
151 
152 // Signed-to-signed range check
153 
154 template <typename From, typename To>
155 struct BoundsCheckImpl<From, To, FromIsSigned, ToIsSigned> {
156  public:
157   static bool checkBounds(const From aFrom) {
158     if (sizeof(From) <= sizeof(To)) {
159       return true;
160     }
161     const To MaxValue = To((1ULL << (CHAR_BIT * sizeof(To) - 1)) - 1);
162     const To MinValue = -MaxValue - To(1);
163     return From(MinValue) <= aFrom && From(aFrom) <= From(MaxValue);
164   }
165 };
166 
167 template <typename From, typename To,
168           bool TypesAreIntegral =
169               IsIntegral<From>::value&& IsIntegral<To>::value>
170 class BoundsChecker;
171 
172 template <typename From>
173 class BoundsChecker<From, From, true> {
174  public:
175   static bool checkBounds(const From aFrom) { return true; }
176 };
177 
178 template <typename From, typename To>
179 class BoundsChecker<From, To, true> {
180  public:
181   static bool checkBounds(const From aFrom) {
182     return BoundsCheckImpl<From, To>::checkBounds(aFrom);
183   }
184 };
185 
186 template <typename From, typename To>
187 inline bool IsInBounds(const From aFrom) {
188   return BoundsChecker<From, To>::checkBounds(aFrom);
189 }
190 
191 }  // namespace detail
192 
193 /**
194  * Cast a value of integral type |From| to a value of integral type |To|,
195  * asserting that the cast will be a safe cast per C++ (that is, that |to| is in
196  * the range of values permitted for the type |From|).
197  */
198 template <typename To, typename From>
199 inline To AssertedCast(const From aFrom) {
200   MOZ_ASSERT((detail::IsInBounds<From, To>(aFrom)));
201   return static_cast<To>(aFrom);
202 }
203 
204 /**
205  * Cast a value of integral type |From| to a value of integral type |To|,
206  * release asserting that the cast will be a safe cast per C++ (that is, that
207  * |to| is in the range of values permitted for the type |From|).
208  */
209 template <typename To, typename From>
210 inline To ReleaseAssertedCast(const From aFrom) {
211   MOZ_RELEASE_ASSERT((detail::IsInBounds<From, To>(aFrom)));
212   return static_cast<To>(aFrom);
213 }
214 
215 }  // namespace mozilla
216 
217 #endif /* mozilla_Casting_h */
218