Bug 987274 - Add IntegerTypeTraits.h to MFBT for additional integer traits and helpers that don't have type_traits equivalents - r=Waldo
authorBenoit Jacob <bjacob@mozilla.com>
Tue, 01 Apr 2014 09:38:42 -0400
changeset 176440 cb1ddab163f98106bfe2627bae7c7cc9473c941a
parent 176439 65338f03492b3ec36f5923ebc82431a4f2c66f53
child 176441 517d3a482f5f12367316b7b96b5c64b43dea60a1
push id41763
push userbjacob@mozilla.com
push dateTue, 01 Apr 2014 13:39:00 +0000
treeherdermozilla-inbound@cb1ddab163f9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs987274
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 987274 - Add IntegerTypeTraits.h to MFBT for additional integer traits and helpers that don't have type_traits equivalents - r=Waldo
mfbt/CheckedInt.h
mfbt/IntegerTypeTraits.h
mfbt/moz.build
mfbt/tests/TestCheckedInt.cpp
--- a/mfbt/CheckedInt.h
+++ b/mfbt/CheckedInt.h
@@ -4,31 +4,19 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Provides checked integers, detecting integer overflow and divide-by-0. */
 
 #ifndef mozilla_CheckedInt_h
 #define mozilla_CheckedInt_h
 
-// Enable relying of Mozilla's MFBT for possibly-available C++11 features
-#define MOZ_CHECKEDINT_USE_MFBT
-
 #include <stdint.h>
