0031943: Foundation Classes, TColStd_PackedMapOfInteger - get rid of TCollection_BasicMap
[occt.git] / src / TColStd / TColStd_PackedMapOfInteger.hxx
index 0baa0f6..c8dd276 100644 (file)
 #ifndef TColStd_PackedMapOfInteger_HeaderFile
 #define TColStd_PackedMapOfInteger_HeaderFile
 
+#include <Standard.hxx>
+#include <Standard_Address.hxx>
+#include <Standard_Boolean.hxx>
 #include <Standard_DefineAlloc.hxx>
+#include <Standard_Integer.hxx>
 #include <Standard_NoSuchObject.hxx>
-#include <TCollection_BasicMap.hxx>
-#include <TCollection_BasicMapIterator.hxx>
+#include <Standard_OStream.hxx>
+#include <Standard_Handle.hxx>
 
 /**
  * Optimized Map of integer values. Each block of 32 integers is stored in 8 bytes in memory.
  */
-class TColStd_PackedMapOfInteger : private TCollection_BasicMap
+class TColStd_PackedMapOfInteger
 {
- public:
-  // operators new and delete must be defined explicitly 
-  // since inherited ones are not accessible
+public:
   DEFINE_STANDARD_ALLOC
 
+private:
+
+  //! 5 lower bits
+  static const unsigned int MASK_LOW = 0x001f;
+
+  //! 27 upper bits
+  static const unsigned int MASK_HIGH = ~MASK_LOW;
+
+  //! Class implementing a block of 32 consecutive integer values as a node of a Map collection.
+  //! The data are stored in 64 bits as:
+  //!  - bits  0 - 4 : (number of integers stored in the block) - 1;
+  //!  - bits  5 - 31: base address of the block of integers (low bits assumed 0)
+  //!  - bits 32 - 63: 32-bit field where each bit indicates the presence of the corresponding integer in the block.
+  //!                  Number of non-zero bits must be equal to the number expressed in bits 0-4.
+  class TColStd_intMapNode
+  {
+  public:
+    TColStd_intMapNode (TColStd_intMapNode* thePtr = NULL)
+    : myNext (thePtr), myMask (0), myData (0) {}
+
+    TColStd_intMapNode (Standard_Integer theValue, TColStd_intMapNode*& thePtr)
+    : myNext (thePtr),
+      myMask ((unsigned int) (theValue & MASK_HIGH)),
+      myData (1 << (theValue & MASK_LOW)) {}
+
+    TColStd_intMapNode (unsigned int theMask, unsigned int theData, TColStd_intMapNode* thePtr)
+    : myNext (thePtr),
+      myMask (theMask),
+      myData (theData) {}
+
+    unsigned int Mask() const { return myMask; }
+
+    unsigned int Data() const { return myData; }
+
+    unsigned int& ChangeMask() { return myMask; }
+
+    unsigned int& ChangeData() { return myData; }
+
+    //! Compute the sequential index of this packed node in the map.
+    Standard_Integer Key() const { return Standard_Integer (myMask & MASK_HIGH); }
+
+    //! Return the number of set integer keys.
+    size_t NbValues() const { return size_t(myMask & MASK_LOW) + 1; }
+
+    //! Return TRUE if this packed node is not empty.
+    Standard_Boolean HasValues() const { return (myData != 0); }
+
+    //! Return TRUE if the given integer key is set within this packed node.
+    Standard_Integer HasValue (Standard_Integer theValue) const { return (myData & (1 << (theValue & MASK_LOW))); }
+
+    //! Add integer key to this packed node.
+    //! @return TRUE if key has been added
+    Standard_Boolean AddValue (Standard_Integer theValue)
+    {
+      const Standard_Integer aValInt = (1 << (theValue & MASK_LOW));
+      if ((myData & aValInt) == 0)
+      {
+        myData ^= aValInt;
+        ++myMask;
+        return Standard_True;
+      }
+      return Standard_False;
+    }
+
+    //! Delete integer key from this packed node.
+    //! @return TRUE if key has been deleted
+    Standard_Boolean DelValue (Standard_Integer theValue)
+    {
+      const Standard_Integer aValInt = (1 << (theValue & MASK_LOW));
+      if ((myData & aValInt) != 0)
+      {
+        myData ^= aValInt;
+        myMask--;
+        return Standard_True;
+      }
+      return Standard_False;
+    }
+
+    //! Find the smallest non-zero bit under the given mask. Outputs the new mask
+    //! that does not contain the detected bit.
+    Standard_Integer FindNext (unsigned int& theMask) const;
+
+    //! Return the next node having the same hash code.
+    TColStd_intMapNode* Next() const { return myNext; }
+
+    //! Set the next node having the same hash code.
+    void SetNext (TColStd_intMapNode* theNext) { myNext = theNext; }
+
+  public:
+    //! Support of Map interface.
+    Standard_Integer HashCode (Standard_Integer theUpper) const
+    {
+      return ::HashCode (Standard_Integer(myMask >> 5), theUpper);
+    }
+
+    //! Support of Map interface.
+    Standard_Boolean IsEqual (Standard_Integer theOther) const
+    {
+      return ((myMask >> 5) == (unsigned)theOther);
+    }
+
+  private:
+    TColStd_intMapNode* myNext;
+    unsigned int myMask;
+    unsigned int myData;
+  };
+
 public:
 
   //! Iterator of class TColStd_PackedMapOfInteger.
-  class Iterator : public TCollection_BasicMapIterator
+  class Iterator
   {
   public:
 
     /// Empty Constructor.
-    Iterator() : myIntMask (~0U), myKey (0) {}
+    Iterator()
+    : myBuckets (NULL),
+      myNode (NULL),
+      myNbBuckets (-1),
+      myBucket (-1),
+      myIntMask (~0U),
+      myKey (0) {}
 
     /// Constructor.
     Iterator (const TColStd_PackedMapOfInteger& theMap)
-    : TCollection_BasicMapIterator (theMap),
+    : myBuckets (theMap.myData1),
+      myNode (NULL),
+      myNbBuckets (theMap.myData1 != NULL ? theMap.myNbBuckets : -1),
+      myBucket (-1),
       myIntMask (~0U)
     {
+      next();
       myKey = myNode != NULL ? TColStd_intMapNode_findNext (myNode, myIntMask) : 0;
     }
 
     //! Re-initialize with the same or another Map instance.
     void Initialize (const TColStd_PackedMapOfInteger& theMap)
     {
-      TCollection_BasicMapIterator::Initialize (theMap);
+      myBuckets = theMap.myData1;
+      myBucket = -1;
+      myNode = NULL;
+      myNbBuckets = theMap.myData1 != NULL ? theMap.myNbBuckets : -1;
+      next();
+
       myIntMask = ~0U;
       myKey = myNode != NULL ? TColStd_intMapNode_findNext (myNode, myIntMask) : 0;
     }
@@ -60,7 +184,10 @@ public:
     //! Restart the iteration
     void Reset()
     {
-      TCollection_BasicMapIterator::Reset();
+      myBucket = -1;
+      myNode = NULL;
+      next();
+
       myIntMask = ~0U;
       myKey = myNode != NULL ? TColStd_intMapNode_findNext (myNode, myIntMask) : 0;
     }
@@ -72,10 +199,13 @@ public:
       return myKey;
     }
 
+    //! Return TRUE if iterator points to the node.
+    Standard_Boolean More() const { return myNode != NULL; }
+
     //! Increment the iterator
     void Next()
     {
-      for (; myNode != NULL; TCollection_BasicMapIterator::Next())
+      for (; myNode != NULL; next())
       {
         myKey = TColStd_intMapNode_findNext (myNode, myIntMask);
         if (myIntMask != ~0u)
@@ -84,24 +214,59 @@ public:
         }
       }
     }
