diff --git a/src/AsmResolver.DotNet/Builder/DotNetDirectoryBuffer.MemberTree.cs b/src/AsmResolver.DotNet/Builder/DotNetDirectoryBuffer.MemberTree.cs index 8e2e2ee27..617253eef 100644 --- a/src/AsmResolver.DotNet/Builder/DotNetDirectoryBuffer.MemberTree.cs +++ b/src/AsmResolver.DotNet/Builder/DotNetDirectoryBuffer.MemberTree.cs @@ -132,6 +132,9 @@ public void DefineTypes(IEnumerable types) var typeDefTable = Metadata.TablesStream.GetTable(TableIndex.TypeDef); var nestedClassTable = Metadata.TablesStream.GetSortedTable(TableIndex.NestedClass); + if (types is ICollection collection) + typeDefTable.EnsureCapacity(typeDefTable.Count + collection.Count); + foreach (var type in types) { // At this point, we might not have added all type defs/refs/specs yet, so we cannot determine @@ -177,6 +180,8 @@ public void DefineTypes(IEnumerable types) public void DefineFields(IEnumerable fields) { var table = Metadata.TablesStream.GetTable(TableIndex.Field); + if (fields is ICollection collection) + table.EnsureCapacity(table.Count + collection.Count); foreach (var field in fields) { @@ -197,6 +202,8 @@ public void DefineFields(IEnumerable fields) public void DefineMethods(IEnumerable methods) { var table = Metadata.TablesStream.GetTable(TableIndex.Method); + if (methods is ICollection collection) + table.EnsureCapacity(table.Count + collection.Count); foreach (var method in methods) { @@ -227,6 +234,8 @@ public void DefineMethods(IEnumerable methods) public void DefineParameters(IEnumerable parameters) { var table = Metadata.TablesStream.GetTable(TableIndex.Param); + if (parameters is ICollection collection) + table.EnsureCapacity(table.Count + collection.Count); foreach (var parameter in parameters) { @@ -247,6 +256,8 @@ public void DefineParameters(IEnumerable parameters) public void DefineProperties(IEnumerable properties) { var table = Metadata.TablesStream.GetTable(TableIndex.Property); + if (properties is ICollection collection) + table.EnsureCapacity(table.Count + collection.Count); foreach (var property in properties) { @@ -267,6 +278,8 @@ public void DefineProperties(IEnumerable properties) public void DefineEvents(IEnumerable events) { var table = Metadata.TablesStream.GetTable(TableIndex.Event); + if (events is ICollection collection) + table.EnsureCapacity(table.Count + collection.Count); foreach (var @event in events) { @@ -299,6 +312,17 @@ public void FinalizeTypes() uint propertyList = 1; uint eventList = 1; + tablesStream.GetTable(TableIndex.FieldPtr) + .EnsureCapacity(tablesStream.GetTable(TableIndex.Field).Count); + tablesStream.GetTable(TableIndex.MethodPtr) + .EnsureCapacity(tablesStream.GetTable(TableIndex.Method).Count); + tablesStream.GetTable(TableIndex.ParamPtr) + .EnsureCapacity(tablesStream.GetTable(TableIndex.Param).Count); + tablesStream.GetTable(TableIndex.PropertyPtr) + .EnsureCapacity(tablesStream.GetTable(TableIndex.Property).Count); + tablesStream.GetTable(TableIndex.EventPtr) + .EnsureCapacity(tablesStream.GetTable(TableIndex.Event).Count); + for (uint rid = 1; rid <= typeDefTable.Count; rid++) { var typeToken = new MetadataToken(TableIndex.TypeDef, rid); diff --git a/src/AsmResolver.DotNet/Builder/Metadata/Tables/DistinctMetadataTableBuffer.cs b/src/AsmResolver.DotNet/Builder/Metadata/Tables/DistinctMetadataTableBuffer.cs index 2fba58233..2a7ecdef5 100644 --- a/src/AsmResolver.DotNet/Builder/Metadata/Tables/DistinctMetadataTableBuffer.cs +++ b/src/AsmResolver.DotNet/Builder/Metadata/Tables/DistinctMetadataTableBuffer.cs @@ -44,6 +44,16 @@ public TRow this[uint rid] } } + /// + public void EnsureCapacity(int capacity) + { + _underlyingBuffer.EnsureCapacity(capacity); + +#if NETSTANDARD2_1_OR_GREATER + _entries.EnsureCapacity(capacity); +#endif + } + /// public ref TRow GetRowRef(uint rid) => ref _underlyingBuffer.GetRowRef(rid); diff --git a/src/AsmResolver.DotNet/Builder/Metadata/Tables/IMetadataTableBuffer.cs b/src/AsmResolver.DotNet/Builder/Metadata/Tables/IMetadataTableBuffer.cs index bcf1b8bfd..66a6c1836 100644 --- a/src/AsmResolver.DotNet/Builder/Metadata/Tables/IMetadataTableBuffer.cs +++ b/src/AsmResolver.DotNet/Builder/Metadata/Tables/IMetadataTableBuffer.cs @@ -44,6 +44,12 @@ TRow this[uint rid] set; } + /// + /// Ensures the capacity of the table buffer is at least the provided amount of elements. + /// + /// The number of elements to store. + void EnsureCapacity(int capacity); + /// /// Gets or sets a reference to a row in the metadata table. /// diff --git a/src/AsmResolver.DotNet/Builder/Metadata/Tables/UnsortedMetadataTableBuffer.cs b/src/AsmResolver.DotNet/Builder/Metadata/Tables/UnsortedMetadataTableBuffer.cs index e94a81d44..8e9d4395f 100644 --- a/src/AsmResolver.DotNet/Builder/Metadata/Tables/UnsortedMetadataTableBuffer.cs +++ b/src/AsmResolver.DotNet/Builder/Metadata/Tables/UnsortedMetadataTableBuffer.cs @@ -36,6 +36,13 @@ public virtual TRow this[uint rid] set => _entries[(int) (rid - 1)] = value; } + /// + public void EnsureCapacity(int capacity) + { + if (_entries.Capacity < capacity) + _entries.Capacity = capacity; + } + /// public ref TRow GetRowRef(uint rid) => ref _entries.GetElementRef((int)(rid - 1)); @@ -49,6 +56,9 @@ public virtual MetadataToken Add(in TRow row) /// public void FlushToTable() { + if (_table.Capacity < _entries.Count) + _table.Capacity = _entries.Count; + foreach (var row in _entries) _table.Add(row); } diff --git a/src/AsmResolver.DotNet/Collections/MethodSemanticsCollection.cs b/src/AsmResolver.DotNet/Collections/MethodSemanticsCollection.cs index 0310136af..35cfefda3 100644 --- a/src/AsmResolver.DotNet/Collections/MethodSemanticsCollection.cs +++ b/src/AsmResolver.DotNet/Collections/MethodSemanticsCollection.cs @@ -20,6 +20,16 @@ public MethodSemanticsCollection(IHasSemantics owner) { } + /// + /// Creates a new instance of the class. + /// + /// The owner of the collection. + /// The initial number of elements the collection can store. + public MethodSemanticsCollection(IHasSemantics owner, int capacity) + : base(owner, capacity) + { + } + internal bool ValidateMembership { get; diff --git a/src/AsmResolver.DotNet/Serialized/SerializedGenericParameter.cs b/src/AsmResolver.DotNet/Serialized/SerializedGenericParameter.cs index 841c47adc..3d876c1d5 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedGenericParameter.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedGenericParameter.cs @@ -58,10 +58,11 @@ public SerializedGenericParameter(ModuleReaderContext context, MetadataToken tok /// protected override IList GetConstraints() { - var result = new OwnedCollection(this); - var module = _context.ParentModule; - foreach (uint rid in module.GetGenericParameterConstraints(MetadataToken)) + var rids = module.GetGenericParameterConstraints(MetadataToken); + var result = new OwnedCollection(this, rids.Count); + + foreach (uint rid in rids) { var constraintToken = new MetadataToken(TableIndex.GenericParamConstraint, rid); result.Add((GenericParameterConstraint) module.LookupMember(constraintToken)); diff --git a/src/AsmResolver.DotNet/Serialized/SerializedMethodDefinition.cs b/src/AsmResolver.DotNet/Serialized/SerializedMethodDefinition.cs index ed4ed367c..c35f28b9e 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedMethodDefinition.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedMethodDefinition.cs @@ -81,9 +81,10 @@ protected override IList GetSecurityDeclarations() => /// protected override IList GetParameterDefinitions() { - var result = new OwnedCollection(this); + var parameterRange = _context.ParentModule.GetParameterRange(MetadataToken.Rid); + var result = new OwnedCollection(this, parameterRange.Count); - foreach (var token in _context.ParentModule.GetParameterRange(MetadataToken.Rid)) + foreach (var token in parameterRange) { if (_context.ParentModule.TryLookupMember(token, out var member) && member is ParameterDefinition parameter) result.Add(parameter); @@ -108,9 +109,10 @@ protected override IList GetParameterDefinitions() /// protected override IList GetGenericParameters() { - var result = new OwnedCollection(this); + var rids = _context.ParentModule.GetGenericParameters(MetadataToken); + var result = new OwnedCollection(this, rids.Count); - foreach (uint rid in _context.ParentModule.GetGenericParameters(MetadataToken)) + foreach (uint rid in rids) { if (_context.ParentModule.TryLookupMember(new MetadataToken(TableIndex.GenericParam, rid), out var member) && member is GenericParameter genericParameter) diff --git a/src/AsmResolver.DotNet/Serialized/SerializedModuleDefinition.Metadata.cs b/src/AsmResolver.DotNet/Serialized/SerializedModuleDefinition.Metadata.cs index f7e13d09f..e81e548aa 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedModuleDefinition.Metadata.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedModuleDefinition.Metadata.cs @@ -59,7 +59,7 @@ private OneToManyRelation InitializeTypeDefinitionTree() return typeDefTree; } - internal IEnumerable GetNestedTypeRids(uint enclosingTypeRid) + internal ICollection GetNestedTypeRids(uint enclosingTypeRid) { EnsureTypeDefinitionTreeInitialized(); return _typeDefTree.GetValues(enclosingTypeRid); @@ -107,8 +107,8 @@ private void InitializeMethodSemantics() var semanticsTable = tablesStream.GetTable(TableIndex.MethodSemantics); var encoder = tablesStream.GetIndexEncoder(CodedIndex.HasSemantics); - var semantics = new OneToManyRelation(); - var semanticMethods = new Dictionary(); + var semantics = new OneToManyRelation(semanticsTable.Count); + var semanticMethods = new Dictionary(semanticsTable.Count); for (int i = 0; i < semanticsTable.Count; i++) { var methodSemanticsRow = semanticsTable[i]; @@ -123,7 +123,7 @@ private void InitializeMethodSemantics() Interlocked.CompareExchange(ref _semanticMethods, semanticMethods, null); } - internal IEnumerable GetMethodSemantics(MetadataToken owner) + internal ICollection GetMethodSemantics(MetadataToken owner) { EnsureMethodSemanticsInitialized(); return _semantics.GetValues(owner); @@ -155,7 +155,7 @@ private OneToOneRelation GetConstants() var constantTable = tablesStream.GetTable(TableIndex.Constant); var encoder = tablesStream.GetIndexEncoder(CodedIndex.HasConstant); - var constants = new OneToOneRelation(); + var constants = new OneToOneRelation(constantTable.Count); for (int i = 0; i < constantTable.Count; i++) { var ownerToken = encoder.DecodeIndex(constantTable[i].Parent); @@ -199,7 +199,7 @@ private OneToManyRelation InitializeCustomAttributes() var attributeTable = tablesStream.GetTable(TableIndex.CustomAttribute); var encoder = tablesStream.GetIndexEncoder(CodedIndex.HasCustomAttribute); - var customAttributes = new OneToManyRelation(); + var customAttributes = new OneToManyRelation(attributeTable.Count); for (int i = 0; i < attributeTable.Count; i++) { var ownerToken = encoder.DecodeIndex(attributeTable[i].Parent); @@ -228,9 +228,10 @@ internal MetadataToken GetCustomAttributeOwner(uint attributeRid) internal IList GetCustomAttributeCollection(IHasCustomAttribute owner) { EnsureCustomAttributesInitialized(); - var result = new OwnedCollection(owner); + var rids = _customAttributes.GetValues(owner.MetadataToken); + var result = new OwnedCollection(owner, rids.Count); - foreach (uint rid in _customAttributes.GetValues(owner.MetadataToken)) + foreach (uint rid in rids) { var attribute = (CustomAttribute) LookupMember(new MetadataToken(TableIndex.CustomAttribute, rid)); result.Add(attribute); @@ -252,7 +253,7 @@ private OneToManyRelation InitializeSecurityDeclarations() var declarationTable = tablesStream.GetTable(TableIndex.DeclSecurity); var encoder = tablesStream.GetIndexEncoder(CodedIndex.HasDeclSecurity); - var securityDeclarations = new OneToManyRelation(); + var securityDeclarations = new OneToManyRelation(declarationTable.Count); for (int i = 0; i < declarationTable.Count; i++) { var ownerToken = encoder.DecodeIndex(declarationTable[i].Parent); @@ -272,9 +273,10 @@ internal MetadataToken GetSecurityDeclarationOwner(uint attributeRid) internal IList GetSecurityDeclarationCollection(IHasSecurityDeclaration owner) { EnsureSecurityDeclarationsInitialized(); - var result = new OwnedCollection(owner); + var rids = _securityDeclarations.GetValues(owner.MetadataToken); + var result = new OwnedCollection(owner, rids.Count); - foreach (uint rid in _securityDeclarations.GetValues(owner.MetadataToken)) + foreach (uint rid in rids) { var attribute = (SecurityDeclaration) LookupMember(new MetadataToken(TableIndex.DeclSecurity, rid)); result.Add(attribute); @@ -296,7 +298,7 @@ private OneToManyRelation InitializeGenericParameters() var parameterTable = tablesStream.GetTable(TableIndex.GenericParam); var encoder = tablesStream.GetIndexEncoder(CodedIndex.TypeOrMethodDef); - var genericParameters = new OneToManyRelation(); + var genericParameters = new OneToManyRelation(parameterTable.Count); for (int i = 0; i < parameterTable.Count; i++) { var ownerToken = encoder.DecodeIndex(parameterTable[i].Owner); @@ -331,7 +333,7 @@ private OneToManyRelation InitializeGenericParameterConstra var tablesStream = DotNetDirectory.Metadata!.GetStream(); var constraintTable = tablesStream.GetTable(TableIndex.GenericParamConstraint); - var constraints = new OneToManyRelation(); + var constraints = new OneToManyRelation(constraintTable.Count); for (int i = 0; i < constraintTable.Count; i++) { var ownerToken = new MetadataToken(TableIndex.GenericParam, constraintTable[i].Owner); @@ -366,7 +368,7 @@ private OneToManyRelation InitializeInterfaces() var tablesStream = DotNetDirectory.Metadata!.GetStream(); var interfaceImplTable = tablesStream.GetTable(TableIndex.InterfaceImpl); - var interfaces = new OneToManyRelation(); + var interfaces = new OneToManyRelation(interfaceImplTable.Count); for (int i = 0; i < interfaceImplTable.Count; i++) { var ownerToken = new MetadataToken(TableIndex.TypeDef, interfaceImplTable[i].Class); @@ -401,7 +403,7 @@ private OneToManyRelation InitializeMethodImplementations() var tablesStream = DotNetDirectory.Metadata!.GetStream(); var methodImplTable = tablesStream.GetTable(TableIndex.MethodImpl); - var methodImplementations = new OneToManyRelation(); + var methodImplementations = new OneToManyRelation(methodImplTable.Count); for (int i = 0; i < methodImplTable.Count; i++) { var ownerToken = new MetadataToken(TableIndex.TypeDef, methodImplTable[i].Class); @@ -430,7 +432,7 @@ private OneToOneRelation InitializeClassLayouts() var tablesStream = DotNetDirectory.Metadata!.GetStream(); var layoutTable = tablesStream.GetTable(TableIndex.ClassLayout); - var layouts = new OneToOneRelation(); + var layouts = new OneToOneRelation(layoutTable.Count); for (int i = 0; i < layoutTable.Count; i++) { var ownerToken = new MetadataToken(TableIndex.TypeDef, layoutTable[i].Parent); @@ -460,7 +462,7 @@ private OneToOneRelation InitializeImplementationMaps() var mapTable = tablesStream.GetTable(TableIndex.ImplMap); var encoder = tablesStream.GetIndexEncoder(CodedIndex.TypeOrMethodDef); - var maps = new OneToOneRelation(); + var maps = new OneToOneRelation(mapTable.Count); for (int i = 0; i < mapTable.Count; i++) { var ownerToken = encoder.DecodeIndex(mapTable[i].MemberForwarded); @@ -495,7 +497,7 @@ private OneToOneRelation InitializeFieldRvas() var tablesStream = DotNetDirectory.Metadata!.GetStream(); var rvaTable = tablesStream.GetTable(TableIndex.FieldRva); - var rvas = new OneToOneRelation(); + var rvas = new OneToOneRelation(rvaTable.Count); for (int i = 0; i < rvaTable.Count; i++) { var ownerToken = new MetadataToken(TableIndex.Field, rvaTable[i].Field); @@ -525,7 +527,7 @@ private OneToOneRelation InitializeFieldMarshals() var marshalTable = tablesStream.GetTable(TableIndex.FieldMarshal); var encoder = tablesStream.GetIndexEncoder(CodedIndex.HasFieldMarshal); - var marshals = new OneToOneRelation(); + var marshals = new OneToOneRelation(marshalTable.Count); for (int i = 0; i < marshalTable.Count; i++) { var ownerToken = encoder.DecodeIndex(marshalTable[i].Parent); @@ -571,7 +573,7 @@ private OneToOneRelation InitializeFieldLayouts() var tablesStream = DotNetDirectory.Metadata!.GetStream(); var layoutTable = tablesStream.GetTable(TableIndex.FieldLayout); - var fieldLayouts = new OneToOneRelation(); + var fieldLayouts = new OneToOneRelation(layoutTable.Count); for (int i = 0; i < layoutTable.Count; i++) { var ownerToken = new MetadataToken(TableIndex.Field, layoutTable[i].Field); diff --git a/src/AsmResolver.DotNet/Serialized/SerializedModuleDefinition.cs b/src/AsmResolver.DotNet/Serialized/SerializedModuleDefinition.cs index e147de1e8..5ed4f0d4a 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedModuleDefinition.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedModuleDefinition.cs @@ -204,12 +204,18 @@ protected override IList GetTopLevelTypes() { EnsureTypeDefinitionTreeInitialized(); - var types = new OwnedCollection(this); - var typeDefTable = ReaderContext.Metadata .GetStream() .GetTable(TableIndex.TypeDef); + int nestedTypeCount = ReaderContext.Metadata + .GetStream() + .GetTable(TableIndex.NestedClass) + .Count; + + var types = new OwnedCollection(this, + typeDefTable.Count - nestedTypeCount); + for (int i = 0; i < typeDefTable.Count; i++) { uint rid = (uint) i + 1; @@ -226,12 +232,12 @@ protected override IList GetTopLevelTypes() /// protected override IList GetAssemblyReferences() { - var result = new OwnedCollection(this); - var table = ReaderContext.Metadata .GetStream() .GetTable(TableIndex.AssemblyRef); + var result = new OwnedCollection(this, table.Count); + // Don't use the member factory here, this method may be called before the member factory is initialized. for (int i = 0; i < table.Count; i++) { @@ -245,12 +251,12 @@ protected override IList GetAssemblyReferences() /// protected override IList GetModuleReferences() { - var result = new OwnedCollection(this); - var table = ReaderContext.Metadata .GetStream() .GetTable(TableIndex.ModuleRef); + var result = new OwnedCollection(this, table.Count); + for (int i = 0; i < table.Count; i++) { var token = new MetadataToken(TableIndex.ModuleRef, (uint) i + 1); @@ -264,12 +270,12 @@ protected override IList GetModuleReferences() /// protected override IList GetFileReferences() { - var result = new OwnedCollection(this); - var table = ReaderContext.Metadata .GetStream() .GetTable(TableIndex.File); + var result = new OwnedCollection(this, table.Count); + for (int i = 0; i < table.Count; i++) { var token = new MetadataToken(TableIndex.File, (uint) i + 1); @@ -283,12 +289,12 @@ protected override IList GetFileReferences() /// protected override IList GetResources() { - var result = new OwnedCollection(this); - var table = ReaderContext.Metadata .GetStream() .GetTable(TableIndex.ManifestResource); + var result = new OwnedCollection(this, table.Count); + for (int i = 0; i < table.Count; i++) { var token = new MetadataToken(TableIndex.ManifestResource, (uint) i + 1); @@ -302,12 +308,12 @@ protected override IList GetResources() /// protected override IList GetExportedTypes() { - var result = new OwnedCollection(this); - var table = ReaderContext.Metadata .GetStream() .GetTable(TableIndex.ExportedType); + var result = new OwnedCollection(this, table.Count); + for (int i = 0; i < table.Count; i++) { var token = new MetadataToken(TableIndex.ExportedType, (uint) i + 1); @@ -350,12 +356,11 @@ protected override IList GetExportedTypes() if (assemblyTable.Count > 0) { - var assembly = new SerializedAssemblyDefinition( + return new SerializedAssemblyDefinition( ReaderContext, new MetadataToken(TableIndex.Assembly, 1), assemblyTable[0], this); - return assembly; } return null; diff --git a/src/AsmResolver.DotNet/Serialized/SerializedPropertyDefinition.cs b/src/AsmResolver.DotNet/Serialized/SerializedPropertyDefinition.cs index 9739939fd..b1f1fa227 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedPropertyDefinition.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedPropertyDefinition.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using AsmResolver.DotNet.Signatures; using AsmResolver.DotNet.Collections; using AsmResolver.PE.DotNet.Metadata; @@ -70,11 +71,13 @@ public SerializedPropertyDefinition(ModuleReaderContext context, MetadataToken t /// protected override IList GetSemantics() { - var result = new MethodSemanticsCollection(this); + var module = _context.ParentModule; + var rids = module.GetMethodSemantics(MetadataToken); + + var result = new MethodSemanticsCollection(this, rids.Count); result.ValidateMembership = false; - var module = _context.ParentModule; - foreach (uint rid in module.GetMethodSemantics(MetadataToken)) + foreach (uint rid in rids) { var semanticsToken = new MetadataToken(TableIndex.MethodSemantics, rid); result.Add((MethodSemantics) module.LookupMember(semanticsToken)); diff --git a/src/AsmResolver.DotNet/Serialized/SerializedTypeDefinition.cs b/src/AsmResolver.DotNet/Serialized/SerializedTypeDefinition.cs index 9b87e76e3..2ee5afe6b 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedTypeDefinition.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedTypeDefinition.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using AsmResolver.Collections; using AsmResolver.DotNet.Collections; using AsmResolver.PE.DotNet.Metadata; @@ -67,9 +68,9 @@ public SerializedTypeDefinition(ModuleReaderContext context, MetadataToken token /// protected override IList GetNestedTypes() { - var result = new OwnedCollection(this); - var rids = _context.ParentModule.GetNestedTypeRids(MetadataToken.Rid); + var result = new OwnedCollection(this, rids.Count); + foreach (uint rid in rids) { var nestedType = (TypeDefinition) _context.ParentModule.LookupMember(new MetadataToken(TableIndex.TypeDef, rid)); @@ -107,7 +108,7 @@ protected override IList GetEvents() => private IList CreateMemberCollection(MetadataRange range) where TMember : class, IMetadataMember, IOwnedCollectionElement { - var result = new OwnedCollection(this); + var result = new OwnedCollection(this, range.Count); foreach (var token in range) result.Add((TMember) _context.ParentModule.LookupMember(token)); @@ -126,9 +127,10 @@ protected override IList GetSecurityDeclarations() => /// protected override IList GetGenericParameters() { - var result = new OwnedCollection(this); + var rids = _context.ParentModule.GetGenericParameters(MetadataToken); + var result = new OwnedCollection(this, rids.Count); - foreach (uint rid in _context.ParentModule.GetGenericParameters(MetadataToken)) + foreach (uint rid in rids) { if (_context.ParentModule.TryLookupMember(new MetadataToken(TableIndex.GenericParam, rid), out var member) && member is GenericParameter genericParameter) @@ -143,9 +145,9 @@ protected override IList GetGenericParameters() /// protected override IList GetInterfaces() { - var result = new OwnedCollection(this); - var rids = _context.ParentModule.GetInterfaceImplementationRids(MetadataToken); + var result = new OwnedCollection(this, rids.Count); + foreach (uint rid in rids) { if (_context.ParentModule.TryLookupMember(new MetadataToken(TableIndex.InterfaceImpl, rid), out var member) @@ -161,13 +163,13 @@ protected override IList GetInterfaces() /// protected override IList GetMethodImplementations() { - var result = new List(); - var tablesStream = _context.Metadata.GetStream(); var table = tablesStream.GetTable(TableIndex.MethodImpl); var encoder = tablesStream.GetIndexEncoder(CodedIndex.MethodDefOrRef); var rids = _context.ParentModule.GetMethodImplementationRids(MetadataToken); + var result = new List(rids.Count); + foreach (uint rid in rids) { var row = table.GetByRid(rid); diff --git a/src/AsmResolver.DotNet/Signatures/CustomAttributeSignature.cs b/src/AsmResolver.DotNet/Signatures/CustomAttributeSignature.cs index 1676016d9..15d4834fb 100644 --- a/src/AsmResolver.DotNet/Signatures/CustomAttributeSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/CustomAttributeSignature.cs @@ -12,6 +12,8 @@ namespace AsmResolver.DotNet.Signatures /// public class CustomAttributeSignature : ExtendableBlobSignature { + private readonly List _fixedArguments; + private readonly List _namedArguments; private const ushort CustomAttributeSignaturePrologue = 0x0001; /// @@ -35,18 +37,20 @@ public class CustomAttributeSignature : ExtendableBlobSignature // Read fixed arguments. var parameterTypes = ctor.Signature?.ParameterTypes ?? Array.Empty(); + result._fixedArguments.Capacity = parameterTypes.Count; for (int i = 0; i < parameterTypes.Count; i++) { var argument = CustomAttributeArgument.FromReader(context, parameterTypes[i], ref reader); - result.FixedArguments.Add(argument); + result._fixedArguments.Add(argument); } // Read named arguments. ushort namedArgumentCount = reader.ReadUInt16(); + result._namedArguments.Capacity = namedArgumentCount; for (int i = 0; i < namedArgumentCount; i++) { var argument = CustomAttributeNamedArgument.FromReader(context, ref reader); - result.NamedArguments.Add(argument); + result._namedArguments.Add(argument); } return result; @@ -73,25 +77,19 @@ public CustomAttributeSignature(IEnumerable fixedArgume /// public CustomAttributeSignature(IEnumerable fixedArguments, IEnumerable namedArguments) { - FixedArguments = new List(fixedArguments); - NamedArguments = new List(namedArguments); + _fixedArguments = new List(fixedArguments); + _namedArguments = new List(namedArguments); } /// /// Gets a collection of fixed arguments that are passed onto the constructor of the attribute. /// - public IList FixedArguments - { - get; - } + public IList FixedArguments => _fixedArguments; /// /// Gets a collection of values that are assigned to fields and/or members of the attribute class. /// - public IList NamedArguments - { - get; - } + public IList NamedArguments => _namedArguments; /// public override string ToString() diff --git a/src/AsmResolver.DotNet/Signatures/GenericInstanceMethodSignature.cs b/src/AsmResolver.DotNet/Signatures/GenericInstanceMethodSignature.cs index 2e6286418..291a07c25 100644 --- a/src/AsmResolver.DotNet/Signatures/GenericInstanceMethodSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/GenericInstanceMethodSignature.cs @@ -21,14 +21,14 @@ public class GenericInstanceMethodSignature : CallingConventionSignature, IGener } var attributes = (CallingConventionAttributes) reader.ReadByte(); - var result = new GenericInstanceMethodSignature(attributes); if (!reader.TryReadCompressedUInt32(out uint count)) { context.ReaderContext.BadImage("Invalid number of type arguments in generic method signature."); - return result; + return new GenericInstanceMethodSignature(attributes); } + var result = new GenericInstanceMethodSignature(attributes, (int) count); for (int i = 0; i < count; i++) result.TypeArguments.Add(TypeSignature.FromReader(context, ref reader)); @@ -44,6 +44,17 @@ public GenericInstanceMethodSignature(CallingConventionAttributes attributes) { } + /// + /// Creates a new instantiation signature for a generic method. + /// + /// The attributes. + /// The initial number of elements that the property can store. + public GenericInstanceMethodSignature(CallingConventionAttributes attributes, int capacity) + : base(attributes) + { + TypeArguments = new List(capacity); + } + /// /// Creates a new instantiation signature for a generic method with the provided type arguments. /// diff --git a/src/AsmResolver.DotNet/Signatures/MethodSignature.cs b/src/AsmResolver.DotNet/Signatures/MethodSignature.cs index 67d530c14..818452aa6 100644 --- a/src/AsmResolver.DotNet/Signatures/MethodSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/MethodSignature.cs @@ -20,8 +20,10 @@ public class MethodSignature : MethodSignatureBase /// The method signature. public static MethodSignature FromReader(in BlobReadContext context, ref BinaryStreamReader reader) { - var result = new MethodSignature((CallingConventionAttributes) reader.ReadByte(), - context.ReaderContext.ParentModule.CorLibTypeFactory.Void, Enumerable.Empty()); + var result = new MethodSignature( + (CallingConventionAttributes) reader.ReadByte(), + context.ReaderContext.ParentModule.CorLibTypeFactory.Void, + Enumerable.Empty()); // Generic parameter count. if (result.IsGeneric) diff --git a/src/AsmResolver.DotNet/Signatures/MethodSignatureBase.cs b/src/AsmResolver.DotNet/Signatures/MethodSignatureBase.cs index 4e58f029a..191bc2710 100644 --- a/src/AsmResolver.DotNet/Signatures/MethodSignatureBase.cs +++ b/src/AsmResolver.DotNet/Signatures/MethodSignatureBase.cs @@ -10,28 +10,27 @@ namespace AsmResolver.DotNet.Signatures /// public abstract class MethodSignatureBase : MemberSignature { + private readonly List _parameterTypes; + /// /// Initializes the base of a method signature. /// - /// - /// - /// + /// The attributes associated to the signature. + /// The return type of the member. + /// The types of all (non-sentinel) parameters. protected MethodSignatureBase( CallingConventionAttributes attributes, TypeSignature memberReturnType, IEnumerable parameterTypes) : base(attributes, memberReturnType) { - ParameterTypes = new List(parameterTypes); + _parameterTypes = new List(parameterTypes); } /// /// Gets an ordered list of types indicating the types of the parameters that this member defines. /// - public IList ParameterTypes - { - get; - } + public IList ParameterTypes => _parameterTypes; /// /// Gets or sets the type of the value that this member returns. @@ -120,6 +119,7 @@ protected void ReadParametersAndReturnType(in BlobReadContext context, ref Binar ReturnType = TypeSignature.FromReader(context, ref reader); // Parameter types. + _parameterTypes.Capacity = (int) parameterCount; bool sentinel = false; for (int i = 0; i < parameterCount; i++) { diff --git a/src/AsmResolver.DotNet/Signatures/Types/ArrayTypeSignature.cs b/src/AsmResolver.DotNet/Signatures/Types/ArrayTypeSignature.cs index 193142038..e81140d88 100644 --- a/src/AsmResolver.DotNet/Signatures/Types/ArrayTypeSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/Types/ArrayTypeSignature.cs @@ -87,7 +87,7 @@ public IList Dimensions return signature; } - var sizes = new List(); + var sizes = new List((int) numSizes); for (int i = 0; i < numSizes; i++) { if (!reader.TryReadCompressedUInt32(out uint size)) @@ -106,7 +106,7 @@ public IList Dimensions return signature; } - var loBounds = new List(); + var loBounds = new List((int) numLoBounds); for (int i = 0; i < numLoBounds; i++) { if (!reader.TryReadCompressedUInt32(out uint bound)) diff --git a/src/AsmResolver.DotNet/Signatures/Types/FunctionPointerTypeSignature.cs b/src/AsmResolver.DotNet/Signatures/Types/FunctionPointerTypeSignature.cs index ff2acc2c8..1fe273fa5 100644 --- a/src/AsmResolver.DotNet/Signatures/Types/FunctionPointerTypeSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/Types/FunctionPointerTypeSignature.cs @@ -47,7 +47,7 @@ public MethodSignature Signature /// public override ITypeDefOrRef? GetUnderlyingTypeDefOrRef() => - Signature?.ReturnType?.Module?.CorLibTypeFactory.IntPtr.Type; + Signature.ReturnType.Module?.CorLibTypeFactory.IntPtr.Type; /// public override bool IsImportedInModule(ModuleDefinition module) => Signature.IsImportedInModule(module); diff --git a/src/AsmResolver.DotNet/Signatures/Types/GenericInstanceTypeSignature.cs b/src/AsmResolver.DotNet/Signatures/Types/GenericInstanceTypeSignature.cs index 7ed057d8b..e05310b12 100644 --- a/src/AsmResolver.DotNet/Signatures/Types/GenericInstanceTypeSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/Types/GenericInstanceTypeSignature.cs @@ -10,6 +10,8 @@ namespace AsmResolver.DotNet.Signatures.Types /// public class GenericInstanceTypeSignature : TypeSignature, IGenericArgumentsProvider { + private readonly List _typeArguments; + internal new static GenericInstanceTypeSignature FromReader(in BlobReadContext context, ref BinaryStreamReader reader) { var genericType = TypeSignature.FromReader(context, ref reader); @@ -21,8 +23,9 @@ public class GenericInstanceTypeSignature : TypeSignature, IGenericArgumentsProv return signature; } + signature._typeArguments.Capacity = (int) count; for (int i = 0; i < count; i++) - signature.TypeArguments.Add(TypeSignature.FromReader(context, ref reader)); + signature._typeArguments.Add(TypeSignature.FromReader(context, ref reader)); return signature; } @@ -53,7 +56,7 @@ private GenericInstanceTypeSignature(ITypeDefOrRef genericType, bool isValueType IEnumerable typeArguments) { GenericType = genericType; - TypeArguments = new List(typeArguments); + _typeArguments = new List(typeArguments); IsValueType = isValueType; } @@ -72,10 +75,7 @@ public ITypeDefOrRef GenericType /// /// Gets a collection of type arguments used to instantiate the generic type. /// - public IList TypeArguments - { - get; - } + public IList TypeArguments => _typeArguments; /// public override string? Name diff --git a/src/AsmResolver.PE/DotNet/Metadata/Tables/MetadataTable.cs b/src/AsmResolver.PE/DotNet/Metadata/Tables/MetadataTable.cs index 2b8f01739..94983d7d9 100644 --- a/src/AsmResolver.PE/DotNet/Metadata/Tables/MetadataTable.cs +++ b/src/AsmResolver.PE/DotNet/Metadata/Tables/MetadataTable.cs @@ -88,6 +88,15 @@ protected RefList Rows /// public virtual int Count => Rows.Count; + /// + /// Gets or sets the total number of rows that the underlying array can store. + /// + public int Capacity + { + get => Rows.Capacity; + set => Rows.Capacity = value; + } + /// public bool IsReadOnly => false; // TODO: it might be necessary later to make this configurable. diff --git a/src/AsmResolver/Collections/LazyList.cs b/src/AsmResolver/Collections/LazyList.cs index 90b584a75..0dd0cc0ce 100644 --- a/src/AsmResolver/Collections/LazyList.cs +++ b/src/AsmResolver/Collections/LazyList.cs @@ -12,7 +12,24 @@ namespace AsmResolver.Collections [DebuggerDisplay("Count = {" + nameof(Count) + "}")] public abstract class LazyList : IList { - private readonly List _items = new(); + private readonly List _items; + + /// + /// Creates a new, empty, uninitialized list. + /// + public LazyList() + { + _items = new List(); + } + + /// + /// Creates a new, empty, uninitialized list. + /// + /// The initial number of elements the list can store. + public LazyList(int capacity) + { + _items = new List(capacity); + } /// public TItem this[int index] @@ -39,6 +56,15 @@ public virtual int Count } } + /// + /// Gets or sets the total number of elements the list can contain before it has to resize its internal buffer. + /// + public int Capacity + { + get => _items.Capacity; + set => _items.Capacity = value; + } + /// public bool IsReadOnly => false; diff --git a/src/AsmResolver/Collections/OneToManyRelation.cs b/src/AsmResolver/Collections/OneToManyRelation.cs index 570768c7e..30df87146 100644 --- a/src/AsmResolver/Collections/OneToManyRelation.cs +++ b/src/AsmResolver/Collections/OneToManyRelation.cs @@ -11,8 +11,27 @@ public sealed class OneToManyRelation where TKey : notnull where TValue : notnull { - private readonly Dictionary> _memberLists = new(); - private readonly Dictionary _memberOwners = new(); + private readonly Dictionary> _memberLists; + private readonly Dictionary _memberOwners; + + /// + /// Creates a new, empty one-to-many relation mapping. + /// + public OneToManyRelation() + { + _memberLists = new Dictionary>(); + _memberOwners = new Dictionary(); + } + + /// + /// Creates a new, empty one-to-many relation mapping. + /// + /// The initial number of elements the relation can store. + public OneToManyRelation(int capacity) + { + _memberLists = new Dictionary>(capacity); + _memberOwners = new Dictionary(capacity); + } /// /// Registers a relation between two objects. diff --git a/src/AsmResolver/Collections/OneToOneRelation.cs b/src/AsmResolver/Collections/OneToOneRelation.cs index 881915925..dbefc7bcc 100644 --- a/src/AsmResolver/Collections/OneToOneRelation.cs +++ b/src/AsmResolver/Collections/OneToOneRelation.cs @@ -11,8 +11,27 @@ public sealed class OneToOneRelation where TKey : notnull where TValue : notnull { - private readonly Dictionary _keyToValue = new(); - private readonly Dictionary _valueToKey = new(); + private readonly Dictionary _keyToValue; + private readonly Dictionary _valueToKey; + + /// + /// Creates a new, empty one-to-one mapping. + /// + public OneToOneRelation() + { + _keyToValue = new Dictionary(); + _valueToKey = new Dictionary(); + } + + /// + /// Creates a new, empty one-to-one mapping. + /// + /// The initial number of elements the relation can store. + public OneToOneRelation(int capacity) + { + _keyToValue = new Dictionary(capacity); + _valueToKey = new Dictionary(capacity); + } /// /// Registers a one-to-one relation between two objects. diff --git a/src/AsmResolver/Collections/OwnedCollection.cs b/src/AsmResolver/Collections/OwnedCollection.cs index 82a7d6b33..6facee7e8 100644 --- a/src/AsmResolver/Collections/OwnedCollection.cs +++ b/src/AsmResolver/Collections/OwnedCollection.cs @@ -26,6 +26,17 @@ public OwnedCollection(TOwner owner) Owner = owner ?? throw new ArgumentNullException(nameof(owner)); } + /// + /// Creates a new empty collection that is owned by an object. + /// + /// The owner of the collection. + /// The initial number of elements the collection can store. + public OwnedCollection(TOwner owner, int capacity) + : base(capacity) + { + Owner = owner ?? throw new ArgumentNullException(nameof(owner)); + } + /// /// Gets the owner of the collection. /// diff --git a/src/AsmResolver/Collections/RefList.cs b/src/AsmResolver/Collections/RefList.cs index f454fe6ff..4fa091140 100644 --- a/src/AsmResolver/Collections/RefList.cs +++ b/src/AsmResolver/Collections/RefList.cs @@ -46,9 +46,23 @@ public RefList(int capacity) public int Count => _count; /// - /// Gets the capacity of the underlying array. + /// Gets or sets the total number of elements that the underlying array can store. /// - public int Capacity => _items.Length; + public int Capacity + { + get => _items.Length; + set + { + if (value == _items.Length) + return; + + if (value < _count) + throw new ArgumentException("Capacity must be equal or larger than the current number of elements in the list."); + + EnsureCapacity(value); + IncrementVersion(); + } + } /// /// Gets a number indicating the current version of the list. @@ -127,7 +141,7 @@ public ref T GetElementRef(int index, out int version) /// The element. public void Add(in T item) { - EnsureEnoughCapacity(_count + 1); + EnsureCapacity(_count + 1); _items[_count] = item; _count++; IncrementVersion(); @@ -194,7 +208,7 @@ public bool Remove(in T item) /// The element to insert. public void Insert(int index, in T item) { - EnsureEnoughCapacity(_count + 1); + EnsureCapacity(_count + 1); if (index < _count) Array.Copy(_items, index, _items, index + 1, _count - index); @@ -246,7 +260,7 @@ private void AssertIsValidIndex(int index) throw new IndexOutOfRangeException(); } - private void EnsureEnoughCapacity(int requiredCount) + private void EnsureCapacity(int requiredCount) { if (_items.Length >= requiredCount) return; diff --git a/test/AsmResolver.Tests/Collections/RefListTest.cs b/test/AsmResolver.Tests/Collections/RefListTest.cs index 8c7b5f201..5f7c0adae 100644 --- a/test/AsmResolver.Tests/Collections/RefListTest.cs +++ b/test/AsmResolver.Tests/Collections/RefListTest.cs @@ -1,3 +1,4 @@ +using System; using AsmResolver.Collections; using Xunit; @@ -11,6 +12,47 @@ public void EmptyList() Assert.Empty(new RefList()); } + [Fact] + public void SetCapacityToCount() + { + var list = new RefList + { + 1, + 2, + 3 + }; + + list.Capacity = 3; + Assert.True(list.Capacity >= 3); + } + + [Fact] + public void SetCapacityToLargerThanCount() + { + var list = new RefList + { + 1, + 2, + 3 + }; + + list.Capacity = 6; + Assert.True(list.Capacity >= 6); + } + + [Fact] + public void SetCapacityToSmallerThanCountShouldThrow() + { + var list = new RefList + { + 1, + 2, + 3 + }; + + Assert.Throws(() => list.Capacity = 2); + } + [Fact] public void AddItemShouldUpdateVersion() {