Click here to Skip to main content
15,896,726 members
Articles / Desktop Programming / MFC

Binary Data Marshaling

Rate me:
Please Sign up or sign in to vote.
4.63/5 (12 votes)
13 Jan 20055 min read 60.8K   1.9K   33  
Fast binary data marshaling using simple CMarshal class.
// String.cpp: implementation of the CXMLNode class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "string.h"
#include <tchar.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#endif

//////////////////////////////////////////////////////////////////////

String::String()
{
	InitValues();
}

String::String(const String& string)
{
	InitValues();
	Assign(string.m_pBuffer, string.GetLength());
}

String::String(LPCTSTR lpsz)
{
	InitValues();
	*this += lpsz;
}

String::String(char ch, int nRepeat /*= 1*/)
{
	InitValues();
	ReleaseBuffer(nRepeat);
	memset(m_pBuffer, ch, nRepeat);
}

String::String(LPCTSTR lpch, int nLength)
{
	InitValues();
	memcpy(GetBuffer(nLength), lpch, nLength);
}

String::String(const unsigned char* lpsz)
{
	InitValues();
	*this = (LPCSTR)lpsz;
}

int StrLen(const char* p)
{
	return p ? (int)strlen(p) : 0;
}

void String::InitValues()
{
	m_pBuffer = NULL;
	m_nAllocLength = 0;
	m_nLength = 0;
	m_nGrowBytes = 100;
}

String::~String()
{
	Empty();
}

void String::SetGrowBytes(int nGrowBytes)
{
	m_nGrowBytes = nGrowBytes;
}

int String::GetGrowBytes()
{
	return m_nGrowBytes;
}

void String::TrimRight(TCHAR chTarget)
{
	if(m_pBuffer)
		while(m_nLength && chTarget == m_pBuffer[m_nLength-1])
			m_pBuffer[--m_nLength] = 0;
}

void String::TrimRight()
{
	TrimRight(" \r\n\t");
}

void String::TrimRight(LPCTSTR lpszTargets)
{
	if(m_pBuffer)
		while(m_nLength && strchr(lpszTargets, m_pBuffer[m_nLength-1]) != NULL)
			m_pBuffer[--m_nLength] = 0;
}

void String::TrimLeft(TCHAR chTarget)
{
	if(m_pBuffer)
		while(m_nLength && chTarget == m_pBuffer[0])
			Delete(0);
}

void String::TrimLeft()
{
	TrimLeft(" \r\n\t");
}

void String::TrimLeft(LPCTSTR lpszTargets)
{
	if(m_pBuffer)
		while(m_nLength && strchr(lpszTargets, m_pBuffer[0]) != NULL)
			Delete(0);
}

#define TCHAR_ARG   TCHAR
#define WCHAR_ARG   WCHAR
#define CHAR_ARG    char
#define FORCE_ANSI      0x10000
#define FORCE_UNICODE   0x20000
#define FORCE_INT64     0x40000

void String::Format(LPCTSTR lpszFormat, ...)
{
	va_list argList;
	va_start(argList, lpszFormat);
	
	va_list argListSave = argList;
	// make a guess at the maximum length of the resulting string
	int nMaxLen = 0;
	for (LPCTSTR lpsz = lpszFormat; *lpsz != '\0'; lpsz = _tcsinc(lpsz))
	{
		// handle '%' character, but watch out for '%%'
		if (*lpsz != '%' || *(lpsz = _tcsinc(lpsz)) == '%')
		{
			nMaxLen += (int)_tclen(lpsz);
			continue;
		}

		int nItemLen = 0;

		// handle '%' character with format
		int nWidth = 0;
		for (; *lpsz != '\0'; lpsz = _tcsinc(lpsz))
		{
			// check for valid flags
			if (*lpsz == '#')
				nMaxLen += 2;   // for '0x'
			else if (*lpsz == '*')
				nWidth = va_arg(argList, int);
			else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0' ||
				*lpsz == ' ')
				;
			else // hit non-flag character
				break;
		}
		// get width and skip it
		if (nWidth == 0)
		{
			// width indicated by
			nWidth = _ttoi(lpsz);
			for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz))
				;
		}
