Click here to Skip to main content
15,893,190 members
Articles / Desktop Programming / MFC

XBitArray - a non-MFC C++ class to manipulate bits in a bit array.

Rate me:
Please Sign up or sign in to vote.
4.74/5 (26 votes)
10 Feb 2004CPOL5 min read 71.5K   1K   37  
XBitArray provides functions to set, test, and find bits in an array of bytes.
// XBitArray.h  Version 1.2
//
// Author: Hans Dietrich
//         hdietrich2@hotmail.com
//
// Description:
//     XBitArray.h implements CXBitArray(), a class to handle bit operations
//     on an array of BYTEs.
//
// History
//     Version 1.2 - 2004 February 10
//                 - Initial public release
//
// Public APIs:
//             NAME                            DESCRIPTION
//     --------------------  -------------------------------------------------------
//     CXBitArray() #1         Construct uninitialized CXBitArray object
//     CXBitArray() #2         Construct CXBitArray object from existing array
//     CXBitArray() #3         Construct CXBitArray object with array allocation
//     CXBitArray() #4         Construct CXBitArray object from file
//     CXBitArray() #5         Construct CXBitArray object from registry
//     ~CXBitArray()           Save bit array to file and/or registry and deallocate bit array
//     Attach()                Attach an existing bit array to a CXBitArray object
//     Count()                 Return counts of bits set to 0 and bits set to 1
//     Find()                  Find next bit that has the value specified by bit_value
//     Get()                   Get bit value
//     GetArraySizeBits()      Get size of bit array in bits
//     GetArraySizeBytes()     Get size of bit array in bytes
//     GetBitNo()              Convert bit mask to bit number
//     GetBitPos()             Convert bit number to byte and bit indexes and bit mask
//     GetPersistFileName()    Get persist file name
//     GetRegistryKeyName()    Get registry key name
//     GetRegistryValueName()  Get registry value name
//     Init()                  Initialize CXBitArray object
//     operator []             Get bit value at a bit index
//     operator LPBYTE         Get address of bit array
//     ReadPersistFile()       Read bit array from file
//     ReadRegistry()          Read bit array from registry
//     Set()                   Set bit value
//     SetAll()                Set all bits to a value
//     SetPersistFileName()    Set persist file name
//     SetRegistryNames()      Set registry key and value names
//     ToString()              Returns a string that represents the bit array
//     WritePersistFile()      Write bit array to file
//     WriteRegistry()         Write bit array to registry
//
// This software is released into the public domain.  You are free to use it
// in any way you like, except that you may not sell this source code.
//
// This software is provided "as is" with no expressed or implied warranty.
// I accept no liability for any damage or loss of business that this software
// may cause.
//
///////////////////////////////////////////////////////////////////////////////

#ifndef XBITARRAY_H
#define XBITARRAY_H

#pragma warning(push)
#pragma warning(disable : 4127)		// conditional expression is constant
									// (needed for _ASSERTE)
#include <io.h>

#define XBITARRAY_TRACE ((void)0)

///////////////////////////////////////////////////////////////////////////////
//
// If you want TRACE output you can uncomment the following lines:
//
//#undef XBITARRAY_TRACE
//#define XBITARRAY_TRACE TRACE
//


///////////////////////////////////////////////////////////////////////////////
// CXBitArray class
class CXBitArray
{

// Constructors / Destructors
public:

