xref: /reactos/sdk/lib/atl/atlsimpstr.h (revision ebfec38c)
1 #ifndef __ATLSIMPSTR_H__
2 #define __ATLSIMPSTR_H__
3 
4 #pragma once
5 
6 #include <atlcore.h>
7 #include <atlexcept.h>
8 
9 #ifdef __RATL__
10     #ifndef _In_count_
11         #define _In_count_(nLength)
12     #endif
13 #endif
14 
15 namespace ATL
16 {
17 struct CStringData;
18 
19 // Pure virtual interface
20 class IAtlStringMgr
21 {
22 public:
23 
24     virtual ~IAtlStringMgr() {}
25 
26     virtual _Ret_maybenull_ _Post_writable_byte_size_(sizeof(CStringData) + nAllocLength*nCharSize)
27         CStringData* Allocate(
28         _In_ int nAllocLength,
29         _In_ int nCharSize
30         ) = 0;
31 
32     virtual void Free(
33         _Inout_ CStringData* pData
34         ) = 0;
35 
36     virtual _Ret_maybenull_ _Post_writable_byte_size_(sizeof(CStringData) + nAllocLength*nCharSize)
37         CStringData* Reallocate(
38         _Inout_ CStringData* pData,
39         _In_ int nAllocLength,
40         _In_ int nCharSize
41         ) = 0;
42 
43     virtual CStringData* GetNilString(void) = 0;
44     virtual IAtlStringMgr* Clone(void) = 0;
45 };
46 
47 
48 struct CStringData
49 {
50     IAtlStringMgr* pStringMgr;
51     int nAllocLength;
52     int nDataLength;
53     long nRefs;
54 
55     void* data() throw()
56     {
57         return (this + 1);
58     }
59 
60     void AddRef() throw()
61     {
62         ATLASSERT(nRefs > 0);
63         _InterlockedIncrement(&nRefs);
64     }
65 
66     void Release() throw()
67     {
68         ATLASSERT(nRefs != 0);
69 
70         if (_InterlockedDecrement(&nRefs) <= 0)
71         {
72             pStringMgr->Free(this);
73         }
74     }
75 
76     bool IsLocked() const throw()
77     {
78         return (nRefs < 0);
79     }
80 
81     bool IsShared() const throw()
82     {
83         return (nRefs > 1);
84     }
85 };
86 
87 class CNilStringData :
88     public CStringData
89 {
90 public:
91     CNilStringData() throw()
92     {
93         pStringMgr = NULL;
94         nRefs = 2;
95         nDataLength = 0;
96         nAllocLength = 0;
97         achNil[0] = 0;
98         achNil[1] = 0;
99     }
100 
101     void SetManager(_In_ IAtlStringMgr* pMgr) throw()
102     {
103         ATLASSERT(pStringMgr == NULL);
104         pStringMgr = pMgr;
105     }
106 
107 public:
108     wchar_t achNil[2];
109 };
110 
111 
112 template< typename BaseType = char >
113 class ChTraitsBase
114 {
115 public:
116     typedef char XCHAR;
117     typedef LPSTR PXSTR;
118     typedef LPCSTR PCXSTR;
119     typedef wchar_t YCHAR;
120     typedef LPWSTR PYSTR;
121     typedef LPCWSTR PCYSTR;
122 };
123 
124 template<>
125 class ChTraitsBase<wchar_t>
126 {
127 public:
128     typedef wchar_t XCHAR;
129     typedef LPWSTR PXSTR;
130     typedef LPCWSTR PCXSTR;
131     typedef char YCHAR;
132     typedef LPSTR PYSTR;
133     typedef LPCSTR PCYSTR;
134 };
135 
136 template< typename BaseType, bool t_bMFCDLL = false>
137 class CSimpleStringT
138 {
139 public:
140     typedef typename ChTraitsBase<BaseType>::XCHAR XCHAR;
141     typedef typename ChTraitsBase<BaseType>::PXSTR PXSTR;
142     typedef typename ChTraitsBase<BaseType>::PCXSTR PCXSTR;
143     typedef typename ChTraitsBase<BaseType>::YCHAR YCHAR;
144     typedef typename ChTraitsBase<BaseType>::PYSTR PYSTR;
145     typedef typename ChTraitsBase<BaseType>::PCYSTR PCYSTR;
146 
147 private:
148     PXSTR m_pszData;
149 
150 public:
151     explicit CSimpleStringT(_Inout_ IAtlStringMgr* pStringMgr)
152     {
153         CStringData* pData = pStringMgr->GetNilString();
154         Attach(pData);
155     }
156 
157     CSimpleStringT(_In_ const CSimpleStringT& strSrc)
158     {
159         CStringData* pSrcData = strSrc.GetData();
160         CStringData* pNewData = CloneData(pSrcData);
161         Attach(pNewData);
162     }
163 
164     CSimpleStringT(
165         _In_z_ PCXSTR pszSrc,
166         _Inout_ IAtlStringMgr* pStringMgr)
167     {
168         int nLength = StringLength(pszSrc);
169         CStringData* pData = pStringMgr->Allocate(nLength, sizeof(XCHAR));
170         if (pData == NULL)
171             ThrowMemoryException();
172 
173         Attach(pData);
174         SetLength(nLength);
175         CopyChars(m_pszData, nLength, pszSrc, nLength);
176     }
177 
178     CSimpleStringT(
179         _In_count_(nLength) const XCHAR* pchSrc,
180         _In_ int nLength,
181         _Inout_ IAtlStringMgr* pStringMgr)
182     {
183         if (pchSrc == NULL && nLength != 0)
184             ThrowInvalidArgException();
185 
186         CStringData* pData = pStringMgr->Allocate(nLength, sizeof(XCHAR));
187         if (pData == NULL)
188         {
189             ThrowMemoryException();
190         }
191         Attach(pData);
192         SetLength(nLength);
193         CopyChars(m_pszData, nLength, pchSrc, nLength);
194     }
195 
196     ~CSimpleStringT() throw()
197     {
198         CStringData* pData = GetData();
199         pData->Release();
200     }
201 
202     CSimpleStringT& operator=(_In_opt_z_ PCXSTR pszSrc)
203     {
204         SetString(pszSrc);
205         return *this;
206     }
207 
208     CSimpleStringT& operator=(_In_ const CSimpleStringT& strSrc)
209     {
210         CStringData* pData = GetData();
211         CStringData* pNewData = strSrc.GetData();
212 
213         if (pNewData != pData)
214         {
215             if (!pData->IsLocked() && (pNewData->pStringMgr == pData->pStringMgr))
216             {
217                 pNewData = CloneData(pNewData);
218                 pData->Release();
219                 Attach(pNewData);
220             }
221             else
222             {
223                 SetString(strSrc.GetString(), strSrc.GetLength());
224             }
225         }
226 
227         return *this;
228     }
229 
230     CSimpleStringT& operator+=(_In_ const CSimpleStringT& strSrc)
231     {
232         Append(strSrc);
233         return *this;
234     }
235 
236     CSimpleStringT& operator+=(_In_z_ PCXSTR pszSrc)
237     {
238         Append(pszSrc);
239         return *this;
240     }
241 
242     CSimpleStringT& operator+=(XCHAR ch)
243     {
244         Append(&ch, 1);
245         return *this;
246     }
247 
248     operator PCXSTR() const throw()
249     {
250         return m_pszData;
251     }
252 
253     void Empty() throw()
254     {
255         CStringData* pOldData = GetData();
256         IAtlStringMgr* pStringMgr = pOldData->pStringMgr;
257         if (pOldData->nDataLength == 0) return;
258 
259         if (pOldData->IsLocked())
260         {
261             SetLength(0);
262         }
263         else
264         {
265             pOldData->Release();
266             CStringData* pNewData = pStringMgr->GetNilString();
267             Attach(pNewData);
268         }
269     }
270 
271     void Append(
272         _In_count_(nLength) PCXSTR pszSrc,
273         _In_ int nLength)
274     {
275         UINT_PTR nOffset = pszSrc - GetString();
276 
277         int nOldLength = GetLength();
278         if (nOldLength < 0)
279             nOldLength = 0;
280 
281         ATLASSERT(nLength >= 0);
282 
283 #if 0 // FIXME: See comment for StringLengthN below.
284         nLength = StringLengthN(pszSrc, nLength);
285         if (!(INT_MAX - nLength >= nOldLength))
286             throw;
287 #endif
288 
289         int nNewLength = nOldLength + nLength;
290         PXSTR pszBuffer = GetBuffer(nNewLength);
291         if (nOffset <= (UINT_PTR)nOldLength)
292         {
293             pszSrc = pszBuffer + nOffset;
294         }
295         CopyChars(pszBuffer + nOldLength, nLength, pszSrc, nLength);
296         ReleaseBufferSetLength(nNewLength);
297     }
298 
299     void Append(_In_z_ PCXSTR pszSrc)
300     {
301         Append(pszSrc, StringLength(pszSrc));
302     }
303 
304     void Append(_In_ const CSimpleStringT& strSrc)
305     {
306         Append(strSrc.GetString(), strSrc.GetLength());
307     }
308 
309     void SetString(_In_opt_z_ PCXSTR pszSrc)
310     {
311         SetString(pszSrc, StringLength(pszSrc));
312     }
313 
314     void SetString(_In_reads_opt_(nLength) PCXSTR pszSrc,
315                    _In_ int nLength)
316     {
317         if (nLength == 0)
318         {
319             Empty();
320         }
321         else
322         {
323             UINT nOldLength = GetLength();
324             UINT_PTR nOffset = pszSrc - GetString();
325 
326             PXSTR pszBuffer = GetBuffer(nLength);
327             if (nOffset <= nOldLength)
328             {
329                 CopyCharsOverlapped(pszBuffer, GetAllocLength(),
330                                     pszBuffer + nOffset, nLength);
331             }
332             else
333             {
334                 CopyChars(pszBuffer, GetAllocLength(), pszSrc, nLength);
335             }
336             ReleaseBufferSetLength(nLength);
337         }
338     }
339 
340     PXSTR GetBuffer()
341     {
342         CStringData* pData = GetData();
343         if (pData->IsShared())
344         {
345             // We should fork here
346             Fork(pData->nDataLength);
347         }
348 
349         return m_pszData;
350     }
351 
352     _Ret_notnull_ _Post_writable_size_(nMinBufferLength + 1) PXSTR GetBuffer(_In_ int nMinBufferLength)
353     {
354         return PrepareWrite(nMinBufferLength);
355     }
356 
357     int GetAllocLength() const throw()
358     {
359         return GetData()->nAllocLength;
360     }
361 
362     int GetLength() const throw()
363     {
364         return GetData()->nDataLength;
365     }
366 
367     PXSTR GetString() throw()
368     {
369         return m_pszData;
370     }
371     PCXSTR GetString() const throw()
372     {
373         return m_pszData;
374     }
375 
376     void ReleaseBufferSetLength(_In_ int nNewLength)
377     {
378         ATLASSERT(nNewLength >= 0);
379         SetLength(nNewLength);
380     }
381 
382     void ReleaseBuffer(_In_ int nNewLength = -1)
383     {
384         if (nNewLength < 0)
385             nNewLength = StringLength(m_pszData);
386         ReleaseBufferSetLength(nNewLength);
387     }
388 
389     bool IsEmpty() const throw()
390     {
391         return (GetLength() == 0);
392     }
393 
394     CStringData* GetData() const throw()
395     {
396         return (reinterpret_cast<CStringData*>(m_pszData) - 1);
397     }
398 
399     IAtlStringMgr* GetManager() const throw()
400     {
401         IAtlStringMgr* pStringMgr = GetData()->pStringMgr;
402         return (pStringMgr ? pStringMgr->Clone() : NULL);
403     }
404 
405 public:
406     friend CSimpleStringT operator+(
407         _In_ const CSimpleStringT& str1,
408         _In_ const CSimpleStringT& str2)
409     {
410         CSimpleStringT s(str1.GetManager());
411         Concatenate(s, str1, str1.GetLength(), str2, str2.GetLength());
412         return s;
413     }
414 
415     friend CSimpleStringT operator+(
416         _In_ const CSimpleStringT& str1,
417         _In_z_ PCXSTR psz2)
418     {
419         CSimpleStringT s(str1.GetManager());
420         Concatenate(s, str1, str1.GetLength(), psz2, StringLength(psz2));
421         return s;
422     }
423 
424     friend CSimpleStringT operator+(
425         _In_z_ PCXSTR psz1,
426         _In_ const CSimpleStringT& str2)
427     {
428         CSimpleStringT s(str2.GetManager());
429         Concatenate(s, psz1, StringLength(psz1), str2, str2.GetLength());
430         return s;
431     }
432 
433     static void __cdecl CopyChars(
434         _Out_writes_to_(nDestLen, nChars) XCHAR* pchDest,
435         _In_ size_t nDestLen,
436         _In_reads_opt_(nChars) const XCHAR* pchSrc,
437         _In_ int nChars) throw()
438     {
439         memcpy(pchDest, pchSrc, nChars * sizeof(XCHAR));
440     }
441 
442     static void __cdecl CopyCharsOverlapped(
443         _Out_writes_to_(nDestLen, nDestLen) XCHAR* pchDest,
444         _In_ size_t nDestLen,
445         _In_reads_(nChars) const XCHAR* pchSrc,
446         _In_ int nChars) throw()
447     {
448         memmove(pchDest, pchSrc, nChars * sizeof(XCHAR));
449     }
450 
451     static int __cdecl StringLength(_In_opt_z_ const char* psz) throw()
452     {
453         if (psz == NULL) return 0;
454         return (int)strlen(psz);
455     }
456 
457     static int __cdecl StringLength(_In_opt_z_ const wchar_t* psz) throw()
458     {
459         if (psz == NULL) return 0;
460         return (int)wcslen(psz);
461     }
462 
463 #if 0 // For whatever reason we do not link with strnlen / wcsnlen. Please investigate!
464       // strnlen / wcsnlen are available in MSVCRT starting Vista+.
465     static int __cdecl StringLengthN(
466         _In_opt_z_count_(sizeInXChar) const char* psz,
467         _In_ size_t sizeInXChar) throw()
468     {
469         if (psz == NULL) return 0;
470         return (int)strnlen(psz, sizeInXChar);
471     }
472 
473     static int __cdecl StringLengthN(
474         _In_opt_z_count_(sizeInXChar) const wchar_t* psz,
475         _In_ size_t sizeInXChar) throw()
476     {
477         if (psz == NULL) return 0;
478         return (int)wcsnlen(psz, sizeInXChar);
479     }
480 #endif
481 
482 protected:
483     static void __cdecl Concatenate(
484         _Inout_ CSimpleStringT& strResult,
485         _In_count_(nLength1) PCXSTR psz1,
486         _In_ int nLength1,
487         _In_count_(nLength2) PCXSTR psz2,
488         _In_ int nLength2)
489     {
490         int nNewLength = nLength1 + nLength2;
491         PXSTR pszBuffer = strResult.GetBuffer(nNewLength);
492         CopyChars(pszBuffer, nLength1, psz1, nLength1);
493         CopyChars(pszBuffer + nLength1, nLength2, psz2, nLength2);
494         strResult.ReleaseBufferSetLength(nNewLength);
495     }
496 
497 private:
498     void Attach(_Inout_ CStringData* pData) throw()
499     {
500         m_pszData = static_cast<PXSTR>(pData->data());
501     }
502 
503     __declspec(noinline) void Fork(_In_ int nLength)
504     {
505         CStringData* pOldData = GetData();
506         int nOldLength = pOldData->nDataLength;
507         CStringData* pNewData = pOldData->pStringMgr->Clone()->Allocate(nLength, sizeof(XCHAR));
508         if (pNewData == NULL)
509         {
510             ThrowMemoryException();
511         }
512         int nCharsToCopy = ((nOldLength < nLength) ? nOldLength : nLength) + 1;
513         CopyChars(PXSTR(pNewData->data()), nCharsToCopy,
514                   PCXSTR(pOldData->data()), nCharsToCopy);
515         pNewData->nDataLength = nOldLength;
516         pOldData->Release();
517         Attach(pNewData);
518     }
519 
520     PXSTR PrepareWrite(_In_ int nLength)
521     {
522         CStringData* pOldData = GetData();
523         int nShared = 1 - pOldData->nRefs;
524         int nTooShort = pOldData->nAllocLength - nLength;
525         if ((nShared | nTooShort) < 0)
526         {
527             PrepareWrite2(nLength);
528         }
529 
530         return m_pszData;
531     }
532     void PrepareWrite2(_In_ int nLength)
533     {
534         CStringData* pOldData = GetData();
535         if (pOldData->nDataLength > nLength)
536         {
537             nLength = pOldData->nDataLength;
538         }
539         if (pOldData->IsShared())
540         {
541             Fork(nLength);
542             //ATLASSERT(FALSE);
543         }
544         else if (pOldData->nAllocLength < nLength)
545         {
546             int nNewLength = pOldData->nAllocLength;
547             if (nNewLength > 1024 * 1024 * 1024)
548             {
549                 nNewLength += 1024 * 1024;
550             }
551             else
552             {
553                 nNewLength = nNewLength + nNewLength / 2;
554             }
555             if (nNewLength < nLength)
556             {
557                 nNewLength = nLength;
558             }
559             Reallocate(nNewLength);
560         }
561     }
562 
563     void Reallocate(_In_ int nLength)
564     {
565         CStringData* pOldData = GetData();
566         ATLASSERT(pOldData->nAllocLength < nLength);
567         IAtlStringMgr* pStringMgr = pOldData->pStringMgr;
568         if (pOldData->nAllocLength >= nLength || nLength <= 0)
569         {
570             return;
571         }
572         CStringData* pNewData = pStringMgr->Reallocate(pOldData, nLength, sizeof(XCHAR));
573         if (pNewData == NULL)
574         {
575             ThrowMemoryException();
576         }
577 
578         Attach(pNewData);
579     }
580 
581     void SetLength(_In_ int nLength)
582     {
583         ATLASSERT(nLength >= 0);
584         ATLASSERT(nLength <= GetData()->nAllocLength);
585 
586         if (nLength < 0 || nLength > GetData()->nAllocLength)
587         {
588             AtlThrow(E_INVALIDARG);
589         }
590 
591         GetData()->nDataLength = nLength;
592         m_pszData[nLength] = 0;
593     }
594 
595     static CStringData* __cdecl CloneData(_Inout_ CStringData* pData)
596     {
597         CStringData* pNewData = NULL;
598 
599         IAtlStringMgr* pNewStringMgr = pData->pStringMgr->Clone();
600         if (!pData->IsLocked() && (pNewStringMgr == pData->pStringMgr))
601         {
602             pNewData = pData;
603             pNewData->AddRef();
604         }
605         else
606         {
607             pNewData = pNewStringMgr->Allocate(pData->nDataLength, sizeof(XCHAR));
608             if (pNewData == NULL)
609             {
610                 ThrowMemoryException();
611             }
612 
613             pNewData->nDataLength = pData->nDataLength;
614             CopyChars(PXSTR(pNewData->data()), pData->nDataLength + 1,
615                       PCXSTR(pData->data()), pData->nDataLength + 1);
616         }
617 
618         return pNewData;
619     }
620 
621 protected:
622     static void ThrowMemoryException()
623     {
624         AtlThrow(E_OUTOFMEMORY);
625     }
626 
627     static void ThrowInvalidArgException()
628     {
629         AtlThrow(E_INVALIDARG);
630     }
631 };
632 
633 #ifdef UNICODE
634 typedef CSimpleStringT<WCHAR>   CSimpleString;
635 #else
636 typedef CSimpleStringT<CHAR>    CSimpleString;
637 #endif
638 }
639 
640 #endif
641