using System; using System.Runtime.CompilerServices; namespace Unity.Collections { /// /// Provides methods for parsing numbers from FixedString*N*Bytes. /// [GenerateTestsForBurstCompatibility] public unsafe static partial class FixedStringMethods { [MethodImpl(MethodImplOptions.AggressiveInlining)] [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] internal static bool ParseLongInternal(ref T fs, ref int offset, out long value) where T : unmanaged, INativeList, IUTF8Bytes { int resetOffset = offset; int sign = 1; if (offset < fs.Length) { if (fs.Peek(offset).value == '+') fs.Read(ref offset); else if (fs.Peek(offset).value == '-') { sign = -1; fs.Read(ref offset); } } int digitOffset = offset; value = 0; while (offset < fs.Length && Unicode.Rune.IsDigit(fs.Peek(offset))) { value *= 10; value += fs.Read(ref offset).value - '0'; } value = sign * value; // If there was no number parsed, revert the offset since it's a syntax error and we might // have erroneously parsed a '-' or '+' if (offset == digitOffset) { offset = resetOffset; return false; } return true; } /// /// Parses an int from this string starting at a byte offset. /// /// /// 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.) /// /// The parsed value is bitwise-identical to the result of System.Int32.Parse. /// /// A FixedString*N*Bytes type. /// The string from which to parse. /// A reference to an index of the byte at which to parse an int. /// Outputs the parsed int. Ignore if parsing fails. /// ParseError.None if successful. Otherwise returns ParseError.Overflow or ParseError.Syntax. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static ParseError Parse(ref this T fs, ref int offset, ref int output) where T : unmanaged, INativeList, IUTF8Bytes { if (!ParseLongInternal(ref fs, ref offset, out long value)) return ParseError.Syntax; if (value > int.MaxValue) return ParseError.Overflow; if (value < int.MinValue) return ParseError.Overflow; output = (int)value; return ParseError.None; } /// /// Parses an uint from this string starting at a byte offset. /// /// /// 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.) /// /// The parsed value is bitwise-identical to the result of System.UInt32.Parse. /// /// A FixedString*N*Bytes type. /// The string from which to parse. /// A reference to an index of the byte at which to parse a uint. /// Outputs the parsed uint. Ignore if parsing fails. /// ParseError.None if successful. Otherwise returns ParseError.Overflow or ParseError.Syntax. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static ParseError Parse(ref this T fs, ref int offset, ref uint output) where T : unmanaged, INativeList, IUTF8Bytes { if (!ParseLongInternal(ref fs, ref offset, out long value)) return ParseError.Syntax; if (value > uint.MaxValue) return ParseError.Overflow; if (value < uint.MinValue) return ParseError.Overflow; output = (uint)value; return ParseError.None; } /// /// Parses a float from this string starting at a byte offset. /// /// 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.) /// /// The parsed value is bitwise-identical to the result of System.Single.Parse. /// A FixedString*N*Bytes type. /// The string from which to parse. /// Index of the byte at which to parse a float. /// Outputs the parsed float. Ignore if parsing fails. /// The character used to separate the integer part of the number from the fractional part. Defaults to '.' (period). /// ParseError.None if successful. Otherwise returns ParseError.Overflow, ParseError.Underflow, or ParseError.Syntax. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static ParseError Parse(ref this T fs, ref int offset, ref float output, char decimalSeparator = '.') where T : unmanaged, INativeList, IUTF8Bytes { int resetOffset = offset; int sign = 1; if (offset < fs.Length) { if (fs.Peek(offset).value == '+') fs.Read(ref offset); else if (fs.Peek(offset).value == '-') { sign = -1; fs.Read(ref offset); } } if (fs.Found(ref offset, 'n', 'a', 'n')) { FixedStringUtils.UintFloatUnion ufu = new FixedStringUtils.UintFloatUnion(); ufu.uintValue = 4290772992U; output = ufu.floatValue; return ParseError.None; } if (fs.Found(ref offset, 'i', 'n', 'f', 'i', 'n', 'i', 't', 'y')) { output = (sign == 1) ? Single.PositiveInfinity : Single.NegativeInfinity; return ParseError.None; } ulong decimalMantissa = 0; int significantDigits = 0; int digitsAfterDot = 0; int mantissaDigits = 0; while (offset < fs.Length && Unicode.Rune.IsDigit(fs.Peek(offset))) { ++mantissaDigits; if (significantDigits < 9) { var temp = decimalMantissa * 10 + (ulong)(fs.Peek(offset).value - '0'); if (temp > decimalMantissa) ++significantDigits; decimalMantissa = temp; } else --digitsAfterDot; fs.Read(ref offset); } if (offset < fs.Length && fs.Peek(offset).value == decimalSeparator) { fs.Read(ref offset); while (offset < fs.Length && Unicode.Rune.IsDigit(fs.Peek(offset))) { ++mantissaDigits; if (significantDigits < 9) { var temp = decimalMantissa * 10 + (ulong)(fs.Peek(offset).value - '0'); if (temp > decimalMantissa) ++significantDigits; decimalMantissa = temp; ++digitsAfterDot; } fs.Read(ref offset); } } if (mantissaDigits == 0) { // Reset offset in case '+' or '-' was erroneously parsed offset = resetOffset; return ParseError.Syntax; } int decimalExponent = 0; int decimalExponentSign = 1; if (offset < fs.Length && (fs.Peek(offset).value | 32) == 'e') { fs.Read(ref offset); if (offset < fs.Length) { if (fs.Peek(offset).value == '+') fs.Read(ref offset); else if (fs.Peek(offset).value == '-') { decimalExponentSign = -1; fs.Read(ref offset); } } int digitOffset = offset; while (offset < fs.Length && Unicode.Rune.IsDigit(fs.Peek(offset))) { decimalExponent = decimalExponent * 10 + (fs.Peek(offset).value - '0'); fs.Read(ref offset); } if (offset == digitOffset) { // Reset offset in case '+' or '-' was erroneously parsed offset = resetOffset; return ParseError.Syntax; } if (decimalExponent > 38) { if (decimalExponentSign == 1) return ParseError.Overflow; else return ParseError.Underflow; } } decimalExponent = decimalExponent * decimalExponentSign - digitsAfterDot; var error = FixedStringUtils.Base10ToBase2(ref output, decimalMantissa, decimalExponent); if (error != ParseError.None) return error; output *= sign; return ParseError.None; } } }