/*
	$Id: CString.cpp,v 1.2 2003/08/14 04:09:52 dasenbro Exp $

	File:	CString.cpp

	Contains: Implementation of a Reasonable string class

	Version:	Apple Mail Server - Mac OS X :  $Revision: 1.2 $

	Written by: Claris Corporation

	Copyright:	© 1996-2001 by Apple Computer, Inc., all rights reserved.

 	NOT_FOR_OPEN_SOURCE <to be reevaluated at a later time>

	Change History:

		$Log: CString.cpp,v $
		Revision 1.2  2003/08/14 04:09:52  dasenbro
		Removed code to strip off \r\n form migrated messages.
		
		Revision 1.1  2003/04/20 23:29:40  dasenbro
		Initial check-in.
		
		Revision 1.20  2002/06/07 22:40:46  dasenbro
		Added GetDataLength() MemSet() to handle data blocks with imbedded NULL's.
		
		Revision 1.19  2002/05/09 16:50:23  dasenbro
		hanged all str... calls to CUtils::Str... to be NULL safe.
		
		Revision 1.17  2002/04/15 21:30:57  dasenbro
		Syntax changes for better readability.
		
		Revision 1.16  2002/03/21 16:41:47  dasenbro
		Updated file version information.
		
		Revision 1.15  2002/03/05 20:50:23  dasenbro
		Fixed CString::Append() to append to the end of the string.
		
		Revision 1.14  2002/02/20 20:23:36  dasenbro
		gcc3 syntax changes.
		
		Revision 1.13  2002/01/14 17:04:09  dasenbro
		Initial S4 updates.
		
		Revision 1.12  2001/06/27 22:03:30  dasenbro
		Removed some dead code.
		
		Revision 1.11  2001/06/21 20:51:11  dasenbro
		Updated file header info.
		
		Revision 1.10  2001/06/21 20:10:47  dasenbro
		Added Change History.
		

	Projector History:

		 <9>	 7/30/98	cpj		Complete optimization rewrite.
		 <8>	  6/8/98	DOR		Make it so that "Set" doesn't allow you to set strings with a
									negative length..
		 <7>	 4/24/98	DOR		Make sprinf work correctly!!
		 <6>	 4/10/98	DOR		Clear the string at the top of the sprintf
		 <5>	  4/8/98	MED		Made ::Sprintf's buffer safe from being overwritten.
		 <4>	 2/23/98	MED		Fixed the delete for an array of chars.
		 <3>	 2/18/98	MED		changed an array delete in the destructor.
		 <2>	10/16/97	MED		Append (char inChar ) to call Append(...

	To Do:
 */


// MacOS
#include <string.h>
#include <stdio.h>		// for sprintf()
#include <stdlib.h>		// for calloc and free
#include <stdarg.h>

// App
#include "CString.h"
#include "CStatStr.h"
#include "UException.h"
#include "CUtils.h"


// ----------------------------------------------------------------------------
//	* CString Instance Methods
// ----------------------------------------------------------------------------

#pragma mark **** CString Public Instance Methods ****

// ctor & dtor
// Unless an exception is thrown, the ctors must always call Grow() so mData
// is always valid.


// ----------------------------------------------------------------------------
// * CString
// ----------------------------------------------------------------------------

CString::CString ( int sz )
	: mSize( 0 ), mLength( 0 ), mData( nil )
{
	Grow( sz );
}


// ----------------------------------------------------------------------------
// * CString
// ----------------------------------------------------------------------------

CString::CString ( const char *str )
	: mSize( 0 ), mLength( 0 ), mData( nil )
{
	// let's not throw if NULL string...
	//ThrowIfNULL_(str);

	// Set will not call Grow() if passed an empty string.
	if ( (str != NULL) && *str )
	{
		Set( str );
	}
	else
	{
		Grow();
	}
} // 


// ----------------------------------------------------------------------------
// * CString
// ----------------------------------------------------------------------------

CString::CString ( const char *str, int len )
	: mSize( 0 ), mLength( 0 ), mData( nil )
{
	// Set will not call Grow() if passed an empty string.
	if ((str != NULL) && *str && len)
	{
		Set (str, len);
	}
	else
	{
		Grow();
	}
}


// ----------------------------------------------------------------------------
// * CString
// ----------------------------------------------------------------------------