//		ASSERT(nWidth >= 0);

		int nPrecision = 0;
		if (*lpsz == '.')
		{
			// skip past '.' separator (width.precision)
			lpsz = _tcsinc(lpsz);

			// get precision and skip it
			if (*lpsz == '*')
			{
				nPrecision = va_arg(argList, int);
				lpsz = _tcsinc(lpsz);
			}
			else
			{
				nPrecision = _ttoi(lpsz);
				for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz))
					;
			}
//			ASSERT(nPrecision >= 0);
		}

		// should be on type modifier or specifier
		int nModifier = 0;
		if (_tcsncmp(lpsz, _T("I64"), 3) == 0)
		{
			lpsz += 3;
			nModifier = FORCE_INT64;
#if !defined(_X86_) && !defined(_ALPHA_)
			// __int64 is only available on X86 and ALPHA platforms
//			ASSERT(FALSE);
#endif
		}
		else
		{
			switch (*lpsz)
			{
			// modifiers that affect size
			case 'h':
				nModifier = FORCE_ANSI;
				lpsz = _tcsinc(lpsz);
				break;
			case 'l':
				nModifier = FORCE_UNICODE;
				lpsz = _tcsinc(lpsz);
				break;

			// modifiers that do not affect size
			case 'F':
			case 'N':
			case 'L':
				lpsz = _tcsinc(lpsz);
				break;
			}
		}

		// now should be on specifier
		switch (*lpsz | nModifier)
		{
		// single characters
		case 'c':
		case 'C':
			nItemLen = 2;
			va_arg(argList, TCHAR_ARG);
			break;
		case 'c'|FORCE_ANSI:
		case 'C'|FORCE_ANSI:
			nItemLen = 2;
			va_arg(argList, CHAR_ARG);
			break;
		case 'c'|FORCE_UNICODE:
		case 'C'|FORCE_UNICODE:
			nItemLen = 2;
			va_arg(argList, WCHAR_ARG);
			break;

		// strings
		case 's':
			{
				LPCTSTR pstrNextArg = va_arg(argList, LPCTSTR);
				if (pstrNextArg == NULL)
				   nItemLen = 6;  // "(null)"
				else
				{
				   nItemLen = lstrlen(pstrNextArg);
				   nItemLen = max(1, nItemLen);
				}
			}
			break;

		case 'S':
			{
#ifndef _UNICODE
				LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
				if (pstrNextArg == NULL)
				   nItemLen = 6;  // "(null)"
				else
				{
				   nItemLen = (int)wcslen(pstrNextArg);
				   nItemLen = max(1, nItemLen);
				}
#else
				LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
				if (pstrNextArg == NULL)
				   nItemLen = 6; // "(null)"
				else
				{
				   nItemLen = lstrlenA(pstrNextArg);
				   nItemLen = max(1, nItemLen);
				}
#endif
			}
			break;

		case 's'|FORCE_ANSI:
		case 'S'|FORCE_ANSI:
			{
				LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
				if (pstrNextArg == NULL)
				   nItemLen = 6; // "(null)"
				else
				{
				   nItemLen = lstrlenA(pstrNextArg);
				   nItemLen = max(1, nItemLen);
				}
			}
			break;

		case 's'|FORCE_UNICODE:
		case 'S'|FORCE_UNICODE:
			{
				LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
				if (pstrNextArg == NULL)
				   nItemLen = 6; // "(null)"
				else
				{
				   nItemLen = (int)wcslen(pstrNextArg);
				   nItemLen = max(1, nItemLen);
				}
			}
			break;
		}

		// adjust nItemLen for strings
		if (nItemLen != 0)
		{
			if (nPrecision != 0)
				nItemLen = min(nItemLen, nPrecision);
			nItemLen = max(nItemLen, nWidth);
		}
		else
		{
			switch (*lpsz)
			{
			// integers
			case 'd':
			case 'i':
			case 'u':
			case 'x':
			case 'X':
			case 'o':
				if (nModifier & FORCE_INT64)
					va_arg(argList, __int64);
				else
					va_arg(argList, int);
				nItemLen = 32;
				nItemLen = max(nItemLen, nWidth+nPrecision);
				break;

			case 'e':
			case 'g':
			case 'G':
				va_arg(argList, double/*DOUBLE_ARG*/);
				nItemLen = 128;
				nItemLen = max(nItemLen, nWidth+nPrecision);
				break;

			case 'f':
				{
					double f;
					LPTSTR pszTemp;

					// 312 == strlen("-1+(309 zeroes).")
					// 309 zeroes == max precision of a double
					// 6 == adjustment in case precision is not specified,
					//   which means that the precision defaults to 6
					pszTemp = (LPTSTR)malloc(max(nWidth, 312+nPrecision+6));

					f = va_arg(argList, double);
					_stprintf( pszTemp, _T( "%*.*f" ), nWidth, nPrecision+6, f );
					nItemLen = (int)_tcslen(pszTemp);
					free(pszTemp);
				}
				break;

			case 'p':
				va_arg(argList, void*);
				nItemLen = 32;
				nItemLen = max(nItemLen, nWidth+nPrecision);
				break;

			// no output
			case 'n':
				va_arg(argList, int*);
				break;

//			default:
//				ASSERT(FALSE);  // unknown formatting option
			}
		}

		// adjust nMaxLen for output nItemLen
		nMaxLen += nItemLen;
	}

	_vstprintf(GetBuffer(nMaxLen), lpszFormat, argListSave);
	ReleaseBuffer();

	va_end(argListSave);

	va_end(argList);
}

