Click here to Skip to main content
15,891,473 members
Articles / Programming Languages / C++

CBSTRStream - A simple BSTR stream implementation

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
12 Apr 20012 min read 67.4K   518   23  
CBSTRStream is a simple BSTR stream implemenation with some useful data type conversion functions.
///////////////////////////////////////////////////////////////////
// EnBSTR
// Copyright (C) 2001 Morten Abrahamsen (mabraha@online.no)
// All rights reserved.
//
// The code and information is provided "as-is" without
// warranty of any kind, either expressed or implied.
///////////////////////////////////////////////////////////////////
// 2001-02-25: 
//	- Initial Release
// 2001-02-28:
//	- Added upper, lower, reverse
//	- Added replace, spanInclude, spanExclude
///////////////////////////////////////////////////////////////////


#ifndef __ENBSTR_H__
#define __ENBSTR_H__

#pragma once

#ifndef __cplusplus
	#error ATL requires C++ compilation (use a .cpp suffix)
#endif

#include "comdef.h"

class CEnBSTR : public _bstr_t
{
public:
	// Constructors
	CEnBSTR() : _bstr_t() {}
	CEnBSTR(const char* s) : _bstr_t(s) {}
    CEnBSTR(const CEnBSTR& s) : _bstr_t(s) {}
    CEnBSTR(const _bstr_t& s) : _bstr_t(s) {}
    CEnBSTR(const wchar_t* s) : _bstr_t(s) {}
    CEnBSTR(const _variant_t& var) : _bstr_t(var) {}
    CEnBSTR(BSTR bstr, bool fCopy) : _bstr_t(bstr, fCopy) {}

	// Analysis
	int find(const _bstr_t& bstr) const
	{
		const wchar_t* pstr = _bstr_t::operator const wchar_t*();
		wchar_t* pdest = wcsstr(pstr, bstr);
		return pdest ? pdest - pstr : -1;
	}
	int find(const wint_t& ch) const
	{
		const wchar_t* pstr = _bstr_t::operator const wchar_t*();
		wchar_t* pdest = wcschr(pstr, ch);
		return pdest ? pdest - pstr : -1;
	}
	int find(const _bstr_t& bstr, const unsigned int nStart) const
	{
		const wchar_t* pstr = _bstr_t::operator const wchar_t*();
		if (nStart >= length()) return -1;

		wchar_t* pdest = wcsstr(pstr+nStart, bstr);
		return pdest ? pdest - pstr : -1;
	}
	int find(const wint_t& ch, const unsigned int nStart) const
	{
		const wchar_t* pstr = _bstr_t::operator const wchar_t*();

		if (nStart >= length()) return -1;

		wchar_t* pdest = wcschr(pstr+nStart, ch);
		return pdest ? pdest - pstr : -1;
	}

	/////////////////////////////////////////////////////////////////////
	// Extraction
	CEnBSTR mid(const unsigned int nFirst, unsigned int nCount = 0) const
	{
		unsigned int nLength = _bstr_t::length();
		if (nCount == 0) 
			nCount = nLength-nFirst;

		if (nFirst == 0 && nCount >= nLength)
			return *this;


		const wchar_t* pstr = _bstr_t::operator const wchar_t*();
		CEnBSTR bstr;

		if (nLength >= (nFirst+nCount))
		{
			wchar_t* pNewStr = new wchar_t[nCount+1];
			ZeroMemory(pNewStr, sizeof(wchar_t)*(nCount+1));
			wcsncpy(pNewStr, pstr+nFirst, nCount);
			bstr = pNewStr;
			delete [] pNewStr;
		}

		return bstr;
	}

	CEnBSTR left(const unsigned int nCount) const
	{
		return mid(0, nCount);
	}

	CEnBSTR right(const unsigned int nCount) const
	{
		unsigned int nLength = _bstr_t::length();
		if (nLength >= nCount)
			return mid(nLength-nCount, nCount);

		return *this;
	}

	CEnBSTR spanInclude(const _bstr_t& bstrCharset) const
	{
		const wchar_t* pstr = _bstr_t::operator const wchar_t*();
		unsigned int nLen = wcsspn(pstr, bstrCharset);
		
		return nLen ? mid(0, nLen) : "";
	}

	CEnBSTR spanExclude(const _bstr_t& bstrCharset) const
	{
		const wchar_t* pstr = _bstr_t::operator const wchar_t*();
		unsigned int nLen = wcscspn(pstr, bstrCharset);
		
		return nLen ? mid(0, nLen) : "";
	}

	/////////////////////////////////////////////////////////////////////
	// Manipulation
	void upper()
	{
		_bstr_t bstr(*this, true);
		_wcsupr((wchar_t*)bstr);
		*this = bstr;
	}

	void lower()
	{
		_bstr_t bstr(*this, true);
		_wcslwr((wchar_t*)bstr);
		*this = bstr;
	}

	void reverse()
	{
		_bstr_t bstr(*this, true);
		_wcsrev((wchar_t*)bstr);
		*this = bstr;
	}

