// dnlib: See LICENSE.txt for more info using System; using System.Diagnostics; using dnlib.IO; namespace dnlib.DotNet.MD { /// /// .NET metadata stream /// [DebuggerDisplay("{dataReader.Length} {streamHeader.Name}")] public abstract class DotNetStream : IFileSection, IDisposable { /// /// Reader that can access the whole stream. /// /// NOTE: Always copy this field to a local variable before using it since it must be thread safe. /// protected DataReader dataReader; /// /// null if it wasn't present in the file /// StreamHeader streamHeader; DataReaderFactory mdReaderFactory; uint metadataBaseOffset; /// public FileOffset StartOffset => (FileOffset)dataReader.StartOffset; /// public FileOffset EndOffset => (FileOffset)dataReader.EndOffset; /// /// Gets the length of this stream in the metadata /// public uint StreamLength => dataReader.Length; /// /// Gets the stream header /// public StreamHeader StreamHeader => streamHeader; /// /// Gets the name of the stream /// public string Name => streamHeader is null ? string.Empty : streamHeader.Name; /// /// Gets a data reader that can read the full stream /// /// public DataReader CreateReader() => dataReader; /// /// Default constructor /// protected DotNetStream() { streamHeader = null; dataReader = default; } /// /// Constructor /// /// Data reader factory /// Offset of metadata /// The stream header protected DotNetStream(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader) { this.mdReaderFactory = mdReaderFactory; mdReaderFactory.DataReaderInvalidated += DataReaderFactory_DataReaderInvalidated; this.mdReaderFactory = mdReaderFactory; this.metadataBaseOffset = metadataBaseOffset; this.streamHeader = streamHeader; RecreateReader(mdReaderFactory, metadataBaseOffset, streamHeader, notifyThisClass: false); } void DataReaderFactory_DataReaderInvalidated(object sender, EventArgs e) => RecreateReader(mdReaderFactory, metadataBaseOffset, streamHeader, notifyThisClass: true); void RecreateReader(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader, bool notifyThisClass) { if (mdReaderFactory is null || streamHeader is null) dataReader = default; else dataReader = mdReaderFactory.CreateReader(metadataBaseOffset + streamHeader.Offset, streamHeader.StreamSize); if (notifyThisClass) OnReaderRecreated(); } /// /// Called after gets recreated /// protected virtual void OnReaderRecreated() { } /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Dispose method /// /// true if called by protected virtual void Dispose(bool disposing) { if (disposing) { var mdReaderFactory = this.mdReaderFactory; if (mdReaderFactory is not null) mdReaderFactory.DataReaderInvalidated -= DataReaderFactory_DataReaderInvalidated; streamHeader = null; this.mdReaderFactory = null; } } /// /// Checks whether an index is valid /// /// The index /// true if the index is valid public virtual bool IsValidIndex(uint index) => IsValidOffset(index); /// /// Check whether an offset is within the stream /// /// Stream offset /// true if the offset is valid public bool IsValidOffset(uint offset) => offset == 0 || offset < dataReader.Length; /// /// Check whether an offset is within the stream /// /// Stream offset /// Size of data /// true if the offset is valid public bool IsValidOffset(uint offset, int size) { if (size == 0) return IsValidOffset(offset); return size > 0 && (ulong)offset + (uint)size <= dataReader.Length; } } /// /// Base class of #US, #Strings, #Blob, and #GUID classes /// public abstract class HeapStream : DotNetStream { /// protected HeapStream() { } /// protected HeapStream(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader) : base(mdReaderFactory, metadataBaseOffset, streamHeader) { } } }