33
44using System . Collections . Concurrent ;
55using System . Collections . ObjectModel ;
6- using System . IO . MemoryMappedFiles ;
7- using System . Reflection . PortableExecutable ;
86
97namespace Microsoft . Windows . CsWin32 ;
108
@@ -25,19 +23,7 @@ internal class MetadataIndex
2523{
2624 private static readonly int MaxPooledObjectCount = Math . Max ( Environment . ProcessorCount , 4 ) ;
2725
28- private static readonly Action < MetadataReader , object ? > ReaderRecycleDelegate = Recycle ;
29-
30- private static readonly Dictionary < CacheKey , MetadataIndex > Cache = new ( ) ;
31-
32- /// <summary>
33- /// A cache of metadata files read.
34- /// All access to this should be within a <see cref="Cache"/> lock.
35- /// </summary>
36- private static readonly Dictionary < string , MemoryMappedFile > MetadataFiles = new ( StringComparer . OrdinalIgnoreCase ) ;
37-
38- private static readonly ConcurrentDictionary < string , ConcurrentBag < ( PEReader , MetadataReader ) > > PooledPEReaders = new ( StringComparer . OrdinalIgnoreCase ) ;
39-
40- private readonly string metadataPath ;
26+ private readonly MetadataFile metadataFile ;
4127
4228 private readonly Platform ? platform ;
4329
@@ -72,14 +58,14 @@ internal class MetadataIndex
7258 /// <summary>
7359 /// Initializes a new instance of the <see cref="MetadataIndex"/> class.
7460 /// </summary>
75- /// <param name="metadataPath ">The path to the metadata that this index will represent.</param>
61+ /// <param name="metadataFile ">The metadata file that this index will represent.</param>
7662 /// <param name="platform">The platform filter to apply when reading the metadata.</param>
77- private MetadataIndex ( string metadataPath , Platform ? platform )
63+ internal MetadataIndex ( MetadataFile metadataFile , Platform ? platform )
7864 {
79- this . metadataPath = metadataPath ;
65+ this . metadataFile = metadataFile ;
8066 this . platform = platform ;
8167
82- using Rental < MetadataReader > mrRental = GetMetadataReader ( metadataPath ) ;
68+ using MetadataFile . Rental mrRental = metadataFile . GetMetadataReader ( ) ;
8369 MetadataReader mr = mrRental . Value ;
8470 this . MetadataName = Path . GetFileNameWithoutExtension ( mr . GetString ( mr . GetAssemblyDefinition ( ) . Name ) ) ;
8571
@@ -246,50 +232,7 @@ void PopulateNamespace(NamespaceDefinition ns, string? parentNamespace)
246232
247233 internal string CommonNamespaceDot { get ; }
248234
249- private string DebuggerDisplay => $ "{ this . metadataPath } ({ this . platform } )";
250-
251- internal static MetadataIndex Get ( string metadataPath , Platform ? platform )
252- {
253- metadataPath = Path . GetFullPath ( metadataPath ) ;
254- CacheKey key = new ( metadataPath , platform ) ;
255- lock ( Cache )
256- {
257- if ( ! Cache . TryGetValue ( key , out MetadataIndex index ) )
258- {
259- Cache . Add ( key , index = new MetadataIndex ( metadataPath , platform ) ) ;
260- }
261-
262- return index ;
263- }
264- }
265-
266- internal static Rental < MetadataReader > GetMetadataReader ( string metadataPath )
267- {
268- if ( PooledPEReaders . TryGetValue ( metadataPath , out ConcurrentBag < ( PEReader , MetadataReader ) > ? pool ) && pool . TryTake ( out ( PEReader , MetadataReader ) readers ) )
269- {
270- return new ( readers . Item2 , ReaderRecycleDelegate , ( readers . Item1 , metadataPath ) ) ;
271- }
272-
273- PEReader peReader = new PEReader ( CreateFileView ( metadataPath ) ) ;
274- return new ( peReader . GetMetadataReader ( ) , ReaderRecycleDelegate , ( peReader , metadataPath ) ) ;
275- }
276-
277- internal static MemoryMappedViewStream CreateFileView ( string metadataPath )
278- {
279- lock ( Cache )
280- {
281- // We use a memory mapped file so that many threads can perform random access on it concurrently,
282- // only mapping the file into memory once.
283- if ( ! MetadataFiles . TryGetValue ( metadataPath , out MemoryMappedFile ? file ) )
284- {
285- var metadataStream = new FileStream ( metadataPath , FileMode . Open , FileAccess . Read , FileShare . Read ) ;
286- file = MemoryMappedFile . CreateFromFile ( metadataStream , mapName : null , capacity : 0 , MemoryMappedFileAccess . Read , HandleInheritability . None , leaveOpen : false ) ;
287- MetadataFiles . Add ( metadataPath , file ) ;
288- }
289-
290- return file . CreateViewStream ( offset : 0 , size : 0 , MemoryMappedFileAccess . Read ) ;
291- }
292- }
235+ private string DebuggerDisplay => $ "{ this . metadataFile . Path } ({ this . platform } )";
293236
294237 /// <summary>
295238 /// Attempts to translate a <see cref="TypeReferenceHandle"/> to a <see cref="TypeDefinitionHandle"/>.
@@ -423,21 +366,6 @@ internal bool TryGetEnumName(MetadataReader reader, string enumValueName, [NotNu
423366 return false ;
424367 }
425368
426- private static void Recycle ( MetadataReader metadataReader , object ? state )
427- {
428- ( PEReader peReader , string metadataPath ) = ( ( PEReader , string ) ) state ! ;
429- ConcurrentBag < ( PEReader , MetadataReader ) > pool = PooledPEReaders . GetOrAdd ( metadataPath , _ => new ( ) ) ;
430- if ( pool . Count < MaxPooledObjectCount )
431- {
432- pool . Add ( ( peReader , metadataReader ) ) ;
433- }
434- else
435- {
436- // The pool is full. Dispose of this rather than recycle it.
437- peReader . Dispose ( ) ;
438- }
439- }
440-
441369 private static string CommonPrefix ( IReadOnlyList < string > ss )
442370 {
443371 if ( ss . Count == 0 )
@@ -497,33 +425,4 @@ private static string CommonPrefix(IReadOnlyList<string> ss)
497425 // Return null if the value was determined to be missing.
498426 return this . enumTypeReference . HasValue && ! this . enumTypeReference . Value . IsNil ? this . enumTypeReference . Value : null ;
499427 }
500-
501- [ DebuggerDisplay ( "{" + nameof ( DebuggerDisplay ) + ",nq}" ) ]
502- private struct CacheKey : IEquatable < CacheKey >
503- {
504- internal CacheKey ( string metadataPath , Platform ? platform )
505- {
506- this . MetadataPath = metadataPath ;
507- this . Platform = platform ;
508- }
509-
510- internal string MetadataPath { get ; }
511-
512- internal Platform ? Platform { get ; }
513-
514- private string DebuggerDisplay => $ "{ this . MetadataPath } ({ this . Platform } )";
515-
516- public override bool Equals ( object obj ) => obj is CacheKey other && this . Equals ( other ) ;
517-
518- public bool Equals ( CacheKey other )
519- {
520- return this . Platform == other . Platform
521- && string . Equals ( this . MetadataPath , other . MetadataPath , StringComparison . OrdinalIgnoreCase ) ;
522- }
523-
524- public override int GetHashCode ( )
525- {
526- return StringComparer . OrdinalIgnoreCase . GetHashCode ( this . MetadataPath ) + ( this . Platform . HasValue ? ( int ) this . Platform . Value : 0 ) ;
527- }
528- }
529428}
0 commit comments