//
// Utilities.cs
//
// Author:
//   Jb Evain (jbevain@gmail.com)
//
// Generated by /CodeGen/cecil-gen.rb do not edit
// Sat Feb 16 23:23:29 +0100 2008
//
// (C) 2005 Jb Evain
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

namespace Mono.Cecil.Metadata {

	using System;
	using System.Collections;
	using System.IO;

	sealed class Utilities {

		Utilities ()
		{
		}

		public static int ReadCompressedInteger (byte [] data, int pos, out int start)
		{
			int integer;
			start = pos;
			if ((data [pos] & 0x80) == 0) {
				integer = data [pos];
				start++;
			} else if ((data [pos] & 0x40) == 0) {
				integer = (data [start] & ~0x80) << 8;
				integer |= data [pos + 1];
				start += 2;
			} else {
				integer = (data [start] & ~0xc0) << 24;
				integer |= data [pos + 1] << 16;
				integer |= data [pos + 2] << 8;
				integer |= data [pos + 3];
				start += 4;
			}
			return integer;
		}

		public static int ReadCompressedSignedInteger (byte [] data, int pos, out int start)
		{
			int integer = ReadCompressedInteger (data, pos, out start) >> 1;
			if ((integer & 1) == 0)
				return integer;

			if (integer < 0x40)
				return integer - 0x40;

			if (integer < 0x2000)
				return integer - 0x2000;

			if (integer < 0x10000000)
				return integer - 0x10000000;

			return integer - 0x20000000;
		}

		public static int WriteCompressedInteger (BinaryWriter writer, int value)
		{
			if (value < 0x80)
				writer.Write ((byte) value);
			else if (value < 0x4000) {
				writer.Write ((byte) (0x80 | (value >> 8)));
				writer.Write ((byte) (value & 0xff));
			} else {
				writer.Write ((byte) ((value >> 24) | 0xc0));
				writer.Write ((byte) ((value >> 16) & 0xff));
				writer.Write ((byte) ((value >> 8) & 0xff));
				writer.Write ((byte) (value & 0xff));
			}
			return (int) writer.BaseStream.Position;
		}

