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.

680 lines
16 KiB

  1. // CSTRING.CPP
  2. //
  3. // Based on the original MFC source file.
  4. //
  5. // This is a part of the Microsoft Foundation Classes C++ library.
  6. // Copyright (C) 1992-1995 Microsoft Corporation
  7. // All rights reserved.
  8. //
  9. // This source code is only intended as a supplement to the
  10. // Microsoft Foundation Classes Reference and related
  11. // electronic documentation provided with the library.
  12. // See these sources for detailed information regarding the
  13. // Microsoft Foundation Classes product.
  14. #include "precomp.h"
  15. #include <cstring.hpp>
  16. #ifdef AFX_CORE1_SEG
  17. #pragma code_seg(AFX_CORE1_SEG)
  18. #endif
  19. #ifdef _DEBUG
  20. #undef THIS_FILE
  21. static char THIS_FILE[] = __FILE__;
  22. #endif
  23. /////////////////////////////////////////////////////////////////////////////
  24. // static class data, special inlines
  25. // afxChNil is left for backward compatibility
  26. REMAFX_DATADEF TCHAR AFXChNil = '\0';
  27. // For an empty string, m_pchData will point here
  28. // (note: avoids special case of checking for NULL m_pchData)
  29. // empty string data (and locked)
  30. static int RGInitData[] = { -1, 0, 0, 0 };
  31. static REMAFX_DATADEF CSTRINGData* AFXDataNil = (CSTRINGData*)&RGInitData;
  32. static LPCTSTR AFXPchNil = (LPCTSTR)(((BYTE*)&RGInitData)+sizeof(CSTRINGData));
  33. // special function to make AFXEmptyString work even during initialization
  34. const CSTRING& REMAFXAPI AFXGetEmptyString()
  35. { return *(CSTRING*)&AFXPchNil; }
  36. //////////////////////////////////////////////////////////////////////////////
  37. // Construction/Destruction
  38. CSTRING::CSTRING()
  39. {
  40. Init();
  41. }
  42. CSTRING::CSTRING(const CSTRING& stringSrc)
  43. {
  44. ASSERT(stringSrc.GetData()->nRefs != 0);
  45. if (stringSrc.GetData()->nRefs >= 0)
  46. {
  47. ASSERT(stringSrc.GetData() != AFXDataNil);
  48. m_pchData = stringSrc.m_pchData;
  49. InterlockedIncrement(&GetData()->nRefs);
  50. }
  51. else
  52. {
  53. Init();
  54. *this = stringSrc.m_pchData;
  55. }
  56. }
  57. CSTRING::CSTRING(LPCTSTR lpch, int nLength)
  58. {
  59. Init();
  60. if (nLength != 0)
  61. {
  62. // ASSERT(AfxIsValidAddress(lpch, nLength, FALSE));
  63. AllocBuffer(nLength);
  64. memcpy(m_pchData, lpch, nLength*sizeof(TCHAR));
  65. }
  66. }
  67. void CSTRING::AllocBuffer(int nLen)
  68. // always allocate one extra character for '\0' termination
  69. // assumes [optimistically] that data length will equal allocation length
  70. {
  71. ASSERT(nLen >= 0);
  72. ASSERT(nLen <= INT_MAX-1); // max size (enough room for 1 extra)
  73. if (nLen == 0)
  74. Init();
  75. else
  76. {
  77. CSTRINGData* pData =
  78. (CSTRINGData*)new BYTE[sizeof(CSTRINGData) + (nLen+1)*sizeof(TCHAR)];
  79. pData->nRefs = 1;
  80. pData->data()[nLen] = '\0';
  81. pData->nDataLength = nLen;
  82. pData->nAllocLength = nLen;
  83. m_pchData = pData->data();
  84. }
  85. }
  86. void CSTRING::Release()
  87. {
  88. if (GetData() != AFXDataNil)
  89. {
  90. ASSERT(GetData()->nRefs != 0);
  91. if (InterlockedDecrement(&GetData()->nRefs) <= 0)
  92. delete[] (BYTE*)GetData();
  93. Init();
  94. }
  95. }
  96. void PASCAL CSTRING::Release(CSTRINGData* pData)
  97. {
  98. if (pData != AFXDataNil)
  99. {
  100. ASSERT(pData->nRefs != 0);
  101. if (InterlockedDecrement(&pData->nRefs) <= 0)
  102. delete[] (BYTE*)pData;
  103. }
  104. }
  105. void CSTRING::Empty()
  106. {
  107. if (GetData()->nRefs >= 0)
  108. Release();
  109. else
  110. *this = &AFXChNil;
  111. ASSERT(GetData()->nDataLength == 0);
  112. ASSERT(GetData()->nRefs < 0 || GetData()->nAllocLength == 0);
  113. }
  114. void CSTRING::CopyBeforeWrite()
  115. {
  116. if (GetData()->nRefs > 1)
  117. {
  118. CSTRINGData* pData = GetData();
  119. Release();
  120. AllocBuffer(pData->nDataLength);
  121. memcpy(m_pchData, pData->data(), (pData->nDataLength+1)*sizeof(TCHAR));
  122. }
  123. ASSERT(GetData()->nRefs <= 1);
  124. }
  125. void CSTRING::AllocBeforeWrite(int nLen)
  126. {
  127. if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength)
  128. {
  129. Release();
  130. AllocBuffer(nLen);
  131. }
  132. ASSERT(GetData()->nRefs <= 1);
  133. }
  134. CSTRING::~CSTRING()
  135. // free any attached data
  136. {
  137. if (GetData() != AFXDataNil)
  138. {
  139. if (InterlockedDecrement(&GetData()->nRefs) <= 0)
  140. delete[] (BYTE*)GetData();
  141. }
  142. }
  143. //////////////////////////////////////////////////////////////////////////////
  144. // Helpers for the rest of the implementation
  145. void CSTRING::AllocCopy(CSTRING& dest, int nCopyLen, int nCopyIndex,
  146. int nExtraLen) const
  147. {
  148. // will clone the data attached to this string
  149. // allocating 'nExtraLen' characters
  150. // Places results in uninitialized string 'dest'
  151. // Will copy the part or all of original data to start of new string
  152. int nNewLen = nCopyLen + nExtraLen;
  153. if (nNewLen == 0)
  154. {
  155. dest.Init();
  156. }
  157. else
  158. {
  159. dest.AllocBuffer(nNewLen);
  160. memcpy(dest.m_pchData, m_pchData+nCopyIndex, nCopyLen*sizeof(TCHAR));
  161. }
  162. }
  163. //////////////////////////////////////////////////////////////////////////////
  164. // More sophisticated construction
  165. CSTRING::CSTRING(LPCTSTR lpsz)
  166. {
  167. Init();
  168. // if (lpsz != NULL && HIWORD(lpsz) == NULL)
  169. // {
  170. // UINT nID = LOWORD((DWORD)lpsz);
  171. // if (!LoadString(nID)) {
  172. // ;// TRACE1("Warning: implicit LoadString(%u) failed\n", nID);
  173. // }
  174. // }
  175. // else
  176. {
  177. int nLen = SafeStrlen(lpsz);
  178. if (nLen != 0)
  179. {
  180. AllocBuffer(nLen);
  181. memcpy(m_pchData, lpsz, nLen*sizeof(TCHAR));
  182. }
  183. }
  184. }
  185. /////////////////////////////////////////////////////////////////////////////
  186. // Special conversion constructors
  187. #ifdef _UNICODE
  188. CSTRING::CSTRING(LPCSTR lpsz)
  189. {
  190. Init();
  191. int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
  192. if (nSrcLen != 0)
  193. {
  194. AllocBuffer(nSrcLen);
  195. _mbstowcsz(m_pchData, lpsz, nSrcLen+1);
  196. ReleaseBuffer();
  197. }
  198. }
  199. #else //_UNICODE
  200. CSTRING::CSTRING(LPCWSTR lpsz)
  201. {
  202. Init();
  203. int nSrcLen = lpsz != NULL ? LStrLenW(lpsz) : 0;
  204. if (nSrcLen != 0)
  205. {
  206. AllocBuffer(nSrcLen*2);
  207. _wcstombsz(m_pchData, lpsz, (nSrcLen*2)+1);
  208. ReleaseBuffer();
  209. }
  210. }
  211. #endif //!_UNICODE
  212. //////////////////////////////////////////////////////////////////////////////
  213. // Diagnostic support
  214. //#ifdef _DEBUG
  215. //CDumpContext& REMAFXAPI operator<<(CDumpContext& dc, const CSTRING& string)
  216. //{
  217. // dc << string.m_pchData;
  218. // return dc;
  219. //}
  220. //#endif //_DEBUG
  221. //////////////////////////////////////////////////////////////////////////////
  222. // Assignment operators
  223. // All assign a new value to the string
  224. // (a) first see if the buffer is big enough
  225. // (b) if enough room, copy on top of old buffer, set size and type
  226. // (c) otherwise free old string data, and create a new one
  227. //
  228. // All routines return the new string (but as a 'const CSTRING&') so that
  229. // assigning it again will cause a copy, eg: s1 = s2 = "hi there".
  230. //
  231. void CSTRING::AssignCopy(int nSrcLen, LPCTSTR lpszSrcData)
  232. {
  233. AllocBeforeWrite(nSrcLen);
  234. memcpy(m_pchData, lpszSrcData, nSrcLen*sizeof(TCHAR));
  235. GetData()->nDataLength = nSrcLen;
  236. m_pchData[nSrcLen] = '\0';
  237. }
  238. const CSTRING& CSTRING::operator=(const CSTRING& stringSrc)
  239. {
  240. if (m_pchData != stringSrc.m_pchData)
  241. {
  242. if ((GetData()->nRefs < 0 && GetData() != AFXDataNil) ||
  243. stringSrc.GetData()->nRefs < 0)
  244. {
  245. // actual copy necessary since one of the strings is locked
  246. AssignCopy(stringSrc.GetData()->nDataLength, stringSrc.m_pchData);
  247. }
  248. else
  249. {
  250. // can just copy references around
  251. Release();
  252. ASSERT(stringSrc.GetData() != AFXDataNil);
  253. m_pchData = stringSrc.m_pchData;
  254. InterlockedIncrement(&GetData()->nRefs);
  255. }
  256. }
  257. return *this;
  258. }
  259. const CSTRING& CSTRING::operator=(LPCTSTR lpsz)
  260. {
  261. //lts ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE));
  262. AssignCopy(SafeStrlen(lpsz), lpsz);
  263. return *this;
  264. }
  265. /////////////////////////////////////////////////////////////////////////////
  266. // Special conversion assignment
  267. #ifdef _UNICODE
  268. const CSTRING& CSTRING::operator=(LPCSTR lpsz)
  269. {
  270. int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
  271. AllocBeforeWrite(nSrcLen);
  272. _mbstowcsz(m_pchData, lpsz, nSrcLen+1);
  273. ReleaseBuffer();
  274. return *this;
  275. }
  276. #else //!_UNICODE
  277. const CSTRING& CSTRING::operator=(LPCWSTR lpsz)
  278. {
  279. int nSrcLen = lpsz != NULL ? LStrLenW(lpsz) : 0;
  280. AllocBeforeWrite(nSrcLen*2);
  281. _wcstombsz(m_pchData, lpsz, (nSrcLen*2)+1);
  282. ReleaseBuffer();
  283. return *this;
  284. }
  285. #endif //!_UNICODE
  286. //////////////////////////////////////////////////////////////////////////////
  287. // concatenation
  288. // NOTE: "operator+" is done as friend functions for simplicity
  289. // There are three variants:
  290. // CSTRING + CSTRING
  291. // and for ? = TCHAR, LPCTSTR
  292. // CSTRING + ?
  293. // ? + CSTRING
  294. void CSTRING::ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data,
  295. int nSrc2Len, LPCTSTR lpszSrc2Data)
  296. {
  297. // -- master concatenation routine
  298. // Concatenate two sources
  299. // -- assume that 'this' is a new CSTRING object
  300. int nNewLen = nSrc1Len + nSrc2Len;
  301. if (nNewLen != 0)
  302. {
  303. AllocBuffer(nNewLen);
  304. memcpy(m_pchData, lpszSrc1Data, nSrc1Len*sizeof(TCHAR));
  305. memcpy(m_pchData+nSrc1Len, lpszSrc2Data, nSrc2Len*sizeof(TCHAR));
  306. }
  307. }
  308. CSTRING REMAFXAPI operator+(const CSTRING& string1, const CSTRING& string2)
  309. {
  310. CSTRING s;
  311. s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData,
  312. string2.GetData()->nDataLength, string2.m_pchData);
  313. return s;
  314. }
  315. CSTRING REMAFXAPI operator+(const CSTRING& string, LPCTSTR lpsz)
  316. {
  317. // ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE));
  318. CSTRING s;
  319. s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData,
  320. CSTRING::SafeStrlen(lpsz), lpsz);
  321. return s;
  322. }
  323. CSTRING REMAFXAPI operator+(LPCTSTR lpsz, const CSTRING& string)
  324. {
  325. // ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE));
  326. CSTRING s;
  327. s.ConcatCopy(CSTRING::SafeStrlen(lpsz), lpsz, string.GetData()->nDataLength,
  328. string.m_pchData);
  329. return s;
  330. }
  331. //////////////////////////////////////////////////////////////////////////////
  332. // concatenate in place
  333. void CSTRING::ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData)
  334. {
  335. // -- the main routine for += operators
  336. // if the buffer is too small, or we have a width mis-match, just
  337. // allocate a new buffer (slow but sure)
  338. if (GetData()->nRefs > 1 || GetData()->nDataLength + nSrcLen > GetData()->nAllocLength)
  339. {
  340. // we have to grow the buffer, use the ConcatCopy routine
  341. CSTRINGData* pOldData = GetData();
  342. ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData);
  343. ASSERT(pOldData != NULL);
  344. CSTRING::Release(pOldData);
  345. }
  346. else
  347. {
  348. // fast concatenation when buffer big enough
  349. memcpy(m_pchData+GetData()->nDataLength, lpszSrcData, nSrcLen*sizeof(TCHAR));
  350. GetData()->nDataLength += nSrcLen;
  351. ASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
  352. m_pchData[GetData()->nDataLength] = '\0';
  353. }
  354. }
  355. const CSTRING& CSTRING::operator+=(LPCTSTR lpsz)
  356. {
  357. // ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE));
  358. ConcatInPlace(SafeStrlen(lpsz), lpsz);
  359. return *this;
  360. }
  361. const CSTRING& CSTRING::operator+=(TCHAR ch)
  362. {
  363. ConcatInPlace(1, &ch);
  364. return *this;
  365. }
  366. const CSTRING& CSTRING::operator+=(const CSTRING& string)
  367. {
  368. ConcatInPlace(string.GetData()->nDataLength, string.m_pchData);
  369. return *this;
  370. }
  371. /*
  372. * Length-sensitive comparison
  373. *
  374. * NOTE: FEqual returns TRUE if the 2 CSTRINGS have the same length and contain
  375. * the same characters, and FALSE, otherwise.
  376. */
  377. BOOL CSTRING::FEqual (const CSTRING &s2) const
  378. {
  379. int length;
  380. // Compare the lengths first
  381. length = GetData()->nDataLength;
  382. if (length != s2.GetData()->nDataLength)
  383. return FALSE;
  384. #ifdef _UNICODE
  385. // adjust the length in bytes
  386. length *= sizeof (TCHAR);
  387. #endif
  388. /*
  389. * Now, compare the strings themselves
  390. * We use memcmp and not lstrcmp because the stings may
  391. * have embedded null characters.
  392. */
  393. if (memcmp ((const void *) m_pchData, (const void *) s2.m_pchData, length))
  394. return FALSE;
  395. else
  396. return TRUE;
  397. }
  398. ///////////////////////////////////////////////////////////////////////////////
  399. // Advanced direct buffer access
  400. LPTSTR CSTRING::GetBuffer(int nMinBufLength)
  401. {
  402. ASSERT(nMinBufLength >= 0);
  403. if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength)
  404. {
  405. // we have to grow the buffer
  406. CSTRINGData* pOldData = GetData();
  407. int nOldLen = GetData()->nDataLength; // AllocBuffer will tromp it
  408. if (nMinBufLength < nOldLen)
  409. nMinBufLength = nOldLen;
  410. AllocBuffer(nMinBufLength);
  411. memcpy(m_pchData, pOldData->data(), (nOldLen+1)*sizeof(TCHAR));
  412. GetData()->nDataLength = nOldLen;
  413. CSTRING::Release(pOldData);
  414. }
  415. ASSERT(GetData()->nRefs <= 1);
  416. // return a pointer to the character storage for this string
  417. ASSERT(m_pchData != NULL);
  418. return m_pchData;
  419. }
  420. void CSTRING::ReleaseBuffer(int nNewLength)
  421. {
  422. CopyBeforeWrite(); // just in case GetBuffer was not called
  423. if (nNewLength == -1)
  424. nNewLength = lstrlen(m_pchData); // zero terminated
  425. ASSERT(nNewLength <= GetData()->nAllocLength);
  426. GetData()->nDataLength = nNewLength;
  427. m_pchData[nNewLength] = '\0';
  428. }
  429. LPTSTR CSTRING::GetBufferSetLength(int nNewLength)
  430. {
  431. ASSERT(nNewLength >= 0);
  432. GetBuffer(nNewLength);
  433. GetData()->nDataLength = nNewLength;
  434. m_pchData[nNewLength] = '\0';
  435. return m_pchData;
  436. }
  437. void CSTRING::FreeExtra()
  438. {
  439. ASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
  440. if (GetData()->nDataLength != GetData()->nAllocLength)
  441. {
  442. CSTRINGData* pOldData = GetData();
  443. AllocBuffer(GetData()->nDataLength);
  444. memcpy(m_pchData, pOldData->data(), pOldData->nDataLength*sizeof(TCHAR));
  445. ASSERT(m_pchData[GetData()->nDataLength] == '\0');
  446. CSTRING::Release(pOldData);
  447. }
  448. ASSERT(GetData() != NULL);
  449. }
  450. LPTSTR CSTRING::LockBuffer()
  451. {
  452. LPTSTR lpsz = GetBuffer(0);
  453. GetData()->nRefs = -1;
  454. return lpsz;
  455. }
  456. void CSTRING::UnlockBuffer()
  457. {
  458. ASSERT(GetData()->nRefs == -1);
  459. if (GetData() != AFXDataNil)
  460. GetData()->nRefs = 1;
  461. }
  462. ///////////////////////////////////////////////////////////////////////////////
  463. // Commonly used routines (rarely used routines in STREX.CPP)
  464. // find position of the first character match (or -1 on failure)
  465. int CSTRING::Find(TCHAR ch) const
  466. {
  467. for (TCHAR * pch = m_pchData; _T('\0') != *pch; pch = CharNext(pch))
  468. {
  469. if (ch == *pch)
  470. return ((int)(pch - m_pchData) / sizeof(TCHAR));
  471. }
  472. return -1;
  473. }
  474. CSTRING CSTRING::Left(int nCount) const
  475. {
  476. if (nCount < 0)
  477. nCount = 0;
  478. else if (nCount > GetData()->nDataLength)
  479. nCount = GetData()->nDataLength;
  480. CSTRING dest;
  481. AllocCopy(dest, nCount, 0, 0);
  482. return dest;
  483. }
  484. CSTRING CSTRING::Mid(int nFirst) const
  485. {
  486. return Mid(nFirst, GetData()->nDataLength - nFirst);
  487. }
  488. CSTRING CSTRING::Mid(int nFirst, int nCount) const
  489. {
  490. // out-of-bounds requests return sensible things
  491. if (nFirst < 0)
  492. nFirst = 0;
  493. if (nCount < 0)
  494. nCount = 0;
  495. if (nFirst + nCount > GetData()->nDataLength)
  496. nCount = GetData()->nDataLength - nFirst;
  497. if (nFirst > GetData()->nDataLength)
  498. nCount = 0;
  499. CSTRING dest;
  500. AllocCopy(dest, nCount, nFirst, 0);
  501. return dest;
  502. }
  503. void CSTRING::MakeUpper()
  504. {
  505. CopyBeforeWrite();
  506. ::CharUpper(m_pchData);
  507. }
  508. void CSTRING::MakeLower()
  509. {
  510. CopyBeforeWrite();
  511. ::CharLower(m_pchData);
  512. }
  513. void CSTRING::SetAt(int nIndex, TCHAR ch)
  514. {
  515. ASSERT(nIndex >= 0);
  516. ASSERT(nIndex < GetData()->nDataLength);
  517. CopyBeforeWrite();
  518. m_pchData[nIndex] = ch;
  519. }
  520. #ifndef _UNICODE
  521. void CSTRING::AnsiToOem()
  522. {
  523. CopyBeforeWrite();
  524. ::AnsiToOem(m_pchData, m_pchData);
  525. }
  526. #endif
  527. ///////////////////////////////////////////////////////////////////////////////
  528. // CSTRING conversion helpers (these use the current system locale)
  529. int REMAFX_CDECL _wcstombsz(char* mbstr, const wchar_t* wcstr, size_t count)
  530. {
  531. if (count == 0 && mbstr != NULL)
  532. return 0;
  533. int result = ::WideCharToMultiByte(CP_ACP, 0, wcstr, -1,
  534. mbstr, count, NULL, NULL);
  535. ASSERT(mbstr == NULL || result <= (int)count);
  536. if (result > 0)
  537. mbstr[result-1] = 0;
  538. return result;
  539. }
  540. int REMAFX_CDECL _mbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count)
  541. {
  542. if (count == 0 && wcstr != NULL)
  543. return 0;
  544. int result = ::MultiByteToWideChar(CP_ACP, 0, mbstr, -1,
  545. wcstr, count);
  546. ASSERT(wcstr == NULL || result <= (int)count);
  547. if (result > 0)
  548. wcstr[result-1] = 0;
  549. return result;
  550. }
  551. LPWSTR REMAFXAPI AfxA2WHelper(LPWSTR lpw, LPCSTR lpa, int nChars)
  552. {
  553. if (lpa == NULL)
  554. return NULL;
  555. ASSERT(lpw != NULL);
  556. // verify that no illegal character present
  557. // since lpw was allocated based on the size of lpa
  558. // don't worry about the number of chars
  559. lpw[0] = '\0';
  560. //lts VERIFY(MultiByteToWideChar(CP_ACP, 0, lpa, -1, lpw, nChars));
  561. MultiByteToWideChar(CP_ACP, 0, lpa, -1, lpw, nChars);
  562. return lpw;
  563. }
  564. LPSTR REMAFXAPI AfxW2AHelper(LPSTR lpa, LPCWSTR lpw, int nChars)
  565. {
  566. if (lpw == NULL)
  567. return NULL;
  568. ASSERT(lpa != NULL);
  569. // verify that no illegal character present
  570. // since lpa was allocated based on the size of lpw
  571. // don't worry about the number of chars
  572. lpa[0] = '\0';
  573. //lts VERIFY(WideCharToMultiByte(CP_ACP, 0, lpw, -1, lpa, nChars, NULL, NULL));
  574. WideCharToMultiByte(CP_ACP, 0, lpw, -1, lpa, nChars, NULL, NULL);
  575. return lpa;
  576. }
  577. ///////////////////////////////////////////////////////////////////////////////
  578. BOOL CSTRING::LoadString(HINSTANCE hInstance, UINT nID)
  579. {
  580. // try buffer size of 256, then larger size until entire string is retrieved
  581. int nSize = -1;
  582. int nLen;
  583. do
  584. {
  585. nSize += 256;
  586. nLen = ::LoadString(hInstance, nID, GetBuffer(nSize), nSize+1);
  587. } while (nLen == nSize);
  588. ReleaseBuffer();
  589. return nLen > 0;
  590. }