+  private:
+    //! Go to the next bucket in the map.
+    void next()
+    {
+      if (myBuckets == NULL)
+      {
+        return;
+      }
+
+      if (myNode != NULL)
+      {
+        myNode = myNode->Next();
+      }
+
+      while (myNode == NULL)
+      {
+        ++myBucket;
+        if (myBucket > myNbBuckets)
+        {
+          return;
+        }
+        myNode = myBuckets[myBucket];
+      }
+    }
 
   private:
+    TColStd_intMapNode** myBuckets;
+    TColStd_intMapNode*  myNode;
+    Standard_Integer myNbBuckets;
+    Standard_Integer myBucket;
+
     unsigned int     myIntMask; //!< all bits set above the iterated position
     Standard_Integer myKey;     //!< Currently iterated key
   };
 
 public:
 
-  /// Constructor
-  inline  TColStd_PackedMapOfInteger  (const Standard_Integer NbBuckets = 1)
-    : TCollection_BasicMap (NbBuckets, Standard_True),
-      myExtent             (0) {}
-
-  /// Copy constructor
-  inline TColStd_PackedMapOfInteger (const TColStd_PackedMapOfInteger& theOther)
-    : TCollection_BasicMap (1, Standard_True),
-      myExtent             (0)
-  { Assign(theOther); }
+  //! Constructor
+  TColStd_PackedMapOfInteger (const Standard_Integer theNbBuckets = 1)
+  : myData1 (NULL),
+    myNbBuckets (theNbBuckets),
+    myNbPackedMapNodes (0),
+    myExtent (0) {}
+
+  //! Copy constructor
+  TColStd_PackedMapOfInteger (const TColStd_PackedMapOfInteger& theOther)
+  : myData1 (NULL),
+    myNbBuckets (1),
+    myNbPackedMapNodes (0),
+    myExtent (0)
+  {
+    Assign (theOther);
+  }
 
   inline TColStd_PackedMapOfInteger&
                           operator =  (const TColStd_PackedMapOfInteger& Other) 
