Casting.h 6.6 KB

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