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