1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: dll/win32/framedyn/chstring.cpp 5 * PURPOSE: CHString class implementation 6 * PROGRAMMERS: Pierre Schweitzer (pierre@reactos.org) 7 * 8 * NOTE: This implementation is BROKEN on PURPOSE 9 * The CHString is a mix between std::string and 10 * UNICODE_STRING. It appears that basically it takes only 11 * the worse from both approaches. 12 * I've copied the behavior and implementation of Windows 2k3 even if 13 * it implies unsafe, wrong or unefficient methods. 14 * Note that the string at m_pchData might not be null terminated! 15 * Also, important note, two (or even more) CHString instances might 16 * have the same m_pchData object! Never forget that while modifying 17 * a string. You might be modifying the string for everyone. 18 * This is why a protected method is being used in the code: CopyBeforeWrite 19 * It copies source first, to ensure we only modify current string 20 * Side note, all the sizes are actually a number of chars. Only the size 21 * for implementation is the number of bytes 22 * Now, you know why this class is deprecated and shouldn't be used 23 */ 24 25 /* INCLUDES ******************************************************************/ 26 27 #include <chstring.h> 28 #define NDEBUG 29 #include <debug.h> 30 31 /* PRIVATE FUNCTIONS *********************************************************/ 32 33 // This is the empty string that defaults strings without text 34 // This is unsafe. This string show be LPCWSTR 35 // However we have to assign it to LPWSTR var. So, let's ignore about const, 36 // as MS does. Normally we check in our code that we don't overwrite this string. 37 LPWSTR afxPchNil = (LPWSTR)L"\0"; 38 // This is the data that are matching the null string upper 39 CHStringData afxNullData = {0, 0, 0}; 40 // Exception we may throw in case of allocation failure 41 CHeap_Exception HeapException(CHeap_Exception::E_ALLOCATION_ERROR); 42 43 // Our own delete operator 44 // It is here basically because MS guys don't known about set_new_handler() 45 // See operator new 46 void operator delete(void* ptr) 47 { 48 // In Windows 2k3, they check for ptr being null. 49 // ISO, POSIX and even MSDN explains that it is allowed 50 // to call free with NULL pointer... 51 if (ptr) 52 { 53 free(ptr); 54 } 55 } 56 57 // Implement our own new operator so that we can throw our own exception in case 58 // of allocation failure. 59 // It could have been done using set_new_handler(), but well. MS guys didn't do it 60 // that way. So, let's mimic. 61 void* operator new(size_t uSize) 62 { 63 void* Buffer; 64 65 Buffer = malloc(uSize); 66 if (!Buffer) 67 { 68 throw HeapException; 69 } 70 71 return Buffer; 72 } 73 74 // This is a char to wchar string conversion helper 75 int mbstowcsz(LPWSTR lpDest, LPCSTR lpSrc, int nLen) 76 { 77 int Conv; 78 79 // If we have nothing to convert or if output doesn't exist, return 80 if (nLen == 0 || lpDest == 0) 81 { 82 return 0; 83 } 84 85 // Then, simply convert 86 Conv = MultiByteToWideChar(CP_ACP, 0, lpSrc, -1, lpDest, nLen); 87 // In case of conversion success, null terminate the string 88 if (Conv != 0) 89 { 90 lpDest[nLen] = 0; 91 } 92 93 return Conv; 94 } 95 96 /* PUBLIC FUNCTIONS **********************************************************/ 97 98 /* 99 * @implemented 100 */ 101 CHString::CHString() 102 { 103 // Set to empty string 104 m_pchData = afxPchNil; 105 } 106 107 /* 108 * @implemented 109 */ 110 CHString::CHString(WCHAR ch, int nRepeat) throw (CHeap_Exception) 111 { 112 // Allow null initialize, in case something goes wrong 113 m_pchData = afxPchNil; 114 115 // If we have a char to insert 116 if (nRepeat >= 1) 117 { 118 // Allocate a buffer big enough 119 AllocBuffer(nRepeat); 120 // And if possible, repeat char 121 if (m_pchData) 122 { 123 for (int i = 0; i < nRepeat; ++i) 124 { 125 m_pchData[i] = ch; 126 } 127 } 128 } 129 } 130 131 /* 132 * @implemented 133 */ 134 CHString::CHString(LPCWSTR lpsz) throw (CHeap_Exception) 135 { 136 // Allow null initialize, in case something goes wrong 137 m_pchData = afxPchNil; 138 139 // If we have an input string 140 if (lpsz != 0) 141 { 142 // Get its length 143 int Len = SafeStrlen(lpsz); 144 // Then, allocate a big enough buffer and copy string 145 // Note that here, we don't null terminate the string... 146 if (Len) 147 { 148 AllocBuffer(Len); 149 wcsncpy(m_pchData, lpsz, Len); 150 } 151 } 152 } 153 154 /* 155 * @implemented 156 */ 157 CHString::CHString(LPCWSTR lpch, int nLength) throw (CHeap_Exception) 158 { 159 // Allow null initialize, in case something goes wrong 160 m_pchData = afxPchNil; 161 162 // In case we have a string with a len 163 if (lpch != 0 && nLength != 0) 164 { 165 // Just copy the string 166 AllocBuffer(nLength); 167 wcsncpy(m_pchData, lpch, nLength); 168 } 169 } 170 171 /* 172 * @implemented 173 */ 174 CHString::CHString(LPCSTR lpsz) throw (CHeap_Exception) 175 { 176 // Allow null initialize, in case something goes wrong 177 m_pchData = afxPchNil; 178 179 // If we have input string 180 if (lpsz != 0) 181 { 182 // Get its length 183 int Len = (int)strlen(lpsz); 184 if (Len) 185 { 186 // Allocate and convert the string 187 AllocBuffer(Len); 188 mbstowcsz(m_pchData, lpsz, Len + 1); 189 // Releasing buffer here is to allow to 190 // update the buffer size. We notify we're 191 // done with changing the string: recompute its 192 // length, please 193 ReleaseBuffer(); 194 } 195 } 196 } 197 198 /* 199 * @implemented 200 */ 201 CHString::CHString(const unsigned char* lpsz) 202 { 203 // Null init 204 Init(); 205 // And call operator= with const char*, easier 206 *this = (LPCSTR)lpsz; 207 } 208 209 /* 210 * @implemented 211 */ 212 CHString::CHString(const CHString& stringSrc) 213 { 214 // If we have currently no referenced string 215 if (stringSrc.GetData()->nRefs < 0) 216 { 217 // Ensure we have the null string 218 m_pchData = afxPchNil; 219 // And then call, the copy operator with input string 220 *this = stringSrc.m_pchData; 221 } 222 else 223 { 224 // Otherwise, just copy the input string 225 m_pchData = stringSrc.m_pchData; 226 // And increment the number of references 227 InterlockedIncrement(&GetData()->nRefs); 228 // The whole point here is: Am I forget to release the old 229 // data?! MS doesn't release it, but I guess we should... 230 } 231 } 232 233 /* 234 * @implemented 235 */ 236 CHString::~CHString() 237 { 238 // If we have a string 239 if (GetData() != &afxNullData) 240 { 241 // Check whether it's still in use after we release it 242 if (InterlockedDecrement(&GetData()->nRefs) == 0) 243 { 244 // If so, delete it 245 delete GetData(); 246 } 247 } 248 } 249 250 /* 251 * @implemented 252 */ 253 void CHString::AllocBeforeWrite(int nLen) throw (CHeap_Exception) 254 { 255 // In case we have several strings pointing to our memory zone 256 // Or we need bigger buffer than actual 257 if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength) 258 { 259 // Just drop current 260 // And allocate a new one which is big enough 261 Release(); 262 AllocBuffer(nLen); 263 } 264 } 265 266 /* 267 * @implemented 268 */ 269 void CHString::AllocBuffer(int nSize) throw (CHeap_Exception) 270 { 271 // Here we have to allocate a buffer for the string 272 // It actually consists in: CHStringData structure 273 // with a buffer big enough at its end to store the 274 // string. 275 CHStringData* Data; 276 277 // Null size is easy allocation 278 if (nSize == 0) 279 { 280 m_pchData = afxPchNil; 281 return; 282 } 283 284 // We cannot allow negative sizes 285 if (nSize < 0) 286 { 287 RaiseException(ERROR_INVALID_PARAMETER, EXCEPTION_NONCONTINUABLE, 0, 0); 288 } 289 290 // Nor too big 291 if (nSize > (INT_MAX - (int)sizeof(CHStringData)) / (int)sizeof(WCHAR)) 292 { 293 RaiseException(STATUS_INTEGER_OVERFLOW, EXCEPTION_NONCONTINUABLE, 0, 0); 294 } 295 296 // Just allocate big enough buffer, using our own operator new 297 Data = (CHStringData *)operator new(nSize * sizeof(WCHAR) + sizeof(CHStringData)); 298 // In case Data is null, throw an exception 299 // Yes, this is stupid! Our operator new is already supposed to through an exception... 300 // Thanks MS 301 if (!Data) 302 { 303 throw HeapException; 304 } 305 306 Data->nRefs = 1; 307 Data->nDataLength = nSize; 308 Data->nAllocLength = nSize; 309 Data->data()[0] = 0; 310 311 // We only return the string 312 // We can find back data with some mathematics 313 m_pchData = Data->data(); 314 } 315 316 /* 317 * @implemented 318 */ 319 void CHString::AllocCopy(CHString& dest, int nCopyLen, int nCopyIndex, int nExtraLen) const throw (CHeap_Exception) 320 { 321 // Once again, we cannot deal with negative lens 322 if (nCopyLen < 0) 323 { 324 RaiseException(ERROR_INVALID_PARAMETER, EXCEPTION_NONCONTINUABLE, 0, 0); 325 } 326 327 if (nCopyIndex < 0) 328 { 329 RaiseException(ERROR_INVALID_PARAMETER, EXCEPTION_NONCONTINUABLE, 0, 0); 330 } 331 332 if (nExtraLen < 0) 333 { 334 RaiseException(ERROR_INVALID_PARAMETER, EXCEPTION_NONCONTINUABLE, 0, 0); 335 } 336 337 // In case what we have to copy is null-sized, just set empty string 338 if (nCopyLen + nExtraLen == 0) 339 { 340 dest.m_pchData = afxPchNil; 341 return; 342 } 343 344 // Otherwise, allocate a buffer in new string which is big enough 345 // You can note that we absolutely don't check about any existing 346 // (referenced) buffer in dest. Actually, dest is to be EMPTY string. 347 // The whole point of this function is to initialize a virgin string by 348 // copying data from another. This is needed by Left/Mid/Right 349 dest.AllocBuffer(nCopyLen + nExtraLen); 350 // And copy our stuff in 351 wcsncpy(dest.m_pchData, m_pchData + nCopyIndex, nCopyLen); 352 } 353 354 /* 355 * @implemented 356 */ 357 BSTR CHString::AllocSysString() const throw (CHeap_Exception) 358 { 359 BSTR SysString; 360 361 // Just allocate the string 362 SysString = SysAllocStringLen(m_pchData, GetData()->nDataLength); 363 if (!SysString) 364 { 365 throw HeapException; 366 } 367 368 return SysString; 369 } 370 371 /* 372 * @implemented 373 */ 374 void CHString::AssignCopy(int nSrcLen, LPCWSTR lpszSrcData) throw (CHeap_Exception) 375 { 376 // Don't allow negative len 377 if (nSrcLen < 0) 378 { 379 RaiseException(ERROR_INVALID_PARAMETER, EXCEPTION_NONCONTINUABLE, 0, 0); 380 } 381 382 // We will have to modify a string that might be shared, so duplicate it 383 // Ensuring it's big enough to contain our new stuff 384 AllocBeforeWrite(nSrcLen); 385 if (nSrcLen == 0) 386 { 387 Release(); 388 return; 389 } 390 391 // Just copy, write down new size, and ensure it's null terminated 392 wcsncpy(m_pchData, lpszSrcData, nSrcLen); 393 GetData()->nDataLength = nSrcLen; 394 m_pchData[nSrcLen] = 0; 395 } 396 397 /* 398 * @implemented 399 */ 400 int CHString::Collate(LPCWSTR lpsz) const 401 { 402 // Just call the deprecated function here - no matter we are null terminated 403 // Did you read my statement about how safe is this implementation? 404 return wcscoll(m_pchData, lpsz); 405 } 406 407 /* 408 * @implemented 409 */ 410 int CHString::Compare(LPCWSTR lpsz) const 411 { 412 // Just call the deprecated function here - no matter we are null terminated 413 // Did you read my statement about how safe is this implementation? 414 return wcscmp(m_pchData, lpsz); 415 } 416 417 /* 418 * @implemented 419 */ 420 int CHString::CompareNoCase(LPCWSTR lpsz) const 421 { 422 // Just call the deprecated function here - no matter we are null terminated 423 // Did you read my statement about how safe is this implementation? 424 return wcsicmp(m_pchData, lpsz); 425 } 426 427 /* 428 * @implemented 429 */ 430 void CHString::ConcatInPlace(int nSrcLen, LPCWSTR lpszSrcData) 431 { 432 // With null length, there's not that much to concat... 433 if (nSrcLen == 0) 434 { 435 return; 436 } 437 438 // Still no negative length 439 if (nSrcLen < 0) 440 { 441 RaiseException(ERROR_INVALID_PARAMETER, EXCEPTION_NONCONTINUABLE, 0, 0); 442 } 443 444 // Ensure we wouldn't overflow with the concat 445 if (GetData()->nDataLength > INT_MAX - nSrcLen) 446 { 447 RaiseException(STATUS_INTEGER_OVERFLOW, EXCEPTION_NONCONTINUABLE, 0, 0); 448 } 449 450 // In case we have to modify a shared string OR if it can't fit into current buffer... 451 if (GetData()->nRefs > 1 || GetData()->nDataLength + nSrcLen > GetData()->nAllocLength) 452 { 453 // Allocate a new buffer! (without forgetting to release old one) 454 CHStringData* OldData = GetData(); 455 456 // You remember about "InPlace" in the function's name? 457 // The cake is a lie 458 ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData); 459 Release(OldData); 460 } 461 else 462 { 463 // Ensure we don't overflow 464 if (nSrcLen > INT_MAX - GetData()->nDataLength) 465 { 466 RaiseException(STATUS_INTEGER_OVERFLOW, EXCEPTION_NONCONTINUABLE, 0, 0); 467 } 468 469 // Then, just copy and null terminate 470 wcsncpy(m_pchData + GetData()->nDataLength, lpszSrcData, nSrcLen); 471 GetData()->nDataLength += nSrcLen; 472 m_pchData[GetData()->nDataLength] = 0; 473 } 474 } 475 476 /* 477 * @implemented 478 */ 479 void CHString::ConcatCopy(int nSrc1Len, LPCWSTR lpszSrc1Data, int nSrc2Len, LPCWSTR lpszSrc2Data) throw (CHeap_Exception) 480 { 481 int TotalLen; 482 483 if (nSrc1Len < 0 || nSrc2Len < 0) 484 { 485 RaiseException(ERROR_INVALID_PARAMETER, EXCEPTION_NONCONTINUABLE, 0, 0); 486 } 487 488 // If both len are null, do nothing 489 TotalLen = nSrc1Len + nSrc2Len; 490 if (TotalLen == 0) 491 { 492 return; 493 } 494 495 // Otherwise, allocate a new buffer to hold everything (caller will release previous buffer) 496 AllocBuffer(TotalLen); 497 // And concat stuff 498 wcsncpy(m_pchData, lpszSrc1Data, nSrc1Len); 499 wcsncpy(m_pchData + nSrc1Len, lpszSrc2Data, nSrc2Len); 500 } 501 502 /* 503 * @implemented 504 */ 505 void CHString::CopyBeforeWrite() throw (CHeap_Exception) 506 { 507 CHStringData* Data; 508 509 // First, we need to get reference count 510 // And we also need to save Data for later copy 511 Data = GetData(); 512 513 if (Data->nRefs <= 1) 514 { 515 // If its not used, don't waste time to realloc, it will do the job 516 return; 517 } 518 519 // Release current data - we are sure it won't be freed upon that point 520 // Thanks to the reference count check previously done 521 Release(); 522 // Alloc new buffer and copy old data in it 523 AllocBuffer(Data->nDataLength); 524 wcsncpy(m_pchData, Data->data(), Data->nDataLength); 525 } 526 527 /* 528 * @implemented 529 */ 530 void CHString::Empty() 531 { 532 // Already empty 533 if (GetData()->nDataLength == 0) 534 { 535 return; 536 } 537 538 // Empty it easily given it's reference count 539 if (GetData()->nRefs < 0) 540 { 541 *this = afxPchNil; 542 } 543 else 544 { 545 // Otherwise, just release it 546 // It will set back this instance to afxPchNil 547 // while decreasing reference count 548 Release(); 549 } 550 } 551 552 /* 553 * @implemented 554 */ 555 int CHString::Find(WCHAR ch) const 556 { 557 WCHAR *Found; 558 559 // Let's use appropriate helper 560 Found = wcschr(m_pchData, ch); 561 // We have to return a position, so compute it 562 if (Found) 563 { 564 return (Found - m_pchData); 565 } 566 567 // Otherwise, return no position 568 return -1; 569 } 570 571 /* 572 * @implemented 573 */ 574 int CHString::Find(LPCWSTR lpszSub) const 575 { 576 WCHAR *Found; 577 578 // Let's use appropriate helper 579 Found = wcsstr(m_pchData, lpszSub); 580 // We have to return a position, so compute it 581 if (Found) 582 { 583 return (Found - m_pchData); 584 } 585 586 // Otherwise, return no position 587 return -1; 588 } 589 590 /* 591 * @implemented 592 */ 593 int CHString::FindOneOf(LPCWSTR lpszCharSet) const 594 { 595 WCHAR *Found; 596 597 // Let's use appropriate helper 598 Found = wcspbrk(m_pchData, lpszCharSet); 599 // We have to return a position, so compute it 600 if (Found) 601 { 602 return (Found - m_pchData); 603 } 604 605 // Otherwise, return no position 606 return -1; 607 } 608 609 /* 610 * @implemented 611 */ 612 void CHString::Format(UINT nFormatID, ...) throw (CHeap_Exception) 613 { 614 // Deprecated and not implemented any longer - well, this is its implementation 615 return; 616 } 617 618 /* 619 * @implemented 620 */ 621 void CHString::Format(LPCWSTR lpszFormat, ...) throw (CHeap_Exception) 622 { 623 // Forward to FormatV 624 va_list ArgsList; 625 626 va_start(ArgsList, lpszFormat); 627 FormatV(lpszFormat, ArgsList); 628 va_end(ArgsList); 629 } 630 631 /* 632 * @implemented 633 */ 634 void CHString::FormatMessageW(UINT nFormatID, ...) throw (CHeap_Exception) 635 { 636 // Deprecated and not implemented any longer - well, this is its implementation 637 return; 638 } 639 640 /* 641 * @unimplemented 642 */ 643 void CHString::FormatMessageW(LPCWSTR lpszFormat, ...) throw (CHeap_Exception) 644 { 645 UNIMPLEMENTED; 646 } 647 648 /* 649 * @unimplemented 650 */ 651 void CHString::FormatV(LPCWSTR lpszFormat, va_list argList) 652 { 653 UNIMPLEMENTED; 654 } 655 656 /* 657 * @implemented 658 */ 659 void CHString::FreeExtra() throw (CHeap_Exception) 660 { 661 CHStringData* OldData; 662 663 // No extra? Do nothing 664 if (GetData()->nDataLength == GetData()->nAllocLength) 665 { 666 return; 667 } 668 669 // Get old buffer 670 OldData = GetData(); 671 // Allocate a new one, at the right size (with no place for \0 :-)) 672 AllocBuffer(GetData()->nDataLength); 673 // Copy old and release it 674 wcsncpy(m_pchData, OldData->data(), OldData->nDataLength); 675 Release(OldData); 676 } 677 678 /* 679 * @implemented 680 */ 681 int CHString::GetAllocLength() const 682 { 683 return GetData()->nAllocLength; 684 } 685 686 /* 687 * @implemented 688 */ 689 WCHAR CHString::GetAt(int nIndex) const 690 { 691 // It's up to you to check the index! 692 return m_pchData[nIndex]; 693 } 694 695 /* 696 * @implemented 697 */ 698 LPWSTR CHString::GetBuffer(int nMinBufLength) throw (CHeap_Exception) 699 { 700 LPWSTR OldBuffer = m_pchData; 701 702 // We'll have to allocate a new buffer if it's not big enough 703 // or if it's shared by several strings 704 if (GetData()->nRefs > 1 || GetData()->nAllocLength < nMinBufLength) 705 { 706 CHStringData* OldData = GetData(); 707 int OldLen = GetData()->nDataLength; 708 709 // Ensure we can hold enough 710 if (OldLen > nMinBufLength) 711 { 712 nMinBufLength = OldLen; 713 } 714 715 // Allocate new buffer 716 AllocBuffer(nMinBufLength); 717 // Copy contents 718 wcsncpy(m_pchData, OldBuffer, OldLen); 719 GetData()->nDataLength = OldLen; 720 721 // Release old 722 Release(OldData); 723 } 724 725 // Weirdly, here Windows always returns the old buffer 726 // Which basically exposes a wrong buffer 727 return OldBuffer; 728 } 729 730 /* 731 * @implemented 732 */ 733 LPWSTR CHString::GetBufferSetLength(int nNewLength) throw (CHeap_Exception) 734 { 735 // Get a buffer big enough 736 // We don't care about the return, it will be set in the string 737 (void)GetBuffer(nNewLength); 738 // Set length, null-terminate and return 739 GetData()->nDataLength = nNewLength; 740 m_pchData[nNewLength] = 0; 741 return m_pchData; 742 } 743 744 /* 745 * @implemented 746 */ 747 CHStringData* CHString::GetData() const 748 { 749 // In case of empty string, return empty data 750 if (m_pchData == afxPchNil) 751 { 752 return &afxNullData; 753 } 754 755 // Otherwise, do maths 756 return (CHStringData*)((ULONG_PTR)m_pchData - sizeof(CHStringData)); 757 } 758 759 /* 760 * @implemented 761 */ 762 int CHString::GetLength() const 763 { 764 return GetData()->nDataLength; 765 } 766 767 /* 768 * @implemented 769 */ 770 void CHString::Init() 771 { 772 m_pchData = afxPchNil; 773 } 774 775 /* 776 * @implemented 777 */ 778 BOOL CHString::IsEmpty() const 779 { 780 return (GetData()->nDataLength == 0); 781 } 782 783 /* 784 * @implemented 785 */ 786 CHString CHString::Left(int nCount) const throw (CHeap_Exception) 787 { 788 CHString NewString; 789 790 // Validate input (we can't get more than what we have ;-)) 791 if (nCount) 792 { 793 if (nCount > GetData()->nDataLength) 794 { 795 nCount = GetData()->nDataLength; 796 } 797 } 798 799 AllocCopy(NewString, nCount, 0, 0); 800 801 return NewString; 802 } 803 804 /* 805 * @implemented 806 */ 807 int CHString::LoadStringW(UINT nID) throw (CHeap_Exception) 808 { 809 // Deprecated and not implemented any longer - well, this is its implementation 810 return 0; 811 } 812 813 /* 814 * @implemented 815 */ 816 int CHString::LoadStringW(UINT nID, LPWSTR lpszBuf, UINT nMaxBuf) throw (CHeap_Exception) 817 { 818 // Deprecated and not implemented any longer - well, this is its implementation 819 return 0; 820 } 821 822 /* 823 * @implemented 824 */ 825 LPWSTR CHString::LockBuffer() 826 { 827 LPWSTR LockedBuffer; 828 829 // The purpose here is basically to set the nRefs to max int 830 LockedBuffer = GetBuffer(0); 831 GetData()->nRefs = INT_MAX; 832 833 return LockedBuffer; 834 } 835 836 /* 837 * @implemented 838 */ 839 void CHString::MakeLower() throw (CHeap_Exception) 840 { 841 // We'll modify string, duplicate it first if needed 842 CopyBeforeWrite(); 843 844 // Let's use appropriate helper 845 _wcslwr(m_pchData); 846 } 847 848 /* 849 * @implemented 850 */ 851 void CHString::MakeReverse() throw (CHeap_Exception) 852 { 853 // We'll modify string, duplicate it first if needed 854 CopyBeforeWrite(); 855 856 // Let's use appropriate helper 857 _wcsrev(m_pchData); 858 } 859 860 /* 861 * @implemented 862 */ 863 void CHString::MakeUpper() throw (CHeap_Exception) 864 { 865 // We'll modify string, duplicate it first if needed 866 CopyBeforeWrite(); 867 868 // Let's use appropriate helper 869 _wcsupr(m_pchData); 870 } 871 872 /* 873 * @implemented 874 */ 875 CHString CHString::Mid(int nFirst) const throw (CHeap_Exception) 876 { 877 // Take string from nFirst up to the end 878 return Mid(nFirst, GetData()->nDataLength - nFirst); 879 } 880 881 /* 882 * @implemented 883 */ 884 CHString CHString::Mid(int nFirst, int nCount) const throw (CHeap_Exception) 885 { 886 CHString NewString; 887 888 // Validate sizes first 889 if (nFirst < 0) 890 { 891 nFirst = 0; 892 } 893 894 if (nCount < 0) 895 { 896 nCount = 0; 897 } 898 899 // Ensure we don't go beyond the string 900 if (nFirst + nCount > GetData()->nDataLength) 901 { 902 nCount = GetData()->nDataLength - nFirst; 903 } 904 905 // Also ensure we don't read beyond 906 // Yes, this should have been done before previous check 907 // MS does it that way 908 if (nFirst > GetData()->nDataLength) 909 { 910 nCount = 0; 911 } 912 913 AllocCopy(NewString, nCount, nFirst, 0); 914 915 return NewString; 916 } 917 918 /* 919 * @implemented 920 */ 921 void CHString::Release() 922 { 923 // If null string, nothing to do 924 if (GetData() == &afxNullData) 925 { 926 return; 927 } 928 929 // Otherwise, decrement ref count and release if required 930 if (InterlockedDecrement(&GetData()->nRefs) == 0) 931 { 932 delete GetData(); 933 } 934 935 // In all cases, caller doesn't want string anymore 936 // So, switch back to empty string 937 m_pchData = afxPchNil; 938 } 939 940 /* 941 * @implemented 942 */ 943 void WINAPI CHString::Release(CHStringData* pData) 944 { 945 // If empty string, ignore 946 if (pData == &afxNullData) 947 { 948 return; 949 } 950 951 // Otherwise, simply and free if needed 952 if (InterlockedDecrement(&pData->nRefs) == 0) 953 { 954 delete pData; 955 } 956 } 957 958 /* 959 * @implemented 960 */ 961 void CHString::ReleaseBuffer(int nNewLength) throw (CHeap_Exception) 962 { 963 CHStringData* Data; 964 965 // We'll modify buffer, so duplicate 966 CopyBeforeWrite(); 967 968 // If no len provided, get one 969 if (nNewLength == -1) 970 { 971 nNewLength = (int)wcslen(m_pchData); 972 } 973 974 // Set appropriate size and null-terminate 975 Data = GetData(); 976 Data->nDataLength = nNewLength; 977 Data->data()[nNewLength] = 0; 978 } 979 980 /* 981 * @implemented 982 */ 983 int CHString::ReverseFind(WCHAR ch) const 984 { 985 WCHAR *Last; 986 987 // Let's use appropriate helper 988 Last = wcsrchr(m_pchData, ch); 989 // We have to return a position, so compute it 990 if (Last) 991 { 992 return (Last - m_pchData); 993 } 994 995 // Otherwise, return no position 996 return -1; 997 } 998 999 /* 1000 * @implemented 1001 */ 1002 CHString CHString::Right(int nCount) const throw (CHeap_Exception) 1003 { 1004 CHString NewString; 1005 1006 // Validate input (we can't get more than what we have ;-)) 1007 if (nCount >= 0) 1008 { 1009 if (nCount > GetData()->nDataLength) 1010 { 1011 nCount = GetData()->nDataLength; 1012 } 1013 } 1014 1015 AllocCopy(NewString, nCount, GetData()->nDataLength - nCount, 0); 1016 1017 return NewString; 1018 } 1019 1020 /* 1021 * @implemented 1022 */ 1023 int CHString::SafeStrlen(LPCWSTR lpsz) 1024 { 1025 // Check we have a string and then get its length 1026 if (lpsz == 0) 1027 { 1028 return 0; 1029 } 1030 1031 // Of course, it's not safe at all in case string is not null-terminated. 1032 // Things that may happen given strings are not to be null-terminated 1033 // in this class... 1034 return (int)wcslen(lpsz); 1035 } 1036 1037 /* 1038 * @implemented 1039 */ 1040 void CHString::SetAt(int nIndex, WCHAR ch) throw (CHeap_Exception) 1041 { 1042 CopyBeforeWrite(); 1043 1044 m_pchData[nIndex] = ch; 1045 } 1046 1047 /* 1048 * @implemented 1049 */ 1050 CHString CHString::SpanExcluding(LPCWSTR lpszCharSet) const throw (CHeap_Exception) 1051 { 1052 int Count; 1053 1054 // Get position and then, extract 1055 Count = (int)wcscspn(m_pchData, lpszCharSet); 1056 return Left(Count); 1057 } 1058 1059 /* 1060 * @implemented 1061 */ 1062 CHString CHString::SpanIncluding(LPCWSTR lpszCharSet) const throw (CHeap_Exception) 1063 { 1064 int Count; 1065 1066 // Get position and then, extract 1067 Count = (int)wcsspn(m_pchData, lpszCharSet); 1068 return Left(Count); 1069 } 1070 1071 /* 1072 * @implemented 1073 */ 1074 void CHString::TrimLeft() throw (CHeap_Exception) 1075 { 1076 int NewBegin; 1077 int NewLength; 1078 WCHAR *CurrentChar; 1079 1080 // We'll modify, so copy first 1081 CopyBeforeWrite(); 1082 1083 // Start at the begin of the string 1084 CurrentChar = m_pchData; 1085 while (*CurrentChar != 0) 1086 { 1087 // Browse string till we find something which is not a space 1088 if (!iswspace(*CurrentChar)) 1089 { 1090 break; 1091 } 1092 1093 CurrentChar++; 1094 } 1095 1096 // Then, calculate new begin (easy) and new length 1097 // And move memory 1098 NewBegin = (CurrentChar - m_pchData); 1099 NewLength = GetData()->nDataLength - NewBegin; 1100 memmove(m_pchData, CurrentChar, NewLength * sizeof(WCHAR)); 1101 GetData()->nDataLength = NewLength; 1102 } 1103 1104 /* 1105 * @implemented 1106 */ 1107 void CHString::TrimRight() throw (CHeap_Exception) 1108 { 1109 WCHAR *CurrentChar; 1110 WCHAR *CanBeEaten; 1111 1112 // We'll modify, so copy first 1113 CopyBeforeWrite(); 1114 1115 // Start at the begin of the string -- WHAT?! 1116 // Yes, this algorithm is the same that MS is 1117 // using for its TrimRight. 1118 // It is highly unefficient. It would have been 1119 // easier to start at nDataLength and to get back to 1120 // the begin. Note that it would have been safer as 1121 // well, in case the caller is using non-null-terminated 1122 // strings. But, well... 1123 CurrentChar = m_pchData; 1124 CanBeEaten = 0; 1125 while (*CurrentChar != 0) 1126 { 1127 // If not a space, reset what we can trim 1128 if (!iswspace(*CurrentChar)) 1129 { 1130 CanBeEaten = 0; 1131 } 1132 // If it is one, and the first of the spaces serie 1133 // Keep its position 1134 else if (CanBeEaten == 0) 1135 { 1136 CanBeEaten = CurrentChar; 1137 } 1138 1139 CurrentChar++; 1140 } 1141 1142 // If nothing to trim, quit 1143 if (CanBeEaten == 0) 1144 { 1145 return; 1146 } 1147 1148 // Otherwise, shorten the string 1149 GetData()->nDataLength = (CanBeEaten - m_pchData); 1150 } 1151 1152 /* 1153 * @implemented 1154 */ 1155 void CHString::UnlockBuffer() 1156 { 1157 // Unlock means just put ref back to 1 1158 // It was previously set to MAX_INT 1159 if (GetData() != &afxNullData) 1160 { 1161 GetData()->nRefs = 1; 1162 } 1163 } 1164 1165 /* 1166 * @implemented 1167 */ 1168 const CHString& CHString::operator=(char ch) throw (CHeap_Exception) 1169 { 1170 *this = (WCHAR)ch; 1171 return *this; 1172 } 1173 1174 /* 1175 * @implemented 1176 */ 1177 const CHString& CHString::operator=(WCHAR ch) throw (CHeap_Exception) 1178 { 1179 AssignCopy(1, &ch); 1180 return *this; 1181 } 1182 1183 /* 1184 * @implemented 1185 */ 1186 const CHString& CHString::operator=(CHString *p) throw (CHeap_Exception) 1187 { 1188 *this = *p; 1189 return *this; 1190 } 1191 1192 /* 1193 * @implemented 1194 */ 1195 const CHString& CHString::operator=(LPCSTR lpsz) throw (CHeap_Exception) 1196 { 1197 int Len; 1198 1199 // If we have string, get its len 1200 if (lpsz != 0) 1201 { 1202 Len = (int)strlen(lpsz); 1203 } 1204 else 1205 { 1206 Len = 0; 1207 } 1208 1209 // Do this call, even with null len, just to get empty string 1210 AllocBeforeWrite(Len); 1211 if (Len == 0) 1212 { 1213 Release(); 1214 return *this; 1215 } 1216 1217 // Convert and copy 1218 mbstowcsz(m_pchData, lpsz, Len + 1); 1219 // Get new size and so on 1220 ReleaseBuffer(); 1221 1222 return *this; 1223 } 1224 1225 /* 1226 * @implemented 1227 */ 1228 const CHString& CHString::operator=(LPCWSTR lpsz) throw (CHeap_Exception) 1229 { 1230 int Len; 1231 1232 Len = SafeStrlen(lpsz); 1233 AssignCopy(Len, lpsz); 1234 1235 return *this; 1236 } 1237 1238 /* 1239 * @implemented 1240 */ 1241 const CHString& CHString::operator=(const CHString& stringSrc) throw (CHeap_Exception) 1242 { 1243 // Don't copy string on itself 1244 if (&stringSrc == this) 1245 { 1246 return *this; 1247 } 1248 1249 // In case we don't have a referenced string here, 1250 // or if the other is not referenced, just copy here 1251 if ((GetData()->nRefs < 0 && GetData() != &afxNullData) || 1252 stringSrc.GetData()->nRefs < 0) 1253 { 1254 AssignCopy(stringSrc.GetData()->nDataLength, stringSrc.m_pchData); 1255 return *this; 1256 } 1257 1258 // Otherwise, release current buffer 1259 Release(); 1260 // And set buffer as stringSrc buffer 1261 // And increase its reference count 1262 m_pchData = stringSrc.m_pchData; 1263 InterlockedIncrement(&GetData()->nRefs); 1264 1265 return *this; 1266 } 1267 1268 /* 1269 * @implemented 1270 */ 1271 const CHString& CHString::operator=(const unsigned char* lpsz) throw (CHeap_Exception) 1272 { 1273 *this = (LPCSTR)lpsz; 1274 return *this; 1275 } 1276 1277 /* 1278 * @implemented 1279 */ 1280 const CHString& CHString::operator+=(char ch) throw (CHeap_Exception) 1281 { 1282 *this += (WCHAR)ch; 1283 return *this; 1284 } 1285 1286 /* 1287 * @implemented 1288 */ 1289 const CHString& CHString::operator+=(WCHAR ch) throw (CHeap_Exception) 1290 { 1291 ConcatInPlace(1, &ch); 1292 return *this; 1293 } 1294 1295 /* 1296 * @implemented 1297 */ 1298 const CHString& CHString::operator+=(LPCWSTR lpsz) throw (CHeap_Exception) 1299 { 1300 int Len; 1301 1302 Len = SafeStrlen(lpsz); 1303 ConcatInPlace(Len, lpsz); 1304 1305 return *this; 1306 } 1307 1308 /* 1309 * @implemented 1310 */ 1311 const CHString& CHString::operator+=(const CHString& string) throw (CHeap_Exception) 1312 { 1313 ConcatInPlace(string.GetData()->nDataLength, string.m_pchData); 1314 1315 return *this; 1316 } 1317 1318 /* 1319 * @implemented 1320 */ 1321 WCHAR CHString::operator[](int nIndex) const 1322 { 1323 return m_pchData[nIndex]; 1324 } 1325 1326 /* 1327 * @implemented 1328 */ 1329 CHString::operator LPCWSTR() const 1330 { 1331 return m_pchData; 1332 } 1333 1334 /* 1335 * @implemented 1336 */ 1337 CHString WINAPI operator+(WCHAR ch, const CHString& string) throw (CHeap_Exception) 1338 { 1339 CHString NewString; 1340 1341 // Basically concat in a new string 1342 NewString.ConcatCopy(1, &ch, string.GetData()->nDataLength, string.m_pchData); 1343 1344 return NewString; 1345 } 1346 1347 /* 1348 * @implemented 1349 */ 1350 CHString WINAPI operator+(const CHString& string, WCHAR ch) throw (CHeap_Exception) 1351 { 1352 CHString NewString; 1353 1354 // Basically concat in a new string 1355 NewString.ConcatCopy(string.GetData()->nDataLength, string.m_pchData, 1, &ch); 1356 1357 return NewString; 1358 } 1359 1360 /* 1361 * @implemented 1362 */ 1363 CHString WINAPI operator+(const CHString& string, LPCWSTR lpsz) throw (CHeap_Exception) 1364 { 1365 int Len; 1366 CHString NewString; 1367 1368 // Get string length 1369 Len = CHString::SafeStrlen(lpsz); 1370 // And concat in new string 1371 NewString.ConcatCopy(string.GetData()->nDataLength, string.m_pchData, Len, lpsz); 1372 1373 return NewString; 1374 } 1375 1376 /* 1377 * @implemented 1378 */ 1379 CHString WINAPI operator+(LPCWSTR lpsz, const CHString& string) throw (CHeap_Exception) 1380 { 1381 int Len; 1382 CHString NewString; 1383 1384 // Get string length 1385 Len = CHString::SafeStrlen(lpsz); 1386 // And concat in new string 1387 NewString.ConcatCopy(Len, lpsz, string.GetData()->nDataLength, string.m_pchData); 1388 1389 return NewString; 1390 } 1391 1392 /* 1393 * @implemented 1394 */ 1395 CHString WINAPI operator+(const CHString& string1, const CHString& string2) throw (CHeap_Exception) 1396 { 1397 CHString NewString; 1398 1399 // Basically concat in a new string 1400 NewString.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, 1401 string2.GetData()->nDataLength, string2.m_pchData); 1402 1403 return NewString; 1404 } 1405