Bug 1266402 - Add iteration to EnumSet<T> so that it can be used in range-based for loops r=Waldo
authorJon Coppeard <jcoppeard@mozilla.com>
Thu, 28 Apr 2016 14:25:05 +0100
changeset 295333 d88619681bc2aa68a2c4008422ba3f02992af0c8
parent 295332 89b02b6959ee93a58c033c158005668bdb46773b
child 295334 fded1097567f93ee4c48482fb96164df6a134305
push id19006
push userkwierso@gmail.com
push dateFri, 29 Apr 2016 23:06:04 +0000
treeherderfx-team@8471d2d75bf4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs1266402
milestone49.0a1
Bug 1266402 - Add iteration to EnumSet<T> so that it can be used in range-based for loops r=Waldo
mfbt/EnumSet.h
mfbt/tests/TestEnumSet.cpp
--- a/mfbt/EnumSet.h
+++ b/mfbt/EnumSet.h
@@ -22,49 +22,60 @@ namespace mozilla {
  * representation less than 32. It works both for enum and enum class types.
  */
 template<typename T>
 class EnumSet
 {
 public:
   EnumSet()
     : mBitField(0)
-  { }
+  {
+    initVersion();
+  }
 
   MOZ_IMPLICIT EnumSet(T aEnum)
     : mBitField(bitFor(aEnum))
   { }
 
   EnumSet(T aEnum1, T aEnum2)
     : mBitField(bitFor(aEnum1) |
                 bitFor(aEnum2))
-  { }
+  {
+    initVersion();
+  }
 
   EnumSet(T aEnum1, T aEnum2, T aEnum3)
     : mBitField(bitFor(aEnum1) |
                 bitFor(aEnum2) |
                 bitFor(aEnum3))
-  { }
+  {
+    initVersion();
+  }
 
   EnumSet(T aEnum1, T aEnum2, T aEnum3, T aEnum4)
-   : mBitField(bitFor(aEnum1) |
-               bitFor(aEnum2) |
-               bitFor(aEnum3) |
-               bitFor(aEnum4))
-  { }
+    : mBitField(bitFor(aEnum1) |
+                bitFor(aEnum2) |
+                bitFor(aEnum3) |
+                bitFor(aEnum4))
+  {
+    initVersion();
+  }
 
   EnumSet(const EnumSet& aEnumSet)
-   : mBitField(aEnumSet.mBitField)
-  { }
+    : mBitField(aEnumSet.mBitField)
+  {
+    initVersion();
+  }
 
   /**
    * Add an element
    */
   void operator+=(T aEnum)
   {
+    incVersion();
     mBitField |= bitFor(aEnum);
   }
 
   /**
    * Add an element
    */
   EnumSet<T> operator+(T aEnum) const
   {
@@ -73,16 +84,17 @@ public:
     return result;
   }
 
   /**
    * Union
    */
   void operator+=(const EnumSet<T> aEnumSet)
   {
+    incVersion();
     mBitField |= aEnumSet.mBitField;
   }
 
   /**
    * Union
    */
   EnumSet<T> operator+(const EnumSet<T> aEnumSet) const
   {
@@ -91,16 +103,17 @@ public:
     return result;
   }
 
   /**
    * Remove an element
    */
   void operator-=(T aEnum)
   {
+    incVersion();
     mBitField &= ~(bitFor(aEnum));
   }
 
   /**
    * Remove an element
    */
   EnumSet<T> operator-(T aEnum) const
   {
@@ -109,16 +122,17 @@ public:
     return result;
   }
 
   /**
    * Remove a set of elements
    */
   void operator-=(const EnumSet<T> aEnumSet)
   {
+    incVersion();
     mBitField &= ~(aEnumSet.mBitField);
   }
 
   /**
    * Remove a set of elements
    */
   EnumSet<T> operator-(const EnumSet<T> aEnumSet) const
   {
@@ -127,24 +141,26 @@ public:
     return result;
   }
 
   /**
    * Clear
    */
   void clear()
   {
+    incVersion();
     mBitField = 0;
   }
 
   /**
    * Intersection
    */
   void operator&=(const EnumSet<T> aEnumSet)
   {
+    incVersion();
     mBitField &= aEnumSet.mBitField;
   }
 
   /**
    * Intersection
    */
   EnumSet<T> operator&(const EnumSet<T> aEnumSet) const
   {
@@ -167,17 +183,17 @@ public:
   bool contains(T aEnum) const
   {
     return mBitField & bitFor(aEnum);
   }
 
   /**
    * Return the number of elements in the set.
    */
-  uint8_t size()
+  uint8_t size() const
   {
     uint8_t count = 0;
     for (uint32_t bitField = mBitField; bitField; bitField >>= 1) {
       if (bitField & 1) {
         count++;
       }
     }
     return count;
@@ -190,25 +206,128 @@ public:
 
   uint32_t serialize() const
   {
     return mBitField;
   }
 
   void deserialize(uint32_t aValue)
   {
+    incVersion();
     mBitField = aValue;
   }
 
+  class ConstIterator
+  {
+    const EnumSet<T>* mSet;
+    uint32_t mPos;
+#ifdef DEBUG
+    uint64_t mVersion;
+#endif
+
+    void checkVersion() {
+      // 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)
+     : mSet(&aSet), mPos(aPos)
+    {
+#ifdef DEBUG
+      mVersion = mSet->mVersion;
+#endif
+      MOZ_ASSERT(aPos <= kMaxBits);
+      if (aPos != kMaxBits && !mSet->contains(T(mPos)))
+        ++*this;
+    }
+
+    ConstIterator(const ConstIterator& aOther)
+     : mSet(aOther.mSet), mPos(aOther.mPos)
+    {
+#ifdef DEBUG
+      mVersion = aOther.mVersion;
+      checkVersion();
+#endif
+    }
+
+    ConstIterator(ConstIterator&& aOther)
+     : mSet(aOther.mSet), mPos(aOther.mPos)
+    {
+#ifdef DEBUG
+      mVersion = aOther.mVersion;
+      checkVersion();
+#endif
+      aOther.mSet = nullptr;
+    }
+
+    ~ConstIterator() {
+      checkVersion();
+    }
+
+    bool operator==(const ConstIterator& other) {
+      MOZ_ASSERT(mSet == other.mSet);
+      checkVersion();
+      return mPos == other.mPos;
+    }
+
+    bool operator!=(const ConstIterator& other) {
+      return !(*this == other);
+    }
+
+    T operator*() {
+      MOZ_ASSERT(mSet);
+      MOZ_ASSERT(mPos < kMaxBits);
+      MOZ_ASSERT(mSet->contains(T(mPos)));
+      checkVersion();
+      return T(mPos);
+    }
+
+    ConstIterator& operator++() {
+      MOZ_ASSERT(mSet);
+      MOZ_ASSERT(mPos < kMaxBits);
+      checkVersion();
+      do {
+        mPos++;
+      } while (mPos < kMaxBits && !mSet->contains(T(mPos)));
+      return *this;
+    }
+  };
+
+  ConstIterator begin() const {
+    return ConstIterator(*this, 0);
+  }
+
+  ConstIterator end() const {
+    return ConstIterator(*this, kMaxBits);
+  }
+
 private:
   static uint32_t bitFor(T aEnum)
   {
     uint32_t bitNumber = (uint32_t)aEnum;
-    MOZ_ASSERT(bitNumber < 32);
+    MOZ_ASSERT(bitNumber < kMaxBits);
     return 1U << bitNumber;
   }
 
+  void initVersion() {
+#ifdef DEBUG
+    mVersion = 0;
+#endif
+  }
+
+  void incVersion() {
+#ifdef DEBUG
+    mVersion++;
+#endif
+  }
+
+  static const size_t kMaxBits = 32;
   uint32_t mBitField;
+
+#ifdef DEBUG
+  uint64_t mVersion;
+#endif
 };
 
 } // namespace mozilla
 
 #endif /* mozilla_EnumSet_h_*/
--- a/mfbt/tests/TestEnumSet.cpp
+++ b/mfbt/tests/TestEnumSet.cpp
@@ -1,15 +1,16 @@
 /* -*- 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/. */
 
 #include "mozilla/EnumSet.h"
+#include "mozilla/Vector.h"
 
 using namespace mozilla;
 
 enum SeaBird
 {
   PENGUIN,
   ALBATROSS,
   FULMAR,
@@ -54,16 +55,17 @@ public:
     testRemoveFrom();
     testRemove();
     testRemoveAllFrom();
     testRemoveAll();
     testIntersect();
     testInsersection();
     testEquality();
     testDuplicates();
+    testIteration();
   }
 
 private:
   void testSize()
   {
     MOZ_RELEASE_ASSERT(mAlcidae.size() == 0);
     MOZ_RELEASE_ASSERT(mDiomedeidae.size() == 1);
     MOZ_RELEASE_ASSERT(mPetrelProcellariidae.size() == 2);
@@ -228,16 +230,41 @@ private:
     likes += GADFLY_PETREL;
     likes += TRUE_PETREL;
     likes += DIVING_PETREL;
     likes += STORM_PETREL;
     MOZ_RELEASE_ASSERT(likes.size() == 4);
     MOZ_RELEASE_ASSERT(likes == mPetrels);
   }
 
+  void testIteration()
+  {
+    EnumSet<SeaBird> birds;
+    Vector<SeaBird> vec;
+
+    for (auto bird : birds) {
+      MOZ_RELEASE_ASSERT(vec.append(bird));
+    }
+    MOZ_RELEASE_ASSERT(vec.length() == 0);
+
+    birds += DIVING_PETREL;
+    birds += GADFLY_PETREL;
+    birds += STORM_PETREL;
+    birds += TRUE_PETREL;
+    for (auto bird : birds) {
+      MOZ_RELEASE_ASSERT(vec.append(bird));
+    }
+
+    MOZ_RELEASE_ASSERT(vec.length() == 4);
+    MOZ_RELEASE_ASSERT(vec[0] == GADFLY_PETREL);
+    MOZ_RELEASE_ASSERT(vec[1] == TRUE_PETREL);
+    MOZ_RELEASE_ASSERT(vec[2] == DIVING_PETREL);
+    MOZ_RELEASE_ASSERT(vec[3] == STORM_PETREL);
+  }
+
   EnumSet<SeaBird> mAlcidae;
   EnumSet<SeaBird> mDiomedeidae;
   EnumSet<SeaBird> mPetrelProcellariidae;
   EnumSet<SeaBird> mNonPetrelProcellariidae;
   EnumSet<SeaBird> mPetrels;
 };
 
 int