Bug 1052582 Part 1 - Add support to EnumSet for more than 32 values. r?Waldo draft
authorMatt Howell <mhowell@mozilla.com>
Tue, 22 May 2018 16:11:12 -0700
changeset 815089 91340d9e5fee5cab14fe72c1a280b63b3421c2f0
parent 814904 fa376bf17cc95539f5e37186977d760296fb5093
child 815090 8a3b721eb43c3dc8615d1553e8e55b7e975968f8
child 815096 4ececa06c7c43fc212063916ae72c1190f4a1219
push id115431
push usermhowell@mozilla.com
push dateFri, 06 Jul 2018 17:32:44 +0000
reviewersWaldo
bugs1052582
milestone63.0a1
Bug 1052582 Part 1 - Add support to EnumSet for more than 32 values. r?Waldo The next patch in this series extends an enum that's used in an EnumSet to 34 values, so we need to extend EnumSet beyond a hardcoded uint32_t storage type. MozReview-Commit-ID: J3Pxj5xYtdb
mfbt/EnumSet.h
mfbt/tests/TestEnumSet.cpp
--- a/mfbt/EnumSet.h
+++ b/mfbt/EnumSet.h
@@ -8,31 +8,36 @@
 
 #ifndef mozilla_EnumSet_h
 #define mozilla_EnumSet_h
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 
 #include <initializer_list>
+#include <limits>
 
 #include <stdint.h>
 
 namespace mozilla {
 
 /**
- * EnumSet<T> is a set of values defined by an enumeration. It is implemented
- * using a 32 bit mask for each value so it will only work for enums with an int
- * representation less than 32. It works both for enum and enum class types.
+ * EnumSet represents a set of multiple values of an enum (or enum class).
+ * Template parameter T should be the enum you want to represent.
+ * EnumSet is implemented using a bitmask of all possible values of the enum T,
+ * and the default type for the bitmask value is uint32_t, so if your enum has
+ * more than 32 possible values, you should also supply a backing integer type
+ * as template parameter U which has at least as many bits as the number of
+ * possible values of enum T.
  */
-template<typename T>
+template<typename T, typename U = uint32_t>
 class EnumSet
 {
 public:
-  typedef uint32_t serializedType;
+  typedef U serializedType;
 
   EnumSet()
     : mBitField(0)
   {
   }
 
   MOZ_IMPLICIT EnumSet(T aEnum)
     : mBitField(bitFor(aEnum))
@@ -79,112 +84,112 @@ public:
   {
     incVersion();
     mBitField |= bitFor(aEnum);
   }
 
   /**
    * Add an element
    */
-  EnumSet<T> operator+(T aEnum) const
+  EnumSet<T,U> operator+(T aEnum) const
   {
-    EnumSet<T> result(*this);
+    EnumSet<T,U> result(*this);
     result += aEnum;
     return result;
   }
 
   /**
    * Union
    */
-  void operator+=(const EnumSet<T> aEnumSet)
+  void operator+=(const EnumSet<T,U> aEnumSet)
   {
     incVersion();
     mBitField |= aEnumSet.mBitField;
   }
 
   /**
    * Union
    */
-  EnumSet<T> operator+(const EnumSet<T> aEnumSet) const
+  EnumSet<T,U> operator+(const EnumSet<T,U> aEnumSet) const
   {
-    EnumSet<T> result(*this);
+    EnumSet<T,U> result(*this);
     result += aEnumSet;
     return result;
   }
 
   /**
    * Remove an element
    */
   void operator-=(T aEnum)
   {
     incVersion();
     mBitField &= ~(bitFor(aEnum));
   }
 
   /**
    * Remove an element
    */
-  EnumSet<T> operator-(T aEnum) const
+  EnumSet<T,U> operator-(T aEnum) const
   {
-    EnumSet<T> result(*this);
+    EnumSet<T,U> result(*this);
     result -= aEnum;
     return result;
   }
 
   /**
    * Remove a set of elements
    */
-  void operator-=(const EnumSet<T> aEnumSet)
+  void operator-=(const EnumSet<T,U> aEnumSet)
   {
     incVersion();
     mBitField &= ~(aEnumSet.mBitField);
   }
 
   /**
    * Remove a set of elements
    */
-  EnumSet<T> operator-(const EnumSet<T> aEnumSet) const
+  EnumSet<T,U> operator-(const EnumSet<T,U> aEnumSet) const
   {
-    EnumSet<T> result(*this);
+    EnumSet<T,U> result(*this);
     result -= aEnumSet;
     return result;
   }
 
   /**
    * Clear
    */
   void clear()
   {
     incVersion();
     mBitField = 0;
   }
 
   /**
    * Intersection
    */
-  void operator&=(const EnumSet<T> aEnumSet)
+  void operator&=(const EnumSet<T,U> aEnumSet)
   {
     incVersion();
     mBitField &= aEnumSet.mBitField;
   }
 
   /**
    * Intersection
    */
-  EnumSet<T> operator&(const EnumSet<T> aEnumSet) const
+  EnumSet<T,U> operator&(const EnumSet<T,U> aEnumSet) const
   {
-    EnumSet<T> result(*this);
+    EnumSet<T,U> result(*this);
     result &= aEnumSet;
     return result;
   }
 
   /**
    * Equality
    */
-  bool operator==(const EnumSet<T> aEnumSet) const
+  bool operator==(const EnumSet<T,U> aEnumSet) const
   {
     return mBitField == aEnumSet.mBitField;
   }
 
   /**
    * Test is an element is contained in the set.
    */
   bool contains(T aEnum) const
@@ -193,17 +198,17 @@ public:
   }
 
   /**
    * Return the number of elements in the set.
    */
   uint8_t size() const
   {
     uint8_t count = 0;
-    for (uint32_t bitField = mBitField; bitField; bitField >>= 1) {
+    for (serializedType bitField = mBitField; bitField; bitField >>= 1) {
       if (bitField & 1) {
         count++;
       }
     }
     return count;
   }
 
   bool isEmpty() const
@@ -219,29 +224,29 @@ public:
   void deserialize(serializedType aValue)
   {
     incVersion();
     mBitField = aValue;
   }
 
   class ConstIterator
   {
-    const EnumSet<T>* mSet;
+    const EnumSet<T,U>* mSet;
     uint32_t mPos;
 #ifdef DEBUG
     uint64_t mVersion;
 #endif
 
     void checkVersion() const {
       // Check that the set has not been modified while being iterated.
       MOZ_ASSERT_IF(mSet, mSet->mVersion == mVersion);
     }
 
    public:
-    ConstIterator(const EnumSet<T>& aSet, uint32_t aPos)
+    ConstIterator(const EnumSet<T,U>& aSet, uint32_t aPos)
      : mSet(&aSet), mPos(aPos)
     {
 #ifdef DEBUG
       mVersion = mSet->mVersion;
 #endif
       MOZ_ASSERT(aPos <= kMaxBits);
       if (aPos != kMaxBits && !mSet->contains(T(mPos)))
         ++*this;
@@ -303,30 +308,30 @@ public:
     return ConstIterator(*this, 0);
   }
 
   ConstIterator end() const {
     return ConstIterator(*this, kMaxBits);
   }
 
 private:
-  static uint32_t bitFor(T aEnum)
+  static serializedType bitFor(T aEnum)
   {
     uint32_t bitNumber = (uint32_t)aEnum;
     MOZ_ASSERT(bitNumber < kMaxBits);
-    return 1U << bitNumber;
+    return (serializedType)1 << bitNumber;
   }
 
   void incVersion() {
 #ifdef DEBUG
     mVersion++;
 #endif
   }
 
-  static const size_t kMaxBits = 32;
+  static const size_t kMaxBits = std::numeric_limits<serializedType>::digits;
   serializedType mBitField;
 
 #ifdef DEBUG
   uint64_t mVersion = 0;
 #endif
 };
 
 } // namespace mozilla
--- a/mfbt/tests/TestEnumSet.cpp
+++ b/mfbt/tests/TestEnumSet.cpp
@@ -28,16 +28,75 @@ enum SeaBird
   TROPICBIRD,
   SKUA,
   GULL,
   TERN,
   SKIMMER,
   AUK
 };
 
