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// RWUtil.cpp
6//
7
8//
9// contains utility code to MD directory
10//
11//*****************************************************************************
12#include "stdafx.h"
13#include "metadata.h"
14#include "rwutil.h"
15#include "utsem.h"
16#include "../inc/mdlog.h"
17
18//*****************************************************************************
19// Helper methods
20//*****************************************************************************
21void
22Unicode2UTF(
23 LPCWSTR wszSrc, // The string to convert.
24 __out_ecount(cbDst)
25 LPUTF8 szDst, // Buffer for the output UTF8 string.
26 int cbDst) // Size of the buffer for UTF8 string.
27{
28 int cchSrc = (int)wcslen(wszSrc);
29 int cchRet;
30
31 cchRet = WszWideCharToMultiByte(
32 CP_UTF8,
33 0,
34 wszSrc,
35 cchSrc + 1,
36 szDst,
37 cbDst,
38 NULL,
39 NULL);
40
41 if (cchRet == 0)
42 {
43 _ASSERTE_MSG(FALSE, "Converting unicode string to UTF8 string failed!");
44 szDst[0] = '\0';
45 }
46} // Unicode2UTF
47
48
49HRESULT HENUMInternal::CreateSimpleEnum(
50 DWORD tkKind, // kind of token that we are iterating
51 ULONG ridStart, // starting rid
52 ULONG ridEnd, // end rid
53 HENUMInternal **ppEnum) // return the created HENUMInternal
54{
55 HENUMInternal *pEnum;
56 HRESULT hr = NOERROR;
57
58 // Don't create an empty enum.
59 if (ridStart >= ridEnd)
60 {
61 *ppEnum = 0;
62 goto ErrExit;
63 }
64
65 pEnum = new (nothrow) HENUMInternal;
66
67 // check for out of memory error
68 if (pEnum == NULL)
69 IfFailGo( E_OUTOFMEMORY );
70
71 memset(pEnum, 0, sizeof(HENUMInternal));
72 pEnum->m_tkKind = tkKind;
73 pEnum->m_EnumType = MDSimpleEnum;
74 pEnum->u.m_ulStart = pEnum->u.m_ulCur = ridStart;
75 pEnum->u.m_ulEnd = ridEnd;
76 pEnum->m_ulCount = ridEnd - ridStart;
77
78 *ppEnum = pEnum;
79ErrExit:
80 return hr;
81
82} // CreateSimpleEnum
83
84
85//*****************************************************************************
86// Helper function to destroy Enumerator
87//*****************************************************************************
88void HENUMInternal::DestroyEnum(
89 HENUMInternal *pmdEnum)
90{
91 if (pmdEnum == NULL)
92 return;
93
94 if (pmdEnum->m_EnumType == MDDynamicArrayEnum)
95 {
96 TOKENLIST *pdalist;
97 pdalist = (TOKENLIST *) &(pmdEnum->m_cursor);
98
99 // clear the embedded dynamic array before we delete the enum
100 pdalist->Clear();
101 }
102 delete pmdEnum;
103} // DestroyEnum
104
105
106//*****************************************************************************
107// Helper function to destroy Enumerator if the enumerator is empty
108//*****************************************************************************
109void HENUMInternal::DestroyEnumIfEmpty(
110 HENUMInternal **ppEnum) // reset the enumerator pointer to NULL if empty
111{
112
113 if (*ppEnum == NULL)
114 return;
115
116 _ASSERTE((*ppEnum)->m_EnumType != MDCustomEnum);
117
118 if ((*ppEnum)->m_ulCount == 0)
119 {
120 HENUMInternal::DestroyEnum(*ppEnum);
121 *ppEnum = NULL;
122 }
123} // DestroyEnumIfEmpty
124
125
126void HENUMInternal::ClearEnum(
127 HENUMInternal *pmdEnum)
128{
129 if (pmdEnum == NULL)
130 return;
131
132 if (pmdEnum->m_EnumType == MDDynamicArrayEnum)
133 {
134 TOKENLIST *pdalist;
135 pdalist = (TOKENLIST *) &(pmdEnum->m_cursor);
136
137 // clear the embedded dynamic array before we delete the enum
138 pdalist->Clear();
139 }
140} // ClearEnum
141
142
143//*****************************************************************************
144// Helper function to iterate the enum
145//*****************************************************************************
146bool HENUMInternal::EnumNext(
147 HENUMInternal *phEnum, // [IN] the enumerator to retrieve information
148 mdToken *ptk) // [OUT] token to scope the search
149{
150 _ASSERTE(phEnum && ptk);
151 _ASSERTE(phEnum->m_EnumType != MDCustomEnum);
152
153 if (phEnum->u.m_ulCur >= phEnum->u.m_ulEnd)
154 return false;
155
156 if ( phEnum->m_EnumType == MDSimpleEnum )
157 {
158 *ptk = phEnum->u.m_ulCur | phEnum->m_tkKind;
159 phEnum->u.m_ulCur++;
160 }
161 else
162 {
163 TOKENLIST *pdalist = (TOKENLIST *)&(phEnum->m_cursor);
164
165 _ASSERTE( phEnum->m_EnumType == MDDynamicArrayEnum );
166 *ptk = *( pdalist->Get(phEnum->u.m_ulCur++) );
167 }
168 return true;
169} // EnumNext
170
171//*****************************************************************************
172// Number of items in the enumerator.
173//*****************************************************************************
174HRESULT HENUMInternal::GetCount(
175 HENUMInternal *phEnum, // [IN] the enumerator to retrieve information
176 ULONG *pCount) // ]OUT] the index of the desired item
177{
178 // Check for empty enum.
179 if (phEnum == 0)
180 return S_FALSE;
181
182 _ASSERTE(phEnum->m_EnumType != MDCustomEnum);
183
184
185 *pCount = phEnum->u.m_ulEnd - phEnum->u.m_ulStart;
186 return S_OK;
187}
188
189//*****************************************************************************
190// Get a specific element.
191//*****************************************************************************
192HRESULT HENUMInternal::GetElement(
193 HENUMInternal *phEnum, // [IN] the enumerator to retrieve information
194 ULONG ix, // ]IN] the index of the desired item
195 mdToken *ptk) // [OUT] token to fill
196{
197 // Check for empty enum.
198 if (phEnum == 0)
199 return S_FALSE;
200
201 if (ix > (phEnum->u.m_ulEnd - phEnum->u.m_ulStart))
202 return S_FALSE;
203
204 if ( phEnum->m_EnumType == MDSimpleEnum )
205 {
206 *ptk = (phEnum->u.m_ulStart + ix) | phEnum->m_tkKind;
207 }
208 else
209 {
210 TOKENLIST *pdalist = (TOKENLIST *)&(phEnum->m_cursor);
211
212 _ASSERTE( phEnum->m_EnumType == MDDynamicArrayEnum );
213 *ptk = *( pdalist->Get(ix) );
214 }
215
216 return S_OK;
217}
218
219//*****************************************************************************
220// Helper function to fill output token buffers given an enumerator
221//*****************************************************************************
222HRESULT HENUMInternal::EnumWithCount(
223 HENUMInternal *pEnum, // enumerator
224 ULONG cMax, // max tokens that caller wants
225 mdToken rTokens[], // output buffer to fill the tokens
226 ULONG *pcTokens) // number of tokens fill to the buffer upon return
227{
228 ULONG cTokens;
229 HRESULT hr = NOERROR;
230
231 // Check for empty enum.
232 if (pEnum == 0)
233 {
234 if (pcTokens)
235 *pcTokens = 0;
236 return S_FALSE;
237 }
238
239 // we can only fill the minimun of what caller asked for or what we have left
240 cTokens = min ( (pEnum->u.m_ulEnd - pEnum->u.m_ulCur), cMax);
241
242 if (pEnum->m_EnumType == MDSimpleEnum)
243 {
244
245 // now fill the output
246 for (ULONG i = 0; i < cTokens; i ++, pEnum->u.m_ulCur++)
247 {
248 rTokens[i] = TokenFromRid(pEnum->u.m_ulCur, pEnum->m_tkKind);
249 }
250
251 }
252 else
253 {
254 // cannot be any other kind!
255 _ASSERTE( pEnum->m_EnumType == MDDynamicArrayEnum );
256
257 // get the embedded dynamic array
258 TOKENLIST *pdalist = (TOKENLIST *)&(pEnum->m_cursor);
259
260 for (ULONG i = 0; i < cTokens; i ++, pEnum->u.m_ulCur++)
261 {
262 rTokens[i] = *( pdalist->Get(pEnum->u.m_ulCur) );
263 }
264 }
265
266 if (pcTokens)
267 *pcTokens = cTokens;
268
269 if (cTokens == 0)
270 hr = S_FALSE;
271 return hr;
272} // EnumWithCount
273
274
275//*****************************************************************************
276// Helper function to fill output token buffers given an enumerator
277// This is a variation that takes two output arrays. The tokens in the
278// enumerator are interleaved, one for each array. This is currently used by
279// EnumMethodImpl which needs to return two arrays.
280//*****************************************************************************
281HRESULT HENUMInternal::EnumWithCount(
282 HENUMInternal *pEnum, // enumerator
283 ULONG cMax, // max tokens that caller wants
284 mdToken rTokens1[], // first output buffer to fill the tokens
285 mdToken rTokens2[], // second output buffer to fill the tokens
286 ULONG *pcTokens) // number of tokens fill to each buffer upon return
287{
288 ULONG cTokens;
289 HRESULT hr = NOERROR;
290
291 // cannot be any other kind!
292 _ASSERTE( pEnum->m_EnumType == MDDynamicArrayEnum );
293
294 // Check for empty enum.
295 if (pEnum == 0)
296 {
297 if (pcTokens)
298 *pcTokens = 0;
299 return S_FALSE;
300 }
301
302 // Number of tokens must always be a multiple of 2.
303 _ASSERTE(! ((pEnum->u.m_ulEnd - pEnum->u.m_ulCur) % 2) );
304
305 // we can only fill the minimun of what caller asked for or what we have left
306 cTokens = min ( (pEnum->u.m_ulEnd - pEnum->u.m_ulCur), cMax * 2);
307
308 // get the embedded dynamic array
309 TOKENLIST *pdalist = (TOKENLIST *)&(pEnum->m_cursor);
310
311 for (ULONG i = 0; i < (cTokens / 2); i++)
312 {
313 rTokens1[i] = *( pdalist->Get(pEnum->u.m_ulCur++) );
314 rTokens2[i] = *( pdalist->Get(pEnum->u.m_ulCur++) );
315 }
316
317 if (pcTokens)
318 *pcTokens = cTokens / 2;
319
320 if (cTokens == 0)
321 hr = S_FALSE;
322 return hr;
323} // EnumWithCount
324
325
326//*****************************************************************************
327// Helper function to create HENUMInternal
328//*****************************************************************************
329HRESULT HENUMInternal::CreateDynamicArrayEnum(
330 DWORD tkKind, // kind of token that we are iterating
331 HENUMInternal **ppEnum) // return the created HENUMInternal
332{
333 HENUMInternal *pEnum;
334 HRESULT hr = NOERROR;
335 TOKENLIST *pdalist;
336
337 pEnum = new (nothrow) HENUMInternal;
338
339 // check for out of memory error
340 if (pEnum == NULL)
341 IfFailGo( E_OUTOFMEMORY );
342
343 memset(pEnum, 0, sizeof(HENUMInternal));
344 pEnum->m_tkKind = tkKind;
345 pEnum->m_EnumType = MDDynamicArrayEnum;
346
347 // run the constructor in place
348 pdalist = (TOKENLIST *) &(pEnum->m_cursor);
349 ::new (pdalist) TOKENLIST;
350
351 *ppEnum = pEnum;
352ErrExit:
353 return hr;
354
355} // _CreateDynamicArrayEnum
356
357
358
359//*****************************************************************************
360// Helper function to init HENUMInternal
361//*****************************************************************************
362void HENUMInternal::InitDynamicArrayEnum(
363 HENUMInternal *pEnum) // HENUMInternal to be initialized
364{
365 TOKENLIST *pdalist;
366
367 memset(pEnum, 0, sizeof(HENUMInternal));
368 pEnum->m_EnumType = MDDynamicArrayEnum;
369 pEnum->m_tkKind = (DWORD) -1;
370
371 // run the constructor in place
372 pdalist = (TOKENLIST *) &(pEnum->m_cursor);
373 ::new (pdalist) TOKENLIST;
374} // CreateDynamicArrayEnum
375
376
377//*****************************************************************************
378// Helper function to init HENUMInternal
379//*****************************************************************************
380void HENUMInternal::InitSimpleEnum(
381 DWORD tkKind, // kind of token that we are iterating
382 ULONG ridStart, // starting rid
383 ULONG ridEnd, // end rid
384 HENUMInternal *pEnum) // HENUMInternal to be initialized
385{
386 pEnum->m_EnumType = MDSimpleEnum;
387 pEnum->m_tkKind = tkKind;
388 pEnum->u.m_ulStart = pEnum->u.m_ulCur = ridStart;
389 pEnum->u.m_ulEnd = ridEnd;
390 pEnum->m_ulCount = ridEnd - ridStart;
391
392} // InitSimpleEnum
393
394
395
396
397//*****************************************************************************
398// Helper function to init HENUMInternal
399//*****************************************************************************
400HRESULT HENUMInternal::AddElementToEnum(
401 HENUMInternal *pEnum, // return the created HENUMInternal
402 mdToken tk) // token value to be stored
403{
404 HRESULT hr = NOERROR;
405 TOKENLIST *pdalist;
406 mdToken *ptk;
407
408 pdalist = (TOKENLIST *) &(pEnum->m_cursor);
409
410 {
411 // TODO: Revisit this violation.
412 CONTRACT_VIOLATION(ThrowsViolation);
413 ptk = ((mdToken *)pdalist->Append());
414 }
415 if (ptk == NULL)
416 IfFailGo( E_OUTOFMEMORY );
417 *ptk = tk;
418
419 // increase the count
420 pEnum->m_ulCount++;
421 pEnum->u.m_ulEnd++;
422ErrExit:
423 return hr;
424
425} // _AddElementToEnum
426
427
428
429
430
431//*****************************************************************************
432// find a token in the tokenmap.
433//*****************************************************************************
434MDTOKENMAP::~MDTOKENMAP()
435{
436 if (m_pMap)
437 m_pMap->Release();
438} // MDTOKENMAP::~MDTOKENMAP()
439
440HRESULT MDTOKENMAP::Init(
441 IUnknown *pImport) // The import that this map is for.
442{
443 HRESULT hr; // A result.
444 IMetaDataTables *pITables=0; // Table information.
445 ULONG cRows; // Count of rows in a table.
446 ULONG cTotal; // Running total of rows in db.
447 TOKENREC *pRec; // A TOKENREC record.
448 mdToken tkTable; // Token kind for a table.
449
450 hr = pImport->QueryInterface(IID_IMetaDataTables, (void**)&pITables);
451 if (hr == S_OK)
452 {
453 // Determine the size of each table.
454 cTotal = 0;
455 for (ULONG ixTbl=0; ixTbl<TBL_COUNT; ++ixTbl)
456 {
457 // Where does this table's data start.
458 m_TableOffset[ixTbl] = cTotal;
459 // See if this table has tokens.
460 tkTable = CMiniMdRW::GetTokenForTable(ixTbl);
461 if (tkTable == (ULONG) -1)
462 {
463 // It doesn't have tokens, so we won't see any tokens for the table.
464 }
465 else
466 { // It has tokens, so we may see a token for every row.
467 pITables->GetTableInfo(ixTbl, 0, &cRows, 0,0,0);
468 // Safe: cTotal += cRows
469 if (!ClrSafeInt<ULONG>::addition(cTotal, cRows, cTotal))
470 {
471 IfFailGo(COR_E_OVERFLOW);
472 }
473 }
474 }
475 m_TableOffset[TBL_COUNT] = cTotal;
476 m_iCountIndexed = cTotal;
477 // Attempt to allocate space for all of the possible remaps.
478 if (!AllocateBlock(cTotal))
479 IfFailGo(E_OUTOFMEMORY);
480 // Note that no sorts are needed.
481 m_sortKind = Indexed;
482 // Initialize entries to "not found".
483 for (ULONG i=0; i<cTotal; ++i)
484 {
485 pRec = Get(i);
486 pRec->SetEmpty();
487 }
488 }
489#if defined(_DEBUG)
490 if (SUCCEEDED(pImport->QueryInterface(IID_IMetaDataImport, (void**)&m_pImport)))
491 {
492 // Ok, here's a pretty nasty workaround. We're going to make a big assumption here
493 // that we're owned by the pImport, and so we don't need to keep a refcount
494 // on the pImport object.
495 //
496 // If we did, we'd create a circular reference and neither this object nor
497 // the RegMeta would be freed.
498 m_pImport->Release();
499
500 }
501
502
503
504#endif
505
506ErrExit:
507 if (pITables)
508 pITables->Release();
509 return hr;
510} // HRESULT MDTOKENMAP::Init()
511
512HRESULT MDTOKENMAP::EmptyMap()
513{
514 int nCount = Count();
515 for (int i=0; i<nCount; ++i)
516 {
517 Get(i)->SetEmpty();
518 }
519
520 return S_OK;
521}// HRESULT MDTOKENMAP::Clear()
522
523
524//*****************************************************************************
525// find a token in the tokenmap.
526//*****************************************************************************
527bool MDTOKENMAP::Find(
528 mdToken tkFind, // [IN] the token value to find
529 TOKENREC **ppRec) // [OUT] point to the record found in the dynamic array
530{
531 int lo,mid,hi; // binary search indices.
532 TOKENREC *pRec = NULL;
533
534 if (m_sortKind == Indexed && TypeFromToken(tkFind) != mdtString)
535 {
536 // Get the entry.
537 ULONG ixTbl = CMiniMdRW::GetTableForToken(tkFind);
538 if(ixTbl == (ULONG) -1)
539 return false;
540 ULONG iRid = RidFromToken(tkFind);
541 if((m_TableOffset[ixTbl] + iRid) > m_TableOffset[ixTbl+1])
542 return false;
543 pRec = Get(m_TableOffset[ixTbl] + iRid - 1);
544 // See if it has been set.
545 if (pRec->IsEmpty())
546 return false;
547 // Verify that it is what we think it is.
548 _ASSERTE(pRec->m_tkFrom == tkFind);
549 *ppRec = pRec;
550 return true;
551 }
552 else
553 { // Shouldn't be any unsorted records, and table must be sorted in proper ordering.
554 _ASSERTE( m_iCountTotal == m_iCountSorted &&
555 (m_sortKind == SortByFromToken || m_sortKind == Indexed) );
556 _ASSERTE( (m_iCountIndexed + m_iCountTotal) == (ULONG)Count() );
557
558 // Start with entire table.
559 lo = m_iCountIndexed;
560 hi = Count() - 1;
561
562 // While there are rows in the range...
563 while (lo <= hi)
564 { // Look at the one in the middle.
565 mid = (lo + hi) / 2;
566
567 pRec = Get(mid);
568
569 // If equal to the target, done.
570 if (tkFind == pRec->m_tkFrom)
571 {
572 *ppRec = Get(mid);
573 return true;
574 }
575
576 // If middle item is too small, search the top half.
577 if (pRec->m_tkFrom < tkFind)
578 lo = mid + 1;
579 else // but if middle is to big, search bottom half.
580 hi = mid - 1;
581 }
582 }
583
584 // Didn't find anything that matched.
585 return false;
586} // bool MDTOKENMAP::Find()
587
588
589
590//*****************************************************************************
591// remap the token
592//*****************************************************************************
593HRESULT MDTOKENMAP::Remap(
594 mdToken tkFrom,
595 mdToken *ptkTo)
596{
597 HRESULT hr = NOERROR;
598 TOKENREC *pRec;
599
600 // Remap nil to same thing (helps because System.Object has no base class.)
601 if (IsNilToken(tkFrom))
602 {
603 *ptkTo = tkFrom;
604 return hr;
605 }
606
607 if ( Find(tkFrom, &pRec) )
608 {
609 *ptkTo = pRec->m_tkTo;
610 }
611 else
612 {
613 _ASSERTE( !" Bad lookup map!");
614 hr = META_E_BADMETADATA;
615 }
616 return hr;
617} // HRESULT MDTOKENMAP::Remap()
618
619
620
621//*****************************************************************************
622// find a token in the tokenmap.
623//*****************************************************************************
624HRESULT MDTOKENMAP::InsertNotFound(
625 mdToken tkFind,
626 bool fDuplicate,
627 mdToken tkTo,
628 TOKENREC **ppRec)
629{
630 HRESULT hr = NOERROR;
631 int lo, mid, hi; // binary search indices.
632 TOKENREC *pRec;
633
634 // If possible, validate the input.
635 _ASSERTE(!m_pImport || m_pImport->IsValidToken(tkFind));
636
637 if (m_sortKind == Indexed && TypeFromToken(tkFind) != mdtString)
638 {
639 // Get the entry.
640 ULONG ixTbl = CMiniMdRW::GetTableForToken(tkFind);
641 _ASSERTE(ixTbl != (ULONG) -1);
642 ULONG iRid = RidFromToken(tkFind);
643 _ASSERTE((m_TableOffset[ixTbl] + iRid) <= m_TableOffset[ixTbl+1]);
644 pRec = Get(m_TableOffset[ixTbl] + iRid - 1);
645 // See if it has been set.
646 if (!pRec->IsEmpty())
647 { // Verify that it is what we think it is.
648 _ASSERTE(pRec->m_tkFrom == tkFind);
649 }
650 // Store the data.
651 pRec->m_tkFrom = tkFind;
652 pRec->m_isDuplicate = fDuplicate;
653 pRec->m_tkTo = tkTo;
654 pRec->m_isFoundInImport = false;
655 // Return the result.
656 *ppRec = pRec;
657 }
658 else
659 { // Shouldn't be any unsorted records, and table must be sorted in proper ordering.
660 _ASSERTE( m_iCountTotal == m_iCountSorted &&
661 (m_sortKind == SortByFromToken || m_sortKind == Indexed) );
662
663 if ((Count() - m_iCountIndexed) > 0)
664 {
665 // Start with entire table.
666 lo = m_iCountIndexed;
667 hi = Count() - 1;
668
669 // While there are rows in the range...
670 while (lo < hi)
671 { // Look at the one in the middle.
672 mid = (lo + hi) / 2;
673
674 pRec = Get(mid);
675
676 // If equal to the target, done.
677 if (tkFind == pRec->m_tkFrom)
678 {
679 *ppRec = Get(mid);
680 goto ErrExit;
681 }
682
683 // If middle item is too small, search the top half.
684 if (pRec->m_tkFrom < tkFind)
685 lo = mid + 1;
686 else // but if middle is to big, search bottom half.
687 hi = mid - 1;
688 }
689 _ASSERTE(hi <= lo);
690 pRec = Get(lo);
691
692 if (tkFind == pRec->m_tkFrom)
693 {
694 if (tkTo == pRec->m_tkTo && fDuplicate == pRec->m_isDuplicate)
695 {
696 *ppRec = pRec;
697 }
698 else
699 {
700 _ASSERTE(!"inconsistent token has been added to the table!");
701 IfFailGo( E_FAIL );
702 }
703 }
704
705 if (tkFind < pRec->m_tkFrom)
706 {
707 // insert before lo;
708 pRec = Insert(lo);
709 }
710 else
711 {
712 // insert after lo
713 pRec = Insert(lo + 1);
714 }
715 }
716 else
717 {
718 // table is empty
719 pRec = Insert(m_iCountIndexed);
720 }
721
722
723 // If pRec == NULL, return E_OUTOFMEMORY
724 IfNullGo(pRec);
725
726 m_iCountTotal++;
727 m_iCountSorted++;
728
729 *ppRec = pRec;
730
731 // initialize the record
732 pRec->m_tkFrom = tkFind;
733 pRec->m_isDuplicate = fDuplicate;
734 pRec->m_tkTo = tkTo;
735 pRec->m_isFoundInImport = false;
736 }
737
738ErrExit:
739 return hr;
740} // HRESULT MDTOKENMAP::InsertNotFound()
741
742
743//*****************************************************************************
744// find a "to" token in the tokenmap. Now that we are doing the ref to def optimization,
745// we might have several from tokens map to the same to token. We need to return a range of index
746// instead....
747//*****************************************************************************
748bool MDTOKENMAP::FindWithToToken(
749 mdToken tkFind, // [IN] the token value to find
750 int *piPosition) // [OUT] return the first from-token that has the matching to-token
751{
752 int lo, mid, hi; // binary search indices.
753 TOKENREC *pRec;
754 TOKENREC *pRec2;
755
756 // This makes sure that no insertions take place between calls to FindWithToToken.
757 // We want to avoid repeated sorting of the table.
758 _ASSERTE(m_sortKind != SortByToToken || m_iCountTotal == m_iCountSorted);
759
760 // If the map is sorted with From tokens, change it to be sorted with To tokens.
761 if (m_sortKind != SortByToToken)
762 SortTokensByToToken();
763
764 // Start with entire table.
765 lo = 0;
766 hi = Count() - 1;
767
768 // While there are rows in the range...
769 while (lo <= hi)
770 { // Look at the one in the middle.
771 mid = (lo + hi) / 2;
772
773 pRec = Get(mid);
774
775 // If equal to the target, done.
776 if (tkFind == pRec->m_tkTo)
777 {
778 for (int i = mid-1; i >= 0; i--)
779 {
780 pRec2 = Get(i);
781 if (tkFind != pRec2->m_tkTo)
782 {
783 *piPosition = i + 1;
784 return true;
785 }
786 }
787 *piPosition = 0;
788 return true;
789 }
790
791 // If middle item is too small, search the top half.
792 if (pRec->m_tkTo < tkFind)
793 lo = mid + 1;
794 else // but if middle is to big, search bottom half.
795 hi = mid - 1;
796 }
797 // Didn't find anything that matched.
798 return false;
799} // bool MDTOKENMAP::FindWithToToken()
800
801
802
803//*****************************************************************************
804// output a remapped token
805//*****************************************************************************
806mdToken MDTOKENMAP::SafeRemap(
807 mdToken tkFrom) // [IN] the token value to find
808{
809 TOKENREC *pRec;
810
811 // If possible, validate the input.
812 _ASSERTE(!m_pImport || m_pImport->IsValidToken(tkFrom));
813
814 SortTokensByFromToken();
815
816 if ( Find(tkFrom, &pRec) )
817 {
818 return pRec->m_tkTo;
819 }
820
821 return tkFrom;
822} // mdToken MDTOKENMAP::SafeRemap()
823
824
825//*****************************************************************************
826// Sorting
827//*****************************************************************************
828void MDTOKENMAP::SortTokensByToToken()
829{
830 // Only sort if there are unsorted records or the sort kind changed.
831 if (m_iCountSorted < m_iCountTotal || m_sortKind != SortByToToken)
832 {
833 // Sort the entire array.
834 m_iCountTotal = Count();
835 m_iCountIndexed = 0;
836 SortRangeToToken(0, m_iCountTotal - 1);
837 m_iCountSorted = m_iCountTotal;
838 m_sortKind = SortByToToken;
839 }
840} // void MDTOKENMAP::SortTokensByToToken()
841
842void MDTOKENMAP::SortRangeFromToken(
843 int iLeft,
844 int iRight)
845{
846 int iLast;
847 int i; // loop variable.
848
849 // if less than two elements you're done.
850 if (iLeft >= iRight)
851 return;
852
853 // The mid-element is the pivot, move it to the left.
854 Swap(iLeft, (iLeft+iRight)/2);
855 iLast = iLeft;
856
857 // move everything that is smaller than the pivot to the left.
858 for(i = iLeft+1; i <= iRight; i++)
859 if (CompareFromToken(i, iLeft) < 0)
860 Swap(i, ++iLast);
861
862 // Put the pivot to the point where it is in between smaller and larger elements.
863 Swap(iLeft, iLast);
864
865 // Sort the each partition.
866 SortRangeFromToken(iLeft, iLast-1);
867 SortRangeFromToken(iLast+1, iRight);
868} // void MDTOKENMAP::SortRangeFromToken()
869
870
871//*****************************************************************************
872// Sorting
873//*****************************************************************************
874void MDTOKENMAP::SortRangeToToken(
875 int iLeft,
876 int iRight)
877{
878 int iLast;
879 int i; // loop variable.
880
881 // if less than two elements you're done.
882 if (iLeft >= iRight)
883 return;
884
885 // The mid-element is the pivot, move it to the left.
886 Swap(iLeft, (iLeft+iRight)/2);
887 iLast = iLeft;
888
889 // move everything that is smaller than the pivot to the left.
890 for(i = iLeft+1; i <= iRight; i++)
891 if (CompareToToken(i, iLeft) < 0)
892 Swap(i, ++iLast);
893
894 // Put the pivot to the point where it is in between smaller and larger elements.
895 Swap(iLeft, iLast);
896
897 // Sort the each partition.
898 SortRangeToToken(iLeft, iLast-1);
899 SortRangeToToken(iLast+1, iRight);
900} // void MDTOKENMAP::SortRangeToToken()
901
902
903//*****************************************************************************
904// find a token in the tokenmap.
905//*****************************************************************************
906HRESULT MDTOKENMAP::AppendRecord(
907 mdToken tkFind,
908 bool fDuplicate,
909 mdToken tkTo,
910 TOKENREC **ppRec)
911{
912 HRESULT hr = NOERROR;
913 TOKENREC *pRec;
914
915 // If possible, validate the input.
916 _ASSERTE(!m_pImport || m_pImport->IsValidToken(tkFind));
917
918 // If the map is indexed, and this is a table token, update-in-place.
919 if (m_sortKind == Indexed && TypeFromToken(tkFind) != mdtString)
920 {
921 // Get the entry.
922 ULONG ixTbl = CMiniMdRW::GetTableForToken(tkFind);
923 _ASSERTE(ixTbl != (ULONG) -1);
924 ULONG iRid = RidFromToken(tkFind);
925 _ASSERTE((m_TableOffset[ixTbl] + iRid) <= m_TableOffset[ixTbl+1]);
926 pRec = Get(m_TableOffset[ixTbl] + iRid - 1);
927 // See if it has been set.
928 if (!pRec->IsEmpty())
929 { // Verify that it is what we think it is.
930 _ASSERTE(pRec->m_tkFrom == tkFind);
931 }
932 }
933 else
934 {
935 pRec = Append();
936 IfNullGo(pRec);
937
938 // number of entries increased but not the sorted entry
939 m_iCountTotal++;
940 }
941
942 // Store the data.
943 pRec->m_tkFrom = tkFind;
944 pRec->m_isDuplicate = fDuplicate;
945 pRec->m_tkTo = tkTo;
946 pRec->m_isFoundInImport = false;
947 *ppRec = pRec;
948
949ErrExit:
950 return hr;
951} // HRESULT MDTOKENMAP::AppendRecord()
952
953
954
955//*********************************************************************************************************
956//
957// CMapToken's constructor
958//
959//*********************************************************************************************************
960CMapToken::CMapToken()
961{
962 m_cRef = 1;
963 m_pTKMap = NULL;
964 m_isSorted = true;
965} // TokenManager::TokenManager()
966
967
968
969//*********************************************************************************************************
970//
971// CMapToken's destructor
972//
973//*********************************************************************************************************
974CMapToken::~CMapToken()
975{
976 delete m_pTKMap;
977} // CMapToken::~CMapToken()
978
979
980ULONG CMapToken::AddRef()
981{
982 return InterlockedIncrement(&m_cRef);
983} // CMapToken::AddRef()
984
985
986
987ULONG CMapToken::Release()
988{
989 ULONG cRef = InterlockedDecrement(&m_cRef);
990 if (!cRef)
991 delete this;
992 return (cRef);
993} // CMapToken::Release()
994
995
996HRESULT CMapToken::QueryInterface(REFIID riid, void **ppUnk)
997{
998 if (ppUnk == NULL)
999 return E_INVALIDARG;
1000
1001 if (IsEqualIID(riid, IID_IMapToken))
1002 {
1003 *ppUnk = (IMapToken *) this;
1004 }
1005 else if (IsEqualIID(riid, IID_IUnknown))
1006 {
1007 *ppUnk = (IUnknown *) this;
1008 }
1009 else
1010 {
1011 *ppUnk = NULL;
1012 return (E_NOINTERFACE);
1013 }
1014
1015 AddRef();
1016 return (S_OK);
1017} // CMapToken::QueryInterface
1018
1019
1020
1021//*********************************************************************************************************
1022//
1023// Track the token mapping
1024//
1025//*********************************************************************************************************
1026HRESULT CMapToken::Map(
1027 mdToken tkFrom,
1028 mdToken tkTo)
1029{
1030 HRESULT hr = NOERROR;
1031 TOKENREC *pTkRec;
1032
1033 if (m_pTKMap == NULL)
1034 m_pTKMap = new (nothrow) MDTOKENMAP;
1035
1036 IfNullGo( m_pTKMap );
1037
1038 IfFailGo( m_pTKMap->AppendRecord(tkFrom, false, tkTo, &pTkRec) );
1039 _ASSERTE( pTkRec );
1040
1041 m_isSorted = false;
1042ErrExit:
1043 return hr;
1044}
1045
1046
1047//*********************************************************************************************************
1048//
1049// return what tkFrom is mapped to ptkTo. If there is no remap
1050// (ie the token from is filtered out by the filter mechanism, it will return false.
1051//
1052//*********************************************************************************************************
1053bool CMapToken::Find(
1054 mdToken tkFrom,
1055 TOKENREC **pRecTo)
1056{
1057 TOKENREC *pRec;
1058 bool bRet;
1059 if ( m_isSorted == false )
1060 {
1061 // sort the map
1062 m_pTKMap->SortTokensByFromToken();
1063 m_isSorted = true;
1064 }
1065
1066 bRet = m_pTKMap->Find(tkFrom, &pRec) ;
1067 if (bRet)
1068 {
1069 _ASSERTE(pRecTo);
1070 *pRecTo = pRec;
1071 }
1072 else
1073 {
1074 pRec = NULL;
1075 }
1076 return bRet;
1077}
1078
1079
1080//*********************************************************************************************************
1081//
1082// This function returns true if tkFrom is resolved to a def token. Otherwise, it returns
1083// false.
1084//
1085//*********************************************************************************************************
1086bool TokenRemapManager::ResolveRefToDef(
1087 mdToken tkRef, // [IN] ref token
1088 mdToken *ptkDef) // [OUT] def token that it resolves to. If it does not resolve to a def
1089 // token, it will return the tkRef token here.
1090{
1091 mdToken tkTo;
1092
1093 _ASSERTE(ptkDef);
1094
1095 if (TypeFromToken(tkRef) == mdtTypeRef)
1096 {
1097 tkTo = m_TypeRefToTypeDefMap[RidFromToken(tkRef)];
1098 }
1099 else
1100 {
1101 _ASSERTE( TypeFromToken(tkRef) == mdtMemberRef );
1102 tkTo = m_MemberRefToMemberDefMap[RidFromToken(tkRef)];
1103 }
1104 if (RidFromToken(tkTo) == mdTokenNil)
1105 {
1106 *ptkDef = tkRef;
1107 return false;
1108 }
1109 *ptkDef = tkTo;
1110 return true;
1111} // ResolveRefToDef
1112
1113
1114
1115//*********************************************************************************************************
1116//
1117// Destructor
1118//
1119//*********************************************************************************************************
1120TokenRemapManager::~TokenRemapManager()
1121{
1122 m_TypeRefToTypeDefMap.Clear();
1123 m_MemberRefToMemberDefMap.Clear();
1124} // ~TokenRemapManager
1125
1126
1127//*********************************************************************************************************
1128//
1129// Initialize the size of Ref to Def optimization table. We will grow the tables in this function.
1130// We also initialize the table entries to zero.
1131//
1132//*********************************************************************************************************
1133HRESULT TokenRemapManager::ClearAndEnsureCapacity(
1134 ULONG cTypeRef,
1135 ULONG cMemberRef)
1136{
1137 HRESULT hr = NOERROR;
1138 if ( ((ULONG) (m_TypeRefToTypeDefMap.Count())) < (cTypeRef + 1) )
1139 {
1140 if ( m_TypeRefToTypeDefMap.AllocateBlock(cTypeRef + 1 - m_TypeRefToTypeDefMap.Count() ) == 0 )
1141 IfFailGo( E_OUTOFMEMORY );
1142 }
1143 memset( m_TypeRefToTypeDefMap.Get(0), 0, (cTypeRef + 1) * sizeof(mdToken) );
1144
1145 if ( ((ULONG) (m_MemberRefToMemberDefMap.Count())) < (cMemberRef + 1) )
1146 {
1147 if ( m_MemberRefToMemberDefMap.AllocateBlock(cMemberRef + 1 - m_MemberRefToMemberDefMap.Count() ) == 0 )
1148 IfFailGo( E_OUTOFMEMORY );
1149 }
1150 memset( m_MemberRefToMemberDefMap.Get(0), 0, (cMemberRef + 1) * sizeof(mdToken) );
1151
1152ErrExit:
1153 return hr;
1154} // HRESULT TokenRemapManager::ClearAndEnsureCapacity()
1155
1156
1157
1158//*********************************************************************************************************
1159//
1160// Constructor
1161//
1162//*********************************************************************************************************
1163CMDSemReadWrite::CMDSemReadWrite(
1164 UTSemReadWrite * pSem)
1165{
1166 m_fLockedForRead = false;
1167 m_fLockedForWrite = false;
1168 m_pSem = pSem;
1169} // CMDSemReadWrite::CMDSemReadWrite
1170
1171
1172
1173//*********************************************************************************************************
1174//
1175// Destructor
1176//
1177//*********************************************************************************************************
1178CMDSemReadWrite::~CMDSemReadWrite()
1179{
1180 _ASSERTE(!m_fLockedForRead || !m_fLockedForWrite);
1181 if (m_pSem == NULL)
1182 {
1183 return;
1184 }
1185 if (m_fLockedForRead)
1186 {
1187 LOG((LF_METADATA, LL_EVERYTHING, "UnlockRead called from CSemReadWrite::~CSemReadWrite \n"));
1188 m_pSem->UnlockRead();
1189 }
1190 if (m_fLockedForWrite)
1191 {
1192 LOG((LF_METADATA, LL_EVERYTHING, "UnlockWrite called from CSemReadWrite::~CSemReadWrite \n"));
1193 m_pSem->UnlockWrite();
1194 }
1195} // CMDSemReadWrite::~CMDSemReadWrite
1196
1197//*********************************************************************************************************
1198//
1199// Used to obtain the read lock
1200//
1201//*********************************************************************************************************
1202HRESULT CMDSemReadWrite::LockRead()
1203{
1204 HRESULT hr = S_OK;
1205
1206 _ASSERTE(!m_fLockedForRead && !m_fLockedForWrite);
1207
1208 if (m_pSem == NULL)
1209 {
1210 INDEBUG(m_fLockedForRead = true);
1211 return hr;
1212 }
1213
1214 LOG((LF_METADATA, LL_EVERYTHING, "LockRead called from CSemReadWrite::LockRead \n"));
1215 IfFailRet(m_pSem->LockRead());
1216 m_fLockedForRead = true;
1217
1218 return hr;
1219} // CMDSemReadWrite::LockRead
1220
1221//*********************************************************************************************************
1222//
1223// Used to obtain the read lock
1224//
1225//*********************************************************************************************************
1226HRESULT CMDSemReadWrite::LockWrite()
1227{
1228 HRESULT hr = S_OK;
1229
1230 _ASSERTE(!m_fLockedForRead && !m_fLockedForWrite);
1231
1232 if (m_pSem == NULL)
1233 {
1234 INDEBUG(m_fLockedForWrite = true);
1235 return hr;
1236 }
1237
1238 LOG((LF_METADATA, LL_EVERYTHING, "LockWrite called from CSemReadWrite::LockWrite \n"));
1239 IfFailRet(m_pSem->LockWrite());
1240 m_fLockedForWrite = true;
1241
1242 return hr;
1243}
1244
1245//*********************************************************************************************************
1246//
1247// Convert a read lock to a write lock
1248//
1249//*********************************************************************************************************
1250HRESULT CMDSemReadWrite::ConvertReadLockToWriteLock()
1251{
1252 _ASSERTE(!m_fLockedForWrite);
1253
1254 HRESULT hr = S_OK;
1255
1256 if (m_pSem == NULL)
1257 {
1258 INDEBUG(m_fLockedForRead = false);
1259 INDEBUG(m_fLockedForWrite = true);
1260 return hr;
1261 }
1262
1263 if (m_fLockedForRead)
1264 {
1265 LOG((LF_METADATA, LL_EVERYTHING, "UnlockRead called from CSemReadWrite::ConvertReadLockToWriteLock \n"));
1266 m_pSem->UnlockRead();
1267 m_fLockedForRead = false;
1268 }
1269 LOG((LF_METADATA, LL_EVERYTHING, "LockWrite called from CSemReadWrite::ConvertReadLockToWriteLock\n"));
1270 IfFailRet(m_pSem->LockWrite());
1271 m_fLockedForWrite = true;
1272
1273 return hr;
1274} // CMDSemReadWrite::ConvertReadLockToWriteLock
1275
1276
1277//*********************************************************************************************************
1278//
1279// Unlocking for write
1280//
1281//*********************************************************************************************************
1282void CMDSemReadWrite::UnlockWrite()
1283{
1284 _ASSERTE(!m_fLockedForRead);
1285
1286 if (m_pSem == NULL)
1287 {
1288 INDEBUG(m_fLockedForWrite = false);
1289 return;
1290 }
1291 if (m_fLockedForWrite)
1292 {
1293 LOG((LF_METADATA, LL_EVERYTHING, "UnlockWrite called from CSemReadWrite::UnlockWrite \n"));
1294 m_pSem->UnlockWrite();
1295 m_fLockedForWrite = false;
1296 }
1297} // CMDSemReadWrite::UnlockWrite
1298