	///////////////////////////////////////////////////////////////////////////////
	//
	// CXBitArray() #1
	//
	// Purpose:     Construct uninitialized CXBitArray object
	//
	// Parameters:  None
	//
	// Returns:     None
	//
	CXBitArray()
	{
		XBITARRAY_TRACE(_T("in CXBitArray 1\n"));
		m_pBitArray = NULL;
		Init();
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// CXBitArray() #2
	//
	// Purpose:     Construct CXBitArray object from existing array
	//
	// Parameters:  pBitArray      - address of bit array
	//              nArraySizeBits - size of array in bits
	//              bit_value      - bit array will be initialized to this value
	//
	// Returns:     None
	//
	CXBitArray(BYTE *pBitArray, size_t nArraySizeBits, BOOL bit_value)
	{
		XBITARRAY_TRACE(_T("in CXBitArray 2\n"));
		m_pBitArray = NULL;
		Init(NULL, NULL, NULL, pBitArray, nArraySizeBits, bit_value);
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// CXBitArray() #3
	//
	// Purpose:     Construct CXBitArray object with array allocation
	//
	// Parameters:  nArraySizeBits - size of array in bits
	//              bit_value      - bit array will be initialized to this value
	//
	// Returns:     None
	//
	CXBitArray(size_t nArraySizeBits, BOOL bit_value)
	{
		XBITARRAY_TRACE(_T("in CXBitArray 3\n"));
		m_pBitArray = NULL;
		Init(NULL, NULL, NULL, NULL, nArraySizeBits, bit_value);
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// CXBitArray() #4
	//
	// Purpose:     Construct CXBitArray object from file
	//
	// Parameters:  lpszPersistFile - name of file to read bit array from
	//              nArraySizeBits  - size of array in bits
	//              bit_value       - bit array will be initialized to this value
	//                                if it cannot be read from file
	//
	// Returns:     None
	//
	// Notes:       If the file specified by lpszPersistFile cannot be read,
	//              the bit array will be allocated according to nArraySizeBits
	//              and initialized to bit_value.  Regardless of whether the
	//              file exists, the bit array will be saved to this file when
	//              the destructor is called.
	//
	//              If the file specified by lpszPersistFile can be read, the
	//              size of the bit array is determined by the size of the file.
	//
	CXBitArray(LPCTSTR lpszPersistFile, size_t nArraySizeBits, BOOL bit_value)
	{
		XBITARRAY_TRACE(_T("in CXBitArray 4\n"));
		m_pBitArray = NULL;
		Init(lpszPersistFile, NULL, NULL, NULL, nArraySizeBits, bit_value);
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// CXBitArray() #5
	//
	// Purpose:     Construct CXBitArray object from registry
	//
	// Parameters:  lpszKeyName    - key name (must not be NULL)
	//              lpszValueName  - value name (may be NULL)
	//              nArraySizeBits - size of array in bits
	//              bit_value      - bit array will be initialized to this value
	//                               if it cannot be read from registry
	//
	// Returns:     None
	//
	// Notes:       If the registry key specified by lpszKeyName cannot be read,
	//              the bit array will be allocated according to nArraySizeBits
	//              and initialized to bit_value.  Regardless of whether the
	//              registry key exists, the bit array will be saved to this file
	//              when the destructor is called.
	//
	//              If the registry key  specified by lpszKeyName can be read, the
	//              size of the bit array is determined by the size of the registry 
	//              value.
	//
	//              If lpszValueName is NULL, the key's unnamed or default value
	//              will be used.
	//
	CXBitArray(LPCTSTR lpszKeyName, 
			   LPCTSTR lpszValueName, 
			   size_t nArraySizeBits, 
			   BOOL bit_value)
	{
		XBITARRAY_TRACE(_T("in CXBitArray 5\n"));
		m_pBitArray = NULL;
		Init(NULL, lpszKeyName, lpszValueName, NULL, nArraySizeBits, bit_value);
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// ~CXBitArray()
	//
	// Purpose:     Save bit array to file and/or registry and deallocate bit array
	//
	// Parameters:  None
	//
	// Returns:     None
	//
	virtual ~CXBitArray()
	{
		XBITARRAY_TRACE(_T("in ~CXBitArray\n"));

		if (m_szPersistFile[0] != _T('\0'))
			WritePersistFile(m_szPersistFile);

		if (m_szKeyName[0] != _T('\0'))
			WriteRegistry(m_szKeyName, m_szValueName);

		if (m_bDeleteArray && m_pBitArray)
			delete [] m_pBitArray;
		m_pBitArray = NULL;

		m_nArraySizeBytes = 0;
		m_nArraySizeBits = 0;
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// Init()
	//
	// Purpose:     Initialize CXBitArray object
	//
	// Parameters:  lpszPersistFile - name of file to read bit array from
	//              lpszKeyName     - key name
	//           :  lpszValueName   - value name - if lpszKeyName is not NULL, and
	//                                lpszValueName is NULL, the key's unnamed or 
	//                                default value will be used
	//              pBitArray       - address of bit array.  If NULL, a new bit 
	//                                array will be allocated.
	//              nArraySizeBits  - size of array in bits; rounded up to next
	//                                multiple of 8
	//              bit_value       - bit array will be initialized to this value
	//
	// Returns:     None
	//
	// Notes:       See Notes for CXBitArray #4 and #5.
	//
	void Init(LPCTSTR lpszPersistFile = NULL, 
			  LPCTSTR lpszKeyName = NULL, 
			  LPCTSTR lpszValueName = NULL,
			  BYTE *pBitArray = NULL, 
			  size_t nArraySizeBits = 0, 
			  BOOL bit_value = FALSE)
	{
		XBITARRAY_TRACE(_T("in Init:  nArraySizeBits=%d\n"),
			nArraySizeBits);

		SetPersistFileName(lpszPersistFile);

		SetRegistryNames(lpszKeyName, lpszValueName);

		// if Init is being called again, delete the previous heap array
		if (m_bDeleteArray && m_pBitArray)
			delete [] m_pBitArray;

		m_bDeleteArray    = FALSE;
		m_pBitArray       = pBitArray;

		m_nArraySizeBits  = nArraySizeBits;
		if (m_nArraySizeBits == 0)
			m_nArraySizeBits = 1;
		m_nArraySizeBytes = (m_nArraySizeBits + 7) / 8;	// round up
		m_nArraySizeBits  = m_nArraySizeBytes * 8;

		if (m_pBitArray)
			SetAll(bit_value);

		// read bit array from file if specified
		if (m_szPersistFile[0] != _T('\0'))
		{
			ReadPersistFile(m_szPersistFile, m_nArraySizeBits, bit_value);
		}
		else if (m_szKeyName[0] != _T('\0'))
		{
			ReadRegistry(m_szKeyName, m_szValueName, m_nArraySizeBits, 
				bit_value);
		}

		if (m_pBitArray == NULL)
		{
			// allocate array if address is NULL
			XBITARRAY_TRACE(_T("Init: allocating bit array for %d bytes\n"), 
				m_nArraySizeBytes);
			m_pBitArray = new BYTE [m_nArraySizeBytes];
			_ASSERTE(m_pBitArray);
			m_bDeleteArray = TRUE;	// ensure array will be deallocated
			SetAll(bit_value);
		}

		if ((m_pBitArray != NULL) && (m_nArraySizeBytes > 0))
		{
			// one final check to ensure array is ok
			_ASSERTE(!IsBadWritePtr(m_pBitArray, m_nArraySizeBytes));
			if (IsBadWritePtr(m_pBitArray, m_nArraySizeBytes))
			{
				XBITARRAY_TRACE(_T("ERROR: bad pointer\n"));
			}
		}
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// Attach()
	//
	// Purpose:     Attach an existing bit array to a CXBitArray object
	//
	// Parameters:  pBitArray      - address of bit array (must not be NULL)
	//              nArraySizeBits - size of array in bits
	//
	// Returns:     None
	//
	// Notes:       Attach will associate the bit array specified by pBitArray
	//              with the CXBitArray object *without* initializing the bit 
	//              array (unlike CXBitArray() #2, which always initializes 
	//              the bit array).  If a heap-allocated bit array is already 
	//              associated with the CXBitArray object, it will be deallocated.
	//
	BOOL Attach(BYTE *pBitArray, size_t nArraySizeBits)
	{
		XBITARRAY_TRACE(_T("in Attach:  nArraySizeBits=%d\n"),
			nArraySizeBits);

		// if already initialized, delete the previous heap array
		if (m_bDeleteArray && m_pBitArray)
			delete [] m_pBitArray;

		m_bDeleteArray = FALSE;
		m_pBitArray    = NULL;

		_ASSERTE(pBitArray);
		if (!pBitArray)
		{
			XBITARRAY_TRACE(_T("ERROR:  pBitArray is NULL\n"));
			return FALSE;
		}

		_ASSERTE(nArraySizeBits > 0);
		if (nArraySizeBits == 0)
		{
			XBITARRAY_TRACE(_T("ERROR:  nArraySizeBits = 0\n"));
			return FALSE;
		}

		_ASSERTE(((nArraySizeBits/8)*8) == nArraySizeBits);
		if (((nArraySizeBits/8)*8) != nArraySizeBits)
		{
			XBITARRAY_TRACE(_T("ERROR:  nArraySizeBits is not a multiple of 8\n"));
			return FALSE;
		}

		m_pBitArray = pBitArray;
		m_nArraySizeBits  = nArraySizeBits;
		if (m_nArraySizeBits == 0)
			m_nArraySizeBits = 1;
		m_nArraySizeBytes = (m_nArraySizeBits + 7) / 8;	// round up
		m_nArraySizeBits  = m_nArraySizeBytes * 8;

		// one final check to ensure array is ok
		_ASSERTE(!IsBadWritePtr(m_pBitArray, m_nArraySizeBytes));
		if (IsBadWritePtr(m_pBitArray, m_nArraySizeBytes))
		{
			XBITARRAY_TRACE(_T("ERROR: bad pointer\n"));
			return FALSE;
		}

		return TRUE;
	}


// Copy & Assignment - do not allow
private:
	CXBitArray(const CXBitArray&);
	CXBitArray& operator = (const CXBitArray&);


// Attributes
public:

	///////////////////////////////////////////////////////////////////////////////
	//
	// Count()
	//
	// Purpose:     Return counts of bits set to 0 and bits set to 1
	//
	// Parameters:  bits_set_to_zero - pointer to the variable where the count of
	//                                 bits set to 0 is returned.
	//              bits_set_to_one  - pointer to the variable where the count of
	//                                 bits set to 1 is returned.
	//
	// Returns:     BOOL - TRUE = success
	//
	BOOL Count(size_t *bits_set_to_zero, size_t *bits_set_to_one) const
	{
		_ASSERTE(bits_set_to_zero && bits_set_to_one);
		if ((bits_set_to_zero == NULL) || (bits_set_to_one == NULL))
			return FALSE;

		*bits_set_to_zero = 0;
		*bits_set_to_one  = 0;

		_ASSERTE((m_pBitArray != NULL) && (m_nArraySizeBytes != 0));
		if ((m_pBitArray == NULL) || (m_nArraySizeBytes == 0))
		{
			XBITARRAY_TRACE(_T("ERROR:  bit array not initialized\n"));
			return FALSE;
		}

		for (size_t i = 0; i < m_nArraySizeBytes; i++)
		{
			BYTE b = m_pBitArray[i];

			if (b == 0)
			{
				*bits_set_to_zero += 8;
			}
			else if (b == 0xFF)
			{
				*bits_set_to_one += 8;
			}
			else
			{
				// count bits by table lookup

				static BYTE bittable[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 
											 1, 2, 2, 3, 2, 3, 3, 4 };

				// add count of bits in each nibble
				size_t n = bittable[ (int) (b & 0x0F) ] +
						   bittable[ (int)((b & 0xF0) >> 4) ];

				*bits_set_to_one  += n;
				*bits_set_to_zero += 8 - n;
			}
		}

		return TRUE;
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// Find()
	//
	// Purpose:     Find next bit that has the value specified by bit_value
	//
	// Parameters:  start_pos - bit number (0 - N);  the search for a bit will
	//                          start at this bit number.  If necessary, the search
	//                          will wrap to the beginning of the bit array.
	//              bit_value - value to look for (0 or 1)
	//              bit_pos   - pointer to variable where bit number will be 
	//                          returned
	//
	// Returns:     BOOL - TRUE = success
	//
	BOOL Find(size_t start_pos, BOOL bit_value, size_t *bit_pos) const
	{
		_ASSERTE((m_pBitArray != NULL) && (m_nArraySizeBytes != 0));
		if ((m_pBitArray == NULL) || (m_nArraySizeBytes == 0))
		{
			XBITARRAY_TRACE(_T("ERROR:  bit array not initialized\n"));
			return FALSE;
		}

		_ASSERTE(bit_pos);
		if (!bit_pos)
		{
			XBITARRAY_TRACE(_T("ERROR:  bad parameter\n"));
			return FALSE;
		}

		*bit_pos = 0;

		size_t byte_index, bit_index, bit_mask;

		if (!GetBitPos(start_pos, &byte_index, &bit_index, &bit_mask))
			return FALSE;

		XBITARRAY_TRACE(_T("Find:  start_pos=%d byte_index=%d bit_index=%d bit_mask=%02X\n"),
			start_pos, byte_index, bit_index, bit_mask);

		// advance starting point to ensure we look at all bits - 
		// the starting bit might have been in middle of byte
		size_t start_byte_index = byte_index;
		if (start_byte_index >= m_nArraySizeBytes)
			start_byte_index = 0;

		BOOL bFound = FALSE;
		size_t nBitsScanned = 0;			// keep track of number of bits scanned,
											// to determine when to stop
		size_t nBitsInByte = 8 - bit_index;	// scan of starting byte might start 
											// in middle of byte

		// loop thru all bytes in array
		for (;;)
		{
			if (byte_index >= m_nArraySizeBytes)
			{
				// we have reached end of array, so continue at beginning
				byte_index = 0;
				XBITARRAY_TRACE(_T("starting at beginning - nBitsScanned=%d  m_nArraySizeBits=%d\n"),
					nBitsScanned, m_nArraySizeBits);
			}
		
			if (nBitsScanned >= m_nArraySizeBits)
			{
				// we have looked at all bytes --
				// check if search has wrapped
				if (byte_index == start_byte_index)
				{
					break;
				}
			}

			// get next byte
			BYTE b = m_pBitArray[byte_index];
			XBITARRAY_TRACE(_T("b[%d]=%02X\n"), byte_index, b);

			// NOTE:  first (starting) byte might not start at bit 0

			BYTE mask = (BYTE) bit_mask;

			// loop thru all bits in byte - stop when 
			// high bit is reached
			for (;;)
			{
				if (((b & mask) && bit_value) ||
					(((b & mask) == 0) && !bit_value))
				{
					// found bit

					// get bit no.
					size_t index = GetBitNo(mask);

					*bit_pos = byte_index * 8 + index;

					bFound = TRUE;
					break;
				}

				if (mask & 0x80)	// done with byte if this was high bit
					break;

				mask = (BYTE) (mask << 1);	// try next bit

			} // for

			if (bFound)
				break;

			bit_mask = 1;	// start at bit 0 for next byte

			byte_index++;

			// keep track of bits scanned, so we know when to quit
			nBitsScanned += nBitsInByte;

			nBitsInByte = 8;	// next byte will be full 8 bits

		} // for

		XBITARRAY_TRACE(_T("Find:  bit_pos=%d  returning %d\n"), 
			*bit_pos, bFound);

		return bFound;
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// Get()
	//
	// Purpose:     Get bit value at a bit index
	//
	// Parameters:  pos  - bit number (0 - N)
	//
	// Returns:     BOOL - TRUE = bit is 1;  FALSE = bit is 0
	//
	BOOL Get(size_t pos) const
	{
		_ASSERTE((m_pBitArray != NULL) && (m_nArraySizeBytes != 0));

		if ((m_pBitArray == NULL) || (m_nArraySizeBytes == 0))
		{
			XBITARRAY_TRACE(_T("ERROR:  bit array not initialized\n"));
			return 0;
		}

		size_t byte_index, bit_index, bit_mask;

		if (GetBitPos(pos, &byte_index, &bit_index, &bit_mask))
		{
			return (m_pBitArray[byte_index] & bit_mask);	// 0 or 1
		}
		else
		{
			return 0;
		}
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// GetArraySizeBits()
	//
	// Purpose:     Get number of bits in bit array
	//
	// Parameters:  None
	//
	// Returns:     size_t - no. of bits in array
	//
	size_t GetArraySizeBits()  const	{ return m_nArraySizeBits; }


	///////////////////////////////////////////////////////////////////////////////
	//
	// GetArraySizeBytes()
	//
	// Purpose:     Get size of bit array in bytes
	//
	// Parameters:  None
	//
	// Returns:     size_t - no. of bytes in array
	//
	size_t GetArraySizeBytes() const	{ return m_nArraySizeBytes; }


	///////////////////////////////////////////////////////////////////////////////
	//
	// GetBitNo()
	//
	// Purpose:     Convert bit mask to bit number
	//
	// Parameters:  mask - mask of bit (00000001, 00000010, 00000100, etc.)
	//
	// Returns:     size_t - bit number (0 - 7) within byte
	//
	size_t GetBitNo(BYTE mask) const
	{
		size_t index = 0;
		while (mask != 1)
		{
			index++;
			mask = (BYTE) (mask >> 1);
		}
		return index;
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// GetBitPos()
	//
	// Purpose:     Convert bit number to byte and bit indexes and bit mask
	//
	// Parameters:  pos        - bit number (0 - N)
	//              byte_index - index to byte in array
	//              bit_index  - bit number within byte (0 - 7)
	//              bit_mask   - mask of bit (00000001, 00000010, 00000100, etc.)
	//
	// Returns:     BOOL - TRUE = success
	//
	BOOL GetBitPos(size_t pos, 
				   size_t *byte_index, 
				   size_t *bit_index, 
				   size_t *bit_mask) const
	{
		_ASSERTE((m_pBitArray != NULL) && (m_nArraySizeBytes != 0));
		if ((m_pBitArray == NULL) || (m_nArraySizeBytes == 0))
		{
			XBITARRAY_TRACE(_T("ERROR:  bit array not initialized\n"));
			return FALSE;
		}

		*byte_index = pos / 8;			// byte index [0-(N-1)] in array of size N bytes
		*bit_index  = pos % 8;			// bit index [0-7] within the byte
		*bit_mask   = 1 << *bit_index;	// bit mask

		_ASSERTE(*byte_index < m_nArraySizeBytes);
		if (*byte_index >= m_nArraySizeBytes)
		{
			XBITARRAY_TRACE(_T("ERROR:  byte_index (%d) too large\n"), *byte_index);
			*byte_index = 0;
			return FALSE;
		}

		return TRUE;
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// GetPersistFileName()
	//
	// Purpose:     Get persist file name
	//
	// Parameters:  lpszPersistFile - pointer to file path buffer that receives the
	//                                nul-terminated persist file path
	//              dwSize          - size of buffer in TCHARs
	//
	// Returns:     DWORD - length of string copied to lpszPersistFile in TCHARs
	//
	DWORD GetPersistFileName(LPTSTR lpszPersistFile, DWORD dwSize) const
	{
		_ASSERTE(lpszPersistFile);
		if (lpszPersistFile == NULL)
			return 0;

		_ASSERTE(dwSize > 0);
		if (dwSize < 2)
			return 0;

		memset(lpszPersistFile, 0, dwSize * sizeof(TCHAR));
		_tcsncpy(lpszPersistFile, m_szPersistFile, dwSize-1);

		return _tcslen(lpszPersistFile);
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// GetRegistryKeyName()
	//
	// Purpose:     Get registry key name
	//
	// Parameters:  lpszKeyName - pointer to buffer that receives the
	//                            nul-terminated key name
	//              dwSize      - size of buffer in TCHARs
	//
	// Returns:     DWORD - length of string copied to lpszKeyName in TCHARs
	//
	DWORD GetRegistryKeyName(LPTSTR lpszKeyName, DWORD dwSize) const
	{
		_ASSERTE(lpszKeyName);
		if (lpszKeyName == NULL)
			return 0;

		_ASSERTE(dwSize > 0);
		if (dwSize < 2)
			return 0;

		memset(lpszKeyName, 0, dwSize * sizeof(TCHAR));
		_tcsncpy(lpszKeyName, m_szKeyName, dwSize-1);

		return _tcslen(lpszKeyName);
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// GetRegistryValueName()
	//
	// Purpose:     Get registry value name
	//
	// Parameters:  lpszValueName - pointer to buffer that receives the
	//                              nul-terminated value name
	//              dwSize        - size of buffer in TCHARs
	//
	// Returns:     DWORD - length of string copied to lpszValueName in TCHARs
	//
	DWORD GetRegistryValueName(LPTSTR lpszValueName, DWORD dwSize) const
	{
		_ASSERTE(lpszValueName);
		if (lpszValueName == NULL)
			return 0;

		_ASSERTE(dwSize > 0);
		if (dwSize < 2)
			return 0;

		memset(lpszValueName, 0, dwSize * sizeof(TCHAR));
		_tcsncpy(lpszValueName, m_szValueName, dwSize-1);

		return _tcslen(lpszValueName);
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// operator []
	//
	// Purpose:     Get bit value at a bit index
	//
	// Parameters:  pos  - bit number (0 - N)
	//
	// Returns:     BOOL - TRUE = bit is 1;  FALSE = bit is 0
	//
	BOOL operator [] (size_t pos) const
	{
		return Get(pos);
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// operator LPBYTE
	//
	// Purpose:     Get address of bit array
	//
	// Parameters:  None
	//
	// Returns:     LPBYTE - address of bit array, or NULL
	//
	operator LPBYTE () const
	{
		return m_pBitArray;
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// Set()
	//
	// Purpose:     Set bit value
	//
	// Parameters:  pos       - bit number (0 - N)
	//              bit_value - 0 or 1
	//
	// Returns:     None
	//
	void Set(size_t pos, BOOL bit_value)
	{
		_ASSERTE((m_pBitArray != NULL) && (m_nArraySizeBytes != 0));
		if ((m_pBitArray == NULL) || (m_nArraySizeBytes == 0))
		{
			XBITARRAY_TRACE(_T("ERROR:  bit array not initialized\n"));
			return;
		}

		size_t byte_index, bit_index, bit_mask;

		if (GetBitPos(pos, &byte_index, &bit_index, &bit_mask))
		{
			if (bit_value)
				m_pBitArray[byte_index] |= bit_mask;
			else
				m_pBitArray[byte_index] &= ~(bit_mask);
		}
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// SetAll()
	//
	// Purpose:     Set all bits to a value
	//
	// Parameters:  bit_value - 0 or 1
	//
	// Returns:     None
	//
	void SetAll(BOOL bit_value)
	{
		_ASSERTE((m_pBitArray != NULL) && (m_nArraySizeBytes != 0));
		if ((m_pBitArray == NULL) || (m_nArraySizeBytes == 0))
		{
			XBITARRAY_TRACE(_T("ERROR:  bit array not initialized\n"));
			return;
		}

		BYTE value = 0;

		if (bit_value)
			value = 0xFF;

		for (size_t i = 0; i < m_nArraySizeBytes; i++)
			m_pBitArray[i] = value;
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// SetPersistFileName()
	//
	// Purpose:     Set persist file name
	//
	// Parameters:  lpszPersistFile - pointer to name of file containing bit array
	//
	// Returns:     None
	//
	void SetPersistFileName(LPCTSTR lpszPersistFile)
	{
		memset(m_szPersistFile, 0, sizeof(m_szPersistFile));
		if (lpszPersistFile)
			_tcsncpy(m_szPersistFile, lpszPersistFile, 
				sizeof(m_szPersistFile)/sizeof(TCHAR)-2);
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// SetRegistryNames()
	//
	// Purpose:     Set registry key and value names
	//
	// Parameters:  lpszKeyName   - key name
	//              lpszValueName - value name
	//
	// Returns:     None
	//
	void SetRegistryNames(LPCTSTR lpszKeyName, LPCTSTR lpszValueName)
	{
		memset(m_szKeyName, 0, sizeof(m_szKeyName));
		if (lpszKeyName)
			_tcsncpy(m_szKeyName, lpszKeyName, 
				sizeof(m_szKeyName)/sizeof(TCHAR)-2);

		memset(m_szValueName, 0, sizeof(m_szValueName));
		if (lpszValueName)
			_tcsncpy(m_szValueName, lpszValueName, 
				sizeof(m_szValueName)/sizeof(TCHAR)-2);
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// ToString()
	//
	// Purpose:     Returns a string that represents the bit array
	//
	// Parameters:  None
	//
	// Returns:     LPTSTR - pointer to a string that has been allocated on the 
	//                       heap and must be freed by caller.  The string
	//                       contains one TCHAR for each bit in the array, plus
	//                       a trailing nul.  Returns NULL if failure.
	//
	LPTSTR ToString() const
	{
		LPTSTR s = NULL;

		_ASSERTE((m_pBitArray != NULL) && (m_nArraySizeBytes != 0));
		if ((m_pBitArray == NULL) || (m_nArraySizeBytes == 0))
		{
			XBITARRAY_TRACE(_T("ERROR:  bit array not initialized\n"));
		}
		else
		{
			s = new TCHAR [m_nArraySizeBits + 1];
			_ASSERTE(s);

			if (s)
			{
				size_t i = 0;
				for (i = 0; i < m_nArraySizeBits; i++)
				{
					if (Get(i))
						s[i] = _T('1');
					else
						s[i] = _T('0');
				}
				s[i] = _T('\0');
			}
		}

		return s;
	}


// Operations
public:

	///////////////////////////////////////////////////////////////////////////////
	//
	// ReadPersistFile()
	//
	// Purpose:     Read bit array from file
	//
	// Parameters:  lpszFile       - name of file to read bit array from
	//              nArraySizeBits - size of array in bits
	//              bit_value      - bit array will be initialized to this value
	//                               if it cannot be read from file
	//
	// Returns:     BOOL - TRUE = success
	//
	// Notes:       See Notes for CXBitArray #4.  Whatever bit array address that
	//              is currently stored in m_pBitAddress will be replaced with a 
	//              new address that is allocated from the heap, sized to hold the
	//              contents of the file.  If the file cannot be read, the bit 
	//              array will be allocated to hold (nArraySizeBits+7)/8 bytes,
	//              and initialized to bit_value.
	//
	BOOL ReadPersistFile(LPCTSTR lpszFile, size_t nArraySizeBits, BOOL bit_value)
	{
		XBITARRAY_TRACE(_T("in ReadPersistFile: %s\n"), lpszFile);

		BOOL bSuccess = FALSE;

		if (m_bDeleteArray && m_pBitArray)
			delete [] m_pBitArray;
		m_pBitArray = NULL;

		m_bDeleteArray = FALSE;

		_ASSERTE(lpszFile);
		_ASSERTE(lpszFile[0] != _T('\0'));

		if (!lpszFile || lpszFile[0] == _T('\0'))
		{
			XBITARRAY_TRACE(_T("ERROR:  lpszFile is NULL\n"));
		}
		else
		{
			if (_taccess(lpszFile, 04) == 0)
			{
				// file exists, try to open it
				HANDLE hFile = INVALID_HANDLE_VALUE;
				hFile = ::CreateFile(lpszFile,
									 GENERIC_READ,
									 FILE_SHARE_READ | FILE_SHARE_WRITE,
									 NULL,
									 OPEN_EXISTING,
									 FILE_ATTRIBUTE_NORMAL,
									 NULL);

				if (hFile == INVALID_HANDLE_VALUE)
				{
					XBITARRAY_TRACE(_T("ERROR: CreateFile failed for %s\n"), 
						lpszFile);
				}
				else
				{
					DWORD dwFileSize = ::GetFileSize(hFile, NULL);
					if ((dwFileSize != INVALID_FILE_SIZE) && (dwFileSize > 0))
					{
						m_nArraySizeBytes = dwFileSize;
						m_nArraySizeBits  = 8 * m_nArraySizeBytes;
						XBITARRAY_TRACE(_T("ReadPersistFile: allocating bit array for %d bytes\n"), 
							m_nArraySizeBytes);
						m_pBitArray = new BYTE [m_nArraySizeBytes];
						_ASSERTE(m_pBitArray);

						if (m_pBitArray)
						{
							m_bDeleteArray = TRUE;

							DWORD dwBytesRead = 0;

							BOOL bRet = ::ReadFile(hFile, 
												   (LPVOID) m_pBitArray, 
												   dwFileSize, 
												   &dwBytesRead, 
												   NULL);

							XBITARRAY_TRACE(_T("read %d bytes\n"), dwBytesRead);

							if (bRet)
								bSuccess = TRUE;
						}
					}

					::CloseHandle(hFile);
				}
			}
			else
			{
				XBITARRAY_TRACE(_T("ERROR: cannot open %s\n"), 
					lpszFile);
			}
		}

		if (!bSuccess)
		{
			// allocate bit array and fill with default value

			if (m_pBitArray == NULL)
			{
				m_nArraySizeBits  = nArraySizeBits;
				if (m_nArraySizeBits == 0)
					m_nArraySizeBits = 1;
				m_nArraySizeBytes = (m_nArraySizeBits + 7) / 8;
				m_nArraySizeBits = m_nArraySizeBytes * 8;

				XBITARRAY_TRACE(_T("ReadPersistFile: allocating bit array for %d bytes\n"), 
					m_nArraySizeBytes);
				m_pBitArray = new BYTE [m_nArraySizeBytes];
				_ASSERTE(m_pBitArray);
			}

			if (m_pBitArray)
			{
				m_bDeleteArray = TRUE;
				SetAll(bit_value);
			}
		}

		return bSuccess;
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// ReadRegistry()
	//
	// Purpose:     Read bit array from registry
	//
	// Parameters:  lpszKeyName    - key name
	//              lpszValueName  - value name
	//              nArraySizeBits - size of array in bits
	//              bit_value      - bit array will be initialized to this value
	//                               if it cannot be read from registry
	//
	// Returns:     BOOL - TRUE = success
	//
	// Notes:       See Notes for CXBitArray #5.  Whatever bit array address that
	//              is currently stored in m_pBitAddress will be replaced with a 
	//              new address that is allocated from the heap, sized to hold the
	//              contents of the registry value.  If the registry cannot be read, 
	//              the bit array will be allocated to hold (nArraySizeBits+7)/8 
	//              bytes, and initialized to bit_value.
	//
	BOOL ReadRegistry(LPCTSTR lpszKeyName, 
					  LPCTSTR lpszValueName, 
					  size_t nArraySizeBits, 
					  BOOL bit_value)
	{
		XBITARRAY_TRACE(_T("in ReadRegistry: %s\n"), lpszKeyName);

		BOOL bSuccess = FALSE;

		if (m_bDeleteArray && m_pBitArray)
			delete [] m_pBitArray;
		m_pBitArray = NULL;

		m_bDeleteArray = FALSE;

		_ASSERTE(lpszKeyName);
		_ASSERTE(lpszKeyName[0] != _T('\0'));

		if (!lpszKeyName || lpszKeyName[0] == _T('\0'))
		{
			XBITARRAY_TRACE(_T("ERROR:  lpszKeyName is NULL\n"));
		}
		else
		{
			// open the registry key
			XBITARRAY_TRACE(_T("trying to open key\n"));
			HKEY hKey = NULL;
			LONG lRet = ::RegOpenKeyEx(HKEY_CURRENT_USER, lpszKeyName, 0, KEY_READ, &hKey);

			if (lRet == ERROR_SUCCESS)
			{
				// registry key was opened
				XBITARRAY_TRACE(_T("key opened ok\n"));

				// read value
				DWORD dwType = 0;
				DWORD dwSize = 0;

				// get size of data in registry
				lRet = ::RegQueryValueEx(hKey, lpszValueName, 0, &dwType, NULL, &dwSize);

				XBITARRAY_TRACE(_T("dwSize=%d\n"), dwSize);

				if (lRet == ERROR_SUCCESS)
				{
					// we now have size of data

					m_nArraySizeBytes = dwSize;
					m_nArraySizeBits  = 8 * m_nArraySizeBytes;
					XBITARRAY_TRACE(_T("ReadRegistry: allocating bit array for %d bytes\n"), 
						m_nArraySizeBytes);
					m_pBitArray = new BYTE [m_nArraySizeBytes];
					_ASSERTE(m_pBitArray);

					if (m_pBitArray)
					{
						m_bDeleteArray = TRUE;

						// read data from registry into bit array
						lRet = ::RegQueryValueEx(hKey, lpszValueName, 0, &dwType, 
										m_pBitArray, &dwSize);

						if (lRet == ERROR_SUCCESS)
							bSuccess = TRUE;
					}
				}

				::RegCloseKey(hKey);
			}
		}

		if (!bSuccess)
		{
			XBITARRAY_TRACE(_T("registry read failed\n"));

			// allocate bit array and fill with default value

			if (m_pBitArray == NULL)
			{

				m_nArraySizeBits  = nArraySizeBits;
				if (m_nArraySizeBits == 0)
					m_nArraySizeBits = 1;
				m_nArraySizeBytes = (m_nArraySizeBits + 7) / 8;
				m_nArraySizeBits = m_nArraySizeBytes * 8;

				XBITARRAY_TRACE(_T("ReadRegistry: allocating bit array for %d bytes\n"), 
					m_nArraySizeBytes);
				m_pBitArray = new BYTE [m_nArraySizeBytes];
				_ASSERTE(m_pBitArray);
			}

			if (m_pBitArray)
			{
				m_bDeleteArray = TRUE;
				SetAll(bit_value);
			}
		}

		return bSuccess;
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// WritePersistFile()
	//
	// Purpose:     Write bit array to file
	//
	// Parameters:  lpszFile - name of file to write bit array to
	//
	// Returns:     BOOL - TRUE = success
	//
	// Notes:       See Notes for CXBitArray #4.
	//
	BOOL WritePersistFile(LPCTSTR lpszFile) const
	{
		XBITARRAY_TRACE(_T("in WritePersistFile: %s\n"), lpszFile);

		_ASSERTE((m_pBitArray != NULL) && (m_nArraySizeBytes != 0));
		if ((m_pBitArray == NULL) || (m_nArraySizeBytes == 0))
		{
			XBITARRAY_TRACE(_T("ERROR:  bit array not initialized\n"));
			return FALSE;
		}

		BOOL bSuccess = FALSE;

		_ASSERTE(lpszFile);
		_ASSERTE(lpszFile[0] != _T('\0'));

		if (!lpszFile || lpszFile[0] == _T('\0'))
		{
			XBITARRAY_TRACE(_T("ERROR:  lpszFile is NULL\n"));
		}
		else
		{
			HANDLE hFile = INVALID_HANDLE_VALUE;
			hFile = ::CreateFile(lpszFile,
								 GENERIC_READ | GENERIC_WRITE,
								 FILE_SHARE_READ | FILE_SHARE_WRITE,
								 NULL,
								 CREATE_ALWAYS,
								 FILE_ATTRIBUTE_NORMAL,
								 NULL);

			if (hFile == INVALID_HANDLE_VALUE)
			{
				XBITARRAY_TRACE(_T("ERROR: CreateFile failed for %s\n"), 
					lpszFile);
			}
			else
			{
				DWORD dwBytesWritten = 0;

				BOOL bRet = ::WriteFile(hFile, 
										(LPVOID) m_pBitArray, 
										m_nArraySizeBytes, 
										&dwBytesWritten, 
										NULL);

				if (bRet)
				{
					::FlushFileBuffers(hFile);
					bSuccess = TRUE;
				}

				::CloseHandle(hFile);
			}
		}

		return bSuccess;
	}


	///////////////////////////////////////////////////////////////////////////////
	//
	// WriteRegistry()
	//
	// Purpose:     Write bit array to registry
	//
	// Parameters:  lpszKeyName   - key name
	//           :  lpszValueName - value name
	//
	// Returns:     BOOL - TRUE = success
	//
	// Notes:       See Notes for CXBitArray #5.
	//
	BOOL WriteRegistry(LPCTSTR lpszKeyName, 
					   LPCTSTR lpszValueName) const
	{
		XBITARRAY_TRACE(_T("in WriteRegistry: %s\n"), lpszKeyName);

		_ASSERTE((m_pBitArray != NULL) && (m_nArraySizeBytes != 0));
		if ((m_pBitArray == NULL) || (m_nArraySizeBytes == 0))
		{
			XBITARRAY_TRACE(_T("ERROR:  bit array not initialized\n"));
			return FALSE;
		}

		BOOL bSuccess = FALSE;

		_ASSERTE(lpszKeyName);
		_ASSERTE(lpszKeyName[0] != _T('\0'));

		if (!lpszKeyName || lpszKeyName[0] == _T('\0'))
		{
			XBITARRAY_TRACE(_T("ERROR:  lpszKeyName is NULL\n"));
		}
		else
		{
			// open the registry key
			DWORD dwResult = 0;
			HKEY hKey = NULL;
			LONG lRet = ::RegCreateKeyEx(HKEY_CURRENT_USER, lpszKeyName, 0, NULL,
							REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, &dwResult);

			if (lRet == ERROR_SUCCESS)
			{
				// registry key was opened or created

				lRet = ::RegSetValueEx(hKey, lpszValueName, 0, REG_BINARY,
							m_pBitArray, m_nArraySizeBytes);

				if (lRet == ERROR_SUCCESS)
				{
					bSuccess = TRUE;
				}
				else
				{
					XBITARRAY_TRACE(_T("ERROR:  RegSetValueEx failed\n"));
				}

				::RegCloseKey(hKey);
			}
			else
			{
				XBITARRAY_TRACE(_T("ERROR:  RegCreateKeyEx failed\n"));
			}
		}

		if (!bSuccess)
		{
			XBITARRAY_TRACE(_T("ERROR:  WriteRegistry failed\n"));
		}

		return bSuccess;
	}


// Implementation
private:
	TCHAR	m_szPersistFile[_MAX_PATH*2];	// persist file path
	TCHAR	m_szKeyName[_MAX_PATH*2];		// registry key name
	TCHAR	m_szValueName[_MAX_PATH*2];		// registry value name
	BOOL	m_bDeleteArray;					// TRUE = delete array in dtor
	BYTE *	m_pBitArray;					// pointer to bit array
	size_t	m_nArraySizeBytes;				// no. of bytes in array
	size_t	m_nArraySizeBits;				// no. of bits in array (always
											// a multiple of 8)

};

#pragma warning(pop)

#endif //XBITARRAY_H

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Hans Dietrich Software
United States United States
I attended St. Michael's College of the University of Toronto, with the intention of becoming a priest. A friend in the University's Computer Science Department got me interested in programming, and I have been hooked ever since.

Recently, I have moved to Los Angeles where I am doing consulting and development work.

For consulting and custom software development, please see www.hdsoft.org.






Comments and Discussions