CString::CString ( const CString& cs )
	: mSize( 0 ), mLength( 0 ), mData( nil )
{
	// CString should always have a valid size.
	ThrowIf_ (cs.mSize <= 0);

	Grow( cs.mSize );
	if (!cs.mLength)
		return;
	mLength = cs.mLength;
	CUtils::Strcpy (mData, cs.mData);
}


// ----------------------------------------------------------------------------
// * CString
// ----------------------------------------------------------------------------

CString::CString ( int resID, int index )
	: mSize( 0 ), mLength( 0 ), mData( nil )
{
	Set( resID, index );
}


// ----------------------------------------------------------------------------
// * CString
// ----------------------------------------------------------------------------

CString::CString ( const char *pattern, va_list args )
	: mSize( 0 ), mLength( 0 ), mData( nil )
{
	// let's not throw if NULL string...
	ThrowIfNULL_ (pattern);

	// Allocate some space then vsprintf into the buffer.
	Grow();
	Vsprintf (pattern, args);
}


// ----------------------------------------------------------------------------
// * CString
// ----------------------------------------------------------------------------

CString::CString ( int resID, int index, va_list args )
	: mSize( 0 ), mLength( 0 ), mData( nil )
{
	CString	csPattern (resID, index);
	// Allocate some space then vsprintf into the buffer.
	Grow();
	Vsprintf (csPattern, args);
}


// ----------------------------------------------------------------------------
// * ~CString
// ----------------------------------------------------------------------------

CString::~CString()
{
	if ( mData != nil )
	{
		free( mData );
		mData	= nil;
		mLength	= 0;
		mSize	= 0;
	}
}


// ----------------------------------------------------------------------------
// * GetLength ()
// ----------------------------------------------------------------------------

int CString::GetLength ( void ) const
{
	if ( mData != NULL )
	{
//		return( mLength );
		return( CUtils::Strlen( mData ) );
	}
	else
	{
		return( 0 );
	}
} // GetLength


// ----------------------------------------------------------------------------
// * GetDataLength ()
// ----------------------------------------------------------------------------

int CString::GetDataLength ( void ) const
{
	if ( mData != NULL )
	{
		return( mLength );
	}
	else
	{
		return( 0 );
	}
} // GetDataLength


// ----------------------------------------------------------------------------
// * GetPascal - pascal string conversion
// ----------------------------------------------------------------------------

void CString::GetPascal (unsigned char *pstr) const
{
	ThrowIfNULL_ (pstr);
	ThrowIf_ (mLength > 255);
	*pstr++ = (unsigned char) mLength;
	::memcpy (pstr, mData, mLength);
}


// ----------------------------------------------------------------------------
// * Set - string replacement routines (also called by ctors)
// ----------------------------------------------------------------------------

void CString::Set ( const char *str )
{
	ThrowIfNULL_ (str);

	// Clear mLength to avoid a copy during the grow.
	mLength = 0;

	// Handle the corner cases.
	if (!*str)
	{
		*mData = '\0';
		return;
	}

	register int	len = CUtils::Strlen (str);

	Grow( len + 1 );
	strcpy (mData, str);
	mLength = len;
}


// ----------------------------------------------------------------------------
// * Set - string replacement routines (also called by ctors)
// ----------------------------------------------------------------------------

void CString::Set ( const char *str, int len )
{
	ThrowIfNULL_ (str);
	ThrowIf_ (len < 0);

	mLength = 0;

	// Handle the corner cases.
	if (!len || !*str)
	{
		*mData = '\0';
		return;
	}

	Grow( len + 1 );

	register int	strLen = CUtils::Strlen (str);
	mLength = ((strLen < len) ? strLen : len);
	::memcpy (mData, str, mLength);
	mData[mLength] = '\0';
}


// ----------------------------------------------------------------------------
// * Set - string replacement routines (also called by ctors)
// ----------------------------------------------------------------------------

void CString::Set ( int inListID, int inIndex )
{
	uInt32			len		= 0;
	const char	   *pStr	= NULL;

	pStr = CStatStr::GetStringFromList( inListID, inIndex );
	if ( pStr != nil )
	{
		// Get the length of the new string
		len = CUtils::Strlen( pStr );

		// Grow the buffer if we need to
		Grow( len + 1 );

		// Null out the current buffer
		::memset( mData, 0, mSize );

		// Copy in the new string
		CUtils::Strcpy( mData, pStr );

		// Set the length
		mLength = CUtils::Strlen( mData );
	}
}


