summaryrefslogtreecommitdiff
path: root/src/md/enc/rwutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/md/enc/rwutil.cpp')
-rw-r--r--src/md/enc/rwutil.cpp1440
1 files changed, 1440 insertions, 0 deletions
diff --git a/src/md/enc/rwutil.cpp b/src/md/enc/rwutil.cpp
new file mode 100644
index 0000000000..874d972716
--- /dev/null
+++ b/src/md/enc/rwutil.cpp
@@ -0,0 +1,1440 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// RWUtil.cpp
+//
+
+//
+// contains utility code to MD directory
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "metadata.h"
+#include "rwutil.h"
+#include "utsem.h"
+#include "../inc/mdlog.h"
+
+//*****************************************************************************
+// Helper methods
+//*****************************************************************************
+void
+Unicode2UTF(
+ LPCWSTR wszSrc, // The string to convert.
+ __out_ecount(cbDst)
+ LPUTF8 szDst, // Buffer for the output UTF8 string.
+ int cbDst) // Size of the buffer for UTF8 string.
+{
+ int cchSrc = (int)wcslen(wszSrc);
+ int cchRet;
+
+ cchRet = WszWideCharToMultiByte(
+ CP_UTF8,
+ 0,
+ wszSrc,
+ cchSrc + 1,
+ szDst,
+ cbDst,
+ NULL,
+ NULL);
+
+ if (cchRet == 0)
+ {
+ _ASSERTE_MSG(FALSE, "Converting unicode string to UTF8 string failed!");
+ szDst[0] = '\0';
+ }
+} // Unicode2UTF
+
+
+HRESULT HENUMInternal::CreateSimpleEnum(
+ DWORD tkKind, // kind of token that we are iterating
+ ULONG ridStart, // starting rid
+ ULONG ridEnd, // end rid
+ HENUMInternal **ppEnum) // return the created HENUMInternal
+{
+ HENUMInternal *pEnum;
+ HRESULT hr = NOERROR;
+
+ // Don't create an empty enum.
+ if (ridStart >= ridEnd)
+ {
+ *ppEnum = 0;
+ goto ErrExit;
+ }
+
+ pEnum = new (nothrow) HENUMInternal;
+
+ // check for out of memory error
+ if (pEnum == NULL)
+ IfFailGo( E_OUTOFMEMORY );
+
+ memset(pEnum, 0, sizeof(HENUMInternal));
+ pEnum->m_tkKind = tkKind;
+ pEnum->m_EnumType = MDSimpleEnum;
+ pEnum->u.m_ulStart = pEnum->u.m_ulCur = ridStart;
+ pEnum->u.m_ulEnd = ridEnd;
+ pEnum->m_ulCount = ridEnd - ridStart;
+
+ *ppEnum = pEnum;
+ErrExit:
+ return hr;
+
+} // CreateSimpleEnum
+
+
+//*****************************************************************************
+// Helper function to destroy Enumerator
+//*****************************************************************************
+void HENUMInternal::DestroyEnum(
+ HENUMInternal *pmdEnum)
+{
+ if (pmdEnum == NULL)
+ return;
+
+ if (pmdEnum->m_EnumType == MDDynamicArrayEnum)
+ {
+ TOKENLIST *pdalist;
+ pdalist = (TOKENLIST *) &(pmdEnum->m_cursor);
+
+ // clear the embedded dynamic array before we delete the enum
+ pdalist->Clear();
+ }
+ delete pmdEnum;
+} // DestroyEnum
+
+
+//*****************************************************************************
+// Helper function to destroy Enumerator if the enumerator is empty
+//*****************************************************************************
+void HENUMInternal::DestroyEnumIfEmpty(
+ HENUMInternal **ppEnum) // reset the enumerator pointer to NULL if empty
+{
+
+ if (*ppEnum == NULL)
+ return;
+
+ _ASSERTE((*ppEnum)->m_EnumType != MDCustomEnum);
+
+ if ((*ppEnum)->m_ulCount == 0)
+ {
+ HENUMInternal::DestroyEnum(*ppEnum);
+ *ppEnum = NULL;
+ }
+} // DestroyEnumIfEmpty
+
+
+void HENUMInternal::ClearEnum(
+ HENUMInternal *pmdEnum)
+{
+ if (pmdEnum == NULL)
+ return;
+
+ if (pmdEnum->m_EnumType == MDDynamicArrayEnum)
+ {
+ TOKENLIST *pdalist;
+ pdalist = (TOKENLIST *) &(pmdEnum->m_cursor);
+
+ // clear the embedded dynamic array before we delete the enum
+ pdalist->Clear();
+ }
+} // ClearEnum
+
+
+//*****************************************************************************
+// Helper function to iterate the enum
+//*****************************************************************************
+bool HENUMInternal::EnumNext(
+ HENUMInternal *phEnum, // [IN] the enumerator to retrieve information
+ mdToken *ptk) // [OUT] token to scope the search
+{
+ _ASSERTE(phEnum && ptk);
+ _ASSERTE(phEnum->m_EnumType != MDCustomEnum);
+
+ if (phEnum->u.m_ulCur >= phEnum->u.m_ulEnd)
+ return false;
+
+ if ( phEnum->m_EnumType == MDSimpleEnum )
+ {
+ *ptk = phEnum->u.m_ulCur | phEnum->m_tkKind;
+ phEnum->u.m_ulCur++;
+ }
+ else
+ {
+ TOKENLIST *pdalist = (TOKENLIST *)&(phEnum->m_cursor);
+
+ _ASSERTE( phEnum->m_EnumType == MDDynamicArrayEnum );
+ *ptk = *( pdalist->Get(phEnum->u.m_ulCur++) );
+ }
+ return true;
+} // EnumNext
+
+//*****************************************************************************
+// Number of items in the enumerator.
+//*****************************************************************************
+HRESULT HENUMInternal::GetCount(
+ HENUMInternal *phEnum, // [IN] the enumerator to retrieve information
+ ULONG *pCount) // ]OUT] the index of the desired item
+{
+ // Check for empty enum.
+ if (phEnum == 0)
+ return S_FALSE;
+
+ _ASSERTE(phEnum->m_EnumType != MDCustomEnum);
+
+
+ *pCount = phEnum->u.m_ulEnd - phEnum->u.m_ulStart;
+ return S_OK;
+}
+
+//*****************************************************************************
+// Get a specific element.
+//*****************************************************************************
+HRESULT HENUMInternal::GetElement(
+ HENUMInternal *phEnum, // [IN] the enumerator to retrieve information
+ ULONG ix, // ]IN] the index of the desired item
+ mdToken *ptk) // [OUT] token to fill
+{
+ // Check for empty enum.
+ if (phEnum == 0)
+ return S_FALSE;
+
+ if (ix > (phEnum->u.m_ulEnd - phEnum->u.m_ulStart))
+ return S_FALSE;
+
+ if ( phEnum->m_EnumType == MDSimpleEnum )
+ {
+ *ptk = (phEnum->u.m_ulStart + ix) | phEnum->m_tkKind;
+ }
+ else
+ {
+ TOKENLIST *pdalist = (TOKENLIST *)&(phEnum->m_cursor);
+
+ _ASSERTE( phEnum->m_EnumType == MDDynamicArrayEnum );
+ *ptk = *( pdalist->Get(ix) );
+ }
+
+ return S_OK;
+}
+
+//*****************************************************************************
+// Helper function to fill output token buffers given an enumerator
+//*****************************************************************************
+HRESULT HENUMInternal::EnumWithCount(
+ HENUMInternal *pEnum, // enumerator
+ ULONG cMax, // max tokens that caller wants
+ mdToken rTokens[], // output buffer to fill the tokens
+ ULONG *pcTokens) // number of tokens fill to the buffer upon return
+{
+ ULONG cTokens;
+ HRESULT hr = NOERROR;
+
+ // Check for empty enum.
+ if (pEnum == 0)
+ {
+ if (pcTokens)
+ *pcTokens = 0;
+ return S_FALSE;
+ }
+
+ // we can only fill the minimun of what caller asked for or what we have left
+ cTokens = min ( (pEnum->u.m_ulEnd - pEnum->u.m_ulCur), cMax);
+
+ if (pEnum->m_EnumType == MDSimpleEnum)
+ {
+
+ // now fill the output
+ for (ULONG i = 0; i < cTokens; i ++, pEnum->u.m_ulCur++)
+ {
+ rTokens[i] = TokenFromRid(pEnum->u.m_ulCur, pEnum->m_tkKind);
+ }
+
+ }
+ else
+ {
+ // cannot be any other kind!
+ _ASSERTE( pEnum->m_EnumType == MDDynamicArrayEnum );
+
+ // get the embedded dynamic array
+ TOKENLIST *pdalist = (TOKENLIST *)&(pEnum->m_cursor);
+
+ for (ULONG i = 0; i < cTokens; i ++, pEnum->u.m_ulCur++)
+ {
+ rTokens[i] = *( pdalist->Get(pEnum->u.m_ulCur) );
+ }
+ }
+
+ if (pcTokens)
+ *pcTokens = cTokens;
+
+ if (cTokens == 0)
+ hr = S_FALSE;
+ return hr;
+} // EnumWithCount
+
+
+//*****************************************************************************
+// Helper function to fill output token buffers given an enumerator
+// This is a variation that takes two output arrays. The tokens in the
+// enumerator are interleaved, one for each array. This is currently used by
+// EnumMethodImpl which needs to return two arrays.
+//*****************************************************************************
+HRESULT HENUMInternal::EnumWithCount(
+ HENUMInternal *pEnum, // enumerator
+ ULONG cMax, // max tokens that caller wants
+ mdToken rTokens1[], // first output buffer to fill the tokens
+ mdToken rTokens2[], // second output buffer to fill the tokens
+ ULONG *pcTokens) // number of tokens fill to each buffer upon return
+{
+ ULONG cTokens;
+ HRESULT hr = NOERROR;
+
+ // cannot be any other kind!
+ _ASSERTE( pEnum->m_EnumType == MDDynamicArrayEnum );
+
+ // Check for empty enum.
+ if (pEnum == 0)
+ {
+ if (pcTokens)
+ *pcTokens = 0;
+ return S_FALSE;
+ }
+
+ // Number of tokens must always be a multiple of 2.
+ _ASSERTE(! ((pEnum->u.m_ulEnd - pEnum->u.m_ulCur) % 2) );
+
+ // we can only fill the minimun of what caller asked for or what we have left
+ cTokens = min ( (pEnum->u.m_ulEnd - pEnum->u.m_ulCur), cMax * 2);
+
+ // get the embedded dynamic array
+ TOKENLIST *pdalist = (TOKENLIST *)&(pEnum->m_cursor);
+
+ for (ULONG i = 0; i < (cTokens / 2); i++)
+ {
+ rTokens1[i] = *( pdalist->Get(pEnum->u.m_ulCur++) );
+ rTokens2[i] = *( pdalist->Get(pEnum->u.m_ulCur++) );
+ }
+
+ if (pcTokens)
+ *pcTokens = cTokens / 2;
+
+ if (cTokens == 0)
+ hr = S_FALSE;
+ return hr;
+} // EnumWithCount
+
+
+//*****************************************************************************
+// Helper function to create HENUMInternal
+//*****************************************************************************
+HRESULT HENUMInternal::CreateDynamicArrayEnum(
+ DWORD tkKind, // kind of token that we are iterating
+ HENUMInternal **ppEnum) // return the created HENUMInternal
+{
+ HENUMInternal *pEnum;
+ HRESULT hr = NOERROR;
+ TOKENLIST *pdalist;
+
+ pEnum = new (nothrow) HENUMInternal;
+
+ // check for out of memory error
+ if (pEnum == NULL)
+ IfFailGo( E_OUTOFMEMORY );
+
+ memset(pEnum, 0, sizeof(HENUMInternal));
+ pEnum->m_tkKind = tkKind;
+ pEnum->m_EnumType = MDDynamicArrayEnum;
+
+ // run the constructor in place
+ pdalist = (TOKENLIST *) &(pEnum->m_cursor);
+ ::new (pdalist) TOKENLIST;
+
+ *ppEnum = pEnum;
+ErrExit:
+ return hr;
+
+} // _CreateDynamicArrayEnum
+
+
+
+//*****************************************************************************
+// Helper function to init HENUMInternal
+//*****************************************************************************
+void HENUMInternal::InitDynamicArrayEnum(
+ HENUMInternal *pEnum) // HENUMInternal to be initialized
+{
+ TOKENLIST *pdalist;
+
+ memset(pEnum, 0, sizeof(HENUMInternal));
+ pEnum->m_EnumType = MDDynamicArrayEnum;
+ pEnum->m_tkKind = (DWORD) -1;
+
+ // run the constructor in place
+ pdalist = (TOKENLIST *) &(pEnum->m_cursor);
+ ::new (pdalist) TOKENLIST;
+} // CreateDynamicArrayEnum
+
+
+//*****************************************************************************
+// Helper function to init HENUMInternal
+//*****************************************************************************
+void HENUMInternal::InitSimpleEnum(
+ DWORD tkKind, // kind of token that we are iterating
+ ULONG ridStart, // starting rid
+ ULONG ridEnd, // end rid
+ HENUMInternal *pEnum) // HENUMInternal to be initialized
+{
+ pEnum->m_EnumType = MDSimpleEnum;
+ pEnum->m_tkKind = tkKind;
+ pEnum->u.m_ulStart = pEnum->u.m_ulCur = ridStart;
+ pEnum->u.m_ulEnd = ridEnd;
+ pEnum->m_ulCount = ridEnd - ridStart;
+
+} // InitSimpleEnum
+
+
+
+
+//*****************************************************************************
+// Helper function to init HENUMInternal
+//*****************************************************************************
+HRESULT HENUMInternal::AddElementToEnum(
+ HENUMInternal *pEnum, // return the created HENUMInternal
+ mdToken tk) // token value to be stored
+{
+ HRESULT hr = NOERROR;
+ TOKENLIST *pdalist;
+ mdToken *ptk;
+
+ pdalist = (TOKENLIST *) &(pEnum->m_cursor);
+
+ {
+ // TODO: Revisit this violation.
+ CONTRACT_VIOLATION(ThrowsViolation);
+ ptk = ((mdToken *)pdalist->Append());
+ }
+ if (ptk == NULL)
+ IfFailGo( E_OUTOFMEMORY );
+ *ptk = tk;
+
+ // increase the count
+ pEnum->m_ulCount++;
+ pEnum->u.m_ulEnd++;
+ErrExit:
+ return hr;
+
+} // _AddElementToEnum
+
+
+
+
+
+//*****************************************************************************
+// find a token in the tokenmap.
+//*****************************************************************************
+MDTOKENMAP::~MDTOKENMAP()
+{
+ if (m_pMap)
+ m_pMap->Release();
+} // MDTOKENMAP::~MDTOKENMAP()
+
+HRESULT MDTOKENMAP::Init(
+ IUnknown *pImport) // The import that this map is for.
+{
+ HRESULT hr; // A result.
+ IMetaDataTables *pITables=0; // Table information.
+ ULONG cRows; // Count of rows in a table.
+ ULONG cTotal; // Running total of rows in db.
+ TOKENREC *pRec; // A TOKENREC record.
+ mdToken tkTable; // Token kind for a table.
+
+ hr = pImport->QueryInterface(IID_IMetaDataTables, (void**)&pITables);
+ if (hr == S_OK)
+ {
+ // Determine the size of each table.
+ cTotal = 0;
+ for (ULONG ixTbl=0; ixTbl<TBL_COUNT; ++ixTbl)
+ {
+ // Where does this table's data start.
+ m_TableOffset[ixTbl] = cTotal;
+ // See if this table has tokens.
+ tkTable = CMiniMdRW::GetTokenForTable(ixTbl);
+ if (tkTable == (ULONG) -1)
+ {
+ // It doesn't have tokens, so we won't see any tokens for the table.
+ }
+ else
+ { // It has tokens, so we may see a token for every row.
+ pITables->GetTableInfo(ixTbl, 0, &cRows, 0,0,0);
+ // Safe: cTotal += cRows
+ if (!ClrSafeInt<ULONG>::addition(cTotal, cRows, cTotal))
+ {
+ IfFailGo(COR_E_OVERFLOW);
+ }
+ }
+ }
+ m_TableOffset[TBL_COUNT] = cTotal;
+ m_iCountIndexed = cTotal;
+ // Attempt to allocate space for all of the possible remaps.
+ if (!AllocateBlock(cTotal))
+ IfFailGo(E_OUTOFMEMORY);
+ // Note that no sorts are needed.
+ m_sortKind = Indexed;
+ // Initialize entries to "not found".
+ for (ULONG i=0; i<cTotal; ++i)
+ {
+ pRec = Get(i);
+ pRec->SetEmpty();
+ }
+ }
+#if defined(_DEBUG)
+ if (SUCCEEDED(pImport->QueryInterface(IID_IMetaDataImport, (void**)&m_pImport)))
+ {
+ // Ok, here's a pretty nasty workaround. We're going to make a big assumption here
+ // that we're owned by the pImport, and so we don't need to keep a refcount
+ // on the pImport object.
+ //
+ // If we did, we'd create a circular reference and neither this object nor
+ // the RegMeta would be freed.
+ m_pImport->Release();
+
+ }
+
+
+
+#endif
+
+ErrExit:
+ if (pITables)
+ pITables->Release();
+ return hr;
+} // HRESULT MDTOKENMAP::Init()
+
+HRESULT MDTOKENMAP::EmptyMap()
+{
+ int nCount = Count();
+ for (int i=0; i<nCount; ++i)
+ {
+ Get(i)->SetEmpty();
+ }
+
+ return S_OK;
+}// HRESULT MDTOKENMAP::Clear()
+
+
+//*****************************************************************************
+// find a token in the tokenmap.
+//*****************************************************************************
+bool MDTOKENMAP::Find(
+ mdToken tkFind, // [IN] the token value to find
+ TOKENREC **ppRec) // [OUT] point to the record found in the dynamic array
+{
+ int lo,mid,hi; // binary search indices.
+ TOKENREC *pRec = NULL;
+
+ if (m_sortKind == Indexed && TypeFromToken(tkFind) != mdtString)
+ {
+ // Get the entry.
+ ULONG ixTbl = CMiniMdRW::GetTableForToken(tkFind);
+ if(ixTbl == (ULONG) -1)
+ return false;
+ ULONG iRid = RidFromToken(tkFind);
+ if((m_TableOffset[ixTbl] + iRid) > m_TableOffset[ixTbl+1])
+ return false;
+ pRec = Get(m_TableOffset[ixTbl] + iRid - 1);
+ // See if it has been set.
+ if (pRec->IsEmpty())
+ return false;
+ // Verify that it is what we think it is.
+ _ASSERTE(pRec->m_tkFrom == tkFind);
+ *ppRec = pRec;
+ return true;
+ }
+ else
+ { // Shouldn't be any unsorted records, and table must be sorted in proper ordering.
+ _ASSERTE( m_iCountTotal == m_iCountSorted &&
+ (m_sortKind == SortByFromToken || m_sortKind == Indexed) );
+ _ASSERTE( (m_iCountIndexed + m_iCountTotal) == (ULONG)Count() );
+
+ // Start with entire table.
+ lo = m_iCountIndexed;
+ hi = Count() - 1;
+
+ // While there are rows in the range...
+ while (lo <= hi)
+ { // Look at the one in the middle.
+ mid = (lo + hi) / 2;
+
+ pRec = Get(mid);
+
+ // If equal to the target, done.
+ if (tkFind == pRec->m_tkFrom)
+ {
+ *ppRec = Get(mid);
+ return true;
+ }
+
+ // If middle item is too small, search the top half.
+ if (pRec->m_tkFrom < tkFind)
+ lo = mid + 1;
+ else // but if middle is to big, search bottom half.
+ hi = mid - 1;
+ }
+ }
+
+ // Didn't find anything that matched.
+ return false;
+} // bool MDTOKENMAP::Find()
+
+
+
+//*****************************************************************************
+// remap the token
+//*****************************************************************************
+HRESULT MDTOKENMAP::Remap(
+ mdToken tkFrom,
+ mdToken *ptkTo)
+{
+ HRESULT hr = NOERROR;
+ TOKENREC *pRec;
+
+ // Remap nil to same thing (helps because System.Object has no base class.)
+ if (IsNilToken(tkFrom))
+ {
+ *ptkTo = tkFrom;
+ return hr;
+ }
+
+ if ( Find(tkFrom, &pRec) )
+ {
+ *ptkTo = pRec->m_tkTo;
+ }
+ else
+ {
+ _ASSERTE( !" Bad lookup map!");
+ hr = META_E_BADMETADATA;
+ }
+ return hr;
+} // HRESULT MDTOKENMAP::Remap()
+
+
+
+//*****************************************************************************
+// find a token in the tokenmap.
+//*****************************************************************************
+HRESULT MDTOKENMAP::InsertNotFound(
+ mdToken tkFind,
+ bool fDuplicate,
+ mdToken tkTo,
+ TOKENREC **ppRec)
+{
+ HRESULT hr = NOERROR;
+ int lo, mid, hi; // binary search indices.
+ TOKENREC *pRec;
+
+ // If possible, validate the input.
+ _ASSERTE(!m_pImport || m_pImport->IsValidToken(tkFind));
+
+ if (m_sortKind == Indexed && TypeFromToken(tkFind) != mdtString)
+ {
+ // Get the entry.
+ ULONG ixTbl = CMiniMdRW::GetTableForToken(tkFind);
+ _ASSERTE(ixTbl != (ULONG) -1);
+ ULONG iRid = RidFromToken(tkFind);
+ _ASSERTE((m_TableOffset[ixTbl] + iRid) <= m_TableOffset[ixTbl+1]);
+ pRec = Get(m_TableOffset[ixTbl] + iRid - 1);
+ // See if it has been set.
+ if (!pRec->IsEmpty())
+ { // Verify that it is what we think it is.
+ _ASSERTE(pRec->m_tkFrom == tkFind);
+ }
+ // Store the data.
+ pRec->m_tkFrom = tkFind;
+ pRec->m_isDuplicate = fDuplicate;
+ pRec->m_tkTo = tkTo;
+ pRec->m_isFoundInImport = false;
+ // Return the result.
+ *ppRec = pRec;
+ }
+ else
+ { // Shouldn't be any unsorted records, and table must be sorted in proper ordering.
+ _ASSERTE( m_iCountTotal == m_iCountSorted &&
+ (m_sortKind == SortByFromToken || m_sortKind == Indexed) );
+
+ if ((Count() - m_iCountIndexed) > 0)
+ {
+ // Start with entire table.
+ lo = m_iCountIndexed;
+ hi = Count() - 1;
+
+ // While there are rows in the range...
+ while (lo < hi)
+ { // Look at the one in the middle.
+ mid = (lo + hi) / 2;
+
+ pRec = Get(mid);
+
+ // If equal to the target, done.
+ if (tkFind == pRec->m_tkFrom)
+ {
+ *ppRec = Get(mid);
+ goto ErrExit;
+ }
+
+ // If middle item is too small, search the top half.
+ if (pRec->m_tkFrom < tkFind)
+ lo = mid + 1;
+ else // but if middle is to big, search bottom half.
+ hi = mid - 1;
+ }
+ _ASSERTE(hi <= lo);
+ pRec = Get(lo);
+
+ if (tkFind == pRec->m_tkFrom)
+ {
+ if (tkTo == pRec->m_tkTo && fDuplicate == pRec->m_isDuplicate)
+ {
+ *ppRec = pRec;
+ }
+ else
+ {
+ _ASSERTE(!"inconsistent token has been added to the table!");
+ IfFailGo( E_FAIL );
+ }
+ }
+
+ if (tkFind < pRec->m_tkFrom)
+ {
+ // insert before lo;
+ pRec = Insert(lo);
+ }
+ else
+ {
+ // insert after lo
+ pRec = Insert(lo + 1);
+ }
+ }
+ else
+ {
+ // table is empty
+ pRec = Insert(m_iCountIndexed);
+ }
+
+
+ // If pRec == NULL, return E_OUTOFMEMORY
+ IfNullGo(pRec);
+
+ m_iCountTotal++;
+ m_iCountSorted++;
+
+ *ppRec = pRec;
+
+ // initialize the record
+ pRec->m_tkFrom = tkFind;
+ pRec->m_isDuplicate = fDuplicate;
+ pRec->m_tkTo = tkTo;
+ pRec->m_isFoundInImport = false;
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT MDTOKENMAP::InsertNotFound()
+
+
+//*****************************************************************************
+// find a "to" token in the tokenmap. Now that we are doing the ref to def optimization,
+// we might have several from tokens map to the same to token. We need to return a range of index
+// instead....
+//*****************************************************************************
+bool MDTOKENMAP::FindWithToToken(
+ mdToken tkFind, // [IN] the token value to find
+ int *piPosition) // [OUT] return the first from-token that has the matching to-token
+{
+ int lo, mid, hi; // binary search indices.
+ TOKENREC *pRec;
+ TOKENREC *pRec2;
+
+ // This makes sure that no insertions take place between calls to FindWithToToken.
+ // We want to avoid repeated sorting of the table.
+ _ASSERTE(m_sortKind != SortByToToken || m_iCountTotal == m_iCountSorted);
+
+ // If the map is sorted with From tokens, change it to be sorted with To tokens.
+ if (m_sortKind != SortByToToken)
+ SortTokensByToToken();
+
+ // Start with entire table.
+ lo = 0;
+ hi = Count() - 1;
+
+ // While there are rows in the range...
+ while (lo <= hi)
+ { // Look at the one in the middle.
+ mid = (lo + hi) / 2;
+
+ pRec = Get(mid);
+
+ // If equal to the target, done.
+ if (tkFind == pRec->m_tkTo)
+ {
+ for (int i = mid-1; i >= 0; i--)
+ {
+ pRec2 = Get(i);
+ if (tkFind != pRec2->m_tkTo)
+ {
+ *piPosition = i + 1;
+ return true;
+ }
+ }
+ *piPosition = 0;
+ return true;
+ }
+
+ // If middle item is too small, search the top half.
+ if (pRec->m_tkTo < tkFind)
+ lo = mid + 1;
+ else // but if middle is to big, search bottom half.
+ hi = mid - 1;
+ }
+ // Didn't find anything that matched.
+ return false;
+} // bool MDTOKENMAP::FindWithToToken()
+
+
+
+//*****************************************************************************
+// output a remapped token
+//*****************************************************************************
+mdToken MDTOKENMAP::SafeRemap(
+ mdToken tkFrom) // [IN] the token value to find
+{
+ TOKENREC *pRec;
+
+ // If possible, validate the input.
+ _ASSERTE(!m_pImport || m_pImport->IsValidToken(tkFrom));
+
+ SortTokensByFromToken();
+
+ if ( Find(tkFrom, &pRec) )
+ {
+ return pRec->m_tkTo;
+ }
+
+ return tkFrom;
+} // mdToken MDTOKENMAP::SafeRemap()
+
+
+//*****************************************************************************
+// Sorting
+//*****************************************************************************
+void MDTOKENMAP::SortTokensByToToken()
+{
+ // Only sort if there are unsorted records or the sort kind changed.
+ if (m_iCountSorted < m_iCountTotal || m_sortKind != SortByToToken)
+ {
+ // Sort the entire array.
+ m_iCountTotal = Count();
+ m_iCountIndexed = 0;
+ SortRangeToToken(0, m_iCountTotal - 1);
+ m_iCountSorted = m_iCountTotal;
+ m_sortKind = SortByToToken;
+ }
+} // void MDTOKENMAP::SortTokensByToToken()
+
+void MDTOKENMAP::SortRangeFromToken(
+ int iLeft,
+ int iRight)
+{
+ int iLast;
+ int i; // loop variable.
+
+ // if less than two elements you're done.
+ if (iLeft >= iRight)
+ return;
+
+ // The mid-element is the pivot, move it to the left.
+ Swap(iLeft, (iLeft+iRight)/2);
+ iLast = iLeft;
+
+ // move everything that is smaller than the pivot to the left.
+ for(i = iLeft+1; i <= iRight; i++)
+ if (CompareFromToken(i, iLeft) < 0)
+ Swap(i, ++iLast);
+
+ // Put the pivot to the point where it is in between smaller and larger elements.
+ Swap(iLeft, iLast);
+
+ // Sort the each partition.
+ SortRangeFromToken(iLeft, iLast-1);
+ SortRangeFromToken(iLast+1, iRight);
+} // void MDTOKENMAP::SortRangeFromToken()
+
+
+//*****************************************************************************
+// Sorting
+//*****************************************************************************
+void MDTOKENMAP::SortRangeToToken(
+ int iLeft,
+ int iRight)
+{
+ int iLast;
+ int i; // loop variable.
+
+ // if less than two elements you're done.
+ if (iLeft >= iRight)
+ return;
+
+ // The mid-element is the pivot, move it to the left.
+ Swap(iLeft, (iLeft+iRight)/2);
+ iLast = iLeft;
+
+ // move everything that is smaller than the pivot to the left.
+ for(i = iLeft+1; i <= iRight; i++)
+ if (CompareToToken(i, iLeft) < 0)
+ Swap(i, ++iLast);
+
+ // Put the pivot to the point where it is in between smaller and larger elements.
+ Swap(iLeft, iLast);
+
+ // Sort the each partition.
+ SortRangeToToken(iLeft, iLast-1);
+ SortRangeToToken(iLast+1, iRight);
+} // void MDTOKENMAP::SortRangeToToken()
+
+
+//*****************************************************************************
+// find a token in the tokenmap.
+//*****************************************************************************
+HRESULT MDTOKENMAP::AppendRecord(
+ mdToken tkFind,
+ bool fDuplicate,
+ mdToken tkTo,
+ TOKENREC **ppRec)
+{
+ HRESULT hr = NOERROR;
+ TOKENREC *pRec;
+
+ // If possible, validate the input.
+ _ASSERTE(!m_pImport || m_pImport->IsValidToken(tkFind));
+
+ // If the map is indexed, and this is a table token, update-in-place.
+ if (m_sortKind == Indexed && TypeFromToken(tkFind) != mdtString)
+ {
+ // Get the entry.
+ ULONG ixTbl = CMiniMdRW::GetTableForToken(tkFind);
+ _ASSERTE(ixTbl != (ULONG) -1);
+ ULONG iRid = RidFromToken(tkFind);
+ _ASSERTE((m_TableOffset[ixTbl] + iRid) <= m_TableOffset[ixTbl+1]);
+ pRec = Get(m_TableOffset[ixTbl] + iRid - 1);
+ // See if it has been set.
+ if (!pRec->IsEmpty())
+ { // Verify that it is what we think it is.
+ _ASSERTE(pRec->m_tkFrom == tkFind);
+ }
+ }
+ else
+ {
+ pRec = Append();
+ IfNullGo(pRec);
+
+ // number of entries increased but not the sorted entry
+ m_iCountTotal++;
+ }
+
+ // Store the data.
+ pRec->m_tkFrom = tkFind;
+ pRec->m_isDuplicate = fDuplicate;
+ pRec->m_tkTo = tkTo;
+ pRec->m_isFoundInImport = false;
+ *ppRec = pRec;
+
+ErrExit:
+ return hr;
+} // HRESULT MDTOKENMAP::AppendRecord()
+
+
+
+
+//*********************************************************************************************************
+//
+// Merge Token manager's constructor
+//
+//*********************************************************************************************************
+MergeTokenManager::MergeTokenManager(MDTOKENMAP *pTkMapList, IUnknown *pHandler)
+{
+ m_cRef = 1;
+ m_pTkMapList = pTkMapList;
+ m_pDefaultHostRemap = NULL;
+ if (pHandler)
+ pHandler->QueryInterface(IID_IMapToken, (void **) &m_pDefaultHostRemap);
+} // TokenManager::TokenManager()
+
+
+
+//*********************************************************************************************************
+//
+// Merge Token manager's destructor
+//
+//*********************************************************************************************************
+MergeTokenManager::~MergeTokenManager()
+{
+ if (m_pDefaultHostRemap)
+ m_pDefaultHostRemap->Release();
+} // TokenManager::~TokenManager()
+
+
+
+
+ULONG MergeTokenManager::AddRef()
+{
+ return InterlockedIncrement(&m_cRef);
+} // TokenManager::AddRef()
+
+
+
+ULONG MergeTokenManager::Release()
+{
+ ULONG cRef = InterlockedDecrement(&m_cRef);
+ if (!cRef)
+ delete this;
+ return (cRef);
+} // TokenManager::Release()
+
+
+HRESULT MergeTokenManager::QueryInterface(REFIID riid, void **ppUnk)
+{
+ if (ppUnk == NULL)
+ return E_INVALIDARG;
+
+ if (IsEqualIID(riid, IID_IMapToken))
+ {
+ //*ppUnk = (IUnknown *) (IMapToken *) this;
+ // it should return the accurate type requested,
+ // if IUnknown is returned, it will finally converted to IMapToken*
+ *ppUnk = (IMapToken *) this;
+ }
+ else if (IsEqualIID(riid, IID_IUnknown))
+ {
+ // add query handling for IUnknown
+ // this upcasting (converting a derived-class
+ // reference or pointer to a base-class) is safe
+ *ppUnk = (IUnknown *) this;
+ }
+ else
+ {
+ *ppUnk = NULL;
+ return (E_NOINTERFACE);
+ }
+
+ AddRef();
+ return (S_OK);
+} // TokenManager::QueryInterface
+
+
+
+//*********************************************************************************************************
+//
+// Token manager keep tracks a list of tokenmaps. Each tokenmap corresponding
+// to an imported scope. Note that with this, we do have problem in how to
+// tell linker regarding the token movement when the token is added by Define
+// rather than merge. This should be fixed with new merge implementation.
+// The tkImp is the old tokens in the emit scope, tkEmit is the new token in the
+// emit scope. We need to find the token from an import scope that is resolved
+// to the tkImp. We then need to tell linker about this token movement.
+// If we don't find any import scope which generates the tkImp token, that is
+// this tkImp is generated by calling DefinXXX directly on the final merged scope.
+// Then we use the default host remap to send the notification.
+//
+//*********************************************************************************************************
+HRESULT MergeTokenManager::Map(mdToken tkImp, mdToken tkEmit)
+{
+ HRESULT hr = NOERROR;
+ MDTOKENMAP *pTkMapList = m_pTkMapList;
+ bool fFoundInImport = false;
+ int iPosition;
+ TOKENREC *pRec;
+
+ _ASSERTE(m_pTkMapList);
+ while ( pTkMapList )
+ {
+ // FindWithToToken will return the first match with the To token.
+ // pTkMapList is sorted with To token. It might contain several From tokens
+ // that map to the To token due to ref to def optimiation. Make sure that
+ // all notification is sent to all of these From tokens.
+ //
+ if ( pTkMapList->FindWithToToken(tkImp, &iPosition) )
+ {
+ // make sure that we don't walk over the last entry
+ while (iPosition < pTkMapList->Count())
+ {
+ pRec = pTkMapList->Get(iPosition);
+ if (pRec->m_tkTo != tkImp)
+ {
+ // we are done!
+ break;
+ }
+
+ // more matching record...
+ fFoundInImport = true;
+ if (pTkMapList->m_pMap)
+ hr = pTkMapList->m_pMap->Map(pRec->m_tkFrom, tkEmit);
+ _ASSERTE(SUCCEEDED(hr));
+ IfFailGo( hr );
+ iPosition++;
+ }
+ }
+ pTkMapList = pTkMapList->m_pNextMap;
+ }
+
+ if (fFoundInImport == false && m_pDefaultHostRemap)
+ {
+ // use the default remap to send the notification
+ IfFailGo( m_pDefaultHostRemap->Map(tkImp, tkEmit) );
+ }
+ErrExit:
+ return hr;
+}
+
+
+
+//*********************************************************************************************************
+//
+// CMapToken's constructor
+//
+//*********************************************************************************************************
+CMapToken::CMapToken()
+{
+ m_cRef = 1;
+ m_pTKMap = NULL;
+ m_isSorted = true;
+} // TokenManager::TokenManager()
+
+
+
+//*********************************************************************************************************
+//
+// CMapToken's destructor
+//
+//*********************************************************************************************************
+CMapToken::~CMapToken()
+{
+ delete m_pTKMap;
+} // CMapToken::~CMapToken()
+
+
+ULONG CMapToken::AddRef()
+{
+ return InterlockedIncrement(&m_cRef);
+} // CMapToken::AddRef()
+
+
+
+ULONG CMapToken::Release()
+{
+ ULONG cRef = InterlockedDecrement(&m_cRef);
+ if (!cRef)
+ delete this;
+ return (cRef);
+} // CMapToken::Release()
+
+
+HRESULT CMapToken::QueryInterface(REFIID riid, void **ppUnk)
+{
+ if (ppUnk == NULL)
+ return E_INVALIDARG;
+
+ if (IsEqualIID(riid, IID_IMapToken))
+ {
+ *ppUnk = (IMapToken *) this;
+ }
+ else if (IsEqualIID(riid, IID_IUnknown))
+ {
+ *ppUnk = (IUnknown *) this;
+ }
+ else
+ {
+ *ppUnk = NULL;
+ return (E_NOINTERFACE);
+ }
+
+ AddRef();
+ return (S_OK);
+} // CMapToken::QueryInterface
+
+
+
+//*********************************************************************************************************
+//
+// Track the token mapping
+//
+//*********************************************************************************************************
+HRESULT CMapToken::Map(
+ mdToken tkFrom,
+ mdToken tkTo)
+{
+ HRESULT hr = NOERROR;
+ TOKENREC *pTkRec;
+
+ if (m_pTKMap == NULL)
+ m_pTKMap = new (nothrow) MDTOKENMAP;
+
+ IfNullGo( m_pTKMap );
+
+ IfFailGo( m_pTKMap->AppendRecord(tkFrom, false, tkTo, &pTkRec) );
+ _ASSERTE( pTkRec );
+
+ m_isSorted = false;
+ErrExit:
+ return hr;
+}
+
+
+//*********************************************************************************************************
+//
+// return what tkFrom is mapped to ptkTo. If there is no remap
+// (ie the token from is filtered out by the filter mechanism, it will return false.
+//
+//*********************************************************************************************************
+bool CMapToken::Find(
+ mdToken tkFrom,
+ TOKENREC **pRecTo)
+{
+ TOKENREC *pRec;
+ bool bRet;
+ if ( m_isSorted == false )
+ {
+ // sort the map
+ m_pTKMap->SortTokensByFromToken();
+ m_isSorted = true;
+ }
+
+ bRet = m_pTKMap->Find(tkFrom, &pRec) ;
+ if (bRet)
+ {
+ _ASSERTE(pRecTo);
+ *pRecTo = pRec;
+ }
+ else
+ {
+ pRec = NULL;
+ }
+ return bRet;
+}
+
+
+//*********************************************************************************************************
+//
+// This function returns true if tkFrom is resolved to a def token. Otherwise, it returns
+// false.
+//
+//*********************************************************************************************************
+bool TokenRemapManager::ResolveRefToDef(
+ mdToken tkRef, // [IN] ref token
+ mdToken *ptkDef) // [OUT] def token that it resolves to. If it does not resolve to a def
+ // token, it will return the tkRef token here.
+{
+ mdToken tkTo;
+
+ _ASSERTE(ptkDef);
+
+ if (TypeFromToken(tkRef) == mdtTypeRef)
+ {
+ tkTo = m_TypeRefToTypeDefMap[RidFromToken(tkRef)];
+ }
+ else
+ {
+ _ASSERTE( TypeFromToken(tkRef) == mdtMemberRef );
+ tkTo = m_MemberRefToMemberDefMap[RidFromToken(tkRef)];
+ }
+ if (RidFromToken(tkTo) == mdTokenNil)
+ {
+ *ptkDef = tkRef;
+ return false;
+ }
+ *ptkDef = tkTo;
+ return true;
+} // ResolveRefToDef
+
+
+
+//*********************************************************************************************************
+//
+// Destructor
+//
+//*********************************************************************************************************
+TokenRemapManager::~TokenRemapManager()
+{
+ m_TypeRefToTypeDefMap.Clear();
+ m_MemberRefToMemberDefMap.Clear();
+} // ~TokenRemapManager
+
+
+//*********************************************************************************************************
+//
+// Initialize the size of Ref to Def optimization table. We will grow the tables in this function.
+// We also initialize the table entries to zero.
+//
+//*********************************************************************************************************
+HRESULT TokenRemapManager::ClearAndEnsureCapacity(
+ ULONG cTypeRef,
+ ULONG cMemberRef)
+{
+ HRESULT hr = NOERROR;
+ if ( ((ULONG) (m_TypeRefToTypeDefMap.Count())) < (cTypeRef + 1) )
+ {
+ if ( m_TypeRefToTypeDefMap.AllocateBlock(cTypeRef + 1 - m_TypeRefToTypeDefMap.Count() ) == 0 )
+ IfFailGo( E_OUTOFMEMORY );
+ }
+ memset( m_TypeRefToTypeDefMap.Get(0), 0, (cTypeRef + 1) * sizeof(mdToken) );
+
+ if ( ((ULONG) (m_MemberRefToMemberDefMap.Count())) < (cMemberRef + 1) )
+ {
+ if ( m_MemberRefToMemberDefMap.AllocateBlock(cMemberRef + 1 - m_MemberRefToMemberDefMap.Count() ) == 0 )
+ IfFailGo( E_OUTOFMEMORY );
+ }
+ memset( m_MemberRefToMemberDefMap.Get(0), 0, (cMemberRef + 1) * sizeof(mdToken) );
+
+ErrExit:
+ return hr;
+} // HRESULT TokenRemapManager::ClearAndEnsureCapacity()
+
+
+
+//*********************************************************************************************************
+//
+// Constructor
+//
+//*********************************************************************************************************
+CMDSemReadWrite::CMDSemReadWrite(
+ UTSemReadWrite * pSem)
+{
+ m_fLockedForRead = false;
+ m_fLockedForWrite = false;
+ m_pSem = pSem;
+} // CMDSemReadWrite::CMDSemReadWrite
+
+
+
+//*********************************************************************************************************
+//
+// Destructor
+//
+//*********************************************************************************************************
+CMDSemReadWrite::~CMDSemReadWrite()
+{
+ _ASSERTE(!m_fLockedForRead || !m_fLockedForWrite);
+ if (m_pSem == NULL)
+ {
+ return;
+ }
+ if (m_fLockedForRead)
+ {
+ LOG((LF_METADATA, LL_EVERYTHING, "UnlockRead called from CSemReadWrite::~CSemReadWrite \n"));
+ m_pSem->UnlockRead();
+ }
+ if (m_fLockedForWrite)
+ {
+ LOG((LF_METADATA, LL_EVERYTHING, "UnlockWrite called from CSemReadWrite::~CSemReadWrite \n"));
+ m_pSem->UnlockWrite();
+ }
+} // CMDSemReadWrite::~CMDSemReadWrite
+
+//*********************************************************************************************************
+//
+// Used to obtain the read lock
+//
+//*********************************************************************************************************
+HRESULT CMDSemReadWrite::LockRead()
+{
+ HRESULT hr = S_OK;
+
+ _ASSERTE(!m_fLockedForRead && !m_fLockedForWrite);
+
+ if (m_pSem == NULL)
+ {
+ INDEBUG(m_fLockedForRead = true);
+ return hr;
+ }
+
+ LOG((LF_METADATA, LL_EVERYTHING, "LockRead called from CSemReadWrite::LockRead \n"));
+ IfFailRet(m_pSem->LockRead());
+ m_fLockedForRead = true;
+
+ return hr;
+} // CMDSemReadWrite::LockRead
+
+//*********************************************************************************************************
+//
+// Used to obtain the read lock
+//
+//*********************************************************************************************************
+HRESULT CMDSemReadWrite::LockWrite()
+{
+ HRESULT hr = S_OK;
+
+ _ASSERTE(!m_fLockedForRead && !m_fLockedForWrite);
+
+ if (m_pSem == NULL)
+ {
+ INDEBUG(m_fLockedForWrite = true);
+ return hr;
+ }
+
+ LOG((LF_METADATA, LL_EVERYTHING, "LockWrite called from CSemReadWrite::LockWrite \n"));
+ IfFailRet(m_pSem->LockWrite());
+ m_fLockedForWrite = true;
+
+ return hr;
+}
+
+//*********************************************************************************************************
+//
+// Convert a read lock to a write lock
+//
+//*********************************************************************************************************
+HRESULT CMDSemReadWrite::ConvertReadLockToWriteLock()
+{
+ _ASSERTE(!m_fLockedForWrite);
+
+ HRESULT hr = S_OK;
+
+ if (m_pSem == NULL)
+ {
+ INDEBUG(m_fLockedForRead = false);
+ INDEBUG(m_fLockedForWrite = true);
+ return hr;
+ }
+
+ if (m_fLockedForRead)
+ {
+ LOG((LF_METADATA, LL_EVERYTHING, "UnlockRead called from CSemReadWrite::ConvertReadLockToWriteLock \n"));
+ m_pSem->UnlockRead();
+ m_fLockedForRead = false;
+ }
+ LOG((LF_METADATA, LL_EVERYTHING, "LockWrite called from CSemReadWrite::ConvertReadLockToWriteLock\n"));
+ IfFailRet(m_pSem->LockWrite());
+ m_fLockedForWrite = true;
+
+ return hr;
+} // CMDSemReadWrite::ConvertReadLockToWriteLock
+
+
+//*********************************************************************************************************
+//
+// Unlocking for write
+//
+//*********************************************************************************************************
+void CMDSemReadWrite::UnlockWrite()
+{
+ _ASSERTE(!m_fLockedForRead);
+
+ if (m_pSem == NULL)
+ {
+ INDEBUG(m_fLockedForWrite = false);
+ return;
+ }
+ if (m_fLockedForWrite)
+ {
+ LOG((LF_METADATA, LL_EVERYTHING, "UnlockWrite called from CSemReadWrite::UnlockWrite \n"));
+ m_pSem->UnlockWrite();
+ m_fLockedForWrite = false;
+ }
+} // CMDSemReadWrite::UnlockWrite