LPTSTR String::GetBuffer(int nMinBufLength)
{
	if(nMinBufLength > (int)m_nLength)
		ReleaseBuffer(nMinBufLength);
	return (LPTSTR)m_pBuffer;
}

void String::AllocBuffer(int nAllocLength)
{
	if(nAllocLength <= m_nAllocLength)
		return;
	m_nAllocLength = nAllocLength;
	if(m_pBuffer == NULL)
		m_pBuffer = (char*)AllocPtr(m_nAllocLength);
	else
		m_pBuffer = (char*)ReAllocPtr(m_pBuffer, m_nAllocLength);
}

void String::ReleaseBuffer(int nNewLength /*= -1*/)
{
	if(nNewLength == -1)
		nNewLength = StrLen(m_pBuffer);
	if(nNewLength == 0)
		Empty();
	else	if(nNewLength >= m_nAllocLength)
		AllocBuffer(nNewLength+m_nGrowBytes+1);
	m_nLength = nNewLength;
	if(m_pBuffer)
		m_pBuffer[m_nLength] = 0;
}

void String::Empty()
{
	if(m_pBuffer)
	{
		FreePtr(m_pBuffer);
		m_pBuffer = NULL;
	}
	m_nAllocLength = 0;
	m_nLength = 0;
}


const String& String::Assign(char* pBuffer, int nLength)
{
	if(pBuffer && nLength > 0)
	{
		ReleaseBuffer(nLength);
		memcpy(m_pBuffer, pBuffer, nLength);
	}
	else
		Empty();
	return *this;
}

void String::Attach(char* pBuffer, int nLength)
{
	Empty();
	m_pBuffer = pBuffer;
	m_nLength = nLength;
	m_nAllocLength = nLength;
}

char *String::Detach()
{
	char *pBuffer = m_pBuffer;
	m_pBuffer = NULL;
	Empty();
	return pBuffer;
}

//////////////////////////////////////////////////////////////////////////////
// Assignment String::operators

const String& String::operator=(const String& string)
{
	LPCSTR lpcs = string;
	return Assign((char*)lpcs, string.GetLength());
}

const String& String::operator=(LPCTSTR lpsz)
{
	return Assign((char*)lpsz, StrLen(lpsz));
}

const String& String::operator=(LPSTR lpsz)
{
	return Assign(lpsz, StrLen(lpsz));
}

const String& String::operator=(int n)
{
	return Assign<int>(n);
}

const String& String::operator=(short s)
{
	return Assign<short>(s);
}

const String& String::operator=(double d)
{
	return Assign<double>(d);
}

const String& String::operator=(float f)
{
	return Assign<float>(f);
}

const String& String::operator=(char c)
{
	return Assign<char>(c);
}
//////////////////////////////////////////////////////////////////////////////
// Concatenation String::operators

const String& String::Append(char* pBuffer, int nLength)
{
	int nCurLength = m_nLength;
	ReleaseBuffer(m_nLength+nLength);
	memcpy(m_pBuffer+nCurLength, pBuffer, nLength);
	return *this;
}