// ----------------------------------------------------------------------------
// * Set - string replacement routines (also called by ctors)
// ----------------------------------------------------------------------------

void CString::Set (const unsigned char *pstr)
{
	ThrowIfNULL_ (pstr);

	register int	len = (int) *pstr++;

	mLength = 0;

	// Handle the corner case.
	if (!len)
	{
		*mData = '\0';
		return;
	}

	Grow( len + 1 );
	::memcpy (mData, pstr, len);
	mLength = len;
	mData[len] = '\0';
}


// ----------------------------------------------------------------------------
// * Set - string replacement routines (also called by ctors)
// ----------------------------------------------------------------------------

void CString::Set (const CString &cs)
{
	register int	len = cs.mLength;

	mLength = 0;

	// Handle the corner case.
	if (!len)
	{
		*mData = '\0';
		return;
	}

	Grow( len + 1 );
	// strcpy will add the terminator.
	CUtils::Strcpy (mData, cs.mData);
	mLength = len;
} // Set


// ----------------------------------------------------------------------------
//	* MemSet ()
//
//		 - set string
//
// ----------------------------------------------------------------------------

void CString::MemSet ( const char *inStr, int inLen )
{
	ThrowIfNULL_( inStr );
	ThrowIf_( inLen < 0 );

	mLength = 0;

	// Handle the corner cases.
	if ( !inLen || !*inStr )
	{
		*mData = '\0';
		return;
	}

	Grow( inLen + 1 );

	mLength = inLen;
	::memcpy( mData, inStr, mLength );

	mData[ mLength ] = '\0';
} // MemSet


// ----------------------------------------------------------------------------
// * Append
// ----------------------------------------------------------------------------

void CString::Append (char inChar)
{
	Grow( mLength + 2 );
	// Append the char and the terminator.
	mData[mLength++] = inChar;
	mData[mLength] = '\0';
}


// ----------------------------------------------------------------------------
// * Append
// ----------------------------------------------------------------------------

void CString::Append ( const char *str )
{
	ThrowIfNULL_ (str);
	// Handle the corner case.
	if ( !*str )
		return;

	register int	len = CUtils::Strlen( str );
	Grow( mLength + len + 1 );
	// strcpy will add the terminator.
	CUtils::Strcpy( &mData[mLength], str );
	mLength += len;
}


// ----------------------------------------------------------------------------
// * Append
// ----------------------------------------------------------------------------

void CString::Append ( const char *inStr, const int inLen )
{
	ThrowIfNULL_( inStr );
	ThrowIf_ (inLen < 0);

	// Handle the corner cases.
	if ( !inLen || !*inStr )
		return;

	register int len = CUtils::Strlen( inStr );
	if ( inLen < len )
	{
		len = inLen;
	}

	Grow( mLength + len + 1 );
//	::memcpy( &mData[ mLength ], str, len );
	::strncat( mData, inStr, inLen );
	mLength += len;
	mData[ mLength ] = '\0';
}


// ----------------------------------------------------------------------------
// * Append
// ----------------------------------------------------------------------------

void CString::Append ( const unsigned char *pstr )
{
	ThrowIfNULL_ (pstr);

	register int	len = (int) *pstr++;

	// Handle the corner case.
	if (!len)
		return;

	Grow( mLength + len + 1 );
	::memcpy (&mData[mLength], pstr, len);
	mLength += len;
	mData[mLength] = '\0';
}


// ----------------------------------------------------------------------------
// * Append
// ----------------------------------------------------------------------------

void CString::Append (const CString &cs)
{
	register int	len = cs.mLength;

	// Handle the corner case.
	if (!len)
		return;

	Grow( mLength + len + 1 );
	// strcpy will add the terminator.
	CUtils::Strcpy (&mData[mLength], cs.mData);
	mLength += len;
} // Append



// ----------------------------------------------------------------------------
// * Prepend
//
//		- Prepending is an expensive operation because the string must be copied
//			completely. To avoid multiple moves, this function -- and this
//			function alone -- duplicates the memory management in Grow().
// ----------------------------------------------------------------------------