	unsigned int replace(const wint_t& chFind, const wint_t& chReplace)
	{
		const wchar_t* pstr = _bstr_t::operator const wchar_t*();
		if (pstr == NULL) return 0;
		if (chFind == chReplace) return 0;

		// Make a copy of the string to avoid reference counting problems.
		_bstr_t bstrNew(*this, true);
		pstr = (wchar_t*)bstrNew;


		unsigned int nCount = 0;
		wchar_t* pSearchPos = 0;
		while (pSearchPos = wcschr(pSearchPos ? pSearchPos : pstr, chFind))
		{
			*pSearchPos = chReplace;
			++nCount;			
		}

		// Assign the new string (avoids refcount and deletes cached char-string)
		*this = bstrNew;
		return nCount;
	}

	unsigned int replace(const _bstr_t& bstrFind, const _bstr_t& bstrReplace)
	{
		const wchar_t* pSrcStr = _bstr_t::operator const wchar_t*();
		if (pSrcStr == NULL) return 0;

		unsigned int nCount = 0;
		unsigned int nSearchStrLen = bstrFind.length();

		// Calculate new length
		wchar_t* pSearchPos = 0;
		while (pSearchPos = wcsstr(pSearchPos ? pSearchPos : pSrcStr, bstrFind))
		{
			pSearchPos += nSearchStrLen;
			++nCount;			
		}

		// Replace bstrFind substrings with bstrReplace substrings
		if (nCount)
		{
			unsigned int nReplaceStrLen = bstrReplace.length();
			unsigned int nOrgStrLen = length();
			unsigned int nNewStrLength = nOrgStrLen - (nSearchStrLen - nReplaceStrLen)*nCount;
			nCount = 0;

			wchar_t* pNewStr = SysAllocStringLen(0, nNewStrLength);
			wchar_t* pSrcPos = (wchar_t*)pSrcStr;
			wchar_t* pDestPos = pNewStr;

			wchar_t* pNextSrcPos = 0;
			while (pNextSrcPos = wcsstr(pSrcPos, bstrFind))
			{
				if (pSrcPos != pNextSrcPos)
				{
					unsigned int nCopyLength = pNextSrcPos-pSrcPos;
					wcsncpy(pDestPos, pSrcPos, nCopyLength);
					pDestPos += nCopyLength;

					pSrcPos = pNextSrcPos;
				}

				wcsncpy(pDestPos, bstrReplace, nReplaceStrLen);
				pDestPos += nReplaceStrLen;
				pSrcPos += nSearchStrLen;
				++nCount;
			}

			wcsncpy(pDestPos, pSrcPos, nOrgStrLen - (pSrcPos-pSrcStr));

			*this = bstr_t(pNewStr, false);
		}
		return nCount;
	}

};







///////////////////////////////////////////////////////////////////
// CBSTRStream
// Copyright (C) 2001 Morten Abrahamsen (mabraha@online.no)
// All rights reserved.
//
// The code and information is provided "as-is" without
// warranty of any kind, either expressed or implied.
///////////////////////////////////////////////////////////////////
// 2001-04-13: 
//	- Initial Release
///////////////////////////////////////////////////////////////////
// Notes:
//	- This class is NOT threadsafe
///////////////////////////////////////////////////////////////////

class CBSTRStream
{
public:
	CBSTRStream(const CBSTRStream& bsSrc, long nInitialBufferSize = 50, long nChunkSize = 50)
	{
		init(nInitialBufferSize, nChunkSize);
		operator << (bsSrc);
	}
	CBSTRStream(long nInitialBufferSize = 50, long nChunkSize = 50)
	{
		init(nInitialBufferSize, nChunkSize);
	}

	virtual ~CBSTRStream()
	{
		clearCache();
		
		if (m_bstr)
			::SysFreeString(m_bstr);

#ifdef _DEBUG
		if (m_nReAllocCount > 1)
		{
			ATLTRACE("CBSTRStream::%d buffer reallocations were performed. You might want to increase the buffersize\n", 
				m_nReAllocCount);
		}
#endif
	}
	
	CBSTRStream& operator << (const _bstr_t& bstrData)
	{
		appendBSTR(bstrData);
		return *this;
	}
	CBSTRStream& operator << (wchar_t* pstr)
	{
		long nLength = wcslen(pstr);
		appendWCHAR(pstr, nLength);
		return *this;
	}

	CBSTRStream& operator << (REFGUID src)
	{
		LPOLESTR szGUID;
		HRESULT hr = ::StringFromCLSID(src, &szGUID);
		if (FAILED(hr)) throw _com_error(hr);
		appendWCHAR(szGUID, wcslen(szGUID));
		::CoTaskMemFree(szGUID);
		return *this;
	}
	
	CBSTRStream& operator << (long nData)
	{
		wchar_t szBuf[33];
		ZeroMemory(szBuf, sizeof(wchar_t)*33);
		_ltow(nData, szBuf, 10);
		appendWCHAR(szBuf, wcslen(szBuf));
		return *this;
	}

