713 lines
24 KiB
C
713 lines
24 KiB
C
|
#if !defined(_FUSION_INC_FUSIONCHARTRAITS_H_INCLUDED_)
|
||
|
#define _FUSION_INC_FUSIONCHARTRAITS_H_INCLUDED_
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <limits.h>
|
||
|
#include "returnstrategy.h"
|
||
|
#include "fusionhashstring.h"
|
||
|
#include "fusionstring.h"
|
||
|
|
||
|
enum StringComparisonResult {
|
||
|
eLessThan,
|
||
|
eEquals,
|
||
|
eGreaterThan
|
||
|
};
|
||
|
|
||
|
//
|
||
|
// This is not the base of all possible CharTraits, but it is the base of the ones
|
||
|
// we have so far. There are pieces of this you can imagine changing.
|
||
|
// StringLength could be strlen/wcslen (msvcrt/Rtl/ntoskrnl)
|
||
|
// CompareStrings could be stricmp/wcsicmp or like unilib and do all the work itself
|
||
|
// WideCharToMultiByte / MultiByteToWideChar could use Rtl.
|
||
|
// more
|
||
|
//
|
||
|
template <typename Char, typename OtherChar>
|
||
|
class CCharTraitsBase
|
||
|
{
|
||
|
typedef CCharTraitsBase<Char, OtherChar> TThis;
|
||
|
|
||
|
public:
|
||
|
typedef Char TChar;
|
||
|
typedef Char* TMutableString;
|
||
|
typedef const Char* TConstantString;
|
||
|
|
||
|
// MFC 7.0 templatized CString makes some good use of this idea; we do not yet.
|
||
|
typedef OtherChar TOtherChar;
|
||
|
typedef OtherChar* TOtherString;
|
||
|
typedef const OtherChar* TOtherConstantString;
|
||
|
|
||
|
inline static TChar NullCharacter() { return 0; }
|
||
|
inline static bool IsNullCharacter(TChar ch)
|
||
|
{ return ch == NullCharacter(); }
|
||
|
|
||
|
inline static TConstantString PreferredPathSeparatorString()
|
||
|
{
|
||
|
const static TChar Result[] = { '\\', 0 };
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
inline static TChar PreferredPathSeparator()
|
||
|
{ return '\\'; }
|
||
|
inline static bool IsPathSeparator(TChar ch)
|
||
|
{ return ((ch == '\\') || (ch == '/')); }
|
||
|
inline static TConstantString PathSeparators()
|
||
|
{
|
||
|
const static TChar Result[] = { '\\', '/', 0 };
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
inline static TChar DotChar()
|
||
|
{ return '.'; }
|
||
|
|
||
|
// copy into buffer from TChar to TChar
|
||
|
template <typename ReturnStrategy>
|
||
|
inline static ReturnStrategy::ReturnType
|
||
|
CopyIntoBuffer(
|
||
|
ReturnStrategy &returnStrategy,
|
||
|
TChar rgchBuffer[],
|
||
|
SIZE_T cchBuffer,
|
||
|
TConstantString szString,
|
||
|
SIZE_T cchIn
|
||
|
)
|
||
|
{
|
||
|
if (cchBuffer != 0)
|
||
|
{
|
||
|
if (szString != NULL)
|
||
|
{
|
||
|
SIZE_T cchToCopy = cchIn;
|
||
|
|
||
|
if (cchToCopy >= cchBuffer)
|
||
|
cchToCopy = cchBuffer - 1;
|
||
|
|
||
|
memcpy(rgchBuffer, szString, cchToCopy * sizeof(TChar));
|
||
|
rgchBuffer[cchToCopy] = NullCharacter();
|
||
|
}
|
||
|
else
|
||
|
rgchBuffer[0] = NullCharacter();
|
||
|
}
|
||
|
returnStrategy.SetWin32Bool(TRUE);
|
||
|
return returnStrategy.Return();
|
||
|
}
|
||
|
|
||
|
// copy into buffer from TChar to TChar
|
||
|
inline static HRESULT CopyIntoBuffer(TChar rgchBuffer[], SIZE_T cchBuffer, TConstantString szString, SIZE_T cchIn)
|
||
|
{
|
||
|
CReturnStrategyHresult hr;
|
||
|
return TThis::CopyIntoBuffer(hr, rgchBuffer, cchBuffer, szString, cchIn);
|
||
|
}
|
||
|
|
||
|
// copy into buffer from TChar to TChar
|
||
|
inline static BOOL Win32CopyIntoBuffer(TChar rgchBuffer[], SIZE_T cchBuffer, TConstantString szString, SIZE_T cchIn)
|
||
|
{
|
||
|
CReturnStrategyBoolLastError f;
|
||
|
return TThis::CopyIntoBuffer(f, rgchBuffer, cchBuffer, szString, cchIn);
|
||
|
}
|
||
|
|
||
|
|
||
|
// copy into buffer from TChar to TChar
|
||
|
template <typename ReturnStrategy>
|
||
|
inline static ReturnStrategy::ReturnType
|
||
|
CopyIntoBufferAndAdvanceCursor(
|
||
|
ReturnStrategy &returnStrategy,
|
||
|
TMutableString &rBuffer,
|
||
|
SIZE_T &cchBuffer,
|
||
|
TConstantString szString,
|
||
|
SIZE_T cchIn
|
||
|
)
|
||
|
{
|
||
|
FN_TRACE();
|
||
|
|
||
|
ASSERT((cchBuffer != 0) || (cchIn == 0));
|
||
|
ASSERT((szString != NULL) || (cchIn == 0));
|
||
|
|
||
|
if (cchBuffer != 0)
|
||
|
{
|
||
|
if (szString != NULL)
|
||
|
{
|
||
|
SIZE_T cchToCopy = static_cast<SIZE_T>(cchIn);
|
||
|
|
||
|
// Someone should have stopped this before we got this far.
|
||
|
ASSERT(cchToCopy <= cchBuffer);
|
||
|
// You should not include the null character in the count in
|
||
|
ASSERT((cchToCopy == NULL) || (szString[cchToCopy-1] != NullCharacter()));
|
||
|
|
||
|
if (cchToCopy > cchBuffer)
|
||
|
cchToCopy = cchBuffer;
|
||
|
|
||
|
memcpy(rBuffer, szString, cchToCopy * sizeof(TChar));
|
||
|
|
||
|
rBuffer += cchToCopy;
|
||
|
cchBuffer -= cchToCopy;
|
||
|
}
|
||
|
}
|
||
|
returnStrategy.SetWin32Bool(TRUE);
|
||
|
return returnStrategy.Return();
|
||
|
}
|
||
|
|
||
|
inline static BOOL Win32HashString(TConstantString szString, SIZE_T cchIn, ULONG &rulPseudoKey, bool fCaseInsensitive)
|
||
|
{
|
||
|
BOOL fSuccess = FALSE;
|
||
|
FN_TRACE_WIN32(fSuccess);
|
||
|
IFW32FALSE_EXIT(::FusionpHashUnicodeString(szString, cchIn, &rulPseudoKey, fCaseInsensitive));
|
||
|
fSuccess = TRUE;
|
||
|
Exit:
|
||
|
return fSuccess;
|
||
|
}
|
||
|
|
||
|
// copy into buffer from TChar to TChar
|
||
|
inline static BOOL Win32CopyIntoBufferAndAdvanceCursor(TMutableString &rBuffer, SIZE_T &cchBuffer, TConstantString szString, SIZE_T cchIn)
|
||
|
{
|
||
|
CReturnStrategyBoolLastError f;
|
||
|
return CopyIntoBufferAndAdvanceCursor(f, rBuffer, cchBuffer, szString, cchIn);
|
||
|
}
|
||
|
|
||
|
// copy into buffer from TChar to TChar
|
||
|
inline static HRESULT ComCopyIntoBufferAndAdvanceCursor(TMutableString &rBuffer, SIZE_T &cchBuffer, TConstantString szString, SIZE_T cchIn)
|
||
|
{
|
||
|
CReturnStrategyHresult hr;
|
||
|
return CopyIntoBufferAndAdvanceCursor(hr, rBuffer, cchBuffer, szString, cchIn);
|
||
|
}
|
||
|
|
||
|
// determine characters required for matching type (TChar)
|
||
|
// like strlen but checks for null and optionally can be told the length
|
||
|
template <typename ReturnStrategy>
|
||
|
inline static ReturnStrategy::ReturnType
|
||
|
DetermineRequiredCharacters(
|
||
|
ReturnStrategy &returnStrategy,
|
||
|
TConstantString /* sz */,
|
||
|
SIZE_T cchIn,
|
||
|
SIZE_T &rcch
|
||
|
)
|
||
|
{
|
||
|
rcch = cchIn + 1;
|
||
|
returnStrategy.SetWin32Bool(TRUE);
|
||
|
return returnStrategy.Return();
|
||
|
}
|
||
|
|
||
|
// determine characters required for matching type (TChar)
|
||
|
inline static HRESULT DetermineRequiredCharacters(TConstantString sz, SIZE_T cchIn, SIZE_T &rcch)
|
||
|
{
|
||
|
CReturnStrategyHresult returnStrategy;
|
||
|
return DetermineRequiredCharacters(returnStrategy, sz, cchIn, rcch);
|
||
|
}
|
||
|
|
||
|
// determine characters required for matching type (TChar)
|
||
|
inline static BOOL Win32DetermineRequiredCharacters(TConstantString sz, SIZE_T cchIn, SIZE_T &rcch)
|
||
|
{
|
||
|
CReturnStrategyBoolLastError returnStrategy;
|
||
|
return DetermineRequiredCharacters(returnStrategy, sz, cchIn, rcch);
|
||
|
}
|
||
|
|
||
|
inline static BOOL Win32EqualStrings(bool &rfMatches, PCWSTR psz1, SIZE_T cch1, PCWSTR psz2, SIZE_T cch2, bool fCaseInsensitive)
|
||
|
{
|
||
|
rfMatches = (::FusionpCompareStrings(psz1, cch1, psz2, cch2, fCaseInsensitive) == 0);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
inline static BOOL Win32EqualStrings(bool &rfMatches, PCSTR psz1, SIZE_T cch1, PCSTR psz2, SIZE_T cch2, bool fCaseInsensitive)
|
||
|
{
|
||
|
rfMatches = (::FusionpCompareStrings(psz1, cch1, psz2, cch2, fCaseInsensitive) == 0);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
inline static BOOL Win32CompareStrings(StringComparisonResult &rscr, PCWSTR psz1, SIZE_T cch1, PCWSTR psz2, SIZE_T cch2, bool fCaseInsensitive)
|
||
|
{
|
||
|
int i = ::FusionpCompareStrings(psz1, cch1, psz2, cch2, fCaseInsensitive);
|
||
|
|
||
|
if (i == 0)
|
||
|
rscr = eEquals;
|
||
|
else if (i < 0)
|
||
|
rscr = eLessThan;
|
||
|
else
|
||
|
rscr = eGreaterThan;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
inline static BOOL Win32CompareStrings(StringComparisonResult &rscr, PCSTR psz1, SIZE_T cch1, PCSTR psz2, SIZE_T cch2, bool fCaseInsensitive)
|
||
|
{
|
||
|
int i = ::FusionpCompareStrings(psz1, cch1, psz2, cch2, fCaseInsensitive);
|
||
|
|
||
|
if (i == 0)
|
||
|
rscr = eEquals;
|
||
|
else if (i < 0)
|
||
|
rscr = eLessThan;
|
||
|
else
|
||
|
rscr = eGreaterThan;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
inline static int CompareStrings(LCID lcid, DWORD dwCmpFlags, PCWSTR psz1, int cch1, PCWSTR psz2, int cch2)
|
||
|
{
|
||
|
return ::CompareStringW(lcid, dwCmpFlags, psz1, cch1, psz2, cch2);
|
||
|
}
|
||
|
|
||
|
inline static int CompareStrings(LCID lcid, DWORD dwCmpFlags, PCSTR psz1, int cch1, PCSTR psz2, int cch2)
|
||
|
{
|
||
|
return ::CompareStringA(lcid, dwCmpFlags, psz1, cch1, psz2, cch2);
|
||
|
}
|
||
|
|
||
|
inline static int FormatV(PSTR pszBuffer, SIZE_T nBufferSize, PCSTR pszFormat, va_list args)
|
||
|
{
|
||
|
return _vsnprintf(pszBuffer, nBufferSize, pszFormat, args);
|
||
|
}
|
||
|
|
||
|
inline static int FormatV(PWSTR pszBuffer, SIZE_T nBufferSize, PCWSTR pszFormat, va_list args)
|
||
|
{
|
||
|
return _vsnwprintf(pszBuffer, nBufferSize, pszFormat, args);
|
||
|
}
|
||
|
#if 0
|
||
|
inline static BOOL Win32GetRequiredBufferSizeInCharsForFormatV(PCWSTR pszFormat, va_list args, SIZE_T& rcch)
|
||
|
{
|
||
|
FN_PROLOG_WIN32
|
||
|
INT i = 0;
|
||
|
rcch = 0;
|
||
|
if ((i = _vscwprintf(pszFormat, args)) < 0)
|
||
|
ORIGINATE_WIN32_FAILURE_AND_EXIT(_vscwprintf, ERROR_INVALID_PARAMETER);
|
||
|
rcch = static_cast<SIZE_T>(i);
|
||
|
FN_EPILOG
|
||
|
}
|
||
|
|
||
|
inline static BOOL Win32GetRequiredBufferSizeInCharsForFormatV(PCSTR pszFormat, va_list args, SIZE_T& rcch)
|
||
|
{
|
||
|
FN_PROLOG_WIN32
|
||
|
INT i = 0;
|
||
|
rcch = 0;
|
||
|
if ((i = _vscprintf(pszFormat, args)) < 0)
|
||
|
ORIGINATE_WIN32_FAILURE_AND_EXIT(_vscprintf, ERROR_INVALID_PARAMETER);
|
||
|
rcch = static_cast<SIZE_T>(i);
|
||
|
FN_EPILOG
|
||
|
}
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
class CUnicodeCharTraits : public CCharTraitsBase<WCHAR, CHAR>
|
||
|
{
|
||
|
typedef CUnicodeCharTraits TThis;
|
||
|
typedef CCharTraitsBase<WCHAR, CHAR> Base;
|
||
|
|
||
|
public:
|
||
|
// without using, we end up hiding these by providing equally named functions
|
||
|
using Base::DetermineRequiredCharacters;
|
||
|
using Base::Win32DetermineRequiredCharacters;
|
||
|
using Base::CopyIntoBuffer;
|
||
|
using Base::Win32CopyIntoBuffer;
|
||
|
|
||
|
inline static PCWSTR DotString() { return L"."; }
|
||
|
inline static SIZE_T DotStringCch() { return 1; }
|
||
|
|
||
|
// determine characters required for mismatched type (CHAR -> WCHAR)
|
||
|
template <typename ReturnStrategy>
|
||
|
inline static ReturnStrategy::ReturnType
|
||
|
DetermineRequiredCharacters(
|
||
|
ReturnStrategy &returnStrategy,
|
||
|
PCSTR sz,
|
||
|
SIZE_T cchIn,
|
||
|
SIZE_T &rcch,
|
||
|
UINT cp = CP_THREAD_ACP,
|
||
|
DWORD dwFlags = MB_ERR_INVALID_CHARS
|
||
|
)
|
||
|
{
|
||
|
FN_TRACE();
|
||
|
|
||
|
if (sz != NULL)
|
||
|
{
|
||
|
// For 64-bit, clamp the maximum size passed in to the largest that the INT
|
||
|
// parameter to MultiByteToWideChar() can take.
|
||
|
ASSERT2(cchIn <= INT_MAX, "large parameter clamped");
|
||
|
if (cchIn > INT_MAX)
|
||
|
cchIn = INT_MAX;
|
||
|
|
||
|
INT cch = ::MultiByteToWideChar(cp, dwFlags, sz, static_cast<INT>(cchIn), NULL, 0);
|
||
|
if ((cch == 0) && (cchIn > 0))
|
||
|
{
|
||
|
returnStrategy.SetWin32Bool(FALSE);
|
||
|
goto Exit;
|
||
|
}
|
||
|
rcch = static_cast<SIZE_T>(cch) + 1;
|
||
|
}
|
||
|
else
|
||
|
rcch = 1;
|
||
|
|
||
|
returnStrategy.SetWin32Bool(TRUE);
|
||
|
Exit:
|
||
|
return returnStrategy.Return();
|
||
|
}
|
||
|
|
||
|
inline static BOOL FindCharacter(PCWSTR sz, SIZE_T cch, WCHAR ch, BOOL *pfFound, SIZE_T *pich)
|
||
|
{
|
||
|
BOOL fSuccess = FALSE;
|
||
|
FN_TRACE_WIN32(fSuccess);
|
||
|
// There doesn't seem to be a builtin to do this...
|
||
|
SIZE_T i;
|
||
|
|
||
|
if (pfFound != NULL)
|
||
|
*pfFound = FALSE;
|
||
|
|
||
|
if (pich != NULL)
|
||
|
*pich = 0;
|
||
|
|
||
|
PARAMETER_CHECK((pfFound != NULL) && (pich != NULL));
|
||
|
|
||
|
for (i=0; i<cch; i++)
|
||
|
{
|
||
|
if (sz[i] == ch)
|
||
|
{
|
||
|
*pich = i;
|
||
|
*pfFound = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fSuccess = TRUE;
|
||
|
|
||
|
Exit:
|
||
|
return fSuccess;
|
||
|
}
|
||
|
|
||
|
inline static bool ContainsCharacter(PCWSTR sz, SIZE_T cch, WCHAR ch)
|
||
|
{
|
||
|
SIZE_T i;
|
||
|
|
||
|
for (i=0; i<cch; i++)
|
||
|
{
|
||
|
if (sz[i] == ch)
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
inline static BOOL Win32ToLower(WCHAR wchToConvert, WCHAR &rwchConverted)
|
||
|
{
|
||
|
rwchConverted = ::FusionpRtlDowncaseUnicodeChar(wchToConvert);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
inline static BOOL Win32ToUpper(WCHAR wchToConvert, WCHAR &rwchConverted)
|
||
|
{
|
||
|
rwchConverted = ::FusionpRtlUpcaseUnicodeChar(wchToConvert);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
inline static BOOL Win32ReverseFind(PCWSTR &rpchFound, PCWSTR psz, SIZE_T cch, WCHAR wchToFind, bool fCaseInsensitive)
|
||
|
{
|
||
|
BOOL fSuccess = FALSE;
|
||
|
SIZE_T i = 0;
|
||
|
|
||
|
rpchFound = NULL;
|
||
|
|
||
|
if (fCaseInsensitive)
|
||
|
{
|
||
|
// Map the character to its lower case equivalent...
|
||
|
if (!TThis::Win32ToLower(wchToFind, wchToFind))
|
||
|
goto Exit;
|
||
|
|
||
|
for (i=cch; i>0; i--)
|
||
|
{
|
||
|
bool fMatch;
|
||
|
|
||
|
if (!TThis::Win32CompareLowerCaseCharacterToCharCaseInsensitively(fMatch, wchToFind, psz[i - 1]))
|
||
|
goto Exit;
|
||
|
|
||
|
if (fMatch)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for (i=cch; i>0; i--)
|
||
|
{
|
||
|
if (psz[i - 1] == wchToFind)
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (i != 0)
|
||
|
rpchFound = &psz[i - 1];
|
||
|
|
||
|
fSuccess = TRUE;
|
||
|
Exit:
|
||
|
return fSuccess;
|
||
|
}
|
||
|
|
||
|
inline static BOOL Win32CompareLowerCaseCharacterToCharCaseInsensitively(bool &rfMatch, WCHAR wchLowerCase, WCHAR wchCandidate)
|
||
|
{
|
||
|
BOOL fSuccess = FALSE;
|
||
|
|
||
|
rfMatch = false;
|
||
|
|
||
|
if (!TThis::Win32ToLower(wchCandidate, wchCandidate))
|
||
|
goto Exit;
|
||
|
|
||
|
if (wchCandidate == wchLowerCase)
|
||
|
rfMatch = true;
|
||
|
|
||
|
fSuccess = TRUE;
|
||
|
Exit:
|
||
|
return fSuccess;
|
||
|
}
|
||
|
|
||
|
// determine characters required for mismatched type (CHAR -> WCHAR)
|
||
|
inline static HRESULT DetermineRequiredCharacters(PCSTR sz, SIZE_T cchIn, SIZE_T &rcch, UINT cp = CP_THREAD_ACP, DWORD dwFlags = MB_ERR_INVALID_CHARS)
|
||
|
{
|
||
|
CReturnStrategyHresult hr;
|
||
|
return DetermineRequiredCharacters(hr, sz, cchIn, rcch, cp, dwFlags);
|
||
|
}
|
||
|
|
||
|
// determine characters required for mismatched type (CHAR -> WCHAR)
|
||
|
inline static BOOL Win32DetermineRequiredCharacters(PCSTR sz, SIZE_T cchIn, SIZE_T &rcch, UINT cp = CP_THREAD_ACP, DWORD dwFlags = MB_ERR_INVALID_CHARS)
|
||
|
{
|
||
|
CReturnStrategyBoolLastError f;
|
||
|
return DetermineRequiredCharacters(f, sz, cchIn, rcch, cp, dwFlags);
|
||
|
}
|
||
|
|
||
|
inline static SIZE_T NullTerminatedStringLength(PCWSTR sz) { return (sz != NULL) ? ::wcslen(sz) : 0; }
|
||
|
|
||
|
// copy into buffer from CHAR to WCHAR
|
||
|
template <typename ReturnStrategy>
|
||
|
inline static ReturnStrategy::ReturnType
|
||
|
CopyIntoBuffer(
|
||
|
ReturnStrategy &returnStrategy,
|
||
|
WCHAR rgchBuffer[],
|
||
|
SIZE_T cchBuffer,
|
||
|
PCSTR szString,
|
||
|
SIZE_T cchIn,
|
||
|
UINT cp = CP_THREAD_ACP,
|
||
|
DWORD dwFlags = MB_ERR_INVALID_CHARS
|
||
|
)
|
||
|
{
|
||
|
FN_TRACE();
|
||
|
|
||
|
// The caller must be on drugs if they (think that they) have a buffer larger than 2gb, but
|
||
|
// let's at least clamp it so that we don't get a negative int value passed in
|
||
|
// to ::MultiByteToWideChar().
|
||
|
ASSERT2(cchBuffer <= INT_MAX, "large parameter clamped");
|
||
|
if (cchBuffer > INT_MAX)
|
||
|
cchBuffer = INT_MAX;
|
||
|
|
||
|
if (cchBuffer != 0)
|
||
|
{
|
||
|
if (szString != NULL)
|
||
|
{
|
||
|
// It would seem that you could just pass the -1 into MultiByteToWideChar(), but
|
||
|
// you get some errors on the boundary conditions, because -1 implies that you
|
||
|
// want to consider the null termination on the input string, and the output
|
||
|
// string will be null terminated also. Consider the degenerate case of a 2
|
||
|
// character output buffer and an input string that's a single non-null
|
||
|
// character followed by a null character. We're going to trim the size of
|
||
|
// cchBuffer by 1 so that we manually null-terminate in case the input string
|
||
|
// was not null-terminated, so MultiByteToWideChar() just writes a single
|
||
|
// null character to the output buffer since it thinks it must write a null-
|
||
|
// terminated string.
|
||
|
//
|
||
|
// Instead, we'll just always pass in an exact length, not including the null character
|
||
|
// in the input, and we'll always put the null in place after the conversion succeeds.
|
||
|
//
|
||
|
// (this comment is mostly outdated - 11/24/2000 - but the discussion of how the
|
||
|
// MultiByteToWideChar() API works is worth keeping -mgrier)
|
||
|
|
||
|
// Since MultiByteToWideChar() takes an "int" length, clamp the maximum
|
||
|
// value we pass in to 2gb.
|
||
|
ASSERT2(cchIn <= INT_MAX, "large parameter clamped");
|
||
|
if (cchIn > INT_MAX)
|
||
|
cchIn = INT_MAX;
|
||
|
|
||
|
INT cch = ::MultiByteToWideChar(cp, dwFlags, szString, static_cast<INT>(cchIn), rgchBuffer, static_cast<INT>(cchBuffer) - 1);
|
||
|
if ((cch == 0) && (cchBuffer > 1))
|
||
|
{
|
||
|
returnStrategy.SetWin32Bool(FALSE);
|
||
|
goto Exit;
|
||
|
}
|
||
|
rgchBuffer[cch] = NullCharacter();
|
||
|
}
|
||
|
else
|
||
|
rgchBuffer[0] = NullCharacter();
|
||
|
}
|
||
|
|
||
|
returnStrategy.SetWin32Bool(TRUE);
|
||
|
Exit:
|
||
|
return returnStrategy.Return();
|
||
|
}
|
||
|
|
||
|
// copy into buffer from CHAR to WCHAR
|
||
|
inline static BOOL Win32CopyIntoBuffer(WCHAR rgchBuffer[], SIZE_T cchBuffer, PCSTR szString, SIZE_T cchIn, UINT cp = CP_THREAD_ACP, DWORD dwFlags = MB_ERR_INVALID_CHARS)
|
||
|
{
|
||
|
CReturnStrategyBoolLastError f;
|
||
|
return CopyIntoBuffer(f, rgchBuffer, cchBuffer, szString, cchIn, cp, dwFlags);
|
||
|
}
|
||
|
|
||
|
// copy into buffer from CHAR to WCHAR
|
||
|
inline static HRESULT CopyIntoBuffer(WCHAR rgchBuffer[], SIZE_T cchBuffer, PCSTR szString, SIZE_T cchIn, UINT cp = CP_THREAD_ACP, DWORD dwFlags = MB_ERR_INVALID_CHARS)
|
||
|
{
|
||
|
CReturnStrategyHresult hr;
|
||
|
return CopyIntoBuffer(hr, rgchBuffer, cchBuffer, szString, cchIn, cp, dwFlags);
|
||
|
}
|
||
|
|
||
|
inline static SIZE_T Cch(PCWSTR psz) { return (psz != NULL) ? ::wcslen(psz) : 0; }
|
||
|
|
||
|
};
|
||
|
|
||
|
template <UINT cp = CP_THREAD_ACP> class CMBCSCharTraits : public CCharTraitsBase<CHAR, WCHAR>
|
||
|
{
|
||
|
private:
|
||
|
typedef CCharTraitsBase<CHAR, WCHAR> Base;
|
||
|
public:
|
||
|
typedef CHAR TChar;
|
||
|
typedef LPSTR TMutableString;
|
||
|
typedef PCSTR TConstantString;
|
||
|
|
||
|
typedef CUnicodeCharTraits TOtherTraits;
|
||
|
typedef TOtherTraits::TOtherChar TOtherChar;
|
||
|
typedef TOtherTraits::TOtherString TOtherString;
|
||
|
typedef TOtherTraits::TConstantString TOtherConstantString;
|
||
|
|
||
|
inline static PCSTR DotString() { return "."; }
|
||
|
inline static SIZE_T DotStringCch() { return 1; }
|
||
|
|
||
|
// without using, we end up hiding these by providing equally named functions
|
||
|
using Base::DetermineRequiredCharacters;
|
||
|
using Base::Win32DetermineRequiredCharacters;
|
||
|
using Base::CopyIntoBuffer;
|
||
|
using Base::Win32CopyIntoBuffer;
|
||
|
|
||
|
// determine characters required for mismatched type (WCHAR -> CHAR)
|
||
|
template <typename ReturnStrategy>
|
||
|
inline static ReturnStrategy::ReturnType
|
||
|
DetermineRequiredCharacters(
|
||
|
ReturnStrategy &returnStrategy,
|
||
|
PCWSTR sz,
|
||
|
SIZE_T cchIn,
|
||
|
SIZE_T &rcch,
|
||
|
DWORD dwFlags = 0,
|
||
|
PCSTR pszDefaultChar = NULL,
|
||
|
LPBOOL lpUsedDefaultChar = NULL
|
||
|
)
|
||
|
{
|
||
|
if (sz != NULL)
|
||
|
{
|
||
|
ASSERT2(cchIn <= INT_MAX, "large parameter clamped");
|
||
|
ASSERT(cchIn <= INT_MAX);
|
||
|
if (cchIn > INT_MAX)
|
||
|
cchIn = INT_MAX;
|
||
|
|
||
|
INT cch = ::WideCharToMultiByte(cp, dwFlags, sz, static_cast<INT>(cchIn), NULL, 0, pszDefaultChar, lpUsedDefaultChar);
|
||
|
if ((cch == 0) && (cchIn > 0))
|
||
|
{
|
||
|
returnStrategy.SetWin32Bool(FALSE);
|
||
|
goto Exit;
|
||
|
}
|
||
|
|
||
|
rcch = static_cast<SIZE_T>(cch) + 1;
|
||
|
} else
|
||
|
rcch = 1;
|
||
|
|
||
|
returnStrategy.SetWin32Bool(TRUE);
|
||
|
Exit:
|
||
|
return returnStrategy.Return();
|
||
|
}
|
||
|
|
||
|
inline static SIZE_T NullTerminatedStringLength(PCSTR sz) { return ::strlen(sz); }
|
||
|
|
||
|
// determine characters required for mismatched type (WCHAR -> CHAR)
|
||
|
inline static BOOL Win32DetermineRequiredCharacters(PCWSTR sz, SIZE_T cchIn, SIZE_T &rcch, DWORD dwFlags = 0, PCSTR pszDefaultChar = NULL, LPBOOL lpUsedDefaultChar = NULL)
|
||
|
{
|
||
|
CReturnStrategyBoolLastError f;
|
||
|
return DetermineRequiredCharacters(f, sz, cchIn, rcch, dwFlags, pszDefaultChar, lpUsedDefaultChar);
|
||
|
}
|
||
|
|
||
|
// determine characters required for mismatched type (WCHAR -> CHAR)
|
||
|
inline static HRESULT DetermineRequiredCharacters(PCWSTR sz, SIZE_T cchIn, SIZE_T &rcch, DWORD dwFlags = 0, PCSTR pszDefaultChar = NULL, LPBOOL lpUsedDefaultChar = NULL)
|
||
|
{
|
||
|
CReturnStrategyHresult hr;
|
||
|
return DetermineRequiredCharacters(hr, sz, cchIn, rcch, dwFlags, pszDefaultChar, lpUsedDefaultChar);
|
||
|
}
|
||
|
|
||
|
// copy into buffer from WCHAR to CHAR
|
||
|
template <typename ReturnStrategy>
|
||
|
inline static ReturnStrategy::ReturnType
|
||
|
CopyIntoBuffer(
|
||
|
ReturnStrategy &returnStrategy,
|
||
|
CHAR rgchBuffer[],
|
||
|
SIZE_T cchBuffer,
|
||
|
PCWSTR szString,
|
||
|
SIZE_T cchIn,
|
||
|
DWORD dwFlags = 0,
|
||
|
PCSTR pszDefaultChar = NULL,
|
||
|
LPBOOL lpUsedDefaultChar = NULL
|
||
|
)
|
||
|
{
|
||
|
if (cchBuffer != 0)
|
||
|
{
|
||
|
// Clamp the maximum buffer size to maxint, since the buffer size passed in to
|
||
|
// WideCharToMultiByte() is an INT rather than a SIZE_T or INT_PTR etc.
|
||
|
// After all, who's really going to have a buffer size > 2gb? The caller
|
||
|
// probably just messed up.
|
||
|
ASSERT2(cchBuffer <= INT_MAX, "large parameter clamped");
|
||
|
if (cchBuffer > INT_MAX)
|
||
|
cchBuffer = INT_MAX;
|
||
|
|
||
|
if (szString != NULL)
|
||
|
{
|
||
|
// It would seem that you could just pass the -1 into MultiByteToWideChar(), but
|
||
|
// you get some errors on the boundary conditions, because -1 implies that you
|
||
|
// want to consider the null termination on the input string, and the output
|
||
|
// string will be null terminated also. Consider the degenerate case of a 2
|
||
|
// character output buffer and an input string that's a single non-null
|
||
|
// character followed by a null character. We're going to trim the size of
|
||
|
// cchBuffer by 1 so that we manually null-terminate in case the input string
|
||
|
// was not null-terminated, so MultiByteToWideChar() just writes a single
|
||
|
// null character to the output buffer since it thinks it must write a null-
|
||
|
// terminated string.
|
||
|
//
|
||
|
// Instead, we'll just always pass in an exact length, not including the null character
|
||
|
// in the input, and we'll always put the null in place after the conversion succeeds.
|
||
|
//
|
||
|
|
||
|
ASSERT2(cchIn <= INT_MAX, "large parameter clamped");
|
||
|
if (cchIn > INT_MAX)
|
||
|
cchIn = INT_MAX;
|
||
|
|
||
|
INT cch = ::WideCharToMultiByte(cp, dwFlags, szString, static_cast<INT>(cchIn), rgchBuffer, static_cast<INT>(cchBuffer - 1), pszDefaultChar, lpUsedDefaultChar);
|
||
|
if ((cch == 0) && (cchBuffer > 1))
|
||
|
{
|
||
|
returnStrategy.SetWin32Bool(FALSE);
|
||
|
goto Exit;
|
||
|
}
|
||
|
|
||
|
rgchBuffer[cch] = NullCharacter();
|
||
|
}
|
||
|
else
|
||
|
rgchBuffer[0] = NullCharacter();
|
||
|
}
|
||
|
returnStrategy.SetWin32Bool(TRUE);
|
||
|
Exit:
|
||
|
return returnStrategy.Return();
|
||
|
}
|
||
|
|
||
|
// copy into buffer from WCHAR to CHAR
|
||
|
inline static HRESULT CopyIntoBuffer(CHAR rgchBuffer[], SIZE_T cchBuffer, PCWSTR szString, SIZE_T cchIn, DWORD dwFlags = 0, PCSTR pszDefaultChar = NULL, LPBOOL lpUsedDefaultChar = NULL)
|
||
|
{
|
||
|
CReturnStrategyHresult hr;
|
||
|
return CopyIntoBuffer(hr, rgchBuffer, cchBuffer, szString, cchIn, dwFlags, pszDefaultChar, lpUsedDefaultChar);
|
||
|
}
|
||
|
|
||
|
// copy into buffer from WCHAR to CHAR
|
||
|
inline static BOOL Win32CopyIntoBuffer(CHAR rgchBuffer[], SIZE_T cchBuffer, PCWSTR szString, SIZE_T cchIn, DWORD dwFlags = 0, PCSTR pszDefaultChar = NULL, LPBOOL lpUsedDefaultChar = NULL)
|
||
|
{
|
||
|
CReturnStrategyBoolLastError f;
|
||
|
return CopyIntoBuffer(f, rgchBuffer, cchBuffer, szString, cchIn, dwFlags, pszDefaultChar, lpUsedDefaultChar);
|
||
|
}
|
||
|
|
||
|
inline static SIZE_T Cch(PCSTR psz) { return ::strlen(psz); }
|
||
|
|
||
|
};
|
||
|
|
||
|
typedef CMBCSCharTraits<CP_THREAD_ACP> CANSICharTraits;
|
||
|
|
||
|
#endif
|