Skip to content

Commit ac65b9f

Browse files
committed
simplify inf/nan parsing - massively reduce ToLower overhead
1 parent ec531da commit ac65b9f

File tree

1 file changed

+62
-40
lines changed

1 file changed

+62
-40
lines changed

src/StackExchange.Redis/Format.cs

Lines changed: 62 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Diagnostics.CodeAnalysis;
55
using System.Globalization;
66
using System.Net;
7+
using System.Runtime.CompilerServices;
78
using System.Text;
89

910
#if UNIX_SOCKET
@@ -168,24 +169,41 @@ internal static bool TryParseDouble(string? s, out double value)
168169
value = s[0] - '0';
169170
return true;
170171
// RESP3 spec demands inf/nan handling
171-
case 3 when CaseInsensitiveASCIIEqual("inf", s):
172-
value = double.PositiveInfinity;
173-
return true;
174-
case 3 when CaseInsensitiveASCIIEqual("nan", s):
175-
value = double.NaN;
176-
return true;
177-
case 4 when CaseInsensitiveASCIIEqual("+inf", s):
178-
value = double.PositiveInfinity;
179-
return true;
180-
case 4 when CaseInsensitiveASCIIEqual("-inf", s):
181-
value = double.NegativeInfinity;
182-
return true;
183-
case 4 when CaseInsensitiveASCIIEqual("+nan", s):
184-
case 4 when CaseInsensitiveASCIIEqual("-nan", s):
185-
value = double.NaN;
172+
case 3 when TryParseInfNaN(s.AsSpan(), true, out value):
173+
case 4 when s[0] == '+' && TryParseInfNaN(s.AsSpan(1), true, out value):
174+
case 4 when s[0] == '-' && TryParseInfNaN(s.AsSpan(1), false, out value):
186175
return true;
187176
}
188177
return double.TryParse(s, NumberStyles.Any, NumberFormatInfo.InvariantInfo, out value);
178+
179+
static bool TryParseInfNaN(ReadOnlySpan<char> s, bool positive, out double value)
180+
{
181+
switch (s[0])
182+
{
183+
case 'i':
184+
case 'I':
185+
if (s[1] is 'n' or 'N' && s[2] is 'f' or 'F')
186+
{
187+
value = positive ? double.PositiveInfinity : double.NegativeInfinity;
188+
return true;
189+
}
190+
break;
191+
case 'n':
192+
case 'N':
193+
if (s[1] is 'a' or 'A' && s[2] is 'n' or 'N')
194+
{
195+
value = double.NaN;
196+
return true;
197+
}
198+
break;
199+
}
200+
#if NET6_0_OR_GREATER
201+
Unsafe.SkipInit(out value);
202+
#else
203+
value = 0;
204+
#endif
205+
return false;
206+
}
189207
}
190208

191209
internal static bool TryParseUInt64(string s, out ulong value) =>
@@ -235,37 +253,41 @@ internal static bool TryParseDouble(ReadOnlySpan<byte> s, out double value)
235253
value = s[0] - '0';
236254
return true;
237255
// RESP3 spec demands inf/nan handling
238-
case 3 when CaseInsensitiveASCIIEqual("inf", s):
239-
value = double.PositiveInfinity;
240-
return true;
241-
case 3 when CaseInsensitiveASCIIEqual("nan", s):
242-
value = double.NaN;
243-
return true;
244-
case 4 when CaseInsensitiveASCIIEqual("+inf", s):
245-
value = double.PositiveInfinity;
246-
return true;
247-
case 4 when CaseInsensitiveASCIIEqual("-inf", s):
248-
value = double.NegativeInfinity;
249-
return true;
250-
case 4 when CaseInsensitiveASCIIEqual("+nan", s):
251-
case 4 when CaseInsensitiveASCIIEqual("-nan", s):
252-
value = double.NaN;
256+
case 3 when TryParseInfNaN(s, true, out value):
257+
case 4 when s[0] == '+' && TryParseInfNaN(s.Slice(1), true, out value):
258+
case 4 when s[0] == '-' && TryParseInfNaN(s.Slice(1), false, out value):
253259
return true;
254260
}
255261
return Utf8Parser.TryParse(s, out value, out int bytes) & bytes == s.Length;
256-
}
257262

258-
private static bool CaseInsensitiveASCIIEqual(string xLowerCase, string y)
259-
=> string.Equals(xLowerCase, y, StringComparison.OrdinalIgnoreCase);
260-
261-
private static bool CaseInsensitiveASCIIEqual(string xLowerCase, ReadOnlySpan<byte> y)
262-
{
263-
if (y.Length != xLowerCase.Length) return false;
264-
for (int i = 0; i < y.Length; i++)
263+
static bool TryParseInfNaN(ReadOnlySpan<byte> s, bool positive, out double value)
265264
{
266-
if (char.ToLower((char)y[i]) != xLowerCase[i]) return false;
265+
switch (s[0])
266+
{
267+
case (byte)'i':
268+
case (byte)'I':
269+
if (s[1] is (byte)'n' or (byte)'N' && s[2] is (byte)'f' or (byte)'F')
270+
{
271+
value = positive ? double.PositiveInfinity : double.NegativeInfinity;
272+
return true;
273+
}
274+
break;
275+
case (byte)'n':
276+
case (byte)'N':
277+
if (s[1] is (byte)'a' or (byte)'A' && s[2] is (byte)'n' or (byte)'N')
278+
{
279+
value = double.NaN;
280+
return true;
281+
}
282+
break;
283+
}
284+
#if NET6_0_OR_GREATER
285+
Unsafe.SkipInit(out value);
286+
#else
287+
value = 0;
288+
#endif
289+
return false;
267290
}
268-
return true;
269291
}
270292

271293
/// <summary>

0 commit comments

Comments
 (0)