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
21HRESULT
22CMiniMd::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//*****************************************************************************
65HRESULT
66CMiniMd::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
88ErrExit:
89 return hr;
90} // CMiniMd::InitOnMem
91
92//*****************************************************************************
93// Validate cross-stream consistency.
94//*****************************************************************************
95HRESULT
96CMiniMd::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//*****************************************************************************
105HRESULT
106CMiniMd::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
152ErrExit:
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
164HRESULT
165CMiniMd::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//*****************************************************************************
201HRESULT
202CMiniMd::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
242ErrExit:
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
255HRESULT
256CMiniMd::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
307HRESULT
308CMiniMd::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//*****************************************************************************
396HRESULT
397CMiniMd::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
442ErrExit:
443 return hr;
444} // CMiniMd::CommonGetCustomAttributeByName
445