xref: /reactos/sdk/lib/atl/atlsimpstr.h (revision 190b3da9)
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() noexcept
56     {
57         return (this + 1);
58     }
59 
60     void AddRef() noexcept
61     {
62         ATLASSERT(nRefs > 0);
63         _InterlockedIncrement(&nRefs);
64     }
65 
66     void Release() noexcept
67     {
68         ATLASSERT(nRefs != 0);
69 
70         if (_InterlockedDecrement(&nRefs) <= 0)
71         {
72             pStringMgr->Free(this);
73         }
74     }
75 
76     bool IsLocked() const noexcept
77     {
78         return (nRefs < 0);
79     }
80 
81     bool IsShared() const noexcept
82     {
83         return (nRefs > 1);
84     }
85 };
86 
87 class CNilStringData :
88     public CStringData
89 {
90 public:
91     CNilStringData() noexcept
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) noexcept
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() noexcept
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 noexcept
249     {
250         return m_pszData;
251     }
252 
253     void Empty() noexcept
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 noexcept
358     {
359         return GetData()->nAllocLength;
360     }
361 
362     int GetLength() const noexcept
363     {
364         return GetData()->nDataLength;
365     }
366 
367     PXSTR GetString() noexcept
368     {
369         return m_pszData;
370     }
371     PCXSTR GetString() const noexcept
372     {
373         return m_pszData;
374     }
375 
376     void Preallocate(_In_ int nLength)
377     {
378         PrepareWrite(nLength);
379     }
380 
381     void ReleaseBufferSetLength(_In_ int nNewLength)
382     {
383         ATLASSERT(nNewLength >= 0);
384         SetLength(nNewLength);
385     }
386 
387     void ReleaseBuffer(_In_ int nNewLength = -1)
388     {
389         if (nNewLength < 0)
390             nNewLength = StringLength(m_pszData);
391         ReleaseBufferSetLength(nNewLength);
392     }
393 
394     bool IsEmpty() const noexcept
395     {
396         return (GetLength() == 0);
397     }
398 
399     CStringData* GetData() const noexcept
400     {
401         return (reinterpret_cast<CStringData*>(m_pszData) - 1);
402     }
403 
404     IAtlStringMgr* GetManager() const noexcept
405     {
406         IAtlStringMgr* pStringMgr = GetData()->pStringMgr;
407         return (pStringMgr ? pStringMgr->Clone() : NULL);
408     }
409 
410 public:
411     friend CSimpleStringT operator+(
412         _In_ const CSimpleStringT& str1,
413         _In_ const CSimpleStringT& str2)
414     {
415         CSimpleStringT s(str1.GetManager());
416         Concatenate(s, str1, str1.GetLength(), str2, str2.GetLength());
417         return s;
418     }
419 
420     friend CSimpleStringT operator+(
421         _In_ const CSimpleStringT& str1,
422         _In_z_ PCXSTR psz2)
423     {
424         CSimpleStringT s(str1.GetManager());
425         Concatenate(s, str1, str1.GetLength(), psz2, StringLength(psz2));
426         return s;
427     }
428 
429     friend CSimpleStringT operator+(
430         _In_z_ PCXSTR psz1,
431         _In_ const CSimpleStringT& str2)
432     {
433         CSimpleStringT s(str2.GetManager());
434         Concatenate(s, psz1, StringLength(psz1), str2, str2.GetLength());
435         return s;
436     }
437 
438     static void __cdecl CopyChars(
439         _Out_writes_to_(nDestLen, nChars) XCHAR* pchDest,
440         _In_ size_t nDestLen,
441         _In_reads_opt_(nChars) const XCHAR* pchSrc,
442         _In_ int nChars) noexcept
443     {
444         memcpy(pchDest, pchSrc, nChars * sizeof(XCHAR));
445     }
446 
447     static void __cdecl CopyCharsOverlapped(
448         _Out_writes_to_(nDestLen, nDestLen) XCHAR* pchDest,
449         _In_ size_t nDestLen,
450         _In_reads_(nChars) const XCHAR* pchSrc,
451         _In_ int nChars) noexcept
452     {
453         memmove(pchDest, pchSrc, nChars * sizeof(XCHAR));
454     }
455 
456     static int __cdecl StringLength(_In_opt_z_ const char* psz) noexcept
457     {
458         if (psz == NULL) return 0;
459         return (int)strlen(psz);
460     }
461 
462     static int __cdecl StringLength(_In_opt_z_ const wchar_t* psz) noexcept
463     {
464         if (psz == NULL) return 0;
465         return (int)wcslen(psz);
466     }
467 
468 #if 0 // For whatever reason we do not link with strnlen / wcsnlen. Please investigate!
469       // strnlen / wcsnlen are available in MSVCRT starting Vista+.
470     static int __cdecl StringLengthN(
471         _In_opt_z_count_(sizeInXChar) const char* psz,
472         _In_ size_t sizeInXChar) noexcept
473     {
474         if (psz == NULL) return 0;
475         return (int)strnlen(psz, sizeInXChar);
476     }
477 
478     static int __cdecl StringLengthN(
479         _In_opt_z_count_(sizeInXChar) const wchar_t* psz,
480         _In_ size_t sizeInXChar) noexcept
481     {
482         if (psz == NULL) return 0;
483         return (int)wcsnlen(psz, sizeInXChar);
484     }
485 #endif
486 
487 protected:
488     static void __cdecl Concatenate(
489         _Inout_ CSimpleStringT& strResult,
490         _In_count_(nLength1) PCXSTR psz1,
491         _In_ int nLength1,
492         _In_count_(nLength2) PCXSTR psz2,
493         _In_ int nLength2)
494     {
495         int nNewLength = nLength1 + nLength2;
496         PXSTR pszBuffer = strResult.GetBuffer(nNewLength);
497         CopyChars(pszBuffer, nLength1, psz1, nLength1);
498         CopyChars(pszBuffer + nLength1, nLength2, psz2, nLength2);
499         strResult.ReleaseBufferSetLength(nNewLength);
500     }
501 
502 private:
503     void Attach(_Inout_ CStringData* pData) noexcept
504     {
505         m_pszData = static_cast<PXSTR>(pData->data());
506     }
507 
508     __declspec(noinline) void Fork(_In_ int nLength)
509     {
510         CStringData* pOldData = GetData();
511         int nOldLength = pOldData->nDataLength;
512         CStringData* pNewData = pOldData->pStringMgr->Clone()->Allocate(nLength, sizeof(XCHAR));
513         if (pNewData == NULL)
514         {
515             ThrowMemoryException();
516         }
517         int nCharsToCopy = ((nOldLength < nLength) ? nOldLength : nLength) + 1;
518         CopyChars(PXSTR(pNewData->data()), nCharsToCopy,
519                   PCXSTR(pOldData->data()), nCharsToCopy);
520         pNewData->nDataLength = nOldLength;
521         pOldData->Release();
522         Attach(pNewData);
523     }
524 
525     PXSTR PrepareWrite(_In_ int nLength)
526     {
527         CStringData* pOldData = GetData();
528         int nShared = 1 - pOldData->nRefs;
529         int nTooShort = pOldData->nAllocLength - nLength;
530         if ((nShared | nTooShort) < 0)
531         {
532             PrepareWrite2(nLength);
533         }
534 
535         return m_pszData;
536     }
537     void PrepareWrite2(_In_ int nLength)
538     {
539         CStringData* pOldData = GetData();
540         if (pOldData->nDataLength > nLength)
541         {
542             nLength = pOldData->nDataLength;
543         }
544         if (pOldData->IsShared())
545         {
546             Fork(nLength);
547             //ATLASSERT(FALSE);
548         }
549         else if (pOldData->nAllocLength < nLength)
550         {
551             int nNewLength = pOldData->nAllocLength;
552             if (nNewLength > 1024 * 1024 * 1024)
553             {
554                 nNewLength += 1024 * 1024;
555             }
556             else
557             {
558                 nNewLength = nNewLength + nNewLength / 2;
559             }
560             if (nNewLength < nLength)
561             {
562                 nNewLength = nLength;
563             }
564             Reallocate(nNewLength);
565         }
566     }
567 
568     void Reallocate(_In_ int nLength)
569     {
570         CStringData* pOldData = GetData();
571         ATLASSERT(pOldData->nAllocLength < nLength);
572         IAtlStringMgr* pStringMgr = pOldData->pStringMgr;
573         if (pOldData->nAllocLength >= nLength || nLength <= 0)
574         {
575             return;
576         }
577         CStringData* pNewData = pStringMgr->Reallocate(pOldData, nLength, sizeof(XCHAR));
578         if (pNewData == NULL)
579         {
580             ThrowMemoryException();
581         }
582 
583         Attach(pNewData);
584     }
585 
586     void SetLength(_In_ int nLength)
587     {
588         ATLASSERT(nLength >= 0);
589         ATLASSERT(nLength <= GetData()->nAllocLength);
590 
591         if (nLength < 0 || nLength > GetData()->nAllocLength)
592         {
593             AtlThrow(E_INVALIDARG);
594         }
595 
596         GetData()->nDataLength = nLength;
597         m_pszData[nLength] = 0;
598     }
599 
600     static CStringData* __cdecl CloneData(_Inout_ CStringData* pData)
601     {
602         CStringData* pNewData = NULL;
603 
604         IAtlStringMgr* pNewStringMgr = pData->pStringMgr->Clone();
605         if (!pData->IsLocked() && (pNewStringMgr == pData->pStringMgr))
606         {
607             pNewData = pData;
608             pNewData->AddRef();
609         }
610         else
611         {
612             pNewData = pNewStringMgr->Allocate(pData->nDataLength, sizeof(XCHAR));
613             if (pNewData == NULL)
614             {
615                 ThrowMemoryException();
616             }
617 
618             pNewData->nDataLength = pData->nDataLength;
619             CopyChars(PXSTR(pNewData->data()), pData->nDataLength + 1,
620                       PCXSTR(pData->data()), pData->nDataLength + 1);
621         }
622 
623         return pNewData;
624     }
625 
626 protected:
627     static void ThrowMemoryException()
628     {
629         AtlThrow(E_OUTOFMEMORY);
630     }
631 
632     static void ThrowInvalidArgException()
633     {
634         AtlThrow(E_INVALIDARG);
635     }
636 };
637 
638 #ifdef UNICODE
639 typedef CSimpleStringT<WCHAR>   CSimpleString;
640 #else
641 typedef CSimpleStringT<CHAR>    CSimpleString;
642 #endif
643 }
644 
645 #endif
646