void CString::Prepend (const char *str)
{
	ThrowIfNULL_ (str);

	// Handle the corner case.
	if (!*str)
		return;
	// Optimization when prepending to an empty string.
	if (!mLength)
	{
		Append (str);
		return;
	}

	register int	len = CUtils::Strlen (str);
	register int	newlen = mLength + len + 1;

	// Handle the easy case first: allocate a new block and copy into it.
	if ( newlen > mSize )
	{
		register int	pow2 = 16;
		while (pow2 < newlen)
		{
			pow2 <<= 1;
		}
		register char	*newData = (char *)::calloc( pow2, sizeof( char ) );
		ThrowIfNULL_( newData );
		CUtils::Strcpy( newData, str );
		CUtils::Strcpy( &newData[ len ], mData );

		free( mData );
		mData = nil;

		mSize = pow2;
		mLength += len;
		mData = newData;
	}
	else
	{
		register char	*cpNew = &mData[--newlen];
		register char	*cpOld = &mData[(newlen = mLength)];
		// Copy the string backwards, starting with the terminator.
		for (newlen++; newlen--; )
			*cpNew-- = *cpOld--;
		// Copy the prepended string; using memcpy() to avoid the terminator.
		::memcpy (mData, str, len);
		mLength += len;
	}
} // Prepend


// ----------------------------------------------------------------------------
// * Clear - nulls the string and sets the size to inNewSize
// ----------------------------------------------------------------------------

void CString::Clear ( int inNewSize )
{
	if ( mData != nil )
	{
		*mData = '\0';
		mLength = 0;
	
		if ( inNewSize )
		{
			Grow( inNewSize );
		}
		::memset( mData, 0, mSize );
	}
}


// ----------------------------------------------------------------------------
//	* Sprintf ()
// ----------------------------------------------------------------------------

void CString::Sprintf ( const char *pattern, ... )
{
	va_list	args;
	va_start (args, pattern);
	this->Vsprintf (pattern, args);

} // Sprintf w/pattern


// ----------------------------------------------------------------------------
//	* Sprintf ()
// ----------------------------------------------------------------------------

void CString::Sprintf ( int resID, int index, ... )
{
	va_list	args;
	va_start (args, index);

	CString	csPattern( resID, index );
	this->Vsprintf( csPattern, args );

} // Sprintf w/resID & index


// ----------------------------------------------------------------------------
// * Vsprintf
// ----------------------------------------------------------------------------