const String& String::operator+=(const String& string)
{
	LPCSTR lpcs = string;
	return Append((char*)lpcs, string.GetLength());
}
const String& String::operator+=(LPCTSTR lpsz)
{
	return Append((char*)lpsz, StrLen(lpsz));
}
const String& String::operator+=(LPTSTR lpsz)
{
	return Append((char*)lpsz, StrLen(lpsz));
}

const String& String::operator+=(const unsigned char* lpsz)
{
	return Append((char*)lpsz, StrLen((char*)lpsz));
}

const String& String::operator+=(int n)
{
	return Append<int>(n);
}

const String& String::operator+=(short s)
{
	return Append<short>(s);
}

const String& String::operator+=(double d)
{
	return Append<double>(d);
}

const String& String::operator+=(float f)
{
	return Append<float>(f);
}

const String& String::operator+=(char c)
{
	return Append<char>(c);
}

///////////////////////////////////////////////////////////////////////////////
// Advanced direct buffer access

LPTSTR String::GetBufferSetLength(int nNewLength)
{
	ReleaseBuffer(nNewLength);
	return (LPTSTR)m_pBuffer;
}

void String::FreeExtra()
{
}

///////////////////////////////////////////////////////////////////////////////
// Commonly used routines (rarely used routines in STREX.CPP)

int String::Find(TCHAR ch) const
{
	return Find(ch, 0);
}

int String::Find(TCHAR ch, int nStart) const
{
	if(m_pBuffer == NULL)
		return -1;
	LPTSTR lpsz = strchr(m_pBuffer+nStart, ch);
	return (lpsz == NULL) ? -1 : (int)(lpsz - m_pBuffer);
}

int String::FindOneOf(LPCTSTR lpszCharSet) const
{
	if(m_pBuffer == NULL)
		return -1;
	LPTSTR lpsz = _tcspbrk(m_pBuffer, lpszCharSet);
	return (lpsz == NULL) ? -1 : (int)(lpsz - m_pBuffer);
}

void String::MakeUpper()
{
	if(m_pBuffer == NULL)
		return;
	_tcsupr((LPTSTR)m_pBuffer);
}

void String::MakeLower()
{
	if(m_pBuffer == NULL)
		return;
	_tcslwr((LPTSTR)m_pBuffer);
}

void String::MakeReverse()
{
	if(m_pBuffer == NULL)
		return;
	_tcsrev((LPTSTR)m_pBuffer);
}

void String::SetAt(int nIndex, TCHAR ch)
{
	if(nIndex >= m_nLength)
        GetBuffer(nIndex+1);
	m_pBuffer[nIndex] = ch;
}

//////////////////////////////////////////////////////////////////////////////
// Advanced manipulation

int String::Delete(int nIndex, int nCount /* = 1 */)
{
	if (nIndex < 0)
		nIndex = 0;
	int nNewLength = m_nLength;
	if (nCount > 0 && nIndex < nNewLength)
	{
		int nBytesToCopy = nNewLength - (nIndex + nCount) + 1;

		memcpy((char*)m_pBuffer + nIndex,
			m_pBuffer + nIndex + nCount, nBytesToCopy * sizeof(TCHAR));
		ReleaseBuffer(nNewLength - nCount);
	}

	return nNewLength;
}

int String::Insert(int nIndex, char* pBuffer, int nLength)
{
	if(nIndex > m_nLength || nLength <= 0)
		return m_nLength;
	int nCurLength = m_nLength;
	ReleaseBuffer(m_nLength+nLength);
	memmove(m_pBuffer+nIndex+nLength, m_pBuffer+nIndex, nCurLength-nIndex);
	memcpy(m_pBuffer+nIndex, pBuffer, nLength);
	return m_nLength;
}

int String::Replace(TCHAR chOld, TCHAR chNew)
{
	int nCount = 0;

	// short-circuit the nop case
	if (chOld != chNew)
	{
		// otherwise modify each character that matches in the string
		LPTSTR psz = (char*)m_pBuffer;
		LPTSTR pszEnd = psz + m_nLength;
		while (psz < pszEnd)
		{
			// replace instances of the specified character only
			if (*psz == chOld)
			{
				*psz = chNew;
				nCount++;
			}
			psz = _tcsinc(psz);
		}
	}
	return nCount;
}

