File: System\Diagnostics\Eventing\TraceLogging\Statics.cs
Project: ndp\clr\src\bcl\mscorlib.csproj (mscorlib)
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using Encoding = System.Text.Encoding;
 
#if ES_BUILD_STANDALONE
using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment;
namespace Microsoft.Diagnostics.Tracing
#else
namespace System.Diagnostics.Tracing
#endif
{
    /// <summary>
    /// TraceLogging: Constants and utility functions.
    /// </summary>
    internal static class Statics
    {
        #region Constants
 
        public const byte DefaultLevel = 5;
        public const byte TraceLoggingChannel = 0xb;
        public const byte InTypeMask = 31;
        public const byte InTypeFixedCountFlag = 32;
        public const byte InTypeVariableCountFlag = 64;
        public const byte InTypeCustomCountFlag = 96;
        public const byte InTypeCountMask = 96;
        public const byte InTypeChainFlag = 128;
        public const byte OutTypeMask = 127;
        public const byte OutTypeChainFlag = 128;
        public const EventTags EventTagsMask = (EventTags)0xfffffff;
 
        public static readonly TraceLoggingDataType IntPtrType = IntPtr.Size == 8
            ? TraceLoggingDataType.Int64
            : TraceLoggingDataType.Int32;
        public static readonly TraceLoggingDataType UIntPtrType = IntPtr.Size == 8
            ? TraceLoggingDataType.UInt64
            : TraceLoggingDataType.UInt32;
        public static readonly TraceLoggingDataType HexIntPtrType = IntPtr.Size == 8
            ? TraceLoggingDataType.HexInt64
            : TraceLoggingDataType.HexInt32;
 
        #endregion
 
        #region Metadata helpers
 
        /// <summary>
        /// A complete metadata chunk can be expressed as:
        /// length16 + prefix + null-terminated-utf8-name + suffix + additionalData.
        /// We assume that excludedData will be provided by some other means,
        /// but that its size is known. This function returns a blob containing
        /// length16 + prefix + name + suffix, with prefix and suffix initialized
        /// to 0's. The length16 value is initialized to the length of the returned
        /// blob plus additionalSize, so that the concatenation of the returned blob
        /// plus a blob of size additionalSize constitutes a valid metadata blob.
        /// </summary>
        /// <param name="name">
        /// The name to include in the blob.
        /// </param>
        /// <param name="prefixSize">
        /// Amount of space to reserve before name. For provider or field blobs, this
        /// should be 0. For event blobs, this is used for the tags field and will vary
        /// from 1 to 4, depending on how large the tags field needs to be.
        /// </param>
        /// <param name="suffixSize">
        /// Amount of space to reserve after name. For example, a provider blob with no
        /// traits would reserve 0 extra bytes, but a provider blob with a single GroupId
        /// field would reserve 19 extra bytes.
        /// </param>
        /// <param name="additionalSize">
        /// Amount of additional data in another blob. This value will be counted in the
        /// blob's length field, but will not be included in the returned byte[] object.
        /// The complete blob would then be the concatenation of the returned byte[] object
        /// with another byte[] object of length additionalSize.
        /// </param>
        /// <returns>
        /// A byte[] object with the length and name fields set, with room reserved for
        /// prefix and suffix. If additionalSize was 0, the byte[] object is a complete
        /// blob. Otherwise, another byte[] of size additionalSize must be concatenated
        /// with this one to form a complete blob.
        /// </returns>
        public static byte[] MetadataForString(
            string name,
            int prefixSize,
            int suffixSize,
            int additionalSize)
        {
            Statics.CheckName(name);
            int metadataSize = Encoding.UTF8.GetByteCount(name) + 3 + prefixSize + suffixSize;
            var metadata = new byte[metadataSize];
            ushort totalSize = checked((ushort)(metadataSize + additionalSize));
            metadata[0] = unchecked((byte)totalSize);
            metadata[1] = unchecked((byte)(totalSize >> 8));
            Encoding.UTF8.GetBytes(name, 0, name.Length, metadata, 2 + prefixSize);
            return metadata;
        }
 
        /// <summary>
        /// Serialize the low 28 bits of the tags value into the metadata stream,
        /// starting at the index given by pos. Updates pos. Writes 1 to 4 bytes,
        /// depending on the value of the tags variable. Usable for event tags and
        /// field tags.
        /// 
        /// Note that 'metadata' can be null, in which case it only updates 'pos'.
        /// This is useful for a two pass approach where you figure out how big to
        /// make the array, and then you fill it in.   
        /// </summary>
        public static void EncodeTags(int tags, ref int pos, byte[] metadata)
        {
            // We transmit the low 28 bits of tags, high bits first, 7 bits at a time.
            var tagsLeft = tags & 0xfffffff;
            bool more;
            do
            {
                byte current = (byte)((tagsLeft >> 21) & 0x7f);
                more = (tagsLeft & 0x1fffff) != 0;
                current |= (byte)(more ? 0x80 : 0x00);
                tagsLeft = tagsLeft << 7;
 
                if (metadata != null)
                {
                    metadata[pos] = current;
                }
                pos += 1;
            }
            while (more);
        }
 
        public static byte Combine(
            int settingValue,
            byte defaultValue)
        {
            unchecked
            {
                return (byte)settingValue == settingValue
                    ? (byte)settingValue
                    : defaultValue;
            }
        }
 
        public static byte Combine(
            int settingValue1,
            int settingValue2,
            byte defaultValue)
        {
            unchecked
            {
                return (byte)settingValue1 == settingValue1
                    ? (byte)settingValue1
                    : (byte)settingValue2 == settingValue2
                    ? (byte)settingValue2
                    : defaultValue;
            }
        }
 
        public static int Combine(
            int settingValue1,
            int settingValue2)
        {
            unchecked
            {
                return (byte)settingValue1 == settingValue1
                    ? settingValue1
                    : settingValue2;
            }
        }
 
        public static void CheckName(string name)
        {
            if (name != null && 0 <= name.IndexOf('\0'))
            {
                throw new ArgumentOutOfRangeException("name");
            }
        }
 
        public static bool ShouldOverrideFieldName(string fieldName)
        {
            return (fieldName.Length <= 2 && fieldName[0] == '_');
        }
 
        public static TraceLoggingDataType MakeDataType(
            TraceLoggingDataType baseType,
            EventFieldFormat format)
        {
            return (TraceLoggingDataType)(((int)baseType & 0x1f) | ((int)format << 8));
        }
 
        /// <summary>
        /// Adjusts the native type based on format.
        /// - If format is default, return native.
        /// - If format is recognized, return the canonical type for that format.
        /// - Otherwise remove existing format from native and apply the requested format.
        /// </summary>
        public static TraceLoggingDataType Format8(
            EventFieldFormat format,
            TraceLoggingDataType native)
        {
            switch (format)
            {
                case EventFieldFormat.Default:
                    return native;
                case EventFieldFormat.String:
                    return TraceLoggingDataType.Char8;
                case EventFieldFormat.Boolean:
                    return TraceLoggingDataType.Boolean8;
                case EventFieldFormat.Hexadecimal:
                    return TraceLoggingDataType.HexInt8;
#if false 
                case EventSourceFieldFormat.Signed:
                    return TraceLoggingDataType.Int8;
                case EventSourceFieldFormat.Unsigned:
                    return TraceLoggingDataType.UInt8;
#endif 
                default:
                    return MakeDataType(native, format);
            }
        }
 
        /// <summary>
        /// Adjusts the native type based on format.
        /// - If format is default, return native.
        /// - If format is recognized, return the canonical type for that format.
        /// - Otherwise remove existing format from native and apply the requested format.
        /// </summary>
        public static TraceLoggingDataType Format16(
            EventFieldFormat format,
            TraceLoggingDataType native)
        {
            switch (format)
            {
                case EventFieldFormat.Default:
                    return native;
                case EventFieldFormat.String:
                    return TraceLoggingDataType.Char16;
                case EventFieldFormat.Hexadecimal:
                    return TraceLoggingDataType.HexInt16;
#if false
                case EventSourceFieldFormat.Port:
                    return TraceLoggingDataType.Port;
                case EventSourceFieldFormat.Signed:
                    return TraceLoggingDataType.Int16;
                case EventSourceFieldFormat.Unsigned:
                    return TraceLoggingDataType.UInt16;
#endif
                default:
                    return MakeDataType(native, format);
            }
        }
 
        /// <summary>
        /// Adjusts the native type based on format.
        /// - If format is default, return native.
        /// - If format is recognized, return the canonical type for that format.
        /// - Otherwise remove existing format from native and apply the requested format.
        /// </summary>
        public static TraceLoggingDataType Format32(
            EventFieldFormat format,
            TraceLoggingDataType native)
        {
            switch (format)
            {
                case EventFieldFormat.Default:
                    return native;
                case EventFieldFormat.Boolean:
                    return TraceLoggingDataType.Boolean32;
                case EventFieldFormat.Hexadecimal:
                    return TraceLoggingDataType.HexInt32;
#if false 
                case EventSourceFieldFormat.Ipv4Address:
                    return TraceLoggingDataType.Ipv4Address;
                case EventSourceFieldFormat.ProcessId:
                    return TraceLoggingDataType.ProcessId;
                case EventSourceFieldFormat.ThreadId:
                    return TraceLoggingDataType.ThreadId;
                case EventSourceFieldFormat.Win32Error:
                    return TraceLoggingDataType.Win32Error;
                case EventSourceFieldFormat.NTStatus:
                    return TraceLoggingDataType.NTStatus;
#endif
                case EventFieldFormat.HResult:
                    return TraceLoggingDataType.HResult;
#if false 
                case EventSourceFieldFormat.Signed:
                    return TraceLoggingDataType.Int32;
                case EventSourceFieldFormat.Unsigned:
                    return TraceLoggingDataType.UInt32;
#endif
                default:
                    return MakeDataType(native, format);
            }
        }
 
        /// <summary>
        /// Adjusts the native type based on format.
        /// - If format is default, return native.
        /// - If format is recognized, return the canonical type for that format.
        /// - Otherwise remove existing format from native and apply the requested format.
        /// </summary>
        public static TraceLoggingDataType Format64(
            EventFieldFormat format,
            TraceLoggingDataType native)
        {
            switch (format)
            {
                case EventFieldFormat.Default:
                    return native;
                case EventFieldFormat.Hexadecimal:
                    return TraceLoggingDataType.HexInt64;
#if false 
                case EventSourceFieldFormat.FileTime:
                    return TraceLoggingDataType.FileTime;
                case EventSourceFieldFormat.Signed:
                    return TraceLoggingDataType.Int64;
                case EventSourceFieldFormat.Unsigned:
                    return TraceLoggingDataType.UInt64;
#endif
                default:
                    return MakeDataType(native, format);
            }
        }
 
        /// <summary>
        /// Adjusts the native type based on format.
        /// - If format is default, return native.
        /// - If format is recognized, return the canonical type for that format.
        /// - Otherwise remove existing format from native and apply the requested format.
        /// </summary>
        public static TraceLoggingDataType FormatPtr(
            EventFieldFormat format,
            TraceLoggingDataType native)
        {
            switch (format)
            {
                case EventFieldFormat.Default:
                    return native;
                case EventFieldFormat.Hexadecimal:
                    return HexIntPtrType;
#if false 
                case EventSourceFieldFormat.Signed:
                    return IntPtrType;
                case EventSourceFieldFormat.Unsigned:
                    return UIntPtrType;
#endif 
                default:
                    return MakeDataType(native, format);
            }
        }
 
        #endregion
 
        #region Reflection helpers
 
        /*
        All TraceLogging use of reflection APIs should go through wrappers here.
        This helps with portability, and it also makes it easier to audit what
        kinds of reflection operations are being done.
        */
 
        public static object CreateInstance(Type type, params object[] parameters)
        {
            return Activator.CreateInstance(type, parameters);
        }
 
        public static bool IsValueType(Type type)
        {
            bool result;
#if ES_BUILD_PCL
            result = type.GetTypeInfo().IsValueType;
#else
            result = type.IsValueType;
#endif
            return result;
        }
 
        public static bool IsEnum(Type type)
        {
            bool result;
#if ES_BUILD_PCL
            result = type.GetTypeInfo().IsEnum;
#else
            result = type.IsEnum;
#endif
            return result;
        }
 
        public static IEnumerable<PropertyInfo> GetProperties(Type type)
        {
            IEnumerable<PropertyInfo> result;
#if ES_BUILD_PCL
            result = type.GetRuntimeProperties();
#else
            result = type.GetProperties();
#endif
            return result;
        }
 
        public static MethodInfo GetGetMethod(PropertyInfo propInfo)
        {
            MethodInfo result;
#if ES_BUILD_PCL
            result = propInfo.GetMethod;
#else
            result = propInfo.GetGetMethod();
#endif
            return result;
        }
 
        public static MethodInfo GetDeclaredStaticMethod(Type declaringType, string name)
        {
            MethodInfo result;
#if ES_BUILD_PCL
            result = declaringType.GetTypeInfo().GetDeclaredMethod(name);
#else
            result = declaringType.GetMethod(
                name,
                BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.NonPublic);
#endif
            return result;
        }
 
        public static bool HasCustomAttribute(
            PropertyInfo propInfo,
            Type attributeType)
        {
            bool result;
#if ES_BUILD_PCL
            result = propInfo.IsDefined(attributeType);
#else
            var attributes = propInfo.GetCustomAttributes(
                attributeType,
                false);
            result = attributes.Length != 0;
#endif
            return result;
        }
 
        public static AttributeType GetCustomAttribute<AttributeType>(PropertyInfo propInfo)
            where AttributeType : Attribute
        {
            AttributeType result = null;
#if ES_BUILD_PCL
            foreach (var attrib in propInfo.GetCustomAttributes<AttributeType>(false))
            {
                result = attrib;
                break;
            }
#else
            var attributes = propInfo.GetCustomAttributes(typeof(AttributeType), false);
            if (attributes.Length != 0)
            {
                result = (AttributeType)attributes[0];
            }
#endif
            return result;
        }
 
        public static AttributeType GetCustomAttribute<AttributeType>(Type type)
            where AttributeType : Attribute
        {
            AttributeType result = null;
#if ES_BUILD_PCL
            foreach (var attrib in type.GetTypeInfo().GetCustomAttributes<AttributeType>(false))
            {
                result = attrib;
                break;
            }
#else
            var attributes = type.GetCustomAttributes(typeof(AttributeType), false);
            if (attributes.Length != 0)
            {
                result = (AttributeType)attributes[0];
            }
#endif
            return result;
        }
 
        public static Type[] GetGenericArguments(Type type)
        {
#if ES_BUILD_PCL
            return type.GenericTypeArguments;
#else
            return type.GetGenericArguments();
#endif
        }
 
        public static Type FindEnumerableElementType(Type type)
        {
            Type elementType = null;
 
            if (IsGenericMatch(type, typeof(IEnumerable<>)))
            {
                elementType = GetGenericArguments(type)[0];
            }
            else
            {
#if ES_BUILD_PCL
            var ifaceTypes = type.GetTypeInfo().ImplementedInterfaces;
#else
                var ifaceTypes = type.FindInterfaces(IsGenericMatch, typeof(IEnumerable<>));
#endif
 
                foreach (var ifaceType in ifaceTypes)
                {
#if ES_BUILD_PCL
                if (!IsGenericMatch(ifaceType, typeof(IEnumerable<>)))
                {
                    continue;
                }
#endif
 
                    if (elementType != null)
                    {
                        // ambiguous match. report no match at all.
                        elementType = null;
                        break;
                    }
 
                    elementType = GetGenericArguments(ifaceType)[0];
                }
            }
 
            return elementType;
        }
 
        public static bool IsGenericMatch(Type type, object openType)
        {
            bool isGeneric;
#if ES_BUILD_PCL
            isGeneric = type.IsConstructedGenericType;
#else
            isGeneric = type.IsGenericType;
#endif
            return isGeneric && type.GetGenericTypeDefinition() == (Type)openType;
        }
 
        public static Delegate CreateDelegate(Type delegateType, MethodInfo methodInfo)
        {
            Delegate result;
#if ES_BUILD_PCL
            result = methodInfo.CreateDelegate(
                delegateType);
#else
            result = Delegate.CreateDelegate(
                delegateType,
                methodInfo);
#endif
            return result;
        }
 
        public static TraceLoggingTypeInfo GetTypeInfoInstance(Type dataType, List<Type> recursionCheck)
        {
            TraceLoggingTypeInfo result;
 
            if (dataType == typeof(Int32))
            {
                result = TraceLoggingTypeInfo<Int32>.Instance;
            }
            else if (dataType == typeof(Int64))
            {
                result = TraceLoggingTypeInfo<Int64>.Instance;
            }
            else if (dataType == typeof(String))
            {
                result = TraceLoggingTypeInfo<String>.Instance;
            }
            else
            {
                var getInstanceInfo = Statics.GetDeclaredStaticMethod(
                    typeof(TraceLoggingTypeInfo<>).MakeGenericType(dataType),
                    "GetInstance");
                var typeInfoObj = getInstanceInfo.Invoke(null, new object[] { recursionCheck });
                result = (TraceLoggingTypeInfo)typeInfoObj;
            }
 
            return result;
        }
 
        public static TraceLoggingTypeInfo<DataType> CreateDefaultTypeInfo<DataType>(
            List<Type> recursionCheck)
        {
            TraceLoggingTypeInfo result;
            var dataType = typeof(DataType);
 
            if (recursionCheck.Contains(dataType))
            {
                throw new NotSupportedException(Environment.GetResourceString("EventSource_RecursiveTypeDefinition"));
            }
 
            recursionCheck.Add(dataType);
 
            var eventAttrib = Statics.GetCustomAttribute<EventDataAttribute>(dataType);
            if (eventAttrib != null ||
                Statics.GetCustomAttribute<CompilerGeneratedAttribute>(dataType) != null)
            {
                var analysis = new TypeAnalysis(dataType, eventAttrib, recursionCheck);
                result = new InvokeTypeInfo<DataType>(analysis);
            }
            else if (dataType.IsArray)
            {
                var elementType = dataType.GetElementType();
                if (elementType == typeof(Boolean))
                {
                    result = new BooleanArrayTypeInfo();
                }
                else if (elementType == typeof(Byte))
                {
                    result = new ByteArrayTypeInfo();
                }
                else if (elementType == typeof(SByte))
                {
                    result = new SByteArrayTypeInfo();
                }
                else if (elementType == typeof(Int16))
                {
                    result = new Int16ArrayTypeInfo();
                }
                else if (elementType == typeof(UInt16))
                {
                    result = new UInt16ArrayTypeInfo();
                }
                else if (elementType == typeof(Int32))
                {
                    result = new Int32ArrayTypeInfo();
                }
                else if (elementType == typeof(UInt32))
                {
                    result = new UInt32ArrayTypeInfo();
                }
                else if (elementType == typeof(Int64))
                {
                    result = new Int64ArrayTypeInfo();
                }
                else if (elementType == typeof(UInt64))
                {
                    result = new UInt64ArrayTypeInfo();
                }
                else if (elementType == typeof(Char))
                {
                    result = new CharArrayTypeInfo();
                }
                else if (elementType == typeof(Double))
                {
                    result = new DoubleArrayTypeInfo();
                }
                else if (elementType == typeof(Single))
                {
                    result = new SingleArrayTypeInfo();
                }
                else if (elementType == typeof(IntPtr))
                {
                    result = new IntPtrArrayTypeInfo();
                }
                else if (elementType == typeof(UIntPtr))
                {
                    result = new UIntPtrArrayTypeInfo();
                }
                else if (elementType == typeof(Guid))
                {
                    result = new GuidArrayTypeInfo();
                }
                else
                {
                    result = (TraceLoggingTypeInfo<DataType>)CreateInstance(
                        typeof(ArrayTypeInfo<>).MakeGenericType(elementType),
                        GetTypeInfoInstance(elementType, recursionCheck));
                }
            }
            else if (Statics.IsEnum(dataType))
            {
                var underlyingType = Enum.GetUnderlyingType(dataType);
                if (underlyingType == typeof(Int32))
                {
                    result = new EnumInt32TypeInfo<DataType>();
                }
                else if (underlyingType == typeof(UInt32))
                {
                    result = new EnumUInt32TypeInfo<DataType>();
                }
                else if (underlyingType == typeof(Byte))
                {
                    result = new EnumByteTypeInfo<DataType>();
                }
                else if (underlyingType == typeof(SByte))
                {
                    result = new EnumSByteTypeInfo<DataType>();
                }
                else if (underlyingType == typeof(Int16))
                {
                    result = new EnumInt16TypeInfo<DataType>();
                }
                else if (underlyingType == typeof(UInt16))
                {
                    result = new EnumUInt16TypeInfo<DataType>();
                }
                else if (underlyingType == typeof(Int64))
                {
                    result = new EnumInt64TypeInfo<DataType>();
                }
                else if (underlyingType == typeof(UInt64))
                {
                    result = new EnumUInt64TypeInfo<DataType>();
                }
                else
                {
                    // Unexpected
                    throw new NotSupportedException(Environment.GetResourceString("EventSource_NotSupportedEnumType", dataType.Name, underlyingType.Name));
                }
            }
            else if (dataType == typeof(String))
            {
                result = new StringTypeInfo();
            }
            else if (dataType == typeof(Boolean))
            {
                result = new BooleanTypeInfo();
            }
            else if (dataType == typeof(Byte))
            {
                result = new ByteTypeInfo();
            }
            else if (dataType == typeof(SByte))
            {
                result = new SByteTypeInfo();
            }
            else if (dataType == typeof(Int16))
            {
                result = new Int16TypeInfo();
            }
            else if (dataType == typeof(UInt16))
            {
                result = new UInt16TypeInfo();
            }
            else if (dataType == typeof(Int32))
            {
                result = new Int32TypeInfo();
            }
            else if (dataType == typeof(UInt32))
            {
                result = new UInt32TypeInfo();
            }
            else if (dataType == typeof(Int64))
            {
                result = new Int64TypeInfo();
            }
            else if (dataType == typeof(UInt64))
            {
                result = new UInt64TypeInfo();
            }
            else if (dataType == typeof(Char))
            {
                result = new CharTypeInfo();
            }
            else if (dataType == typeof(Double))
            {
                result = new DoubleTypeInfo();
            }
            else if (dataType == typeof(Single))
            {
                result = new SingleTypeInfo();
            }
            else if (dataType == typeof(DateTime))
            {
                result = new DateTimeTypeInfo();
            }
            else if (dataType == typeof(Decimal))
            {
                result = new DecimalTypeInfo();
            }
            else if (dataType == typeof(IntPtr))
            {
                result = new IntPtrTypeInfo();
            }
            else if (dataType == typeof(UIntPtr))
            {
                result = new UIntPtrTypeInfo();
            }
            else if (dataType == typeof(Guid))
            {
                result = new GuidTypeInfo();
            }
            else if (dataType == typeof(TimeSpan))
            {
                result = new TimeSpanTypeInfo();
            }
            else if (dataType == typeof(DateTimeOffset))
            {
                result = new DateTimeOffsetTypeInfo();
            }
            else if (dataType == typeof(EmptyStruct))
            {
                result = new NullTypeInfo<EmptyStruct>();
            }
            else if (IsGenericMatch(dataType, typeof(KeyValuePair<,>)))
            {
                var args = GetGenericArguments(dataType);
                result = (TraceLoggingTypeInfo<DataType>)CreateInstance(
                    typeof(KeyValuePairTypeInfo<,>).MakeGenericType(args[0], args[1]),
                    recursionCheck);
            }
            else if (IsGenericMatch(dataType, typeof(Nullable<>)))
            {
                var args = GetGenericArguments(dataType);
                result = (TraceLoggingTypeInfo<DataType>)CreateInstance(
                    typeof(NullableTypeInfo<>).MakeGenericType(args[0]),
                    recursionCheck);
            }
            else
            {
                var elementType = FindEnumerableElementType(dataType);
                if (elementType != null)
                {
                    result = (TraceLoggingTypeInfo<DataType>)CreateInstance(
                        typeof(EnumerableTypeInfo<,>).MakeGenericType(dataType, elementType),
                        GetTypeInfoInstance(elementType, recursionCheck));
                }
                else
                {
                    throw new ArgumentException(Environment.GetResourceString("EventSource_NonCompliantTypeError", dataType.Name));
                }
            }
 
            return (TraceLoggingTypeInfo<DataType>)result;
        }
 
        #endregion
    }
}