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
operator delete(void * ptr)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.
operator new(size_t uSize)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
mbstowcsz(LPWSTR lpDest,LPCSTR lpSrc,int nLen)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 */
CHString()103 CHString::CHString()
104 {
105 // Set to empty string
106 m_pchData = afxPchNil;
107 }
108
109 /*
110 * @implemented
111 */
CHString(CHSTRING_WCHAR ch,int nRepeat)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 */
CHString(CHSTRING_LPCWSTR lpsz)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 */
CHString(CHSTRING_LPCWSTR lpch,int nLength)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 */
CHString(LPCSTR lpsz)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 */
CHString(const unsigned char * lpsz)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 */
CHString(const CHString & stringSrc)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 */
~CHString()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 */
AllocBeforeWrite(int nLen)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 */
AllocBuffer(int nSize)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 */
AllocCopy(CHString & dest,int nCopyLen,int nCopyIndex,int nExtraLen) const325 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 */
AllocSysString() const365 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 */
AssignCopy(int nSrcLen,CHSTRING_LPCWSTR lpszSrcData)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 */
Collate(CHSTRING_LPCWSTR lpsz) const409 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 */
Compare(CHSTRING_LPCWSTR lpsz) const419 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 */
CompareNoCase(CHSTRING_LPCWSTR lpsz) const429 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 */
ConcatInPlace(int nSrcLen,CHSTRING_LPCWSTR lpszSrcData)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 */
ConcatCopy(int nSrc1Len,CHSTRING_LPCWSTR lpszSrc1Data,int nSrc2Len,CHSTRING_LPCWSTR lpszSrc2Data)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 */
CopyBeforeWrite()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 */
Empty()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 */
Find(CHSTRING_WCHAR ch) const574 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 */
Find(CHSTRING_LPCWSTR lpszSub) const593 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 */
FindOneOf(CHSTRING_LPCWSTR lpszCharSet) const612 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 */
Format(UINT nFormatID,...)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 */
Format(CHSTRING_LPCWSTR lpszFormat,...)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 */
FormatMessageW(UINT nFormatID,...)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 */
FormatMessageW(CHSTRING_LPCWSTR lpszFormat,...)662 void CHString::FormatMessageW(CHSTRING_LPCWSTR lpszFormat, ...)
663 {
664 UNIMPLEMENTED;
665 }
666
667 /*
668 * @unimplemented
669 */
FormatV(CHSTRING_LPCWSTR lpszFormat,va_list argList)670 void CHString::FormatV(CHSTRING_LPCWSTR lpszFormat, va_list argList)
671 {
672 UNIMPLEMENTED;
673 }
674
675 /*
676 * @implemented
677 */
FreeExtra()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 */
GetAllocLength() const700 int CHString::GetAllocLength() const
701 {
702 return GetData()->nAllocLength;
703 }
704
705 /*
706 * @implemented
707 */
GetAt(int nIndex) const708 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 */
GetBuffer(int nMinBufLength)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 */
GetBufferSetLength(int nNewLength)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 */
GetData() const766 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 */
GetLength() const781 int CHString::GetLength() const
782 {
783 return GetData()->nDataLength;
784 }
785
786 /*
787 * @implemented
788 */
Init()789 void CHString::Init()
790 {
791 m_pchData = afxPchNil;
792 }
793
794 /*
795 * @implemented
796 */
IsEmpty() const797 BOOL CHString::IsEmpty() const
798 {
799 return (GetData()->nDataLength == 0);
800 }
801
802 /*
803 * @implemented
804 */
Left(int nCount) const805 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 */
LoadStringW(UINT nID)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 */
LoadStringW(UINT nID,CHSTRING_LPWSTR lpszBuf,UINT nMaxBuf)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 */
LockBuffer()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 */
MakeLower()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 */
MakeReverse()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 */
MakeUpper()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 */
Mid(int nFirst) const894 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 */
Mid(int nFirst,int nCount) const903 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 */
Release()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 */
Release(CHStringData * pData)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 */
ReleaseBuffer(int nNewLength)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 */
ReverseFind(CHSTRING_WCHAR ch) const1002 int CHString::ReverseFind(CHSTRING_WCHAR ch) const
1003 {
1004 CHSTRING_LPCWSTR Last;
1005
1006 // Let's use appropriate helper
1007 Last = reinterpret_cast<CHSTRING_LPCWSTR>(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 */
Right(int nCount) const1021 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 */
SafeStrlen(CHSTRING_LPCWSTR lpsz)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 */
SetAt(int nIndex,CHSTRING_WCHAR ch)1059 void CHString::SetAt(int nIndex, CHSTRING_WCHAR ch)
1060 {
1061 CopyBeforeWrite();
1062
1063 m_pchData[nIndex] = ch;
1064 }
1065
1066 /*
1067 * @implemented
1068 */
SpanExcluding(CHSTRING_LPCWSTR lpszCharSet) const1069 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 */
SpanIncluding(CHSTRING_LPCWSTR lpszCharSet) const1081 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 */
TrimLeft()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 */
TrimRight()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 */
UnlockBuffer()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 */
operator =(char ch)1187 const CHString& CHString::operator=(char ch)
1188 {
1189 *this = (CHSTRING_WCHAR)ch;
1190 return *this;
1191 }
1192
1193 /*
1194 * @implemented
1195 */
operator =(CHSTRING_WCHAR ch)1196 const CHString& CHString::operator=(CHSTRING_WCHAR ch)
1197 {
1198 AssignCopy(1, &ch);
1199 return *this;
1200 }
1201
1202 /*
1203 * @implemented
1204 */
operator =(CHString * p)1205 const CHString& CHString::operator=(CHString *p)
1206 {
1207 *this = *p;
1208 return *this;
1209 }
1210
1211 /*
1212 * @implemented
1213 */
operator =(LPCSTR lpsz)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 */
operator =(CHSTRING_LPCWSTR lpsz)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 */
operator =(const CHString & stringSrc)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 */
operator =(const unsigned char * lpsz)1290 const CHString& CHString::operator=(const unsigned char* lpsz)
1291 {
1292 *this = (LPCSTR)lpsz;
1293 return *this;
1294 }
1295
1296 /*
1297 * @implemented
1298 */
operator +=(char ch)1299 const CHString& CHString::operator+=(char ch)
1300 {
1301 *this += (CHSTRING_WCHAR)ch;
1302 return *this;
1303 }
1304
1305 /*
1306 * @implemented
1307 */
operator +=(CHSTRING_WCHAR ch)1308 const CHString& CHString::operator+=(CHSTRING_WCHAR ch)
1309 {
1310 ConcatInPlace(1, &ch);
1311 return *this;
1312 }
1313
1314 /*
1315 * @implemented
1316 */
operator +=(CHSTRING_LPCWSTR lpsz)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 */
operator +=(const CHString & string)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 */
operator [](int nIndex) const1340 CHSTRING_WCHAR CHString::operator[](int nIndex) const
1341 {
1342 return m_pchData[nIndex];
1343 }
1344
1345 /*
1346 * @implemented
1347 */
operator CHSTRING_LPCWSTR() const1348 CHString::operator CHSTRING_LPCWSTR() const
1349 {
1350 return m_pchData;
1351 }
1352
1353 /*
1354 * @implemented
1355 */
operator +(CHSTRING_WCHAR ch,const CHString & string)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 */
operator +(const CHString & string,CHSTRING_WCHAR ch)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 */
operator +(const CHString & string,CHSTRING_LPCWSTR lpsz)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 */
operator +(CHSTRING_LPCWSTR lpsz,const CHString & string)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 */
operator +(const CHString & string1,const CHString & string2)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