Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1776 lines
58 KiB

  1. #if !defined(FUSION_INC_FUSIONBUFFER_H_INCLUDED_)
  2. #define FUSION_INC_FUSIONBUFFER_H_INCLUDED_
  3. #pragma once
  4. #include <stdio.h>
  5. #include <limits.h>
  6. #include "arrayhelp.h"
  7. #include "smartref.h"
  8. #include "ReturnStrategy.h"
  9. #include "FusionString.h"
  10. #include "fusiontrace.h"
  11. #include "fusionchartraits.h"
  12. // avoid circular reference to Util.h
  13. BOOL FusionpIsPathSeparator(WCHAR ch);
  14. BOOL FusionpIsDriveLetter(WCHAR ch);
  15. //
  16. // This header file defines the Fusion character string buffer class.
  17. // The purpose of this class is to encapsulate common activities that
  18. // callers want to do with character string buffers and handle it in
  19. // a generic fashion. A principle tenet of this class is that it is
  20. // not a string class, although one could consider building a string
  21. // class upon it.
  22. //
  23. // The buffer maintains a certain amount of storage within the buffer
  24. // object itself, and if more storage is required, a buffer is
  25. // dynamically allocated from a heap.
  26. //
  27. //
  28. // Like the STL string class, we use a helper class called a "character
  29. // traits" class to provide the actual code to manipulate character string
  30. // buffers with a specific encoding.
  31. //
  32. // All the members are inline static and with normal optimization turned
  33. // on, the C++ compiler generates code that fully meets expectations.
  34. //
  35. //
  36. // We provide two implementations: one for Unicode strings, and another
  37. // template class for MBCS strings. The code page of the string is a
  38. // template parameter for the MBCS string, so without any extra storage
  39. // wasted per-instance, code can separately handle MBCS strings which
  40. // are expected to be in the thread-default windows code page (CP_THREAD_ACP),
  41. // process-default windows code page (CP_ACP) or even a particular code
  42. // page (e.g. CP_UTF8).
  43. //
  44. //
  45. // This template class uses a number of non-type template parameters to
  46. // control things like growth algorithms etc. As a result there are
  47. // many comparisons of template parameters against well-known constant
  48. // values, for which the compiler generates warning C4127. We'll turn that
  49. // warning off.
  50. //
  51. #pragma warning(disable:4127)
  52. #pragma warning(disable:4284)
  53. #if !defined(FUSION_DEFAULT_STRINGBUFFER_CHARS)
  54. #define FUSION_DEFAULT_STRINGBUFFER_CHARS (MAX_PATH)
  55. #endif
  56. #if !defined(FUSION_DEFAULT_TINY_STRINGBUFFER_CHARS)
  57. #define FUSION_DEFAULT_TINY_STRINGBUFFER_CHARS (8)
  58. #endif
  59. #if !defined(FUSION_DEFAULT_SMALL_STRINGBUFFER_CHARS)
  60. #define FUSION_DEFAULT_SMALL_STRINGBUFFER_CHARS (64)
  61. #endif
  62. #if !defined(FUSION_DEFAULT_MEDIUM_STRINGBUFFER_CHARS)
  63. #define FUSION_DEFAULT_MEDIUM_STRINGBUFFER_CHARS (128)
  64. #endif
  65. enum EIfNoExtension
  66. {
  67. eAddIfNoExtension,
  68. eDoNothingIfNoExtension,
  69. eErrorIfNoExtension
  70. };
  71. enum ECaseConversionDirection
  72. {
  73. eConvertToUpperCase,
  74. eConvertToLowerCase
  75. };
  76. enum EPreserveContents
  77. {
  78. ePreserveBufferContents,
  79. eDoNotPreserveBufferContents
  80. };
  81. template <typename TCharTraits> class CGenericStringBufferAccessor;
  82. template <typename TCharTraits> class CGenericBaseStringBuffer
  83. {
  84. friend TCharTraits;
  85. friend CGenericStringBufferAccessor<TCharTraits>;
  86. //
  87. // These two are to induce build breaks on people doing sb1 = sb2
  88. //
  89. CGenericBaseStringBuffer& operator=(PCWSTR OtherString);
  90. CGenericBaseStringBuffer& operator=(CGenericBaseStringBuffer &rOtherString);
  91. public:
  92. typedef TCharTraits::TChar TChar;
  93. typedef TCharTraits::TMutableString TMutableString;
  94. typedef TCharTraits::TConstantString TConstantString;
  95. typedef CGenericStringBufferAccessor<TCharTraits> TAccessor;
  96. inline static TChar NullCharacter() { return TCharTraits::NullCharacter(); }
  97. inline static bool IsNullCharacter(TChar ch) { return TCharTraits::IsNullCharacter(ch); }
  98. inline static TChar PreferredPathSeparator() { return TCharTraits::PreferredPathSeparator(); }
  99. inline static TConstantString PreferredPathSeparatorString() { return TCharTraits::PreferredPathSeparatorString(); }
  100. inline static TConstantString PathSeparators() { return TCharTraits::PathSeparators(); }
  101. inline static bool IsPathSeparator(TChar ch) { return TCharTraits::IsPathSeparator(ch); }
  102. inline static TConstantString DotString() { return TCharTraits::DotString(); }
  103. inline static SIZE_T DotStringCch() { return TCharTraits::DotStringCch(); }
  104. inline static TChar DotChar() { return TCharTraits::DotChar(); }
  105. protected:
  106. // You may not instantiate an instance of this class directly; you need to provide a derived
  107. // class which adds allocation/deallocation particulars.
  108. CGenericBaseStringBuffer() : m_prgchBuffer(NULL), m_cchBuffer(0), m_cAttachedAccessors(0), m_cch(0)
  109. {
  110. }
  111. //
  112. // Note that somewhat counter-intuitively, there is neither an assignment operator,
  113. // copy constructor or constructor taking a TConstantString. This is necessary
  114. // because such a constructor would need to perform a dynamic allocation
  115. // if the path passed in were longer than nInlineChars which could fail and
  116. // since we do not throw exceptions, constructors may not fail. Instead the caller
  117. // must just perform the default construction and then use the Assign() member
  118. // function, remembering of course to check its return status.
  119. //
  120. ~CGenericBaseStringBuffer()
  121. {
  122. ASSERT_NTC(m_cAttachedAccessors == 0);
  123. }
  124. inline void IntegrityCheck() const
  125. {
  126. #if DBG
  127. ASSERT_NTC(m_cch < m_cchBuffer);
  128. #endif // DBG
  129. }
  130. // Derived constructors should call this to get the initial buffer pointers set up.
  131. inline void InitializeInlineBuffer()
  132. {
  133. ASSERT_NTC(m_prgchBuffer == NULL);
  134. ASSERT_NTC(m_cchBuffer == 0);
  135. m_prgchBuffer = this->GetInlineBuffer();
  136. m_cchBuffer = this->GetInlineBufferCch();
  137. }
  138. VOID AttachAccessor(TAccessor *)
  139. {
  140. ::InterlockedIncrement(&m_cAttachedAccessors);
  141. }
  142. VOID DetachAccessor(TAccessor *)
  143. {
  144. ::InterlockedDecrement(&m_cAttachedAccessors);
  145. }
  146. virtual BOOL Win32AllocateBuffer(SIZE_T cch, TMutableString &rpsz) const = 0;
  147. virtual VOID DeallocateBuffer(TMutableString sz) const = 0;
  148. virtual TMutableString GetInlineBuffer() const = 0;
  149. virtual SIZE_T GetInlineBufferCch() const = 0;
  150. public:
  151. BOOL Win32Assign(PCWSTR psz, SIZE_T cchIn)
  152. {
  153. FN_PROLOG_WIN32
  154. ASSERT(static_cast<SSIZE_T>(cchIn) >= 0);
  155. this->IntegrityCheck();
  156. SIZE_T cchIncludingTrailingNull;
  157. // it would seem innocuous to allow assigns that don't resize the buffer to not
  158. // invalidate accessors, but that makes finding such bugs subject to even more
  159. // strenuous coverage problems than this simple error. The simple rule is that
  160. // you should not have an accessor attached to a string buffer when you use
  161. // any of member functions that may mutate the value.
  162. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0);
  163. IFW32FALSE_EXIT(TCharTraits::Win32DetermineRequiredCharacters(psz, cchIn, cchIncludingTrailingNull));
  164. // Only force the buffer to be dynamically grown if the new contents do not
  165. // fit in the old buffer.
  166. if (cchIncludingTrailingNull > m_cchBuffer)
  167. IFW32FALSE_EXIT(this->Win32ResizeBuffer(cchIncludingTrailingNull, ePreserveBufferContents));
  168. IFW32FALSE_EXIT(TCharTraits::Win32CopyIntoBuffer(m_prgchBuffer, m_cchBuffer, psz, cchIn));
  169. ASSERT(cchIncludingTrailingNull <= m_cchBuffer);
  170. ASSERT((cchIncludingTrailingNull == 0) || this->IsNullCharacter(m_prgchBuffer[cchIncludingTrailingNull - 1]));
  171. // cch was the buffer size we needed (including the trailing null); we don't need the trailing
  172. // null any more...
  173. m_cch = cchIncludingTrailingNull - 1;
  174. FN_EPILOG
  175. }
  176. BOOL Win32Assign(PCSTR psz, SIZE_T cchIn)
  177. {
  178. FN_PROLOG_WIN32
  179. ASSERT(static_cast<SSIZE_T>(cchIn) >= 0);
  180. this->IntegrityCheck();
  181. SIZE_T cchIncludingTrailingNull;
  182. // it would seem innocuous to allow assigns that don't resize the buffer to not
  183. // invalidate accessors, but that makes finding such bugs subject to even more
  184. // strenuous coverage problems than this simple error. The simple rule is that
  185. // you should not have an accessor attached to a string buffer when you use
  186. // any of member functions that may mutate the value.
  187. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0);
  188. IFW32FALSE_EXIT(TCharTraits::Win32DetermineRequiredCharacters(psz, cchIn, cchIncludingTrailingNull));
  189. // Only force the buffer to be dynamically grown if the new contents do not
  190. // fit in the old buffer.
  191. if (cchIncludingTrailingNull > m_cchBuffer)
  192. IFW32FALSE_EXIT(this->Win32ResizeBuffer(cchIncludingTrailingNull, ePreserveBufferContents));
  193. IFW32FALSE_EXIT(TCharTraits::Win32CopyIntoBuffer(m_prgchBuffer, m_cchBuffer, psz, cchIn));
  194. ASSERT(cchIncludingTrailingNull <= m_cchBuffer);
  195. ASSERT((cchIncludingTrailingNull == 0) || this->IsNullCharacter(m_prgchBuffer[cchIncludingTrailingNull - 1]));
  196. // cch was the buffer size we needed (including the trailing null); we don't need the trailing
  197. // null any more...
  198. m_cch = cchIncludingTrailingNull - 1;
  199. FN_EPILOG
  200. }
  201. BOOL Win32Assign(const UNICODE_STRING* NtString)
  202. {
  203. return Win32Assign(NtString->Buffer, RTL_STRING_GET_LENGTH_CHARS(NtString));
  204. }
  205. BOOL Win32Assign(const ANSI_STRING* NtString)
  206. {
  207. return Win32Assign(NtString->Buffer, RTL_STRING_GET_LENGTH_CHARS(NtString));
  208. }
  209. BOOL Win32Assign(const CGenericBaseStringBuffer &r) { return this->Win32Assign(r, r.Cch()); }
  210. BOOL Win32AssignWVa(SIZE_T cStrings, va_list ap)
  211. {
  212. this->IntegrityCheck();
  213. BOOL fSuccess = FALSE;
  214. FN_TRACE_WIN32(fSuccess);
  215. TMutableString pszCursor;
  216. SIZE_T cchIncludingTrailingNull = 1; // leave space for trailing null...
  217. SIZE_T cchTemp;
  218. SIZE_T i;
  219. va_list ap2 = ap;
  220. // it would seem innocuous to allow assigns that don't resize the buffer to not
  221. // invalidate accessors, but that makes finding such bugs subject to even more
  222. // strenuous coverage problems than this simple error. The simple rule is that
  223. // you should not have an accessor attached to a string buffer when you use
  224. // any of member functions that may mutate the value.
  225. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0);
  226. for (i=0; i<cStrings; i++)
  227. {
  228. PCWSTR psz = va_arg(ap, PCWSTR);
  229. INT cchArg = va_arg(ap, INT);
  230. SIZE_T cchThis = (cchArg < 0) ? ((psz != NULL) ? ::wcslen(psz) : 0) : static_cast<SIZE_T>(cchArg);
  231. SIZE_T cchRequired;
  232. IFW32FALSE_EXIT(TCharTraits::Win32DetermineRequiredCharacters(psz, cchThis, cchRequired));
  233. ASSERT((cchRequired != 0) || (cchThis == 0));
  234. cchIncludingTrailingNull += (cchRequired - 1);
  235. }
  236. IFW32FALSE_EXIT(this->Win32ResizeBuffer(cchIncludingTrailingNull, eDoNotPreserveBufferContents));
  237. pszCursor = m_prgchBuffer;
  238. cchTemp = cchIncludingTrailingNull;
  239. for (i=0; i<cStrings; i++)
  240. {
  241. PCWSTR psz = va_arg(ap2, PCWSTR);
  242. INT cchArg = va_arg(ap2, INT);
  243. SIZE_T cchThis = (cchArg < 0) ? ((psz != NULL) ? ::wcslen(psz) : 0) : static_cast<SIZE_T>(cchArg);
  244. IFW32FALSE_EXIT(TCharTraits::Win32CopyIntoBufferAndAdvanceCursor(pszCursor, cchTemp, psz, cchThis));
  245. }
  246. *pszCursor++ = this->NullCharacter();
  247. ASSERT(cchTemp == 1);
  248. ASSERT(static_cast<SIZE_T>(pszCursor - m_prgchBuffer) == cchIncludingTrailingNull);
  249. m_cch = (cchIncludingTrailingNull - 1);
  250. FN_EPILOG
  251. }
  252. BOOL Win32AssignW(ULONG cStrings, ...)
  253. {
  254. this->IntegrityCheck();
  255. BOOL fSuccess = FALSE;
  256. FN_TRACE_WIN32(fSuccess);
  257. va_list ap;
  258. va_start(ap, cStrings);
  259. IFW32FALSE_EXIT(this->Win32AssignWVa(cStrings, ap));
  260. fSuccess = TRUE;
  261. Exit:
  262. va_end(ap);
  263. return fSuccess;
  264. }
  265. BOOL Win32AssignFill(TChar ch, SIZE_T cch)
  266. {
  267. FN_PROLOG_WIN32
  268. TMutableString Cursor;
  269. ASSERT(static_cast<SSIZE_T>(cch) >= 0);
  270. IFW32FALSE_EXIT(this->Win32ResizeBuffer(cch + 1, eDoNotPreserveBufferContents));
  271. Cursor = m_prgchBuffer;
  272. while (cch > 0)
  273. {
  274. *Cursor++ = ch;
  275. cch--;
  276. }
  277. *Cursor = NullCharacter();
  278. m_cch = (Cursor - m_prgchBuffer);
  279. FN_EPILOG
  280. }
  281. BOOL Win32Append(const UNICODE_STRING *pus) { return this->Win32Append(pus->Buffer, RTL_STRING_GET_LENGTH_CHARS(pus)); }
  282. BOOL Win32Append(PCWSTR sz, SIZE_T cchIn)
  283. {
  284. this->IntegrityCheck();
  285. BOOL fSuccess = FALSE;
  286. FN_TRACE_WIN32(fSuccess);
  287. ASSERT(static_cast<SSIZE_T>(cchIn) >= 0);
  288. SIZE_T cchIncludingTrailingNull; // note that cch will include space for a tailing null character
  289. // it would seem innocuous to allow assigns that don't resize the buffer to not
  290. // invalidate accessors, but that makes finding such bugs subject to even more
  291. // strenuous coverage problems than this simple error. The simple rule is that
  292. // you should not have an accessor attached to a string buffer when you use
  293. // any of member functions that may mutate the value.
  294. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0);
  295. IFW32FALSE_EXIT(TCharTraits::Win32DetermineRequiredCharacters(sz, cchIn, cchIncludingTrailingNull));
  296. // Bypass all this junk if the string to append is empty.
  297. if (cchIncludingTrailingNull > 1)
  298. {
  299. IFW32FALSE_EXIT(this->Win32ResizeBuffer(m_cch + cchIncludingTrailingNull, ePreserveBufferContents));
  300. IFW32FALSE_EXIT(TCharTraits::Win32CopyIntoBuffer(&m_prgchBuffer[m_cch], m_cchBuffer - m_cch, sz, cchIn));
  301. m_cch += (cchIncludingTrailingNull - 1);
  302. }
  303. fSuccess = TRUE;
  304. Exit:
  305. return fSuccess;
  306. }
  307. BOOL Win32Append(PCSTR sz, SIZE_T cchIn)
  308. {
  309. this->IntegrityCheck();
  310. BOOL fSuccess = FALSE;
  311. FN_TRACE_WIN32(fSuccess);
  312. ASSERT(static_cast<SSIZE_T>(cchIn) >= 0);
  313. SIZE_T cchIncludingTrailingNull;
  314. // it would seem innocuous to allow assigns that don't resize the buffer to not
  315. // invalidate accessors, but that makes finding such bugs subject to even more
  316. // strenuous coverage problems than this simple error. The simple rule is that
  317. // you should not have an accessor attached to a string buffer when you use
  318. // any of member functions that may mutate the value.
  319. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0);
  320. IFW32FALSE_EXIT(TCharTraits::Win32DetermineRequiredCharacters(sz, cchIn, cchIncludingTrailingNull));
  321. // Bypass all this junk if the string to append is empty.
  322. if (cchIncludingTrailingNull > 1)
  323. {
  324. IFW32FALSE_EXIT(this->Win32ResizeBuffer(m_cch + cchIncludingTrailingNull, ePreserveBufferContents));
  325. IFW32FALSE_EXIT(TCharTraits::Win32CopyIntoBuffer(&m_prgchBuffer[m_cch], m_cchBuffer - m_cch, sz, cchIn));
  326. m_cch += (cchIncludingTrailingNull - 1);
  327. this->IntegrityCheck();
  328. }
  329. FN_EPILOG
  330. }
  331. BOOL Win32Append(const CGenericBaseStringBuffer &r) { return this->Win32Append(r, r.Cch()); }
  332. BOOL Win32Append(WCHAR wch) { WCHAR rgwch[1] = { wch }; return this->Win32Append(rgwch, 1); }
  333. BOOL Win32AppendFill(TChar ch, SIZE_T cch)
  334. {
  335. FN_PROLOG_WIN32
  336. ASSERT(static_cast<SSIZE_T>(cch) >= 0);
  337. TMutableString Cursor;
  338. IFW32FALSE_EXIT(this->Win32ResizeBuffer(m_cch + cch + 1, ePreserveBufferContents));
  339. Cursor = m_prgchBuffer + m_cch;
  340. while (cch > 0)
  341. {
  342. *Cursor++ = ch;
  343. cch--;
  344. }
  345. *Cursor = NullCharacter();
  346. m_cch = Cursor - m_prgchBuffer;
  347. FN_EPILOG
  348. }
  349. BOOL Win32Prepend(const CGenericBaseStringBuffer& other ) { return this->Win32Prepend(other, other.Cch()); }
  350. BOOL Win32Prepend(TConstantString sz, SIZE_T cchIn)
  351. {
  352. this->IntegrityCheck();
  353. BOOL fSuccess = FALSE;
  354. FN_TRACE_WIN32(fSuccess);
  355. ASSERT(static_cast<SSIZE_T>(cchIn) >= 0);
  356. SIZE_T cchIncludingTrailingNull; // note that cch will include space for a tailing null character
  357. // it would seem innocuous to allow assigns that don't resize the buffer to not
  358. // invalidate accessors, but that makes finding such bugs subject to even more
  359. // strenuous coverage problems than this simple error. The simple rule is that
  360. // you should not have an accessor attached to a string buffer when you use
  361. // any of member functions that may mutate the value.
  362. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0);
  363. if ( m_cch == 0 )
  364. {
  365. IFW32FALSE_EXIT(this->Win32Assign(sz, cchIn));
  366. }
  367. else
  368. {
  369. //
  370. // Enlarge the buffer, move the current data to past where the new data will need
  371. // to go, copy in the new data, and place the trailing null.
  372. //
  373. TChar SavedChar = m_prgchBuffer[0];
  374. IFW32FALSE_EXIT(TCharTraits::Win32DetermineRequiredCharacters(sz, cchIn, cchIncludingTrailingNull));
  375. IFW32FALSE_EXIT(this->Win32ResizeBuffer(m_cch + cchIncludingTrailingNull, ePreserveBufferContents));
  376. // Move current buffer "up"
  377. memmove(m_prgchBuffer + ( cchIncludingTrailingNull - 1), m_prgchBuffer, (m_cch + 1) * sizeof(TChar));
  378. // Copy from the source string into the buffer.
  379. IFW32FALSE_EXIT(TCharTraits::Win32CopyIntoBuffer(
  380. this->m_prgchBuffer,
  381. this->m_cchBuffer,
  382. sz,
  383. cchIn));
  384. m_prgchBuffer[cchIncludingTrailingNull - 1] = SavedChar;
  385. m_cch += cchIncludingTrailingNull - 1;
  386. }
  387. #if 0
  388. IFW32FALSE_EXIT(TCharTraits::Win32DetermineRequiredCharacters(sz, cchIn, cchIncludingTrailingNull));
  389. // Bypass all this junk if the string to prepend is empty.
  390. if (cchIncludingTrailingNull > 1)
  391. {
  392. if (m_cch == 0)
  393. {
  394. // Empty string, simple operation
  395. IFW32FALSE_EXIT(this->Win32Assign(sz, cchIn));
  396. }
  397. else
  398. {
  399. // Otherwise, resize the buffer and
  400. //
  401. TChar chTemp = m_prgchBuffer[0];
  402. IFW32FALSE_EXIT(this->Win32ResizeBuffer(m_cch + cchIncludingTrailingNull, ePreserveBufferContents));
  403. memmove(&m_prgchBuffer[cchIncludingTrailingNull - 1], &m_prgchBuffer[0], (m_cch + 1) * sizeof(TChar));
  404. IFW32FALSE_EXIT(TCharTraits::Win32CopyIntoBuffer(&m_prgchBuffer[0], m_cchBuffer, sz, cchIn));
  405. // Restore old first character of the string overwritten by the null
  406. m_prgchBuffer[cchIncludingTrailingNull - 1] = chTemp;
  407. m_cch += (cchIncludingTrailingNull - 1);
  408. }
  409. }
  410. #endif
  411. FN_EPILOG
  412. }
  413. BOOL Win32Prepend(TChar ch)
  414. {
  415. FN_PROLOG_WIN32
  416. IFW32FALSE_EXIT(this->Win32ResizeBuffer(m_cch + 1 + 1, ePreserveBufferContents));
  417. // move buffer ahead, including null
  418. memmove(m_prgchBuffer + 1, m_prgchBuffer, (m_cch + 1) * sizeof(TChar));
  419. m_prgchBuffer[0] = ch;
  420. m_cch++;
  421. FN_EPILOG
  422. }
  423. operator TConstantString() const { this->IntegrityCheck(); return m_prgchBuffer; }
  424. inline VOID Clear(bool fFreeStorage = false)
  425. {
  426. FN_TRACE();
  427. this->IntegrityCheck();
  428. // You can't free the storage if there's an attached accessor
  429. ASSERT(!fFreeStorage || m_cAttachedAccessors == 0);
  430. if (fFreeStorage && (m_cAttachedAccessors == 0))
  431. {
  432. if (m_prgchBuffer != NULL)
  433. {
  434. const TMutableString pszInlineBuffer = this->GetInlineBuffer();
  435. if (m_prgchBuffer != pszInlineBuffer)
  436. {
  437. this->DeallocateBuffer(m_prgchBuffer);
  438. m_prgchBuffer = pszInlineBuffer;
  439. m_cchBuffer = this->GetInlineBufferCch();
  440. }
  441. }
  442. }
  443. if (m_prgchBuffer != NULL)
  444. m_prgchBuffer[0] = this->NullCharacter();
  445. m_cch = 0;
  446. }
  447. BOOL Win32ConvertCase( ECaseConversionDirection direction )
  448. {
  449. #if !FUSION_WIN
  450. return FALSE;
  451. #else
  452. FN_PROLOG_WIN32
  453. this->IntegrityCheck();
  454. // it would seem innocuous to allow assigns that don't resize the buffer to not
  455. // invalidate accessors, but that makes finding such bugs subject to even more
  456. // strenuous coverage problems than this simple error. The simple rule is that
  457. // you should not have an accessor attached to a string buffer when you use
  458. // any of member functions that may mutate the value.
  459. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0);
  460. TMutableString Cursor = m_prgchBuffer;
  461. for ( ULONG ul = 0; ul < this->Cch(); ul++ )
  462. {
  463. if ( direction == eConvertToUpperCase )
  464. *Cursor = RtlUpcaseUnicodeChar(*Cursor);
  465. else
  466. *Cursor = RtlDowncaseUnicodeChar(*Cursor);
  467. Cursor++;
  468. }
  469. FN_EPILOG
  470. #endif
  471. }
  472. BOOL Win32Compare(TConstantString szCandidate, SIZE_T cchCandidate, StringComparisonResult &rscrOut, bool fCaseInsensitive) const
  473. {
  474. this->IntegrityCheck();
  475. BOOL fSuccess = FALSE;
  476. FN_TRACE_WIN32(fSuccess);
  477. IFW32FALSE_EXIT(TCharTraits::Win32CompareStrings(rscrOut, m_prgchBuffer, m_cch, szCandidate, cchCandidate, fCaseInsensitive));
  478. FN_EPILOG
  479. }
  480. BOOL Win32Equals(TConstantString szCandidate, SIZE_T cchCandidate, bool &rfMatches, bool fCaseInsensitive) const
  481. {
  482. this->IntegrityCheck();
  483. BOOL fSuccess = FALSE;
  484. FN_TRACE_WIN32(fSuccess);
  485. IFW32FALSE_EXIT(
  486. TCharTraits::Win32EqualStrings(
  487. rfMatches,
  488. m_prgchBuffer,
  489. m_cch,
  490. szCandidate,
  491. cchCandidate,
  492. fCaseInsensitive));
  493. FN_EPILOG
  494. }
  495. BOOL Win32Equals(const CGenericBaseStringBuffer &r, bool &rfMatches, bool fCaseInsensitive) const
  496. {
  497. return this->Win32Equals(r, r.Cch(), rfMatches, fCaseInsensitive);
  498. }
  499. SIZE_T GetBufferCch() const { this->IntegrityCheck(); return m_cchBuffer; }
  500. INT GetBufferCchAsINT() const { this->IntegrityCheck(); if (m_cchBuffer > INT_MAX) return INT_MAX; return static_cast<INT>(m_cchBuffer); }
  501. DWORD GetBufferCchAsDWORD() const { this->IntegrityCheck(); if (m_cchBuffer > DWORD_MAX) return DWORD_MAX; return static_cast<DWORD>(m_cchBuffer); }
  502. SIZE_T GetBufferCb() const { this->IntegrityCheck(); return m_cchBuffer * sizeof(TChar); }
  503. INT GetBufferCbAsINT() const { this->IntegrityCheck(); if ((m_cchBuffer * sizeof(TChar)) > INT_MAX) return INT_MAX; return static_cast<INT>(m_cchBuffer * sizeof(TChar)); }
  504. DWORD GetBufferCbAsDWORD() const { this->IntegrityCheck(); if ((m_cchBuffer * sizeof(TChar)) > DWORD_MAX) return DWORD_MAX; return static_cast<DWORD>(m_cchBuffer * sizeof(TChar)); }
  505. bool ContainsCharacter(WCHAR wch) const
  506. {
  507. this->IntegrityCheck();
  508. return TCharTraits::ContainsCharacter(m_prgchBuffer, m_cch, wch);
  509. }
  510. BOOL Win32ResizeBuffer(
  511. SIZE_T cch,
  512. EPreserveContents epc
  513. )
  514. {
  515. FN_PROLOG_WIN32
  516. this->IntegrityCheck();
  517. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0);
  518. PARAMETER_CHECK((epc == ePreserveBufferContents) || (epc == eDoNotPreserveBufferContents));
  519. if (cch > m_cchBuffer)
  520. {
  521. TMutableString prgchBufferNew = NULL;
  522. IFW32FALSE_EXIT(this->Win32AllocateBuffer(cch, prgchBufferNew));
  523. if (epc == ePreserveBufferContents)
  524. {
  525. // We assume that the buffer is/was null-terminated.
  526. IFW32FALSE_EXIT(TCharTraits::Win32CopyIntoBuffer(prgchBufferNew, cch, m_prgchBuffer, m_cch));
  527. }
  528. else
  529. {
  530. m_prgchBuffer[0] = this->NullCharacter();
  531. m_cch = 0;
  532. }
  533. if ((m_prgchBuffer != NULL) && (m_prgchBuffer != this->GetInlineBuffer()))
  534. this->DeallocateBuffer(m_prgchBuffer);
  535. m_prgchBuffer = prgchBufferNew;
  536. m_cchBuffer = cch;
  537. }
  538. FN_EPILOG
  539. }
  540. BOOL Win32Format(TConstantString pszFormat, ...)
  541. {
  542. this->IntegrityCheck();
  543. va_list args;
  544. va_start(args, pszFormat);
  545. BOOL f = this->Win32FormatV(pszFormat, args);
  546. va_end(args);
  547. return f;
  548. }
  549. BOOL Win32FormatAppend(TConstantString pszFormat, ...)
  550. {
  551. this->IntegrityCheck();
  552. va_list args;
  553. va_start(args, pszFormat);
  554. BOOL f = Win32FormatAppendV(pszFormat, args);
  555. va_end(args);
  556. return f;
  557. }
  558. BOOL Win32FormatV(TConstantString pszFormat, va_list args)
  559. {
  560. BOOL fSuccess = FALSE;
  561. this->Clear();
  562. fSuccess = Win32FormatAppendV(pszFormat, args);
  563. return fSuccess;
  564. }
  565. BOOL Win32FormatAppendV(TConstantString pszFormat, va_list args)
  566. {
  567. BOOL fSuccess = FALSE;
  568. FN_TRACE_WIN32(fSuccess);
  569. SIZE_T cchRequiredBufferSize = 0;
  570. INT i = 0;
  571. this->IntegrityCheck();
  572. // it would seem innocuous to allow assigns that don't resize the buffer to not
  573. // invalidate accessors, but that makes finding such bugs subject to even more
  574. // strenuous coverage problems than this simple error. The simple rule is that
  575. // you should not have an accessor attached to a string buffer when you use
  576. // any of member functions that may mutate the value.
  577. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0);
  578. #if 0 // ntdll.dll dependency
  579. IFW32FALSE_EXIT(TCharTraits::Win32GetRequiredBufferSizeInCharsForFormatV(pszFormat, args, cchRequiredBufferSize));
  580. IFW32FALSE_EXIT(this->Win32ResizeBuffer(m_cch + cchRequiredBufferSize + 1, ePreserveBufferContents));
  581. #endif
  582. m_prgchBuffer[m_cchBuffer - 1] = this->NullCharacter();
  583. i = TCharTraits::FormatV(m_prgchBuffer + m_cch, m_cchBuffer - 1 - m_cch, pszFormat, args);
  584. ASSERT(m_prgchBuffer[m_cchBuffer - 1] == NullCharacter());
  585. fSuccess = (i >= 0);
  586. if ( fSuccess )
  587. m_cch += i;
  588. else
  589. {
  590. //
  591. // Sprintf doesn't touch last error. The fn tracer
  592. // will fail an assertion if we return false but FusionpGetLastWin32Error()==NOERROR
  593. //
  594. ORIGINATE_WIN32_FAILURE_AND_EXIT(snwprintf_MaybeBufferTooSmall, ERROR_INVALID_PARAMETER);
  595. }
  596. Exit:
  597. return fSuccess;
  598. }
  599. DWORD GetCchAsDWORD() const
  600. {
  601. this->IntegrityCheck();
  602. if (m_cch > MAXDWORD)
  603. return MAXDWORD;
  604. else
  605. return (DWORD)m_cch;
  606. }
  607. SIZE_T Cch() const
  608. {
  609. this->IntegrityCheck();
  610. return m_cch;
  611. }
  612. BOOL IsEmpty() const
  613. {
  614. this->IntegrityCheck();
  615. return m_prgchBuffer[0] == 0;
  616. }
  617. BOOL Win32EnsureTrailingChar(WCHAR ch)
  618. {
  619. this->IntegrityCheck();
  620. BOOL fSuccess = FALSE;
  621. // it would seem innocuous to allow assigns that don't resize the buffer to not
  622. // invalidate accessors, but that makes finding such bugs subject to even more
  623. // strenuous coverage problems than this simple error. The simple rule is that
  624. // you should not have an accessor attached to a string buffer when you use
  625. // any of member functions that may mutate the value.
  626. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0);
  627. if ((m_cch == 0) || (m_prgchBuffer[m_cch - 1] != ch))
  628. {
  629. IFW32FALSE_EXIT(this->Win32ResizeBuffer(m_cch + 1 + 1, ePreserveBufferContents));
  630. m_prgchBuffer[m_cch++] = ch;
  631. m_prgchBuffer[m_cch] = this->NullCharacter();
  632. }
  633. fSuccess = TRUE;
  634. Exit:
  635. return fSuccess;
  636. }
  637. BOOL Win32EnsureTrailingPathSeparator()
  638. {
  639. this->IntegrityCheck();
  640. BOOL fSuccess = FALSE;
  641. FN_TRACE_WIN32(fSuccess);
  642. // it would seem innocuous to allow assigns that don't resize the buffer to not
  643. // invalidate accessors, but that makes finding such bugs subject to even more
  644. // strenuous coverage problems than this simple error. The simple rule is that
  645. // you should not have an accessor attached to a string buffer when you use
  646. // any of member functions that may mutate the value.
  647. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0);
  648. if ((m_cch == 0) || !TCharTraits::IsPathSeparator(m_prgchBuffer[m_cch - 1]))
  649. {
  650. IFW32FALSE_EXIT(this->Win32ResizeBuffer(m_cch + 1 + 1, ePreserveBufferContents));
  651. m_prgchBuffer[m_cch++] = this->PreferredPathSeparator();
  652. m_prgchBuffer[m_cch] = this->NullCharacter();
  653. }
  654. fSuccess = TRUE;
  655. Exit:
  656. return fSuccess;
  657. }
  658. BOOL Win32AppendPathElement(PCWSTR pathElement, SIZE_T cchPathElement)
  659. {
  660. this->IntegrityCheck();
  661. BOOL fSuccess = FALSE;
  662. FN_TRACE_WIN32(fSuccess);
  663. // it would seem innocuous to allow assigns that don't resize the buffer to not
  664. // invalidate accessors, but that makes finding such bugs subject to even more
  665. // strenuous coverage problems than this simple error. The simple rule is that
  666. // you should not have an accessor attached to a string buffer when you use
  667. // any of member functions that may mutate the value.
  668. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0);
  669. IFW32FALSE_EXIT(this->Win32EnsureTrailingPathSeparator());
  670. IFW32FALSE_EXIT(this->Win32Append(pathElement, cchPathElement));
  671. fSuccess = TRUE;
  672. Exit:
  673. return fSuccess;
  674. }
  675. BOOL Win32AppendPathElement(const UNICODE_STRING *pus) { return this->Win32AppendPathElement(pus->Buffer, RTL_STRING_GET_LENGTH_CHARS(pus)); }
  676. BOOL Win32AppendPathElement(const CGenericBaseStringBuffer &r) { return this->Win32AppendPathElement(r, r.Cch()); }
  677. BOOL Win32AppendPathElement(PCSTR pathElement, SIZE_T cchPathElement)
  678. {
  679. this->IntegrityCheck();
  680. BOOL fSuccess = FALSE;
  681. FN_TRACE_WIN32(fSuccess);
  682. // it would seem innocuous to allow assigns that don't resize the buffer to not
  683. // invalidate accessors, but that makes finding such bugs subject to even more
  684. // strenuous coverage problems than this simple error. The simple rule is that
  685. // you should not have an accessor attached to a string buffer when you use
  686. // any of member functions that may mutate the value.
  687. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0);
  688. IFW32FALSE_EXIT(this->Win32EnsureTrailingPathSeparator());
  689. IFW32FALSE_EXIT(this->Win32Append(pathElement, cchPathElement));
  690. fSuccess = TRUE;
  691. Exit:
  692. return fSuccess;
  693. }
  694. VOID Left(SIZE_T newLength)
  695. {
  696. FN_TRACE();
  697. this->IntegrityCheck();
  698. ASSERT(newLength <= m_cch);
  699. // it would seem innocuous to allow assigns that don't resize the buffer to not
  700. // invalidate accessors, but that makes finding such bugs subject to even more
  701. // strenuous coverage problems than this simple error. The simple rule is that
  702. // you should not have an accessor attached to a string buffer when you use
  703. // any of member functions that may mutate the value.
  704. // Note also that while the current implementation does not change the buffer
  705. // pointer, this is just a shortcut in the implementation; if a call to Left()
  706. // were to make the string short enough to fit in the inline buffer, we should
  707. // copy it to the inline buffer and deallocate the dynamic one.
  708. ASSERT(m_cAttachedAccessors == 0);
  709. if (m_cchBuffer > newLength)
  710. {
  711. m_prgchBuffer[newLength] = this->NullCharacter();
  712. }
  713. m_cch = newLength;
  714. }
  715. TConstantString Begin() const
  716. {
  717. this->IntegrityCheck();
  718. return m_prgchBuffer;
  719. }
  720. TConstantString End() const
  721. {
  722. this->IntegrityCheck();
  723. return &m_prgchBuffer[m_cch];
  724. }
  725. // should factor this for reuse in CchWithoutLastPathElement
  726. SIZE_T CchWithoutTrailingPathSeparators() const
  727. {
  728. this->IntegrityCheck();
  729. // Until GetLength is constant time, optimize its use..
  730. SIZE_T length = m_cch;
  731. if (length > 0)
  732. {
  733. length -= ::StringReverseSpan(&*m_prgchBuffer, &*m_prgchBuffer + length, TCharTraits::PathSeparators());
  734. }
  735. return length;
  736. }
  737. BOOL RestoreNextPathElement()
  738. {
  739. SIZE_T index;
  740. index = m_cch;
  741. m_prgchBuffer[index++] = L'\\'; // replace trailing NULL with '\'
  742. while ((index < m_cchBuffer) && (!this->IsNullCharacter(m_prgchBuffer[index])))
  743. {
  744. if (::FusionpIsPathSeparator(m_prgchBuffer[index]))
  745. {
  746. this->Left(index);
  747. return TRUE;
  748. }
  749. index++;
  750. }
  751. return FALSE;
  752. }
  753. bool HasTrailingPathSeparator() const
  754. {
  755. FN_TRACE();
  756. this->IntegrityCheck();
  757. if ((m_cch != 0) && TCharTraits::IsPathSeparator(m_prgchBuffer[m_cch - 1]))
  758. return true;
  759. return false;
  760. }
  761. VOID RemoveTrailingPathSeparators()
  762. {
  763. FN_TRACE();
  764. this->IntegrityCheck();
  765. // it would seem innocuous to allow assigns that don't resize the buffer to not
  766. // invalidate accessors, but that makes finding such bugs subject to even more
  767. // strenuous coverage problems than this simple error. The simple rule is that
  768. // you should not have an accessor attached to a string buffer when you use
  769. // any of member functions that may mutate the value.
  770. // Note also that while the current implementation does not change the buffer
  771. // pointer, this is just a shortcut in the implementation; if a call to Left()
  772. // were to make the string short enough to fit in the inline buffer, we should
  773. // copy it to the inline buffer and deallocate the dynamic one.
  774. ASSERT(m_cAttachedAccessors == 0);
  775. while ((m_cch != 0) && TCharTraits::IsPathSeparator(m_prgchBuffer[m_cch - 1]))
  776. m_cch--;
  777. m_prgchBuffer[m_cch] = this->NullCharacter();
  778. }
  779. VOID Right( SIZE_T cchRightCount )
  780. {
  781. FN_TRACE();
  782. this->IntegrityCheck();
  783. ASSERT(m_cAttachedAccessors == 0);
  784. ASSERT(cchRightCount <= m_cch);
  785. if (cchRightCount < m_cch)
  786. {
  787. ::memmove(
  788. m_prgchBuffer,
  789. &m_prgchBuffer[m_cch - cchRightCount],
  790. (cchRightCount + 1)*sizeof(TCharTraits::TChar));
  791. m_cch = cchRightCount;
  792. }
  793. }
  794. VOID RemoveLeadingPathSeparators()
  795. {
  796. this->Right(m_cch - wcsspn(m_prgchBuffer, TCharTraits::PathSeparators()));
  797. }
  798. BOOL Win32StripToLastPathElement()
  799. {
  800. BOOL fSuccess = FALSE;
  801. FN_TRACE_WIN32(fSuccess);
  802. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0);
  803. this->Right(m_cch - this->CchWithoutLastPathElement());
  804. this->RemoveLeadingPathSeparators();
  805. fSuccess = TRUE;
  806. Exit:
  807. return fSuccess;
  808. }
  809. BOOL Win32GetFirstPathElement( CGenericBaseStringBuffer &sbDestination, BOOL bRemoveAsWell = FALSE )
  810. {
  811. FN_PROLOG_WIN32
  812. this->IntegrityCheck();
  813. IFW32FALSE_EXIT( sbDestination.Win32Assign( m_prgchBuffer, this->CchOfFirstPathElement() ) );
  814. sbDestination.RemoveLeadingPathSeparators();
  815. if ( bRemoveAsWell )
  816. IFW32FALSE_EXIT(this->Win32RemoveFirstPathElement());
  817. FN_EPILOG
  818. }
  819. BOOL Win32GetFirstPathElement( CGenericBaseStringBuffer &sbDestination ) const
  820. {
  821. BOOL bSuccess = FALSE;
  822. this->IntegrityCheck();
  823. if ( sbDestination.Win32Assign( m_prgchBuffer, CchOfFirstPathElement() ) )
  824. {
  825. sbDestination.RemoveLeadingPathSeparators();
  826. bSuccess = TRUE;
  827. }
  828. return bSuccess;
  829. }
  830. BOOL Win32StripToFirstPathElement()
  831. {
  832. BOOL fSuccess = FALSE;
  833. FN_TRACE_WIN32(fSuccess);
  834. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0);
  835. this->IntegrityCheck();
  836. this->Left(this->CchOfFirstPathElement());
  837. this->RemoveLeadingPathSeparators();
  838. fSuccess = TRUE;
  839. Exit:
  840. return fSuccess;
  841. }
  842. BOOL Win32RemoveFirstPathElement()
  843. {
  844. BOOL fSuccess = FALSE;
  845. FN_TRACE_WIN32(fSuccess);
  846. IntegrityCheck();
  847. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0);
  848. this->Right(this->CchWithoutFirstPathElement());
  849. this->RemoveLeadingPathSeparators();
  850. fSuccess = TRUE;
  851. Exit:
  852. return fSuccess;
  853. }
  854. SIZE_T CchOfFirstPathElement() const
  855. {
  856. return Cch() - CchWithoutFirstPathElement();
  857. }
  858. SIZE_T CchWithoutFirstPathElement() const
  859. {
  860. this->IntegrityCheck();
  861. SIZE_T cch = m_cch;
  862. //
  863. // We just look for the first path element, which can also be the drive
  864. // letter!
  865. //
  866. if ( cch != 0 )
  867. {
  868. cch -= wcscspn( m_prgchBuffer, PathSeparators() );
  869. }
  870. return cch;
  871. }
  872. BOOL Win32GetLastPathElement(CGenericBaseStringBuffer &sbDestination) const
  873. {
  874. BOOL bSuccess = FALSE;
  875. FN_TRACE_WIN32(bSuccess);
  876. this->IntegrityCheck();
  877. IFW32FALSE_EXIT(sbDestination.Win32Assign(m_prgchBuffer, m_cch));
  878. IFW32FALSE_EXIT(sbDestination.Win32StripToLastPathElement());
  879. bSuccess = TRUE;
  880. Exit:
  881. return bSuccess;
  882. }
  883. SIZE_T CchWithoutLastPathElement() const
  884. {
  885. this->IntegrityCheck();
  886. // Paths are assumed to be
  887. // "\\machine\share"
  888. // or
  889. // "x:\"
  890. // Worry about alternate NTFS streams at a later date.
  891. // Worry about NT paths at a later date.
  892. // Worry about URLs at a later date.
  893. const SIZE_T length = m_cch;
  894. SIZE_T newLength = length;
  895. if (length > 0)
  896. {
  897. if ((length == 3) &&
  898. (m_prgchBuffer[1] == ':') &&
  899. ::FusionpIsPathSeparator(m_prgchBuffer[2]) &&
  900. ::FusionpIsDriveLetter(m_prgchBuffer[0]))
  901. {
  902. // c:\ => empty string
  903. newLength = 0;
  904. }
  905. else
  906. {
  907. // Remove trailing path seperators here, in the future when it is not risky.
  908. //newLength -= ::StringReverseSpan(&*m_prgchBuffer, &*m_prgchBuffer + newLength, PathSeparators());
  909. newLength -= ::StringReverseComplementSpan(&*m_prgchBuffer, &*m_prgchBuffer + newLength, PathSeparators());
  910. newLength -= ::StringReverseSpan(&*m_prgchBuffer, &*m_prgchBuffer + newLength, PathSeparators());
  911. if ((newLength == 2) && // "c:"
  912. (length >= 4) && // "c:\d"
  913. (m_prgchBuffer[1] == ':') &&
  914. ::FusionpIsPathSeparator(m_prgchBuffer[2]) &&
  915. ::FusionpIsDriveLetter(m_prgchBuffer[0]))
  916. {
  917. ++newLength; // put back the slash in "c:\"
  918. }
  919. }
  920. }
  921. return newLength;
  922. }
  923. VOID RemoveLastPathElement()
  924. {
  925. FN_TRACE();
  926. this->IntegrityCheck();
  927. // it would seem innocuous to allow assigns that don't resize the buffer to not
  928. // invalidate accessors, but that makes finding such bugs subject to even more
  929. // strenuous coverage problems than this simple error. The simple rule is that
  930. // you should not have an accessor attached to a string buffer when you use
  931. // any of member functions that may mutate the value.
  932. // Note also that while the current implementation does not change the buffer
  933. // pointer, this is just a shortcut in the implementation; if a call to Left()
  934. // were to make the string short enough to fit in the inline buffer, we should
  935. // copy it to the inline buffer and deallocate the dynamic one.
  936. ASSERT(m_cAttachedAccessors == 0);
  937. this->Left(this->CchWithoutLastPathElement());
  938. }
  939. BOOL Win32ClearPathExtension()
  940. {
  941. //
  942. // Replace the final '.' with a \0 to clear the path extension
  943. //
  944. BOOL fSuccess = FALSE;
  945. FN_TRACE_WIN32( fSuccess );
  946. IntegrityCheck();
  947. TMutableString dot;
  948. const TMutableString end = End();
  949. IFW32FALSE_EXIT(TCharTraits::Win32ReverseFind(dot, m_prgchBuffer, m_cch, this->DotChar(), false));
  950. if((dot != end) && (dot != NULL))
  951. {
  952. *dot = this->NullCharacter();
  953. m_cch = (dot - m_prgchBuffer);
  954. }
  955. fSuccess = TRUE;
  956. Exit:
  957. return fSuccess;
  958. }
  959. BOOL Win32GetPathExtension(CGenericBaseStringBuffer<TCharTraits> &destination) const
  960. {
  961. this->IntegrityCheck();
  962. BOOL fSuccess = FALSE;
  963. FN_TRACE_WIN32(fSuccess);
  964. SIZE_T cchExtension;
  965. const TConstantString start = Begin();
  966. const TConstantString end = End();
  967. cchExtension = StringReverseComplementSpan( &(*start), &(*end), L"." );
  968. IFW32FALSE_EXIT(destination.Win32Assign( static_cast<PCWSTR>(*this) + ( m_cch - cchExtension ), cchExtension));
  969. fSuccess = TRUE;
  970. Exit:
  971. return fSuccess;
  972. }
  973. // newExtension can start with a dot or not
  974. BOOL Win32ChangePathExtension(PCWSTR newExtension, SIZE_T cchExtension, EIfNoExtension e)
  975. {
  976. this->IntegrityCheck();
  977. BOOL fSuccess = FALSE;
  978. FN_TRACE_WIN32(fSuccess);
  979. TMutableString end;
  980. TMutableString dot;
  981. INTERNAL_ERROR_CHECK(m_cAttachedAccessors == 0);
  982. PARAMETER_CHECK((e == eAddIfNoExtension) ||
  983. (e == eDoNothingIfNoExtension) ||
  984. (e == eErrorIfNoExtension));
  985. if ((cchExtension != 0) && (newExtension[0] == L'.'))
  986. {
  987. cchExtension--;
  988. newExtension++;
  989. }
  990. // the use of append when we know where the end of the string is inefficient
  991. end = this->End();
  992. IFW32FALSE_EXIT(TCharTraits::Win32ReverseFind(dot, m_prgchBuffer, m_cch, this->DotChar(), false));
  993. // Found the end of the string, or Win32ReverseFind didn't find the dot anywhere...
  994. if ((dot == end) || (dot == NULL))
  995. {
  996. switch (e)
  997. {
  998. case eAddIfNoExtension:
  999. IFW32FALSE_EXIT(this->Win32Append(this->DotString(), 1));
  1000. IFW32FALSE_EXIT(this->Win32Append(newExtension, cchExtension));
  1001. break;
  1002. case eDoNothingIfNoExtension:
  1003. break;
  1004. case eErrorIfNoExtension:
  1005. ORIGINATE_WIN32_FAILURE_AND_EXIT(MissingExtension, ERROR_BAD_PATHNAME);
  1006. }
  1007. }
  1008. else
  1009. {
  1010. ++dot;
  1011. this->Left(dot - this->Begin());
  1012. IFW32FALSE_EXIT(this->Win32Append(newExtension, cchExtension));
  1013. }
  1014. fSuccess = TRUE;
  1015. Exit:
  1016. return fSuccess;
  1017. }
  1018. BOOL Win32CopyStringOut(LPWSTR sz, ULONG *pcch)
  1019. {
  1020. FN_PROLOG_WIN32
  1021. this->IntegrityCheck();
  1022. SIZE_T cwchRequired;
  1023. PARAMETER_CHECK(pcch != NULL);
  1024. IFW32FALSE_EXIT(TCharTraits::Win32DetermineRequiredCharacters(m_prgchBuffer, m_cch, cwchRequired));
  1025. if ((*pcch) < cwchRequired)
  1026. {
  1027. *pcch = static_cast<DWORD>(cwchRequired);
  1028. ORIGINATE_WIN32_FAILURE_AND_EXIT(NoRoom, ERROR_INSUFFICIENT_BUFFER);
  1029. }
  1030. IFW32FALSE_EXIT(TCharTraits::Win32CopyIntoBuffer(sz, *pcch, m_prgchBuffer, m_cch));
  1031. FN_EPILOG
  1032. }
  1033. //
  1034. // This function is rather special purpose in that several design choices are not
  1035. // implemented as parameters. In particular, the pcbBytesWritten is assumed to
  1036. // accumulate a number (thus it's updated by adding the number of bytes written to
  1037. // it rather than just setting it to the count of bytes written).
  1038. //
  1039. // It also writes 0 bytes into the buffer is the string is zero length; if the string
  1040. // is not zero length, it writes the string including a trailing null.
  1041. //
  1042. inline BOOL Win32CopyIntoBuffer(
  1043. PWSTR *ppszCursor,
  1044. SIZE_T *pcbBuffer,
  1045. SIZE_T *pcbBytesWritten,
  1046. PVOID pvBase,
  1047. ULONG *pulOffset,
  1048. ULONG *pulLength
  1049. ) const
  1050. {
  1051. this->IntegrityCheck();
  1052. BOOL fSuccess = FALSE;
  1053. FN_TRACE_WIN32(fSuccess);
  1054. PWSTR pszCursor;
  1055. SSIZE_T dptr;
  1056. SIZE_T cbRequired;
  1057. SIZE_T cch;
  1058. if (pulOffset != NULL)
  1059. *pulOffset = 0;
  1060. if (pulLength != NULL)
  1061. *pulLength = 0;
  1062. PARAMETER_CHECK(pcbBuffer != NULL);
  1063. PARAMETER_CHECK(ppszCursor != NULL);
  1064. pszCursor = *ppszCursor;
  1065. dptr = ((SSIZE_T) pszCursor) - ((SSIZE_T) pvBase);
  1066. // If they're asking for an offset or length and the cursor is too far from the base,
  1067. // fail.
  1068. PARAMETER_CHECK((pulOffset == NULL) || (dptr <= ULONG_MAX));
  1069. cch = m_cch;
  1070. cbRequired = (cch != 0) ? ((cch + 1) * sizeof(WCHAR)) : 0;
  1071. if ((*pcbBuffer) < cbRequired)
  1072. {
  1073. ::FusionpSetLastWin32Error(ERROR_INSUFFICIENT_BUFFER);
  1074. goto Exit;
  1075. }
  1076. if (cbRequired > ULONG_MAX)
  1077. {
  1078. ::FusionpSetLastWin32Error(ERROR_INSUFFICIENT_BUFFER);
  1079. goto Exit;
  1080. }
  1081. memcpy(pszCursor, static_cast<PCWSTR>(*this), cbRequired);
  1082. if (pulOffset != NULL)
  1083. {
  1084. if (cbRequired != 0)
  1085. *pulOffset = (ULONG) dptr;
  1086. }
  1087. if (pulLength != NULL)
  1088. {
  1089. if (cbRequired == 0)
  1090. *pulLength = 0;
  1091. else
  1092. {
  1093. *pulLength = (ULONG) (cbRequired - sizeof(WCHAR));
  1094. }
  1095. }
  1096. *pcbBytesWritten += cbRequired;
  1097. *pcbBuffer -= cbRequired;
  1098. *ppszCursor = (PWSTR) (((ULONG_PTR) pszCursor) + cbRequired);
  1099. fSuccess = TRUE;
  1100. Exit:
  1101. return fSuccess;
  1102. }
  1103. protected:
  1104. TMutableString Begin()
  1105. {
  1106. this->IntegrityCheck();
  1107. /* CopyBeforeWrite() */
  1108. return m_prgchBuffer;
  1109. }
  1110. TMutableString End()
  1111. {
  1112. this->IntegrityCheck();
  1113. return &m_prgchBuffer[m_cch];
  1114. }
  1115. LONG m_cAttachedAccessors;
  1116. TChar *m_prgchBuffer;
  1117. SIZE_T m_cchBuffer;
  1118. SIZE_T m_cch; // current length of string
  1119. };
  1120. template <typename TCharTraits> class CGenericStringBufferAccessor
  1121. {
  1122. public:
  1123. typedef CGenericBaseStringBuffer<TCharTraits> TBuffer;
  1124. typedef CGenericBaseStringBuffer<TCharTraits>::TChar TChar;
  1125. CGenericStringBufferAccessor(TBuffer* pBuffer = NULL)
  1126. : m_pBuffer(NULL),
  1127. m_pszBuffer(NULL),
  1128. m_cchBuffer(NULL)
  1129. {
  1130. if (pBuffer != NULL)
  1131. {
  1132. Attach(pBuffer);
  1133. }
  1134. }
  1135. ~CGenericStringBufferAccessor()
  1136. {
  1137. if (m_pBuffer != NULL)
  1138. {
  1139. m_pBuffer->m_cch = TCharTraits::NullTerminatedStringLength(m_pszBuffer);
  1140. m_pBuffer->DetachAccessor(this);
  1141. m_pBuffer = NULL;
  1142. m_pszBuffer = NULL;
  1143. m_cchBuffer = 0;
  1144. }
  1145. }
  1146. bool IsAttached() const
  1147. {
  1148. return (m_pBuffer != NULL);
  1149. }
  1150. static TChar NullCharacter() { return TCharTraits::NullCharacter(); }
  1151. void Attach(TBuffer *pBuffer)
  1152. {
  1153. FN_TRACE();
  1154. ASSERT(!IsAttached());
  1155. if (!IsAttached())
  1156. {
  1157. pBuffer->AttachAccessor(this);
  1158. m_pBuffer = pBuffer;
  1159. m_pszBuffer = m_pBuffer->m_prgchBuffer;
  1160. m_cchBuffer = m_pBuffer->m_cchBuffer;
  1161. }
  1162. }
  1163. void Detach()
  1164. {
  1165. FN_TRACE();
  1166. ASSERT (IsAttached());
  1167. if (IsAttached())
  1168. {
  1169. ASSERT(m_pszBuffer == m_pBuffer->m_prgchBuffer);
  1170. m_pBuffer->m_cch = TCharTraits::NullTerminatedStringLength(m_pszBuffer);
  1171. m_pBuffer->DetachAccessor(this);
  1172. m_pBuffer = NULL;
  1173. m_pszBuffer = NULL;
  1174. m_cchBuffer = 0;
  1175. }
  1176. else
  1177. {
  1178. ASSERT(m_pszBuffer == NULL);
  1179. ASSERT(m_cchBuffer == 0);
  1180. }
  1181. }
  1182. operator TCharTraits::TMutableString() const { ASSERT_NTC(this->IsAttached()); return m_pszBuffer; }
  1183. SIZE_T Cch() const { ASSERT_NTC(this->IsAttached()); return (m_pszBuffer != NULL) ? ::wcslen(m_pszBuffer) : 0; }
  1184. TCharTraits::TMutableString GetBufferPtr() const { ASSERT_NTC(IsAttached()); return m_pszBuffer; }
  1185. SIZE_T GetBufferCch() const { ASSERT_NTC(this->IsAttached()); return m_cchBuffer; }
  1186. INT GetBufferCchAsINT() const { ASSERT_NTC(this->IsAttached()); if (m_cchBuffer > INT_MAX) return INT_MAX; return static_cast<INT>(m_cchBuffer); }
  1187. UINT GetBufferCchAsUINT() const { ASSERT_NTC(this->IsAttached()); if (m_cchBuffer > UINT_MAX) return UINT_MAX; return static_cast<UINT>(m_cchBuffer); }
  1188. DWORD GetBufferCchAsDWORD() const { ASSERT_NTC(this->IsAttached()); if (m_cchBuffer > MAXDWORD) return MAXDWORD; return static_cast<DWORD>(m_cchBuffer); }
  1189. SIZE_T GetBufferCb() const { ASSERT_NTC(this->IsAttached()); return m_cchBuffer * sizeof(*m_pszBuffer); }
  1190. INT GetBufferCbAsINT() const { ASSERT_NTC(this->IsAttached()); if ((m_cchBuffer * sizeof(TChar)) > INT_MAX) return INT_MAX; return static_cast<INT>(m_cchBuffer * sizeof(TChar)); }
  1191. DWORD GetBufferCbAsDWORD() const { ASSERT_NTC(this->IsAttached()); if ((m_cchBuffer * sizeof(TChar)) > MAXDWORD) return MAXDWORD; return static_cast<DWORD>(m_cchBuffer * sizeof(TChar)); }
  1192. protected:
  1193. TBuffer *m_pBuffer;
  1194. TCharTraits::TMutableString m_pszBuffer;
  1195. SIZE_T m_cchBuffer;
  1196. };
  1197. template <SIZE_T nInlineChars, typename TCharTraits> class CGenericStringBuffer : public CGenericBaseStringBuffer<TCharTraits>
  1198. {
  1199. typedef CGenericBaseStringBuffer<TCharTraits> Base;
  1200. protected:
  1201. BOOL Win32AllocateBuffer(SIZE_T cch, TMutableString &rpsz) const
  1202. {
  1203. // You shouldn't be doing this if the required buffer size is small enough to be inline...
  1204. ASSERT_NTC(cch > nInlineChars);
  1205. rpsz = NULL;
  1206. TCharTraits::TMutableString String = NULL;
  1207. String = reinterpret_cast<TCharTraits::TMutableString>(::FusionpHeapAllocEx(
  1208. FUSION_DEFAULT_PROCESS_HEAP(),
  1209. 0,
  1210. cch * sizeof(TCharTraits::TChar),
  1211. "<string buffer>",
  1212. __FILE__,
  1213. __LINE__,
  1214. 0)); // fusion heap allocation flags
  1215. if (String == NULL)
  1216. {
  1217. ::FusionpSetLastWin32Error(FUSION_WIN32_ALLOCFAILED_ERROR);
  1218. return FALSE;
  1219. }
  1220. rpsz = String;
  1221. return TRUE;
  1222. }
  1223. VOID DeallocateBuffer(TMutableString sz) const
  1224. {
  1225. VERIFY_NTC(::FusionpHeapFree(FUSION_DEFAULT_PROCESS_HEAP(), 0, sz));
  1226. }
  1227. TMutableString GetInlineBuffer() const { return const_cast<TMutableString>(m_rgchInlineBuffer); }
  1228. SIZE_T GetInlineBufferCch() const { return nInlineChars; }
  1229. public:
  1230. CGenericStringBuffer() { m_rgchInlineBuffer[0] = this->NullCharacter(); Base::InitializeInlineBuffer(); }
  1231. ~CGenericStringBuffer() { if (m_prgchBuffer != m_rgchInlineBuffer) { this->DeallocateBuffer(m_prgchBuffer); } m_prgchBuffer = NULL; m_cchBuffer = 0; }
  1232. protected:
  1233. TChar m_rgchInlineBuffer[nInlineChars];
  1234. private:
  1235. CGenericStringBuffer(const CGenericStringBuffer &); // intentionally not implemented
  1236. void operator =(const CGenericStringBuffer &); // intentionally not implemented
  1237. };
  1238. template <SIZE_T nInlineChars, typename TCharTraits> class CGenericHeapStringBuffer : public CGenericBaseStringBuffer<TCharTraits>
  1239. {
  1240. // friend CGenericBaseStringBuffer<TCharTraits>;
  1241. typedef CGenericBaseStringBuffer<TCharTraits> Base;
  1242. protected:
  1243. BOOL Win32AllocateBuffer(SIZE_T cch, TMutableString &rpsz) const
  1244. {
  1245. // You shouldn't be doing this if the required buffer size is small enough to be inline...
  1246. ASSERT_NTC(cch > nInlineChars);
  1247. rpsz = NULL;
  1248. TCharTraits::TMutableString String = NULL;
  1249. String = reinterpret_cast<TCharTraits::TMutableString>(::FusionpHeapAllocEx(
  1250. m_hHeap,
  1251. dwDefaultWin32HeapAllocFlags,
  1252. cch * sizeof(TCharTraits::TChar),
  1253. "<string buffer>",
  1254. __FILE__,
  1255. __LINE__,
  1256. 0)) // fusion heap allocation flags
  1257. if (String == NULL)
  1258. {
  1259. ::FusionpSetLastWin32Error(FUSION_WIN32_ALLOCFAILED_ERROR);
  1260. return FALSE;
  1261. }
  1262. rpsz = String;
  1263. return TRUE;
  1264. }
  1265. VOID DeallocateBuffer(TMutableString sz) const
  1266. {
  1267. VERIFY_NTC(::FusionpHeapFree(m_hHeap, dwDefaultWin32HeapFreeFlags, sz));
  1268. }
  1269. TMutableString GetInlineBuffer() const { return m_rgchInlineBuffer; }
  1270. SIZE_T GetInlineBufferCch() const { return nInlineChars; }
  1271. public:
  1272. CGenericHeapStringBuffer(HANDLE hHeap) : m_hHeap(hHeap) { m_rgchInlineBuffer[0] = this->NullCharacter(); Base::InitializeInlineBuffer(); }
  1273. ~CGenericHeapStringBuffer() { ASSERT(m_cchBuffer == 0); ASSERT(m_prgchBuffer == NULL); }
  1274. protected:
  1275. HANDLE m_hHeap;
  1276. TChar m_rgchInlineBuffer[nInlineChars];
  1277. };
  1278. typedef CGenericStringBufferAccessor<CUnicodeCharTraits> CUnicodeStringBufferAccessor;
  1279. typedef CGenericBaseStringBuffer<CUnicodeCharTraits> CUnicodeBaseStringBuffer;
  1280. typedef CGenericStringBuffer<FUSION_DEFAULT_STRINGBUFFER_CHARS, CUnicodeCharTraits> CUnicodeStringBuffer;
  1281. typedef CGenericHeapStringBuffer<FUSION_DEFAULT_STRINGBUFFER_CHARS, CUnicodeCharTraits> CUnicodeHeapStringBuffer;
  1282. typedef CGenericStringBuffer<FUSION_DEFAULT_TINY_STRINGBUFFER_CHARS, CUnicodeCharTraits> CTinyUnicodeStringBuffer;
  1283. typedef CGenericHeapStringBuffer<FUSION_DEFAULT_TINY_STRINGBUFFER_CHARS, CUnicodeCharTraits> CTinyUnicodeHeapStringBuffer;
  1284. typedef CGenericStringBuffer<FUSION_DEFAULT_SMALL_STRINGBUFFER_CHARS, CUnicodeCharTraits> CSmallUnicodeStringBuffer;
  1285. typedef CGenericHeapStringBuffer<FUSION_DEFAULT_SMALL_STRINGBUFFER_CHARS, CUnicodeCharTraits> CSmallUnicodeHeapStringBuffer;
  1286. typedef CGenericStringBuffer<FUSION_DEFAULT_MEDIUM_STRINGBUFFER_CHARS, CUnicodeCharTraits> CMediumUnicodeStringBuffer;
  1287. typedef CGenericHeapStringBuffer<FUSION_DEFAULT_MEDIUM_STRINGBUFFER_CHARS, CUnicodeCharTraits> CMediumUnicodeHeapStringBuffer;
  1288. typedef CGenericStringBufferAccessor<CANSICharTraits> CANSIStringBufferAccessor;
  1289. typedef CGenericBaseStringBuffer<CANSICharTraits> CANSIBaseStringBuffer;
  1290. typedef CGenericStringBuffer<FUSION_DEFAULT_STRINGBUFFER_CHARS, CANSICharTraits> CANSIStringBuffer;
  1291. typedef CGenericHeapStringBuffer<FUSION_DEFAULT_STRINGBUFFER_CHARS, CANSICharTraits> CANSIHeapStringBuffer;
  1292. typedef CGenericStringBuffer<FUSION_DEFAULT_TINY_STRINGBUFFER_CHARS, CANSICharTraits> CTinyANSIStringBuffer;
  1293. typedef CGenericHeapStringBuffer<FUSION_DEFAULT_TINY_STRINGBUFFER_CHARS, CANSICharTraits> CTinyANSIHeapStringBuffer;
  1294. typedef CGenericStringBuffer<FUSION_DEFAULT_SMALL_STRINGBUFFER_CHARS, CANSICharTraits> CSmallANSIStringBuffer;
  1295. typedef CGenericHeapStringBuffer<FUSION_DEFAULT_SMALL_STRINGBUFFER_CHARS, CANSICharTraits> CSmallANSIHeapStringBuffer;
  1296. typedef CGenericStringBuffer<FUSION_DEFAULT_MEDIUM_STRINGBUFFER_CHARS, CANSICharTraits> CMediumANSIStringBuffer;
  1297. typedef CGenericHeapStringBuffer<FUSION_DEFAULT_MEDIUM_STRINGBUFFER_CHARS, CANSICharTraits> CMediumANSIHeapStringBuffer;
  1298. typedef CUnicodeBaseStringBuffer CBaseStringBuffer;
  1299. typedef CUnicodeStringBuffer CStringBuffer;
  1300. typedef CUnicodeHeapStringBuffer CHeapStringBuffer;
  1301. typedef CUnicodeStringBufferAccessor CStringBufferAccessor;
  1302. typedef CTinyUnicodeStringBuffer CTinyStringBuffer;
  1303. typedef CTinyUnicodeHeapStringBuffer CTinyHeapStringBuffer;
  1304. typedef CSmallUnicodeStringBuffer CSmallStringBuffer;
  1305. typedef CSmallUnicodeHeapStringBuffer CSmallHeapStringBuffer;
  1306. typedef CMediumUnicodeStringBuffer CMediumStringBuffer;
  1307. typedef CMediumUnicodeHeapStringBuffer CMediumHeapStringBuffer;
  1308. template <typename T1, typename T2> inline HRESULT HashTableCompareKey(T1 t1, T2 *pt2, bool &rfMatch);
  1309. template <> inline HRESULT HashTableCompareKey(PCWSTR sz, CUnicodeStringBuffer *pbuff, bool &rfMatch)
  1310. {
  1311. HRESULT hr = NOERROR;
  1312. SIZE_T cchKey = (sz != NULL) ? ::wcslen(sz) : 0;
  1313. rfMatch = false;
  1314. if (!pbuff->Win32Equals(sz, cchKey, rfMatch, false))
  1315. {
  1316. hr = HRESULT_FROM_WIN32(::FusionpGetLastWin32Error());
  1317. goto Exit;
  1318. }
  1319. hr = NOERROR;
  1320. Exit:
  1321. return hr;
  1322. }
  1323. template <> inline HRESULT HashTableCompareKey(PCSTR sz, CANSIStringBuffer *pbuff, bool &rfMatch)
  1324. {
  1325. HRESULT hr = NOERROR;
  1326. SIZE_T cchKey = ::strlen(sz);
  1327. rfMatch = false;
  1328. if (!pbuff->Win32Equals(sz, cchKey, rfMatch, false))
  1329. {
  1330. hr = HRESULT_FROM_WIN32(::FusionpGetLastWin32Error());
  1331. goto Exit;
  1332. }
  1333. hr = NOERROR;
  1334. Exit:
  1335. return hr;
  1336. }
  1337. //
  1338. // Support for CFusionArrays of strings
  1339. //
  1340. inline HRESULT
  1341. FusionCopyContents<CBaseStringBuffer>(
  1342. CBaseStringBuffer &Dest,
  1343. const CBaseStringBuffer &Source
  1344. )
  1345. {
  1346. HRESULT hr = NOERROR;
  1347. if (!Dest.Win32Assign(Source, Source.Cch()))
  1348. hr = HRESULT_FROM_WIN32(::FusionpGetLastWin32Error());
  1349. return hr;
  1350. }
  1351. template<>
  1352. inline BOOL
  1353. FusionWin32CopyContents<CStringBuffer>(
  1354. CStringBuffer &rDestination,
  1355. const CStringBuffer &rSource
  1356. )
  1357. {
  1358. return rDestination.Win32Assign(rSource);
  1359. }
  1360. template<>
  1361. inline HRESULT
  1362. FusionCopyContents<CStringBuffer>(
  1363. CStringBuffer &rDest,
  1364. const CStringBuffer &rSource
  1365. )
  1366. {
  1367. FN_PROLOG_HR
  1368. IFW32FALSE_EXIT(::FusionWin32CopyContents<CStringBuffer>(rDest, rSource));
  1369. FN_EPILOG
  1370. }
  1371. template<>
  1372. inline void
  1373. FusionMoveContents<CStringBuffer>(
  1374. CStringBuffer &rDest,
  1375. CStringBuffer &rSource
  1376. )
  1377. {
  1378. FN_TRACE();
  1379. HARD_ASSERT2_ACTION(FusionMoveContents, "FusionMoveContents for CAssemblyRecoveryInfo isn't allowed.");
  1380. }
  1381. #endif