		public static MetadataToken GetMetadataToken (CodedIndex cidx, uint data)
		{
			uint rid = 0;
			switch (cidx) {
			case CodedIndex.TypeDefOrRef :
				rid = data >> 2;
				switch (data & 3) {
				case 0 :
					return new MetadataToken (TokenType.TypeDef, rid);
				case 1 :
					return new MetadataToken (TokenType.TypeRef, rid);
				case 2 :
					return new MetadataToken (TokenType.TypeSpec, rid);
				default :
					return MetadataToken.Zero;
				}
			case CodedIndex.HasConstant :
				rid = data >> 2;
				switch (data & 3) {
				case 0 :
					return new MetadataToken (TokenType.Field, rid);
				case 1 :
					return new MetadataToken (TokenType.Param, rid);
				case 2 :
					return new MetadataToken (TokenType.Property, rid);
				default :
					return MetadataToken.Zero;
				}
			case CodedIndex.HasCustomAttribute :
				rid = data >> 5;
				switch (data & 31) {
				case 0 :
					return new MetadataToken (TokenType.Method, rid);
				case 1 :
					return new MetadataToken (TokenType.Field, rid);
				case 2 :
					return new MetadataToken (TokenType.TypeRef, rid);
				case 3 :
					return new MetadataToken (TokenType.TypeDef, rid);
				case 4 :
					return new MetadataToken (TokenType.Param, rid);
				case 5 :
					return new MetadataToken (TokenType.InterfaceImpl, rid);
				case 6 :
					return new MetadataToken (TokenType.MemberRef, rid);
				case 7 :
					return new MetadataToken (TokenType.Module, rid);
				case 8 :
					return new MetadataToken (TokenType.Permission, rid);
				case 9 :
					return new MetadataToken (TokenType.Property, rid);
				case 10 :
					return new MetadataToken (TokenType.Event, rid);
				case 11 :
					return new MetadataToken (TokenType.Signature, rid);
				case 12 :
					return new MetadataToken (TokenType.ModuleRef, rid);
				case 13 :
					return new MetadataToken (TokenType.TypeSpec, rid);
				case 14 :
					return new MetadataToken (TokenType.Assembly, rid);
				case 15 :
					return new MetadataToken (TokenType.AssemblyRef, rid);
				case 16 :
					return new MetadataToken (TokenType.File, rid);
				case 17 :
					return new MetadataToken (TokenType.ExportedType, rid);
				case 18 :
					return new MetadataToken (TokenType.ManifestResource, rid);
				case 19 :
					return new MetadataToken (TokenType.GenericParam, rid);
				default :
					return MetadataToken.Zero;
				}
			case CodedIndex.HasFieldMarshal :
				rid = data >> 1;
				switch (data & 1) {
				case 0 :
					return new MetadataToken (TokenType.Field, rid);
				case 1 :
					return new MetadataToken (TokenType.Param, rid);
				default :
					return MetadataToken.Zero;
				}
			case CodedIndex.HasDeclSecurity :
				rid = data >> 2;
				switch (data & 3) {
				case 0 :
					return new MetadataToken (TokenType.TypeDef, rid);
				case 1 :
					return new MetadataToken (TokenType.Method, rid);
				case 2 :
					return new MetadataToken (TokenType.Assembly, rid);
				default :
					return MetadataToken.Zero;
				}
			case CodedIndex.MemberRefParent :
				rid = data >> 3;
				switch (data & 7) {
				case 0 :
					return new MetadataToken (TokenType.TypeDef, rid);
				case 1 :
					return new MetadataToken (TokenType.TypeRef, rid);
				case 2 :
					return new MetadataToken (TokenType.ModuleRef, rid);
				case 3 :
					return new MetadataToken (TokenType.Method, rid);
				case 4 :
					return new MetadataToken (TokenType.TypeSpec, rid);
				default :
					return MetadataToken.Zero;
				}
			case CodedIndex.HasSemantics :
				rid = data >> 1;
				switch (data & 1) {
				case 0 :
					return new MetadataToken (TokenType.Event, rid);
				case 1 :
					return new MetadataToken (TokenType.Property, rid);
				default :
					return MetadataToken.Zero;
				}
			case CodedIndex.MethodDefOrRef :
				rid = data >> 1;
				switch (data & 1) {
				case 0 :
					return new MetadataToken (TokenType.Method, rid);
				case 1 :
					return new MetadataToken (TokenType.MemberRef, rid);
				default :
					return MetadataToken.Zero;
				}
			case CodedIndex.MemberForwarded :
				rid = data >> 1;
				switch (data & 1) {
				case 0 :
					return new MetadataToken (TokenType.Field, rid);
				case 1 :
					return new MetadataToken (TokenType.Method, rid);
				default :
					return MetadataToken.Zero;
				}
			case CodedIndex.Implementation :
				rid = data >> 2;
				switch (data & 3) {
				case 0 :
					return new MetadataToken (TokenType.File, rid);
				case 1 :
					return new MetadataToken (TokenType.AssemblyRef, rid);
				case 2 :
					return new MetadataToken (TokenType.ExportedType, rid);
				default :
					return MetadataToken.Zero;
				}
			case CodedIndex.CustomAttributeType :
				rid = data >> 3;
				switch (data & 7) {
				case 2 :
					return new MetadataToken (TokenType.Method, rid);
				case 3 :
					return new MetadataToken (TokenType.MemberRef, rid);
				default :
					return MetadataToken.Zero;
				}
			case CodedIndex.ResolutionScope :
				rid = data >> 2;
				switch (data & 3) {
				case 0 :
					return new MetadataToken (TokenType.Module, rid);
				case 1 :
					return new MetadataToken (TokenType.ModuleRef, rid);
				case 2 :
					return new MetadataToken (TokenType.AssemblyRef, rid);
				case 3 :
					return new MetadataToken (TokenType.TypeRef, rid);
				default :
					return MetadataToken.Zero;
				}
			case CodedIndex.TypeOrMethodDef :
				rid = data >> 1;
				switch (data & 1) {
				case 0 :
					return new MetadataToken (TokenType.TypeDef, rid);
				case 1 :
					return new MetadataToken (TokenType.Method, rid);
				default :
					return MetadataToken.Zero;
				}
			default :
				return MetadataToken.Zero;
			}
		}

