[FRAMEDYN]
[reactos.git] / reactos / dll / win32 / framedyn / chstring.cpp
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: dll/win32/framedyn/chstring.cpp
5 * PURPOSE: CHString class implementation
6 * PROGRAMMERS: Pierre Schweitzer (pierre@reactos.org)
7 *
8 * NOTE: This implementation is BROKEN on PURPOSE
9 * The CHString is a mix between std::string and
10 * UNICODE_STRING. It appears that basically it takes only
11 * the worse from both approaches.
12 * I've copied the behavior and implementation of Windows 2k3 even if
13 * it implies unsafe, wrong or unefficient methods.
14 * Note that the string at m_pchData might not be null terminated!
15 * Also, important note, two (or even more) CHString instances might
16 * have the same m_pchData object! Never forget that while modifying
17 * a string. You might be modifying the string for everyone.
18 * This is why a protected method is being used in the code: CopyBeforeWrite
19 * It copies source first, to ensure we only modify current string
20 * Side note, all the sizes are actually a number of chars. Only the size
21 * for implementation is the number of bytes
22 * Now, you know why this class is deprecated and shouldn't be used
23 */
24
25 /* INCLUDES ******************************************************************/
26
27 #include <chstring.h>
28 #define NDEBUG
29 #include <debug.h>
30
31 /* PRIVATE FUNCTIONS *********************************************************/
32
33 // This is the empty string that defaults strings without text
34 // This is unsafe. This string show be LPCWSTR
35 // However we have to assign it to LPWSTR var. So, let's ignore about const,
36 // as MS does. Normally we check in our code that we don't overwrite this string.
37 LPWSTR afxPchNil = (LPWSTR)L"\0";
38 // This is the data that are matching the null string upper
39 CHStringData afxNullData = {0, 0, 0};
40 // Exception we may throw in case of allocation failure
41 CHeap_Exception HeapException(CHeap_Exception::E_ALLOCATION_ERROR);
42
43 // Our own delete operator
44 // It is here basically because MS guys don't known about set_new_handler()
45 // See operator new
46 void operator delete(void* ptr)
47 {
48 // In Windows 2k3, they check for ptr being null.
49 // ISO, POSIX and even MSDN explains that it is allowed
50 // to call free with NULL pointer...
51 if (ptr)
52 {
53 free(ptr);
54 }
55 }
56
57 // 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 }