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 | // MetaModelRO.cpp -- Read-only implementation of compressed COM+ metadata. |
6 | // |
7 | |
8 | // |
9 | //***************************************************************************** |
10 | #include "stdafx.h" |
11 | |
12 | #include "metamodelro.h" |
13 | #include <posterror.h> |
14 | #include <corerror.h> |
15 | #include "metadatatracker.h" |
16 | |
17 | //***************************************************************************** |
18 | // Set the pointers to consecutive areas of a large buffer. |
19 | //***************************************************************************** |
20 | __checkReturn |
21 | HRESULT |
22 | CMiniMd::InitializeTables( |
23 | MetaData::DataBlob tablesData) |
24 | { |
25 | HRESULT hr; |
26 | |
27 | for (int i = 0; i < TBL_COUNT; i++) |
28 | { |
29 | // This table data |
30 | MetaData::DataBlob tableData; |
31 | |
32 | S_UINT32 cbTableSize = |
33 | S_UINT32(m_TableDefs[i].m_cbRec) * |
34 | S_UINT32(m_Schema.m_cRecs[i]); |
35 | if (cbTableSize.IsOverflow()) |
36 | { |
37 | Debug_ReportError("Table is too large - size overflow." ); |
38 | return CLDB_E_FILE_CORRUPT; |
39 | } |
40 | if (!tablesData.GetDataOfSize(cbTableSize.Value(), &tableData)) |
41 | { |
42 | Debug_ReportError("Table is not within MetaData tables block." ); |
43 | return CLDB_E_FILE_CORRUPT; |
44 | } |
45 | _ASSERTE(cbTableSize.Value() == tableData.GetSize()); |
46 | |
47 | METADATATRACKER_ONLY(MetaDataTracker::NoteSection( |
48 | i, |
49 | tableData.GetDataPointer(), |
50 | tableData.GetSize(), |
51 | m_TableDefs[i].m_cbRec)); |
52 | |
53 | IfFailRet(m_Tables[i].Initialize( |
54 | m_TableDefs[i].m_cbRec, |
55 | tableData, |
56 | FALSE)); // fCopyData |
57 | } |
58 | |
59 | return S_OK; |
60 | } // CMiniMd::SetTablePointers |
61 | |
62 | //***************************************************************************** |
63 | // Given a buffer that contains a MiniMd, init to read it. |
64 | //***************************************************************************** |
65 | HRESULT |
66 | CMiniMd::InitOnMem( |
67 | void *pvBuf, // The buffer. |
68 | ULONG ulBufLen) // Size of the buffer.. |
69 | { |
70 | HRESULT hr = S_OK; |
71 | ULONG cbData; |
72 | BYTE *pBuf = reinterpret_cast<BYTE*>(pvBuf); |
73 | |
74 | // Uncompress the schema from the buffer into our structures. |
75 | IfFailGo(SchemaPopulate(pvBuf, ulBufLen, &cbData)); |
76 | PREFAST_ASSUME(cbData <= ulBufLen); |
77 | |
78 | // There shouldn't be any pointer tables. |
79 | if ((m_Schema.m_cRecs[TBL_MethodPtr] != 0) || (m_Schema.m_cRecs[TBL_FieldPtr] != 0)) |
80 | { |
81 | Debug_ReportError("MethodPtr and FieldPtr tables are not allowed in Read-Only format." ); |
82 | return PostError(CLDB_E_FILE_CORRUPT); |
83 | } |
84 | |
85 | // initialize the pointers to the rest of the data. |
86 | IfFailGo(InitializeTables(MetaData::DataBlob(pBuf + Align4(cbData), ulBufLen-cbData))); |
87 | |
88 | ErrExit: |
89 | return hr; |
90 | } // CMiniMd::InitOnMem |
91 | |
92 | //***************************************************************************** |
93 | // Validate cross-stream consistency. |
94 | //***************************************************************************** |
95 | HRESULT |
96 | CMiniMd::PostInit( |
97 | int iLevel) |
98 | { |
99 | return S_OK; |
100 | } // CMiniMd::PostInit |
101 | |
102 | //***************************************************************************** |
103 | // converting a ANSI heap string to unicode string to an output buffer |
104 | //***************************************************************************** |
105 | HRESULT |
106 | CMiniMd::Impl_GetStringW( |
107 | ULONG ix, |
108 | __inout_ecount (cchBuffer) LPWSTR szOut, |
109 | ULONG cchBuffer, |
110 | ULONG *pcchBuffer) |
111 | { |
112 | LPCSTR szString; // Single byte version. |
113 | int iSize; // Size of resulting string, in wide chars. |
114 | HRESULT hr = NOERROR; |
115 | |
116 | IfFailGo(getString(ix, &szString)); |
117 | |
118 | if (*szString == 0) |
119 | { |
120 | // If emtpy string "", return pccBuffer 0 |
121 | if ((szOut != NULL) && (cchBuffer != 0)) |
122 | szOut[0] = W('\0'); |
123 | if (pcchBuffer != NULL) |
124 | *pcchBuffer = 0; |
125 | goto ErrExit; |
126 | } |
127 | iSize = ::WszMultiByteToWideChar(CP_UTF8, 0, szString, -1, szOut, cchBuffer); |
128 | if (iSize == 0) |
129 | { |
130 | // What was the problem? |
131 | DWORD dwNT = GetLastError(); |
132 | |
133 | // Not truncation? |
134 | if (dwNT != ERROR_INSUFFICIENT_BUFFER) |
135 | IfFailGo(HRESULT_FROM_NT(dwNT)); |
136 | |
137 | // Truncation error; get the size required. |
138 | if (pcchBuffer != NULL) |
139 | *pcchBuffer = ::WszMultiByteToWideChar(CP_UTF8, 0, szString, -1, NULL, 0); |
140 | |
141 | if ((szOut != NULL) && (cchBuffer > 0)) |
142 | { // null-terminate the truncated output string |
143 | szOut[cchBuffer - 1] = W('\0'); |
144 | } |
145 | |
146 | hr = CLDB_S_TRUNCATION; |
147 | goto ErrExit; |
148 | } |
149 | if (pcchBuffer != NULL) |
150 | *pcchBuffer = iSize; |
151 | |
152 | ErrExit: |
153 | return hr; |
154 | } // CMiniMd::Impl_GetStringW |
155 | |
156 | |
157 | //***************************************************************************** |
158 | // Given a table with a pointer (index) to a sequence of rows in another |
159 | // table, get the RID of the end row. This is the STL-ish end; the first row |
160 | // not in the list. Thus, for a list of 0 elements, the start and end will |
161 | // be the same. |
162 | //***************************************************************************** |
163 | __checkReturn |
164 | HRESULT |
165 | CMiniMd::Impl_GetEndRidForColumn( // The End rid. |
166 | UINT32 nTableIndex, |
167 | RID nRowIndex, |
168 | CMiniColDef &def, // Column containing the RID into other table. |
169 | UINT32 nTargetTableIndex, // The other table. |
170 | RID *pEndRid) |
171 | { |
172 | HRESULT hr; |
173 | _ASSERTE(nTableIndex < TBL_COUNT); |
174 | RID nLastRowIndex = m_Schema.m_cRecs[nTableIndex]; |
175 | |
176 | // Last rid in range from NEXT record, or count of table, if last record. |
177 | if (nRowIndex < nLastRowIndex) |
178 | { |
179 | BYTE *pRow; |
180 | IfFailRet(Impl_GetRow(nTableIndex, nRowIndex + 1, &pRow)); |
181 | *pEndRid = getIX(pRow, def); |
182 | } |
183 | else // Convert count to 1-based rid. |
184 | { |
185 | if (nRowIndex != nLastRowIndex) |
186 | { |
187 | Debug_ReportError("Invalid table row index." ); |
188 | IfFailRet(METADATA_E_INDEX_NOTFOUND); |
189 | } |
190 | _ASSERTE(nTargetTableIndex < TBL_COUNT); |
191 | *pEndRid = m_Schema.m_cRecs[nTargetTableIndex] + 1; |
192 | } |
193 | |
194 | return S_OK; |
195 | } // CMiniMd::Impl_GetEndRidForColumn |
196 | |
197 | |
198 | //***************************************************************************** |
199 | // return all found CAs in an enumerator |
200 | //***************************************************************************** |
201 | HRESULT |
202 | CMiniMd::CommonEnumCustomAttributeByName( |
203 | mdToken tkObj, // [IN] Object with Custom Attribute. |
204 | LPCUTF8 szName, // [IN] Name of desired Custom Attribute. |
205 | bool fStopAtFirstFind, // [IN] just find the first one |
206 | HENUMInternal *phEnum) // enumerator to fill up |
207 | { |
208 | HRESULT hr = S_OK; |
209 | HRESULT hrRet = S_FALSE; // Assume that we won't find any |
210 | ULONG ridStart, ridEnd; // Loop start and endpoints. |
211 | |
212 | _ASSERTE(phEnum != NULL); |
213 | |
214 | memset(phEnum, 0, sizeof(HENUMInternal)); |
215 | |
216 | HENUMInternal::InitDynamicArrayEnum(phEnum); |
217 | |
218 | phEnum->m_tkKind = mdtCustomAttribute; |
219 | |
220 | // Get the list of custom values for the parent object. |
221 | |
222 | IfFailGo(getCustomAttributeForToken(tkObj, &ridEnd, &ridStart)); |
223 | if (ridStart == 0) |
224 | return S_FALSE; |
225 | |
226 | // Look for one with the given name. |
227 | for (; ridStart < ridEnd; ++ridStart) |
228 | { |
229 | IfFailGoto(CompareCustomAttribute( tkObj, szName, ridStart), ErrExit); |
230 | if (hr == S_OK) |
231 | { |
232 | // If here, found a match. |
233 | hrRet = S_OK; |
234 | IfFailGo(HENUMInternal::AddElementToEnum( |
235 | phEnum, |
236 | TokenFromRid(ridStart, mdtCustomAttribute))); |
237 | if (fStopAtFirstFind) |
238 | goto ErrExit; |
239 | } |
240 | } |
241 | |
242 | ErrExit: |
243 | if (FAILED(hr)) |
244 | return hr; |
245 | return hrRet; |
246 | } // CMiniMd::CommonEnumCustomAttributeByName |
247 | |
248 | |
249 | //***************************************************************************** |
250 | // Search a table for the row containing the given key value. |
251 | // EG. Constant table has pointer back to Param or Field. |
252 | // |
253 | //***************************************************************************** |
254 | __checkReturn |
255 | HRESULT |
256 | CMiniMd::vSearchTable( |
257 | ULONG ixTbl, // Table to search. |
258 | CMiniColDef sColumn, // Sorted key column, containing search value. |
259 | ULONG ulTarget, // Target for search. |
260 | RID *pRid) // RID of matching row, or 0. |
261 | { |
262 | HRESULT hr; |
263 | void *pRow = NULL; // Row from a table. |
264 | ULONG val; // Value from a row. |
265 | int lo, mid, hi; // binary search indices. |
266 | |
267 | // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
268 | // If you change the rows touched while searching, please update |
269 | // CMiniMdRW::GetHotMetadataTokensSearchAware |
270 | // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
271 | |
272 | // Start with entire table. |
273 | lo = 1; |
274 | hi = GetCountRecs(ixTbl); |
275 | // While there are rows in the range... |
276 | while (lo <= hi) |
277 | { // Look at the one in the middle. |
278 | mid = (lo + hi) / 2; |
279 | IfFailRet(getRow(ixTbl, mid, &pRow)); |
280 | val = getIX_NoLogging(pRow, sColumn); |
281 | // If equal to the target, done. |
282 | if (val == ulTarget) |
283 | { |
284 | *pRid = mid; |
285 | METADATATRACKER_ONLY(MetaDataTracker::NoteSearch(pRow)); |
286 | return S_OK; |
287 | } |
288 | // If middle item is too small, search the top half. |
289 | if (val < ulTarget) |
290 | lo = mid + 1; |
291 | else // but if middle is to big, search bottom half. |
292 | hi = mid - 1; |
293 | } |
294 | // Didn't find anything that matched. |
295 | *pRid = 0; |
296 | |
297 | METADATATRACKER_ONLY(MetaDataTracker::NoteSearch(pRow)); |
298 | return S_OK; |
299 | } // CMiniMd::vSearchTable |
300 | |
301 | //***************************************************************************** |
302 | // Search a table for the highest-RID row containing a value that is less than |
303 | // or equal to the target value. EG. TypeDef points to first Field, but if |
304 | // a TypeDef has no fields, it points to first field of next TypeDef. |
305 | //***************************************************************************** |
306 | __checkReturn |
307 | HRESULT |
308 | CMiniMd::vSearchTableNotGreater( |
309 | ULONG ixTbl, // Table to search. |
310 | CMiniColDef sColumn, // the column def containing search value |
311 | ULONG ulTarget, // target for search |
312 | RID *pRid) // RID of matching row, or 0 |
313 | { |
314 | HRESULT hr; |
315 | void *pRow = NULL; // Row from a table. |
316 | ULONG cRecs; // Rows in the table. |
317 | ULONG val = 0; // Value from a table. |
318 | ULONG lo, mid = 0, hi; // binary search indices. |
319 | |
320 | cRecs = GetCountRecs(ixTbl); |
321 | |
322 | // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
323 | // If you change the rows touched while searching, please update |
324 | // CMiniMdRW::GetHotMetadataTokensSearchAware |
325 | // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
326 | |
327 | // Start with entire table. |
328 | lo = 1; |
329 | hi = cRecs; |
330 | // If no recs, return. |
331 | if (lo > hi) |
332 | { |
333 | *pRid = 0; |
334 | return S_OK; |
335 | } |
336 | // While there are rows in the range... |
337 | while (lo <= hi) |
338 | { // Look at the one in the middle. |
339 | mid = (lo + hi) / 2; |
340 | IfFailRet(getRow(ixTbl, mid, &pRow)); |
341 | val = getIX_NoLogging(pRow, sColumn); |
342 | // If equal to the target, done searching. |
343 | if (val == ulTarget) |
344 | break; |
345 | // If middle item is too small, search the top half. |
346 | if (val < ulTarget) |
347 | lo = mid + 1; |
348 | else // but if middle is to big, search bottom half. |
349 | hi = mid - 1; |
350 | } |
351 | |
352 | METADATATRACKER_ONLY(MetaDataTracker::NoteSearch(pRow)); |
353 | |
354 | // May or may not have found anything that matched. Mid will be close, but may |
355 | // be to high or too low. It should point to the highest acceptable |
356 | // record. |
357 | |
358 | // If the value is greater than the target, back up just until the value is |
359 | // less than or equal to the target. SHOULD only be one step. |
360 | if (val > ulTarget) |
361 | { |
362 | while (val > ulTarget) |
363 | { |
364 | // If there is nothing else to look at, we won't find it. |
365 | if (--mid < 1) |
366 | break; |
367 | IfFailRet(getRow(ixTbl, mid, &pRow)); |
368 | val = getIX(pRow, sColumn); |
369 | } |
370 | } |
371 | else |
372 | { |
373 | // Value is less than or equal to the target. As long as the next |
374 | // record is also acceptable, move forward. |
375 | while (mid < cRecs) |
376 | { |
377 | // There is another record. Get its value. |
378 | IfFailRet(getRow(ixTbl, mid+1, &pRow)); |
379 | val = getIX(pRow, sColumn); |
380 | // If that record is too high, stop. |
381 | if (val > ulTarget) |
382 | break; |
383 | mid++; |
384 | } |
385 | } |
386 | |
387 | // Return the value that's just less than the target. |
388 | *pRid = mid; |
389 | return S_OK; |
390 | } // CMiniMd::vSearchTableNotGreater |
391 | |
392 | //***************************************************************************** |
393 | // return just the blob value of the first found CA matching the query. |
394 | // returns S_FALSE if there is no match |
395 | //***************************************************************************** |
396 | HRESULT |
397 | CMiniMd::CommonGetCustomAttributeByNameEx( |
398 | mdToken tkObj, // [IN] Object with Custom Attribute. |
399 | LPCUTF8 szName, // [IN] Name of desired Custom Attribute. |
400 | mdCustomAttribute *ptkCA, // [OUT] put custom attribute token here |
401 | const void **ppData, // [OUT] Put pointer to data here. |
402 | ULONG *pcbData) // [OUT] Put size of data here. |
403 | { |
404 | HRESULT hr; |
405 | |
406 | ULONG cbData; |
407 | CustomAttributeRec *pRec; |
408 | |
409 | ULONG ridStart, ridEnd; // Loop start and endpoints. |
410 | |
411 | // Get the list of custom values for the parent object. |
412 | |
413 | IfFailGo(getCustomAttributeForToken(tkObj, &ridEnd, &ridStart)); |
414 | |
415 | hr = S_FALSE; |
416 | if (ridStart == 0) |
417 | { |
418 | goto ErrExit; |
419 | } |
420 | |
421 | // Look for one with the given name. |
422 | for (; ridStart < ridEnd; ++ridStart) |
423 | { |
424 | IfFailGoto(CompareCustomAttribute( tkObj, szName, ridStart), ErrExit); |
425 | if (hr == S_OK) |
426 | { |
427 | if (ppData != NULL) |
428 | { |
429 | // now get the record out. |
430 | if (pcbData == NULL) |
431 | pcbData = &cbData; |
432 | |
433 | IfFailGo(GetCustomAttributeRecord(ridStart, &pRec)); |
434 | IfFailGo(getValueOfCustomAttribute(pRec, reinterpret_cast<const BYTE **>(ppData), pcbData)); |
435 | if (ptkCA) |
436 | *ptkCA = TokenFromRid(mdtCustomAttribute, ridStart); |
437 | } |
438 | break; |
439 | } |
440 | } |
441 | |
442 | ErrExit: |
443 | return hr; |
444 | } // CMiniMd::CommonGetCustomAttributeByName |
445 | |