ConvertUtils
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.SqlTypes;
using System.Globalization;
using System.Numerics;
using System.Reflection;
namespace Newtonsoft.Json.Utilities
{
internal static class ConvertUtils
{
internal struct TypeConvertKey : IEquatable<TypeConvertKey>
{
private readonly Type _initialType;
private readonly Type _targetType;
public Type InitialType => _initialType;
public Type TargetType => _targetType;
public TypeConvertKey(Type initialType, Type targetType)
{
_initialType = initialType;
_targetType = targetType;
}
public override int GetHashCode()
{
return _initialType.GetHashCode() ^ _targetType.GetHashCode();
}
public override bool Equals(object obj)
{
if (!(obj is TypeConvertKey))
return false;
return Equals((TypeConvertKey)obj);
}
public bool Equals(TypeConvertKey other)
{
if (_initialType == other._initialType)
return _targetType == other._targetType;
return false;
}
}
internal enum ConvertResult
{
Success,
CannotConvertNull,
NotInstantiableType,
NoValidConversion
}
private static readonly Dictionary<Type, PrimitiveTypeCode> TypeCodeMap = new Dictionary<Type, PrimitiveTypeCode> {
{
typeof(char),
PrimitiveTypeCode.Char
},
{
typeof(char?),
PrimitiveTypeCode.CharNullable
},
{
typeof(bool),
PrimitiveTypeCode.Boolean
},
{
typeof(bool?),
PrimitiveTypeCode.BooleanNullable
},
{
typeof(sbyte),
PrimitiveTypeCode.SByte
},
{
typeof(sbyte?),
PrimitiveTypeCode.SByteNullable
},
{
typeof(short),
PrimitiveTypeCode.Int16
},
{
typeof(short?),
PrimitiveTypeCode.Int16Nullable
},
{
typeof(ushort),
PrimitiveTypeCode.UInt16
},
{
typeof(ushort?),
PrimitiveTypeCode.UInt16Nullable
},
{
typeof(int),
PrimitiveTypeCode.Int32
},
{
typeof(int?),
PrimitiveTypeCode.Int32Nullable
},
{
typeof(byte),
PrimitiveTypeCode.Byte
},
{
typeof(byte?),
PrimitiveTypeCode.ByteNullable
},
{
typeof(uint),
PrimitiveTypeCode.UInt32
},
{
typeof(uint?),
PrimitiveTypeCode.UInt32Nullable
},
{
typeof(long),
PrimitiveTypeCode.Int64
},
{
typeof(long?),
PrimitiveTypeCode.Int64Nullable
},
{
typeof(ulong),
PrimitiveTypeCode.UInt64
},
{
typeof(ulong?),
PrimitiveTypeCode.UInt64Nullable
},
{
typeof(float),
PrimitiveTypeCode.Single
},
{
typeof(float?),
PrimitiveTypeCode.SingleNullable
},
{
typeof(double),
PrimitiveTypeCode.Double
},
{
typeof(double?),
PrimitiveTypeCode.DoubleNullable
},
{
typeof(DateTime),
PrimitiveTypeCode.DateTime
},
{
typeof(DateTime?),
PrimitiveTypeCode.DateTimeNullable
},
{
typeof(DateTimeOffset),
PrimitiveTypeCode.DateTimeOffset
},
{
typeof(DateTimeOffset?),
PrimitiveTypeCode.DateTimeOffsetNullable
},
{
typeof(decimal),
PrimitiveTypeCode.Decimal
},
{
typeof(decimal?),
PrimitiveTypeCode.DecimalNullable
},
{
typeof(Guid),
PrimitiveTypeCode.Guid
},
{
typeof(Guid?),
PrimitiveTypeCode.GuidNullable
},
{
typeof(TimeSpan),
PrimitiveTypeCode.TimeSpan
},
{
typeof(TimeSpan?),
PrimitiveTypeCode.TimeSpanNullable
},
{
typeof(BigInteger),
PrimitiveTypeCode.BigInteger
},
{
typeof(BigInteger?),
PrimitiveTypeCode.BigIntegerNullable
},
{
typeof(Uri),
PrimitiveTypeCode.Uri
},
{
typeof(string),
PrimitiveTypeCode.String
},
{
typeof(byte[]),
PrimitiveTypeCode.Bytes
},
{
typeof(DBNull),
PrimitiveTypeCode.DBNull
}
};
private static readonly TypeInformation[] PrimitiveTypeCodes = new TypeInformation[19] {
new TypeInformation {
Type = typeof(object),
TypeCode = PrimitiveTypeCode.Empty
},
new TypeInformation {
Type = typeof(object),
TypeCode = PrimitiveTypeCode.Object
},
new TypeInformation {
Type = typeof(object),
TypeCode = PrimitiveTypeCode.DBNull
},
new TypeInformation {
Type = typeof(bool),
TypeCode = PrimitiveTypeCode.Boolean
},
new TypeInformation {
Type = typeof(char),
TypeCode = PrimitiveTypeCode.Char
},
new TypeInformation {
Type = typeof(sbyte),
TypeCode = PrimitiveTypeCode.SByte
},
new TypeInformation {
Type = typeof(byte),
TypeCode = PrimitiveTypeCode.Byte
},
new TypeInformation {
Type = typeof(short),
TypeCode = PrimitiveTypeCode.Int16
},
new TypeInformation {
Type = typeof(ushort),
TypeCode = PrimitiveTypeCode.UInt16
},
new TypeInformation {
Type = typeof(int),
TypeCode = PrimitiveTypeCode.Int32
},
new TypeInformation {
Type = typeof(uint),
TypeCode = PrimitiveTypeCode.UInt32
},
new TypeInformation {
Type = typeof(long),
TypeCode = PrimitiveTypeCode.Int64
},
new TypeInformation {
Type = typeof(ulong),
TypeCode = PrimitiveTypeCode.UInt64
},
new TypeInformation {
Type = typeof(float),
TypeCode = PrimitiveTypeCode.Single
},
new TypeInformation {
Type = typeof(double),
TypeCode = PrimitiveTypeCode.Double
},
new TypeInformation {
Type = typeof(decimal),
TypeCode = PrimitiveTypeCode.Decimal
},
new TypeInformation {
Type = typeof(DateTime),
TypeCode = PrimitiveTypeCode.DateTime
},
new TypeInformation {
Type = typeof(object),
TypeCode = PrimitiveTypeCode.Empty
},
new TypeInformation {
Type = typeof(string),
TypeCode = PrimitiveTypeCode.String
}
};
private static readonly ThreadSafeStore<TypeConvertKey, Func<object, object>> CastConverters = new ThreadSafeStore<TypeConvertKey, Func<object, object>>(CreateCastConverter);
private static decimal[] _decimalFactors;
private static decimal[] DecimalFactors {
get {
decimal[] array = _decimalFactors;
if (array == null) {
array = new decimal[28];
decimal d = decimal.One;
for (int i = 0; i < array.Length; i++) {
d = (array[i] = d * 10);
}
_decimalFactors = array;
}
return array;
}
}
public static PrimitiveTypeCode GetTypeCode(Type t)
{
bool isEnum;
return GetTypeCode(t, out isEnum);
}
public static PrimitiveTypeCode GetTypeCode(Type t, out bool isEnum)
{
if (TypeCodeMap.TryGetValue(t, out PrimitiveTypeCode value)) {
isEnum = false;
return value;
}
if (t.IsEnum()) {
isEnum = true;
return GetTypeCode(Enum.GetUnderlyingType(t));
}
if (ReflectionUtils.IsNullableType(t)) {
Type underlyingType = Nullable.GetUnderlyingType(t);
if (underlyingType.IsEnum()) {
Type t2 = typeof(Nullable<>).MakeGenericType(Enum.GetUnderlyingType(underlyingType));
isEnum = true;
return GetTypeCode(t2);
}
}
isEnum = false;
return PrimitiveTypeCode.Object;
}
public static TypeInformation GetTypeInformation(IConvertible convertable)
{
return PrimitiveTypeCodes[(int)convertable.GetTypeCode()];
}
public static bool IsConvertible(Type t)
{
return typeof(IConvertible).IsAssignableFrom(t);
}
public static TimeSpan ParseTimeSpan(string input)
{
return TimeSpan.Parse(input, CultureInfo.InvariantCulture);
}
private static Func<object, object> CreateCastConverter(TypeConvertKey t)
{
MethodInfo method = t.TargetType.GetMethod("op_Implicit", new Type[1] {
t.InitialType
});
if (method == (MethodInfo)null)
method = t.TargetType.GetMethod("op_Explicit", new Type[1] {
t.InitialType
});
if (method == (MethodInfo)null)
return null;
MethodCall<object, object> call = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(method);
return (object o) => call(null, o);
}
internal static BigInteger ToBigInteger(object value)
{
if (value is BigInteger)
return (BigInteger)value;
string text = value as string;
if (text != null)
return BigInteger.Parse(text, CultureInfo.InvariantCulture);
if (value is float)
return new BigInteger((float)value);
if (value is double)
return new BigInteger((double)value);
if (value is decimal)
return new BigInteger((decimal)value);
if (value is int)
return new BigInteger((int)value);
if (value is long)
return new BigInteger((long)value);
if (value is uint)
return new BigInteger((uint)value);
if (value is ulong)
return new BigInteger((ulong)value);
byte[] array = value as byte[];
if (array != null)
return new BigInteger(array);
throw new InvalidCastException("Cannot convert {0} to BigInteger.".FormatWith(CultureInfo.InvariantCulture, value.GetType()));
}
public static object FromBigInteger(BigInteger i, Type targetType)
{
if (targetType == typeof(decimal))
return (decimal)i;
if (targetType == typeof(double))
return (double)i;
if (targetType == typeof(float))
return (float)i;
if (targetType == typeof(ulong))
return (ulong)i;
if (!(targetType == typeof(bool)))
try {
return System.Convert.ChangeType((long)i, targetType, CultureInfo.InvariantCulture);
} catch (Exception innerException) {
throw new InvalidOperationException("Can not convert from BigInteger to {0}.".FormatWith(CultureInfo.InvariantCulture, targetType), innerException);
}
return i != 0;
}
public static object Convert(object initialValue, CultureInfo culture, Type targetType)
{
object value;
switch (TryConvertInternal(initialValue, culture, targetType, out value)) {
case ConvertResult.Success:
return value;
case ConvertResult.CannotConvertNull:
throw new Exception("Can not convert null {0} into non-nullable {1}.".FormatWith(CultureInfo.InvariantCulture, initialValue.GetType(), targetType));
case ConvertResult.NotInstantiableType:
throw new ArgumentException("Target type {0} is not a value type or a non-abstract class.".FormatWith(CultureInfo.InvariantCulture, targetType), "targetType");
case ConvertResult.NoValidConversion:
throw new InvalidOperationException("Can not convert from {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, initialValue.GetType(), targetType));
default:
throw new InvalidOperationException("Unexpected conversion result.");
}
}
private static bool TryConvert(object initialValue, CultureInfo culture, Type targetType, out object value)
{
try {
if (TryConvertInternal(initialValue, culture, targetType, out value) != 0) {
value = null;
return false;
}
return true;
} catch {
value = null;
return false;
}
}
private static ConvertResult TryConvertInternal(object initialValue, CultureInfo culture, Type targetType, out object value)
{
if (initialValue == null)
throw new ArgumentNullException("initialValue");
if (ReflectionUtils.IsNullableType(targetType))
targetType = Nullable.GetUnderlyingType(targetType);
Type type = initialValue.GetType();
if (targetType == type) {
value = initialValue;
return ConvertResult.Success;
}
if (IsConvertible(initialValue.GetType()) && IsConvertible(targetType)) {
if (targetType.IsEnum()) {
if (initialValue is string) {
value = Enum.Parse(targetType, initialValue.ToString(), true);
return ConvertResult.Success;
}
if (IsInteger(initialValue)) {
value = Enum.ToObject(targetType, initialValue);
return ConvertResult.Success;
}
}
value = System.Convert.ChangeType(initialValue, targetType, culture);
return ConvertResult.Success;
}
if (initialValue is DateTime && targetType == typeof(DateTimeOffset)) {
value = new DateTimeOffset((DateTime)initialValue);
return ConvertResult.Success;
}
byte[] array = initialValue as byte[];
if (array != null && targetType == typeof(Guid)) {
value = new Guid(array);
return ConvertResult.Success;
}
if (initialValue is Guid && targetType == typeof(byte[])) {
value = ((Guid)initialValue).ToByteArray();
return ConvertResult.Success;
}
string text = initialValue as string;
if (text != null) {
if (targetType == typeof(Guid)) {
value = new Guid(text);
return ConvertResult.Success;
}
if (targetType == typeof(Uri)) {
value = new Uri(text, UriKind.RelativeOrAbsolute);
return ConvertResult.Success;
}
if (targetType == typeof(TimeSpan)) {
value = ParseTimeSpan(text);
return ConvertResult.Success;
}
if (targetType == typeof(byte[])) {
value = System.Convert.FromBase64String(text);
return ConvertResult.Success;
}
if (targetType == typeof(Version)) {
if (VersionTryParse(text, out Version result)) {
value = result;
return ConvertResult.Success;
}
value = null;
return ConvertResult.NoValidConversion;
}
if (typeof(Type).IsAssignableFrom(targetType)) {
value = Type.GetType(text, true);
return ConvertResult.Success;
}
}
if (targetType == typeof(BigInteger)) {
value = ToBigInteger(initialValue);
return ConvertResult.Success;
}
if (initialValue is BigInteger) {
value = FromBigInteger((BigInteger)initialValue, targetType);
return ConvertResult.Success;
}
TypeConverter converter = TypeDescriptor.GetConverter(type);
if (converter != null && converter.CanConvertTo(targetType)) {
value = converter.ConvertTo(null, culture, initialValue, targetType);
return ConvertResult.Success;
}
TypeConverter converter2 = TypeDescriptor.GetConverter(targetType);
if (converter2 != null && converter2.CanConvertFrom(type)) {
value = converter2.ConvertFrom(null, culture, initialValue);
return ConvertResult.Success;
}
if (initialValue == DBNull.Value) {
if (ReflectionUtils.IsNullable(targetType)) {
value = EnsureTypeAssignable(null, type, targetType);
return ConvertResult.Success;
}
value = null;
return ConvertResult.CannotConvertNull;
}
INullable nullable = initialValue as INullable;
if (nullable != null) {
value = EnsureTypeAssignable(ToValue(nullable), type, targetType);
return ConvertResult.Success;
}
if (targetType.IsInterface() || targetType.IsGenericTypeDefinition() || targetType.IsAbstract()) {
value = null;
return ConvertResult.NotInstantiableType;
}
value = null;
return ConvertResult.NoValidConversion;
}
public static object ConvertOrCast(object initialValue, CultureInfo culture, Type targetType)
{
if (targetType == typeof(object))
return initialValue;
if (initialValue == null && ReflectionUtils.IsNullable(targetType))
return null;
if (TryConvert(initialValue, culture, targetType, out object value))
return value;
return EnsureTypeAssignable(initialValue, ReflectionUtils.GetObjectType(initialValue), targetType);
}
private static object EnsureTypeAssignable(object value, Type initialType, Type targetType)
{
Type type = value?.GetType();
if (value != null) {
if (targetType.IsAssignableFrom(type))
return value;
Func<object, object> func = CastConverters.Get(new TypeConvertKey(type, targetType));
if (func != null)
return func(value);
} else if (ReflectionUtils.IsNullable(targetType)) {
return null;
}
throw new ArgumentException("Could not cast or convert from {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, initialType?.ToString() ?? "{null}", targetType));
}
public static object ToValue(INullable nullableValue)
{
if (nullableValue == null)
return null;
if (nullableValue is SqlInt32)
return ToValue((SqlInt32)nullableValue);
if (nullableValue is SqlInt64)
return ToValue((SqlInt64)nullableValue);
if (nullableValue is SqlBoolean)
return ToValue((SqlBoolean)nullableValue);
if (nullableValue is SqlString)
return ToValue((SqlString)nullableValue);
if (nullableValue is SqlDateTime)
return ToValue((SqlDateTime)nullableValue);
throw new ArgumentException("Unsupported INullable type: {0}".FormatWith(CultureInfo.InvariantCulture, nullableValue.GetType()));
}
public static bool VersionTryParse(string input, out Version result)
{
return Version.TryParse(input, out result);
}
public static bool IsInteger(object value)
{
switch (GetTypeCode(value.GetType())) {
case PrimitiveTypeCode.SByte:
case PrimitiveTypeCode.Int16:
case PrimitiveTypeCode.UInt16:
case PrimitiveTypeCode.Int32:
case PrimitiveTypeCode.Byte:
case PrimitiveTypeCode.UInt32:
case PrimitiveTypeCode.Int64:
case PrimitiveTypeCode.UInt64:
return true;
default:
return false;
}
}
public static ParseResult Int32TryParse(char[] chars, int start, int length, out int value)
{
value = 0;
if (length == 0)
return ParseResult.Invalid;
bool flag = chars[start] == '-';
if (flag) {
if (length == 1)
return ParseResult.Invalid;
start++;
length--;
}
int num = start + length;
if (length > 10 || (length == 10 && chars[start] - 48 > 2)) {
for (int i = start; i < num; i++) {
int num2 = chars[i] - 48;
if (num2 < 0 || num2 > 9)
return ParseResult.Invalid;
}
return ParseResult.Overflow;
}
for (int j = start; j < num; j++) {
int num3 = chars[j] - 48;
if (num3 < 0 || num3 > 9)
return ParseResult.Invalid;
int num4 = 10 * value - num3;
if (num4 > value) {
for (j++; j < num; j++) {
num3 = chars[j] - 48;
if (num3 < 0 || num3 > 9)
return ParseResult.Invalid;
}
return ParseResult.Overflow;
}
value = num4;
}
if (!flag) {
if (value == -2147483648)
return ParseResult.Overflow;
value = -value;
}
return ParseResult.Success;
}
public static ParseResult Int64TryParse(char[] chars, int start, int length, out long value)
{
value = 0;
if (length == 0)
return ParseResult.Invalid;
bool flag = chars[start] == '-';
if (flag) {
if (length == 1)
return ParseResult.Invalid;
start++;
length--;
}
int num = start + length;
if (length > 19) {
for (int i = start; i < num; i++) {
int num2 = chars[i] - 48;
if (num2 < 0 || num2 > 9)
return ParseResult.Invalid;
}
return ParseResult.Overflow;
}
for (int j = start; j < num; j++) {
int num3 = chars[j] - 48;
if (num3 < 0 || num3 > 9)
return ParseResult.Invalid;
long num4 = 10 * value - num3;
if (num4 > value) {
for (j++; j < num; j++) {
num3 = chars[j] - 48;
if (num3 < 0 || num3 > 9)
return ParseResult.Invalid;
}
return ParseResult.Overflow;
}
value = num4;
}
if (!flag) {
if (value == -9223372036854775808)
return ParseResult.Overflow;
value = -value;
}
return ParseResult.Success;
}
public static ParseResult DecimalTryParse(char[] chars, int start, int length, out decimal value)
{
value = default(decimal);
if (length == 0)
return ParseResult.Invalid;
bool flag = chars[start] == '-';
if (flag) {
if (length == 1)
return ParseResult.Invalid;
start++;
length--;
}
int i = start;
int num = start + length;
int num2 = num;
int num3 = num;
int num4 = 0;
ulong num5 = 0;
ulong num6 = 0;
int num7 = 0;
int num8 = 0;
bool? nullable = null;
bool? nullable2 = null;
for (; i < num; i++) {
char c = chars[i];
if (c == '.')
goto IL_0074;
if (c == 'E' || c == 'e')
goto IL_0091;
if (c < '0' || c > '9')
return ParseResult.Invalid;
if (i == start && c == '0') {
i++;
if (i != num) {
switch (chars[i]) {
case '.':
break;
case 'E':
case 'e':
goto IL_0091;
default:
return ParseResult.Invalid;
}
goto IL_0074;
}
}
if (num7 < 29) {
if (num7 == 28) {
bool? nullable3 = nullable2;
int valueOrDefault;
if (!nullable3.HasValue) {
nullable2 = (num5 > 7922816251426433759 || (num5 == 7922816251426433759 && (num6 > 354395033 || (num6 == 354395033 && c > '5'))));
bool? nullable4 = nullable2;
valueOrDefault = (nullable4.GetValueOrDefault() ? 1 : 0);
} else
valueOrDefault = (nullable3.GetValueOrDefault() ? 1 : 0);
if (valueOrDefault != 0)
goto IL_01ff;
}
if (num7 < 19)
num5 = (ulong)((long)(num5 * 10) + (long)(c - 48));
else
num6 = (ulong)((long)(num6 * 10) + (long)(c - 48));
num7++;
continue;
}
goto IL_01ff;
IL_0091:
if (i == start)
return ParseResult.Invalid;
if (i == num2)
return ParseResult.Invalid;
i++;
if (i == num)
return ParseResult.Invalid;
if (num2 < num)
num3 = i - 1;
c = chars[i];
bool flag2 = false;
switch (c) {
case '-':
flag2 = true;
i++;
break;
case '+':
i++;
break;
}
for (; i < num; i++) {
c = chars[i];
if (c < '0' || c > '9')
return ParseResult.Invalid;
int num9 = 10 * num4 + (c - 48);
if (num4 < num9)
num4 = num9;
}
if (flag2)
num4 = -num4;
continue;
IL_01ff:
if (!nullable.HasValue)
nullable = (c >= '5');
num8++;
continue;
IL_0074:
if (i == start)
return ParseResult.Invalid;
if (i + 1 == num)
return ParseResult.Invalid;
if (num2 != num)
return ParseResult.Invalid;
num2 = i + 1;
}
num4 += num8;
num4 -= num3 - num2;
if (num7 <= 19)
value = num5;
else
value = (decimal)num5 * DecimalFactors[num7 - 20] + (decimal)num6;
if (num4 > 0) {
num7 += num4;
if (num7 > 29)
return ParseResult.Overflow;
if (num7 != 29)
value *= DecimalFactors[num4 - 1];
else {
if (num4 > 1) {
value *= DecimalFactors[num4 - 2];
if (value > 7922816251426433759354395033)
return ParseResult.Overflow;
}
value *= 10;
}
} else {
if (nullable == true && num4 >= -28)
++value;
if (num4 < 0) {
if (num7 + num4 + 28 <= 0) {
value = default(decimal);
return ParseResult.Success;
}
if (num4 >= -28)
value /= DecimalFactors[-num4 - 1];
else {
decimal[] decimalFactors = DecimalFactors;
value /= decimalFactors[27];
value /= decimalFactors[-num4 - 29];
}
}
}
if (flag)
value = -value;
return ParseResult.Success;
}
public static bool TryConvertGuid(string s, out Guid g)
{
return Guid.TryParseExact(s, "D", out g);
}
public static bool TryHexTextToInt(char[] text, int start, int end, out int value)
{
value = 0;
for (int i = start; i < end; i++) {
char c = text[i];
int num;
if (c <= '9' && c >= '0')
num = c - 48;
else if (c <= 'F' && c >= 'A') {
num = c - 55;
} else {
if (c > 'f' || c < 'a') {
value = 0;
return false;
}
num = c - 87;
}
value += num << (end - 1 - i) * 4;
}
return true;
}
}
}