void CString::Vsprintf ( const char *pattern, va_list args )
{
	// Use a temp buffer big enough to hold one 64-bit value more than
	// the default size.
	char			caTemp[ kCStringDefSize + 32 ];
	register char	*cpTemp = caTemp;
	uInt32			ulArg	= 0;
	sInt32			lArg	= 0;
	uInt64			ullArg	= 0;
	sInt64			llArg	= 0;
	int				nArg	= 0;
	char		   *szpArg	= nil;
	StringPtr		spArg;
	FourCharCode	fccArg;
	CString			*cspArg;

	this->Clear();
	
	for ( register const char *p = pattern; *p; p++ )
	{
		// The threshold minimizes the calls to Grow() and is arbitrary.
		if ( (cpTemp - caTemp) > kCStringDefSize )
		{
			*cpTemp = '\0';
			Append( caTemp );
			cpTemp = caTemp;
		}
		if ( *p != '%' )
		{
			*cpTemp++ = *p;
		}
		else
		{
			switch ( *++p )
			{
				// non-standard (A = IP Address)!
				case 'A':
					ulArg = va_arg (args, uInt32);
					cpTemp += ::sprintf ( cpTemp, "%ld.%ld.%ld.%ld",
											((ulArg >> 24) & 0xFF),
											((ulArg >> 16) & 0xFF),
											((ulArg >>  8) & 0xFF),
											(ulArg & 0xFF) );
					break;

				// non-standard (F = FourCharCode)!
				case 'F':
					fccArg = va_arg (args, FourCharCode);
					*cpTemp++ = '\'';
					*cpTemp++ = ((fccArg >> 24) & 0xFF);
					*cpTemp++ = ((fccArg >> 16) & 0xFF);
					*cpTemp++ = ((fccArg >>  8) & 0xFF);
					*cpTemp++ = ( fccArg        & 0xFF);
					*cpTemp++ = '\'';
					break;

				// non-standard (P = Pascal string)!
				case 'P':
				case 'p':	// lower-case 'p' is deprecated usage
					spArg = va_arg (args, StringPtr);
					if (cpTemp != caTemp)
					{
						*cpTemp = '\0';
						Append (caTemp);
						cpTemp = caTemp;
					}
					Append (spArg);
					break;

				// non-standard (S = CString)!
				case 'S':
					cspArg = va_arg (args, CString *);
					if (cpTemp != caTemp)
					{
						*cpTemp = '\0';
						Append (caTemp);
						cpTemp = caTemp;
					}
					Append (*cspArg);
					break;

				// non-standard (long expected)!
				case 'X':
					ulArg = va_arg( args, uInt32 );
					cpTemp += ::sprintf( cpTemp, "0x%08lX", ulArg );
					break;

				// non-standard (long expected)!
				case 'u':
					ulArg = va_arg (args, uInt32);
					cpTemp += ::sprintf( cpTemp, "%lu", ulArg);
					break;

				// non-standard (unsigned double expected)!
				case 'U':
					ullArg = va_arg( args, uInt64 );
					cpTemp += ::sprintf( cpTemp, "%llu", ullArg );
					break;

				// non-standard (not used as modifier)!
				case 'l':
					lArg = va_arg (args, sInt32);
					cpTemp += ::sprintf( cpTemp, "%ld", lArg);
					break;

				// non-standard (double expected)!
				case 'L':
					llArg = va_arg( args, sInt64 );
					cpTemp += ::sprintf( cpTemp, "%lld", llArg );
					break;

				case 'c':
					*cpTemp++ = va_arg( args, int );
//					*cpTemp++ = va_arg( args, char );
					break;

				case 'd':
					nArg = va_arg( args, int );
					cpTemp += ::sprintf( cpTemp, "%d", nArg );
					break;

				case 's':
					szpArg = va_arg( args, char * );
					if ( cpTemp != caTemp )
					{
						*cpTemp = '\0';
						Append( caTemp );
						cpTemp = caTemp;
					}
					Append( szpArg );
					break;

				default:
					Throw_( CString::kUnknownEscapeErr );
			}
		}
	}

	if (cpTemp != caTemp)
	{
		*cpTemp = '\0';
		Append (caTemp);
	}
	va_end (args);
} // Vsprintf


#pragma mark **** CString Protected Instance Methods ****

// ----------------------------------------------------------------------------
// * Grow
//	All memory management (even in the c'tors) are handled in this method.
//	A newSize value of 0 implies the use of the default buffer size!
//	To leave the buffer alone, call with an argument value of 1.
//
//	IMPORTANT NOTE: Any changes to Grow() should be reflected in Prepend()
//	because it performs memory management for efficiency.
// ----------------------------------------------------------------------------

void CString::Grow ( int newSize )
{
	ThrowIf_( newSize < 0 );

	// Allocate the default length if requested.
	if ( !newSize )
	{
		newSize = kCStringDefSize;
	}

	// Don't bother reallocating if there is already enough room.
	if ( newSize <= mSize )
	{
		return;
	}

	// Round the requested size to the nearest power of two (> 16).
	// The comparison is an optimization for the most common case.
	if ( newSize != kCStringDefSize )
	{
		register int	pow2 = 16;
		while (pow2 < newSize)
			pow2 <<= 1;
		newSize = pow2;
	}

//	register char	*newData = new char[ newSize ];
	register char	*newData = (char *)::calloc( newSize, sizeof( char ) );
	ThrowIfNULL_( newData );

	if ( mLength )
	{
		CUtils::Strcpy (newData, mData);
	}
	else
	{
		*newData = '\0';
	}

	if ( mData != NULL )
	{
		free( mData );
		mData = nil;
	}

	mSize = newSize;
	mData = newData;
} // Grow

#pragma mark **** Static Methods ****

void CString::FreeStringList ( StrListObj **inStrList )
{
	StrListObj	*pTmpObj	= nil;
	StrListObj	*pNextObj	= nil;

	if ( *inStrList != nil )
	{
		pTmpObj = *inStrList;
		*inStrList = nil;

		while( pTmpObj != nil )
		{
			if ( pTmpObj->fString != nil )
			{
				free( pTmpObj->fString );
				pTmpObj->fString = nil;
			}

			// Get the next object
			pNextObj = pTmpObj->fNext;

			// Free the current object
			free( pTmpObj );

			// Move to the next object
			pTmpObj = pNextObj;
			pNextObj = nil;
		}
	}
} // FreeStringList


