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