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