michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* Cast operations to supplement the built-in casting operations. */ michael@0: michael@0: #ifndef mozilla_Casting_h michael@0: #define mozilla_Casting_h michael@0: michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/TypeTraits.h" michael@0: michael@0: #include michael@0: michael@0: namespace mozilla { michael@0: michael@0: /** michael@0: * Return a value of type |To|, containing the underlying bit pattern of |from|. michael@0: * michael@0: * |To| and |From| must be types of the same size; be careful of cross-platform michael@0: * size differences, or this might fail to compile on some but not all michael@0: * platforms. michael@0: */ michael@0: template michael@0: inline To michael@0: BitwiseCast(const From from) michael@0: { michael@0: static_assert(sizeof(From) == sizeof(To), michael@0: "To and From must have the same size"); michael@0: union { michael@0: From from; michael@0: To to; michael@0: } u; michael@0: u.from = from; michael@0: return u.to; michael@0: } michael@0: michael@0: namespace detail { michael@0: michael@0: enum ToSignedness { ToIsSigned, ToIsUnsigned }; michael@0: enum FromSignedness { FromIsSigned, FromIsUnsigned }; michael@0: michael@0: template::value ? FromIsSigned : FromIsUnsigned, michael@0: ToSignedness = IsSigned::value ? ToIsSigned : ToIsUnsigned> michael@0: struct BoundsCheckImpl; michael@0: michael@0: // Implicit conversions on operands to binary operations make this all a bit michael@0: // hard to verify. Attempt to ease the pain below by *only* comparing values michael@0: // that are obviously the same type (and will undergo no further conversions), michael@0: // even when it's not strictly necessary, for explicitness. michael@0: michael@0: enum UUComparison { FromIsBigger, FromIsNotBigger }; michael@0: michael@0: // Unsigned-to-unsigned range check michael@0: michael@0: template sizeof(To)) ? FromIsBigger : FromIsNotBigger> michael@0: struct UnsignedUnsignedCheck; michael@0: michael@0: template michael@0: struct UnsignedUnsignedCheck michael@0: { michael@0: public: michael@0: static bool checkBounds(const From from) { michael@0: return from <= From(To(-1)); michael@0: } michael@0: }; michael@0: michael@0: template michael@0: struct UnsignedUnsignedCheck michael@0: { michael@0: public: michael@0: static bool checkBounds(const From from) { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: template michael@0: struct BoundsCheckImpl michael@0: { michael@0: public: michael@0: static bool checkBounds(const From from) { michael@0: return UnsignedUnsignedCheck::checkBounds(from); michael@0: } michael@0: }; michael@0: michael@0: // Signed-to-unsigned range check michael@0: michael@0: template michael@0: struct BoundsCheckImpl michael@0: { michael@0: public: michael@0: static bool checkBounds(const From from) { michael@0: if (from < 0) michael@0: return false; michael@0: if (sizeof(To) >= sizeof(From)) michael@0: return true; michael@0: return from <= From(To(-1)); michael@0: } michael@0: }; michael@0: michael@0: // Unsigned-to-signed range check michael@0: michael@0: enum USComparison { FromIsSmaller, FromIsNotSmaller }; michael@0: michael@0: template michael@0: struct UnsignedSignedCheck; michael@0: michael@0: template michael@0: struct UnsignedSignedCheck michael@0: { michael@0: public: michael@0: static bool checkBounds(const From from) { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: template michael@0: struct UnsignedSignedCheck michael@0: { michael@0: public: michael@0: static bool checkBounds(const From from) { michael@0: const To MaxValue = To((1ULL << (CHAR_BIT * sizeof(To) - 1)) - 1); michael@0: return from <= From(MaxValue); michael@0: } michael@0: }; michael@0: michael@0: template michael@0: struct BoundsCheckImpl michael@0: { michael@0: public: michael@0: static bool checkBounds(const From from) { michael@0: return UnsignedSignedCheck::checkBounds(from); michael@0: } michael@0: }; michael@0: michael@0: // Signed-to-signed range check michael@0: michael@0: template michael@0: struct BoundsCheckImpl michael@0: { michael@0: public: michael@0: static bool checkBounds(const From from) { michael@0: if (sizeof(From) <= sizeof(To)) michael@0: return true; michael@0: const To MaxValue = To((1ULL << (CHAR_BIT * sizeof(To) - 1)) - 1); michael@0: const To MinValue = -MaxValue - To(1); michael@0: return From(MinValue) <= from && michael@0: From(from) <= From(MaxValue); michael@0: } michael@0: }; michael@0: michael@0: template::value && IsIntegral::value> michael@0: class BoundsChecker; michael@0: michael@0: template michael@0: class BoundsChecker michael@0: { michael@0: public: michael@0: static bool checkBounds(const From from) { return true; } michael@0: }; michael@0: michael@0: template michael@0: class BoundsChecker michael@0: { michael@0: public: michael@0: static bool checkBounds(const From from) { michael@0: return BoundsCheckImpl::checkBounds(from); michael@0: } michael@0: }; michael@0: michael@0: template michael@0: inline bool michael@0: IsInBounds(const From from) michael@0: { michael@0: return BoundsChecker::checkBounds(from); michael@0: } michael@0: michael@0: } // namespace detail michael@0: michael@0: /** michael@0: * Cast a value of integral type |From| to a value of integral type |To|, michael@0: * asserting that the cast will be a safe cast per C++ (that is, that |to| is in michael@0: * the range of values permitted for the type |From|). michael@0: */ michael@0: template michael@0: inline To michael@0: SafeCast(const From from) michael@0: { michael@0: MOZ_ASSERT((detail::IsInBounds(from))); michael@0: return static_cast(from); michael@0: } michael@0: michael@0: } // namespace mozilla michael@0: michael@0: #endif /* mozilla_Casting_h */