context: /*
      Copyright (C) 2014-2019 de4dot@gmail.com
  
      This file is part of dnSpy
  
      dnSpy is free software: you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation, either version 3 of the License, or
      (at your option) any later version.
  
      dnSpy is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.
  
      You should have received a copy of the GNU General Public License
      along with dnSpy.  If not, see <http://www.gnu.org/licenses/>.
  */
  
  using System;
  using System.Collections.Generic;
  using System.Diagnostics;
  using dnlib.DotNet.MD;
  
  namespace dnSpy.AsmEditor.Compiler.MDEditor {
  	sealed class TablesMDHeap : MDHeap {
  		readonly MetadataEditor mdEditor;
  		readonly TablesStream tablesStream;
  
  		public string Name => tablesStream.Name;
  
  		public abstract class TableInfo {
  			public abstract MDTable MDTable { get; }
  			public abstract bool HasChanges { get; }
  			public abstract uint FirstModifiedRowId { get; }
  			public bool HasNewRows => Rows > MDTable.Rows;
  			public bool IsEmpty => Rows == 0;
  			public abstract uint Rows { get; }
  			public abstract void WriteRow(uint rowIndex, IList<ColumnInfo> newColumns, byte[] destination, int destinationIndex);
  		}
  
  		public unsafe sealed class TableInfo<TRow> : TableInfo where TRow : struct {
  			public override MDTable MDTable { get; }
  
  			readonly RawRowColumnReader.ReadColumnDelegate<TRow> readColumn;
  			readonly RawModuleBytes moduleData;
  			readonly byte* peFile;
  			readonly Func<uint, TRow> readRow;
  			readonly Dictionary<uint, TRow> rowsDict;
  			readonly uint originalLastRid;
  			uint nextRid;
  			uint firstModifiedRid;
  
  			public TableInfo(RawModuleBytes moduleData, MDTable mdTable, Func<uint, TRow> readRow) {
  				readColumn = (RawRowColumnReader.ReadColumnDelegate<TRow>)RawRowColumnReader.GetDelegate(mdTable.Table);
  				this.moduleData = moduleData;
  				peFile = (byte*)moduleData.Pointer;
  				MDTable = mdTable;
  				this.readRow = readRow;
  				rowsDict = new Dictionary<uint, TRow>();
  				originalLastRid = mdTable.Rows + 1;
  				nextRid = originalLastRid;
  				firstModifiedRid = originalLastRid;
  			}
  
  			public uint Create() {
  				uint rid = nextRid++;
  				rowsDict.Add(rid, default);
  				Debug.Assert(firstModifiedRid <= rid);
  				return rid;
  			}
  
  			public TRow Get(uint rid) {
  				Debug.Assert(rid != 0);
  				if (rid >= firstModifiedRid && rowsDict.TryGetValue(rid, out var row))
  					return row;
  				return readRow(rid);
  			}
  
  			public void Set(uint rid, ref TRow row) {
  				Debug.Assert(rid != 0);
  				rowsDict[rid] = row;
  				firstModifiedRid = Math.Min(firstModifiedRid, rid);
  			}
  
  			public override bool HasChanges => rowsDict.Count > 0;
  			public override uint FirstModifiedRowId => firstModifiedRid;
  			public override uint Rows => nextRid - 1;
  
  			public override void WriteRow(uint rowIndex, IList<ColumnInfo> newColumns, byte[] destination, int destinationIndex) {
  				var oldColumns = MDTable.Columns;
  
  				var rid = rowIndex + 1;
  				if (rid >= firstModifiedRid && rowsDict.TryGetValue(rid, out var row)) {
  					for (int i = 0; i < newColumns.Count; i++) {
  						uint value = readColumn(ref row, i);
  						switch (newColumns[i].Size) {
  						case 1:
  							Debug.Assert(newColumns[i].Size == oldColumns[i].Size);
  							Debug.Assert(value <= byte.MaxValue);
  							destination[destinationIndex++] = (byte)value;
  							break;
  
  						case 2:
  							// The old and new sizes should match, unless the metadata writer used eg. BigStrings
  							// when it wasn't needed.
  							//Debug.Assert(newColumns[i].Size == oldColumns[i].Size);
  							Debug.Assert(value <= ushort.MaxValue);
  							destination[destinationIndex++] = (byte)value;
  							destination[destinationIndex++] = (byte)(value >> 8);
  							break;
  
  						case 4:
  							destination[destinationIndex++] = (byte)value;
  							destination[destinationIndex++] = (byte)(value >> 8);
  							destination[destinationIndex++] = (byte)(value >> 16);
  							destination[destinationIndex++] = (byte)(value >> 24);
  							break;
  
  						default:
  							throw new InvalidOperationException();
  						}
  					}
  				}
  				else {
  					// We're here if the original metadata row hasn't changed. Convert it from the
  					// old layout to the new layout (most likely they're identical)
  
  					Debug.Assert(rowIndex < MDTable.Rows);
  					var p = peFile + (int)MDTable.StartOffset + (int)(rowIndex * MDTable.RowSize);
  
  					Debug.Assert(oldColumns.Count == newColumns.Count);
  					for (int i = 0; i < newColumns.Count; i++) {
  						switch (newColumns[i].Size) {
  						case 1:
  							Debug.Assert(newColumns[i].Size == oldColumns[i].Size);
  							destination[destinationIndex++] = *p++;
  							break;
  
  						case 2:
  							// The old and new sizes should match, unless the metadata writer used eg. BigStrings
  							// when it wasn't needed.
  							//Debug.Assert(newColumns[i].Size == oldColumns[i].Size);
  							destination[destinationIndex++] = *p++;
  							destination[destinationIndex++] = *p++;
  							break;
  
  						case 4:
  							Debug.Assert(oldColumns[i].Size == 2 || oldColumns[i].Size == 4);
  							if (oldColumns[i].Size == 2) {
  								destination[destinationIndex++] = *p++;
  								destination[destinationIndex++] = *p++;
  								destination[destinationIndex++] = 0;
  								destination[destinationIndex++] = 0;
  							}
  							else {
  								destination[destinationIndex++] = *p++;
  								destination[destinationIndex++] = *p++;
  								destination[destinationIndex++] = *p++;
  								destination[destinationIndex++] = *p++;
  							}
  							break;
  
  						default:
  							throw new InvalidOperationException();
  						}
  					}
  				}
  			}
  		}
  
  		public TableInfo<RawModuleRow> ModuleTable { get; }
  		public TableInfo<RawTypeRefRow> TypeRefTable { get; }
  		public TableInfo<RawTypeDefRow> TypeDefTable { get; }
  		public TableInfo<RawFieldPtrRow> FieldPtrTable { get; }
  		public TableInfo<RawFieldRow> FieldTable { get; }
  		public TableInfo<RawMethodPtrRow> MethodPtrTable { get; }
  		public TableInfo<RawMethodRow> MethodTable { get; }
  		public TableInfo<RawParamPtrRow> ParamPtrTable { get; }
  		public TableInfo<RawParamRow> ParamTable { get; }
  		public TableInfo<RawInterfaceImplRow> InterfaceImplTable { get; }
  		public TableInfo<RawMemberRefRow> MemberRefTable { get; }
  		public TableInfo<RawConstantRow> ConstantTable { get; }
  		public TableInfo<RawCustomAttributeRow> CustomAttributeTable { get; }
  		public TableInfo<RawFieldMarshalRow> FieldMarshalTable { get; }
  		public TableInfo<RawDeclSecurityRow> DeclSecurityTable { get; }
  		public TableInfo<RawClassLayoutRow> ClassLayoutTable { get; }
  		public TableInfo<RawFieldLayoutRow> FieldLayoutTable { get; }
  		public TableInfo<RawStandAloneSigRow> StandAloneSigTable { get; }
  		public TableInfo<RawEventMapRow> EventMapTable { get; }
  		public TableInfo<RawEventPtrRow> EventPtrTable { get; }
  		public TableInfo<RawEventRow> EventTable { get; }
  		public TableInfo<RawPropertyMapRow> PropertyMapTable { get; }
  		public TableInfo<RawPropertyPtrRow> PropertyPtrTable { get; }
  		public TableInfo<RawPropertyRow> PropertyTable { get; }
  		public TableInfo<RawMethodSemanticsRow> MethodSemanticsTable { get; }
  		public TableInfo<RawMethodImplRow> MethodImplTable { get; }
  		public TableInfo<RawModuleRefRow> ModuleRefTable { get; }
  		public TableInfo<RawTypeSpecRow> TypeSpecTable { get; }
  		public TableInfo<RawImplMapRow> ImplMapTable { get; }
  		public TableInfo<RawFieldRVARow> FieldRVATable { get; }
  		public TableInfo<RawENCLogRow> ENCLogTable { get; }
  		public TableInfo<RawENCMapRow> ENCMapTable { get; }
  		public TableInfo<RawAssemblyRow> AssemblyTable { get; }
  		public TableInfo<RawAssemblyProcessorRow> AssemblyProcessorTable { get; }
  		public TableInfo<RawAssemblyOSRow> AssemblyOSTable { get; }
  		public TableInfo<RawAssemblyRefRow> AssemblyRefTable { get; }
  		public TableInfo<RawAssemblyRefProcessorRow> AssemblyRefProcessorTable { get; }
  		public TableInfo<RawAssemblyRefOSRow> AssemblyRefOSTable { get; }
  		public TableInfo<RawFileRow> FileTable { get; }
  		public TableInfo<RawExportedTypeRow> ExportedTypeTable { get; }
  		public TableInfo<RawManifestResourceRow> ManifestResourceTable { get; }
  		public TableInfo<RawNestedClassRow> NestedClassTable { get; }
  		public TableInfo<RawGenericParamRow> GenericParamTable { get; }
  		public TableInfo<RawMethodSpecRow> MethodSpecTable { get; }
  		public TableInfo<RawGenericParamConstraintRow> GenericParamConstraintTable { get; }
  		public TableInfo<RawDocumentRow> DocumentTable { get; }
  		public TableInfo<RawMethodDebugInformationRow> MethodDebugInformationTable { get; }
  		public TableInfo<RawLocalScopeRow> LocalScopeTable { get; }
  		public TableInfo<RawLocalVariableRow> LocalVariableTable { get; }
  		public TableInfo<RawLocalConstantRow> LocalConstantTable { get; }
  		public TableInfo<RawImportScopeRow> ImportScopeTable { get; }
  		public TableInfo<RawStateMachineMethodRow> StateMachineMethodTable { get; }
  		public TableInfo<RawCustomDebugInformationRow> CustomDebugInformationTable { get; }
  
  		public TableInfo[] TableInfos => allTableInfos;
  
  		readonly TableInfo[] allTableInfos;
  
  		public TablesMDHeap(MetadataEditor mdEditor, TablesStream tablesStream) {
  			this.mdEditor = mdEditor ?? throw new ArgumentNullException(nameof(mdEditor));
  			this.tablesStream = tablesStream ?? throw new ArgumentNullException(nameof(tablesStream));
  
  
gt: 			allTableInfos = new TableInfo[64];