| 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 | |