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