#if !defined(_BCL_NTUNICODESTRINGALGORITHMS_H_INCLUDED_) #define _BCL_NTUNICODESTRINGALGORITHMS_H_INCLUDED_ #pragma once /*++ Copyright (c) 2000 Microsoft Corporation Module Name: bcl_ntunicodestringalgorithms.h Abstract: Author: Michael Grier (MGrier) 2/6/2002 Revision History: --*/ #include #include #include #include #include #include namespace BCL { template class CNtNullTerminatedUnicodeStringAlgorithms { public: typedef CNtNullTerminatedUnicodeStringAlgorithms TThis; typedef TCallDispositionT TCallDisposition; typedef CNtStringComparisonResult TComparisonResult; typedef BCL::CConstantPointerAndCountPair TConstantPair; typedef BCL::CMutablePointerAndCountPair TMutablePair; typedef CNtCaseInsensitivityData TCaseInsensitivityData; typedef SIZE_T TSizeT; typedef CNtANSIToUnicodeDataIn TDecodingDataIn; typedef CNtANSIToUnicodeDataOut TDecodingDataOut; typedef CNtUnicodeToANSIDataIn TEncodingDataIn; typedef CNtUnicodeToANSIDataOut TEncodingDataOut; typedef CNtOEMToUnicodeDataIn TDecodingDataIn; typedef CNtOEMToUnicodeDataOut TDecodingDataOut; typedef CNtUnicodeToOEMDataIn TEncodingDataIn; typedef CNtUnicodeToOEMDataOut TEncodingDataOut; typedef CConstantPointerAndCountPair TConstantNonNativePair; typedef CMutablePointerAndCountPair TMutableNonNativePair; typedef PSTR TMutableNonNativeString; typedef PCSTR TConstantNonNativeString; typedef PWSTR TMutableString; typedef PCWSTR TConstantString; static inline void _fastcall SetStringCch(BCL::CBaseString *p, SIZE_T cch) { BCL_ASSERT((cch == 0) || (cch < TBuffer::TTraits::GetBufferCch(p))); static_cast(p)->m_cchString = cch; if (TBuffer::TTraits::GetBufferCch(p) != 0) TBuffer::TTraits::GetMutableBufferPtr(p)[cch] = L'\0'; } static inline TCallDisposition __fastcall MapStringCchToBufferCch(SIZE_T cchString, SIZE_T &rcchRequired) { SIZE_T cchRequired = cchString + 1; if (cchRequired == 0) return TCallDisposition::ArithmeticOverflow(); rcchRequired = cchRequired; return TCallDisposition::Success(); } static inline TCallDisposition __fastcall MapBufferCchToStringCch(SIZE_T cchBuffer, SIZE_T &rcchString) { if (cchBuffer == 0) rcchString = 0; else rcchString = cchBuffer - 1; return TCallDisposition::Success(); } static inline TCallDisposition __fastcall IsCharLegalLeadChar(WCHAR wch, bool &rfIsLegal) { BCL_MAYFAIL_PROLOG // fast common path out for ASCII; there are no combining characters in this range if (wch <= 0x007f) rfIsLegal = true; else { // low surrogate if ((wch >= 0xdc00) && (wch <= 0xdfff)) rfIsLegal = false; else rfIsLegal = true; } BCL_MAYFAIL_EPILOG_INTERNAL } static inline TCallDisposition __fastcall UpperCase(BCL::CBaseString *p, const CNtCaseInsensitivityData &rcid) { BCL_MAYFAIL_PROLOG TBuffer::TSizeT cch, i; TBuffer::TMutableString pString = TBuffer::TTraits::GetMutableBufferPtr(p); cch = TBuffer::TTraits::GetStringCch(p); for (i=0; i static inline TCallDisposition __fastcall EqualStringsI( const TSomeInputType1 &rinput1, const TSomeInputType2 &rinput2, const CNtCaseInsensitivityData &rcid, bool &rfMatches ) { BCL_MAYFAIL_PROLOG rfMatches = false; const TBuffer::TSizeT cch1 = TBuffer::TTraits::GetInputCch(rinput1); const TBuffer::TSizeT cch2 = TBuffer::TTraits::GetInputCch(rinput2); if (cch1 == cch2) { const TBuffer::TConstantString pString1 = TBuffer::TTraits::GetInputPtr(rinput1); const TBuffer::TConstantString pString2 = TBuffer::TTraits::GetInputPtr(rinput2); TBuffer::TSizeT i; for (i=0; i static inline TCallDisposition __fastcall CompareStringsI( const TSomeInputType1 &rinput1, const TSomeInputType2 &rinput2, const CNtCaseInsensitivityData &, // unused TComparisonResult &rcr ) { BCL_MAYFAIL_PROLOG rfMatches = false; const TBuffer::TSizeT cch1 = TBuffer::TTraits::GetInputCch(rinput1); const TBuffer::TSizeT cch2 = TBuffer::TTraits::GetInputCch(rinput2); const TBuffer::TSizeT cchMin = (cch1 < cch2) ? cch1 : cch2; const TBuffer::TConstantString pString1 = TBuffer::TTraits::GetInputPtr(rinput1); const TBuffer::TConstantString pString2 = TBuffer::TTraits::GetInputPtr(rinput2); WCHAR wch1, wch2; TBuffer::TSizeT i; wch1 = wch2 = L'\0'; for (i=0; i cchMin) rcr.SetGreaterThan(); else if (cch2 > cchMin) rcr.SetLessThan(); else rcr.SetEqualTo(); } else { // Simpler; wch1 and wch2 are comparable... if (wch1 < wch2) rcr.SetLessThan(); else if (wch1 > wch2) rcr.SetGreaterThan(); else rcr.SetEqualTo(); } BCL_MAYFAIL_EPILOG_INTERNAL } template inline static TCallDisposition ContainsI( const TConstantPair &rpair, const TSomeCharacterMatcher &rscm, const CNtCaseInsensitivityData &rcid, bool &rfFound ) { BCL_MAYFAIL_PROLOG rfFound = false; BCL_PARAMETER_CHECK(rpair.Valid()); SIZE_T cch = rpair.GetCount(); SIZE_T i; const WCHAR *prgch = rpair.GetPointer(); for (i=0; i(cchCandidate), prgwchCandidate, static_cast(cchCandidate)); if (iResult == 0) return TCallDisposition::FromLastError(); if (iResult == CSTR_EQUAL) { rfFound = true; break; } const WCHAR wch = *prgch++; // Skip ahead an additional character if this is a surrogate if ((wch >= 0xd800) && (wch <= 0xdbff)) { i++; prgch++; } } } BCL_MAYFAIL_EPILOG_INTERNAL } inline static TCallDisposition __fastcall FindFirstI( const TConstantPair &rpair, WCHAR ch, const CNtCaseInsensitivityData &rcid, SIZE_T &rich ) { BCL_MAYFAIL_PROLOG BCL_PARAMETER_CHECK(rpair.Valid()); SIZE_T cch = rpair.GetCount(); SIZE_T i; const WCHAR *prgch = rpair.GetPointer(); for (i=0; i(cchCandidate), prgwchCandidate, static_cast(cchCandidate)); if (iResult == 0) return TCallDisposition::FromLastError(); if (iResult == CSTR_EQUAL) { richFound = i; break; } const WCHAR wch = *prgch++; // Skip ahead an additional character if this is a surrogate if ((wch >= 0xd800) && (wch <= 0xdbff)) { i++; prgch++; } } } BCL_MAYFAIL_EPILOG_INTERNAL } inline static TCallDisposition __fastcall FindLastI( const TConstantPair &rpair, WCHAR ch, const CNtCaseInsensitivityData &rcid, SIZE_T &richFound ) { BCL_MAYFAIL_PROLOG SIZE_T i; SIZE_T cch = rpair.GetCount(); const WCHAR *prgwch = rpair.GetPointer() + cch; richFound = cch; for (i=cch; i>0; i--) { int iResult = ::CompareStringW(rcid.m_lcid, rcid.m_dwCmpFlags, --prgwch, 1, &ch, 1); if (iResult == 0) return TCallDisposition::FromLastError(); if (iResult == CSTR_EQUAL) break; } if (i == 0) richFound = cch; else richFound = i - 1; BCL_MAYFAIL_EPILOG_INTERNAL } inline static TCallDisposition FindLastI( const TConstantPair &rpair, const TConstantPair &rpairCandidate, const CNtCaseInsensitivityData &rcid, SIZE_T &richFound ) { BCL_MAYFAIL_PROLOG SIZE_T cch = rpair.GetCount(); richFound = cch; BCL_PARAMETER_CHECK(rpair.Valid()); BCL_PARAMETER_CHECK(rpairCandidate.Valid()); SIZE_T cchCandidate = rpairCandidate.GetCount(); const WCHAR *prgwchCandidate = rpairCandidate.GetPointer(); BCL_PARAMETER_CHECK(cchCandidate <= INT_MAX); if (cchCandidate == 0) { // The null string is in every string richFound = cch; } else { // We can't even short circuit out of here just because the candidate string // is longer than the target string because we don't know what kind of // case folding magic NLS is doing for us behind the scenes based on // the case insensitivity data's dwCmpFlags. SIZE_T i; const WCHAR *prgch = rpair.GetPointer(); // This is a dismal implementation of this kind of search but // I don't know if there's a lot you can do with neato algorithms // while keeping the case insensitivity a black box inside of // CompareStringW(). -mgrier 2/3/2002 for (i=0; i(cchCandidate), prgwchCandidate, static_cast(cchCandidate)); if (iResult == 0) return TCallDisposition::FromLastError(); if (iResult == CSTR_EQUAL) { richFound = i; // keep looking in case there's another } const WCHAR wch = *prgch++; // Skip ahead an additional character if this is a surrogate if ((wch >= 0xd800) && (wch <= 0xdbff)) { i++; prgch++; } } } BCL_MAYFAIL_EPILOG_INTERNAL } static inline TCallDisposition __fastcall SpanI(const TConstantPair &rpairBuffer, const TConstantPair &rpairSet, const CNtCaseInsensitivityData &rcid, SIZE_T &rich) { BCL_MAYFAIL_PROLOG SIZE_T i; SIZE_T cchBuffer = rpairBuffer.GetCount(); const WCHAR *prgwchBuffer = rpairBuffer.GetPointer(); bool fFound; // This does not handle surrogates correctly for (i=0; i0; i--) { BCL_IFCALLFAILED_EXIT(TBuffer::TTraits::ContainsI(rpairSet, prgwchBuffer[i-1], rcid, fFound)); if (!fFound) break; } rich = i; BCL_MAYFAIL_EPILOG_INTERNAL } static inline TCallDisposition __fastcall ReverseComplementSpanI(const TConstantPair &rpairBuffer, const TConstantPair &rpairSet, const CNtCaseInsensitivityData &rcid, SIZE_T &rich) { BCL_MAYFAIL_PROLOG SIZE_T i; SIZE_T cchBuffer = rpairBuffer.GetCount(); const WCHAR *prgwchBuffer = rpairBuffer.GetPointer(); bool fFound; // This does not handle surrogates correctly for (i=cchBuffer; i>0; i--) { BCL_IFCALLFAILED_EXIT(TBuffer::TTraits::ContainsI(rpairSet, prgwchBuffer[i], rcid, fFound)); if (fFound) break; } rich = i; BCL_MAYFAIL_EPILOG_INTERNAL } static inline TCallDisposition DetermineRequiredCharacters( const CNtMBCSToUnicodeDataIn &rddi, const TConstantNonNativePair &rpair, CNtMBCSToUnicodeDataOut &rddo, SIZE_T &rcch ) { BCL_MAYFAIL_PROLOG BCL_PARAMETER_CHECK(rpair.GetCount() <= INT_MAX); // limitation imposed by MultiByteToWideChar API int iResult = ::MultiByteToWideChar( rddi.m_CodePage, rddi.m_dwFlags | MB_ERR_INVALID_CHARS, rpair.GetPointer(), static_cast(rpair.GetCount()), NULL, 0); if (iResult == 0) BCL_ORIGINATE_ERROR(TCallDisposition::FromLastError()); BCL_INTERNAL_ERROR_CHECK(iResult > 0); // I don't know why MultiByteToWide char would return negative but let's make sure rcch = iResult; BCL_MAYFAIL_EPILOG_INTERNAL } static inline TCallDisposition CopyIntoBuffer( const TMutablePair &rpairOut, const CNtMBCSToUnicodeDataIn &rddi, const TConstantNonNativePair &rpairIn, CNtMBCSToUnicodeDataOut &rddo ) { BCL_MAYFAIL_PROLOG BCL_PARAMETER_CHECK(rpairIn.GetCount() <= INT_MAX); // limitation imposed by MultiByteToWideChar API BCL_PARAMETER_CHECK(rpairOut.GetCount() <= INT_MAX); // might make sense to just clamp at INT_MAX but at least we fail correctly instead of silent truncation int iResult = ::MultiByteToWideChar( rddi.m_CodePage, rddi.m_dwFlags | MB_ERR_INVALID_CHARS, rpairIn.GetPointer(), static_cast(rpairIn.GetCount()), rpairOut.GetPointer(), static_cast(rpairOut.GetCount())); if (iResult == 0) return TCallDisposition::FromLastError(); BCL_INTERNAL_ERROR_CHECK(iResult > 0); // I don't know why MultiByteToWide char would return negative but let's make sure BCL_MAYFAIL_EPILOG_INTERNAL } static inline TCallDisposition CopyIntoBuffer( const TMutableNonNativePair &rpairOut, const CNtUnicodeToMBCSDataIn &rddi, const TConstantPair &rpairIn, CNtUnicodeToMBCSDataOut &rddo, SIZE_T &rcchWritten ) { BCL_MAYFAIL_PROLOG BCL_PARAMETER_CHECK(rpairIn.GetCount() <= INT_MAX); BCL_PARAMETER_CHECK(rpairOut.GetCount() <= INT_MAX); // If we want to have any chance of returning ERROR_BUFFER_OVERFLOW // either we need to play the "two null chars at the end of the // buffer" game or we have to do this in two passes - one to // get the desired length and one to actually move the data. // // If someone has an approach which doesn't lose correctness but // avoids the double conversion, be my guest and fix this. -mgrier 2/6/2002 int iResult = ::WideCharToMultiByte( rddi.m_CodePage, rddi.m_dwFlags | WC_NO_BEST_FIT_CHARS, rpairIn.GetPointer(), static_cast(rpairIn.GetCount()), NULL, 0, rddo.m_lpDefaultChar, rddo.m_lpUsedDefaultChar); if (iResult == 0) return TCallDisposition::FromLastError(); BCL_INTERNAL_ERROR_CHECK(iResult >= 0); if (iResult > static_cast(rpairOut.GetCount())) BCL_ORIGINATE_ERROR(TCallDisposition::BufferOverflow()); iResult = ::WideCharToMultiByte( rddi.m_CodePage, rddi.m_dwFlags | WC_NO_BEST_FIT_CHARS, rpairIn.GetPointer(), static_cast(rpairIn.GetCount()), rpairOut.GetPointer(), static_cast(rpairOut.GetCount()), rddo.m_lpDefaultChar, rddo.m_lpUsedDefaultChar); if (iResult == 0) return TCallDisposition::FromLastError(); BCL_INTERNAL_ERROR_CHECK(iResult >= 0); rcchWritten = iResult; BCL_MAYFAIL_EPILOG_INTERNAL } static inline TCallDisposition AllocateAndCopyIntoBuffer( TMutableNonNativeString &rpszOut, const CNtUnicodeToMBCSDataIn &rddi, const TConstantPair &rpairIn, CNtUnicodeToMBCSDataOut &rddo, SIZE_T &rcchWritten ) { BCL_MAYFAIL_PROLOG TSizeT cchInputString, cchBuffer; TBuffer::TTraits::TPSTRAllocationHelper pszTemp; BCL_PARAMETER_CHECK(rpairIn.GetCount() <= INT_MAX); BCL_IFCALLFAILED_EXIT(TBuffer::TTraits::MapStringCchToBufferCch(rpairIn.GetCount(), cchInputString)); if (cchInputString > INT_MAX) BCL_ORIGINATE_ERROR(TCallDisposition::BufferOverflow()); int iResult = ::WideCharToMultiByte( rddi.m_CodePage, rddi.m_dwFlags | WC_NO_BEST_FIT_CHARS, rpairIn.GetPointer(), static_cast(cchInputString), NULL, 0, rddo.m_lpDefaultChar, rddo.m_lpUsedDefaultChar); if (iResult == 0) return TCallDisposition::FromLastError(); BCL_INTERNAL_ERROR_CHECK(iResult >= 0); cchBuffer = iResult; BCL_IFCALLFAILED_EXIT(pszTemp.Allocate(cchBuffer)); INT iResult2 = ::WideCharToMultiByte( rddi.m_CodePage, rddi.m_dwFlags | WC_NO_BEST_FIT_CHARS, rpairIn.GetPointer(), static_cast(cchInputString), static_cast(pszTemp), iResult, rddo.m_lpDefaultChar, rddo.m_lpUsedDefaultChar); if (iResult2 == 0) return TCallDisposition::FromLastError(); BCL_INTERNAL_ERROR_CHECK(iResult2 >= 0); BCL_IFCALLFAILED_EXIT(TBuffer::TTraits::MapBufferCchToStringCch(iResult2, rcchWritten)); rpszOut = pszTemp.Detach(); BCL_MAYFAIL_EPILOG_INTERNAL } static inline TCallDisposition AllocateAndCopyIntoBuffer( TMutableString &rstringOut, const TConstantPair &rpairIn, TSizeT &rcchWritten ) { BCL_MAYFAIL_PROLOG TSizeT cchString = rpairIn.GetCount(); TSizeT cchBuffer; TBuffer::TTraits::TPWSTRAllocationHelper pszTemp; BCL_IFCALLFAILED_EXIT(TBuffer::TTraits::MapStringCchToBufferCch(cchString, cchBuffer)); BCL_IFCALLFAILED_EXIT(pszTemp.Allocate(cchBuffer)); BCL_IFCALLFAILED_EXIT(TBuffer::TTraits::CopyIntoBuffer(TMutablePair(static_cast(pszTemp), cchBuffer), rpairIn, rcchWritten)); rstringOut = pszTemp.Detach(); BCL_MAYFAIL_EPILOG_INTERNAL } }; // class CNtNullTerminatedUnicodeStringAlgorithms }; // namespace BCL #endif // !defined(_BCL_NTUNICODESTRINGALGORITHMS_H_INCLUDED_)