int String::Replace(LPCTSTR lpszOld, LPCTSTR lpszNew)
{
	int nPos, nCount = 0;
	char *p;
	while((p = strstr(m_pBuffer, lpszOld)) != NULL)
	{
		nPos = (int)(p-m_pBuffer);
		Delete(nPos, (int)strlen(lpszOld));
		Insert(nPos, lpszNew);
		nCount++;
	}
	return nCount;
}

int String::Remove(TCHAR chRemove)
{
	int nLength = m_nLength;
	LPTSTR pstrSource = (char*)m_pBuffer;
	LPTSTR pstrDest = (char*)m_pBuffer;
	LPTSTR pstrEnd = (char*)m_pBuffer + nLength;

	while (pstrSource < pstrEnd)
	{
		if (*pstrSource != chRemove)
		{
			*pstrDest = *pstrSource;
			pstrDest = _tcsinc(pstrDest);
		}
		pstrSource = _tcsinc(pstrSource);
	}
	*pstrDest = '\0';
	int nCount = (int)(pstrSource - pstrDest);
	ReleaseBuffer(nLength-nCount);

	return nCount;
}

//////////////////////////////////////////////////////////////////////////////
// Very simple sub-string extraction

String String::Mid(int nFirst) const
{
	return Mid(nFirst, m_nLength - nFirst);
}

String String::Mid(int nFirst, int nCount) const
{
	String s;
	memcpy(s.GetBuffer(nCount), m_pBuffer+nFirst, nCount);
	s.ReleaseBuffer(nCount);
	return s;
}

String String::Right(int nCount) const
{
	String s;
	memcpy(s.GetBuffer(nCount), m_pBuffer+m_nLength-nCount, nCount);
	s.ReleaseBuffer(nCount);
	return s;
}

String String::Left(int nCount) const
{
	String s;
	memcpy(s.GetBuffer(nCount), m_pBuffer, nCount);
	s.ReleaseBuffer(nCount);
	return s;
}

// strspn equivalent
String String::SpanIncluding(LPCTSTR lpszCharSet) const
{
	return Left((int)_tcsspn(m_pBuffer, lpszCharSet));
}

// strcspn equivalent
String String::SpanExcluding(LPCTSTR lpszCharSet) const
{
	return Left((int)_tcscspn(m_pBuffer, lpszCharSet));
}

//////////////////////////////////////////////////////////////////////////////
// Finding

int String::ReverseFind(TCHAR ch) const
{
	if(m_pBuffer == NULL)
		return -1;
	const BYTE* p = (const BYTE*)m_pBuffer;
	BYTE* lpsz = _mbsrchr(p, ch);
	return (lpsz == NULL) ? -1 : (int)(lpsz - p);
}

// find a sub-string (like strstr)
int String::Find(LPCTSTR lpszSub) const
{
	return Find(lpszSub, 0);
}

int String::Find(LPCTSTR lpszSub, int nStart) const
{
	if(m_pBuffer == NULL)
		return -1;
	LPTSTR lpsz = strstr(m_pBuffer+nStart, lpszSub);
	return (lpsz == NULL) ? -1 : (int)(lpsz - m_pBuffer);
}

// String
const String& String::operator=(const unsigned char* lpsz)
	{ *this = (LPCSTR)lpsz; return *this; }

int String::GetLength() const
	{ return m_nLength; }
int String::GetAllocLength() const
	{ return m_nAllocLength; }
BOOL String::IsEmpty() const
	{ return m_nLength == 0; }
String::operator LPCTSTR() const
	{ return (LPCTSTR)m_pBuffer; }

// String support (windows specific)
int String::Compare(LPCTSTR lpsz) const
	{ return _tcscmp(m_pBuffer, lpsz); }    // MBCS/Unicode aware
int String::CompareNoCase(LPCTSTR lpsz) const
	{ return _tcsicmp(m_pBuffer, lpsz); }   // MBCS/Unicode aware

int String::Compare(BYTE *pBuffer, int nLength)
{
	int nResult = memcmp(m_pBuffer, pBuffer, min(m_nLength, nLength));
	if(nResult != 0 || m_nLength == nLength)
		return nResult;
	return m_nLength > nLength ? 1 : -1;
}
// String::Collate is often slower than Compare but is MBSC/Unicode
//  aware as well as locale-sensitive with respect to sort order.
int String::Collate(LPCTSTR lpsz) const
	{ return _tcscoll(m_pBuffer, lpsz); }   // locale sensitive
