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