|
|
//-----------------------------------------------------------------------------
//
//
// File: bitmap.cpp
//
// Description: Contains code for implementation of bitmap functions
//
// Owner: mikeswa
//
// Copyright (C) 1997 Microsoft Corporation
//
//-----------------------------------------------------------------------------
#include "aqprecmp.h"
#include "bitmap.h"
#define BITS_PER_DWORD 32
//Set up static masks for quick parsing
const DWORD s_rgdwMasks[8] = { 0xF0000000, 0x0F000000, 0x00F00000, 0x000F0000, 0x0000F000, 0x00000F00, 0x000000F0, 0x0000000F };
//Used for fast conversion from index to bitmap
const DWORD s_rgdwIndexMasks[32] = { 0x80000000, 0x40000000, 0x20000000, 0x10000000, 0x08000000, 0x04000000, 0x02000000, 0x01000000, 0x00800000, 0x00400000, 0x00200000, 0x00100000, 0x00080000, 0x00040000, 0x00020000, 0x00010000, 0x00008000, 0x00004000, 0x00002000, 0x00001000, 0x00000800, 0x00000400, 0x00000200, 0x00000100, 0x00000080, 0x00000040, 0x00000020, 0x00000010, 0x00000008, 0x00000004, 0x00000002, 0x00000001 };
//Used to check for zero'd bitmaps with cBits does not fill up a DWORD
const DWORD s_rgdwZeroMasks[32] = { 0x80000000, 0xC0000000, 0xE0000000, 0xF0000000, 0xF8000000, 0xFC000000, 0xFE000000, 0xFF000000, 0xFF800000, 0xFFC00000, 0xFFE00000, 0xFFF00000, 0xFFF80000, 0xFFFC0000, 0xFFFE0000, 0xFFFF0000, 0xFFFF8000, 0xFFFFC000, 0xFFFFE000, 0xFFFFF000, 0xFFFFF800, 0xFFFFFC00, 0xFFFFFE00, 0xFFFFFF00, 0xFFFFFF80, 0xFFFFFFC0, 0xFFFFFFE0, 0xFFFFFFF0, 0xFFFFFFF8, 0xFFFFFFFC, 0xFFFFFFFE, 0xFFFFFFFF, };
//---[ fInterlockedDWORDCompareExchange ]--------------------------------------
//
//
// Description:
// Provide an inline function to handle the type-checking, casts,
// and comparison in DWORD chunks.
// Parameters:
// pdwDest Destination to update
// dwNewValue Value to update with
// dwCompare Old value to check against
// Returns:
// TRUE if update succeeded
//
//-----------------------------------------------------------------------------
inline BOOL fInterlockedDWORDCompareExchange(LPDWORD pdwDest, DWORD dwNewValue, DWORD dwCompare) { return( ((DWORD) InterlockedCompareExchange((PLONG)pdwDest, (LONG) dwNewValue, (LONG) dwCompare)) == dwCompare); }
//---[ CMsgBitMap::new ]----------------------------------------------------------
//
//
// Description:
// Overide the new operator to allow for the variable size of this class.
// A good optimization would be to use the C-pool type stuff for the
// 90% case of 1 domain, and allocate the rest on the fly
// Parameters:
// cBits the number of bits this message is being delivered to.
// Returns:
// -
//-----------------------------------------------------------------------------
void * CMsgBitMap::operator new(size_t stIgnored, unsigned int cBits) { void *pvThis = NULL; int i = 0;
_ASSERT(size(cBits) >= sizeof(DWORD)); pvThis = pvMalloc(size(cBits));
return (pvThis); }
//---[ CMsgBitMap::CMsgBitMap ]------------------------------------------------
//
//
// Description:
// Class constructor. Will zero memory for a bitmap that is not part of
// a message reference
// Parameters:
// cBits - The number of bits in the bitmap
// Returns:
//
//
//-----------------------------------------------------------------------------
CMsgBitMap::CMsgBitMap(DWORD cBits) { DWORD cDWORDs = cGetNumDWORDS(cBits); ZeroMemory(m_rgdwBitMap, cDWORDs*sizeof(DWORD)); }
//---[ CMsgBitMap::FAllClear ]-------------------------------------------------
//
//
// Description:
// Checks to see of all relevant bits (1st cBits) are 0
// Parameters:
// cBits the number of bits in the bitmap
// Returns:
// TRUE if all bits are 0, FALSE otherwise
//
//-----------------------------------------------------------------------------
BOOL CMsgBitMap::FAllClear(DWORD cBits) { TraceFunctEnterEx((LPARAM) this, "CMsgBitMap::FAllClear"); BOOL fResult = TRUE; DWORD cDWORDs = cGetNumDWORDS(cBits) ;
//verify all DWORD's by checking if 0
for (DWORD i = 0; i < cDWORDs; i++) { if (m_rgdwBitMap[i] != 0x00000000) { fResult = FALSE; break; } }
TraceFunctLeave(); return fResult; }
//---[ CMsgBitMap::FAllSet ]---------------------------------------------------
//
//
// Description:
// Checks to see of all relevant bits (1st cBits) are 1
// Parameters:
// cBits the number of bits in the bitmap
// Returns:
// TRUE if all bits are 1, FALSE otherwise
//
//-----------------------------------------------------------------------------
BOOL CMsgBitMap::FAllSet(DWORD cBits) { TraceFunctEnterEx((LPARAM) this, "CMsgBitMap::FAllClear"); BOOL fResult = TRUE; DWORD cDWORDs = cGetNumDWORDS(cBits+1) -1; //check all but last DWORD
DWORD iZeroIndex = cBits & 0x0000001F;
//verify all DWORD's by checking if 0
for (DWORD i = 0; i < cDWORDs; i++) { if (m_rgdwBitMap[i] != 0xFFFFFFFF) { fResult = FALSE; goto Exit; //if we hit the iZeroIndex clause, we might assert
} }
_ASSERT(i || iZeroIndex || !fResult); //We must check at least 1 DWORD
if (iZeroIndex) { iZeroIndex--; //we cBits is a count... our index starts at 0.
//last DWORD should be a subset of the ZeroMask
_ASSERT(s_rgdwZeroMasks[iZeroIndex] == (s_rgdwZeroMasks[iZeroIndex] | m_rgdwBitMap[cDWORDs]));
if (s_rgdwZeroMasks[iZeroIndex] != m_rgdwBitMap[cDWORDs]) fResult = FALSE; }
Exit: TraceFunctLeave(); return fResult; }
//---[ CMsgBitMap::HrMarkBits ]------------------------------------------------
//
//
// Description:
// Marks the bits (as 0 or 1) that corresponds to the given indexes
//
// Parameters:
// IN DWORD cBits
// IN DWORD cIndexes # of indexes in array
// IN DWORD rgiBits SORTED array of indexes of bits to mark
// IN BOOL fSet TRUE => set to 1, 0 otherwise
// Returns:
// S_OK on success
//-----------------------------------------------------------------------------
HRESULT CMsgBitMap::HrMarkBits(IN DWORD cBits, IN DWORD cIndexes, IN DWORD *rgiBits, IN BOOL fSet) { TraceFunctEnterEx((LPARAM) this, "CMsgBitMap::HrMarkBits"); HRESULT hr = S_OK; DWORD cDWORDs = cGetNumDWORDS(cBits); DWORD dwTmp; DWORD dwIndex = 0x00000000; DWORD i; DWORD iCurrentIndex = 0; //current index in rgiBits
DWORD iCurrentLimit = BITS_PER_DWORD -1; //current limit of 32 bit range for values of rgiBits
_ASSERT(cIndexes); _ASSERT(cIndexes <= cBits);
for (i = 0; i < cDWORDs; i++) { dwIndex = 0x00000000; while ((iCurrentIndex < cIndexes) && (rgiBits[iCurrentIndex] <= iCurrentLimit)) { _ASSERT(rgiBits[iCurrentIndex] < cBits); dwIndex |= s_rgdwIndexMasks[(rgiBits[iCurrentIndex] % BITS_PER_DWORD)]; iCurrentIndex++; }
if (dwIndex != 0x00000000) //don't perform costly interlocked op if we don't need to
{ if (fSet) //set bit
{ SpinTry1: dwTmp = m_rgdwBitMap[i]; if (!fInterlockedDWORDCompareExchange(&(m_rgdwBitMap[i]), (dwIndex | dwTmp), dwTmp)) goto SpinTry1; } else //clear bit
{ SpinTry2: dwTmp = m_rgdwBitMap[i]; if (!fInterlockedDWORDCompareExchange(&(m_rgdwBitMap[i]), ((~dwIndex) & dwTmp), dwTmp)) goto SpinTry2; } }
if (iCurrentIndex >= cIndexes) break; //don't do more work than we have to
iCurrentLimit += BITS_PER_DWORD; }
TraceFunctLeave(); return hr; }
//---[ CMsgBitMap::HrGetIndexes ]----------------------------------------------
//
//
// Description:
// Generates an array of indexes represented by the bitmap
// Parameters:
// IN DWORD cBits
// OUT DWORD *pcIndexes //# of indexes returned
// OUT DWORD **prgdwIndexes //array of indexes
// Returns:
// S_OK on success
// E_OUTOFMEMORY if memory allocation fails
//-----------------------------------------------------------------------------
HRESULT CMsgBitMap::HrGetIndexes(IN DWORD cBits, OUT DWORD *pcIndexes, OUT DWORD **prgdwIndexes) { TraceFunctEnterEx((LPARAM) this, "CMsgBitMap::HrGetIndexes"); HRESULT hr = S_OK; DWORD *pdwIndexes = NULL; DWORD dwIndex = 0; DWORD dwIndexOffset = 0; DWORD cDWORDs = cGetNumDWORDS(cBits); DWORD cdwAllocated = 0; DWORD cCurrentIndexes = 0; DWORD i = 0; DWORD *pdwTmp = NULL;
//$$REVIEW: How do we balance CPU usage vs memory usage here? We know the
// max size of the output array is cBits DWORDS, but in actuality it can
// little as 1 DWORD. Prognosticating the actual size accurately would
// require scanning the bitmap multiple times.
//
// Easy(studpid) way: Count bits, allocate array, recount and add indexes to array
//
// Idea #1: Allocate in chunks of 32 DWORDS, Realloc if we run out Should
// not have to worry about reallocing for 90% of the cases.
//
// Idea #2: Add some stats to this class, and run some stress tests in debug
// mode, and develop a heuristic that limits reallocs and such (ie alloc
// lg(cBits) to start with).
//
// Idea #3: Continue with Idea #2, but add self-tuning stats
Assert(pcIndexes); Assert(prgdwIndexes);
pdwIndexes = (DWORD *) pvMalloc(BITS_PER_DWORD*sizeof(DWORD)); if (pdwIndexes == NULL) { hr = E_OUTOFMEMORY; goto Exit; } cdwAllocated = BITS_PER_DWORD;
cCurrentIndexes = 0;
for (i = 0; i < cDWORDs; i++) { dwIndex = 0; while (dwIndex < BITS_PER_DWORD) { //can use mask to check if possible
if ((!(dwIndex & 0x00000003)) && //if %4 == 0
!(s_rgdwMasks[dwIndex/4] & m_rgdwBitMap[i])) { dwIndex += 4; //Can skip ahead 4
} else { if (s_rgdwIndexMasks[dwIndex] & m_rgdwBitMap[i]) //Found it!
{ //Write index and check if re-allocation is needed
if (cCurrentIndexes >= cdwAllocated) { cdwAllocated += BITS_PER_DWORD; pdwTmp = (DWORD *) pvRealloc(pdwIndexes, cdwAllocated*sizeof(DWORD)); if (NULL == pdwTmp) { hr = E_OUTOFMEMORY; goto Exit; } pdwIndexes = pdwTmp; } *(pdwIndexes + cCurrentIndexes) = (dwIndex + dwIndexOffset); cCurrentIndexes++; } dwIndex++; } }
//Use dwIndexOffset to break down index generation into 32-bit chunks
dwIndexOffset += BITS_PER_DWORD; }
*prgdwIndexes = pdwIndexes; //set OUT value
*pcIndexes = cCurrentIndexes;
Exit:
if (FAILED(hr)) { *prgdwIndexes = NULL; *pcIndexes = 0; FreePv(pdwIndexes); }
TraceFunctLeave(); return hr; }
//---[ CMsgBitMap::HrGroupOr ]-------------------------------------------------
//
//
// Description:
// Sets thir bitmap to the logical OR of the given list of bitmaps. This
// is used to prepare a bitmap that represents the domains being delivered
// over a list (or destmsg queue). Current bitmap is NOT cleared prior to
// this operation.
// Parameters:
// IN DWORD cBits number of bits in bitmap
// IN DWORD cBitMaps number of bitmaps in array
// IN CMsgBitMap **rgpBitMaps array of bitmaps to OR
// Returns:
// S_OK on success
//
// Note: This is NOT thread safe.. it's intended use is only for tmp bitmaps
//-----------------------------------------------------------------------------
HRESULT CMsgBitMap::HrGroupOr(IN DWORD cBits, IN DWORD cBitMaps, IN CMsgBitMap **rgpBitMaps) { TraceFunctEnterEx((LPARAM) this, "CMsgBitMap::HrGroupOr"); HRESULT hr = S_OK; DWORD cDWORDs = cGetNumDWORDS(cBits); DWORD i, j;
for (i = 0; i < cDWORDs; i++) { for (j = 0; j < cBitMaps; j++) { Assert(rgpBitMaps[j]); m_rgdwBitMap[i] |= rgpBitMaps[j]->m_rgdwBitMap[i]; } }
TraceFunctLeave(); return hr; }
//---[ CMsgBitMap::HrFilter ]--------------------------------------------------
//
//
// Description:
// Filters the current bitmap by setting only the bits that are SET in
// in and UNSET in the given bitmap..
// Performs a logical AND with the complement of the given bitmap
// Parameters:
// IN DWORD cBits # of bits in bitmap
// IN CMsgBitMap *pmbmap bitmap to filter against
// Returns:
// S_OK on success
//
// Truth Table:
// A => this bitmap
// B => pmbmap
//
// A B | A'B'
// ===========
// 0 0 | 0 0
// 0 1 | 0 1
// 1 0 | 1 0
// 1 1 | 0 1
//
// Note: This is NOT thread safe.. it's intended use is only for tmp bitmaps
//-----------------------------------------------------------------------------
HRESULT CMsgBitMap::HrFilter(IN DWORD cBits, IN CMsgBitMap *pmbmap) { TraceFunctEnterEx((LPARAM) this, "CMsgBitMap::HrFilter"); HRESULT hr = S_OK; DWORD cDWORDs = cGetNumDWORDS(cBits);
Assert(pmbmap);
for (DWORD i = 0; i < cDWORDs; i++) { m_rgdwBitMap[i] &= ~(pmbmap->m_rgdwBitMap[i]); }
TraceFunctLeave(); return hr; }
//---[ CMsgBitMap::HrFilterSet ]-----------------------------------------------
//
//
// Description:
// Filters the current bitmap and sets those bits to 1 in the given
// bitmap. Unlike HrFilter, this modifies the given bitmap and does so
// in a thread-safe manner.
// Parameters:
// IN DWORD cBits # of bits in bitmap
// IN CMsgBitMap *pmbmap bitmap to filter against
// Returns:
// S_OK on success
//
// Truth Table:
// A => this bitmap
// B => pmbmap
//
// A B | A'B'
// ===========
// 0 0 | 0 0
// 0 1 | 0 1
// 1 0 | 1 1
// 1 1 | 0 1
//
//-----------------------------------------------------------------------------
HRESULT CMsgBitMap::HrFilterSet(IN DWORD cBits, IN CMsgBitMap *pmbmap) { TraceFunctEnterEx((LPARAM) this, "CMsgBitMap::HrFilterSet"); Assert(pmbmap);
HRESULT hr = S_OK; DWORD cDWORDs = cGetNumDWORDS(cBits); DWORD dwSelfNew; DWORD dwOtherNew; DWORD dwOtherOld; DWORD i; BOOL fDone = FALSE;
for (i = 0; i < cDWORDs; i++) { fDone = FALSE; dwSelfNew = m_rgdwBitMap[i]; while (!fDone) { dwOtherNew = pmbmap->m_rgdwBitMap[i]; dwOtherOld = dwOtherNew;
dwSelfNew &= ~dwOtherNew; //filter
dwOtherNew ^= dwSelfNew; //set
if (fInterlockedDWORDCompareExchange(&(pmbmap->m_rgdwBitMap[i]), dwOtherNew, dwOtherOld)) { fDone = TRUE; m_rgdwBitMap[i] = dwSelfNew; } } }
TraceFunctLeave(); return hr; }
//---[ CMsgBitMap::HrFilterUnset ]-----------------------------------------------
//
//
// Description:
// Uses the current bitmap and sets those bits that are 1 on it to 0 in the
// given bitmap. Unlike HrFilterSet, only the pmbmap is modified.
//
// This also checks that all bits that are 1 in self are also 1 in the
// other... ie that the 1 bits in this are a subset of pmbmap
// Parameters:
// IN DWORD cBits # of bits in bitmap
// IN CMsgBitMap *pmbmap bitmap to filter against
// Returns:
// S_OK on success
//
// Truth Table:
// A => this bitmap
// B => pmbmap
//
// A B | A'B'
// ===========
// 0 0 | 0 0
// 0 1 | 0 1
// 1 0 | x x - undefined (will assert)
// 1 1 | 1 0
//
//-----------------------------------------------------------------------------
HRESULT CMsgBitMap::HrFilterUnset(IN DWORD cBits, IN CMsgBitMap *pmbmap) { TraceFunctEnterEx((LPARAM) this, "CMsgBitMap::HrFilterUnset"); Assert(pmbmap);
HRESULT hr = S_OK; DWORD cDWORDs = cGetNumDWORDS(cBits); BOOL fDone = FALSE; DWORD i; DWORD dwOtherNew; DWORD dwOtherOld;
for (i = 0; i < cDWORDs; i++) { fDone = FALSE;
while (!fDone) { dwOtherNew = pmbmap->m_rgdwBitMap[i]; dwOtherOld = dwOtherNew;
if (m_rgdwBitMap[i] & ~dwOtherNew) { //this bitmap is NOT a subset of the given bitmap
_ASSERT(0); //caller's mistake
hr = E_FAIL; goto Exit; }
dwOtherNew ^= m_rgdwBitMap[i]; //unset
if (fInterlockedDWORDCompareExchange(&(pmbmap->m_rgdwBitMap[i]), dwOtherNew, dwOtherOld)) { fDone = TRUE; } } }
Exit: TraceFunctLeave(); return hr; }
//---[ CMsgBitMap::FTestAndSet ]-----------------------------------------------
//
//
// Description:
// An Interlocked function to test and set a bit on the this bit map.
// Looks for the bit that is set in the given bitmap, if that bit is also
// 1 in this bitmap, returns FALSE. If that bit is 0, it sets it to 1,
// and returns TRUE.
//
// NOTE: Results are UNDEFINED if there is more than 1 bit set in pmbmap.
// Parameters:
// cBits # of bits in bitmap
// pmbmap Bitmap to check against
// Returns:
// TRUE if the corresponding bit was 0 (and is now set to 1)
// FALSE if the corresponding bit was already1
// History:
// 11/8/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
BOOL CMsgBitMap::FTestAndSet(IN DWORD cBits, IN CMsgBitMap *pmbmap) { BOOL fRet = FALSE; DWORD cDWORDs = cGetNumDWORDS(cBits); BOOL fDone = FALSE; DWORD dwThisNew = 0; DWORD dwThisOld = 0; DWORD i = 0;
for (i = 0; i < cDWORDs; i++) {
if (pmbmap->m_rgdwBitMap[i]) { //We've hit the bit in the given bitmap
//See if bit is already set
if (pmbmap->m_rgdwBitMap[i] & m_rgdwBitMap[i]) break;
while (!fDone) { dwThisOld = m_rgdwBitMap[i]; dwThisNew = dwThisOld | pmbmap->m_rgdwBitMap[i];
//See if another thread has set it
if (dwThisOld & pmbmap->m_rgdwBitMap[i]) break;
//Only 1 bit should be set on given bitmap
_ASSERT((dwThisOld | pmbmap->m_rgdwBitMap[i]) == (dwThisOld ^ pmbmap->m_rgdwBitMap[i]));
//Try to set bit
if (fInterlockedDWORDCompareExchange(&(m_rgdwBitMap[i]), dwThisNew, dwThisOld)) { fDone = TRUE; fRet = TRUE; } }
break; } }
return fRet; }
//---[ CMsgBitMap::FTest ]-----------------------------------------------------
//
//
// Description:
// Tests this bitmap against a single bit in the given bitmap
// NOTE: Results are UNDEFINED if there is more than 1 bit set in pmbmap.
// Parameters:
// cBits # of bits in bitmap
// pmbmap Bitmap to check against
// Returns:
// TRUE if the corresponding bit is 1
// FALSE if the corresponding bit is 0
// History:
// 11/8/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
BOOL CMsgBitMap::FTest(IN DWORD cBits, IN CMsgBitMap *pmbmap) { BOOL fRet = FALSE; DWORD cDWORDs = cGetNumDWORDS(cBits); DWORD i = 0;
for (i = 0; i < cDWORDs; i++) { //See if we've hit the bit in the given bitmap
if (pmbmap->m_rgdwBitMap[i]) { //See if bit is already set
if (pmbmap->m_rgdwBitMap[i] & m_rgdwBitMap[i]) fRet = TRUE;
break; } }
return fRet; }
|