int String::CollateNoCase(LPCTSTR lpsz) const
	{ return _tcsicoll(m_pBuffer, lpsz); }   // locale sensitive

TCHAR String::GetAt(int nIndex) const
{
	return m_pBuffer[nIndex];
}
TCHAR String::operator[](int nIndex) const
{
	return m_pBuffer[nIndex];
}

String operator+(const String& string1, const String& string2)
{
	String s = string1;
	s += string2;
	return s;
}

String operator+(const String& string, char ch)
{
	String s = string;
	s += ch;
	return s;
}

String operator+(char ch, const String& string)
{
	String s = ch;
	s += string;
	return s;
}


String operator+(const String& string, short s)
{
	String str = string;
	str += s;
	return str;
}

String operator+(short s, const String& string)
{
	String str;
	str = s;
	str += string;
	return str;
}

String operator+(const String& string, LPCTSTR lpsz)
{
	String s = string;
	s += lpsz;
	return s;
}

String operator+(LPCTSTR lpsz, const String& string)
{
	String s = (LPCSTR)lpsz;
	s += string;
	return s;
}

bool operator==(const String& s1, const String& s2)
{
	return s1.GetLength() == s2.GetLength() && memcmp(s1, s2, s1.GetLength()) == 0;
}

bool operator==(const String& s1, LPCTSTR s2)
{
	return s1.GetLength() == StrLen(s2) && memcmp(s1, s2, s1.GetLength()) == 0;
}

bool operator==(LPCTSTR s1, const String& s2)
{
	return s2.GetLength() == StrLen(s1) && memcmp(s2, s1, s2.GetLength()) == 0;
}

bool operator!=(const String& s1, const String& s2)
{
	return s1.GetLength() != s2.GetLength() || memcmp(s1, s2, s1.GetLength()) != 0;
}

bool operator!=(const String& s1, LPCTSTR s2)
{
	return s1.GetLength() != StrLen(s2) || memcmp(s1, s2, s1.GetLength()) != 0;
}

bool operator!=(LPCTSTR s1, const String& s2)
{
	return s2.GetLength() != StrLen(s1) || memcmp(s2, s1, s2.GetLength()) != 0;
}

bool operator<(const String& s1, const String& s2)
{
	return memcmp(s1, s2, min(s1.GetLength(), s2.GetLength())) < 0;
}

bool operator<(const String& s1, LPCTSTR s2)
{
	return memcmp(s1, s2, min(s1.GetLength(), (int)StrLen(s2))) < 0;
}

bool operator<(LPCTSTR s1, const String& s2)
{
	return memcmp(s1, s2, min((int)StrLen(s1), s2.GetLength())) < 0;
}

bool operator>(const String& s1, const String& s2)
{
	return memcmp(s1, s2, min(s1.GetLength(), s2.GetLength())) > 0;
}

bool operator>(const String& s1, LPCTSTR s2)
{
	return memcmp(s1, s2, min(s1.GetLength(), (int)StrLen(s2))) > 0;
}

bool operator>(LPCTSTR s1, const String& s2)
{
	return memcmp(s1, s2, min((int)StrLen(s1), s2.GetLength())) > 0;
}

bool operator<=(const String& s1, const String& s2)
{
	return memcmp(s1, s2, min(s1.GetLength(), (int)StrLen(s2))) <= 0;
}

bool operator<=(const String& s1, LPCTSTR s2)
{
	return memcmp(s1, s2, min(s1.GetLength(), (int)StrLen(s2))) <= 0;
}

bool operator<=(LPCTSTR s1, const String& s2)
{
	return memcmp(s1, s2, min((int)StrLen(s1), s2.GetLength())) <= 0;
}

bool operator>=(const String& s1, const String& s2)
{
	return memcmp(s1, s2, min(s1.GetLength(), s2.GetLength())) >= 0;
}

bool operator>=(const String& s1, LPCTSTR s2)
{
	return memcmp(s1, s2, min(s1.GetLength(), (int)StrLen(s2))) >= 0;
}

bool operator>=(LPCTSTR s1, const String& s2)
{
	return memcmp(s1, s2, min((int)StrLen(s1), s2.GetLength())) >= 0;
}

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
Software Developer (Senior)
Egypt Egypt

Comments and Discussions