< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 193
Coverable lines: 193
Total lines: 336
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 70
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
CompareTo(...)100%110%
ToString()100%110%
.ctor(...)100%110%
.ctor(...)100%110%
.ctor(...)0%22220%
.ctor(...)100%110%
CalculateBaseCounts(...)0%440%
CalculateHeapSizes(...)0%220%
CalculateRowCounts(...)0%220%
ToImmutable(...)0%220%
GetBaseRowCounts(...)0%220%
CalculateDeltaRowCountsForGeneration(...)0%880%
GetGenerationHandle(...)0%28280%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataAggregator.cs

#LineLine coverage
 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
 4using System.Collections.Generic;
 5using System.Collections.Immutable;
 6using System.Diagnostics;
 7
 8namespace System.Reflection.Metadata.Ecma335
 9{
 10    public sealed class MetadataAggregator
 11    {
 12        // For each heap handle and each delta contains aggregate heap lengths.
 13        // For GUIDs: heapSizes[heap kind][reader index] == reader[reader index].GuidHeap.Block.Length
 14        // For all other heaps: heapSizes[heap kind][reader index] == Sum { 0..reader index | reader[reader index].XxxHe
 15        private readonly ImmutableArray<ImmutableArray<int>> _heapSizes;
 16
 17        private readonly ImmutableArray<ImmutableArray<RowCounts>> _rowCounts;
 18
 19        // internal for testing
 20        internal struct RowCounts : IComparable<RowCounts>
 21        {
 22            public int AggregateInserts;
 23            public int Updates;
 24
 25            public int CompareTo(RowCounts other)
 026            {
 027                return AggregateInserts - other.AggregateInserts;
 028            }
 29
 30            public override string ToString()
 031            {
 032                return $"+0x{AggregateInserts:x} ~0x{Updates:x}";
 033            }
 34        }
 35
 36        public MetadataAggregator(MetadataReader baseReader, IReadOnlyList<MetadataReader> deltaReaders)
 037            : this(baseReader, null, null, deltaReaders)
 038        {
 039        }
 40
 41        public MetadataAggregator(
 42            IReadOnlyList<int>? baseTableRowCounts,
 43            IReadOnlyList<int>? baseHeapSizes,
 44            IReadOnlyList<MetadataReader>? deltaReaders)
 045            : this(null, baseTableRowCounts, baseHeapSizes, deltaReaders)
 046        {
 047        }
 48
 049        private MetadataAggregator(
 050            MetadataReader? baseReader,
 051            IReadOnlyList<int>? baseTableRowCounts,
 052            IReadOnlyList<int>? baseHeapSizes,
 053            IReadOnlyList<MetadataReader>? deltaReaders)
 054        {
 055            if (baseTableRowCounts == null)
 056            {
 057                if (baseReader == null)
 058                {
 059                    Throw.ArgumentNull(nameof(baseReader));
 60                }
 61
 062                if (baseReader.GetTableRowCount(TableIndex.EncMap) != 0)
 063                {
 064                    throw new ArgumentException(SR.BaseReaderMustBeFullMetadataReader, nameof(baseReader));
 65                }
 66
 067                CalculateBaseCounts(baseReader, out baseTableRowCounts, out baseHeapSizes);
 068                Debug.Assert(baseTableRowCounts != null);
 069            }
 70            else
 071            {
 072                if (baseTableRowCounts.Count != MetadataTokens.TableCount)
 073                {
 074                    throw new ArgumentException(SR.Format(SR.ExpectedListOfSize, MetadataTokens.TableCount), nameof(base
 75                }
 76
 077                if (baseHeapSizes == null)
 078                {
 079                    Throw.ArgumentNull(nameof(baseHeapSizes));
 80                }
 81
 082                if (baseHeapSizes.Count != MetadataTokens.HeapCount)
 083                {
 084                    throw new ArgumentException(SR.Format(SR.ExpectedListOfSize, MetadataTokens.HeapCount), nameof(baseT
 85                }
 086            }
 87
 088            if (deltaReaders == null || deltaReaders.Count == 0)
 089            {
 090                throw new ArgumentException(SR.ExpectedNonEmptyList, nameof(deltaReaders));
 91            }
 92
 093            for (int i = 0; i < deltaReaders.Count; i++)
 094            {
 095                if (deltaReaders[i].GetTableRowCount(TableIndex.EncMap) == 0 || !deltaReaders[i].IsMinimalDelta)
 096                {
 097                    throw new ArgumentException(SR.ReadersMustBeDeltaReaders, nameof(deltaReaders));
 98                }
 099            }
 100
 0101            _heapSizes = CalculateHeapSizes(baseHeapSizes, deltaReaders);
 0102            _rowCounts = CalculateRowCounts(baseTableRowCounts, deltaReaders);
 0103        }
 104
 105        // for testing only
 0106        internal MetadataAggregator(RowCounts[][] rowCounts, int[][] heapSizes)
 0107        {
 0108            _rowCounts = ToImmutable(rowCounts);
 0109            _heapSizes = ToImmutable(heapSizes);
 0110        }
 111
 112        private static void CalculateBaseCounts(
 113            MetadataReader baseReader,
 114            out IReadOnlyList<int> baseTableRowCounts,
 115            out IReadOnlyList<int> baseHeapSizes)
 0116        {
 0117            int[] rowCounts = new int[MetadataTokens.TableCount];
 0118            int[] heapSizes = new int[MetadataTokens.HeapCount];
 119
 0120            for (int i = 0; i < rowCounts.Length; i++)
 0121            {
 0122                rowCounts[i] = baseReader.GetTableRowCount((TableIndex)i);
 0123            }
 124
 0125            for (int i = 0; i < heapSizes.Length; i++)
 0126            {
 0127                heapSizes[i] = baseReader.GetHeapSize((HeapIndex)i);
 0128            }
 129
 0130            baseTableRowCounts = rowCounts;
 0131            baseHeapSizes = heapSizes;
 0132        }
 133
 134        private static ImmutableArray<ImmutableArray<int>> CalculateHeapSizes(
 135            IReadOnlyList<int> baseSizes,
 136            IReadOnlyList<MetadataReader> deltaReaders)
 0137        {
 138            // GUID heap index is multiple of sizeof(Guid) == 16
 139            const int guidSize = 16;
 0140            int generationCount = 1 + deltaReaders.Count;
 141
 0142            var userStringSizes = new int[generationCount];
 0143            var stringSizes = new int[generationCount];
 0144            var blobSizes = new int[generationCount];
 0145            var guidSizes = new int[generationCount];
 146
 0147            userStringSizes[0] = baseSizes[(int)HeapIndex.UserString];
 0148            stringSizes[0] = baseSizes[(int)HeapIndex.String];
 0149            blobSizes[0] = baseSizes[(int)HeapIndex.Blob];
 0150            guidSizes[0] = baseSizes[(int)HeapIndex.Guid] / guidSize;
 151
 0152            for (int r = 0; r < deltaReaders.Count; r++)
 0153            {
 0154                userStringSizes[r + 1] = userStringSizes[r] + deltaReaders[r].GetHeapSize(HeapIndex.UserString);
 0155                stringSizes[r + 1] = stringSizes[r] + deltaReaders[r].GetHeapSize(HeapIndex.String);
 0156                blobSizes[r + 1] = blobSizes[r] + deltaReaders[r].GetHeapSize(HeapIndex.Blob);
 0157                guidSizes[r + 1] = deltaReaders[r].GetHeapSize(HeapIndex.Guid) / guidSize;
 0158            }
 159
 0160            return ImmutableArray.Create(
 0161                userStringSizes.ToImmutableArray(),
 0162                stringSizes.ToImmutableArray(),
 0163                blobSizes.ToImmutableArray(),
 0164                guidSizes.ToImmutableArray());
 0165        }
 166
 167        private static ImmutableArray<ImmutableArray<RowCounts>> CalculateRowCounts(
 168            IReadOnlyList<int> baseRowCounts,
 169            IReadOnlyList<MetadataReader> deltaReaders)
 0170        {
 171            // TODO: optimize - we don't need to allocate all these arrays
 0172            var rowCounts = GetBaseRowCounts(baseRowCounts, generations: 1 + deltaReaders.Count);
 173
 0174            for (int generation = 1; generation <= deltaReaders.Count; generation++)
 0175            {
 0176                CalculateDeltaRowCountsForGeneration(rowCounts, generation, ref deltaReaders[generation - 1].EncMapTable
 0177            }
 178
 0179            return ToImmutable(rowCounts);
 0180        }
 181
 182        private static ImmutableArray<ImmutableArray<T>> ToImmutable<T>(T[][] array)
 0183        {
 0184            var immutable = new ImmutableArray<T>[array.Length];
 0185            for (int i = 0; i < array.Length; i++)
 0186            {
 0187                immutable[i] = array[i].ToImmutableArray();
 0188            }
 189
 0190            return immutable.ToImmutableArray();
 0191        }
 192
 193        // internal for testing
 194        internal static RowCounts[][] GetBaseRowCounts(IReadOnlyList<int> baseRowCounts, int generations)
 0195        {
 0196            var rowCounts = new RowCounts[MetadataTokens.TableCount][];
 197
 0198            for (int t = 0; t < rowCounts.Length; t++)
 0199            {
 0200                rowCounts[t] = new RowCounts[generations];
 0201                rowCounts[t][0].AggregateInserts = baseRowCounts[t];
 0202            }
 203
 0204            return rowCounts;
 0205        }
 206
 207        // internal for testing
 208        internal static void CalculateDeltaRowCountsForGeneration(RowCounts[][] rowCounts, int generation, ref EnCMapTab
 0209        {
 0210            foreach (var tableRowCounts in rowCounts)
 0211            {
 0212                tableRowCounts[generation].AggregateInserts = tableRowCounts[generation - 1].AggregateInserts;
 0213            }
 214
 0215            int mapRowCount = encMapTable.NumberOfRows;
 0216            for (int mapRid = 1; mapRid <= mapRowCount; mapRid++)
 0217            {
 0218                uint token = encMapTable.GetToken(mapRid);
 0219                int rid = (int)(token & TokenTypeIds.RIDMask);
 220
 0221                var tableRowCounts = rowCounts[token >> TokenTypeIds.RowIdBitCount];
 222
 0223                if (rid > tableRowCounts[generation].AggregateInserts)
 0224                {
 0225                    if (rid != tableRowCounts[generation].AggregateInserts + 1)
 0226                    {
 0227                        throw new BadImageFormatException(SR.EnCMapNotSorted);
 228                    }
 229
 230                    // insert:
 0231                    tableRowCounts[generation].AggregateInserts = rid;
 0232                }
 233                else
 0234                {
 235                    // update:
 0236                    tableRowCounts[generation].Updates++;
 0237                }
 0238            }
 0239        }
 240
 241        /// <summary>
 242        /// Given a handle of an entity in an aggregate metadata calculates
 243        /// a handle of the entity within the metadata generation it is defined in.
 244        /// </summary>
 245        /// <param name="handle">Handle of an entity in an aggregate metadata.</param>
 246        /// <param name="generation">The generation the entity is defined in.</param>
 247        /// <returns>Handle of the entity within the metadata generation <paramref name="generation"/>.</returns>
 248        public Handle GetGenerationHandle(Handle handle, out int generation)
 0249        {
 0250            if (handle.IsVirtual)
 0251            {
 252                // TODO: if a virtual handle is connected to real handle then translate the rid,
 253                // otherwise return vhandle and base.
 0254                throw new NotSupportedException();
 255            }
 256
 0257            if (handle.IsHeapHandle)
 0258            {
 0259                int heapOffset = handle.Offset;
 260
 261                HeapIndex heapIndex;
 0262                MetadataTokens.TryGetHeapIndex(handle.Kind, out heapIndex);
 263
 0264                var sizes = _heapSizes[(int)heapIndex];
 265
 266                // #Guid heap offset is 1-based, other heaps have 0-based offset:
 0267                var size = (handle.Type == HandleType.Guid) ? heapOffset - 1 : heapOffset;
 268
 0269                generation = sizes.BinarySearch(size);
 0270                if (generation >= 0)
 0271                {
 0272                    Debug.Assert(sizes[generation] == size);
 273
 274                    // the index points to the start of the next generation that added data to the heap:
 275                    do
 0276                    {
 0277                        generation++;
 0278                    }
 0279                    while (generation < sizes.Length && sizes[generation] == size);
 0280                }
 281                else
 0282                {
 0283                    generation = ~generation;
 0284                }
 285
 0286                if (generation >= sizes.Length)
 0287                {
 0288                    throw new ArgumentException(SR.HandleBelongsToFutureGeneration, nameof(handle));
 289                }
 290
 291                // GUID heap accumulates - previous heap is copied to the next generation
 0292                int relativeHeapOffset = (handle.Type == HandleType.Guid || generation == 0) ? heapOffset : heapOffset -
 293
 0294                return new Handle((byte)handle.Type, relativeHeapOffset);
 295            }
 296            else
 0297            {
 0298                int rowId = handle.RowId;
 299
 0300                var sizes = _rowCounts[(int)handle.Type];
 301
 0302                generation = sizes.BinarySearch(new RowCounts { AggregateInserts = rowId });
 0303                if (generation >= 0)
 0304                {
 0305                    Debug.Assert(sizes[generation].AggregateInserts == rowId);
 306
 307                    // the row is in a generation that inserted exactly one row -- the one that we are looking for;
 308                    // or it's in a preceding generation if the current one didn't insert any rows of the kind:
 0309                    while (generation > 0 && sizes[generation - 1].AggregateInserts == rowId)
 0310                    {
 0311                        generation--;
 0312                    }
 0313                }
 314                else
 0315                {
 316                    // the row is in a generation that inserted multiple new rows:
 0317                    generation = ~generation;
 318
 0319                    if (generation >= sizes.Length)
 0320                    {
 0321                        throw new ArgumentException(SR.HandleBelongsToFutureGeneration, nameof(handle));
 322                    }
 0323                }
 324
 325                // In each delta table updates always precede inserts.
 0326                int relativeRowId = (generation == 0) ? rowId :
 0327                    rowId -
 0328                    sizes[generation - 1].AggregateInserts +
 0329                    sizes[generation].Updates;
 330
 0331                return new Handle((byte)handle.Type, relativeRowId);
 332            }
 0333        }
 334    }
 335}
 336

Methods/Properties

CompareTo(System.Reflection.Metadata.Ecma335.MetadataAggregator/RowCounts)
ToString()
.ctor(System.Reflection.Metadata.MetadataReader,System.Collections.Generic.IReadOnlyList`1<System.Reflection.Metadata.MetadataReader>)
.ctor(System.Collections.Generic.IReadOnlyList`1<System.Int32>,System.Collections.Generic.IReadOnlyList`1<System.Int32>,System.Collections.Generic.IReadOnlyList`1<System.Reflection.Metadata.MetadataReader>)
.ctor(System.Reflection.Metadata.MetadataReader,System.Collections.Generic.IReadOnlyList`1<System.Int32>,System.Collections.Generic.IReadOnlyList`1<System.Int32>,System.Collections.Generic.IReadOnlyList`1<System.Reflection.Metadata.MetadataReader>)
.ctor(System.Reflection.Metadata.Ecma335.MetadataAggregator/RowCounts[][],System.Int32[][])
CalculateBaseCounts(System.Reflection.Metadata.MetadataReader,System.Collections.Generic.IReadOnlyList`1<System.Int32>&,System.Collections.Generic.IReadOnlyList`1<System.Int32>&)
CalculateHeapSizes(System.Collections.Generic.IReadOnlyList`1<System.Int32>,System.Collections.Generic.IReadOnlyList`1<System.Reflection.Metadata.MetadataReader>)
CalculateRowCounts(System.Collections.Generic.IReadOnlyList`1<System.Int32>,System.Collections.Generic.IReadOnlyList`1<System.Reflection.Metadata.MetadataReader>)
ToImmutable(T[][])
GetBaseRowCounts(System.Collections.Generic.IReadOnlyList`1<System.Int32>,System.Int32)
CalculateDeltaRowCountsForGeneration(System.Reflection.Metadata.Ecma335.MetadataAggregator/RowCounts[][],System.Int32,System.Reflection.Metadata.Ecma335.EnCMapTableReader&)
GetGenerationHandle(System.Reflection.Metadata.Handle,System.Int32&)