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