	CBSTRStream& operator << (unsigned long nData)
	{
		wchar_t szBuf[33];
		ZeroMemory(szBuf, sizeof(wchar_t)*33);
		_ultow(nData, szBuf, 10);
		appendWCHAR(szBuf, wcslen(szBuf));
		return *this;
	}

	CBSTRStream& operator << (int nData)
	{
		wchar_t szBuf[33];
		ZeroMemory(szBuf, sizeof(wchar_t)*33);
		_itow(nData, szBuf, 10);
		appendWCHAR(szBuf, wcslen(szBuf));
		return *this;
	}

	CBSTRStream& operator << (double dValue)
	{
		char buffer[50];
		ZeroMemory(buffer, 50);
		_gcvt(dValue, 10, buffer);
		_bstr_t bstrData(buffer);
		appendBSTR(bstrData);
		return *this;
	}

	CBSTRStream& operator << (const CBSTRStream& bsSrc)
	{
		appendWCHAR(bsSrc.m_bstr, bsSrc.m_nSize);
		return *this;
	}
	
	void reset()
	{
		m_nSize = 0;
	}

	operator BSTR()
	{
		if (m_nSize == m_nAllocatedSize) return m_bstr;

		if (m_bstrCache == NULL)
			m_bstrCache = ::SysAllocStringLen(m_bstr, m_nSize);

		return m_bstrCache;
	}

	operator _bstr_t() const
	{
		return _bstr_t(::SysAllocStringLen(m_bstr, m_nSize), false);
	}
	
	long length() const
	{
		return m_nSize;
	}

	bool operator == (const CBSTRStream& bsSrc) const
	{
		if (bsSrc.m_bstr == NULL && m_bstr == NULL)
			return true;
		if (m_nSize != bsSrc.m_nSize) 
			return false;
		if (bsSrc.m_bstr != NULL && m_bstr != NULL)
			return wcsncmp(m_bstr, bsSrc.m_bstr, m_nSize) == 0;
		return false;
	}

	CBSTRStream& operator = (const CBSTRStream& bsSrc)
	{
		reset();
		appendWCHAR(bsSrc.m_bstr, bsSrc.m_nSize);
		return *this;
	}
	
protected:
	void appendBSTR(BSTR bstr)
	{
		clearCache();
		long nLength = ::SysStringLen(bstr);
		manageBuffer(nLength);
		wcsncpy(m_bstr+m_nSize, bstr, nLength);
		m_nSize += nLength;
	}
	
	void appendWCHAR(wchar_t* pstr, long nLength)
	{
		clearCache();
		manageBuffer(nLength);
		wcsncpy(m_bstr+m_nSize, pstr, nLength);
		m_nSize += nLength;
	}
	
	void manageBuffer(long nExtraSize)
	{
		long nFreeSpace = m_nAllocatedSize - m_nSize;
		if (nFreeSpace >= nExtraSize) return;
		
		long nAppendLength = m_nAllocatedSize == 0 ? m_nInitialBufferSize : m_nChunkSize;
		if (nExtraSize > nAppendLength+nFreeSpace)	
			nAppendLength += nExtraSize-nFreeSpace;
		
		if (m_nAllocatedSize == 0)
		{
			m_bstr = ::SysAllocStringLen(0, nAppendLength);
			m_nAllocatedSize = nAppendLength;
		}
		else
		{
			long nNewSize = m_nAllocatedSize + nAppendLength;
			BSTR bstrTmp = ::SysAllocStringLen(m_bstr, nNewSize);
			::SysFreeString(m_bstr);
			m_bstr = bstrTmp;
			m_nAllocatedSize = nNewSize;
			
#ifdef _DEBUG
			++m_nReAllocCount;
#endif
		}
	}

	// the cached bstr is used with the BSTR operator. As the allocated string is probably larger than the
	// actual string.
	void clearCache() 
	{
		if (m_bstrCache)
		{
			::SysFreeString(m_bstrCache);
			m_bstrCache = NULL;
		}
	}
	
	void init(long nInitialBufferSize, long nChunkSize)
	{
		m_nChunkSize = nChunkSize;
		m_bstrCache = 0;
		m_nInitialBufferSize = nInitialBufferSize;
		m_nSize = 0;
		m_bstr = 0;
		m_nAllocatedSize = 0;
#ifdef _DEBUG
		m_nReAllocCount = 0;
#endif
	}
	
	
	
private:
	BSTR m_bstr;
	BSTR m_bstrCache;
	long m_nChunkSize;
	long m_nAllocatedSize;
	long m_nInitialBufferSize;
	long m_nSize;
#ifdef _DEBUG
	long m_nReAllocCount;
#endif
};
	
	
	
#endif (__ENBSTR_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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Norway Norway
Morten Abrahamsen is a software architect living in Oslo, Norway. He is currently designing and developing a commerce platform using Microsoft .Net technologies. He has extensive experience with the .Net Framework, COM+, SQL Server 2000, Visual C++/ATL and Visual C#.

Comments and Discussions