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