		public static uint CompressMetadataToken (CodedIndex cidx, MetadataToken token)
		{
			uint ret = 0;
			if (token.RID == 0)
				return ret;
			switch (cidx) {
			case CodedIndex.TypeDefOrRef :
				ret = token.RID << 2;
				switch (token.TokenType) {
				case TokenType.TypeDef :
					return ret | 0;
				case TokenType.TypeRef :
					return ret | 1;
				case TokenType.TypeSpec :
					return ret | 2;
				default :
					throw new MetadataFormatException("Non valid Token for TypeDefOrRef");
				}
			case CodedIndex.HasConstant :
				ret = token.RID << 2;
				switch (token.TokenType) {
				case TokenType.Field :
					return ret | 0;
				case TokenType.Param :
					return ret | 1;
				case TokenType.Property :
					return ret | 2;
				default :
					throw new MetadataFormatException("Non valid Token for HasConstant");
				}
			case CodedIndex.HasCustomAttribute :
				ret = token.RID << 5;
				switch (token.TokenType) {
				case TokenType.Method :
					return ret | 0;
				case TokenType.Field :
					return ret | 1;
				case TokenType.TypeRef :
					return ret | 2;
				case TokenType.TypeDef :
					return ret | 3;
				case TokenType.Param :
					return ret | 4;
				case TokenType.InterfaceImpl :
					return ret | 5;
				case TokenType.MemberRef :
					return ret | 6;
				case TokenType.Module :
					return ret | 7;
				case TokenType.Permission :
					return ret | 8;
				case TokenType.Property :
					return ret | 9;
				case TokenType.Event :
					return ret | 10;
				case TokenType.Signature :
					return ret | 11;
				case TokenType.ModuleRef :
					return ret | 12;
				case TokenType.TypeSpec :
					return ret | 13;
				case TokenType.Assembly :
					return ret | 14;
				case TokenType.AssemblyRef :
					return ret | 15;
				case TokenType.File :
					return ret | 16;
				case TokenType.ExportedType :
					return ret | 17;
				case TokenType.ManifestResource :
					return ret | 18;
				case TokenType.GenericParam :
					return ret | 19;
				default :
					throw new MetadataFormatException("Non valid Token for HasCustomAttribute");
				}
			case CodedIndex.HasFieldMarshal :
				ret = token.RID << 1;
				switch (token.TokenType) {
				case TokenType.Field :
					return ret | 0;
				case TokenType.Param :
					return ret | 1;
				default :
					throw new MetadataFormatException("Non valid Token for HasFieldMarshal");
				}
			case CodedIndex.HasDeclSecurity :
				ret = token.RID << 2;
				switch (token.TokenType) {
				case TokenType.TypeDef :
					return ret | 0;
				case TokenType.Method :
					return ret | 1;
				case TokenType.Assembly :
					return ret | 2;
				default :
					throw new MetadataFormatException("Non valid Token for HasDeclSecurity");
				}
			case CodedIndex.MemberRefParent :
				ret = token.RID << 3;
				switch (token.TokenType) {
				case TokenType.TypeDef :
					return ret | 0;
				case TokenType.TypeRef :
					return ret | 1;
				case TokenType.ModuleRef :
					return ret | 2;
				case TokenType.Method :
					return ret | 3;
				case TokenType.TypeSpec :
					return ret | 4;
				default :
					throw new MetadataFormatException("Non valid Token for MemberRefParent");
				}
			case CodedIndex.HasSemantics :
				ret = token.RID << 1;
				switch (token.TokenType) {
				case TokenType.Event :
					return ret | 0;
				case TokenType.Property :
					return ret | 1;
				default :
					throw new MetadataFormatException("Non valid Token for HasSemantics");
				}
			case CodedIndex.MethodDefOrRef :
				ret = token.RID << 1;
				switch (token.TokenType) {
				case TokenType.Method :
					return ret | 0;
				case TokenType.MemberRef :
					return ret | 1;
				default :
					throw new MetadataFormatException("Non valid Token for MethodDefOrRef");
				}
			case CodedIndex.MemberForwarded :
				ret = token.RID << 1;
				switch (token.TokenType) {
				case TokenType.Field :
					return ret | 0;
				case TokenType.Method :
					return ret | 1;
				default :
					throw new MetadataFormatException("Non valid Token for MemberForwarded");
				}
			case CodedIndex.Implementation :
				ret = token.RID << 2;
				switch (token.TokenType) {
				case TokenType.File :
					return ret | 0;
				case TokenType.AssemblyRef :
					return ret | 1;
				case TokenType.ExportedType :
					return ret | 2;
				default :
					throw new MetadataFormatException("Non valid Token for Implementation");
				}
			case CodedIndex.CustomAttributeType :
				ret = token.RID << 3;
				switch (token.TokenType) {
				case TokenType.Method :
					return ret | 2;
				case TokenType.MemberRef :
					return ret | 3;
				default :
					throw new MetadataFormatException("Non valid Token for CustomAttributeType");
				}
			case CodedIndex.ResolutionScope :
				ret = token.RID << 2;
				switch (token.TokenType) {
				case TokenType.Module :
					return ret | 0;
				case TokenType.ModuleRef :
					return ret | 1;
				case TokenType.AssemblyRef :
					return ret | 2;
				case TokenType.TypeRef :
					return ret | 3;
				default :
					throw new MetadataFormatException("Non valid Token for ResolutionScope");
				}
			case CodedIndex.TypeOrMethodDef :
				ret = token.RID << 1;
				switch (token.TokenType) {
				case TokenType.TypeDef :
					return ret | 0;
				case TokenType.Method :
					return ret | 1;
				default :
					throw new MetadataFormatException("Non valid Token for TypeOrMethodDef");
				}
			default :
				throw new MetadataFormatException ("Non valid CodedIndex");
			}
		}