+enum DeepSeaFish
+{
+  FLATFISH,
+  HAGFISH,
+  EELPOUT,
+  GREENEYE_EEL,
+  STRINGRAY,
+  LUMPFISH,
+  BATFISH,
+  RATTAIL,
+  BROTULA,
+  BRISTLEMOUTH,
+  ANGLERFISH,
+  FANGTOOTH,
+  VIPERFISH,
+  BLACK_SWALLOWER,
+  TELESCOPEFISH,
+  HAMMERJAW,
+  DAGGERTOOTH,
+  BARRACUDINA,
+  SCABBARDFISH,
+  BOBTAIL_SNIPE_EEL,
+  UNICORN_CRESTFISH,
+  PELICAN_EEL,
+  FLABBY_WHALEFISH,
+  LANTERNFISH,
+  OPAH,
+  LANCEFISH,
+  BARRELEYE,
+  RIDGEHEAD,
+  SABRETOOTH,
+  STOPLIGHT_LOOSEJAW,
+  HATCHETFISH,
+  DOLPHINFISH,
+  POMFRET,
+  BARRACUDA,
+  SNAGGLETOOTH,
+  BLACKSMELT,
+  TAPIRFISH,
+  GRENADIER,
+  SLICKHEAD,
+  OREODORY,
+  SPIDERFISH,
+};
+
+enum Mollusc
+{
+  SNAIL,
+  SLUG,
+  CLAM,
+  OYSTER,
+  SCALLOP,
+  GEODUCK,
+  MUSSEL,
+  SQUID,
+  OCTOPUS,
+  CUTTLEFISH
+};
+
 class EnumSetSuite
 {
 public:
   EnumSetSuite()
     : mAlcidae()
     , mDiomedeidae(ALBATROSS)
     , mPetrelProcellariidae(GADFLY_PETREL, TRUE_PETREL)
     , mNonPetrelProcellariidae(FULMAR, PRION, SHEARWATER)
@@ -57,16 +116,18 @@ public:
     testRemoveAllFrom();
     testRemoveAll();
     testIntersect();
     testInsersection();
     testEquality();
     testDuplicates();
     testIteration();
     testInitializerListConstuctor();
+    testLargeEnum();
+    testSmallEnum();
   }
 
 private:
   void testSize()
   {
     MOZ_RELEASE_ASSERT(mAlcidae.size() == 0);
     MOZ_RELEASE_ASSERT(mDiomedeidae.size() == 1);
     MOZ_RELEASE_ASSERT(mPetrelProcellariidae.size() == 2);
@@ -268,16 +329,76 @@ private:
 
     EnumSet<SeaBird> someBirds { SKIMMER, GULL, BOOBY };
     MOZ_RELEASE_ASSERT(someBirds.size() == 3);
     MOZ_RELEASE_ASSERT(someBirds.contains(SKIMMER));
     MOZ_RELEASE_ASSERT(someBirds.contains(GULL));
     MOZ_RELEASE_ASSERT(someBirds.contains(BOOBY));
   }
 