@@ -119,17 +284,14 @@ public:
   Standard_EXPORT  Standard_Boolean
                           Remove        (const Standard_Integer aKey);
 
-  inline Standard_Integer NbBuckets     () const
-  { return TCollection_BasicMap::NbBuckets(); }
-
-  inline Standard_Integer Extent        () const
-  { return Standard_Integer (myExtent); }
+  //! Returns the number of map buckets (not that since integers are packed in this map, the number is smaller than extent).
+  Standard_Integer NbBuckets() const { return myNbBuckets; }
 
-  inline Standard_Boolean IsEmpty       () const
-  { return TCollection_BasicMap::IsEmpty(); }
+  //! Returns map extent.
+  Standard_Integer Extent() const { return Standard_Integer (myExtent); }
 
-  inline void             Statistics    (Standard_OStream& outStream) const
-  { TCollection_BasicMap::Statistics (outStream); }
+  //! Returns TRUE if map is empty.
+  Standard_Boolean IsEmpty() const { return myNbPackedMapNodes == 0; }
 
   /**
    * Query the minimal contained key value.
@@ -141,6 +303,10 @@ public:
    */
   Standard_EXPORT Standard_Integer GetMaximalMapped () const;
 
+  //! Prints useful statistics about the map.
+  //! It can be used to test the quality of the hashcoding.
+  Standard_EXPORT void Statistics (Standard_OStream& theStream) const;
+
 public:
   //!@name Boolean operations with maps as sets of integers
   //!@{
@@ -270,19 +436,14 @@ public:
   //!@}
   
  protected:
-  // ---------- PROTECTED METHODS ----------
-  inline Standard_Integer InternalExtent () const
-  { return TCollection_BasicMap::Extent(); }
 
-private:
+   //! Returns TRUE if resizing the map should be considered.
+   Standard_Boolean Resizable() const { return IsEmpty() || (myNbPackedMapNodes > myNbBuckets); }
 
-  //! Class implementing a block of 32 consecutive integer values as a node of a Map collection.
-  //! The data are stored in 64 bits as:
-  //!  - bits  0 - 4 : (number of integers stored in the block) - 1;
-  //!  - bits  5 - 31: base address of the block of integers (low bits assumed 0)
-  //!  - bits 32 - 63: 32-bit field where each bit indicates the presence of the corresponding integer in the block.
-  //!                  Number of non-zero bits must be equal to the number expressed in bits 0-4.
-  class TColStd_intMapNode;
+   //! Return an integer index for specified key.
+   static Standard_Integer packedKeyIndex (Standard_Integer theKey) { return (unsigned)theKey >> 5; }
+
+private:
 
   //! Find the smallest non-zero bit under the given mask.
   //! Outputs the new mask that does not contain the detected bit.
@@ -294,8 +455,26 @@ private:
   Standard_EXPORT static Standard_Integer TColStd_intMapNode_findPrev (const Standard_Address theNode,
                                                                        unsigned int& theMask);
 
+  //! Compute the population (i.e., the number of non-zero bits) of the 32-bit word theData.
+  //! The population is stored decremented as it is defined in TColStd_intMapNode.
+  //! Source: H.S.Warren, Hacker's Delight, Addison-Wesley Inc. 2002, Ch.5.1
+  static size_t TColStd_Population (unsigned int& theMask, unsigned int theData)
+  {
+    unsigned int aRes = theData - ((theData>>1) & 0x55555555);
+    aRes  = (aRes & 0x33333333) + ((aRes>>2)    & 0x33333333);
+    aRes  = (aRes + (aRes>>4)) & 0x0f0f0f0f;
+    aRes  = aRes + (aRes>>8);
+    aRes  = aRes + (aRes>>16);
+    theMask = (theMask & TColStd_PackedMapOfInteger::MASK_HIGH) | ((aRes - 1) & TColStd_PackedMapOfInteger::MASK_LOW);
+    return size_t(aRes & 0x3f);
+  }
+
 private:
-  size_t myExtent;
+
+  TColStd_intMapNode** myData1;            //!< data array
+  Standard_Integer     myNbBuckets;        //!< number of buckets (size of data array)
+  Standard_Integer     myNbPackedMapNodes; //!< amount of packed map nodes
+  Standard_Size        myExtent;           //!< extent of this map (number of unpacked integer keys)
 };
 
 #endif