		internal static Type GetCorrespondingTable (TokenType t)
		{
			switch (t) {
			case TokenType.Assembly :
				return typeof (AssemblyTable);
			case TokenType.AssemblyRef :
				return typeof (AssemblyRefTable);
			case TokenType.CustomAttribute :
				return typeof (CustomAttributeTable);
			case TokenType.Event :
				return typeof (EventTable);
			case TokenType.ExportedType :
				return typeof (ExportedTypeTable);
			case TokenType.Field :
				return typeof (FieldTable);
			case TokenType.File :
				return typeof (FileTable);
			case TokenType.InterfaceImpl :
				return typeof (InterfaceImplTable);
			case TokenType.MemberRef :
				return typeof (MemberRefTable);
			case TokenType.Method :
				return typeof (MethodTable);
			case TokenType.Module :
				return typeof (ModuleTable);
			case TokenType.ModuleRef :
				return typeof (ModuleRefTable);
			case TokenType.Param :
				return typeof (ParamTable);
			case TokenType.Permission :
				return typeof (DeclSecurityTable);
			case TokenType.Property :
				return typeof (PropertyTable);
			case TokenType.Signature :
				return typeof (StandAloneSigTable);
			case TokenType.TypeDef :
				return typeof (TypeDefTable);
			case TokenType.TypeRef :
				return typeof (TypeRefTable);
			case TokenType.TypeSpec :
				return typeof (TypeSpecTable);
			default :
				return null;
			}
		}

		internal delegate int TableRowCounter (int rid);

