/*++ Copyright (c) Microsoft Corporation Module Name: fusionbuffer.h Abstract: Author: Revision History: --*/ #if !defined(FUSION_INC_FUSIONBUFFER_H_INCLUDED_) #define FUSION_INC_FUSIONBUFFER_H_INCLUDED_ #pragma once #include #include #include "arrayhelp.h" #include "smartref.h" #include "returnstrategy.h" #include "fusionstring.h" #include "fusiontrace.h" #include "fusionchartraits.h" // avoid circular reference to Util.h BOOL FusionpIsPathSeparator(WCHAR ch); BOOL FusionpIsDriveLetter(WCHAR ch); // // This header file defines the Fusion character string buffer class. // The purpose of this class is to encapsulate common activities that // callers want to do with character string buffers and handle it in // a generic fashion. A principle tenet of this class is that it is // not a string class, although one could consider building a string // class upon it. // // The buffer maintains a certain amount of storage within the buffer // object itself, and if more storage is required, a buffer is // dynamically allocated from a heap. // // // Like the STL string class, we use a helper class called a "character // traits" class to provide the actual code to manipulate character string // buffers with a specific encoding. // // All the members are inline static and with normal optimization turned // on, the C++ compiler generates code that fully meets expectations. // // // We provide two implementations: one for Unicode strings, and another // template class for MBCS strings. The code page of the string is a // template parameter for the MBCS string, so without any extra storage // wasted per-instance, code can separately handle MBCS strings which // are expected to be in the thread-default windows code page (CP_THREAD_ACP), // process-default windows code page (CP_ACP) or even a particular code // page (e.g. CP_UTF8). // // // This template class uses a number of non-type template parameters to // control things like growth algorithms etc. As a result there are // many comparisons of template parameters against well-known constant // values, for which the compiler generates warning C4127. We'll turn that // warning off. // #pragma warning(disable:4127) #pragma warning(disable:4284) #if !defined(FUSION_DEFAULT_STRINGBUFFER_CHARS) #define FUSION_DEFAULT_STRINGBUFFER_CHARS (MAX_PATH) #endif #if !defined(FUSION_DEFAULT_TINY_STRINGBUFFER_CHARS) #define FUSION_DEFAULT_TINY_STRINGBUFFER_CHARS (8) #endif #if !defined(FUSION_DEFAULT_SMALL_STRINGBUFFER_CHARS) #define FUSION_DEFAULT_SMALL_STRINGBUFFER_CHARS (64) #endif #if !defined(FUSION_DEFAULT_MEDIUM_STRINGBUFFER_CHARS) #define FUSION_DEFAULT_MEDIUM_STRINGBUFFER_CHARS (128) #endif enum EIfNoExtension { eAddIfNoExtension, eDoNothingIfNoExtension, eErrorIfNoExtension }; enum ECaseConversionDirection { eConvertToUpperCase, eConvertToLowerCase }; enum EPreserveContents { ePreserveBufferContents, eDoNotPreserveBufferContents }; template class CGenericStringBufferAccessor; template class CGenericBaseStringBuffer { friend TCharTraits; friend CGenericStringBufferAccessor; // // These two are to induce build breaks on people doing sb1 = sb2 // CGenericBaseStringBuffer& operator=(PCWSTR OtherString); CGenericBaseStringBuffer& operator=(CGenericBaseStringBuffer &rOtherString); public: typedef typename TCharTraits::TChar TChar; typedef typename TCharTraits::TMutableString TMutableString; typedef typename TCharTraits::TConstantString TConstantString; typedef CGenericStringBufferAccessor TAccessor; inline static TChar NullCharacter() { return TCharTraits::NullCharacter(); } inline static bool IsNullCharacter(TChar ch) { return TCharTraits::IsNullCharacter(ch); } inline static TChar PreferredPathSeparator() { return TCharTraits::PreferredPathSeparator(); } inline static TConstantString PreferredPathSeparatorString() { return TCharTraits::PreferredPathSeparatorString(); } inline static TConstantString PathSeparators() { return TCharTraits::PathSeparators(); } inline static bool IsPathSeparator(TChar ch) { return TCharTraits::IsPathSeparator(ch); } inline static TConstantString DotString() { return TCharTraits::DotString(); } inline static SIZE_T DotStringCch() { return TCharTraits::DotStringCch(); } inline static TChar DotChar() { return TCharTraits::DotChar(); } protected: // You may not instantiate an instance of this class directly; you need to provide a derived // class which adds allocation/deallocation particulars. CGenericBaseStringBuffer() : m_prgchBuffer(NULL), m_cchBuffer(0), m_cAttachedAccessors(0), m_cch(0) { } // // Note that somewhat counter-intuitively, there is neither an assignment operator, // copy constructor or constructor taking a TConstantString. This is necessary // because such a constructor would need to perform a dynamic allocation // if the path passed in were longer than nInlineChars which could fail and // since we do not throw exceptions, constructors may not fail. Instead the caller // must just perform the default construction and then use the Assign() member // function, remembering of course to check its return status. // ~CGenericBaseStringBuffer() { ASSERT_NTC(m_cAttachedAccessors == 0); } inline void IntegrityCheck() const { #if DBG ASSERT_NTC(m_cch < m_cchBuffer); #endif // DBG } // Derived constructors should call this to get the initial buffer pointers set up. inline void InitializeInlineBuffer() { ASSERT_NTC(m_prgchBuffer == NULL); ASSERT_NTC(m_cchBuffer == 0); m_prgchBuffer = this->GetInlineBuffer(); m_cchBuffer = this->GetInlineBufferCch(); } VOID AttachAccessor(TAccessor *) { ::InterlockedIncrement(&m_cAttachedAccessors); } VOID DetachAccessor(TAccessor *) { ::InterlockedDecrement(&m_cAttachedAccessors); } virtual BOOL Win32AllocateBuffer(SIZE_T cch, TMutableString &rpsz) const = 0; virtual VOID DeallocateBuffer(TMutableString sz) const = 0; virtual TMutableString GetInlineBuffer() const = 0; virtual SIZE_T GetInlineBufferCch() const = 0; public: BOOL Win32Assign(PCWSTR psz, SIZE_T cchIn) { FN_PROLOG_WIN32 ASSERT(static_cast(cchIn) >= 0); this->IntegrityCheck(); SIZE_T cchIncludingTrailingNull; // it would seem innocuous to allow assigns that don't resize the buffer to not // invalidate accessors, but that makes finding such bugs subject to even more // strenuous coverage problems than this simple error. The simple rule is that // you should not have an accessor attached to a string buffer when you use // any of member functions that may mutate the value. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0); IFW32FALSE_EXIT(TCharTraits::Win32DetermineRequiredCharacters(psz, cchIn, cchIncludingTrailingNull)); // Only force the buffer to be dynamically grown if the new contents do not // fit in the old buffer. if (cchIncludingTrailingNull > m_cchBuffer) IFW32FALSE_EXIT(this->Win32ResizeBufferPreserveContentsInternal(cchIncludingTrailingNull)); IFW32FALSE_EXIT(TCharTraits::Win32CopyIntoBuffer(m_prgchBuffer, m_cchBuffer, psz, cchIn)); ASSERT(cchIncludingTrailingNull <= m_cchBuffer); ASSERT((cchIncludingTrailingNull == 0) || this->IsNullCharacter(m_prgchBuffer[cchIncludingTrailingNull - 1])); // cch was the buffer size we needed (including the trailing null); we don't need the trailing // null any more... m_cch = cchIncludingTrailingNull - 1; FN_EPILOG } BOOL Win32Assign(PCSTR psz, SIZE_T cchIn) { FN_PROLOG_WIN32 ASSERT(static_cast(cchIn) >= 0); this->IntegrityCheck(); SIZE_T cchIncludingTrailingNull; // it would seem innocuous to allow assigns that don't resize the buffer to not // invalidate accessors, but that makes finding such bugs subject to even more // strenuous coverage problems than this simple error. The simple rule is that // you should not have an accessor attached to a string buffer when you use // any of member functions that may mutate the value. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0); IFW32FALSE_EXIT(TCharTraits::Win32DetermineRequiredCharacters(psz, cchIn, cchIncludingTrailingNull)); // Only force the buffer to be dynamically grown if the new contents do not // fit in the old buffer. if (cchIncludingTrailingNull > m_cchBuffer) IFW32FALSE_EXIT(this->Win32ResizeBufferPreserveContentsInternal(cchIncludingTrailingNull)); IFW32FALSE_EXIT(TCharTraits::Win32CopyIntoBuffer(m_prgchBuffer, m_cchBuffer, psz, cchIn)); ASSERT(cchIncludingTrailingNull <= m_cchBuffer); ASSERT((cchIncludingTrailingNull == 0) || this->IsNullCharacter(m_prgchBuffer[cchIncludingTrailingNull - 1])); // cch was the buffer size we needed (including the trailing null); we don't need the trailing // null any more... m_cch = cchIncludingTrailingNull - 1; FN_EPILOG } BOOL Win32Assign(const UNICODE_STRING* NtString) { return Win32Assign(NtString->Buffer, RTL_STRING_GET_LENGTH_CHARS(NtString)); } BOOL Win32Assign(const ANSI_STRING* NtString) { return Win32Assign(NtString->Buffer, RTL_STRING_GET_LENGTH_CHARS(NtString)); } BOOL Win32Append(const UNICODE_STRING* NtString) { return this->Win32Append(NtString->Buffer, RTL_STRING_GET_LENGTH_CHARS(NtString)); } BOOL Win32AppendPathElement(const UNICODE_STRING* NtString) { return this->Win32AppendPathElement(NtString->Buffer, RTL_STRING_GET_LENGTH_CHARS(NtString)); } BOOL Win32Assign(const CGenericBaseStringBuffer &r) { return this->Win32Assign(r, r.Cch()); } BOOL Win32AssignWVa(SIZE_T cStrings, va_list ap) { this->IntegrityCheck(); BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); TMutableString pszCursor; SIZE_T cchIncludingTrailingNull = 1; // leave space for trailing null... SIZE_T cchTemp = 0; SIZE_T i = 0; va_list ap2 = ap; // it would seem innocuous to allow assigns that don't resize the buffer to not // invalidate accessors, but that makes finding such bugs subject to even more // strenuous coverage problems than this simple error. The simple rule is that // you should not have an accessor attached to a string buffer when you use // any of member functions that may mutate the value. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0); for (i=0; i(cchArg); SIZE_T cchRequired; IFW32FALSE_EXIT(TCharTraits::Win32DetermineRequiredCharacters(psz, cchThis, cchRequired)); ASSERT((cchRequired != 0) || (cchThis == 0)); cchIncludingTrailingNull += (cchRequired - 1); } IFW32FALSE_EXIT(this->Win32ResizeBuffer(cchIncludingTrailingNull, eDoNotPreserveBufferContents)); pszCursor = m_prgchBuffer; cchTemp = cchIncludingTrailingNull; for (i=0; i(cchArg); IFW32FALSE_EXIT(TCharTraits::Win32CopyIntoBufferAndAdvanceCursor(pszCursor, cchTemp, psz, cchThis)); } *pszCursor++ = this->NullCharacter(); ASSERT(cchTemp == 1); ASSERT(static_cast(pszCursor - m_prgchBuffer) == cchIncludingTrailingNull); m_cch = (cchIncludingTrailingNull - 1); FN_EPILOG } BOOL Win32AssignW(ULONG cStrings, ...) { this->IntegrityCheck(); BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); va_list ap; va_start(ap, cStrings); IFW32FALSE_EXIT(this->Win32AssignWVa(cStrings, ap)); fSuccess = TRUE; Exit: va_end(ap); return fSuccess; } BOOL Win32AssignFill(TChar ch, SIZE_T cch) { FN_PROLOG_WIN32 TMutableString Cursor; ASSERT(static_cast(cch) >= 0); IFW32FALSE_EXIT(this->Win32ResizeBuffer(cch + 1, eDoNotPreserveBufferContents)); Cursor = m_prgchBuffer; while (cch > 0) { *Cursor++ = ch; cch--; } *Cursor = NullCharacter(); m_cch = (Cursor - m_prgchBuffer); FN_EPILOG } BOOL Win32Append(PCWSTR sz, SIZE_T cchIn) { this->IntegrityCheck(); BOOL fSuccess = FALSE; ASSERT_NTC(static_cast(cchIn) >= 0); SIZE_T cchIncludingTrailingNull; // note that cch will include space for a tailing null character // it would seem innocuous to allow assigns that don't resize the buffer to not // invalidate accessors, but that makes finding such bugs subject to even more // strenuous coverage problems than this simple error. The simple rule is that // you should not have an accessor attached to a string buffer when you use // any of member functions that may mutate the value. ASSERT_NTC(m_cAttachedAccessors == 0); if (!TCharTraits::Win32DetermineRequiredCharacters(sz, cchIn, cchIncludingTrailingNull)) goto Exit; // Bypass all this junk if the string to append is empty. if (cchIncludingTrailingNull > 1) { if (!this->Win32ResizeBufferPreserveContentsInternal(m_cch + cchIncludingTrailingNull)) goto Exit; if (!TCharTraits::Win32CopyIntoBuffer(&m_prgchBuffer[m_cch], m_cchBuffer - m_cch, sz, cchIn)) goto Exit; m_cch += (cchIncludingTrailingNull - 1); } fSuccess = TRUE; Exit: return fSuccess; } BOOL Win32Append(PCSTR sz, SIZE_T cchIn) { this->IntegrityCheck(); BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); ASSERT(static_cast(cchIn) >= 0); SIZE_T cchIncludingTrailingNull; // it would seem innocuous to allow assigns that don't resize the buffer to not // invalidate accessors, but that makes finding such bugs subject to even more // strenuous coverage problems than this simple error. The simple rule is that // you should not have an accessor attached to a string buffer when you use // any of member functions that may mutate the value. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0); IFW32FALSE_EXIT(TCharTraits::Win32DetermineRequiredCharacters(sz, cchIn, cchIncludingTrailingNull)); // Bypass all this junk if the string to append is empty. if (cchIncludingTrailingNull > 1) { IFW32FALSE_EXIT(this->Win32ResizeBufferPreserveContentsInternal(m_cch + cchIncludingTrailingNull)); IFW32FALSE_EXIT(TCharTraits::Win32CopyIntoBuffer(&m_prgchBuffer[m_cch], m_cchBuffer - m_cch, sz, cchIn)); m_cch += (cchIncludingTrailingNull - 1); this->IntegrityCheck(); } FN_EPILOG } BOOL Win32Append(const CGenericBaseStringBuffer &r) { return this->Win32Append(r, r.Cch()); } BOOL Win32Append(WCHAR wch) { WCHAR rgwch[1] = { wch }; return this->Win32Append(rgwch, 1); } BOOL Win32AppendFill(TChar ch, SIZE_T cch) { FN_PROLOG_WIN32 ASSERT(static_cast(cch) >= 0); TMutableString Cursor; IFW32FALSE_EXIT(this->Win32ResizeBufferPreserveContentsInternal(m_cch + cch + 1)); Cursor = m_prgchBuffer + m_cch; while (cch > 0) { *Cursor++ = ch; cch--; } *Cursor = NullCharacter(); m_cch = Cursor - m_prgchBuffer; FN_EPILOG } BOOL Win32Prepend(const CGenericBaseStringBuffer& other ) { return this->Win32Prepend(other, other.Cch()); } BOOL Win32Prepend(TConstantString sz, SIZE_T cchIn) { this->IntegrityCheck(); BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); ASSERT(static_cast(cchIn) >= 0); SIZE_T cchIncludingTrailingNull; // note that cch will include space for a tailing null character // it would seem innocuous to allow assigns that don't resize the buffer to not // invalidate accessors, but that makes finding such bugs subject to even more // strenuous coverage problems than this simple error. The simple rule is that // you should not have an accessor attached to a string buffer when you use // any of member functions that may mutate the value. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0); if ( m_cch == 0 ) { IFW32FALSE_EXIT(this->Win32Assign(sz, cchIn)); } else { // // Enlarge the buffer, move the current data to past where the new data will need // to go, copy in the new data, and place the trailing null. // TChar SavedChar = m_prgchBuffer[0]; IFW32FALSE_EXIT(TCharTraits::Win32DetermineRequiredCharacters(sz, cchIn, cchIncludingTrailingNull)); IFW32FALSE_EXIT(this->Win32ResizeBufferPreserveContentsInternal(m_cch + cchIncludingTrailingNull)); // Move current buffer "up" MoveMemory(m_prgchBuffer + ( cchIncludingTrailingNull - 1), m_prgchBuffer, (m_cch + 1) * sizeof(TChar)); // Copy from the source string into the buffer. IFW32FALSE_EXIT(TCharTraits::Win32CopyIntoBuffer( this->m_prgchBuffer, this->m_cchBuffer, sz, cchIn)); m_prgchBuffer[cchIncludingTrailingNull - 1] = SavedChar; m_cch += cchIncludingTrailingNull - 1; } FN_EPILOG } BOOL Win32Prepend(TChar ch) { FN_PROLOG_WIN32 IFW32FALSE_EXIT(this->Win32ResizeBufferPreserveContentsInternal(m_cch + 1 + 1)); // move buffer ahead, including null MoveMemory(m_prgchBuffer + 1, m_prgchBuffer, (m_cch + 1) * sizeof(TChar)); m_prgchBuffer[0] = ch; m_cch++; FN_EPILOG } operator TConstantString() const { this->IntegrityCheck(); return m_prgchBuffer; } inline VOID Clear(bool fFreeStorage = false) { FN_TRACE(); this->IntegrityCheck(); // You can't free the storage if there's an attached accessor ASSERT(!fFreeStorage || m_cAttachedAccessors == 0); if (fFreeStorage && (m_cAttachedAccessors == 0)) { if (m_prgchBuffer != NULL) { const TMutableString pszInlineBuffer = this->GetInlineBuffer(); if (m_prgchBuffer != pszInlineBuffer) { this->DeallocateBuffer(m_prgchBuffer); m_prgchBuffer = pszInlineBuffer; m_cchBuffer = this->GetInlineBufferCch(); } } } if (m_prgchBuffer != NULL) m_prgchBuffer[0] = this->NullCharacter(); m_cch = 0; } BOOL Win32ConvertCase( ECaseConversionDirection direction ) { #if !FUSION_WIN return FALSE; #else FN_PROLOG_WIN32 this->IntegrityCheck(); // it would seem innocuous to allow assigns that don't resize the buffer to not // invalidate accessors, but that makes finding such bugs subject to even more // strenuous coverage problems than this simple error. The simple rule is that // you should not have an accessor attached to a string buffer when you use // any of member functions that may mutate the value. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0); TMutableString Cursor = m_prgchBuffer; for ( ULONG ul = 0; ul < this->Cch(); ul++ ) { if ( direction == eConvertToUpperCase ) *Cursor = RtlUpcaseUnicodeChar(*Cursor); else *Cursor = RtlDowncaseUnicodeChar(*Cursor); Cursor++; } FN_EPILOG #endif } BOOL Win32Compare(TConstantString szCandidate, SIZE_T cchCandidate, StringComparisonResult &rscrOut, bool fCaseInsensitive) const { this->IntegrityCheck(); BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); IFW32FALSE_EXIT(TCharTraits::Win32CompareStrings(rscrOut, m_prgchBuffer, m_cch, szCandidate, cchCandidate, fCaseInsensitive)); FN_EPILOG } BOOL Win32Equals(TConstantString szCandidate, SIZE_T cchCandidate, bool &rfMatches, bool fCaseInsensitive) const { this->IntegrityCheck(); BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); IFW32FALSE_EXIT( TCharTraits::Win32EqualStrings( rfMatches, m_prgchBuffer, m_cch, szCandidate, cchCandidate, fCaseInsensitive)); FN_EPILOG } BOOL Win32Equals(const CGenericBaseStringBuffer &r, bool &rfMatches, bool fCaseInsensitive) const { return this->Win32Equals(r, r.Cch(), rfMatches, fCaseInsensitive); } SIZE_T GetBufferCch() const { this->IntegrityCheck(); return m_cchBuffer; } INT GetBufferCchAsINT() const { this->IntegrityCheck(); if (m_cchBuffer > INT_MAX) return INT_MAX; return static_cast(m_cchBuffer); } DWORD GetBufferCchAsDWORD() const { this->IntegrityCheck(); if (m_cchBuffer > MAXDWORD) return MAXDWORD; return static_cast(m_cchBuffer); } DWORD GetCchAsDWORD() const { this->IntegrityCheck(); if (m_cch > MAXDWORD) return MAXDWORD; return static_cast(m_cch); } INT GetCchAsINT() const { this->IntegrityCheck(); if (m_cch > INT_MAX) return INT_MAX; return static_cast(m_cch); } UINT GetCchAsUINT() const { this->IntegrityCheck(); if (m_cch > UINT_MAX) return UINT_MAX; return static_cast(m_cch); } SIZE_T GetBufferCb() const { this->IntegrityCheck(); return m_cchBuffer * sizeof(TChar); } INT GetBufferCbAsINT() const { this->IntegrityCheck(); if ((m_cchBuffer * sizeof(TChar)) > INT_MAX) return INT_MAX; return static_cast(m_cchBuffer * sizeof(TChar)); } DWORD GetBufferCbAsDWORD() const { this->IntegrityCheck(); if ((m_cchBuffer * sizeof(TChar)) > MAXDWORD) return MAXDWORD; return static_cast(m_cchBuffer * sizeof(TChar)); } DWORD GetCbAsDWORD() const { this->IntegrityCheck(); if ((m_cch * sizeof(TChar)) > MAXDWORD) return MAXDWORD; return static_cast(m_cch * sizeof(TChar)); } INT GetCbAsINT() const { this->IntegrityCheck(); if ((m_cch * sizeof(TChar)) > INT_MAX) return INT_MAX; return static_cast(m_cch * sizeof(TChar)); } UINT GetCbAsUINT() const { this->IntegrityCheck(); if ((m_cch * sizeof(TChar)) > UINT_MAX) return UINT_MAX; return static_cast(m_cch * sizeof(TChar)); } bool ContainsCharacter(WCHAR wch) const { this->IntegrityCheck(); return TCharTraits::ContainsCharacter(m_prgchBuffer, m_cch, wch); } BOOL Win32ResizeBuffer( SIZE_T cch, EPreserveContents epc ) { FN_PROLOG_WIN32 this->IntegrityCheck(); INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0); PARAMETER_CHECK((epc == ePreserveBufferContents) || (epc == eDoNotPreserveBufferContents)); if (cch > m_cchBuffer) { TMutableString prgchBufferNew = NULL; IFW32FALSE_EXIT(this->Win32AllocateBuffer(cch, prgchBufferNew)); if (epc == ePreserveBufferContents) { // We assume that the buffer is/was null-terminated. IFW32FALSE_EXIT(TCharTraits::Win32CopyIntoBuffer(prgchBufferNew, cch, m_prgchBuffer, m_cch)); } else { m_prgchBuffer[0] = this->NullCharacter(); m_cch = 0; } if ((m_prgchBuffer != NULL) && (m_prgchBuffer != this->GetInlineBuffer())) this->DeallocateBuffer(m_prgchBuffer); m_prgchBuffer = prgchBufferNew; m_cchBuffer = cch; } FN_EPILOG } BOOL Win32Format(TConstantString pszFormat, ...) { this->IntegrityCheck(); va_list args; va_start(args, pszFormat); BOOL f = this->Win32FormatV(pszFormat, args); va_end(args); return f; } BOOL Win32FormatAppend(TConstantString pszFormat, ...) { this->IntegrityCheck(); va_list args; va_start(args, pszFormat); BOOL f = Win32FormatAppendV(pszFormat, args); va_end(args); return f; } BOOL Win32FormatV(TConstantString pszFormat, va_list args) { BOOL fSuccess = FALSE; this->Clear(); fSuccess = Win32FormatAppendV(pszFormat, args); return fSuccess; } BOOL Win32FormatAppendV(TConstantString pszFormat, va_list args) { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); SIZE_T cchRequiredBufferSize = 0; INT i = 0; this->IntegrityCheck(); // it would seem innocuous to allow assigns that don't resize the buffer to not // invalidate accessors, but that makes finding such bugs subject to even more // strenuous coverage problems than this simple error. The simple rule is that // you should not have an accessor attached to a string buffer when you use // any of member functions that may mutate the value. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0); m_prgchBuffer[m_cchBuffer - 1] = this->NullCharacter(); i = TCharTraits::FormatV(m_prgchBuffer + m_cch, m_cchBuffer - 1 - m_cch, pszFormat, args); ASSERT(m_prgchBuffer[m_cchBuffer - 1] == NullCharacter()); fSuccess = (i >= 0); if ( fSuccess ) m_cch += i; else { // // Sprintf doesn't touch last error. The fn tracer // will fail an assertion if we return false but FusionpGetLastWin32Error()==NOERROR // ORIGINATE_WIN32_FAILURE_AND_EXIT(snwprintf_MaybeBufferTooSmall, ERROR_INVALID_PARAMETER); } Exit: return fSuccess; } SIZE_T Cch() const { this->IntegrityCheck(); return this->m_cch; } BOOL IsEmpty() const { this->IntegrityCheck(); const BOOL fResult = (this->m_prgchBuffer[0] == this->NullCharacter()); #if DBG // // We should probably reverse how we compute the result in // retail vs. what we assert. That would be one pointer // deref instead of two in retail; not worth churn right now. // - JayKrell, June 2002. // if (fResult) { ASSERT_NTC(this->m_cch == 0); } #endif return fResult; } WCHAR GetLastCharUnsafe() const { ASSERT_NTC(!this->IsEmpty()); return this->m_prgchBuffer[this->m_cch - 1]; } BOOL Win32EnsureTrailingChar(WCHAR ch) { this->IntegrityCheck(); BOOL fSuccess = FALSE; // it would seem innocuous to allow assigns that don't resize the buffer to not // invalidate accessors, but that makes finding such bugs subject to even more // strenuous coverage problems than this simple error. The simple rule is that // you should not have an accessor attached to a string buffer when you use // any of member functions that may mutate the value. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0); if ((m_cch == 0) || (m_prgchBuffer[m_cch - 1] != ch)) { IFW32FALSE_EXIT(this->Win32ResizeBufferPreserveContentsInternal(m_cch + 1 + 1)); m_prgchBuffer[m_cch++] = ch; m_prgchBuffer[m_cch] = this->NullCharacter(); } fSuccess = TRUE; Exit: return fSuccess; } BOOL Win32EnsureTrailingPathSeparator() { this->IntegrityCheck(); BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); // it would seem innocuous to allow assigns that don't resize the buffer to not // invalidate accessors, but that makes finding such bugs subject to even more // strenuous coverage problems than this simple error. The simple rule is that // you should not have an accessor attached to a string buffer when you use // any of member functions that may mutate the value. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0); if ((m_cch == 0) || !TCharTraits::IsPathSeparator(m_prgchBuffer[m_cch - 1])) { IFW32FALSE_EXIT(this->Win32ResizeBufferPreserveContentsInternal(m_cch + 1 + 1)); m_prgchBuffer[m_cch++] = this->PreferredPathSeparator(); m_prgchBuffer[m_cch] = this->NullCharacter(); } fSuccess = TRUE; Exit: return fSuccess; } BOOL Win32AppendPathElement(PCWSTR pathElement, SIZE_T cchPathElement) { this->IntegrityCheck(); BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); // it would seem innocuous to allow assigns that don't resize the buffer to not // invalidate accessors, but that makes finding such bugs subject to even more // strenuous coverage problems than this simple error. The simple rule is that // you should not have an accessor attached to a string buffer when you use // any of member functions that may mutate the value. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0); IFW32FALSE_EXIT(this->Win32EnsureTrailingPathSeparator()); IFW32FALSE_EXIT(this->Win32Append(pathElement, cchPathElement)); fSuccess = TRUE; Exit: return fSuccess; } BOOL Win32AppendPathElement(const CGenericBaseStringBuffer &r) { return this->Win32AppendPathElement(r, r.Cch()); } BOOL Win32AppendPathElement(PCSTR pathElement, SIZE_T cchPathElement) { this->IntegrityCheck(); BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); // it would seem innocuous to allow assigns that don't resize the buffer to not // invalidate accessors, but that makes finding such bugs subject to even more // strenuous coverage problems than this simple error. The simple rule is that // you should not have an accessor attached to a string buffer when you use // any of member functions that may mutate the value. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0); IFW32FALSE_EXIT(this->Win32EnsureTrailingPathSeparator()); IFW32FALSE_EXIT(this->Win32Append(pathElement, cchPathElement)); fSuccess = TRUE; Exit: return fSuccess; } BOOL Left(SIZE_T newLength) { this->IntegrityCheck(); ASSERT_NTC(newLength <= m_cch); // it would seem innocuous to allow assigns that don't resize the buffer to not // invalidate accessors, but that makes finding such bugs subject to even more // strenuous coverage problems than this simple error. The simple rule is that // you should not have an accessor attached to a string buffer when you use // any of member functions that may mutate the value. // Note also that while the current implementation does not change the buffer // pointer, this is just a shortcut in the implementation; if a call to Left() // were to make the string short enough to fit in the inline buffer, we should // copy it to the inline buffer and deallocate the dynamic one. ASSERT_NTC(m_cAttachedAccessors == 0); if (m_cchBuffer > newLength) { m_prgchBuffer[newLength] = this->NullCharacter(); } m_cch = newLength; this->IntegrityCheck(); return TRUE; } TConstantString Begin() const { this->IntegrityCheck(); return m_prgchBuffer; } TConstantString End() const { this->IntegrityCheck(); return &m_prgchBuffer[m_cch]; } // should factor this for reuse in CchWithoutLastPathElement SIZE_T CchWithoutTrailingPathSeparators() const { this->IntegrityCheck(); // Until GetLength is constant time, optimize its use.. SIZE_T length = m_cch; if (length > 0) { length -= ::StringReverseSpan(&*m_prgchBuffer, &*m_prgchBuffer + length, TCharTraits::PathSeparators()); } return length; } BOOL RestoreNextPathElement() { SIZE_T index; this->IntegrityCheck(); index = m_cch; m_prgchBuffer[index++] = L'\\'; // replace trailing NULL with '\' while ((index < m_cchBuffer) && (!this->IsNullCharacter(m_prgchBuffer[index]))) { if (::FusionpIsPathSeparator(m_prgchBuffer[index])) { this->Left(index); return TRUE; } index++; } return FALSE; } bool HasTrailingPathSeparator() const { FN_TRACE(); this->IntegrityCheck(); if ((m_cch != 0) && TCharTraits::IsPathSeparator(m_prgchBuffer[m_cch - 1])) return true; return false; } BOOL Win32RemoveTrailingPathSeparators() { this->IntegrityCheck(); // it would seem innocuous to allow assigns that don't resize the buffer to not // invalidate accessors, but that makes finding such bugs subject to even more // strenuous coverage problems than this simple error. The simple rule is that // you should not have an accessor attached to a string buffer when you use // any of member functions that may mutate the value. // Note also that while the current implementation does not change the buffer // pointer, this is just a shortcut in the implementation; if a call to Left() // were to make the string short enough to fit in the inline buffer, we should // copy it to the inline buffer and deallocate the dynamic one. ASSERT_NTC(m_cAttachedAccessors == 0); while ((m_cch != 0) && TCharTraits::IsPathSeparator(m_prgchBuffer[m_cch - 1])) m_cch--; m_prgchBuffer[m_cch] = this->NullCharacter(); this->IntegrityCheck(); return TRUE; } BOOL Right( SIZE_T cchRightCount ) { this->IntegrityCheck(); ASSERT_NTC(m_cAttachedAccessors == 0); ASSERT_NTC(cchRightCount <= m_cch); if (cchRightCount < m_cch) { ::MoveMemory( m_prgchBuffer, &m_prgchBuffer[m_cch - cchRightCount], (cchRightCount + 1)*sizeof(TCharTraits::TChar)); m_cch = cchRightCount; } this->IntegrityCheck(); return TRUE; } BOOL RemoveLeadingPathSeparators() { this->IntegrityCheck(); BOOL fSuccess = this->Right(m_cch - wcsspn(m_prgchBuffer, TCharTraits::PathSeparators())); this->IntegrityCheck(); return fSuccess; } BOOL Win32StripToLastPathElement() { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0); this->IntegrityCheck(); IFW32FALSE_EXIT(this->Right(m_cch - this->CchWithoutLastPathElement())); IFW32FALSE_EXIT(this->RemoveLeadingPathSeparators()); fSuccess = TRUE; Exit: this->IntegrityCheck(); return fSuccess; } BOOL Win32GetFirstPathElement( CGenericBaseStringBuffer &sbDestination, BOOL bRemoveAsWell = FALSE ) { FN_PROLOG_WIN32 this->IntegrityCheck(); IFW32FALSE_EXIT( sbDestination.Win32Assign( m_prgchBuffer, this->CchOfFirstPathElement() ) ); sbDestination.RemoveLeadingPathSeparators(); if ( bRemoveAsWell ) IFW32FALSE_EXIT(this->Win32RemoveFirstPathElement()); this->IntegrityCheck(); FN_EPILOG } BOOL Win32GetFirstPathElement( CGenericBaseStringBuffer &sbDestination ) const { BOOL bSuccess = FALSE; this->IntegrityCheck(); if ( sbDestination.Win32Assign( m_prgchBuffer, CchOfFirstPathElement() ) ) { sbDestination.RemoveLeadingPathSeparators(); bSuccess = TRUE; } return bSuccess; } BOOL Win32StripToFirstPathElement() { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0); this->IntegrityCheck(); IFW32FALSE_EXIT(this->Left(this->CchOfFirstPathElement())); IFW32FALSE_EXIT(this->RemoveLeadingPathSeparators()); fSuccess = TRUE; Exit: this->IntegrityCheck(); return fSuccess; } BOOL Win32RemoveFirstPathElement() { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); this->IntegrityCheck(); INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0); IFW32FALSE_EXIT(this->Right(this->CchWithoutFirstPathElement())); IFW32FALSE_EXIT(this->RemoveLeadingPathSeparators()); fSuccess = TRUE; Exit: this->IntegrityCheck(); return fSuccess; } SIZE_T CchOfFirstPathElement() const { return Cch() - CchWithoutFirstPathElement(); } SIZE_T CchWithoutFirstPathElement() const { this->IntegrityCheck(); SIZE_T cch = m_cch; // // We just look for the first path element, which can also be the drive // letter! // if ( cch != 0 ) { cch -= wcscspn( m_prgchBuffer, PathSeparators() ); } return cch; } BOOL Win32GetLastPathElement(CGenericBaseStringBuffer &sbDestination) const { BOOL bSuccess = FALSE; FN_TRACE_WIN32(bSuccess); this->IntegrityCheck(); sbDestination.IntegrityCheck(); IFW32FALSE_EXIT(sbDestination.Win32Assign(m_prgchBuffer, m_cch)); IFW32FALSE_EXIT(sbDestination.Win32StripToLastPathElement()); bSuccess = TRUE; Exit: this->IntegrityCheck(); sbDestination.IntegrityCheck(); return bSuccess; } SIZE_T CchWithoutLastPathElement() const { this->IntegrityCheck(); // Paths are assumed to be // "\\machine\share" // or // "x:\" // Worry about alternate NTFS streams at a later date. // Worry about NT paths at a later date. // Worry about URLs at a later date. const SIZE_T length = m_cch; SIZE_T newLength = length; if (length > 0) { if ((length == 3) && (m_prgchBuffer[1] == ':') && ::FusionpIsPathSeparator(m_prgchBuffer[2]) && ::FusionpIsDriveLetter(m_prgchBuffer[0])) { // c:\ => empty string newLength = 0; } else { // Remove trailing path seperators here, in the future when it is not risky. //newLength -= ::StringReverseSpan(&*m_prgchBuffer, &*m_prgchBuffer + newLength, PathSeparators()); newLength -= ::StringReverseComplementSpan(&*m_prgchBuffer, &*m_prgchBuffer + newLength, PathSeparators()); newLength -= ::StringReverseSpan(&*m_prgchBuffer, &*m_prgchBuffer + newLength, PathSeparators()); if ((newLength == 2) && // "c:" (length >= 4) && // "c:\d" (m_prgchBuffer[1] == ':') && ::FusionpIsPathSeparator(m_prgchBuffer[2]) && ::FusionpIsDriveLetter(m_prgchBuffer[0])) { ++newLength; // put back the slash in "c:\" } } } return newLength; } BOOL Win32RemoveLastPathElement() { BOOL fSuccess = FALSE; FN_TRACE_WIN32( fSuccess ); this->IntegrityCheck(); // it would seem innocuous to allow assigns that don't resize the buffer to not // invalidate accessors, but that makes finding such bugs subject to even more // strenuous coverage problems than this simple error. The simple rule is that // you should not have an accessor attached to a string buffer when you use // any of member functions that may mutate the value. // Note also that while the current implementation does not change the buffer // pointer, this is just a shortcut in the implementation; if a call to Left() // were to make the string short enough to fit in the inline buffer, we should // copy it to the inline buffer and deallocate the dynamic one. ASSERT_NTC(m_cAttachedAccessors == 0); IFW32FALSE_EXIT(this->Left(this->CchWithoutLastPathElement())); fSuccess = TRUE; Exit: this->IntegrityCheck(); return fSuccess; } BOOL Win32ClearPathExtension() { // // Replace the final '.' with a \0 to clear the path extension // BOOL fSuccess = FALSE; FN_TRACE_WIN32( fSuccess ); this->IntegrityCheck(); TMutableString dot = 0; const TMutableString end = End(); IFW32FALSE_EXIT(TCharTraits::Win32ReverseFind(dot, m_prgchBuffer, m_cch, this->DotChar(), false)); if((dot != end) && (dot != NULL)) { *dot = this->NullCharacter(); m_cch = (dot - m_prgchBuffer); } fSuccess = TRUE; Exit: this->IntegrityCheck(); return fSuccess; } BOOL Win32GetPathExtension(CGenericBaseStringBuffer &destination) const { this->IntegrityCheck(); BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); SIZE_T cchExtension; const TConstantString start = Begin(); const TConstantString end = End(); cchExtension = ::StringReverseComplementSpan( &(*start), &(*end), L"." ); IFW32FALSE_EXIT(destination.Win32Assign( static_cast(*this) + ( m_cch - cchExtension ), cchExtension)); fSuccess = TRUE; Exit: return fSuccess; } // newExtension can start with a dot or not BOOL Win32ChangePathExtension(PCWSTR newExtension, SIZE_T cchExtension, EIfNoExtension e) { this->IntegrityCheck(); BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); TMutableString end = 0; TMutableString dot = 0; INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0); PARAMETER_CHECK((e == eAddIfNoExtension) || (e == eDoNothingIfNoExtension) || (e == eErrorIfNoExtension)); if ((cchExtension != 0) && (newExtension[0] == L'.')) { cchExtension--; newExtension++; } // the use of append when we know where the end of the string is inefficient end = this->End(); IFW32FALSE_EXIT(TCharTraits::Win32ReverseFind(dot, m_prgchBuffer, m_cch, this->DotChar(), false)); // Found the end of the string, or Win32ReverseFind didn't find the dot anywhere... if ((dot == end) || (dot == NULL)) { switch (e) { case eAddIfNoExtension: IFW32FALSE_EXIT(this->Win32Append(this->DotString(), 1)); IFW32FALSE_EXIT(this->Win32Append(newExtension, cchExtension)); break; case eDoNothingIfNoExtension: break; case eErrorIfNoExtension: ORIGINATE_WIN32_FAILURE_AND_EXIT(MissingExtension, ERROR_BAD_PATHNAME); } } else { ++dot; IFW32FALSE_EXIT(this->Left(dot - this->Begin())); IFW32FALSE_EXIT(this->Win32Append(newExtension, cchExtension)); } fSuccess = TRUE; Exit: this->IntegrityCheck(); return fSuccess; } BOOL Win32ChangePathExtension(const UNICODE_STRING* NtString, EIfNoExtension e) { return Win32ChangePathExtension( NtString->Buffer, RTL_STRING_GET_LENGTH_CHARS(NtString), e); } BOOL Win32CopyStringOut(LPWSTR sz, ULONG *pcch) const { FN_PROLOG_WIN32 this->IntegrityCheck(); SIZE_T cwchRequired = 0; PARAMETER_CHECK(pcch != NULL); IFW32FALSE_EXIT(TCharTraits::Win32DetermineRequiredCharacters(m_prgchBuffer, m_cch, cwchRequired)); if ((*pcch) < cwchRequired) { *pcch = static_cast(cwchRequired); ORIGINATE_WIN32_FAILURE_AND_EXIT(NoRoom, ERROR_INSUFFICIENT_BUFFER); } IFW32FALSE_EXIT(TCharTraits::Win32CopyIntoBuffer(sz, *pcch, m_prgchBuffer, m_cch)); FN_EPILOG } // // This function is rather special purpose in that several design choices are not // implemented as parameters. In particular, the pcbBytesWritten is assumed to // accumulate a number (thus it's updated by adding the number of bytes written to // it rather than just setting it to the count of bytes written). // // It also writes 0 bytes into the buffer is the string is zero length; if the string // is not zero length, it writes the string including a trailing null. // inline BOOL Win32CopyIntoBuffer( PWSTR *ppszCursor, SIZE_T *pcbBuffer, SIZE_T *pcbBytesWritten, PVOID pvBase, ULONG *pulOffset, ULONG *pulLength ) const { this->IntegrityCheck(); BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); PWSTR pszCursor = NULL; SSIZE_T dptr = 0; SIZE_T cbRequired = 0; SIZE_T cch = 0; if (pulOffset != NULL) *pulOffset = 0; if (pulLength != NULL) *pulLength = 0; PARAMETER_CHECK(pcbBuffer != NULL); PARAMETER_CHECK(ppszCursor != NULL); pszCursor = *ppszCursor; dptr = ((SSIZE_T) pszCursor) - ((SSIZE_T) pvBase); // If they're asking for an offset or length and the cursor is too far from the base, // fail. PARAMETER_CHECK((pulOffset == NULL) || (dptr <= ULONG_MAX)); cch = m_cch; cbRequired = (cch != 0) ? ((cch + 1) * sizeof(WCHAR)) : 0; if ((*pcbBuffer) < cbRequired) { ::FusionpSetLastWin32Error(ERROR_INSUFFICIENT_BUFFER); goto Exit; } if (cbRequired > ULONG_MAX) { ::FusionpSetLastWin32Error(ERROR_INSUFFICIENT_BUFFER); goto Exit; } CopyMemory(pszCursor, static_cast(*this), cbRequired); if (pulOffset != NULL) { if (cbRequired != 0) *pulOffset = (ULONG) dptr; } if (pulLength != NULL) { if (cbRequired == 0) *pulLength = 0; else { *pulLength = (ULONG) (cbRequired - sizeof(WCHAR)); } } *pcbBytesWritten += cbRequired; *pcbBuffer -= cbRequired; *ppszCursor = (PWSTR) (((ULONG_PTR) pszCursor) + cbRequired); fSuccess = TRUE; Exit: return fSuccess; } protected: BOOL __fastcall Win32ResizeBufferPreserveContentsInternal( SIZE_T cch ) { // Note: this function is performance-sensitive, so we do not use the normal // tracing infrastucture here if (cch > m_cchBuffer) { TMutableString prgchBufferNew = NULL; if (!this->Win32AllocateBuffer(cch, prgchBufferNew)) return FALSE; // We assume that the buffer is/was null-terminated. if (!TCharTraits::Win32CopyIntoBuffer(prgchBufferNew, cch, m_prgchBuffer, m_cch)) { this->DeallocateBuffer(prgchBufferNew); return FALSE; } if ((m_prgchBuffer != NULL) && (m_prgchBuffer != this->GetInlineBuffer())) this->DeallocateBuffer(m_prgchBuffer); m_prgchBuffer = prgchBufferNew; m_cchBuffer = cch; } return TRUE; } TMutableString Begin() { this->IntegrityCheck(); /* CopyBeforeWrite() */ return m_prgchBuffer; } TMutableString End() { this->IntegrityCheck(); return &m_prgchBuffer[m_cch]; } LONG m_cAttachedAccessors; TChar *m_prgchBuffer; SIZE_T m_cchBuffer; SIZE_T m_cch; // current length of string }; template class CGenericStringBufferAccessor { public: typedef CGenericBaseStringBuffer TBuffer; typedef typename CGenericBaseStringBuffer::TChar TChar; CGenericStringBufferAccessor(TBuffer* pBuffer = NULL) : m_pBuffer(NULL), m_pszBuffer(NULL), m_cchBuffer(NULL) { if (pBuffer != NULL) { Attach(pBuffer); } } ~CGenericStringBufferAccessor() { if (m_pBuffer != NULL) { m_pBuffer->m_cch = TCharTraits::NullTerminatedStringLength(m_pszBuffer); m_pBuffer->DetachAccessor(this); m_pBuffer = NULL; m_pszBuffer = NULL; m_cchBuffer = 0; } } bool IsAttached() const { return (m_pBuffer != NULL); } static TChar NullCharacter() { return TCharTraits::NullCharacter(); } void Attach(TBuffer *pBuffer) { FN_TRACE(); // NTRAID#NTBUG9 - 586534 - 2002/03/26 - xiaoyuw // should be changed to be INTERNAL_ERROR_CHECK // ASSERT(!this->IsAttached()); if (!this->IsAttached()) { pBuffer->AttachAccessor(this); m_pBuffer = pBuffer; m_pszBuffer = m_pBuffer->m_prgchBuffer; m_cchBuffer = m_pBuffer->m_cchBuffer; } } void Detach() { FN_TRACE(); // NTRAID#NTBUG9 - 586534 - 2002/03/26 - xiaoyuw // should be changed to be INTERNAL_ERROR_CHECK // ASSERT (IsAttached()); if (IsAttached()) { ASSERT(m_pszBuffer == m_pBuffer->m_prgchBuffer); m_pBuffer->m_cch = TCharTraits::NullTerminatedStringLength(m_pszBuffer); m_pBuffer->DetachAccessor(this); m_pBuffer = NULL; m_pszBuffer = NULL; m_cchBuffer = 0; } else { ASSERT(m_pszBuffer == NULL); ASSERT(m_cchBuffer == 0); } } operator typename TCharTraits::TMutableString() const { ASSERT_NTC(this->IsAttached()); return m_pszBuffer; } SIZE_T Cch() const { ASSERT_NTC(this->IsAttached()); return (m_pszBuffer != NULL) ? ::wcslen(m_pszBuffer) : 0; } typename TCharTraits::TMutableString GetBufferPtr() const { ASSERT_NTC(IsAttached()); return m_pszBuffer; } SIZE_T GetBufferCch() const { ASSERT_NTC(this->IsAttached()); return m_cchBuffer; } INT GetBufferCchAsINT() const { ASSERT_NTC(this->IsAttached()); if (m_cchBuffer > INT_MAX) return INT_MAX; return static_cast(m_cchBuffer); } UINT GetBufferCchAsUINT() const { ASSERT_NTC(this->IsAttached()); if (m_cchBuffer > UINT_MAX) return UINT_MAX; return static_cast(m_cchBuffer); } DWORD GetBufferCchAsDWORD() const { ASSERT_NTC(this->IsAttached()); if (m_cchBuffer > MAXDWORD) return MAXDWORD; return static_cast(m_cchBuffer); } DWORD GetCchAsDWORD() const { ASSERT_NTC(this->IsAttached()); if (m_cch > MAXDWORD) return MAXDWORD; return static_cast(m_cch); } // NTRAID#NTBUG9 - 586534 - 2002/03/26 - xiaoyuw // (1) overflow of the result of m_cchBuffer * sizeof(TChar) // (2) calls GetXXXAsDWORD need to check whether the return value is DWORDMAX, if so, stop; SIZE_T GetBufferCb() const { ASSERT_NTC(this->IsAttached()); return m_cchBuffer * sizeof(*m_pszBuffer); } INT GetBufferCbAsINT() const { ASSERT_NTC(this->IsAttached()); if ((m_cchBuffer * sizeof(TChar)) > INT_MAX) return INT_MAX; return static_cast(m_cchBuffer * sizeof(TChar)); } DWORD GetBufferCbAsDWORD() const { ASSERT_NTC(this->IsAttached()); if ((m_cchBuffer * sizeof(TChar)) > MAXDWORD) return MAXDWORD; return static_cast(m_cchBuffer * sizeof(TChar)); } DWORD GetCbAsDWORD() const { ASSERT_NTC(this->IsAttached()); if ((m_cch * sizeof(TChar)) > MAXDWORD) return MAXDWORD; return static_cast(m_cch * sizeof(TChar)); } protected: TBuffer *m_pBuffer; typename TCharTraits::TMutableString m_pszBuffer; SIZE_T m_cchBuffer; }; template class CGenericStringBuffer : public CGenericBaseStringBuffer { typedef CGenericBaseStringBuffer Base; protected: BOOL Win32AllocateBuffer(SIZE_T cch, TMutableString &rpsz) const { // You shouldn't be doing this if the required buffer size is small enough to be inline... ASSERT_NTC(cch > nInlineChars); rpsz = NULL; TCharTraits::TMutableString String = NULL; String = reinterpret_cast(::FusionpHeapAllocEx( FUSION_DEFAULT_PROCESS_HEAP(), 0, cch * sizeof(TCharTraits::TChar), "", __FILE__, __LINE__, 0)); // fusion heap allocation flags if (String == NULL) { ::FusionpSetLastWin32Error(FUSION_WIN32_ALLOCFAILED_ERROR); return FALSE; } rpsz = String; return TRUE; } VOID DeallocateBuffer(TMutableString sz) const { VERIFY_NTC(::FusionpHeapFree(FUSION_DEFAULT_PROCESS_HEAP(), 0, sz)); } TMutableString GetInlineBuffer() const { return const_cast(m_rgchInlineBuffer); } SIZE_T GetInlineBufferCch() const { return nInlineChars; } void Initialize() { m_rgchInlineBuffer[0] = this->NullCharacter(); Base::InitializeInlineBuffer(); } void Cleanup() { if (m_prgchBuffer != m_rgchInlineBuffer) { this->DeallocateBuffer(m_prgchBuffer); } m_prgchBuffer = NULL; m_cchBuffer = 0; } public: void Reinitialize() { Cleanup(); Initialize(); } CGenericStringBuffer() { Initialize(); } ~CGenericStringBuffer() { Cleanup(); } protected: TChar m_rgchInlineBuffer[nInlineChars]; private: CGenericStringBuffer(const CGenericStringBuffer &); // intentionally not implemented void operator =(const CGenericStringBuffer &); // intentionally not implemented }; template class CGenericHeapStringBuffer : public CGenericBaseStringBuffer { // friend CGenericBaseStringBuffer; typedef CGenericBaseStringBuffer Base; protected: BOOL Win32AllocateBuffer(SIZE_T cch, TMutableString &rpsz) const { // You shouldn't be doing this if the required buffer size is small enough to be inline... ASSERT_NTC(cch > nInlineChars); rpsz = NULL; TCharTraits::TMutableString String = NULL; String = reinterpret_cast(::FusionpHeapAllocEx( m_hHeap, dwDefaultWin32HeapAllocFlags, cch * sizeof(TCharTraits::TChar), "", __FILE__, __LINE__, 0)) // fusion heap allocation flags if (String == NULL) { ::FusionpSetLastWin32Error(FUSION_WIN32_ALLOCFAILED_ERROR); return FALSE; } rpsz = String; return TRUE; } VOID DeallocateBuffer(TMutableString sz) const { VERIFY_NTC(::FusionpHeapFree(m_hHeap, dwDefaultWin32HeapFreeFlags, sz)); } TMutableString GetInlineBuffer() const { return m_rgchInlineBuffer; } SIZE_T GetInlineBufferCch() const { return nInlineChars; } public: CGenericHeapStringBuffer(HANDLE hHeap) : m_hHeap(hHeap) { m_rgchInlineBuffer[0] = this->NullCharacter(); Base::InitializeInlineBuffer(); } ~CGenericHeapStringBuffer() { ASSERT(m_cchBuffer == 0); ASSERT(m_prgchBuffer == NULL); } protected: HANDLE m_hHeap; TChar m_rgchInlineBuffer[nInlineChars]; }; typedef CGenericStringBufferAccessor CUnicodeStringBufferAccessor; typedef CGenericBaseStringBuffer CUnicodeBaseStringBuffer; typedef CGenericStringBuffer CUnicodeStringBuffer; typedef CGenericHeapStringBuffer CUnicodeHeapStringBuffer; typedef CGenericStringBuffer CTinyUnicodeStringBuffer; typedef CGenericHeapStringBuffer CTinyUnicodeHeapStringBuffer; typedef CGenericStringBuffer CSmallUnicodeStringBuffer; typedef CGenericHeapStringBuffer CSmallUnicodeHeapStringBuffer; typedef CGenericStringBuffer CMediumUnicodeStringBuffer; typedef CGenericHeapStringBuffer CMediumUnicodeHeapStringBuffer; typedef CGenericStringBufferAccessor CANSIStringBufferAccessor; typedef CGenericBaseStringBuffer CANSIBaseStringBuffer; typedef CGenericStringBuffer CANSIStringBuffer; typedef CGenericHeapStringBuffer CANSIHeapStringBuffer; typedef CGenericStringBuffer CTinyANSIStringBuffer; typedef CGenericHeapStringBuffer CTinyANSIHeapStringBuffer; typedef CGenericStringBuffer CSmallANSIStringBuffer; typedef CGenericHeapStringBuffer CSmallANSIHeapStringBuffer; typedef CGenericStringBuffer CMediumANSIStringBuffer; typedef CGenericHeapStringBuffer CMediumANSIHeapStringBuffer; typedef CUnicodeBaseStringBuffer CBaseStringBuffer; typedef CUnicodeStringBuffer CStringBuffer; typedef CUnicodeHeapStringBuffer CHeapStringBuffer; typedef CUnicodeStringBufferAccessor CStringBufferAccessor; typedef CTinyUnicodeStringBuffer CTinyStringBuffer; typedef CTinyUnicodeHeapStringBuffer CTinyHeapStringBuffer; typedef CSmallUnicodeStringBuffer CSmallStringBuffer; typedef CSmallUnicodeHeapStringBuffer CSmallHeapStringBuffer; typedef CMediumUnicodeStringBuffer CMediumStringBuffer; typedef CMediumUnicodeHeapStringBuffer CMediumHeapStringBuffer; template inline HRESULT HashTableCompareKey(T1 t1, T2 *pt2, bool &rfMatch); template <> inline HRESULT HashTableCompareKey(PCWSTR sz, CUnicodeStringBuffer *pbuff, bool &rfMatch) { HRESULT hr = NOERROR; SIZE_T cchKey = (sz != NULL) ? ::wcslen(sz) : 0; rfMatch = false; if (!pbuff->Win32Equals(sz, cchKey, rfMatch, false)) { hr = HRESULT_FROM_WIN32(::FusionpGetLastWin32Error()); goto Exit; } hr = NOERROR; Exit: return hr; } template <> inline HRESULT HashTableCompareKey(PCSTR sz, CANSIStringBuffer *pbuff, bool &rfMatch) { HRESULT hr = NOERROR; SIZE_T cchKey = ::strlen(sz); rfMatch = false; if (!pbuff->Win32Equals(sz, cchKey, rfMatch, false)) { hr = HRESULT_FROM_WIN32(::FusionpGetLastWin32Error()); goto Exit; } hr = NOERROR; Exit: return hr; } // // Support for CFusionArrays of strings // template<> inline BOOL FusionWin32CopyContents( CStringBuffer &rDestination, const CStringBuffer &rSource ) { return rDestination.Win32Assign(rSource); } inline BOOL FusionWin32CopyContents( CStringBuffer &rDestination, const CBaseStringBuffer &rSource ) { return rDestination.Win32Assign(rSource); } #endif