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.Diagnostics;
  
  namespace dnSpy.Contracts.Hex.Files {
  	/// <summary>
  	/// Any data
  	/// </summary>
  	public abstract class BufferData {
  		/// <summary>
  		/// Gets the span
  		/// </summary>
  		public HexBufferSpan Span { get; }
  
  		/// <summary>
  		/// Constructor
  		/// </summary>
  		/// <param name="span">Data span</param>
  		protected BufferData(HexBufferSpan span) {
  			if (span.IsDefault)
  				throw new ArgumentException();
  			Span = span;
  		}
  	}
  
  	/// <summary>
  	/// Simple data that contains no fields
  	/// </summary>
  	public abstract class SimpleData : BufferData {
  		/// <summary>
  		/// Constructor
  		/// </summary>
  		/// <param name="span">Data span</param>
  		protected SimpleData(HexBufferSpan span)
  			: base(span) {
  		}
  
  		/// <summary>
  		/// Writes the value
  		/// </summary>
  		/// <param name="formatter">Formatter</param>
  		public abstract void WriteValue(HexFieldFormatter formatter);
  
  		/// <summary>
  		/// Returns the span the field value references or null. The span can be empty.
  		/// </summary>
  		/// <param name="file">File</param>
  		/// <returns></returns>
  		public virtual HexSpan? GetFieldReferenceSpan(HexBufferFile file) => null;
  	}
  
  	/// <summary>
  	/// Base class of structures and arrays
  	/// </summary>
  	public abstract class ComplexData : BufferData {
  		/// <summary>
  		/// Gets the name or an empty string
  		/// </summary>
  		public string Name { get; }
  
  		/// <summary>
  		/// Constructor
  		/// </summary>
  		/// <param name="name">Name</param>
  		/// <param name="span">Span</param>
  		protected ComplexData(string name, HexBufferSpan span)
  			: base(span) => Name = name ?? throw new ArgumentNullException(nameof(name));
  
  		/// <summary>
  		/// Gets a field
  		/// </summary>
  		/// <param name="index">Index</param>
  		/// <returns></returns>
  		public BufferField this[int index] => GetFieldByIndex(index);
  
  		/// <summary>
  		/// Gets a field
  		/// </summary>
  		/// <param name="name">Name</param>
  		/// <returns></returns>
  		public BufferField? this[string name] => GetFieldByName(name);
  
  		/// <summary>
  		/// Gets the field count
  		/// </summary>
  		public abstract int FieldCount { get; }
  
  		/// <summary>
  		/// Gets a field by index
  		/// </summary>
  		/// <param name="index">Index</param>
  		/// <returns></returns>
  		public abstract BufferField GetFieldByIndex(int index);
  
  		/// <summary>
  		/// Gets a field by position
  		/// </summary>
  		/// <param name="position">Position</param>
  		/// <returns></returns>
  		public abstract BufferField? GetFieldByPosition(HexPosition position);
  
  		/// <summary>
  		/// Gets a field
  		/// </summary>
  		/// <param name="name">Name of field</param>
  		/// <returns></returns>
  		public abstract BufferField? GetFieldByName(string name);
  
  		/// <summary>
  		/// Writes the name
  		/// </summary>
  		/// <param name="formatter">Formatter</param>
  		public abstract void WriteName(HexFieldFormatter formatter);
  
  		/// <summary>
  		/// Returns the first field (recursively) that contains a <see cref="SimpleData"/> or null if none was found.
  		/// This field could be contained in a nested <see cref="ComplexData"/> instance.
  		/// </summary>
  		/// <param name="position">Position</param>
  		/// <returns></returns>
  		public BufferField? GetSimpleField(HexPosition position) {
  			ComplexData? structure = this;
  			for (;;) {
  				var field = structure.GetFieldByPosition(position);
  				if (field is null)
  					return null;
  				structure = field.Data as ComplexData;
  				if (structure is null) {
  					Debug.Assert(field.Data is SimpleData);
  					return field.Data is SimpleData ? field : null;
  				}
  			}
  		}
  	}
  
  	/// <summary>
  	/// A structure
  	/// </summary>
  	public abstract class StructureData : ComplexData {
  		/// <summary>
  		/// Constructor
  		/// </summary>
  		/// <param name="name">Name</param>
  		/// <param name="span">Span</param>
  		protected StructureData(string name, HexBufferSpan span)
  			: base(name, span) {
  		}
  
  		/// <summary>
  		/// Gets the fields
  		/// </summary>
  		protected abstract BufferField[] Fields { get; }
  
  		/// <summary>
  		/// Gets the field count
  		/// </summary>
  		public sealed override int FieldCount => Fields.Length;
  
  		/// <summary>
  		/// Gets a field by index
  		/// </summary>
  		/// <param name="index">Index</param>
  		/// <returns></returns>
  		public sealed override BufferField GetFieldByIndex(int index) => Fields[index];
  
  		/// <summary>
  		/// Gets a field by position
  		/// </summary>
  		/// <param name="position">Position</param>
  		/// <returns></returns>
  		public sealed override BufferField? GetFieldByPosition(HexPosition position) {
  			foreach (var field in Fields) {
  				if (field.Data.Span.Span.Contains(position))
  					return field;
  			}
  			return null;
  		}
  
  		/// <summary>
  		/// Gets a field
  		/// </summary>
  		/// <param name="name">Name of field</param>
  		/// <returns></returns>
  		public override BufferField? GetFieldByName(string name) {
  			if (name is null)
  				throw new ArgumentNullException(nameof(name));
  			foreach (var field in Fields) {
  				if (field.Name == name)
  					return field;
  			}
  			return null;
  		}
  
  		/// <summary>
  		/// Writes the name
  		/// </summary>
  		/// <param name="formatter">Formatter</param>
  		public override void WriteName(HexFieldFormatter formatter) => formatter.WriteStructure(Name);
  	}
  
  	/// <summary>
  	/// An array
  	/// </summary>
  	public abstract class ArrayData : ComplexData {
  		/// <summary>
  		/// Constructor
  		/// </summary>
  		/// <param name="name">Name</param>
  		/// <param name="span">Span</param>
  		protected ArrayData(string name, HexBufferSpan span)
  			: base(name, span) {
  		}
  
  		/// <summary>
  		/// Creates a virtual <see cref="byte"/> array
  		/// </summary>
  		/// <param name="span">Span</param>
  		/// <param name="name">Array name or null</param>
  		/// <returns></returns>
  		public static VirtualArrayData<ByteData> CreateVirtualByteArray(HexBufferSpan span, string? name = null) =>
  			new VirtualArrayData<ByteData>(name ?? string.Empty, span, 1, createByteData);
  		static readonly Func<HexBufferPoint, ByteData> createByteData = p => new ByteData(p.Buffer, p.Position);
  
  		/// <summary>
  		/// Creates a virtual <see cref="ushort"/> array
  		/// </summary>
  		/// <param name="span">Span</param>
  		/// <param name="name">Array name or null</param>
  		/// <returns></returns>
  		public static VirtualArrayData<UInt16Data> CreateVirtualUInt16Array(HexBufferSpan span, string? name = null) {
  			if ((span.Length.ToUInt64() & 1) != 0)
  				throw new ArgumentOutOfRangeException(nameof(span));
  			return new VirtualArrayData<UInt16Data>(name ?? string.Empty, span, 2, createUInt16Data);
  		}
  		static readonly Func<HexBufferPoint, UInt16Data> createUInt16Data = p => new UInt16Data(p.Buffer, p.Position);
  
  		/// <summary>
  		/// Creates a virtual <see cref="uint"/> array
  		/// </summary>
  		/// <param name="span">Span</param>
  		/// <param name="name">Array name or null</param>
  		/// <returns></returns>
  		public static VirtualArrayData<UInt32Data> CreateVirtualUInt32Array(HexBufferSpan span, string? name = null) {
  			if ((span.Length.ToUInt64() & 3) != 0)
  				throw new ArgumentOutOfRangeException(nameof(span));
  			return new VirtualArrayData<UInt32Data>(name ?? string.Empty, span, 4, createUInt32Data);
  		}
  		static readonly Func<HexBufferPoint, UInt32Data> createUInt32Data = p => new UInt32Data(p.Buffer, p.Position);
  
  		/// <summary>
  		/// Creates a <see cref="byte"/> array
  		/// </summary>
  		/// <param name="buffer">Buffer</param>
  		/// <param name="position">Position</param>
  		/// <param name="elements">Number of elements</param>
  		/// <param name="name">Array name or null</param>
  		/// <returns></returns>
  		public static ArrayData<ByteData> CreateByteArray(HexBuffer buffer, HexPosition position, int elements, string? name = null) {
  			var fields = new ArrayField<ByteData>[elements];
  			var currPos = position;
  			for (int i = 0; i < fields.Length; i++) {
  				var field = new ArrayField<ByteData>(new ByteData(buffer, currPos), (uint)i);
  				fields[i] = field;
  				currPos = field.Data.Span.End;
  			}
  			return new ArrayData<ByteData>(name ?? string.Empty, new HexBufferSpan(buffer, HexSpan.FromBounds(position, currPos)), fields);
  		}
  
  		/// <summary>
  		/// Creates a <see cref="ushort"/> array
  		/// </summary>
  		/// <param name="buffer">Buffer</param>
  		/// <param name="position">Position</param>
  		/// <param name="elements">Number of elements</param>
  		/// <param name="name">Array name or null</param>
  		/// <returns></returns>
  		public static ArrayData<UInt16Data> CreateUInt16Array(HexBuffer buffer, HexPosition position, int elements, string? name = null) {
  			var fields = new ArrayField<UInt16Data>[elements];
  			var currPos = position;
  			for (int i = 0; i < fields.Length; i++) {
  				var field = new ArrayField<UInt16Data>(new UInt16Data(buffer, currPos), (uint)i);
  				fields[i] = field;
  				currPos = field.Data.Span.End;
  			}
  			return new ArrayData<UInt16Data>(name ?? string.Empty, new HexBufferSpan(buffer, HexSpan.FromBounds(position, currPos)), fields);
  		}
  
  		/// <summary>
  		/// Creates a <see cref="uint"/> array
  		/// </summary>
  		/// <param name="buffer">Buffer</param>
  		/// <param name="position">Position</param>
  		/// <param name="elements">Number of elements</param>
  		/// <param name="name">Array name or null</param>
  		/// <returns></returns>
  		public static ArrayData<UInt32Data> CreateUInt32Array(HexBuffer buffer, HexPosition position, int elements, string? name = null) {
  			var fields = new ArrayField<UInt32Data>[elements];
  			var currPos = position;
  			for (int i = 0; i < fields.Length; i++) {
  				var field = new ArrayField<UInt32Data>(new UInt32Data(buffer, currPos), (uint)i);
  				fields[i] = field;
  				currPos = field.Data.Span.End;
  			}
  			return new ArrayData<UInt32Data>(name ?? string.Empty, new HexBufferSpan(buffer, HexSpan.FromBounds(position, currPos)), fields);
  		}
  
  		/// <summary>
  		/// Creates a <see cref="ulong"/> array
  		/// </summary>
  		/// <param name="buffer">Buffer</param>
  		/// <param name="position">Position</param>
  		/// <param name="elements">Number of elements</param>
  		/// <param name="name">Array name or null</param>
  		/// <returns></returns>
  		public static ArrayData<UInt64Data> CreateUInt64Array(HexBuffer buffer, HexPosition position, int elements, string? name = null) {
  			var fields = new ArrayField<UInt64Data>[elements];
  			var currPos = position;
  			for (int i = 0; i < fields.Length; i++) {
  				var field = new ArrayField<UInt64Data>(new UInt64Data(buffer, currPos), (uint)i);
  				fields[i] = field;
  				currPos = field.Data.Span.End;
  			}
  			return new ArrayData<UInt64Data>(name ?? string.Empty, new HexBufferSpan(buffer, HexSpan.FromBounds(position, currPos)), fields);
  		}
  
  		/// <summary>
  		/// Creates a <see cref="sbyte"/> array
  		/// </summary>
  		/// <param name="buffer">Buffer</param>
  		/// <param name="position">Position</param>
  		/// <param name="elements">Number of elements</param>
  		/// <param name="name">Array name or null</param>
  		/// <returns></returns>
  		public static ArrayData<SByteData> CreateSByteArray(HexBuffer buffer, HexPosition position, int elements, string? name = null) {
  
gt: 			var fields = new ArrayField<SByteData>[elements];