-
-#ifdef MOZ_CHECKEDINT_USE_MFBT
-#  include "mozilla/Assertions.h"
-#else
-#  include <cassert>
-#  define MOZ_ASSERT(cond, reason) assert((cond) && reason)
-#  define MOZ_DELETE
-#endif
-
-#include <climits>
-#include <cstddef>
+#include "mozilla/Assertions.h"
+#include "mozilla/IntegerTypeTraits.h"
 
 namespace mozilla {
 
 template<typename T> class CheckedInt;
 
 namespace detail {
 
 /*
@@ -129,134 +117,45 @@ template<>
 struct IsSupportedPass2<long long>
 { static const bool value = true; };
 
 template<>
 struct IsSupportedPass2<unsigned long long>
 { static const bool value = true; };
 
 /*
- * Step 2: some integer-traits kind of stuff.
+ * Step 2: Implement the actual validity checks.
+ *
+ * Ideas taken from IntegerLib, code different.
  */
 
-template<size_t Size, bool Signedness>
-struct StdintTypeForSizeAndSignedness
-{};
-
-template<>
-struct StdintTypeForSizeAndSignedness<1, true>
-{ typedef int8_t   Type; };
-
-template<>
-struct StdintTypeForSizeAndSignedness<1, false>
-{ typedef uint8_t  Type; };
-
-template<>
-struct StdintTypeForSizeAndSignedness<2, true>
-{ typedef int16_t  Type; };
-
-template<>
-struct StdintTypeForSizeAndSignedness<2, false>
-{ typedef uint16_t Type; };
-
-template<>
-struct StdintTypeForSizeAndSignedness<4, true>
-{ typedef int32_t  Type; };
-
-template<>
-struct StdintTypeForSizeAndSignedness<4, false>
-{ typedef uint32_t Type; };
-
-template<>
-struct StdintTypeForSizeAndSignedness<8, true>
-{ typedef int64_t  Type; };
-
-template<>
-struct StdintTypeForSizeAndSignedness<8, false>
-{ typedef uint64_t Type; };
-
-template<typename IntegerType>
-struct UnsignedType
-{
-    typedef typename StdintTypeForSizeAndSignedness<sizeof(IntegerType),
-                                                    false>::Type Type;
-};
-
-template<typename IntegerType>
-struct IsSigned
-{
-    static const bool value = IntegerType(-1) <= IntegerType(0);
-};
-
 template<typename IntegerType, size_t Size = sizeof(IntegerType)>
 struct TwiceBiggerType
 {
-    typedef typename StdintTypeForSizeAndSignedness<
+    typedef typename detail::StdintTypeForSizeAndSignedness<
                        sizeof(IntegerType) * 2,
                        IsSigned<IntegerType>::value
                      >::Type Type;
 };
 
 template<typename IntegerType>
 struct TwiceBiggerType<IntegerType, 8>
 {
     typedef UnsupportedType Type;
 };
 
-template<typename IntegerType>
-struct PositionOfSignBit
-{
-    static const size_t value = CHAR_BIT * sizeof(IntegerType) - 1;
-};
-
-template<typename IntegerType>
-struct MinValue
-{
-  private:
-    typedef typename UnsignedType<IntegerType>::Type UnsignedIntegerType;
-    static const size_t PosOfSignBit = PositionOfSignBit<IntegerType>::value;
-
-  public:
-    // Bitwise ops may return a larger type, that's why we cast explicitly.
-    // In C++, left bit shifts on signed values is undefined by the standard
-    // unless the shifted value is representable.
-    // Notice that signed-to-unsigned conversions are always well-defined in
-    // the standard as the value congruent to 2**n, as expected. By contrast,
-    // unsigned-to-signed is only well-defined if the value is representable.
-    static const IntegerType value =
-        IsSigned<IntegerType>::value
-        ? IntegerType(UnsignedIntegerType(1) << PosOfSignBit)
-        : IntegerType(0);
-};
-
-template<typename IntegerType>
-struct MaxValue
-{
-    // Tricksy, but covered by the unit test.
-    // Relies heavily on the type of MinValue<IntegerType>::value
-    // being IntegerType.
-    static const IntegerType value = ~MinValue<IntegerType>::value;
-};
-
-/*
- * Step 3: Implement the actual validity checks.
- *
- * Ideas taken from IntegerLib, code different.
- */
-
 template<typename T>
 inline bool
 HasSignBit(T x)
 {
   // In C++, right bit shifts on negative values is undefined by the standard.
   // Notice that signed-to-unsigned conversions are always well-defined in the
   // standard, as the value congruent modulo 2**n as expected. By contrast,
   // unsigned-to-signed is only well-defined if the value is representable.
-  return bool(typename UnsignedType<T>::Type(x)
-                >> PositionOfSignBit<T>::value);
+  return bool(typename MakeUnsigned<T>::Type(x) >> PositionOfSignBit<T>::value);
 }
 
 // Bitwise ops may return a larger type, so it's good to use this inline
 // helper guaranteeing that the result is really of type T.
 template<typename T>
 inline T
 BinaryComplement(T x)
 {
@@ -355,34 +254,34 @@ inline bool
 IsAddValid(T x, T y)
 {
   // Addition is valid if the sign of x+y is equal to either that of x or that
   // of y. Since the value of x+y is undefined if we have a signed type, we
   // compute it using the unsigned type of the same size.
   // Beware! These bitwise operations can return a larger integer type,
   // if T was a small type like int8_t, so we explicitly cast to T.
 
-  typename UnsignedType<T>::Type ux = x;
-  typename UnsignedType<T>::Type uy = y;
-  typename UnsignedType<T>::Type result = ux + uy;
+  typename MakeUnsigned<T>::Type ux = x;
+  typename MakeUnsigned<T>::Type uy = y;
+  typename MakeUnsigned<T>::Type result = ux + uy;
   return IsSigned<T>::value
          ? HasSignBit(BinaryComplement(T((result ^ x) & (result ^ y))))
          : BinaryComplement(x) >= y;
 }
 
 template<typename T>
 inline bool
 IsSubValid(T x, T y)
 {
   // Subtraction is valid if either x and y have same sign, or x-y and x have
   // same sign. Since the value of x-y is undefined if we have a signed type,
   // we compute it using the unsigned type of the same size.
-  typename UnsignedType<T>::Type ux = x;
-  typename UnsignedType<T>::Type uy = y;
-  typename UnsignedType<T>::Type result = ux - uy;
+  typename MakeUnsigned<T>::Type ux = x;
+  typename MakeUnsigned<T>::Type uy = y;
+  typename MakeUnsigned<T>::Type result = ux - uy;
 
   return IsSigned<T>::value
          ? HasSignBit(BinaryComplement(T((result ^ x) & (x ^ y))))
          : x >= y;
 }
 
 template<typename T,
          bool IsTSigned = IsSigned<T>::value,
@@ -514,17 +413,17 @@ struct NegateImpl<T, true>
       return CheckedInt<T>(-val.mValue, true);
     }
 };
 
 } // namespace detail
 
 
 /*
- * Step 4: Now define the CheckedInt class.
+ * Step 3: Now define the CheckedInt class.
  */
 
 /**
  * @class CheckedInt
  * @brief Integer wrapper class checking for integer overflow and other errors
  * @param T the integer type to wrap. Can be any type among the following:
  *            - any basic integer type such as |int|
  *            - any stdint type such as |int8_t|
new file mode 100644
--- /dev/null
+++ b/mfbt/IntegerTypeTraits.h
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Helpers to manipulate integer types that don't fit in TypeTraits.h */
+
+#ifndef mozilla_IntegerTypeTraits_h
+#define mozilla_IntegerTypeTraits_h
+
+#include "mozilla/TypeTraits.h"
+#include <stdint.h>
+
+namespace mozilla {
+
+namespace detail {
+
+/**
+ * StdintTypeForSizeAndSignedness returns the stdint integer type
+ * of given size (can be 1, 2, 4 or 8) and given signedness
+ * (false means unsigned, true means signed).
+ */
+template<size_t Size, bool Signedness>
+struct StdintTypeForSizeAndSignedness;
+
+template<>
+struct StdintTypeForSizeAndSignedness<1, true>
+{ typedef int8_t   Type; };
+
+template<>
+struct StdintTypeForSizeAndSignedness<1, false>
+{ typedef uint8_t  Type; };
+
+template<>
+struct StdintTypeForSizeAndSignedness<2, true>
+{ typedef int16_t  Type; };
+
+template<>
+struct StdintTypeForSizeAndSignedness<2, false>
+{ typedef uint16_t Type; };
+
+template<>
+struct StdintTypeForSizeAndSignedness<4, true>
+{ typedef int32_t  Type; };
+
+template<>
+struct StdintTypeForSizeAndSignedness<4, false>
+{ typedef uint32_t Type; };
+
+template<>
+struct StdintTypeForSizeAndSignedness<8, true>
+{ typedef int64_t  Type; };
+
+template<>
+struct StdintTypeForSizeAndSignedness<8, false>
+{ typedef uint64_t Type; };
+
+} // namespace detail
+
+template<size_t Size>
+struct UnsignedStdintTypeForSize
+  : detail::StdintTypeForSizeAndSignedness<Size, false>
+{};
+
+template<typename IntegerType>
+struct PositionOfSignBit
+{
+  static_assert(IsIntegral<IntegerType>::value, "PositionOfSignBit is only for integral types");
+  // 8 here should be CHAR_BIT from limits.h, but the world has moved on.
+  static const size_t value = 8 * sizeof(IntegerType) - 1;
+};
+
+/**
+ * MinValue returns the minimum value of the given integer type as a
+ * compile-time constant, which std::numeric_limits<IntegerType>::min()
+ * cannot do in c++98.
+ */
+template<typename IntegerType>
+struct MinValue
+{
+  private:
+    static_assert(IsIntegral<IntegerType>::value, "MinValue is only for integral types");
+
+    typedef typename MakeUnsigned<IntegerType>::Type UnsignedIntegerType;
+    static const size_t PosOfSignBit = PositionOfSignBit<IntegerType>::value;
+
+  public:
+    // Bitwise ops may return a larger type, that's why we cast explicitly.
+    // In C++, left bit shifts on signed values is undefined by the standard
+    // unless the shifted value is representable.
+    // Notice that signed-to-unsigned conversions are always well-defined in
+    // the standard as the value congruent to 2**n, as expected. By contrast,
+    // unsigned-to-signed is only well-defined if the value is representable.
+    static const IntegerType value =
+        IsSigned<IntegerType>::value
+        ? IntegerType(UnsignedIntegerType(1) << PosOfSignBit)
+        : IntegerType(0);
+};
+
+/**
+ * MaxValue returns the maximum value of the given integer type as a
+ * compile-time constant, which std::numeric_limits<IntegerType>::max()
+ * cannot do in c++98.
+ */
+template<typename IntegerType>
+struct MaxValue
+{
+    static_assert(IsIntegral<IntegerType>::value, "MaxValue is only for integral types");
+
+    // Tricksy, but covered by the CheckedInt unit test.
+    // Relies on the type of MinValue<IntegerType>::value
+    // being IntegerType.
+    static const IntegerType value = ~MinValue<IntegerType>::value;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_IntegerTypeTraits_h
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -27,16 +27,17 @@ EXPORTS.mozilla = [
     'DebugOnly.h',
     'decimal/Decimal.h',
     'Endian.h',
     'EnumSet.h',
     'FloatingPoint.h',
     'GuardObjects.h',
     'HashFunctions.h',
     'IntegerPrintfMacros.h',
+    'IntegerTypeTraits.h',
     'Likely.h',
     'LinkedList.h',
     'MathAlgorithms.h',
     'Maybe.h',
     'MemoryChecking.h',
     'MemoryReporting.h',
     'Move.h',
     'MSIntTypes.h',
--- a/mfbt/tests/TestCheckedInt.cpp
+++ b/mfbt/tests/TestCheckedInt.cpp
@@ -3,20 +3,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/CheckedInt.h"
 
 #include <iostream>
 #include <climits>
 
-#ifndef MOZ_CHECKEDINT_USE_MFBT
-#  error "MOZ_CHECKEDINT_USE_MFBT should be defined by CheckedInt.h"
-#endif
-
 using namespace mozilla;
 
 int gIntegerTypesTested = 0;
 int gTestsPassed = 0;
 int gTestsFailed = 0;
 
 void verifyImplFunction(bool x, bool expected,
                         const char* file, int line,
@@ -37,34 +33,34 @@ void verifyImplFunction(bool x, bool exp
 }
 
 #define VERIFY_IMPL(x, expected) \
     verifyImplFunction((x), \
     (expected), \
     __FILE__, \
     __LINE__, \
     sizeof(T), \
-    detail::IsSigned<T>::value)
+    IsSigned<T>::value)
 
 #define VERIFY(x)            VERIFY_IMPL(x, true)
 #define VERIFY_IS_FALSE(x)   VERIFY_IMPL(x, false)
 #define VERIFY_IS_VALID(x)   VERIFY_IMPL((x).isValid(), true)
 #define VERIFY_IS_INVALID(x) VERIFY_IMPL((x).isValid(), false)
 #define VERIFY_IS_VALID_IF(x,condition) VERIFY_IMPL((x).isValid(), (condition))
 
 template<typename T, size_t Size = sizeof(T)>
 struct testTwiceBiggerType
 {
     static void run()
     {
       VERIFY(detail::IsSupported<typename detail::TwiceBiggerType<T>::Type>::value);
       VERIFY(sizeof(typename detail::TwiceBiggerType<T>::Type)
                == 2 * sizeof(T));
-      VERIFY(bool(detail::IsSigned<typename detail::TwiceBiggerType<T>::Type>::value)
-               == bool(detail::IsSigned<T>::value));
+      VERIFY(bool(IsSigned<typename detail::TwiceBiggerType<T>::Type>::value)
+               == bool(IsSigned<T>::value));
     }
 };
 
 template<typename T>
 struct testTwiceBiggerType<T, 8>
 {
     static void run()
     {
@@ -81,28 +77,28 @@ void test()
   static bool alreadyRun = false;
   // Integer types from different families may just be typedefs for types from other families.
   // e.g. int32_t might be just a typedef for int. No point re-running the same tests then.
   if (alreadyRun)
       return;
   alreadyRun = true;
 
   VERIFY(detail::IsSupported<T>::value);
-  const bool isTSigned = detail::IsSigned<T>::value;
+  const bool isTSigned = IsSigned<T>::value;
   VERIFY(bool(isTSigned) == !bool(T(-1) > T(0)));
 
   testTwiceBiggerType<T>::run();
 
-  typedef typename detail::UnsignedType<T>::Type unsignedT;
+  typedef typename MakeUnsigned<T>::Type unsignedT;
 
   VERIFY(sizeof(unsignedT) == sizeof(T));
-  VERIFY(detail::IsSigned<unsignedT>::value == false);
+  VERIFY(IsSigned<unsignedT>::value == false);
 
-  const CheckedInt<T> max(detail::MaxValue<T>::value);
-  const CheckedInt<T> min(detail::MinValue<T>::value);
+  const CheckedInt<T> max(MaxValue<T>::value);
+  const CheckedInt<T> min(MinValue<T>::value);
 
   // Check MinValue and MaxValue, since they are custom implementations and a mistake there
   // could potentially NOT be caught by any other tests... while making everything wrong!
 
   unsignedT bit = 1;
   unsignedT unsignedMinValue(min.value());
   unsignedT unsignedMaxValue(max.value());
   for (size_t i = 0; i < sizeof(T) * CHAR_BIT - 1; i++)
@@ -442,27 +438,27 @@ void test()
     VERIFY_IS_INVALID(foo);
   }
 
   // Check that construction of CheckedInt from an integer value of a mismatched type is checked
   // Also check casting between all types.
 
   #define VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE2(U,V,PostVExpr) \
   { \
-    bool isUSigned = detail::IsSigned<U>::value; \
+    bool isUSigned = IsSigned<U>::value; \
     VERIFY_IS_VALID(CheckedInt<T>(V(  0)PostVExpr)); \
     VERIFY_IS_VALID(CheckedInt<T>(V(  1)PostVExpr)); \
     VERIFY_IS_VALID(CheckedInt<T>(V(100)PostVExpr)); \
     if (isUSigned) \
       VERIFY_IS_VALID_IF(CheckedInt<T>(V(-1)PostVExpr), isTSigned); \
     if (sizeof(U) > sizeof(T)) \
-      VERIFY_IS_INVALID(CheckedInt<T>(V(detail::MaxValue<T>::value)PostVExpr + one.value())); \
-    VERIFY_IS_VALID_IF(CheckedInt<T>(detail::MaxValue<U>::value), \
+      VERIFY_IS_INVALID(CheckedInt<T>(V(MaxValue<T>::value)PostVExpr + one.value())); \
+    VERIFY_IS_VALID_IF(CheckedInt<T>(MaxValue<U>::value), \
       (sizeof(T) > sizeof(U) || ((sizeof(T) == sizeof(U)) && (isUSigned || !isTSigned)))); \
-    VERIFY_IS_VALID_IF(CheckedInt<T>(detail::MinValue<U>::value), \
+    VERIFY_IS_VALID_IF(CheckedInt<T>(MinValue<U>::value), \
       isUSigned == false ? 1 \
                          : bool(isTSigned) == false ? 0 \
                                                     : sizeof(T) >= sizeof(U)); \
   }
   #define VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(U) \
     VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE2(U,U,+0) \
     VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE2(U,CheckedInt<U>,.toChecked<T>())