void CString::DuplicateStringList ( StrListObj **inDestList, StrListObj *inSrcList )
{
	sInt32		siResult	= kNoErr;
	StrListObj	*pSrcObj	= nil;
	StrListObj	*pDestObj	= nil;

	if ( (inDestList != nil) && (inSrcList != nil) )
	{
		pSrcObj = inSrcList;

		while( (pSrcObj != nil) && (siResult == kNoErr) )
		{
			if ( pSrcObj->fString != nil )
			{
				// Allocate the string object
				if ( pDestObj == nil )
				{
					pDestObj = (StrListObj *)::malloc( sizeof( StrListObj ) );
					*inDestList = pDestObj;
				}
				else
				{
					pDestObj->fNext = (StrListObj *)::malloc( sizeof( StrListObj ) );
					pDestObj = pDestObj->fNext;
				}

				if ( pDestObj != nil )
				{
					// Clear object
					pDestObj->fString = nil;
					pDestObj->fNext = nil;

					// Allocate the string
					pDestObj->fString = (char *)::malloc( CUtils::Strlen( pSrcObj->fString ) + 2 );
					if ( pDestObj->fString == nil )
					{
						CString::FreeStringList( inDestList );
						*inDestList = nil;
						pDestObj = nil;

						siResult = -1;
						break;
					}
					CUtils::Strcpy( pDestObj->fString, pSrcObj->fString );
				}
				else
				{
					siResult = -1;
					break;
				}

			}

			// Get the next object
			pSrcObj = pSrcObj->fNext;
		}
	}
} // DuplicateStringList


void CString::AppendString ( StrListObj **inStrList, const char *inStr, unsigned long inStrLen )
{
	long			siResult	= kNoErr;
	unsigned long	uiLen		= inStrLen;
	StrListObj		*pStrObj	= nil;
	StrListObj		*pStrNext	= nil;

	if ( inStr != nil )
	{
		if ( uiLen == 0 )
		{
			uiLen = CUtils::Strlen( inStr );
		}

		pStrObj = (StrListObj *)::malloc( sizeof( StrListObj ) );
		if ( pStrObj != nil )
		{
			pStrObj->fNext = nil;
			pStrObj->fString = (char *)::malloc( uiLen + 2 );
			if ( pStrObj->fString != nil )
			{
				::strncpy( pStrObj->fString, inStr, uiLen );
				pStrObj->fString[ uiLen ] = '\0';

				if ( *inStrList == nil )
				{
					*inStrList = pStrObj;
				}
				else
				{
					pStrNext = *inStrList;
					while ( pStrNext->fNext != nil )
					{
						pStrNext = pStrNext->fNext;
					}
					pStrNext->fNext = pStrObj;
				}
			}
			else
			{
				free( pStrObj );
				pStrObj = nil;
			}
		}
	}

} // AppendString

// ----------------------------------------------------------------------------
//	* CDigest Instance Methods
// ----------------------------------------------------------------------------
#pragma mark **** CDigest Public Instance Methods ****

#ifdef MD5
#include "md5.h"

CDigest::CDigest (CString& str )
{
	ComputeDigest((unsigned char*)str.GetData(), (unsigned int)str.GetLength());
}

CDigest::CDigest (char* str, int len )
{
	ComputeDigest((unsigned char*)str, (unsigned int)len);
}

void CDigest::ComputeOn (CString& str )
{
	ComputeDigest((unsigned char*)str.GetData(), (unsigned int)str.GetLength());
}

int CDigest::operator==(CDigest& other)
{
	return (memcmp (mDigest, other.mDigest, 16) == 0);
}

void CDigest::AsString(CString& str)
{
	char	*ptr = str.mData;

	for (int i=0; i<16; i++)
	{
		::sprintf(ptr,"%02x", mDigest[i]);
		ptr += 2;
	}

	*ptr = '\0';
	str.mSize = 32;
}

void CDigest::ComputeDigest(unsigned char* str, unsigned int len)
{
	MD5_CTX	context;

	::MD5Init(&context);
	::MD5Update(&context, str, len);
	::MD5Final(mDigest, &context);
}

#endif /* MD5 */
