context: using System.Collections.Generic;
  using System.Diagnostics.CodeAnalysis;
  using System.Threading;
  using AsmResolver.Collections;
  using AsmResolver.DotNet.Collections;
  using AsmResolver.DotNet.Signatures.Marshal;
  using AsmResolver.PE.DotNet.Metadata.Blob;
  using AsmResolver.PE.DotNet.Metadata.Tables;
  using AsmResolver.PE.DotNet.Metadata.Tables.Rows;
  using AsmResolver.PE.DotNet.VTableFixups;
  using AsmResolver.PE.Exports;
  using AsmResolver.PE.Platforms;
  
  namespace AsmResolver.DotNet.Serialized
  {
      public partial class SerializedModuleDefinition
      {
          private readonly CachedSerializedMemberFactory _memberFactory;
          private readonly LazyRidListRelation<TypeDefinitionRow> _fieldLists;
          private readonly LazyRidListRelation<TypeDefinitionRow> _methodLists;
          private readonly LazyRidListRelation<MethodDefinitionRow> _paramLists;
          private readonly LazyRidListRelation<PropertyMapRow> _propertyLists;
          private readonly LazyRidListRelation<EventMapRow> _eventLists;
  
          private OneToManyRelation<uint, uint>? _typeDefTree;
          private OneToManyRelation<MetadataToken, uint>? _semantics;
          private Dictionary<uint, MetadataToken>? _semanticMethods;
          private OneToOneRelation<MetadataToken, uint>? _constants;
          private OneToManyRelation<MetadataToken, uint>? _customAttributes;
          private OneToManyRelation<MetadataToken, uint>? _securityDeclarations;
          private OneToManyRelation<MetadataToken, uint>? _genericParameters;
          private OneToManyRelation<MetadataToken, uint>? _genericParameterConstraints;
          private OneToManyRelation<MetadataToken, uint>? _interfaces;
          private OneToManyRelation<MetadataToken, uint>? _methodImplementations;
          private OneToOneRelation<MetadataToken, uint>? _classLayouts;
          private OneToOneRelation<MetadataToken, uint>? _implementationMaps;
          private OneToOneRelation<MetadataToken, uint>? _fieldRvas;
          private OneToOneRelation<MetadataToken, uint>? _fieldMarshals;
          private OneToOneRelation<MetadataToken, uint>? _fieldLayouts;
  
          private Dictionary<MetadataToken, UnmanagedExportInfo>? _exportInfos;
  
          [MemberNotNull(nameof(_typeDefTree))]
          private void EnsureTypeDefinitionTreeInitialized()
          {
              if (_typeDefTree is null)
                  Interlocked.CompareExchange(ref _typeDefTree, InitializeTypeDefinitionTree(), null);
          }
  
          private OneToManyRelation<uint, uint> InitializeTypeDefinitionTree()
          {
              var tablesStream = DotNetDirectory.Metadata!.GetStream<TablesStream>();
              var nestedClassTable = tablesStream.GetTable<NestedClassRow>(TableIndex.NestedClass);
  
              var typeDefTree = new OneToManyRelation<uint, uint>();
              foreach (var nestedClass in nestedClassTable)
                  typeDefTree.Add(nestedClass.EnclosingClass, nestedClass.NestedClass);
  
              return typeDefTree;
          }
  
          internal IEnumerable<uint> GetNestedTypeRids(uint enclosingTypeRid)
          {
              EnsureTypeDefinitionTreeInitialized();
              return _typeDefTree.GetValues(enclosingTypeRid);
          }
  
          internal uint GetParentTypeRid(uint nestedTypeRid)
          {
              EnsureTypeDefinitionTreeInitialized();
              return _typeDefTree.GetKey(nestedTypeRid);
          }
  
          internal MetadataRange GetFieldRange(uint typeRid) => _fieldLists.GetMemberRange(typeRid);
  
          internal uint GetFieldDeclaringType(uint fieldRid) => _fieldLists.GetMemberOwner(fieldRid);
  
          internal MetadataRange GetMethodRange(uint typeRid) => _methodLists.GetMemberRange(typeRid);
  
          internal uint GetMethodDeclaringType(uint methodRid) => _methodLists.GetMemberOwner(methodRid);
  
          internal MetadataRange GetParameterRange(uint methodRid) => _paramLists.GetMemberRange(methodRid);
  
          internal uint GetParameterOwner(uint paramRid) => _paramLists.GetMemberOwner(paramRid);
  
          internal MetadataRange GetPropertyRange(uint typeRid) => _propertyLists.GetMemberRange(typeRid);
  
          internal uint GetPropertyDeclaringType(uint propertyRid) => _propertyLists.GetMemberOwner(propertyRid);
  
          internal MetadataRange GetEventRange(uint typeRid) => _eventLists.GetMemberRange(typeRid);
  
          internal uint GetEventDeclaringType(uint eventRid) => _eventLists.GetMemberOwner(eventRid);
  
          [MemberNotNull(nameof(_semantics))]
          [MemberNotNull(nameof(_semanticMethods))]
          private void EnsureMethodSemanticsInitialized()
          {
              if (_semantics is null || _semanticMethods is null)
                  InitializeMethodSemantics();
          }
  
          [MemberNotNull(nameof(_semantics))]
          [MemberNotNull(nameof(_semanticMethods))]
          private void InitializeMethodSemantics()
          {
              var tablesStream = DotNetDirectory.Metadata!.GetStream<TablesStream>();
              var semanticsTable = tablesStream.GetTable<MethodSemanticsRow>(TableIndex.MethodSemantics);
              var encoder = tablesStream.GetIndexEncoder(CodedIndex.HasSemantics);
  
              var semantics = new OneToManyRelation<MetadataToken, uint>();
              var semanticMethods = new Dictionary<uint, MetadataToken>();
              for (int i = 0; i < semanticsTable.Count; i++)
              {
                  var methodSemanticsRow = semanticsTable[i];
                  var semanticsToken = new MetadataToken(TableIndex.MethodSemantics, (uint) (i + 1));
  
                  var ownerToken = encoder.DecodeIndex(methodSemanticsRow.Association);
                  semantics.Add(ownerToken, semanticsToken.Rid);
                  semanticMethods.Add(methodSemanticsRow.Method, semanticsToken);
              }
  
              Interlocked.CompareExchange(ref _semantics, semantics, null);
              Interlocked.CompareExchange(ref _semanticMethods, semanticMethods, null);
          }
  
          internal IEnumerable<uint> GetMethodSemantics(MetadataToken owner)
          {
              EnsureMethodSemanticsInitialized();
              return _semantics.GetValues(owner);
          }
  
          internal MetadataToken GetMethodSemanticsOwner(uint semanticsRid)
          {
              EnsureMethodSemanticsInitialized();
              return _semantics.GetKey(semanticsRid);
          }
  
          internal MetadataToken GetMethodParentSemantics(uint methodRid)
          {
              EnsureMethodSemanticsInitialized();
              _semanticMethods.TryGetValue(methodRid, out var token);
              return token;
          }
  
          [MemberNotNull(nameof(_constants))]
          private void EnsureConstantsInitialized()
          {
              if (_constants is null)
                  Interlocked.CompareExchange(ref _constants, GetConstants(), null);
          }
  
          private OneToOneRelation<MetadataToken, uint> GetConstants()
          {
              var tablesStream = DotNetDirectory.Metadata!.GetStream<TablesStream>();
              var constantTable = tablesStream.GetTable<ConstantRow>(TableIndex.Constant);
              var encoder = tablesStream.GetIndexEncoder(CodedIndex.HasConstant);
  
              var constants = new OneToOneRelation<MetadataToken, uint>();
              for (int i = 0; i < constantTable.Count; i++)
              {
                  var ownerToken = encoder.DecodeIndex(constantTable[i].Parent);
                  uint constantRid = (uint) (i + 1);
                  constants.Add(ownerToken, constantRid);
              }
  
              return constants;
          }
  
          internal uint GetConstantRid(MetadataToken ownerToken)
          {
              EnsureConstantsInitialized();
              return _constants.GetValue(ownerToken);
          }
  
          internal Constant? GetConstant(MetadataToken ownerToken)
          {
              uint constantRid = GetConstantRid(ownerToken);
              return TryLookupMember(new MetadataToken(TableIndex.Constant, constantRid), out var member)
                  ? member as Constant
                  : null;
          }
  
          internal MetadataToken GetConstantOwner(uint constantRid)
          {
              EnsureConstantsInitialized();
              return _constants.GetKey(constantRid);
          }
  
          [MemberNotNull(nameof(_customAttributes))]
          private void EnsureCustomAttributesInitialized()
          {
              if (_customAttributes is null)
                  Interlocked.CompareExchange(ref _customAttributes, InitializeCustomAttributes(), null);
          }
  
          private OneToManyRelation<MetadataToken, uint> InitializeCustomAttributes()
          {
              var tablesStream = DotNetDirectory.Metadata!.GetStream<TablesStream>();
              var attributeTable = tablesStream.GetTable<CustomAttributeRow>(TableIndex.CustomAttribute);
              var encoder = tablesStream.GetIndexEncoder(CodedIndex.HasCustomAttribute);
  
              var customAttributes = new OneToManyRelation<MetadataToken, uint>();
              for (int i = 0; i < attributeTable.Count; i++)
              {
                  var ownerToken = encoder.DecodeIndex(attributeTable[i].Parent);
                  uint attributeRid = (uint) (i + 1);
                  customAttributes.Add(ownerToken, attributeRid);
              }
  
              return customAttributes;
          }
  
          /// <inheritdoc />
          protected override IList<CustomAttribute> GetCustomAttributes() => GetCustomAttributeCollection(this);
  
          internal ICollection<uint> GetCustomAttributes(MetadataToken ownerToken)
          {
              EnsureCustomAttributesInitialized();
              return _customAttributes.GetValues(ownerToken);
          }
  
          internal MetadataToken GetCustomAttributeOwner(uint attributeRid)
          {
              EnsureCustomAttributesInitialized();
              return _customAttributes.GetKey(attributeRid);
          }
  
          internal IList<CustomAttribute> GetCustomAttributeCollection(IHasCustomAttribute owner)
          {
              EnsureCustomAttributesInitialized();
              var result = new OwnedCollection<IHasCustomAttribute, CustomAttribute>(owner);
  
              foreach (uint rid in _customAttributes.GetValues(owner.MetadataToken))
              {
                  var attribute = (CustomAttribute) LookupMember(new MetadataToken(TableIndex.CustomAttribute, rid));
                  result.Add(attribute);
              }
  
              return result;
          }
  
          [MemberNotNull(nameof(_securityDeclarations))]
          private void EnsureSecurityDeclarationsInitialized()
          {
              if (_securityDeclarations is null)
                  Interlocked.CompareExchange(ref _securityDeclarations, InitializeSecurityDeclarations(), null);
          }
  
          private OneToManyRelation<MetadataToken, uint> InitializeSecurityDeclarations()
          {
              var tablesStream = DotNetDirectory.Metadata!.GetStream<TablesStream>();
              var declarationTable = tablesStream.GetTable<SecurityDeclarationRow>(TableIndex.DeclSecurity);
              var encoder = tablesStream.GetIndexEncoder(CodedIndex.HasDeclSecurity);
  
              var securityDeclarations = new OneToManyRelation<MetadataToken, uint>();
              for (int i = 0; i < declarationTable.Count; i++)
              {
                  var ownerToken = encoder.DecodeIndex(declarationTable[i].Parent);
                  uint attributeRid = (uint) (i + 1);
                  securityDeclarations.Add(ownerToken, attributeRid);
              }
  
              return securityDeclarations;
          }
  
          internal MetadataToken GetSecurityDeclarationOwner(uint attributeRid)
          {
              EnsureSecurityDeclarationsInitialized();
              return _securityDeclarations.GetKey(attributeRid);
          }
  
          internal IList<SecurityDeclaration> GetSecurityDeclarationCollection(IHasSecurityDeclaration owner)
          {
              EnsureSecurityDeclarationsInitialized();
              var result = new OwnedCollection<IHasSecurityDeclaration, SecurityDeclaration>(owner);
  
              foreach (uint rid in _securityDeclarations.GetValues(owner.MetadataToken))
              {
                  var attribute = (SecurityDeclaration) LookupMember(new MetadataToken(TableIndex.DeclSecurity, rid));
                  result.Add(attribute);
              }
  
              return result;
          }
  
          [MemberNotNull(nameof(_genericParameters))]
          private void EnsureGenericParametersInitialized()
          {
              if (_genericParameters is null)
                  Interlocked.CompareExchange(ref _genericParameters, InitializeGenericParameters(), null);
          }
  
          private OneToManyRelation<MetadataToken, uint> InitializeGenericParameters()
          {
              var tablesStream = DotNetDirectory.Metadata!.GetStream<TablesStream>();
              var parameterTable = tablesStream.GetTable<GenericParameterRow>(TableIndex.GenericParam);
              var encoder = tablesStream.GetIndexEncoder(CodedIndex.TypeOrMethodDef);
  
              var genericParameters = new OneToManyRelation<MetadataToken, uint>();
              for (int i = 0; i < parameterTable.Count; i++)
              {
  
gt:                 var ownerToken = encoder.DecodeIndex(parameterTable[i].Owner);