A game about forced loneliness, made by TACStudios
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}