/*========================== begin_copyright_notice ============================ Copyright (C) 2017-2021 Intel Corporation SPDX-License-Identifier: MIT ============================= end_copyright_notice ===========================*/ #pragma once #include "Object.h" #include "utility.h" #include "MemCopy.h" #include "Threading.h" namespace iSTD { /*****************************************************************************\ Template Parameters \*****************************************************************************/ #define BitSetTemplateList class CAllocatorType #define CBitSetType CBitSet /*****************************************************************************\ Struct: TPtrSize Description: Defines type that is size of a pointer. It also provides a set of static functions for managing on bits, depending on pointer size. \*****************************************************************************/ template struct TPtrSize { }; // Specialization for 32bit pointers: template <> struct TPtrSize<4> { typedef unsigned int type; static inline type Bit( const DWORD index ) { return BIT( index ); } static inline DWORD Bsf( const type mask ) { return iSTD::bsf( mask ); } static inline DWORD Bsr( const type mask ) { return iSTD::bsr( mask ); } static inline DWORD Count( const type mask ) { return iSTD::BitCount( mask ); } }; // Specialization for 64bit pointers: template <> struct TPtrSize<8> { typedef unsigned long long type; static inline type Bit( const DWORD index ) { return QWBIT( index ); } static inline DWORD Bsf( const type mask ) { #if defined( _WIN64 ) || defined( __x86_64__ ) return iSTD::bsf64( mask ); #else // Should never compile this code - added to get rid of compilation // warnings on GCC. ASSERT( 0 ); return 0; #endif } static inline DWORD Bsr( const type mask ) { #if defined( _WIN64 ) || defined( __x86_64__ ) return iSTD::bsr64( mask ); #else // Should never compile this code - added to get rid of compilation // warnings on GCC. ASSERT( 0 ); return 0; #endif } static inline DWORD Count( const type mask ) { return iSTD::BitCount64( mask ); } }; /*****************************************************************************\ Class: CBitSet Description: Implements a dynamic bit set. For sets smaller than size of pointer, bits are stored in a pointer to the array, to prevent dynamic allocations. \*****************************************************************************/ template class CBitSet : public CObject { private: static const DWORD BITS_PER_BYTE = 8; typedef DWORD BITSET_ARRAY_TYPE; static const DWORD cBitsPerArrayElement = sizeof( BITSET_ARRAY_TYPE ) * BITS_PER_BYTE; static const DWORD cBitsInPtr = sizeof(BITSET_ARRAY_TYPE*) * 8; typedef TPtrSize CPtrSize; typedef CPtrSize::type bitptr_t; public: CBitSet( void ); CBitSet( DWORD size ); CBitSet( const CBitSetType& other ); virtual ~CBitSet( void ); void Resize( DWORD size ); void Clear( void ); void SetAll( void ); void Invert( void ); bool IsEmpty() const; bool IsEmpty( DWORD start, DWORD length ) const; bool IsSet( DWORD index ) const; bool Intersects( const CBitSetType& other ) const; DWORD GetNextMember( DWORD start ) const; void Set( DWORD index ); void Set( const CBitSetType& other ); void UnSet( DWORD index ); void UnSet( const CBitSetType& other ); DWORD GetSize( void ) const; DWORD BitCount( void ) const; DWORD BitCount( DWORD limit ) const; long Min( void ) const; long Max( void ) const; template T ConvertTo() const; bool operator==( const CBitSetType& other ) const; bool operator!=( const CBitSetType& other ) const; CBitSetType& operator= ( const CBitSetType& other ); CBitSetType& operator|= ( const CBitSetType& other ); CBitSetType& operator&= ( const CBitSetType& other ); protected: // Depending on a set size, bits can be stored in dynamically allocated // memory, or in a pointer. union { BITSET_ARRAY_TYPE* m_BitSetArray; bitptr_t m_PtrBits; }; DWORD m_Size; void Create( DWORD size ); void Copy( const CBitSetType& other ); void Delete( void ); bool StoredInPtr( void ) const; bitptr_t GetActivePtrMask( void ) const; BITSET_ARRAY_TYPE* GetArrayPointer( void ); const BITSET_ARRAY_TYPE* GetArrayPointer( void ) const; DECL_DEBUG_MUTEX( m_InstanceNotThreadSafe ) }; /*****************************************************************************\ Function: CBitSet Constructor Description: Initializes the BitSet Input: none Output: none \*****************************************************************************/ template CBitSetType::CBitSet( void ) : CObject() { m_BitSetArray = NULL; m_Size = 0; INIT_DEBUG_MUTEX( m_InstanceNotThreadSafe ); } /*****************************************************************************\ Function: CBitSet Constructor Description: Initializes the BitSet Input: DWORD size - initial size of the BitSet Output: none \*****************************************************************************/ template CBitSetType::CBitSet( DWORD size ) : CObject() { m_BitSetArray = NULL; m_Size = 0; INIT_DEBUG_MUTEX( m_InstanceNotThreadSafe ); Create( size ); } /*****************************************************************************\ Function: CBitSet Copy Constructor Description: Initializes the BitSet Input: const CBitSetType& other - other bitset to copy Output: none \*****************************************************************************/ template CBitSetType::CBitSet( const CBitSetType& other ) : CObject() { m_BitSetArray = NULL; m_Size = 0; INIT_DEBUG_MUTEX( m_InstanceNotThreadSafe ); Copy( other ); } /*****************************************************************************\ Function: CBitSet Destructor Description: Frees all internal dynamic memory Input: none Output: none \*****************************************************************************/ template CBitSetType::~CBitSet( void ) { Delete(); DELETE_DEBUG_MUTEX( m_InstanceNotThreadSafe ); } /*****************************************************************************\ Function: CBitSet::Resize Description: Resizes the bitset Input: DWORD size - new size of the BitSet Output: none \*****************************************************************************/ template void CBitSetType::Resize( DWORD size ) { ACQUIRE_DEBUG_MUTEX_WRITE( m_InstanceNotThreadSafe ); Create( size ); RELEASE_DEBUG_MUTEX_WRITE( m_InstanceNotThreadSafe ); } /*****************************************************************************\ Function: CBitSet::Clear Description: Unsets all bits in the bitset Input: none Output: none \*****************************************************************************/ template void CBitSetType::Clear( void ) { ACQUIRE_DEBUG_MUTEX_WRITE( m_InstanceNotThreadSafe ); if( StoredInPtr() ) { m_PtrBits = static_cast( 0 ); } else { const DWORD cArraySizeInBytes = ( m_Size + BITS_PER_BYTE - 1 ) / BITS_PER_BYTE; SafeMemSet( m_BitSetArray, 0, cArraySizeInBytes ); } RELEASE_DEBUG_MUTEX_WRITE( m_InstanceNotThreadSafe ); } /*****************************************************************************\ Function: CBitSet::SetAll Description: Sets all the bits in this bitset, from bit zero to bit "size". Note that any "extra" bits (bits that are part of the array but that are less than "size" are not set and remain unset. Input: void Output: void \*****************************************************************************/ template void CBitSetType::SetAll( void ) { ACQUIRE_DEBUG_MUTEX_WRITE( m_InstanceNotThreadSafe ); if( StoredInPtr() ) { m_PtrBits = GetActivePtrMask(); } else { ASSERT( m_BitSetArray ); const DWORD cArraySize = ( m_Size + cBitsPerArrayElement - 1 ) / cBitsPerArrayElement; const BITSET_ARRAY_TYPE cExtraBits = m_Size % cBitsPerArrayElement; const BITSET_ARRAY_TYPE cExtraBitMask = cExtraBits ? ( ( 1 << cExtraBits ) - 1 ) : ( ~0 ); DWORD index; for( index = 0; index < cArraySize - 1; index++ ) { m_BitSetArray[index] = ~((BITSET_ARRAY_TYPE)0); } if( index < cArraySize ) { m_BitSetArray[index] = ~((BITSET_ARRAY_TYPE)0) & cExtraBitMask; } } RELEASE_DEBUG_MUTEX_WRITE( m_InstanceNotThreadSafe ); } /*****************************************************************************\ Function: CBitSet::Invert Description: Computes the inverse of this bitset. Note that any "extra" bits (bits that are part of the array but that are less than "size" are not inverted and remain un-set. Input: void Output: void \*****************************************************************************/ template void CBitSetType::Invert( void ) { ACQUIRE_DEBUG_MUTEX_WRITE( m_InstanceNotThreadSafe ); if( StoredInPtr() ) { m_PtrBits = (~m_PtrBits) & GetActivePtrMask(); } else { ASSERT( m_BitSetArray ); const DWORD cArraySize = ( m_Size + cBitsPerArrayElement - 1 ) / cBitsPerArrayElement; const BITSET_ARRAY_TYPE cExtraBits = m_Size % cBitsPerArrayElement; const BITSET_ARRAY_TYPE cExtraBitMask = cExtraBits ? ( ( 1 << cExtraBits ) - 1 ) : ( ~0 ); DWORD index; for( index = 0; index < cArraySize - 1; index++ ) { m_BitSetArray[index] = ~m_BitSetArray[index]; } if( index < cArraySize ) { m_BitSetArray[index] = ~m_BitSetArray[index] & cExtraBitMask; } } RELEASE_DEBUG_MUTEX_WRITE( m_InstanceNotThreadSafe ); } /*****************************************************************************\ Function: CBitSet::IsEmpty Description: Determines if any bits are on in the bit set. Input: none Output: bool \*****************************************************************************/ template bool CBitSetType::IsEmpty( void ) const { ACQUIRE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); bool isEmpty = true; if( StoredInPtr() ) { isEmpty = ( m_PtrBits == static_cast( 0 ) ); } else { DWORD index = ( m_Size + cBitsPerArrayElement - 1 ) / cBitsPerArrayElement; while( isEmpty && index-- ) { isEmpty = (m_BitSetArray[ index ] == 0 ); } } RELEASE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); return isEmpty; } /*****************************************************************************\ Function: CBitSet::IsEmpty Description: Determines if any bits are set in the bit set in the specified range. Input: DWORD start - Start of the range to check, inclusive. DWORD length - Length of the range to check. Output: bool \*****************************************************************************/ template bool CBitSetType::IsEmpty( DWORD start, DWORD length ) const { ACQUIRE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); bool isEmpty = true; ASSERT( start < m_Size ); ASSERT( length != 0 ); ASSERT( start + length <= m_Size ); if( StoredInPtr() ) { const DWORD end = start + length; // Create a bit mask for this range: bitptr_t mask = ~( CPtrSize::Bit( start ) - 1 ); if( end < cBitsInPtr ) { mask &= CPtrSize::Bit( end ) - 1; } isEmpty = ( ( mask & m_PtrBits ) == static_cast( 0 ) ); } else { const DWORD end = start + length; const DWORD startArrayIndex = start / cBitsPerArrayElement; const DWORD endArrayIndex = ( end - 1 ) / cBitsPerArrayElement; const DWORD startBit = start % cBitsPerArrayElement; const DWORD endBit = end % cBitsPerArrayElement; const BITSET_ARRAY_TYPE startMask = ~( ( 1 << startBit ) - 1 ); const BITSET_ARRAY_TYPE endMask = endBit ? ( ( 1 << endBit ) - 1 ) : ~0; DWORD arrayIndex = startArrayIndex; BITSET_ARRAY_TYPE data = m_BitSetArray[ arrayIndex ]; data &= startMask; if( startArrayIndex == endArrayIndex ) { data &= endMask; isEmpty = ( data == 0 ); } else { isEmpty = ( data == 0 ); if( isEmpty ) { for( arrayIndex = arrayIndex + 1; arrayIndex < endArrayIndex && isEmpty; arrayIndex++ ) { data = m_BitSetArray[ arrayIndex ]; isEmpty = ( data == 0 ); } } if( isEmpty ) { data = m_BitSetArray[ endArrayIndex ]; data &= endMask; isEmpty = ( data == 0 ); } } } RELEASE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); return isEmpty; } /*****************************************************************************\ Function: CBitSet::IsSet Description: Returns true if the bit at the specified index is set, false otherwise. Input: DWORD index - index of the bit to check Output: bool \*****************************************************************************/ template bool CBitSetType::IsSet( DWORD index ) const { ACQUIRE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); bool isSet = false; if( index < m_Size ) { if( StoredInPtr() ) { isSet = ( ( m_PtrBits & CPtrSize::Bit( index ) ) != static_cast( 0 ) ); } else { DWORD arrayIndex = index / cBitsPerArrayElement; DWORD bitIndex = index % cBitsPerArrayElement; isSet = ( ( m_BitSetArray[arrayIndex] & BIT(bitIndex) ) != 0 ); } } RELEASE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); return isSet; } /*****************************************************************************\ Function: CBitSet::Intersects Description: Returns true if any bits are on in both this bit set and the passed-in bit set. This is a shortcut for ( BitSet1 & BitSet2 ).IsEmpty(). Input: CBitSetType other - Other bit set Output: bool \*****************************************************************************/ template bool CBitSetType::Intersects( const CBitSetType& other ) const { ACQUIRE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); bool intersects = false; if( StoredInPtr() && other.StoredInPtr() ) { intersects = ( ( m_PtrBits & other.m_PtrBits ) != static_cast( 0 ) ); } else { // Size of bitptr_t must be multiplicity of size of BITSET_ARRAY_TYPE. C_ASSERT( sizeof( bitptr_t ) % sizeof( BITSET_ARRAY_TYPE ) == 0 ); // If stored in pointer, get pointer to this pointer and use // common implementation: const BITSET_ARRAY_TYPE* ptrThis = GetArrayPointer(); const BITSET_ARRAY_TYPE* ptrOther = other.GetArrayPointer(); DWORD minSize = ( m_Size < other.m_Size ) ? m_Size : other.m_Size; DWORD index = ( minSize + cBitsPerArrayElement - 1 ) / cBitsPerArrayElement; while( !intersects && index-- ) { if( ( ptrThis[ index ] & ptrOther[ index ] ) != 0 ) { intersects = true; } } } RELEASE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); return intersects; } /*****************************************************************************\ Function: CBitSet::GetNextMember Description: Gets the next member in the set, starting from the specified index. If there are no additional members in the set, returns the size of the set. Example usage pattern: for( DWORD value = bitSet.GetNextMember( 0 ); value < bitSet.GetSize(); value = bitSet.GetNextMember( ++value ) ) Input: DWORD start - Index to start the search Output: DWORD - The next member of the set, or the size of the set if there are no more members in the set. \*****************************************************************************/ template DWORD CBitSetType::GetNextMember( DWORD start ) const { ACQUIRE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); DWORD nextMember = m_Size; if( start < m_Size ) { if( StoredInPtr() ) { bitptr_t ptrBits = m_PtrBits; // Mask out bits to start: ptrBits &= ~( CPtrSize::Bit( start ) - 1 ); // Find first bit: if( ptrBits != static_cast( 0 ) ) { nextMember = CPtrSize::Bsf( ptrBits ); } } else { const DWORD startArrayIndex = start / cBitsPerArrayElement; const DWORD endArrayIndex = ( m_Size - 1 ) / cBitsPerArrayElement; const DWORD startBit = start % cBitsPerArrayElement; const DWORD endBit = m_Size % cBitsPerArrayElement; const BITSET_ARRAY_TYPE startMask = ~( ( 1 << startBit ) - 1 ); const BITSET_ARRAY_TYPE endMask = endBit ? ( ( 1 << endBit ) - 1 ) : ~0; DWORD arrayIndex = startArrayIndex; BITSET_ARRAY_TYPE data = m_BitSetArray[ arrayIndex ]; data &= startMask; if( arrayIndex == endArrayIndex ) { data &= endMask; } else { if( data == 0 ) { for( arrayIndex = arrayIndex + 1; arrayIndex < endArrayIndex; arrayIndex++ ) { data = m_BitSetArray[ arrayIndex ]; if( data != 0 ) { break; } } if( data == 0 ) { data = m_BitSetArray[ endArrayIndex ]; data &= endMask; } } } if( data != 0 ) { // Found, find the first bit that's on in "data". const DWORD lsb = bsf( data ); nextMember = ( arrayIndex * cBitsPerArrayElement ) + lsb; } } } RELEASE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); return nextMember; } /*****************************************************************************\ Function: CBitSet::Set Description: Sets the bit at the given index. Input: DWORD index - index of the bit to set Output: void \*****************************************************************************/ template void CBitSetType::Set( DWORD index ) { ACQUIRE_DEBUG_MUTEX_WRITE( m_InstanceNotThreadSafe ); // If the index is larger than the size of the BitSet then grow the BitSet if( index >= m_Size ) { Create( index + 1 ); } ASSERT( index < m_Size ); if( StoredInPtr() ) { m_PtrBits |= CPtrSize::Bit( index ); } else { DWORD arrayIndex = index / cBitsPerArrayElement; DWORD bitIndex = index % cBitsPerArrayElement; m_BitSetArray[arrayIndex] |= BIT(bitIndex); } RELEASE_DEBUG_MUTEX_WRITE( m_InstanceNotThreadSafe ); } /*****************************************************************************\ Function: CBitSet::Set Description: Sets all of the given bits. Input: CBitSetType other - bits to set. Output: void \*****************************************************************************/ template void CBitSetType::Set( const CBitSetType& other ) { ACQUIRE_DEBUG_MUTEX_WRITE( m_InstanceNotThreadSafe ); DWORD size = other.m_Size; if( m_Size < other.m_Size ) { Create( other.m_Size ); size = m_Size; } ASSERT( m_Size >= other.m_Size ); if( StoredInPtr() ) { ASSERT( other.StoredInPtr() ); m_PtrBits |= other.m_PtrBits; } else { const BITSET_ARRAY_TYPE* pOtherArray = other.GetArrayPointer(); DWORD index = ( size + cBitsPerArrayElement - 1 ) / cBitsPerArrayElement; while( index-- ) { m_BitSetArray[ index ] |= pOtherArray[ index ]; } } RELEASE_DEBUG_MUTEX_WRITE( m_InstanceNotThreadSafe ); } /*****************************************************************************\ Function: CBitSet::UnSet Description: Un-Sets the bit at the given index. Input: DWORD index - index of the bit to un-set Output: void \*****************************************************************************/ template void CBitSetType::UnSet( DWORD index ) { ACQUIRE_DEBUG_MUTEX_WRITE( m_InstanceNotThreadSafe ); // If the index is larger than the size of the BitSet then grow the BitSet if( index >= m_Size ) { Create( index + 1 ); if( index >= m_Size ) { // In case allocation failed... ASSERT( index < m_Size ); return; } } if( StoredInPtr() ) { m_PtrBits &= ( ~CPtrSize::Bit( index ) ) & GetActivePtrMask(); } else { DWORD arrayIndex = index / cBitsPerArrayElement; DWORD bitIndex = index % cBitsPerArrayElement; m_BitSetArray[arrayIndex] &= ~BIT(bitIndex); } RELEASE_DEBUG_MUTEX_WRITE( m_InstanceNotThreadSafe ); } /*****************************************************************************\ Function: CBitSet::UnSet Description: Un-Sets all of the given bits. Input: CBitSetType other - bits to un-set. Output: void \*****************************************************************************/ template void CBitSetType::UnSet( const CBitSetType& other ) { ACQUIRE_DEBUG_MUTEX_WRITE( m_InstanceNotThreadSafe ); DWORD size = other.m_Size; if( m_Size < other.m_Size ) { Create( other.m_Size ); size = m_Size; } ASSERT( m_Size >= other.m_Size ); if( StoredInPtr() ) { ASSERT( other.StoredInPtr() ); m_PtrBits &= ~other.m_PtrBits; m_PtrBits &= GetActivePtrMask(); } else { const BITSET_ARRAY_TYPE* pOtherArray = other.GetArrayPointer(); DWORD index = ( size + cBitsPerArrayElement - 1 ) / cBitsPerArrayElement; while( index-- ) { m_BitSetArray[ index ] &= ~pOtherArray[ index ]; } } RELEASE_DEBUG_MUTEX_WRITE( m_InstanceNotThreadSafe ); } /*****************************************************************************\ Function: CBitSet::GetSize Description: Returns the number of bits in the BitSet Input: void Output: DWORD length \*****************************************************************************/ template DWORD CBitSetType::GetSize( void ) const { ACQUIRE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); const DWORD size = m_Size; RELEASE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); return size; } /*****************************************************************************\ Function: CBitSet::Min Description: Returns the minimum bit set Input: void Output: long \*****************************************************************************/ template long CBitSetType::Min( void ) const { ACQUIRE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); long minBit = -1; if( StoredInPtr() ) { if( m_PtrBits ) { minBit = CPtrSize::Bsf( m_PtrBits ); } } else { const DWORD count = ( m_Size + cBitsPerArrayElement - 1 ) / cBitsPerArrayElement; for( DWORD i = 0; i < count; ++i ) { if( m_BitSetArray[i] != 0 ) { const DWORD lsb = bsf( m_BitSetArray[i] ); minBit = (i * cBitsPerArrayElement) + lsb; break; } } } RELEASE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); return minBit; } /*****************************************************************************\ Function: CBitSet::Max Description: Returns the maximum bit set Input: void Output: long \*****************************************************************************/ template long CBitSetType::Max( void ) const { ACQUIRE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); long maxBit = -1; if( StoredInPtr() ) { if( m_PtrBits ) { maxBit = CPtrSize::Bsr( m_PtrBits ); } } else { const DWORD count = ( m_Size + cBitsPerArrayElement - 1 ) / cBitsPerArrayElement; for( long i = count-1; i >= 0; --i ) { if( m_BitSetArray[i] != 0 ) { const DWORD msb = bsr( m_BitSetArray[i] ); maxBit = (i * cBitsPerArrayElement) + msb; break; } } } RELEASE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); return maxBit; } /*****************************************************************************\ Function: template CBitSet::ConvertTo Description: Returns a T representation of the current bit set. Only valid if the bit set consists of less than (or equal to) sizeof(T) * BITS_PER_BYTE bits. It is expected that the types that bitsets get converted to will be BYTES, WORDS, or DWORDS, although other types may work as well. Input: void Output: T representation of the bitfield. \*****************************************************************************/ template template T CBitSetType::ConvertTo() const { ACQUIRE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); T mask = 0; if( StoredInPtr() ) { bitptr_t bits = m_PtrBits; mask = (T)( bits ); } else { ASSERT( m_BitSetArray ); mask = ((T*)m_BitSetArray)[0]; } RELEASE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); return mask; } /*****************************************************************************\ Function: CBitSet::operator == Description: Tests this bitset and another bitset for equality. Input: CBitSetType& other - other BitSet Output: bool \*****************************************************************************/ template bool CBitSetType::operator==( const CBitSetType& other ) const { ACQUIRE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); bool isEqual = false; if( m_Size == other.m_Size ) { ASSERT( this->StoredInPtr() == other.StoredInPtr() ); if( StoredInPtr() ) { isEqual = ( m_PtrBits == other.m_PtrBits ); } else { const DWORD cArraySizeInBytes = ( m_Size + BITS_PER_BYTE - 1 ) / BITS_PER_BYTE; isEqual = ( 0 == SafeMemCompare( m_BitSetArray, other.m_BitSetArray, cArraySizeInBytes ) ); } } RELEASE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); return isEqual; } /*****************************************************************************\ Function: CBitSet::operator != Description: Tests this bitset and another bitset for inequality. Input: CBitSetType& other - other BitSet Output: bool \*****************************************************************************/ template bool CBitSetType::operator!=( const CBitSetType& other ) const { ACQUIRE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); bool isNotEqual = true; if( m_Size == other.m_Size ) { ASSERT( this->StoredInPtr() == other.StoredInPtr() ); if( StoredInPtr() ) { isNotEqual = ( m_PtrBits != other.m_PtrBits ); } else { const DWORD cArraySizeInBytes = ( m_Size + BITS_PER_BYTE - 1 ) / BITS_PER_BYTE; isNotEqual = ( 0 != SafeMemCompare( m_BitSetArray, other.m_BitSetArray, cArraySizeInBytes ) ); } } RELEASE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); return isNotEqual; } /*****************************************************************************\ Function: CBitSet::operator = Description: Equal operator to copy a BitSet Input: CBitSetType& other - BitSet to copy Output: *this \*****************************************************************************/ template CBitSetType& CBitSetType::operator= ( const CBitSetType &other ) { ACQUIRE_DEBUG_MUTEX_WRITE( m_InstanceNotThreadSafe ); Copy( other ); RELEASE_DEBUG_MUTEX_WRITE( m_InstanceNotThreadSafe ); return *this; } /*****************************************************************************\ Function: CBitSet::operator |= Description: Computes the union of this bitset with another bitset. Input: CBitSetType& other - other BitSet Output: *this \*****************************************************************************/ template CBitSetType& CBitSetType::operator|= ( const CBitSetType &other ) { ACQUIRE_DEBUG_MUTEX_WRITE( m_InstanceNotThreadSafe ); DWORD size = other.m_Size; if( m_Size < other.m_Size ) { Create( other.m_Size ); size = m_Size; } ASSERT( m_Size >= other.m_Size ); if( StoredInPtr() ) { ASSERT( other.StoredInPtr() ); m_PtrBits |= other.m_PtrBits; } else { const BITSET_ARRAY_TYPE* pOtherArray = other.GetArrayPointer(); DWORD index = ( size + cBitsPerArrayElement - 1 ) / cBitsPerArrayElement; while( index-- ) { m_BitSetArray[ index ] |= pOtherArray[ index ]; } } RELEASE_DEBUG_MUTEX_WRITE( m_InstanceNotThreadSafe ); return *this; } /*****************************************************************************\ Function: CBitSet::operator &= Description: Computes the intersection of this bitset with another bitset. Input: CBitSetType& other - other BitSet Output: *this \*****************************************************************************/ template CBitSetType& CBitSetType::operator&= ( const CBitSetType &other ) { ACQUIRE_DEBUG_MUTEX_WRITE( m_InstanceNotThreadSafe ); DWORD size = other.m_Size; if( m_Size < other.m_Size ) { Create( other.m_Size ); size = m_Size; } ASSERT( m_Size >= other.m_Size ); if( StoredInPtr() ) { ASSERT( other.StoredInPtr() ); m_PtrBits &= other.m_PtrBits; } else { const BITSET_ARRAY_TYPE* pOtherArray = other.GetArrayPointer(); DWORD index = ( size + cBitsPerArrayElement - 1 ) / cBitsPerArrayElement; while( index-- ) { m_BitSetArray[ index ] &= pOtherArray[ index ]; } } RELEASE_DEBUG_MUTEX_WRITE( m_InstanceNotThreadSafe ); return *this; } /*****************************************************************************\ Function: CBitSet::Create Description: Creates the internal BitSet structure of the specified size Input: DWORD size - number of elements Output: void \*****************************************************************************/ template void CBitSetType::Create( DWORD size ) { if( size == m_Size ) { // Nothing to do... } if( size <= cBitsInPtr ) { // Bitset will fit in pointer. bitptr_t newPtrBits = this->ConvertTo(); Delete(); m_Size = size; m_PtrBits = newPtrBits & GetActivePtrMask(); } else { BITSET_ARRAY_TYPE* ptrThis = GetArrayPointer(); const DWORD cNewArraySize = ( size + cBitsPerArrayElement - 1 ) / cBitsPerArrayElement; const DWORD cOldArraySize = ( m_Size + cBitsPerArrayElement - 1 ) / cBitsPerArrayElement; const BITSET_ARRAY_TYPE cExtraBits = size % cBitsPerArrayElement; const BITSET_ARRAY_TYPE cExtraBitMask = cExtraBits ? ( ( 1 << cExtraBits ) - 1 ) : ( ~0 ); if( cNewArraySize == cOldArraySize ) { m_Size = size; if( cNewArraySize ) { ptrThis[ cNewArraySize - 1 ] &= cExtraBitMask; } } else if( cNewArraySize ) { BITSET_ARRAY_TYPE* ptr = (BITSET_ARRAY_TYPE*) CAllocatorType::Allocate( sizeof(BITSET_ARRAY_TYPE) * cNewArraySize ); if( ptr ) { if( ptrThis ) { if( cNewArraySize > cOldArraySize ) { MemCopy( ptr, ptrThis, cOldArraySize * sizeof(ptrThis[0]) ); SafeMemSet( ptr + cOldArraySize, 0, ( cNewArraySize - cOldArraySize) * sizeof(ptrThis[0]) ); } else { MemCopy( ptr, ptrThis, cNewArraySize * sizeof(ptrThis[0]) ); if( cNewArraySize ) { ptr[ cNewArraySize - 1 ] &= cExtraBitMask; } } } else { SafeMemSet( ptr, 0, cNewArraySize * sizeof(ptrThis[0]) ); } Delete(); m_BitSetArray = ptr; m_Size = size; } else { ASSERT( 0 ); } } else { Delete(); } } } /*****************************************************************************\ Function: CBitSet::Copy Description: Copies information from one bitset to this bitset. Input: const CBitSetType& other - bitset to copy. Output: void \*****************************************************************************/ template void CBitSetType::Copy( const CBitSetType& other ) { if( this != &other ) { if( m_Size != other.m_Size ) { Create( other.m_Size ); } if( m_Size == other.m_Size ) { ASSERT( this->StoredInPtr() == other.StoredInPtr() ); if( StoredInPtr() ) { m_PtrBits = other.m_PtrBits; } else { const DWORD cArraySizeInBytes = ( other.m_Size + BITS_PER_BYTE - 1 ) / BITS_PER_BYTE; MemCopy( m_BitSetArray, other.m_BitSetArray, cArraySizeInBytes ); } } else { // Should not happen - indicates failed memory allocation. ASSERT( 0 ); } } } /*****************************************************************************\ Function: CBitSet::Delete Description: Deletes the internal BitSet structure Input: void Output: void \*****************************************************************************/ template void CBitSetType::Delete( void ) { ASSERT( m_BitSetArray || StoredInPtr() ); if( !StoredInPtr() && m_BitSetArray ) { CAllocatorType::Deallocate( m_BitSetArray ); } m_BitSetArray = NULL; m_Size = 0; } /*****************************************************************************\ Function: CBitSet::BitCount Description: Counts number of bits set in BitSet Input: void Output: DWORD number of bits set in BitSet \*****************************************************************************/ template DWORD CBitSetType::BitCount( void ) const { ACQUIRE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); DWORD bitCount = 0; if( StoredInPtr() ) { bitCount = CPtrSize::Count( m_PtrBits ); } else { const DWORD cBitsPerArrayElement = ( sizeof(m_BitSetArray[0]) * 8 ); DWORD index = ( m_Size + cBitsPerArrayElement - 1 ) / cBitsPerArrayElement; while( index-- ) { BITSET_ARRAY_TYPE Elem = m_BitSetArray[index]; bitCount += iSTD::BitCount( Elem ); } } RELEASE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); return bitCount; } /*****************************************************************************\ Function: CBitSet::BitCount Description: Counts number of bits set in BitSet up to (and including) limit-th index Input: DWORD index Output: DWORD number of bits set in BitSet up to (and including) limit-th index \*****************************************************************************/ template DWORD CBitSetType::BitCount( DWORD limit ) const { ACQUIRE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); DWORD bitCount = 0; limit = iSTD::Min( limit, m_Size-1 ); for( DWORD i=0; i <= limit; i++ ) { if( IsSet( i ) ) { bitCount++; } } RELEASE_DEBUG_MUTEX_READ( m_InstanceNotThreadSafe ); return bitCount; } /*****************************************************************************\ Function: CBitSet::StoredInPtr Description: True if bits are stored in pointer. Input: Output: bool \*****************************************************************************/ template bool CBitSetType::StoredInPtr( void ) const { return m_Size <= cBitsInPtr; } /*****************************************************************************\ Function: CBitSet::GetActivePtrMask Description: Returns pointer mask depending on set size. Can only be called if bits are stored in pointer. Input: Output: bitptr_t - active mask \*****************************************************************************/ template typename CBitSetType::bitptr_t CBitSetType::GetActivePtrMask( void ) const { ASSERT( StoredInPtr() ); if( m_Size == cBitsInPtr ) { return static_cast( -1 ); } return CPtrSize::Bit( m_Size ) - 1; } /*****************************************************************************\ Function: CBitSet::GetArrayPointer Description: Return pointer to bitset array. If bits are stored in pointer itself, return pointer to this pointer. Input: Output: BITSET_ARRAY_TYPE* \*****************************************************************************/ template typename CBitSetType::BITSET_ARRAY_TYPE* CBitSetType::GetArrayPointer( void ) { if( StoredInPtr() ) { return ( reinterpret_cast( &m_PtrBits ) ); } else { return m_BitSetArray; } } /*****************************************************************************\ Function: CBitSet::GetArrayPointer Description: Return pointer to bitset array. If bits are stored in pointer itself, return pointer to this pointer. Input: Output: const BITSET_ARRAY_TYPE* \*****************************************************************************/ template const typename CBitSetType::BITSET_ARRAY_TYPE* CBitSetType::GetArrayPointer( void ) const { if( StoredInPtr() ) { return ( reinterpret_cast( &m_PtrBits ) ); } else { return m_BitSetArray; } } } // iSTD