#if !defined(_FUSION_INC_FUSIONCHARTRAITS_H_INCLUDED_) #define _FUSION_INC_FUSIONCHARTRAITS_H_INCLUDED_ #pragma once #include #include #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 class CCharTraitsBase { typedef CCharTraitsBase 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 inline static typename 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; // NTRAID#NTBUG9 - 590078 - 2002/03/29 - mgrier - Silent truncation. if (cchToCopy >= cchBuffer) cchToCopy = cchBuffer - 1; CopyMemory(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 inline static typename ReturnStrategy::ReturnType CopyIntoBufferAndAdvanceCursor( ReturnStrategy &returnStrategy, TMutableString &rBuffer, SIZE_T &cchBuffer, TConstantString szString, SIZE_T cchIn ) { // NTRAID#NTBUG9 - 590078 - 2002/03/29 - mgrier - Should be parameter checks ASSERT_NTC((cchBuffer != 0) || (cchIn == 0)); ASSERT_NTC((szString != NULL) || (cchIn == 0)); if (cchBuffer != 0) { if (szString != NULL) { SIZE_T cchToCopy = static_cast(cchIn); // NTRAID#NTBUG9 - 590078 - 2002/03/29 - mgrier - Should be internal error checks // Someone should have stopped this before we got this far. ASSERT_NTC(cchToCopy <= cchBuffer); // You should not include the null character in the count in ASSERT_NTC((cchToCopy == NULL) || (szString[cchToCopy-1] != NullCharacter())); if (cchToCopy > cchBuffer) cchToCopy = cchBuffer; CopyMemory(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 TThis::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 TThis::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 inline static typename 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 TThis::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 TThis::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); } }; class CUnicodeCharTraits : public CCharTraitsBase { typedef CUnicodeCharTraits TThis; typedef CCharTraitsBase 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 inline static typename 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(); // NTRAID#NTBUG9 - 590078 - 2002/03/29 - mgrier - Missing parameter checks if (sz != NULL) { // NTRAID#NTBUG9 - 590078 - 2002/03/29 - mgrier - Should be parameter check // 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(cchIn), NULL, 0); if ((cch == 0) && (cchIn > 0)) { returnStrategy.SetWin32Bool(FALSE); goto Exit; } rcch = static_cast(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; i0; i--) { bool fMatch = false; 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 TThis::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 TThis::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 inline static typename 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 ) { // NTRAID#NTBUG9 - 590078 - 2002/03/29 - mgrier - Missing explicit parameter checks // NTRAID#NTBUG9 - 590078 - 2002/03/29 - mgrier - Missing explicit parameter checks // 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_NTC(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. // NTRAID#NTBUG9 - 590078 - 2002/03/29 - mgrier - Missing parameter check ASSERT2_NTC(cchIn <= INT_MAX, "large parameter clamped"); if (cchIn > INT_MAX) cchIn = INT_MAX; INT cch = ::MultiByteToWideChar(cp, dwFlags, szString, static_cast(cchIn), rgchBuffer, static_cast(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 TThis::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 TThis::CopyIntoBuffer(hr, rgchBuffer, cchBuffer, szString, cchIn, cp, dwFlags); } inline static SIZE_T Cch(PCWSTR psz) { return (psz != NULL) ? ::wcslen(psz) : 0; } }; template class CMBCSCharTraits : public CCharTraitsBase { private: typedef CCharTraitsBase 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 inline static typename ReturnStrategy::ReturnType DetermineRequiredCharacters( ReturnStrategy &returnStrategy, PCWSTR sz, SIZE_T cchIn, SIZE_T &rcch, DWORD dwFlags = 0, PCSTR pszDefaultChar = NULL, LPBOOL lpUsedDefaultChar = NULL ) { // NTRAID#NTBUG9 - 590078 - 2002/03/29 - mgrier - Missing parameter checks 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(cchIn), NULL, 0, pszDefaultChar, lpUsedDefaultChar); if ((cch == 0) && (cchIn > 0)) { returnStrategy.SetWin32Bool(FALSE); goto Exit; } // NTRAID#NTBUG9 - 590078 - 2002/03/29 - mgrier - internal error check that cch >= 0 rcch = static_cast(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 TThis::DetermineRequiredCharacters(f, sz, cchIn, rcch, dwFlags, pszDefaultChar, lpUsedDefaultChar); } // # 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 TThis::DetermineRequiredCharacters(hr, sz, cchIn, rcch, dwFlags, pszDefaultChar, lpUsedDefaultChar); } // copy into buffer from WCHAR to CHAR template inline static typename ReturnStrategy::ReturnType CopyIntoBuffer( ReturnStrategy &returnStrategy, CHAR rgchBuffer[], SIZE_T cchBuffer, PCWSTR szString, SIZE_T cchIn, DWORD dwFlags = 0, PCSTR pszDefaultChar = NULL, LPBOOL lpUsedDefaultChar = NULL ) { // NTRAID#NTBUG9 - 590078 - 2002/03/29 - mgrier - Missing parameter checks 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(cchIn), rgchBuffer, static_cast(cchBuffer - 1), pszDefaultChar, lpUsedDefaultChar); if ((cch == 0) && (cchBuffer > 1)) { returnStrategy.SetWin32Bool(FALSE); goto Exit; } // NTRAID#NTBUG9 - 590078 - 2002/03/29 - mgrier - Missing internal error check that cch >= 0 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 TThis::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 TThis::CopyIntoBuffer(f, rgchBuffer, cchBuffer, szString, cchIn, dwFlags, pszDefaultChar, lpUsedDefaultChar); } inline static SIZE_T Cch(PCSTR psz) { return ::strlen(psz); } }; typedef CMBCSCharTraits CANSICharTraits; #endif