+  void testLargeEnum()
+  {
+    typedef EnumSet<DeepSeaFish, uint64_t> FishSet;
+    FishSet fishes {};
+    MOZ_RELEASE_ASSERT(fishes.size() == 0);
+    MOZ_RELEASE_ASSERT(fishes.isEmpty());
+
+    fishes += ANGLERFISH;
+    fishes += SPIDERFISH;
+    fishes += GRENADIER;
+    fishes += FLATFISH;
+
+    MOZ_RELEASE_ASSERT(fishes.size() == 4);
+    MOZ_RELEASE_ASSERT(fishes.contains(ANGLERFISH));
+    MOZ_RELEASE_ASSERT(fishes.contains(SPIDERFISH));
+    MOZ_RELEASE_ASSERT(fishes.contains(GRENADIER));
+    MOZ_RELEASE_ASSERT(fishes.contains(FLATFISH));
+
+    Vector<DeepSeaFish> vec;
+    for (auto fish : fishes) {
+      MOZ_RELEASE_ASSERT(vec.append(fish));
+    }
+
+    MOZ_RELEASE_ASSERT(vec.length() == 4);
+    MOZ_RELEASE_ASSERT(vec[0] == FLATFISH);
+    MOZ_RELEASE_ASSERT(vec[1] == ANGLERFISH);
+    MOZ_RELEASE_ASSERT(vec[2] == GRENADIER);
+    MOZ_RELEASE_ASSERT(vec[3] == SPIDERFISH);
+  }
+
+  void testSmallEnum()
+  {
+    typedef EnumSet<Mollusc, uint16_t> MolluscSet;
+    MolluscSet molluscs {};
+    MOZ_RELEASE_ASSERT(molluscs.size() == 0);
+    MOZ_RELEASE_ASSERT(molluscs.isEmpty());
+
+    molluscs += OYSTER;
+    molluscs += SQUID;
+    molluscs += SNAIL;
+    molluscs += CUTTLEFISH;
+
+    MOZ_RELEASE_ASSERT(molluscs.size() == 4);
+    MOZ_RELEASE_ASSERT(molluscs.contains(OYSTER));
+    MOZ_RELEASE_ASSERT(molluscs.contains(SQUID));
+    MOZ_RELEASE_ASSERT(molluscs.contains(SNAIL));
+    MOZ_RELEASE_ASSERT(molluscs.contains(CUTTLEFISH));
+
+    Vector<Mollusc> vec;
+    for (auto mollusc : molluscs) {
+      MOZ_RELEASE_ASSERT(vec.append(mollusc));
+    }
+
+    MOZ_RELEASE_ASSERT(vec.length() == 4);
+    MOZ_RELEASE_ASSERT(vec[0] == SNAIL);
+    MOZ_RELEASE_ASSERT(vec[1] == OYSTER);
+    MOZ_RELEASE_ASSERT(vec[2] == SQUID);
+    MOZ_RELEASE_ASSERT(vec[3] == CUTTLEFISH);
+  }
+
   EnumSet<SeaBird> mAlcidae;
   EnumSet<SeaBird> mDiomedeidae;
   EnumSet<SeaBird> mPetrelProcellariidae;
   EnumSet<SeaBird> mNonPetrelProcellariidae;
   EnumSet<SeaBird> mPetrels;
 };
 
 int