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