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