// 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) {
}
}
}