From 3bb6b6ad33cf41b86411142a50332c0423020126 Mon Sep 17 00:00:00 2001 From: Washi Date: Sat, 17 Sep 2022 17:18:52 +0200 Subject: [PATCH] Consolidate MetadataRange types into a single readonly struct. --- .../Tables/ContinuousMetadataRange.cs | 75 ---------- .../DotNet/Metadata/Tables/IMetadataTable.cs | 11 +- .../DotNet/Metadata/Tables/MetadataRange.cs | 129 ++++++++++++++++-- .../DotNet/Metadata/Tables/MetadataTable.cs | 13 ++ .../Tables/RedirectedMetadataRange.cs | 95 ------------- .../DotNet/Metadata/Tables/TablesStream.cs | 4 +- .../Metadata/Tables/MetadataRangeTest.cs | 24 ++-- 7 files changed, 154 insertions(+), 197 deletions(-) delete mode 100644 src/AsmResolver.PE/DotNet/Metadata/Tables/ContinuousMetadataRange.cs delete mode 100644 src/AsmResolver.PE/DotNet/Metadata/Tables/RedirectedMetadataRange.cs diff --git a/src/AsmResolver.PE/DotNet/Metadata/Tables/ContinuousMetadataRange.cs b/src/AsmResolver.PE/DotNet/Metadata/Tables/ContinuousMetadataRange.cs deleted file mode 100644 index 84128c52c..000000000 --- a/src/AsmResolver.PE/DotNet/Metadata/Tables/ContinuousMetadataRange.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; - -namespace AsmResolver.PE.DotNet.Metadata.Tables -{ - /// - /// Represents a simple range of metadata tokens that is continuous from the start to the end of the range. - /// - public class ContinuousMetadataRange : MetadataRange - { - /// - /// Creates a new continuous metadata range, indicating the table, the start- and end row within the table. - /// - /// The table. - /// The starting row identifier. - /// The ending row identifier. This identifier is exclusive. - public ContinuousMetadataRange(TableIndex table, uint startRid, uint endRid) - : base(table, startRid, endRid) - { - } - - /// - public override IEnumerator GetEnumerator() - { - return new Enumerator(this); - } - - /// - /// Provides an implementation of an enumerator for a continuous metadata range. - /// - public struct Enumerator : IEnumerator - { - private readonly ContinuousMetadataRange _range; - private uint _currentRid; - - /// - /// Creates a new enumerator for the provided continuous range. - /// - /// The range. - public Enumerator(ContinuousMetadataRange range) - { - _range = range; - _currentRid = _range.StartRid - 1; - } - - /// - public MetadataToken Current => new(_range.Table, _currentRid); - - object IEnumerator.Current => Current; - - /// - public bool MoveNext() - { - if (_currentRid < _range.EndRid - 1) - { - _currentRid++; - return true; - } - - return false; - } - - /// - public void Reset() - { - _currentRid = _range.StartRid - 1; - } - - void IDisposable.Dispose() - { - } - } - } -} diff --git a/src/AsmResolver.PE/DotNet/Metadata/Tables/IMetadataTable.cs b/src/AsmResolver.PE/DotNet/Metadata/Tables/IMetadataTable.cs index 59b6520f6..0b2352cb4 100644 --- a/src/AsmResolver.PE/DotNet/Metadata/Tables/IMetadataTable.cs +++ b/src/AsmResolver.PE/DotNet/Metadata/Tables/IMetadataTable.cs @@ -53,12 +53,21 @@ bool IsSorted /// The row. IMetadataRow GetByRid(uint rid); + /// + /// Attempts to get the contents of a cell in the table by its row identifier and column index. + /// + /// The row identifier. + /// The column index. + /// When successful, the contents of the cell, converted to an unsigned integer. + /// true if the cell existed and was obtained successfully, false otherwise. + bool TryGetCell(uint rid, int column, out uint value); + /// /// Attempts to get the contents of a row by its row identifier. /// /// The row identifier. /// When successful, the read row. - /// true if the RID existed an the row was obtained successfully, false otherwise. + /// true if the RID existed and the row was obtained successfully, false otherwise. bool TryGetByRid(uint rid, out IMetadataRow row); /// diff --git a/src/AsmResolver.PE/DotNet/Metadata/Tables/MetadataRange.cs b/src/AsmResolver.PE/DotNet/Metadata/Tables/MetadataRange.cs index 16116f60f..6dad92aa4 100644 --- a/src/AsmResolver.PE/DotNet/Metadata/Tables/MetadataRange.cs +++ b/src/AsmResolver.PE/DotNet/Metadata/Tables/MetadataRange.cs @@ -1,39 +1,56 @@ using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace AsmResolver.PE.DotNet.Metadata.Tables { /// /// Represents a range of metadata tokens, indicated by a starting and ending row identifier within a metadata table. /// - public abstract class MetadataRange : IEnumerable + public readonly struct MetadataRange : IEnumerable { /// /// Represents the empty metadata range. /// - public static readonly MetadataRange Empty = new ContinuousMetadataRange(TableIndex.Module, 1, 1); - + public static readonly MetadataRange Empty = new(TableIndex.Module, 1, 1); + /// /// Initializes the range. /// /// The table. /// The starting row identifier. /// The ending row identifier. This identifier is exclusive. - protected MetadataRange(TableIndex table, uint startRid, uint endRid) + public MetadataRange(TableIndex table, uint startRid, uint endRid) { Table = table; StartRid = startRid; EndRid = endRid; + RedirectionTable = null; } - + /// - /// Gets the index of the metadata table this range is targeting. + /// Initializes the range. + /// + /// The table that is used for translating raw indices. + /// The table. + /// The starting row identifier. + /// The ending row identifier. This identifier is exclusive. + public MetadataRange(IMetadataTable redirectionTable, TableIndex table, uint startRid, uint endRid) + { + Table = table; + StartRid = startRid; + EndRid = endRid; + RedirectionTable = redirectionTable; + } + + /// + /// Gets the index of the metadata table this range is targeting. /// public TableIndex Table { get; } - + /// /// Gets the first row identifier that this range includes. /// @@ -43,24 +60,112 @@ public uint StartRid } /// - /// Gets the row identifier indicating the end of the range. The range excludes this row identifier. + /// Gets the row identifier indicating the end of the range. The range excludes this row identifier. /// public uint EndRid { get; } + /// + /// Gets a value indicating whether the range is empty or not. + /// + public bool IsEmpty => EndRid == StartRid; + + /// + /// Gets the table that is used for translating raw indices. + /// + public IMetadataTable? RedirectionTable + { + get; + } + + /// + /// Gets a value indicating whether the range is associated to a redirection table. + /// + [MemberNotNullWhen(true, nameof(RedirectionTable))] + public bool IsRedirected => RedirectionTable is not null; + /// /// Gets the number of metadata rows this range spans. /// public int Count => (int) (EndRid - StartRid); + /// + /// Obtains an enumerator that enumerates all metadata tokens within the range. + /// + /// + public Enumerator GetEnumerator() => new(this); + /// - public abstract IEnumerator GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() + /// + public override string ToString() { - return GetEnumerator(); + var start = new MetadataToken(Table, StartRid); + var end = new MetadataToken(Table, EndRid); + return $"[0x{start.ToString()}..0x{end.ToString()})"; + } + + /// + /// Represents an enumerator that enumerates all metadata tokens within a token range. + /// + public struct Enumerator : IEnumerator + { + private readonly MetadataRange _range; + private uint _currentRid; + + /// + /// Initializes a new token enumerator. + /// + /// The range to enumerate from. + public Enumerator(MetadataRange range) + { + _range = range; + _currentRid = range.StartRid - 1; + } + + /// + public MetadataToken Current + { + get + { + uint actualRid; + + if (!_range.IsRedirected) + actualRid = _currentRid; + else + _range.RedirectionTable.TryGetCell(_currentRid, 0, out actualRid); + + return new MetadataToken(_range.Table, actualRid); + } + } + + /// + object IEnumerator.Current => Current; + + /// + public bool MoveNext() + { + if (_currentRid < _range.EndRid - 1) + { + _currentRid++; + return true; + } + + return false; + } + + /// + public void Reset() => _currentRid = 0; + + /// + public void Dispose() + { + } } } -} \ No newline at end of file +} diff --git a/src/AsmResolver.PE/DotNet/Metadata/Tables/MetadataTable.cs b/src/AsmResolver.PE/DotNet/Metadata/Tables/MetadataTable.cs index aaaea27c0..724d69d37 100644 --- a/src/AsmResolver.PE/DotNet/Metadata/Tables/MetadataTable.cs +++ b/src/AsmResolver.PE/DotNet/Metadata/Tables/MetadataTable.cs @@ -180,6 +180,19 @@ public int Capacity /// The row. public TRow GetByRid(uint rid) => this[(int) (rid - 1)]; + /// + public bool TryGetCell(uint rid, int column, out uint value) + { + if (column >= 0 && column < Layout.Columns.Count && TryGetByRid(rid, out var row)) + { + value = row[column]; + return true; + } + + value = 0; + return false; + } + IMetadataRow IMetadataTable.GetByRid(uint rid) => GetByRid(rid); /// diff --git a/src/AsmResolver.PE/DotNet/Metadata/Tables/RedirectedMetadataRange.cs b/src/AsmResolver.PE/DotNet/Metadata/Tables/RedirectedMetadataRange.cs deleted file mode 100644 index a8c2f4553..000000000 --- a/src/AsmResolver.PE/DotNet/Metadata/Tables/RedirectedMetadataRange.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; - -namespace AsmResolver.PE.DotNet.Metadata.Tables -{ - /// - /// Provides an implementation of a metadata range that is adjusted by an redirect metadata table (such as the field, - /// method, event or property pointer table). - /// - public class RedirectedMetadataRange : MetadataRange - { - /// - /// Creates a new range of metadata tokens that is adjusted by a redirection table. - /// - /// The table providing the redirections. - /// The table. - /// The starting row identifier. - /// The ending row identifier. This identifier is exclusive. - public RedirectedMetadataRange(IMetadataTable indirectTable, TableIndex table, uint startRid, uint endRid) - : base(table, startRid, endRid) - { - IndirectTable = indirectTable; - } - - /// - /// Gets the table responsible for redirecting metadata tokens. - /// - public IMetadataTable IndirectTable - { - get; - } - - /// - public override IEnumerator GetEnumerator() - { - return new Enumerator(this); - } - - /// - /// Provides an implementation of an enumerator for a redirected metadata range. - /// - public struct Enumerator : IEnumerator - { - private readonly RedirectedMetadataRange _range; - private uint _currentRid; - - /// - /// Creates a new enumerator for the provided continuous range. - /// - /// The range. - public Enumerator(RedirectedMetadataRange range) - { - _range = range; - _currentRid = _range.StartRid - 1; - } - - /// - public MetadataToken Current - { - get - { - uint actualRid = _currentRid - 1 < _range.IndirectTable.Count - ? _range.IndirectTable[(int) (_currentRid - 1)][0] - : _currentRid - 1; - return new MetadataToken(_range.Table, actualRid); - } - } - - object IEnumerator.Current => Current; - - /// - public bool MoveNext() - { - if (_currentRid < _range.EndRid - 1) - { - _currentRid++; - return true; - } - - return false; - } - - /// - public void Reset() - { - _currentRid = _range.StartRid - 1; - } - - void IDisposable.Dispose() - { - } - } - } -} \ No newline at end of file diff --git a/src/AsmResolver.PE/DotNet/Metadata/Tables/TablesStream.cs b/src/AsmResolver.PE/DotNet/Metadata/Tables/TablesStream.cs index 8c6089ad1..67efc4806 100644 --- a/src/AsmResolver.PE/DotNet/Metadata/Tables/TablesStream.cs +++ b/src/AsmResolver.PE/DotNet/Metadata/Tables/TablesStream.cs @@ -610,10 +610,10 @@ protected virtual uint GetColumnSize(ColumnType columnType) // Check if redirect table is present. var redirectTable = GetTable(redirectTableIndex); if (redirectTable.Count > 0) - return new RedirectedMetadataRange(redirectTable, memberTableIndex, startRid, endRid); + return new MetadataRange(redirectTable, memberTableIndex, startRid, endRid); // If not, its a simple range. - return new ContinuousMetadataRange(memberTableIndex, startRid, endRid); + return new MetadataRange(memberTableIndex, startRid, endRid); } } } diff --git a/test/AsmResolver.PE.Tests/DotNet/Metadata/Tables/MetadataRangeTest.cs b/test/AsmResolver.PE.Tests/DotNet/Metadata/Tables/MetadataRangeTest.cs index 5b1b4ca65..6c8e6ed55 100644 --- a/test/AsmResolver.PE.Tests/DotNet/Metadata/Tables/MetadataRangeTest.cs +++ b/test/AsmResolver.PE.Tests/DotNet/Metadata/Tables/MetadataRangeTest.cs @@ -10,7 +10,7 @@ public class MetadataRangeTest [Fact] public void ContinuousRangeEmpty() { - var range = new ContinuousMetadataRange(TableIndex.Method, 3, 3); + var range = new MetadataRange(TableIndex.Method, 3, 3); Assert.Equal(0, range.Count); Assert.Empty(range); } @@ -18,7 +18,7 @@ public void ContinuousRangeEmpty() [Fact] public void ContinuousRangeSingleItem() { - var range = new ContinuousMetadataRange(TableIndex.Method, 3, 4); + var range = new MetadataRange(TableIndex.Method, 3, 4); Assert.Equal(1, range.Count); Assert.Single(range); Assert.Equal(new MetadataToken(TableIndex.Method, 3), range.First()); @@ -27,7 +27,7 @@ public void ContinuousRangeSingleItem() [Fact] public void ContinuousRangeMultipleItems() { - var range = new ContinuousMetadataRange(TableIndex.Method, 3, 10); + var range = new MetadataRange(TableIndex.Method, 3, 10); Assert.Equal(7, range.Count); Assert.Equal(new[] { @@ -46,12 +46,12 @@ public void RedirectedRangeEmpty() { var stream = new TablesStream(); var redirectTable = stream.GetTable(); - - var range = new RedirectedMetadataRange(redirectTable, TableIndex.Method, 3, 3); + + var range = new MetadataRange(redirectTable, TableIndex.Method, 3, 3); Assert.Equal(0, range.Count); Assert.Empty(range); } - + [Fact] public void RedirectedRangeSingleItem() { @@ -62,13 +62,13 @@ public void RedirectedRangeSingleItem() redirectTable.Add(new MethodPointerRow(5)); redirectTable.Add(new MethodPointerRow(4)); redirectTable.Add(new MethodPointerRow(3)); - - var range = new RedirectedMetadataRange(redirectTable, TableIndex.Method, 3, 4); + + var range = new MetadataRange(redirectTable, TableIndex.Method, 3, 4); Assert.Equal(1, range.Count); Assert.Single(range); Assert.Equal(new MetadataToken(TableIndex.Method, 5), range.First()); } - + [Fact] public void RedirectedRangeMultipleItems() { @@ -82,8 +82,8 @@ public void RedirectedRangeMultipleItems() redirectTable.Add(new MethodPointerRow(9)); redirectTable.Add(new MethodPointerRow(8)); redirectTable.Add(new MethodPointerRow(10)); - - var range = new RedirectedMetadataRange(redirectTable, TableIndex.Method, 3, 8); + + var range = new MetadataRange(redirectTable, TableIndex.Method, 3, 8); Assert.Equal(5, range.Count); Assert.Equal(new[] { @@ -96,4 +96,4 @@ public void RedirectedRangeMultipleItems() } } -} \ No newline at end of file +}