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 | // StgPoolReadOnly.cpp |
6 | // |
7 | |
8 | // |
9 | // Read only pools are used to reduce the amount of data actually required in the database. |
10 | // |
11 | //***************************************************************************** |
12 | #include "stdafx.h" // Standard include. |
13 | #include <stgpool.h> // Our interface definitions. |
14 | #include "metadatatracker.h" |
15 | // |
16 | // |
17 | // StgPoolReadOnly |
18 | // |
19 | // |
20 | |
21 | #if METADATATRACKER_ENABLED |
22 | MetaDataTracker *MetaDataTracker::m_MDTrackers = NULL; |
23 | BOOL MetaDataTracker::s_bEnabled = FALSE; |
24 | |
25 | void (*MetaDataTracker::s_IBCLogMetaDataAccess)(const void *addr) = NULL; |
26 | void (*MetaDataTracker::s_IBCLogMetaDataSearch)(const void *result) = NULL; |
27 | |
28 | #endif // METADATATRACKER_ENABLED |
29 | |
30 | const BYTE StgPoolSeg::m_zeros[64] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
31 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
32 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
33 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; |
34 | |
35 | |
36 | //***************************************************************************** |
37 | // Free any memory we allocated. |
38 | //***************************************************************************** |
39 | StgPoolReadOnly::~StgPoolReadOnly() |
40 | { |
41 | LIMITED_METHOD_CONTRACT; |
42 | } |
43 | |
44 | |
45 | //***************************************************************************** |
46 | // Init the pool from existing data. |
47 | //***************************************************************************** |
48 | HRESULT StgPoolReadOnly::InitOnMemReadOnly(// Return code. |
49 | void *pData, // Predefined data. |
50 | ULONG iSize) // Size of data. |
51 | { |
52 | CONTRACTL |
53 | { |
54 | NOTHROW; |
55 | INJECT_FAULT(return E_OUTOFMEMORY); |
56 | } |
57 | CONTRACTL_END |
58 | |
59 | // Make sure we aren't stomping anything and are properly initialized. |
60 | _ASSERTE(m_pSegData == m_zeros); |
61 | |
62 | // Create case requires no further action. |
63 | if (pData == NULL) |
64 | return E_INVALIDARG; |
65 | |
66 | // Keep m_zeros data pointer if there's no content of the pool |
67 | if (iSize != 0) |
68 | { |
69 | m_pSegData = reinterpret_cast<BYTE*>(pData); |
70 | } |
71 | m_cbSegSize = iSize; |
72 | m_cbSegNext = iSize; |
73 | return S_OK; |
74 | } |
75 | |
76 | //***************************************************************************** |
77 | // Prepare to shut down or reinitialize. |
78 | //***************************************************************************** |
79 | void StgPoolReadOnly::Uninit() |
80 | { |
81 | LIMITED_METHOD_CONTRACT; |
82 | |
83 | m_pSegData = (BYTE*)m_zeros; |
84 | m_pNextSeg = 0; |
85 | } |
86 | |
87 | |
88 | //***************************************************************************** |
89 | // Convert a string to UNICODE into the caller's buffer. |
90 | //***************************************************************************** |
91 | HRESULT StgPoolReadOnly::GetStringW( // Return code. |
92 | ULONG iOffset, // Offset of string in pool. |
93 | __out_ecount(cchBuffer) LPWSTR szOut, // Output buffer for string. |
94 | int cchBuffer) // Size of output buffer. |
95 | { |
96 | STATIC_CONTRACT_NOTHROW; |
97 | STATIC_CONTRACT_FAULT; |
98 | |
99 | HRESULT hr; |
100 | LPCSTR pString; // The string in UTF8. |
101 | int iChars; |
102 | |
103 | IfFailRet(GetString(iOffset, &pString)); |
104 | iChars = ::WszMultiByteToWideChar(CP_UTF8, 0, pString, -1, szOut, cchBuffer); |
105 | if (iChars == 0) |
106 | return (BadError(HRESULT_FROM_NT(GetLastError()))); |
107 | return S_OK; |
108 | } |
109 | |
110 | //***************************************************************************** |
111 | // Return a pointer to a null terminated blob given an offset previously |
112 | // handed out by Addblob or Findblob. |
113 | //***************************************************************************** |
114 | HRESULT |
115 | StgPoolReadOnly::GetBlob( |
116 | UINT32 nOffset, // Offset of blob in pool. |
117 | MetaData::DataBlob *pData) |
118 | { |
119 | STATIC_CONTRACT_NOTHROW; |
120 | STATIC_CONTRACT_FORBID_FAULT; |
121 | |
122 | HRESULT hr; |
123 | UINT32 cbBlobContentSize; |
124 | |
125 | // This should not be a necessary special case. The zero byte at the |
126 | // start of the pool will code for a length of zero. We will return |
127 | // a pointer to the next length byte, but the caller should notice that |
128 | // the size is zero, and should not look at any bytes. |
129 | // [SL] Yes, but we don't need all further computations and checks if iOffset==0 |
130 | |
131 | if (nOffset == 0) |
132 | { |
133 | pData->Clear(); |
134 | return S_OK; |
135 | } |
136 | |
137 | // Is the offset within this heap? |
138 | if (!IsValidOffset(nOffset)) |
139 | { |
140 | Debug_ReportError("Invalid blob offset." ); |
141 | IfFailGo(CLDB_E_INDEX_NOTFOUND); |
142 | } |
143 | |
144 | IfFailGo(GetDataReadOnly(nOffset, pData)); |
145 | if (!pData->GetCompressedU(&cbBlobContentSize)) |
146 | { |
147 | Debug_ReportError("Invalid blob - size compression." ); |
148 | IfFailGo(COR_E_BADIMAGEFORMAT); |
149 | } |
150 | if (!pData->TruncateToExactSize(cbBlobContentSize)) |
151 | { |
152 | Debug_ReportError("Invalid blob - reaches behind the end of data block." ); |
153 | IfFailGo(COR_E_BADIMAGEFORMAT); |
154 | } |
155 | |
156 | return S_OK; |
157 | ErrExit: |
158 | pData->Clear(); |
159 | return hr; |
160 | } // StgPoolReadOnly::GetBlob |
161 | |
162 | //***************************************************************************** |
163 | // code:StgPoolReadOnly::GetBlob specialization with inlined check for valid offsets to avoid redundant code:StgPoolReadOnly::GetDataReadOnly calls. |
164 | // code:StgPoolReadOnly::GetDataReadOnly is not cheap because of it performs binary lookup in hot metadata. |
165 | //***************************************************************************** |
166 | HRESULT |
167 | StgBlobPoolReadOnly::GetBlob( |
168 | UINT32 nOffset, // Offset of blob in pool. |
169 | MetaData::DataBlob *pData) |
170 | { |
171 | STATIC_CONTRACT_NOTHROW; |
172 | STATIC_CONTRACT_FORBID_FAULT; |
173 | |
174 | HRESULT hr; |
175 | UINT32 cbBlobContentSize; |
176 | |
177 | // This should not be a necessary special case. The zero byte at the |
178 | // start of the pool will code for a length of zero. We will return |
179 | // a pointer to the next length byte, but the caller should notice that |
180 | // the size is zero, and should not look at any bytes. |
181 | // [SL] Yes, but we don't need all further computations and checks if iOffset==0 |
182 | |
183 | if (nOffset == 0) |
184 | { |
185 | pData->Clear(); |
186 | return S_OK; |
187 | } |
188 | |
189 | if (m_pSegData == m_zeros) |
190 | { |
191 | Debug_ReportError("Invalid blob offset." ); |
192 | IfFailGo(CLDB_E_INDEX_NOTFOUND); |
193 | } |
194 | |
195 | IfFailGo(GetDataReadOnly(nOffset, pData)); |
196 | if (!pData->GetCompressedU(&cbBlobContentSize)) |
197 | { |
198 | Debug_ReportError("Invalid blob - size compression." ); |
199 | IfFailGo(CLDB_E_INDEX_NOTFOUND); |
200 | } |
201 | if (!pData->TruncateToExactSize(cbBlobContentSize)) |
202 | { |
203 | Debug_ReportError("Invalid blob - reaches behind the end of data block." ); |
204 | IfFailGo(CLDB_E_INDEX_NOTFOUND); |
205 | } |
206 | |
207 | return S_OK; |
208 | ErrExit: |
209 | pData->Clear(); |
210 | return hr; |
211 | } // StgBlobPoolReadOnly::GetBlob |
212 | |