891 lines
27 KiB
C++
891 lines
27 KiB
C++
#if !defined(_BCL_W32UNICODESTRINGALGORITHMS_H_INCLUDED_)
|
|
#define _BCL_W32UNICODESTRINGALGORITHMS_H_INCLUDED_
|
|
|
|
#pragma once
|
|
|
|
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
bcl_w32unicodestringalgorithms.h
|
|
|
|
Abstract:
|
|
|
|
|
|
Author:
|
|
|
|
Michael Grier (MGrier) 2/6/2002
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <windows.h>
|
|
|
|
#include <bcl_inlinestring.h>
|
|
#include <bcl_unicodechartraits.h>
|
|
#include <bcl_w32common.h>
|
|
#include <bcl_vararg.h>
|
|
|
|
#include <limits.h>
|
|
|
|
namespace BCL
|
|
{
|
|
|
|
template <typename TBuffer, typename TCallDispositionT>
|
|
class CWin32NullTerminatedUnicodeStringAlgorithms
|
|
{
|
|
public:
|
|
typedef CWin32NullTerminatedUnicodeStringAlgorithms TThis;
|
|
|
|
typedef TCallDispositionT TCallDisposition;
|
|
typedef CWin32StringComparisonResult TComparisonResult;
|
|
|
|
typedef BCL::CConstantPointerAndCountPair<WCHAR, SIZE_T> TConstantPair;
|
|
typedef BCL::CMutablePointerAndCountPair<WCHAR, SIZE_T> TMutablePair;
|
|
|
|
typedef CWin32CaseInsensitivityData TCaseInsensitivityData;
|
|
typedef SIZE_T TSizeT;
|
|
|
|
typedef CWin32MBCSToUnicodeDataIn TDecodingDataIn;
|
|
typedef CWin32MBCSToUnicodeDataOut TDecodingDataOut;
|
|
typedef CWin32UnicodeToMBCSDataIn TEncodingDataIn;
|
|
typedef CWin32UnicodeToMBCSDataOut TEncodingDataOut;
|
|
|
|
typedef CConstantPointerAndCountPair<CHAR, SIZE_T> TConstantNonNativePair;
|
|
typedef CMutablePointerAndCountPair<CHAR, SIZE_T> 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<TBuffer *>(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
|
|
{
|
|
WORD wCharType = 0;
|
|
|
|
if (!::GetStringTypeExW(LOCALE_INVARIANT, CT_CTYPE3, &wch, 1, &wCharType))
|
|
BCL_ORIGINATE_ERROR(TCallDisposition::FromLastError());
|
|
|
|
// If it's not one of these types of nonspacing marks
|
|
rfIsLegal = ((wCharType & (C3_NONSPACING | C3_DIACRITIC | C3_VOWELMARK)) == 0);
|
|
}
|
|
}
|
|
|
|
BCL_MAYFAIL_EPILOG_INTERNAL
|
|
}
|
|
|
|
static inline TCallDisposition __fastcall UpperCase(BCL::CBaseString *p, const CWin32CaseInsensitivityData &rcid)
|
|
{
|
|
BCL_MAYFAIL_PROLOG
|
|
|
|
BCL_PARAMETER_CHECK(TBuffer::TTraits::GetStringCch(p) <= INT_MAX);
|
|
|
|
// LCMapStringW() seems to be nice and allow for in-place case changing...
|
|
|
|
int iResult =
|
|
::LCMapStringW(
|
|
rcid.m_lcid,
|
|
(rcid.m_dwCmpFlags & ~(NORM_IGNORECASE)) | LCMAP_UPPERCASE,
|
|
TBuffer::TTraits::GetBufferPtr(p),
|
|
static_cast<INT>(TBuffer::TTraits::GetStringCch(p)),
|
|
TBuffer::TTraits::GetMutableBufferPtr(p),
|
|
static_cast<INT>(TBuffer::TTraits::GetStringCch(p)));
|
|
|
|
if (iResult == 0)
|
|
return TCallDisposition::FromLastError();
|
|
|
|
BCL_INTERNAL_ERROR_CHECK(iResult == static_cast<INT>(TBuffer::TTraits::GetStringCch(p)));
|
|
|
|
BCL_MAYFAIL_EPILOG_INTERNAL
|
|
}
|
|
|
|
static inline TCallDisposition __fastcall LowerCase(BCL::CBaseString *p, const CWin32CaseInsensitivityData &rcid)
|
|
{
|
|
BCL_MAYFAIL_PROLOG
|
|
|
|
BCL_PARAMETER_CHECK(TBuffer::TTraits::GetStringCch(p) <= INT_MAX);
|
|
|
|
// LCMapStringW() seems to be nice and allow for in-place case changing...
|
|
|
|
int iResult =
|
|
::LCMapStringW(
|
|
rcid.m_lcid,
|
|
(rcid.m_dwCmpFlags & ~(NORM_IGNORECASE)) | LCMAP_LOWERCASE,
|
|
TBuffer::TTraits::GetBufferPtr(p),
|
|
static_cast<INT>(TBuffer::TTraits::GetStringCch(p)),
|
|
TBuffer::TTraits::GetMutableBufferPtr(p),
|
|
static_cast<INT>(TBuffer::TTraits::GetStringCch(p)));
|
|
|
|
if (iResult == 0)
|
|
return TCallDisposition::FromLastError();
|
|
|
|
BCL_INTERNAL_ERROR_CHECK(iResult == static_cast<INT>(TBuffer::TTraits::GetStringCch(p)));
|
|
|
|
BCL_MAYFAIL_EPILOG_INTERNAL
|
|
}
|
|
|
|
template <typename TSomeInputType1, typename TSomeInputType2>
|
|
static inline TCallDisposition __fastcall
|
|
EqualStringsI(
|
|
const TSomeInputType1 &rinput1,
|
|
const TSomeInputType2 &rinput2,
|
|
const CWin32CaseInsensitivityData &rcid,
|
|
bool &rfMatches
|
|
)
|
|
{
|
|
BCL_MAYFAIL_PROLOG
|
|
|
|
rfMatches = false;
|
|
|
|
BCL_PARAMETER_CHECK(TBuffer::TTraits::GetInputCch(rinput1) <= INT_MAX);
|
|
BCL_PARAMETER_CHECK(TBuffer::TTraits::GetInputCch(rinput2) <= INT_MAX);
|
|
|
|
int i = ::CompareStringW(
|
|
rcid.m_lcid,
|
|
rcid.m_dwCmpFlags,
|
|
TBuffer::TTraits::GetInputPtr(rinput1),
|
|
static_cast<INT>(TBuffer::TTraits::GetInputCch(rinput1)),
|
|
TBuffer::TTraits::GetInputPtr(rinput2),
|
|
static_cast<INT>(TBuffer::TTraits::GetInputCch(rinput2)));
|
|
|
|
if (i == 0)
|
|
BCL_ORIGINATE_ERROR(TCallDisposition::FromLastError());
|
|
|
|
rfMatches = (i == CSTR_EQUAL);
|
|
|
|
BCL_MAYFAIL_EPILOG_INTERNAL
|
|
}
|
|
|
|
template <typename TSomeInputType1, typename TSomeInputType2>
|
|
static inline TCallDisposition __fastcall CompareStringsI(
|
|
const TSomeInputType1 &rinput1,
|
|
const TSomeInputType2 &rinput2,
|
|
const CWin32CaseInsensitivityData &rcid,
|
|
TComparisonResult &rcr
|
|
)
|
|
{
|
|
BCL_MAYFAIL_PROLOG
|
|
|
|
BCL_PARAMETER_CHECK(TBuffer::TTraits::GetInputCch(rinput1) <= INT_MAX);
|
|
BCL_PARAMETER_CHECK(TBuffer::TTraits::GetInputCch(rinput2) <= INT_MAX);
|
|
|
|
int i = ::CompareStringW(
|
|
rcid.m_lcid,
|
|
rcid.m_dwCmpFlags,
|
|
TBuffer::TTraits::GetInputPtr(rinput1),
|
|
static_cast<INT>(TBuffer::TTraits::GetInputCch(rinput1)),
|
|
TBuffer::TTraits::GetInputPtr(rinput2),
|
|
static_cast<INT>(TBuffer::TTraits::GetInputCch(rinput2)));
|
|
|
|
if (i == 0)
|
|
BCL_ORIGINATE_ERROR(TCallDisposition::FromLastError());
|
|
|
|
if (i == CSTR_LESS_THAN)
|
|
rcr.SetLessThan();
|
|
else if (i == CSTR_EQUAL)
|
|
rcr.SetEqualTo();
|
|
else
|
|
{
|
|
BCL_INTERNAL_ERROR_CHECK(i == CSTR_GREATER_THAN);
|
|
rcr.SetGreaterThan();
|
|
}
|
|
|
|
BCL_MAYFAIL_EPILOG_INTERNAL
|
|
}
|
|
|
|
template <typename TSomeCharacterMatcher>
|
|
inline
|
|
static
|
|
TCallDisposition
|
|
ContainsI(
|
|
const TConstantPair &rpair,
|
|
const TSomeCharacterMatcher &rscm,
|
|
const CWin32CaseInsensitivityData &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<cch; )
|
|
{
|
|
SIZE_T cchConsumed = 0;
|
|
bool fMatch = false;
|
|
|
|
BCL_IFCALLFAILED_EXIT(rscm.MatchI(rcid, prgch, cchConsumed, fMatch));
|
|
|
|
BCL_INTERNAL_ERROR_CHECK(cchConsumed != 0);
|
|
|
|
if (fMatch)
|
|
break;
|
|
|
|
BCL_IFCALLFAILED_EXIT(TBuffer::TTraits::AddWithOverflowCheck(i, cchConsumed, i));
|
|
}
|
|
|
|
if (i != cch)
|
|
rfFound = true;
|
|
|
|
BCL_MAYFAIL_EPILOG_INTERNAL
|
|
}
|
|
|
|
|
|
inline
|
|
static
|
|
TCallDisposition
|
|
ContainsI(
|
|
const TConstantPair &rpair,
|
|
WCHAR ch,
|
|
const CWin32CaseInsensitivityData &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<cch; i++)
|
|
{
|
|
int iResult = ::CompareStringW(rcid.m_lcid, rcid.m_dwCmpFlags, prgch++, 1, &ch, 1);
|
|
if (iResult == 0)
|
|
return TCallDisposition::FromLastError();
|
|
if (iResult == CSTR_EQUAL)
|
|
break;
|
|
}
|
|
|
|
if (i != cch)
|
|
rfFound = true;
|
|
|
|
BCL_MAYFAIL_EPILOG_INTERNAL
|
|
}
|
|
|
|
inline
|
|
static
|
|
TCallDisposition
|
|
ContainsI(
|
|
const TConstantPair &rpair,
|
|
const TConstantPair &rpairCandidate,
|
|
const CWin32CaseInsensitivityData &rcid,
|
|
bool &rfFound
|
|
)
|
|
{
|
|
BCL_MAYFAIL_PROLOG
|
|
|
|
rfFound = false;
|
|
|
|
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
|
|
rfFound = true;
|
|
}
|
|
else
|
|
{
|
|
SIZE_T cch = rpair.GetCount();
|
|
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<cch; i++)
|
|
{
|
|
int iResult = ::CompareStringW(
|
|
rcid.m_lcid,
|
|
rcid.m_dwCmpFlags,
|
|
prgch,
|
|
static_cast<INT>(cchCandidate),
|
|
prgwchCandidate,
|
|
static_cast<INT>(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 CWin32CaseInsensitivityData &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<cch; i++)
|
|
{
|
|
int iResult = ::CompareStringW(rcid.m_lcid, rcid.m_dwCmpFlags, prgch++, 1, &ch, 1);
|
|
if (iResult == 0)
|
|
return TCallDisposition::FromLastError();
|
|
if (iResult == CSTR_EQUAL)
|
|
break;
|
|
}
|
|
|
|
rich = i;
|
|
|
|
BCL_MAYFAIL_EPILOG_INTERNAL
|
|
}
|
|
|
|
inline
|
|
static
|
|
TCallDisposition
|
|
FindFirstI(
|
|
const TConstantPair &rpair,
|
|
const TConstantPair &rpairCandidate,
|
|
const CWin32CaseInsensitivityData &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
|
|
{
|
|
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<cch; i++)
|
|
{
|
|
int iResult = ::CompareStringW(
|
|
rcid.m_lcid,
|
|
rcid.m_dwCmpFlags,
|
|
prgch,
|
|
static_cast<INT>(cchCandidate),
|
|
prgwchCandidate,
|
|
static_cast<INT>(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 CWin32CaseInsensitivityData &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 CWin32CaseInsensitivityData &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<cch; i++)
|
|
{
|
|
int iResult = ::CompareStringW(
|
|
rcid.m_lcid,
|
|
rcid.m_dwCmpFlags,
|
|
prgch,
|
|
static_cast<INT>(cchCandidate),
|
|
prgwchCandidate,
|
|
static_cast<INT>(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 CWin32CaseInsensitivityData &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; i<cchBuffer; i++)
|
|
{
|
|
BCL_IFCALLFAILED_EXIT(TBuffer::TTraits::ContainsI(rpairSet, prgwchBuffer[i], rcid, fFound));
|
|
if (!fFound)
|
|
break;
|
|
}
|
|
|
|
rich = i;
|
|
|
|
BCL_MAYFAIL_EPILOG_INTERNAL
|
|
}
|
|
|
|
static inline TCallDisposition __fastcall ComplementSpanI(const TConstantPair &rpairBuffer, const TConstantPair &rpairSet, const CWin32CaseInsensitivityData &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; i<cchBuffer; i++)
|
|
{
|
|
BCL_IFCALLFAILED_EXIT(TBuffer::TTraits::ContainsI(rpairSet, prgwchBuffer[i], rcid, fFound));
|
|
if (fFound)
|
|
break;
|
|
}
|
|
|
|
rich = i;
|
|
|
|
BCL_MAYFAIL_EPILOG_INTERNAL
|
|
}
|
|
|
|
static inline TCallDisposition __fastcall ReverseSpanI(const TConstantPair &rpairBuffer, const TConstantPair &rpairSet, const CWin32CaseInsensitivityData &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-1], rcid, fFound));
|
|
if (!fFound)
|
|
break;
|
|
}
|
|
|
|
rich = i;
|
|
|
|
BCL_MAYFAIL_EPILOG_INTERNAL
|
|
}
|
|
|
|
static inline TCallDisposition __fastcall ReverseComplementSpanI(const TConstantPair &rpairBuffer, const TConstantPair &rpairSet, const CWin32CaseInsensitivityData &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 CWin32MBCSToUnicodeDataIn &rddi,
|
|
const TConstantNonNativePair &rpair,
|
|
CWin32MBCSToUnicodeDataOut &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<INT>(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 CWin32MBCSToUnicodeDataIn &rddi,
|
|
const TConstantNonNativePair &rpairIn,
|
|
CWin32MBCSToUnicodeDataOut &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<INT>(rpairIn.GetCount()),
|
|
rpairOut.GetPointer(),
|
|
static_cast<INT>(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 CWin32UnicodeToMBCSDataIn &rddi,
|
|
const TConstantPair &rpairIn,
|
|
CWin32UnicodeToMBCSDataOut &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<INT>(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<INT>(rpairOut.GetCount()))
|
|
BCL_ORIGINATE_ERROR(TCallDisposition::BufferOverflow());
|
|
|
|
iResult = ::WideCharToMultiByte(
|
|
rddi.m_CodePage,
|
|
rddi.m_dwFlags | WC_NO_BEST_FIT_CHARS,
|
|
rpairIn.GetPointer(),
|
|
static_cast<INT>(rpairIn.GetCount()),
|
|
rpairOut.GetPointer(),
|
|
static_cast<INT>(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 CWin32UnicodeToMBCSDataIn &rddi,
|
|
const TConstantPair &rpairIn,
|
|
CWin32UnicodeToMBCSDataOut &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<INT>(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<INT>(cchInputString),
|
|
static_cast<PSTR>(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<PWSTR>(pszTemp), cchBuffer), rpairIn, rcchWritten));
|
|
rstringOut = pszTemp.Detach();
|
|
BCL_MAYFAIL_EPILOG_INTERNAL
|
|
}
|
|
}; // class CWin32NullTerminatedUnicodeStringAlgorithms
|
|
|
|
}; // namespace BCL
|
|
|
|
#endif // !defined(_BCL_W32UNICODESTRINGALGORITHMS_H_INCLUDED_)
|