1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5
6/*============================================================
7**
8** Header: Map used for interning of string literals.
9**
10===========================================================*/
11
12#ifndef _STRINGLITERALMAP_H
13#define _STRINGLITERALMAP_H
14
15#include "vars.hpp"
16#include "appdomain.hpp"
17#include "eehash.h"
18#include "eeconfig.h" // For OS pages size
19#include "memorypool.h"
20
21
22class StringLiteralEntry;
23// Allocate 16 entries (approx size sizeof(StringLiteralEntry)*16)
24#define MAX_ENTRIES_PER_CHUNK 16
25
26STRINGREF AllocateStringObject(EEStringData *pStringData);
27
28// Loader allocator specific string literal map.
29class StringLiteralMap
30{
31public:
32 // Constructor and destructor.
33 StringLiteralMap();
34 ~StringLiteralMap();
35
36 // Initialization method.
37 void Init();
38
39 size_t GetSize()
40 {
41 LIMITED_METHOD_CONTRACT;
42 return m_MemoryPool?m_MemoryPool->GetSize():0;
43 }
44
45 // Method to retrieve a string from the map.
46 STRINGREF *GetStringLiteral(EEStringData *pStringData, BOOL bAddIfNotFound, BOOL bAppDomainWontUnload);
47
48 // Method to explicitly intern a string object.
49 STRINGREF *GetInternedString(STRINGREF *pString, BOOL bAddIfNotFound, BOOL bAppDomainWontUnload);
50
51private:
52 // Hash tables that maps a Unicode string to a COM+ string handle.
53 EEUnicodeStringLiteralHashTable *m_StringToEntryHashTable;
54
55 // The memorypool for hash entries for this hash table.
56 MemoryPool *m_MemoryPool;
57};
58
59// Global string literal map.
60class GlobalStringLiteralMap
61{
62 // StringLiteralMap and StringLiteralEntry need to acquire the crst of the global string literal map.
63 friend class StringLiteralMap;
64 friend class StringLiteralEntry;
65
66public:
67 // Constructor and destructor.
68 GlobalStringLiteralMap();
69 ~GlobalStringLiteralMap();
70
71 // Initialization method.
72 void Init();
73
74 // Method to retrieve a string from the map. Takes a precomputed hash (for perf).
75 StringLiteralEntry *GetStringLiteral(EEStringData *pStringData, DWORD dwHash, BOOL bAddIfNotFound);
76
77 // Method to explicitly intern a string object. Takes a precomputed hash (for perf).
78 StringLiteralEntry *GetInternedString(STRINGREF *pString, DWORD dwHash, BOOL bAddIfNotFound);
79
80 // Method to calculate the hash
81 DWORD GetHash(EEStringData* pData)
82 {
83 WRAPPER_NO_CONTRACT;
84 return m_StringToEntryHashTable->GetHash(pData);
85 }
86
87 // public method to retrieve m_HashTableCrstGlobal
88 Crst* GetHashTableCrstGlobal()
89 {
90 LIMITED_METHOD_CONTRACT;
91 return &m_HashTableCrstGlobal;
92 }
93
94private:
95 // Helper method to add a string to the global string literal map.
96 StringLiteralEntry *AddStringLiteral(EEStringData *pStringData);
97
98 // Helper method to add an interned string.
99 StringLiteralEntry *AddInternedString(STRINGREF *pString);
100
101 // Called by StringLiteralEntry when its RefCount falls to 0.
102 void RemoveStringLiteralEntry(StringLiteralEntry *pEntry);
103
104 // Hash tables that maps a Unicode string to a LiteralStringEntry.
105 EEUnicodeStringLiteralHashTable *m_StringToEntryHashTable;
106
107 // The memorypool for hash entries for this hash table.
108 MemoryPool *m_MemoryPool;
109
110 // The hash table table critical section.
111 // (the Global suffix is so that it is clear in context whether the global table is being locked
112 // or the per app domain table is being locked. Sometimes there was confusion in the code
113 // changing the name of the global one will avoid this problem and prevent copy/paste errors)
114
115 Crst m_HashTableCrstGlobal;
116
117 // The large heap handle table.
118 LargeHeapHandleTable m_LargeHeapHandleTable;
119
120};
121
122class StringLiteralEntryArray;
123
124// Ref counted entry representing a string literal.
125class StringLiteralEntry
126{
127private:
128 StringLiteralEntry(EEStringData *pStringData, STRINGREF *pStringObj)
129 : m_pStringObj(pStringObj), m_dwRefCount(1)
130#ifdef _DEBUG
131 , m_bDeleted(FALSE)
132#endif
133 {
134 LIMITED_METHOD_CONTRACT;
135 }
136protected:
137 ~StringLiteralEntry()
138 {
139 CONTRACTL
140 {
141 NOTHROW;
142 GC_NOTRIGGER;
143 PRECONDITION(CheckPointer<void>(this));
144 }
145 CONTRACTL_END;
146 }
147
148public:
149 void AddRef()
150 {
151 CONTRACTL
152 {
153 NOTHROW;
154 GC_NOTRIGGER;
155 PRECONDITION(CheckPointer<void>(this));
156 PRECONDITION((LONG)VolatileLoad(&m_dwRefCount) > 0);
157 PRECONDITION(SystemDomain::GetGlobalStringLiteralMapNoCreate()->m_HashTableCrstGlobal.OwnedByCurrentThread());
158 }
159 CONTRACTL_END;
160
161 _ASSERTE (!m_bDeleted);
162
163 // We will keep the item alive forever if the refcount overflowed
164 if ((LONG)VolatileLoad(&m_dwRefCount) < 0)
165 return;
166
167 VolatileStore(&m_dwRefCount, VolatileLoad(&m_dwRefCount) + 1);
168 }
169#ifndef DACCESS_COMPILE
170 FORCEINLINE static void StaticRelease(StringLiteralEntry* pEntry)
171 {
172 CONTRACTL
173 {
174 PRECONDITION(SystemDomain::GetGlobalStringLiteralMapNoCreate()->m_HashTableCrstGlobal.OwnedByCurrentThread());
175 }
176 CONTRACTL_END;
177
178 pEntry->Release();
179 }
180#else
181 FORCEINLINE static void StaticRelease(StringLiteralEntry* /* pEntry */)
182 {
183 WRAPPER_NO_CONTRACT;
184 DacNotImpl();
185 }
186#endif // DACCESS_COMPILE
187
188#ifndef DACCESS_COMPILE
189 void Release()
190 {
191 CONTRACTL
192 {
193 NOTHROW;
194 GC_NOTRIGGER;
195 PRECONDITION(CheckPointer<void>(this));
196 PRECONDITION(VolatileLoad(&m_dwRefCount) > 0);
197 PRECONDITION(SystemDomain::GetGlobalStringLiteralMapNoCreate()->m_HashTableCrstGlobal.OwnedByCurrentThread());
198 }
199 CONTRACTL_END;
200
201 // We will keep the item alive forever if the refcount overflowed
202 if ((LONG)VolatileLoad(&m_dwRefCount) < 0)
203 return;
204
205 VolatileStore(&m_dwRefCount, VolatileLoad(&m_dwRefCount) - 1);
206 if (VolatileLoad(&m_dwRefCount) == 0)
207 {
208 _ASSERTE(SystemDomain::GetGlobalStringLiteralMapNoCreate());
209 SystemDomain::GetGlobalStringLiteralMapNoCreate()->RemoveStringLiteralEntry(this);
210 // Puts this entry in the free list
211 DeleteEntry (this);
212 }
213 }
214#endif // DACCESS_COMPILE
215
216 LONG GetRefCount()
217 {
218 CONTRACTL
219 {
220 NOTHROW;
221 if(GetThread()){GC_NOTRIGGER;}else{DISABLED(GC_TRIGGERS);};
222 PRECONDITION(CheckPointer(this));
223 }
224 CONTRACTL_END;
225
226 _ASSERTE (!m_bDeleted);
227
228 return (VolatileLoad(&m_dwRefCount));
229 }
230
231 STRINGREF* GetStringObject()
232 {
233 CONTRACTL
234 {
235 NOTHROW;
236 if(GetThread()){GC_NOTRIGGER;}else{DISABLED(GC_TRIGGERS);};
237 PRECONDITION(CheckPointer(this));
238 }
239 CONTRACTL_END;
240 return m_pStringObj;
241 }
242
243 void GetStringData(EEStringData *pStringData)
244 {
245 CONTRACTL
246 {
247 NOTHROW;
248 if(GetThread()){GC_NOTRIGGER;}else{DISABLED(GC_TRIGGERS);};
249 MODE_COOPERATIVE;
250 PRECONDITION(CheckPointer(this));
251 PRECONDITION(CheckPointer(pStringData));
252 }
253 CONTRACTL_END;
254
255 WCHAR *thisChars;
256 int thisLength;
257
258 ObjectToSTRINGREF(*(StringObject**)m_pStringObj)->RefInterpretGetStringValuesDangerousForGC(&thisChars, &thisLength);
259 pStringData->SetCharCount (thisLength); // thisLength is in WCHARs and that's what EEStringData's char count wants
260 pStringData->SetStringBuffer (thisChars);
261 }
262
263 static StringLiteralEntry *AllocateEntry(EEStringData *pStringData, STRINGREF *pStringObj);
264 static void DeleteEntry (StringLiteralEntry *pEntry);
265
266private:
267 STRINGREF* m_pStringObj;
268 union
269 {
270 DWORD m_dwRefCount;
271 StringLiteralEntry *m_pNext;
272 };
273
274#ifdef _DEBUG
275 BOOL m_bDeleted;
276#endif
277
278 // The static lists below are protected by GetGlobalStringLiteralMap()->m_HashTableCrstGlobal
279 static StringLiteralEntryArray *s_EntryList; // always the first entry array in the chain.
280 static DWORD s_UsedEntries; // number of entries used up in the first array
281 static StringLiteralEntry *s_FreeEntryList; // free list chained thru the arrays.
282};
283
284typedef Wrapper<StringLiteralEntry*,DoNothing,StringLiteralEntry::StaticRelease> StringLiteralEntryHolder;
285
286class StringLiteralEntryArray
287{
288public:
289 StringLiteralEntryArray *m_pNext;
290 BYTE m_Entries[MAX_ENTRIES_PER_CHUNK*sizeof(StringLiteralEntry)];
291};
292
293#endif // _STRINGLITERALMAP_H
294
295