You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1587 lines
54 KiB

  1. #region License
  2. // Copyright (c) 2007 James Newton-King
  3. //
  4. // Permission is hereby granted, free of charge, to any person
  5. // obtaining a copy of this software and associated documentation
  6. // files (the "Software"), to deal in the Software without
  7. // restriction, including without limitation the rights to use,
  8. // copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the
  10. // Software is furnished to do so, subject to the following
  11. // conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be
  14. // included in all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  18. // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  20. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  21. // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  22. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  23. // OTHER DEALINGS IN THE SOFTWARE.
  24. #endregion
  25. using System;
  26. using System.Collections.Generic;
  27. using System.Globalization;
  28. using System.ComponentModel;
  29. #if HAVE_BIG_INTEGER
  30. using System.Numerics;
  31. #endif
  32. #if NET20
  33. using System.Text;
  34. using System.Text.RegularExpressions;
  35. #endif
  36. using Newtonsoft.Json.Serialization;
  37. using System.Reflection;
  38. #if NET20
  39. using Newtonsoft.Json.Utilities.LinqBridge;
  40. #endif
  41. #if HAVE_ADO_NET
  42. using System.Data.SqlTypes;
  43. #endif
  44. namespace Newtonsoft.Json.Utilities
  45. {
  46. internal enum PrimitiveTypeCode
  47. {
  48. Empty = 0,
  49. Object = 1,
  50. Char = 2,
  51. CharNullable = 3,
  52. Boolean = 4,
  53. BooleanNullable = 5,
  54. SByte = 6,
  55. SByteNullable = 7,
  56. Int16 = 8,
  57. Int16Nullable = 9,
  58. UInt16 = 10,
  59. UInt16Nullable = 11,
  60. Int32 = 12,
  61. Int32Nullable = 13,
  62. Byte = 14,
  63. ByteNullable = 15,
  64. UInt32 = 16,
  65. UInt32Nullable = 17,
  66. Int64 = 18,
  67. Int64Nullable = 19,
  68. UInt64 = 20,
  69. UInt64Nullable = 21,
  70. Single = 22,
  71. SingleNullable = 23,
  72. Double = 24,
  73. DoubleNullable = 25,
  74. DateTime = 26,
  75. DateTimeNullable = 27,
  76. DateTimeOffset = 28,
  77. DateTimeOffsetNullable = 29,
  78. Decimal = 30,
  79. DecimalNullable = 31,
  80. Guid = 32,
  81. GuidNullable = 33,
  82. TimeSpan = 34,
  83. TimeSpanNullable = 35,
  84. BigInteger = 36,
  85. BigIntegerNullable = 37,
  86. Uri = 38,
  87. String = 39,
  88. Bytes = 40,
  89. DBNull = 41
  90. }
  91. internal class TypeInformation
  92. {
  93. public Type Type { get; set; }
  94. public PrimitiveTypeCode TypeCode { get; set; }
  95. }
  96. internal enum ParseResult
  97. {
  98. None = 0,
  99. Success = 1,
  100. Overflow = 2,
  101. Invalid = 3
  102. }
  103. internal static class ConvertUtils
  104. {
  105. private static readonly Dictionary<Type, PrimitiveTypeCode> TypeCodeMap =
  106. new Dictionary<Type, PrimitiveTypeCode>
  107. {
  108. { typeof(char), PrimitiveTypeCode.Char },
  109. { typeof(char?), PrimitiveTypeCode.CharNullable },
  110. { typeof(bool), PrimitiveTypeCode.Boolean },
  111. { typeof(bool?), PrimitiveTypeCode.BooleanNullable },
  112. { typeof(sbyte), PrimitiveTypeCode.SByte },
  113. { typeof(sbyte?), PrimitiveTypeCode.SByteNullable },
  114. { typeof(short), PrimitiveTypeCode.Int16 },
  115. { typeof(short?), PrimitiveTypeCode.Int16Nullable },
  116. { typeof(ushort), PrimitiveTypeCode.UInt16 },
  117. { typeof(ushort?), PrimitiveTypeCode.UInt16Nullable },
  118. { typeof(int), PrimitiveTypeCode.Int32 },
  119. { typeof(int?), PrimitiveTypeCode.Int32Nullable },
  120. { typeof(byte), PrimitiveTypeCode.Byte },
  121. { typeof(byte?), PrimitiveTypeCode.ByteNullable },
  122. { typeof(uint), PrimitiveTypeCode.UInt32 },
  123. { typeof(uint?), PrimitiveTypeCode.UInt32Nullable },
  124. { typeof(long), PrimitiveTypeCode.Int64 },
  125. { typeof(long?), PrimitiveTypeCode.Int64Nullable },
  126. { typeof(ulong), PrimitiveTypeCode.UInt64 },
  127. { typeof(ulong?), PrimitiveTypeCode.UInt64Nullable },
  128. { typeof(float), PrimitiveTypeCode.Single },
  129. { typeof(float?), PrimitiveTypeCode.SingleNullable },
  130. { typeof(double), PrimitiveTypeCode.Double },
  131. { typeof(double?), PrimitiveTypeCode.DoubleNullable },
  132. { typeof(DateTime), PrimitiveTypeCode.DateTime },
  133. { typeof(DateTime?), PrimitiveTypeCode.DateTimeNullable },
  134. #if !NET20
  135. { typeof(DateTimeOffset), PrimitiveTypeCode.DateTimeOffset },
  136. { typeof(DateTimeOffset?), PrimitiveTypeCode.DateTimeOffsetNullable },
  137. #endif
  138. { typeof(decimal), PrimitiveTypeCode.Decimal },
  139. { typeof(decimal?), PrimitiveTypeCode.DecimalNullable },
  140. { typeof(Guid), PrimitiveTypeCode.Guid },
  141. { typeof(Guid?), PrimitiveTypeCode.GuidNullable },
  142. { typeof(TimeSpan), PrimitiveTypeCode.TimeSpan },
  143. { typeof(TimeSpan?), PrimitiveTypeCode.TimeSpanNullable },
  144. #if HAVE_BIG_INTEGER
  145. { typeof(BigInteger), PrimitiveTypeCode.BigInteger },
  146. { typeof(BigInteger?), PrimitiveTypeCode.BigIntegerNullable },
  147. #endif
  148. { typeof(Uri), PrimitiveTypeCode.Uri },
  149. { typeof(string), PrimitiveTypeCode.String },
  150. { typeof(byte[]), PrimitiveTypeCode.Bytes },
  151. #if HAVE_ADO_NET
  152. { typeof(DBNull), PrimitiveTypeCode.DBNull }
  153. #endif
  154. };
  155. private static readonly TypeInformation[] PrimitiveTypeCodes =
  156. {
  157. // need all of these. lookup against the index with TypeCode value
  158. new TypeInformation { Type = typeof(object), TypeCode = PrimitiveTypeCode.Empty },
  159. new TypeInformation { Type = typeof(object), TypeCode = PrimitiveTypeCode.Object },
  160. new TypeInformation { Type = typeof(object), TypeCode = PrimitiveTypeCode.DBNull },
  161. new TypeInformation { Type = typeof(bool), TypeCode = PrimitiveTypeCode.Boolean },
  162. new TypeInformation { Type = typeof(char), TypeCode = PrimitiveTypeCode.Char },
  163. new TypeInformation { Type = typeof(sbyte), TypeCode = PrimitiveTypeCode.SByte },
  164. new TypeInformation { Type = typeof(byte), TypeCode = PrimitiveTypeCode.Byte },
  165. new TypeInformation { Type = typeof(short), TypeCode = PrimitiveTypeCode.Int16 },
  166. new TypeInformation { Type = typeof(ushort), TypeCode = PrimitiveTypeCode.UInt16 },
  167. new TypeInformation { Type = typeof(int), TypeCode = PrimitiveTypeCode.Int32 },
  168. new TypeInformation { Type = typeof(uint), TypeCode = PrimitiveTypeCode.UInt32 },
  169. new TypeInformation { Type = typeof(long), TypeCode = PrimitiveTypeCode.Int64 },
  170. new TypeInformation { Type = typeof(ulong), TypeCode = PrimitiveTypeCode.UInt64 },
  171. new TypeInformation { Type = typeof(float), TypeCode = PrimitiveTypeCode.Single },
  172. new TypeInformation { Type = typeof(double), TypeCode = PrimitiveTypeCode.Double },
  173. new TypeInformation { Type = typeof(decimal), TypeCode = PrimitiveTypeCode.Decimal },
  174. new TypeInformation { Type = typeof(DateTime), TypeCode = PrimitiveTypeCode.DateTime },
  175. new TypeInformation { Type = typeof(object), TypeCode = PrimitiveTypeCode.Empty }, // no 17 in TypeCode for some reason
  176. new TypeInformation { Type = typeof(string), TypeCode = PrimitiveTypeCode.String }
  177. };
  178. public static PrimitiveTypeCode GetTypeCode(Type t)
  179. {
  180. return GetTypeCode(t, out _);
  181. }
  182. public static PrimitiveTypeCode GetTypeCode(Type t, out bool isEnum)
  183. {
  184. if (TypeCodeMap.TryGetValue(t, out PrimitiveTypeCode typeCode))
  185. {
  186. isEnum = false;
  187. return typeCode;
  188. }
  189. if (t.IsEnum())
  190. {
  191. isEnum = true;
  192. return GetTypeCode(Enum.GetUnderlyingType(t));
  193. }
  194. // performance?
  195. if (ReflectionUtils.IsNullableType(t))
  196. {
  197. Type nonNullable = Nullable.GetUnderlyingType(t);
  198. if (nonNullable.IsEnum())
  199. {
  200. Type nullableUnderlyingType = typeof(Nullable<>).MakeGenericType(Enum.GetUnderlyingType(nonNullable));
  201. isEnum = true;
  202. return GetTypeCode(nullableUnderlyingType);
  203. }
  204. }
  205. isEnum = false;
  206. return PrimitiveTypeCode.Object;
  207. }
  208. public static TypeInformation GetTypeInformation(IConvertible convertable)
  209. {
  210. TypeInformation typeInformation = PrimitiveTypeCodes[(int)convertable.GetTypeCode()];
  211. return typeInformation;
  212. }
  213. public static bool IsConvertible(Type t)
  214. {
  215. return typeof(IConvertible).IsAssignableFrom(t);
  216. // return (
  217. // t == typeof(bool) || t == typeof(byte) || t == typeof(char) || t == typeof(DateTime) || t == typeof(decimal) || t == typeof(double) || t == typeof(short) || t == typeof(int) ||
  218. // t == typeof(long) || t == typeof(sbyte) || t == typeof(float) || t == typeof(string) || t == typeof(ushort) || t == typeof(uint) || t == typeof(ulong) || t.IsEnum());
  219. }
  220. public static TimeSpan ParseTimeSpan(string input)
  221. {
  222. #if !NET20
  223. return TimeSpan.Parse(input, CultureInfo.InvariantCulture);
  224. #else
  225. return TimeSpan.Parse(input);
  226. #endif
  227. }
  228. internal readonly struct TypeConvertKey : IEquatable<TypeConvertKey>
  229. {
  230. public Type InitialType { get; }
  231. public Type TargetType { get; }
  232. public TypeConvertKey(Type initialType, Type targetType)
  233. {
  234. InitialType = initialType;
  235. TargetType = targetType;
  236. }
  237. public override int GetHashCode()
  238. {
  239. return InitialType.GetHashCode() ^ TargetType.GetHashCode();
  240. }
  241. public override bool Equals(object obj)
  242. {
  243. if (!(obj is TypeConvertKey))
  244. {
  245. return false;
  246. }
  247. return Equals((TypeConvertKey)obj);
  248. }
  249. public bool Equals(TypeConvertKey other)
  250. {
  251. return (InitialType == other.InitialType && TargetType == other.TargetType);
  252. }
  253. }
  254. private static readonly ThreadSafeStore<TypeConvertKey, Func<object, object>> CastConverters =
  255. new ThreadSafeStore<TypeConvertKey, Func<object, object>>(CreateCastConverter);
  256. private static Func<object, object> CreateCastConverter(TypeConvertKey t)
  257. {
  258. MethodInfo castMethodInfo = t.TargetType.GetMethod("op_Implicit", new[] { t.InitialType })
  259. ?? t.TargetType.GetMethod("op_Explicit", new[] { t.InitialType });
  260. if (castMethodInfo == null)
  261. {
  262. return null;
  263. }
  264. MethodCall<object, object> call = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(castMethodInfo);
  265. return o => call(null, o);
  266. }
  267. #if HAVE_BIG_INTEGER
  268. internal static BigInteger ToBigInteger(object value)
  269. {
  270. if (value is BigInteger integer)
  271. {
  272. return integer;
  273. }
  274. if (value is string s)
  275. {
  276. return BigInteger.Parse(s, CultureInfo.InvariantCulture);
  277. }
  278. if (value is float f)
  279. {
  280. return new BigInteger(f);
  281. }
  282. if (value is double d)
  283. {
  284. return new BigInteger(d);
  285. }
  286. if (value is decimal @decimal)
  287. {
  288. return new BigInteger(@decimal);
  289. }
  290. if (value is int i)
  291. {
  292. return new BigInteger(i);
  293. }
  294. if (value is long l)
  295. {
  296. return new BigInteger(l);
  297. }
  298. if (value is uint u)
  299. {
  300. return new BigInteger(u);
  301. }
  302. if (value is ulong @ulong)
  303. {
  304. return new BigInteger(@ulong);
  305. }
  306. if (value is byte[] bytes)
  307. {
  308. return new BigInteger(bytes);
  309. }
  310. throw new InvalidCastException("Cannot convert {0} to BigInteger.".FormatWith(CultureInfo.InvariantCulture, value.GetType()));
  311. }
  312. public static object FromBigInteger(BigInteger i, Type targetType)
  313. {
  314. if (targetType == typeof(decimal))
  315. {
  316. return (decimal)i;
  317. }
  318. if (targetType == typeof(double))
  319. {
  320. return (double)i;
  321. }
  322. if (targetType == typeof(float))
  323. {
  324. return (float)i;
  325. }
  326. if (targetType == typeof(ulong))
  327. {
  328. return (ulong)i;
  329. }
  330. if (targetType == typeof(bool))
  331. {
  332. return i != 0;
  333. }
  334. try
  335. {
  336. return System.Convert.ChangeType((long)i, targetType, CultureInfo.InvariantCulture);
  337. }
  338. catch (Exception ex)
  339. {
  340. throw new InvalidOperationException("Can not convert from BigInteger to {0}.".FormatWith(CultureInfo.InvariantCulture, targetType), ex);
  341. }
  342. }
  343. #endif
  344. #region TryConvert
  345. internal enum ConvertResult
  346. {
  347. Success = 0,
  348. CannotConvertNull = 1,
  349. NotInstantiableType = 2,
  350. NoValidConversion = 3
  351. }
  352. public static object Convert(object initialValue, CultureInfo culture, Type targetType)
  353. {
  354. switch (TryConvertInternal(initialValue, culture, targetType, out object value))
  355. {
  356. case ConvertResult.Success:
  357. return value;
  358. case ConvertResult.CannotConvertNull:
  359. throw new Exception("Can not convert null {0} into non-nullable {1}.".FormatWith(CultureInfo.InvariantCulture, initialValue.GetType(), targetType));
  360. case ConvertResult.NotInstantiableType:
  361. throw new ArgumentException("Target type {0} is not a value type or a non-abstract class.".FormatWith(CultureInfo.InvariantCulture, targetType), nameof(targetType));
  362. case ConvertResult.NoValidConversion:
  363. throw new InvalidOperationException("Can not convert from {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, initialValue.GetType(), targetType));
  364. default:
  365. throw new InvalidOperationException("Unexpected conversion result.");
  366. }
  367. }
  368. private static bool TryConvert(object initialValue, CultureInfo culture, Type targetType, out object value)
  369. {
  370. try
  371. {
  372. if (TryConvertInternal(initialValue, culture, targetType, out value) == ConvertResult.Success)
  373. {
  374. return true;
  375. }
  376. value = null;
  377. return false;
  378. }
  379. catch
  380. {
  381. value = null;
  382. return false;
  383. }
  384. }
  385. private static ConvertResult TryConvertInternal(object initialValue, CultureInfo culture, Type targetType, out object value)
  386. {
  387. if (initialValue == null)
  388. {
  389. throw new ArgumentNullException(nameof(initialValue));
  390. }
  391. if (ReflectionUtils.IsNullableType(targetType))
  392. {
  393. targetType = Nullable.GetUnderlyingType(targetType);
  394. }
  395. Type initialType = initialValue.GetType();
  396. if (targetType == initialType)
  397. {
  398. value = initialValue;
  399. return ConvertResult.Success;
  400. }
  401. // use Convert.ChangeType if both types are IConvertible
  402. if (IsConvertible(initialValue.GetType()) && IsConvertible(targetType))
  403. {
  404. if (targetType.IsEnum())
  405. {
  406. if (initialValue is string)
  407. {
  408. value = Enum.Parse(targetType, initialValue.ToString(), true);
  409. return ConvertResult.Success;
  410. }
  411. else if (IsInteger(initialValue))
  412. {
  413. value = Enum.ToObject(targetType, initialValue);
  414. return ConvertResult.Success;
  415. }
  416. }
  417. value = System.Convert.ChangeType(initialValue, targetType, culture);
  418. return ConvertResult.Success;
  419. }
  420. #if !NET20
  421. if (initialValue is DateTime dt && targetType == typeof(DateTimeOffset))
  422. {
  423. value = new DateTimeOffset(dt);
  424. return ConvertResult.Success;
  425. }
  426. #endif
  427. if (initialValue is byte[] bytes && targetType == typeof(Guid))
  428. {
  429. value = new Guid(bytes);
  430. return ConvertResult.Success;
  431. }
  432. if (initialValue is Guid guid && targetType == typeof(byte[]))
  433. {
  434. value = guid.ToByteArray();
  435. return ConvertResult.Success;
  436. }
  437. if (initialValue is string s)
  438. {
  439. if (targetType == typeof(Guid))
  440. {
  441. value = new Guid(s);
  442. return ConvertResult.Success;
  443. }
  444. if (targetType == typeof(Uri))
  445. {
  446. value = new Uri(s, UriKind.RelativeOrAbsolute);
  447. return ConvertResult.Success;
  448. }
  449. if (targetType == typeof(TimeSpan))
  450. {
  451. value = ParseTimeSpan(s);
  452. return ConvertResult.Success;
  453. }
  454. if (targetType == typeof(byte[]))
  455. {
  456. value = System.Convert.FromBase64String(s);
  457. return ConvertResult.Success;
  458. }
  459. if (targetType == typeof(Version))
  460. {
  461. if (VersionTryParse(s, out Version result))
  462. {
  463. value = result;
  464. return ConvertResult.Success;
  465. }
  466. value = null;
  467. return ConvertResult.NoValidConversion;
  468. }
  469. if (typeof(Type).IsAssignableFrom(targetType))
  470. {
  471. value = Type.GetType(s, true);
  472. return ConvertResult.Success;
  473. }
  474. }
  475. #if HAVE_BIG_INTEGER
  476. if (targetType == typeof(BigInteger))
  477. {
  478. value = ToBigInteger(initialValue);
  479. return ConvertResult.Success;
  480. }
  481. if (initialValue is BigInteger integer)
  482. {
  483. value = FromBigInteger(integer, targetType);
  484. return ConvertResult.Success;
  485. }
  486. #endif
  487. // see if source or target types have a TypeConverter that converts between the two
  488. TypeConverter toConverter = TypeDescriptor.GetConverter(initialType);
  489. if (toConverter != null && toConverter.CanConvertTo(targetType))
  490. {
  491. value = toConverter.ConvertTo(null, culture, initialValue, targetType);
  492. return ConvertResult.Success;
  493. }
  494. TypeConverter fromConverter = TypeDescriptor.GetConverter(targetType);
  495. if (fromConverter != null && fromConverter.CanConvertFrom(initialType))
  496. {
  497. value = fromConverter.ConvertFrom(null, culture, initialValue);
  498. return ConvertResult.Success;
  499. }
  500. #if HAVE_ADO_NET
  501. // handle DBNull
  502. if (initialValue == DBNull.Value)
  503. {
  504. if (ReflectionUtils.IsNullable(targetType))
  505. {
  506. value = EnsureTypeAssignable(null, initialType, targetType);
  507. return ConvertResult.Success;
  508. }
  509. // cannot convert null to non-nullable
  510. value = null;
  511. return ConvertResult.CannotConvertNull;
  512. }
  513. #endif
  514. if (targetType.IsInterface() || targetType.IsGenericTypeDefinition() || targetType.IsAbstract())
  515. {
  516. value = null;
  517. return ConvertResult.NotInstantiableType;
  518. }
  519. value = null;
  520. return ConvertResult.NoValidConversion;
  521. }
  522. #endregion
  523. #region ConvertOrCast
  524. /// <summary>
  525. /// Converts the value to the specified type. If the value is unable to be converted, the
  526. /// value is checked whether it assignable to the specified type.
  527. /// </summary>
  528. /// <param name="initialValue">The value to convert.</param>
  529. /// <param name="culture">The culture to use when converting.</param>
  530. /// <param name="targetType">The type to convert or cast the value to.</param>
  531. /// <returns>
  532. /// The converted type. If conversion was unsuccessful, the initial value
  533. /// is returned if assignable to the target type.
  534. /// </returns>
  535. public static object ConvertOrCast(object initialValue, CultureInfo culture, Type targetType)
  536. {
  537. if (targetType == typeof(object))
  538. {
  539. return initialValue;
  540. }
  541. if (initialValue == null && ReflectionUtils.IsNullable(targetType))
  542. {
  543. return null;
  544. }
  545. if (TryConvert(initialValue, culture, targetType, out object convertedValue))
  546. {
  547. return convertedValue;
  548. }
  549. return EnsureTypeAssignable(initialValue, ReflectionUtils.GetObjectType(initialValue), targetType);
  550. }
  551. #endregion
  552. private static object EnsureTypeAssignable(object value, Type initialType, Type targetType)
  553. {
  554. Type valueType = value?.GetType();
  555. if (value != null)
  556. {
  557. if (targetType.IsAssignableFrom(valueType))
  558. {
  559. return value;
  560. }
  561. Func<object, object> castConverter = CastConverters.Get(new TypeConvertKey(valueType, targetType));
  562. if (castConverter != null)
  563. {
  564. return castConverter(value);
  565. }
  566. }
  567. else
  568. {
  569. if (ReflectionUtils.IsNullable(targetType))
  570. {
  571. return null;
  572. }
  573. }
  574. throw new ArgumentException("Could not cast or convert from {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, initialType?.ToString() ?? "{null}", targetType));
  575. }
  576. public static bool VersionTryParse(string input, out Version result)
  577. {
  578. #if !NET20
  579. return Version.TryParse(input, out result);
  580. #else
  581. // improve failure performance with regex?
  582. try
  583. {
  584. result = new Version(input);
  585. return true;
  586. }
  587. catch
  588. {
  589. result = null;
  590. return false;
  591. }
  592. #endif
  593. }
  594. public static bool IsInteger(object value)
  595. {
  596. switch (GetTypeCode(value.GetType()))
  597. {
  598. case PrimitiveTypeCode.SByte:
  599. case PrimitiveTypeCode.Byte:
  600. case PrimitiveTypeCode.Int16:
  601. case PrimitiveTypeCode.UInt16:
  602. case PrimitiveTypeCode.Int32:
  603. case PrimitiveTypeCode.UInt32:
  604. case PrimitiveTypeCode.Int64:
  605. case PrimitiveTypeCode.UInt64:
  606. return true;
  607. default:
  608. return false;
  609. }
  610. }
  611. public static ParseResult Int32TryParse(char[] chars, int start, int length, out int value)
  612. {
  613. value = 0;
  614. if (length == 0)
  615. {
  616. return ParseResult.Invalid;
  617. }
  618. bool isNegative = (chars[start] == '-');
  619. if (isNegative)
  620. {
  621. // text just a negative sign
  622. if (length == 1)
  623. {
  624. return ParseResult.Invalid;
  625. }
  626. start++;
  627. length--;
  628. }
  629. int end = start + length;
  630. // Int32.MaxValue and MinValue are 10 chars
  631. // Or is 10 chars and start is greater than two
  632. // Need to improve this!
  633. if (length > 10 || (length == 10 && chars[start] - '0' > 2))
  634. {
  635. // invalid result takes precedence over overflow
  636. for (int i = start; i < end; i++)
  637. {
  638. int c = chars[i] - '0';
  639. if (c < 0 || c > 9)
  640. {
  641. return ParseResult.Invalid;
  642. }
  643. }
  644. return ParseResult.Overflow;
  645. }
  646. for (int i = start; i < end; i++)
  647. {
  648. int c = chars[i] - '0';
  649. if (c < 0 || c > 9)
  650. {
  651. return ParseResult.Invalid;
  652. }
  653. int newValue = (10 * value) - c;
  654. // overflow has caused the number to loop around
  655. if (newValue > value)
  656. {
  657. i++;
  658. // double check the rest of the string that there wasn't anything invalid
  659. // invalid result takes precedence over overflow result
  660. for (; i < end; i++)
  661. {
  662. c = chars[i] - '0';
  663. if (c < 0 || c > 9)
  664. {
  665. return ParseResult.Invalid;
  666. }
  667. }
  668. return ParseResult.Overflow;
  669. }
  670. value = newValue;
  671. }
  672. // go from negative to positive to avoids overflow
  673. // negative can be slightly bigger than positive
  674. if (!isNegative)
  675. {
  676. // negative integer can be one bigger than positive
  677. if (value == int.MinValue)
  678. {
  679. return ParseResult.Overflow;
  680. }
  681. value = -value;
  682. }
  683. return ParseResult.Success;
  684. }
  685. public static ParseResult Int64TryParse(char[] chars, int start, int length, out long value)
  686. {
  687. value = 0;
  688. if (length == 0)
  689. {
  690. return ParseResult.Invalid;
  691. }
  692. bool isNegative = (chars[start] == '-');
  693. if (isNegative)
  694. {
  695. // text just a negative sign
  696. if (length == 1)
  697. {
  698. return ParseResult.Invalid;
  699. }
  700. start++;
  701. length--;
  702. }
  703. int end = start + length;
  704. // Int64.MaxValue and MinValue are 19 chars
  705. if (length > 19)
  706. {
  707. // invalid result takes precedence over overflow
  708. for (int i = start; i < end; i++)
  709. {
  710. int c = chars[i] - '0';
  711. if (c < 0 || c > 9)
  712. {
  713. return ParseResult.Invalid;
  714. }
  715. }
  716. return ParseResult.Overflow;
  717. }
  718. for (int i = start; i < end; i++)
  719. {
  720. int c = chars[i] - '0';
  721. if (c < 0 || c > 9)
  722. {
  723. return ParseResult.Invalid;
  724. }
  725. long newValue = (10 * value) - c;
  726. // overflow has caused the number to loop around
  727. if (newValue > value)
  728. {
  729. i++;
  730. // double check the rest of the string that there wasn't anything invalid
  731. // invalid result takes precedence over overflow result
  732. for (; i < end; i++)
  733. {
  734. c = chars[i] - '0';
  735. if (c < 0 || c > 9)
  736. {
  737. return ParseResult.Invalid;
  738. }
  739. }
  740. return ParseResult.Overflow;
  741. }
  742. value = newValue;
  743. }
  744. // go from negative to positive to avoids overflow
  745. // negative can be slightly bigger than positive
  746. if (!isNegative)
  747. {
  748. // negative integer can be one bigger than positive
  749. if (value == long.MinValue)
  750. {
  751. return ParseResult.Overflow;
  752. }
  753. value = -value;
  754. }
  755. return ParseResult.Success;
  756. }
  757. #if HAS_CUSTOM_DOUBLE_PARSE
  758. private static class IEEE754
  759. {
  760. /// <summary>
  761. /// Exponents for both powers of 10 and 0.1
  762. /// </summary>
  763. private static readonly int[] MultExp64Power10 = new int[]
  764. {
  765. 4, 7, 10, 14, 17, 20, 24, 27, 30, 34, 37, 40, 44, 47, 50
  766. };
  767. /// <summary>
  768. /// Normalized powers of 10
  769. /// </summary>
  770. private static readonly ulong[] MultVal64Power10 = new ulong[]
  771. {
  772. 0xa000000000000000, 0xc800000000000000, 0xfa00000000000000,
  773. 0x9c40000000000000, 0xc350000000000000, 0xf424000000000000,
  774. 0x9896800000000000, 0xbebc200000000000, 0xee6b280000000000,
  775. 0x9502f90000000000, 0xba43b74000000000, 0xe8d4a51000000000,
  776. 0x9184e72a00000000, 0xb5e620f480000000, 0xe35fa931a0000000,
  777. };
  778. /// <summary>
  779. /// Normalized powers of 0.1
  780. /// </summary>
  781. private static readonly ulong[] MultVal64Power10Inv = new ulong[]
  782. {
  783. 0xcccccccccccccccd, 0xa3d70a3d70a3d70b, 0x83126e978d4fdf3c,
  784. 0xd1b71758e219652e, 0xa7c5ac471b478425, 0x8637bd05af6c69b7,
  785. 0xd6bf94d5e57a42be, 0xabcc77118461ceff, 0x89705f4136b4a599,
  786. 0xdbe6fecebdedd5c2, 0xafebff0bcb24ab02, 0x8cbccc096f5088cf,
  787. 0xe12e13424bb40e18, 0xb424dc35095cd813, 0x901d7cf73ab0acdc,
  788. };
  789. /// <summary>
  790. /// Exponents for both powers of 10^16 and 0.1^16
  791. /// </summary>
  792. private static readonly int[] MultExp64Power10By16 = new int[]
  793. {
  794. 54, 107, 160, 213, 266, 319, 373, 426, 479, 532, 585, 638,
  795. 691, 745, 798, 851, 904, 957, 1010, 1064, 1117,
  796. };
  797. /// <summary>
  798. /// Normalized powers of 10^16
  799. /// </summary>
  800. private static readonly ulong[] MultVal64Power10By16 = new ulong[]
  801. {
  802. 0x8e1bc9bf04000000, 0x9dc5ada82b70b59e, 0xaf298d050e4395d6,
  803. 0xc2781f49ffcfa6d4, 0xd7e77a8f87daf7fa, 0xefb3ab16c59b14a0,
  804. 0x850fadc09923329c, 0x93ba47c980e98cde, 0xa402b9c5a8d3a6e6,
  805. 0xb616a12b7fe617a8, 0xca28a291859bbf90, 0xe070f78d39275566,
  806. 0xf92e0c3537826140, 0x8a5296ffe33cc92c, 0x9991a6f3d6bf1762,
  807. 0xaa7eebfb9df9de8a, 0xbd49d14aa79dbc7e, 0xd226fc195c6a2f88,
  808. 0xe950df20247c83f8, 0x81842f29f2cce373, 0x8fcac257558ee4e2,
  809. };
  810. /// <summary>
  811. /// Normalized powers of 0.1^16
  812. /// </summary>
  813. private static readonly ulong[] MultVal64Power10By16Inv = new ulong[]
  814. {
  815. 0xe69594bec44de160, 0xcfb11ead453994c3, 0xbb127c53b17ec165,
  816. 0xa87fea27a539e9b3, 0x97c560ba6b0919b5, 0x88b402f7fd7553ab,
  817. 0xf64335bcf065d3a0, 0xddd0467c64bce4c4, 0xc7caba6e7c5382ed,
  818. 0xb3f4e093db73a0b7, 0xa21727db38cb0053, 0x91ff83775423cc29,
  819. 0x8380dea93da4bc82, 0xece53cec4a314f00, 0xd5605fcdcf32e217,
  820. 0xc0314325637a1978, 0xad1c8eab5ee43ba2, 0x9becce62836ac5b0,
  821. 0x8c71dcd9ba0b495c, 0xfd00b89747823938, 0xe3e27a444d8d991a,
  822. };
  823. /// <summary>
  824. /// Packs <paramref name="val"/>*10^<paramref name="scale"/> as 64-bit floating point value according to IEEE 754 standard
  825. /// </summary>
  826. /// <param name="negative">Sign</param>
  827. /// <param name="val">Mantissa</param>
  828. /// <param name="scale">Exponent</param>
  829. /// <remarks>
  830. /// Adoption of native function NumberToDouble() from coreclr sources,
  831. /// see https://github.com/dotnet/coreclr/blob/master/src/classlibnative/bcltype/number.cpp#L451
  832. /// </remarks>
  833. public static double PackDouble(bool negative, ulong val, int scale)
  834. {
  835. // handle zero value
  836. if (val == 0)
  837. {
  838. return negative ? -0.0 : 0.0;
  839. }
  840. // normalize the mantissa
  841. int exp = 64;
  842. if ((val & 0xFFFFFFFF00000000) == 0)
  843. {
  844. val <<= 32;
  845. exp -= 32;
  846. }
  847. if ((val & 0xFFFF000000000000) == 0)
  848. {
  849. val <<= 16;
  850. exp -= 16;
  851. }
  852. if ((val & 0xFF00000000000000) == 0)
  853. {
  854. val <<= 8;
  855. exp -= 8;
  856. }
  857. if ((val & 0xF000000000000000) == 0)
  858. {
  859. val <<= 4;
  860. exp -= 4;
  861. }
  862. if ((val & 0xC000000000000000) == 0)
  863. {
  864. val <<= 2;
  865. exp -= 2;
  866. }
  867. if ((val & 0x8000000000000000) == 0)
  868. {
  869. val <<= 1;
  870. exp -= 1;
  871. }
  872. if (scale < 0)
  873. {
  874. scale = -scale;
  875. // check scale bounds
  876. if (scale >= 22 * 16)
  877. {
  878. // underflow
  879. return negative ? -0.0 : 0.0;
  880. }
  881. // perform scaling
  882. int index = scale & 15;
  883. if (index != 0)
  884. {
  885. exp -= MultExp64Power10[index - 1] - 1;
  886. val = Mul64Lossy(val, MultVal64Power10Inv[index - 1], ref exp);
  887. }
  888. index = scale >> 4;
  889. if (index != 0)
  890. {
  891. exp -= MultExp64Power10By16[index - 1] - 1;
  892. val = Mul64Lossy(val, MultVal64Power10By16Inv[index - 1], ref exp);
  893. }
  894. }
  895. else
  896. {
  897. // check scale bounds
  898. if (scale >= 22 * 16)
  899. {
  900. // overflow
  901. return negative ? double.NegativeInfinity : double.PositiveInfinity;
  902. }
  903. // perform scaling
  904. int index = scale & 15;
  905. if (index != 0)
  906. {
  907. exp += MultExp64Power10[index - 1];
  908. val = Mul64Lossy(val, MultVal64Power10[index - 1], ref exp);
  909. }
  910. index = scale >> 4;
  911. if (index != 0)
  912. {
  913. exp += MultExp64Power10By16[index - 1];
  914. val = Mul64Lossy(val, MultVal64Power10By16[index - 1], ref exp);
  915. }
  916. }
  917. // round & scale down
  918. if ((val & (1 << 10)) != 0)
  919. {
  920. // IEEE round to even
  921. ulong tmp = val + ((1UL << 10) - 1 + ((val >> 11) & 1));
  922. if (tmp < val)
  923. {
  924. // overflow
  925. tmp = (tmp >> 1) | 0x8000000000000000;
  926. exp++;
  927. }
  928. val = tmp;
  929. }
  930. // return the exponent to a biased state
  931. exp += 0x3FE;
  932. // handle overflow, underflow, "Epsilon - 1/2 Epsilon", denormalized, and the normal case
  933. if (exp <= 0)
  934. {
  935. if (exp == -52 && (val >= 0x8000000000000058))
  936. {
  937. // round X where {Epsilon > X >= 2.470328229206232730000000E-324} up to Epsilon (instead of down to zero)
  938. val = 0x0000000000000001;
  939. }
  940. else if (exp <= -52)
  941. {
  942. // underflow
  943. val = 0;
  944. }
  945. else
  946. {
  947. // denormalized value
  948. val >>= (-exp + 12);
  949. }
  950. }
  951. else if (exp >= 0x7FF)
  952. {
  953. // overflow
  954. val = 0x7FF0000000000000;
  955. }
  956. else
  957. {
  958. // normal positive exponent case
  959. val = ((ulong)exp << 52) | ((val >> 11) & 0x000FFFFFFFFFFFFF);
  960. }
  961. // apply sign
  962. if (negative)
  963. {
  964. val |= 0x8000000000000000;
  965. }
  966. return BitConverter.Int64BitsToDouble((long)val);
  967. }
  968. private static ulong Mul64Lossy(ulong a, ulong b, ref int exp)
  969. {
  970. ulong a_hi = (a >> 32);
  971. uint a_lo = (uint)a;
  972. ulong b_hi = (b >> 32);
  973. uint b_lo = (uint)b;
  974. ulong result = a_hi * b_hi;
  975. // save some multiplications if lo-parts aren't big enough to produce carry
  976. // (hi-parts will be always big enough, since a and b are normalized)
  977. if ((b_lo & 0xFFFF0000) != 0)
  978. {
  979. result += (a_hi * b_lo) >> 32;
  980. }
  981. if ((a_lo & 0xFFFF0000) != 0)
  982. {
  983. result += (a_lo * b_hi) >> 32;
  984. }
  985. // normalize
  986. if ((result & 0x8000000000000000) == 0)
  987. {
  988. result <<= 1;
  989. exp--;
  990. }
  991. return result;
  992. }
  993. }
  994. public static ParseResult DoubleTryParse(char[] chars, int start, int length, out double value)
  995. {
  996. value = 0;
  997. if (length == 0)
  998. {
  999. return ParseResult.Invalid;
  1000. }
  1001. bool isNegative = (chars[start] == '-');
  1002. if (isNegative)
  1003. {
  1004. // text just a negative sign
  1005. if (length == 1)
  1006. {
  1007. return ParseResult.Invalid;
  1008. }
  1009. start++;
  1010. length--;
  1011. }
  1012. int i = start;
  1013. int end = start + length;
  1014. int numDecimalStart = end;
  1015. int numDecimalEnd = end;
  1016. int exponent = 0;
  1017. ulong mantissa = 0UL;
  1018. int mantissaDigits = 0;
  1019. int exponentFromMantissa = 0;
  1020. for (; i < end; i++)
  1021. {
  1022. char c = chars[i];
  1023. switch (c)
  1024. {
  1025. case '.':
  1026. if (i == start)
  1027. {
  1028. return ParseResult.Invalid;
  1029. }
  1030. if (i + 1 == end)
  1031. {
  1032. return ParseResult.Invalid;
  1033. }
  1034. if (numDecimalStart != end)
  1035. {
  1036. // multiple decimal points
  1037. return ParseResult.Invalid;
  1038. }
  1039. numDecimalStart = i + 1;
  1040. break;
  1041. case 'e':
  1042. case 'E':
  1043. if (i == start)
  1044. {
  1045. return ParseResult.Invalid;
  1046. }
  1047. if (i == numDecimalStart)
  1048. {
  1049. // E follows decimal point
  1050. return ParseResult.Invalid;
  1051. }
  1052. i++;
  1053. if (i == end)
  1054. {
  1055. return ParseResult.Invalid;
  1056. }
  1057. if (numDecimalStart < end)
  1058. {
  1059. numDecimalEnd = i - 1;
  1060. }
  1061. c = chars[i];
  1062. bool exponentNegative = false;
  1063. switch (c)
  1064. {
  1065. case '-':
  1066. exponentNegative = true;
  1067. i++;
  1068. break;
  1069. case '+':
  1070. i++;
  1071. break;
  1072. }
  1073. // parse 3 digit
  1074. for (; i < end; i++)
  1075. {
  1076. c = chars[i];
  1077. if (c < '0' || c > '9')
  1078. {
  1079. return ParseResult.Invalid;
  1080. }
  1081. int newExponent = (10 * exponent) + (c - '0');
  1082. // stops updating exponent when overflowing
  1083. if (exponent < newExponent)
  1084. {
  1085. exponent = newExponent;
  1086. }
  1087. }
  1088. if (exponentNegative)
  1089. {
  1090. exponent = -exponent;
  1091. }
  1092. break;
  1093. default:
  1094. if (c < '0' || c > '9')
  1095. {
  1096. return ParseResult.Invalid;
  1097. }
  1098. if (i == start && c == '0')
  1099. {
  1100. i++;
  1101. if (i != end)
  1102. {
  1103. c = chars[i];
  1104. if (c == '.')
  1105. {
  1106. goto case '.';
  1107. }
  1108. if (c == 'e' || c == 'E')
  1109. {
  1110. goto case 'E';
  1111. }
  1112. return ParseResult.Invalid;
  1113. }
  1114. }
  1115. if (mantissaDigits < 19)
  1116. {
  1117. mantissa = (10 * mantissa) + (ulong)(c - '0');
  1118. if (mantissa > 0)
  1119. {
  1120. ++mantissaDigits;
  1121. }
  1122. }
  1123. else
  1124. {
  1125. ++exponentFromMantissa;
  1126. }
  1127. break;
  1128. }
  1129. }
  1130. exponent += exponentFromMantissa;
  1131. // correct the decimal point
  1132. exponent -= (numDecimalEnd - numDecimalStart);
  1133. value = IEEE754.PackDouble(isNegative, mantissa, exponent);
  1134. return double.IsInfinity(value) ? ParseResult.Overflow : ParseResult.Success;
  1135. }
  1136. #endif
  1137. public static ParseResult DecimalTryParse(char[] chars, int start, int length, out decimal value)
  1138. {
  1139. value = 0M;
  1140. const decimal decimalMaxValueHi28 = 7922816251426433759354395033M;
  1141. const ulong decimalMaxValueHi19 = 7922816251426433759UL;
  1142. const ulong decimalMaxValueLo9 = 354395033UL;
  1143. const char decimalMaxValueLo1 = '5';
  1144. if (length == 0)
  1145. {
  1146. return ParseResult.Invalid;
  1147. }
  1148. bool isNegative = (chars[start] == '-');
  1149. if (isNegative)
  1150. {
  1151. // text just a negative sign
  1152. if (length == 1)
  1153. {
  1154. return ParseResult.Invalid;
  1155. }
  1156. start++;
  1157. length--;
  1158. }
  1159. int i = start;
  1160. int end = start + length;
  1161. int numDecimalStart = end;
  1162. int numDecimalEnd = end;
  1163. int exponent = 0;
  1164. ulong hi19 = 0UL;
  1165. ulong lo10 = 0UL;
  1166. int mantissaDigits = 0;
  1167. int exponentFromMantissa = 0;
  1168. bool? roundUp = null;
  1169. bool? storeOnly28Digits = null;
  1170. for (; i < end; i++)
  1171. {
  1172. char c = chars[i];
  1173. switch (c)
  1174. {
  1175. case '.':
  1176. if (i == start)
  1177. {
  1178. return ParseResult.Invalid;
  1179. }
  1180. if (i + 1 == end)
  1181. {
  1182. return ParseResult.Invalid;
  1183. }
  1184. if (numDecimalStart != end)
  1185. {
  1186. // multiple decimal points
  1187. return ParseResult.Invalid;
  1188. }
  1189. numDecimalStart = i + 1;
  1190. break;
  1191. case 'e':
  1192. case 'E':
  1193. if (i == start)
  1194. {
  1195. return ParseResult.Invalid;
  1196. }
  1197. if (i == numDecimalStart)
  1198. {
  1199. // E follows decimal point
  1200. return ParseResult.Invalid;
  1201. }
  1202. i++;
  1203. if (i == end)
  1204. {
  1205. return ParseResult.Invalid;
  1206. }
  1207. if (numDecimalStart < end)
  1208. {
  1209. numDecimalEnd = i - 1;
  1210. }
  1211. c = chars[i];
  1212. bool exponentNegative = false;
  1213. switch (c)
  1214. {
  1215. case '-':
  1216. exponentNegative = true;
  1217. i++;
  1218. break;
  1219. case '+':
  1220. i++;
  1221. break;
  1222. }
  1223. // parse 3 digit
  1224. for (; i < end; i++)
  1225. {
  1226. c = chars[i];
  1227. if (c < '0' || c > '9')
  1228. {
  1229. return ParseResult.Invalid;
  1230. }
  1231. int newExponent = (10 * exponent) + (c - '0');
  1232. // stops updating exponent when overflowing
  1233. if (exponent < newExponent)
  1234. {
  1235. exponent = newExponent;
  1236. }
  1237. }
  1238. if (exponentNegative)
  1239. {
  1240. exponent = -exponent;
  1241. }
  1242. break;
  1243. default:
  1244. if (c < '0' || c > '9')
  1245. {
  1246. return ParseResult.Invalid;
  1247. }
  1248. if (i == start && c == '0')
  1249. {
  1250. i++;
  1251. if (i != end)
  1252. {
  1253. c = chars[i];
  1254. if (c == '.')
  1255. {
  1256. goto case '.';
  1257. }
  1258. if (c == 'e' || c == 'E')
  1259. {
  1260. goto case 'E';
  1261. }
  1262. return ParseResult.Invalid;
  1263. }
  1264. }
  1265. if (mantissaDigits < 29 && (mantissaDigits != 28 || !(storeOnly28Digits ?? (storeOnly28Digits = (hi19 > decimalMaxValueHi19 || (hi19 == decimalMaxValueHi19 && (lo10 > decimalMaxValueLo9 || (lo10 == decimalMaxValueLo9 && c > decimalMaxValueLo1))))).GetValueOrDefault())))
  1266. {
  1267. if (mantissaDigits < 19)
  1268. {
  1269. hi19 = (hi19 * 10UL) + (ulong)(c - '0');
  1270. }
  1271. else
  1272. {
  1273. lo10 = (lo10 * 10UL) + (ulong)(c - '0');
  1274. }
  1275. ++mantissaDigits;
  1276. }
  1277. else
  1278. {
  1279. if (!roundUp.HasValue)
  1280. {
  1281. roundUp = c >= '5';
  1282. }
  1283. ++exponentFromMantissa;
  1284. }
  1285. break;
  1286. }
  1287. }
  1288. exponent += exponentFromMantissa;
  1289. // correct the decimal point
  1290. exponent -= (numDecimalEnd - numDecimalStart);
  1291. if (mantissaDigits <= 19)
  1292. {
  1293. value = hi19;
  1294. }
  1295. else
  1296. {
  1297. value = (hi19 / new decimal(1, 0, 0, false, (byte)(mantissaDigits - 19))) + lo10;
  1298. }
  1299. if (exponent > 0)
  1300. {
  1301. mantissaDigits += exponent;
  1302. if (mantissaDigits > 29)
  1303. {
  1304. return ParseResult.Overflow;
  1305. }
  1306. if (mantissaDigits == 29)
  1307. {
  1308. if (exponent > 1)
  1309. {
  1310. value /= new decimal(1, 0, 0, false, (byte)(exponent - 1));
  1311. if (value > decimalMaxValueHi28)
  1312. {
  1313. return ParseResult.Overflow;
  1314. }
  1315. }
  1316. value *= 10M;
  1317. }
  1318. else
  1319. {
  1320. value /= new decimal(1, 0, 0, false, (byte)exponent);
  1321. }
  1322. }
  1323. else
  1324. {
  1325. if (roundUp == true && exponent >= -28)
  1326. {
  1327. ++value;
  1328. }
  1329. if (exponent < 0)
  1330. {
  1331. if (mantissaDigits + exponent + 28 <= 0)
  1332. {
  1333. value = isNegative ? -0M : 0M;
  1334. return ParseResult.Success;
  1335. }
  1336. if (exponent >= -28)
  1337. {
  1338. value *= new decimal(1, 0, 0, false, (byte)(-exponent));
  1339. }
  1340. else
  1341. {
  1342. value /= 1e28M;
  1343. value *= new decimal(1, 0, 0, false, (byte)(-exponent - 28));
  1344. }
  1345. }
  1346. }
  1347. if (isNegative)
  1348. {
  1349. value = -value;
  1350. }
  1351. return ParseResult.Success;
  1352. }
  1353. public static bool TryConvertGuid(string s, out Guid g)
  1354. {
  1355. // GUID has to have format 00000000-0000-0000-0000-000000000000
  1356. #if NET20
  1357. if (s == null)
  1358. {
  1359. throw new ArgumentNullException("s");
  1360. }
  1361. Regex format = new Regex("^[A-Fa-f0-9]{8}-([A-Fa-f0-9]{4}-){3}[A-Fa-f0-9]{12}$");
  1362. Match match = format.Match(s);
  1363. if (match.Success)
  1364. {
  1365. g = new Guid(s);
  1366. return true;
  1367. }
  1368. g = Guid.Empty;
  1369. return false;
  1370. #else
  1371. return Guid.TryParseExact(s, "D", out g);
  1372. #endif
  1373. }
  1374. public static bool TryHexTextToInt(char[] text, int start, int end, out int value)
  1375. {
  1376. value = 0;
  1377. for (int i = start; i < end; i++)
  1378. {
  1379. char ch = text[i];
  1380. int chValue;
  1381. if (ch <= 57 && ch >= 48)
  1382. {
  1383. chValue = ch - 48;
  1384. }
  1385. else if (ch <= 70 && ch >= 65)
  1386. {
  1387. chValue = ch - 55;
  1388. }
  1389. else if (ch <= 102 && ch >= 97)
  1390. {
  1391. chValue = ch - 87;
  1392. }
  1393. else
  1394. {
  1395. value = 0;
  1396. return false;
  1397. }
  1398. value += chValue << ((end - 1 - i) * 4);
  1399. }
  1400. return true;
  1401. }
  1402. }
  1403. }