暫無描述
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.

FixedStringParseMethods.cs 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. using System;
  2. using System.Runtime.CompilerServices;
  3. namespace Unity.Collections
  4. {
  5. /// <summary>
  6. /// Provides methods for parsing numbers from FixedString*N*Bytes.
  7. /// </summary>
  8. [GenerateTestsForBurstCompatibility]
  9. public unsafe static partial class FixedStringMethods
  10. {
  11. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  12. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
  13. internal static bool ParseLongInternal<T>(ref T fs, ref int offset, out long value)
  14. where T : unmanaged, INativeList<byte>, IUTF8Bytes
  15. {
  16. int resetOffset = offset;
  17. int sign = 1;
  18. if (offset < fs.Length)
  19. {
  20. if (fs.Peek(offset).value == '+')
  21. fs.Read(ref offset);
  22. else if (fs.Peek(offset).value == '-')
  23. {
  24. sign = -1;
  25. fs.Read(ref offset);
  26. }
  27. }
  28. int digitOffset = offset;
  29. value = 0;
  30. while (offset < fs.Length && Unicode.Rune.IsDigit(fs.Peek(offset)))
  31. {
  32. value *= 10;
  33. value += fs.Read(ref offset).value - '0';
  34. }
  35. value = sign * value;
  36. // If there was no number parsed, revert the offset since it's a syntax error and we might
  37. // have erroneously parsed a '-' or '+'
  38. if (offset == digitOffset)
  39. {
  40. offset = resetOffset;
  41. return false;
  42. }
  43. return true;
  44. }
  45. /// <summary>
  46. /// Parses an int from this string starting at a byte offset.
  47. /// </summary>
  48. /// <remarks>
  49. /// Stops parsing after the last number character. (Unlike parsing methods in other API's, this method does not expect to necessarily parse the entire string.)
  50. ///
  51. /// The parsed value is bitwise-identical to the result of System.Int32.Parse.
  52. /// </remarks>
  53. /// <typeparam name="T">A FixedString*N*Bytes type.</typeparam>
  54. /// <param name="fs">The string from which to parse.</param>
  55. /// <param name="offset">A reference to an index of the byte at which to parse an int.</param>
  56. /// <param name="output">Outputs the parsed int. Ignore if parsing fails.</param>
  57. /// <returns>ParseError.None if successful. Otherwise returns ParseError.Overflow or ParseError.Syntax.</returns>
  58. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
  59. public static ParseError Parse<T>(ref this T fs, ref int offset, ref int output)
  60. where T : unmanaged, INativeList<byte>, IUTF8Bytes
  61. {
  62. if (!ParseLongInternal(ref fs, ref offset, out long value))
  63. return ParseError.Syntax;
  64. if (value > int.MaxValue)
  65. return ParseError.Overflow;
  66. if (value < int.MinValue)
  67. return ParseError.Overflow;
  68. output = (int)value;
  69. return ParseError.None;
  70. }
  71. /// <summary>
  72. /// Parses an uint from this string starting at a byte offset.
  73. /// </summary>
  74. /// <remarks>
  75. /// Stops parsing after the last number character. (Unlike parsing methods in other API's, this method does not expect to necessarily parse the entire string.)
  76. ///
  77. /// The parsed value is bitwise-identical to the result of System.UInt32.Parse.
  78. /// </remarks>
  79. /// <typeparam name="T">A FixedString*N*Bytes type.</typeparam>
  80. /// <param name="fs">The string from which to parse.</param>
  81. /// <param name="offset">A reference to an index of the byte at which to parse a uint.</param>
  82. /// <param name="output">Outputs the parsed uint. Ignore if parsing fails.</param>
  83. /// <returns>ParseError.None if successful. Otherwise returns ParseError.Overflow or ParseError.Syntax.</returns>
  84. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
  85. public static ParseError Parse<T>(ref this T fs, ref int offset, ref uint output)
  86. where T : unmanaged, INativeList<byte>, IUTF8Bytes
  87. {
  88. if (!ParseLongInternal(ref fs, ref offset, out long value))
  89. return ParseError.Syntax;
  90. if (value > uint.MaxValue)
  91. return ParseError.Overflow;
  92. if (value < uint.MinValue)
  93. return ParseError.Overflow;
  94. output = (uint)value;
  95. return ParseError.None;
  96. }
  97. /// <summary>
  98. /// Parses a float from this string starting at a byte offset.
  99. /// </summary>
  100. /// Stops parsing after the last number character. (Unlike parsing methods in other API's, this method does not expect to necessarily parse the entire string.)
  101. ///
  102. /// <remarks>The parsed value is bitwise-identical to the result of System.Single.Parse.</remarks>
  103. /// <typeparam name="T">A FixedString*N*Bytes type.</typeparam>
  104. /// <param name="fs">The string from which to parse.</param>
  105. /// <param name="offset">Index of the byte at which to parse a float.</param>
  106. /// <param name="output">Outputs the parsed float. Ignore if parsing fails.</param>
  107. /// <param name="decimalSeparator">The character used to separate the integer part of the number from the fractional part. Defaults to '.' (period).</param>
  108. /// <returns>ParseError.None if successful. Otherwise returns ParseError.Overflow, ParseError.Underflow, or ParseError.Syntax.</returns>
  109. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
  110. public static ParseError Parse<T>(ref this T fs, ref int offset, ref float output, char decimalSeparator = '.')
  111. where T : unmanaged, INativeList<byte>, IUTF8Bytes
  112. {
  113. int resetOffset = offset;
  114. int sign = 1;
  115. if (offset < fs.Length)
  116. {
  117. if (fs.Peek(offset).value == '+')
  118. fs.Read(ref offset);
  119. else if (fs.Peek(offset).value == '-')
  120. {
  121. sign = -1;
  122. fs.Read(ref offset);
  123. }
  124. }
  125. if (fs.Found(ref offset, 'n', 'a', 'n'))
  126. {
  127. FixedStringUtils.UintFloatUnion ufu = new FixedStringUtils.UintFloatUnion();
  128. ufu.uintValue = 4290772992U;
  129. output = ufu.floatValue;
  130. return ParseError.None;
  131. }
  132. if (fs.Found(ref offset, 'i', 'n', 'f', 'i', 'n', 'i', 't', 'y'))
  133. {
  134. output = (sign == 1) ? Single.PositiveInfinity : Single.NegativeInfinity;
  135. return ParseError.None;
  136. }
  137. ulong decimalMantissa = 0;
  138. int significantDigits = 0;
  139. int digitsAfterDot = 0;
  140. int mantissaDigits = 0;
  141. while (offset < fs.Length && Unicode.Rune.IsDigit(fs.Peek(offset)))
  142. {
  143. ++mantissaDigits;
  144. if (significantDigits < 9)
  145. {
  146. var temp = decimalMantissa * 10 + (ulong)(fs.Peek(offset).value - '0');
  147. if (temp > decimalMantissa)
  148. ++significantDigits;
  149. decimalMantissa = temp;
  150. }
  151. else
  152. --digitsAfterDot;
  153. fs.Read(ref offset);
  154. }
  155. if (offset < fs.Length && fs.Peek(offset).value == decimalSeparator)
  156. {
  157. fs.Read(ref offset);
  158. while (offset < fs.Length && Unicode.Rune.IsDigit(fs.Peek(offset)))
  159. {
  160. ++mantissaDigits;
  161. if (significantDigits < 9)
  162. {
  163. var temp = decimalMantissa * 10 + (ulong)(fs.Peek(offset).value - '0');
  164. if (temp > decimalMantissa)
  165. ++significantDigits;
  166. decimalMantissa = temp;
  167. ++digitsAfterDot;
  168. }
  169. fs.Read(ref offset);
  170. }
  171. }
  172. if (mantissaDigits == 0)
  173. {
  174. // Reset offset in case '+' or '-' was erroneously parsed
  175. offset = resetOffset;
  176. return ParseError.Syntax;
  177. }
  178. int decimalExponent = 0;
  179. int decimalExponentSign = 1;
  180. if (offset < fs.Length && (fs.Peek(offset).value | 32) == 'e')
  181. {
  182. fs.Read(ref offset);
  183. if (offset < fs.Length)
  184. {
  185. if (fs.Peek(offset).value == '+')
  186. fs.Read(ref offset);
  187. else if (fs.Peek(offset).value == '-')
  188. {
  189. decimalExponentSign = -1;
  190. fs.Read(ref offset);
  191. }
  192. }
  193. int digitOffset = offset;
  194. while (offset < fs.Length && Unicode.Rune.IsDigit(fs.Peek(offset)))
  195. {
  196. decimalExponent = decimalExponent * 10 + (fs.Peek(offset).value - '0');
  197. fs.Read(ref offset);
  198. }
  199. if (offset == digitOffset)
  200. {
  201. // Reset offset in case '+' or '-' was erroneously parsed
  202. offset = resetOffset;
  203. return ParseError.Syntax;
  204. }
  205. if (decimalExponent > 38)
  206. {
  207. if (decimalExponentSign == 1)
  208. return ParseError.Overflow;
  209. else
  210. return ParseError.Underflow;
  211. }
  212. }
  213. decimalExponent = decimalExponent * decimalExponentSign - digitsAfterDot;
  214. var error = FixedStringUtils.Base10ToBase2(ref output, decimalMantissa, decimalExponent);
  215. if (error != ParseError.None)
  216. return error;
  217. output *= sign;
  218. return ParseError.None;
  219. }
  220. }
  221. }