		internal static int GetCodedIndexSize (CodedIndex ci, TableRowCounter rowCounter, int [] codedIndexCache)
		{
			int bits = 0, max = 0, index = (int) ci;
			if (codedIndexCache [index] != 0)
				return codedIndexCache [index];

			int res = 0;
			int [] rids;
			switch (ci) {
			case CodedIndex.TypeDefOrRef :
				bits = 2;
				rids = new int [3];
				rids [0] = TypeDefTable.RId;
				rids [1] = TypeRefTable.RId;
				rids [2] = TypeSpecTable.RId;
				break;
			case CodedIndex.HasConstant :
				bits = 2;
				rids = new int [3];
				rids [0] = FieldTable.RId;
				rids [1] = ParamTable.RId;
				rids [2] = PropertyTable.RId;
				break;
			case CodedIndex.HasCustomAttribute :
				bits = 5;
				rids = new int [20];
				rids [0] = MethodTable.RId;
				rids [1] = FieldTable.RId;
				rids [2] = TypeRefTable.RId;
				rids [3] = TypeDefTable.RId;
				rids [4] = ParamTable.RId;
				rids [5] = InterfaceImplTable.RId;
				rids [6] = MemberRefTable.RId;
				rids [7] = ModuleTable.RId;
				rids [8] = DeclSecurityTable.RId;
				rids [9] = PropertyTable.RId;
				rids [10] = EventTable.RId;
				rids [11] = StandAloneSigTable.RId;
				rids [12] = ModuleRefTable.RId;
				rids [13] = TypeSpecTable.RId;
				rids [14] = AssemblyTable.RId;
				rids [15] = AssemblyRefTable.RId;
				rids [16] = FileTable.RId;
				rids [17] = ExportedTypeTable.RId;
				rids [18] = ManifestResourceTable.RId;
				rids [19] = GenericParamTable.RId;
				break;
			case CodedIndex.HasFieldMarshal :
				bits = 1;
				rids = new int [2];
				rids [0] = FieldTable.RId;
				rids [1] = ParamTable.RId;
				break;
			case CodedIndex.HasDeclSecurity :
				bits = 2;
				rids = new int [3];
				rids [0] = TypeDefTable.RId;
				rids [1] = MethodTable.RId;
				rids [2] = AssemblyTable.RId;
				break;
			case CodedIndex.MemberRefParent :
				bits = 3;
				rids = new int [5];
				rids [0] = TypeDefTable.RId;
				rids [1] = TypeRefTable.RId;
				rids [2] = ModuleRefTable.RId;
				rids [3] = MethodTable.RId;
				rids [4] = TypeSpecTable.RId;
				break;
			case CodedIndex.HasSemantics :
				bits = 1;
				rids = new int [2];
				rids [0] = EventTable.RId;
				rids [1] = PropertyTable.RId;
				break;
			case CodedIndex.MethodDefOrRef :
				bits = 1;
				rids = new int [2];
				rids [0] = MethodTable.RId;
				rids [1] = MemberRefTable.RId;
				break;
			case CodedIndex.MemberForwarded :
				bits = 1;
				rids = new int [2];
				rids [0] = FieldTable.RId;
				rids [1] = MethodTable.RId;
				break;
			case CodedIndex.Implementation :
				bits = 2;
				rids = new int [3];
				rids [0] = FileTable.RId;
				rids [1] = AssemblyRefTable.RId;
				rids [2] = ExportedTypeTable.RId;
				break;
			case CodedIndex.CustomAttributeType :
				bits = 3;
				rids = new int [2];
				rids [0] = MethodTable.RId;
				rids [1] = MemberRefTable.RId;
				break;
			case CodedIndex.ResolutionScope :
				bits = 2;
				rids = new int [4];
				rids [0] = ModuleTable.RId;
				rids [1] = ModuleRefTable.RId;
				rids [2] = AssemblyRefTable.RId;
				rids [3] = TypeRefTable.RId;
				break;
			case CodedIndex.TypeOrMethodDef :
				bits = 1;
				rids = new int [2];
				rids [0] = TypeDefTable.RId;
				rids [1] = MethodTable.RId;
				break;
			default :
				throw new MetadataFormatException ("Non valid CodedIndex");
			}

			for (int i = 0; i < rids.Length; i++) {
				int rows = rowCounter (rids [i]);
				if (rows > max) max = rows;
			}

			res = max < (1 << (16 - bits)) ? 2 : 4;
			codedIndexCache [index] = res;
			return res;
		}
	}
}
