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