Skip to content

Commit 5c2dce5

Browse files
authored
Optimize new use of Base64Url (#57050)
* Optimize new use of Base64Url Rather than searching the whole input for + or / to know if we need to use Base64, we can search for those as well as - or _, which then on average means we only need to search through ~32 characters, assuming balanced input. The moment we see a - or _, we can just use Base64Url.
1 parent 876e36d commit 5c2dce5

File tree

1 file changed

+13
-4
lines changed

1 file changed

+13
-4
lines changed

src/Shared/WebEncoders/WebEncoders.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ namespace Microsoft.Extensions.Internal;
2828
#endif
2929
static class WebEncoders
3030
{
31+
#if NET9_0_OR_GREATER
32+
/// <summary>SearchValues for the two Base64 and two Base64Url chars that differ from each other.</summary>
33+
private static readonly SearchValues<char> s_base64vsBase64UrlDifferentiators = SearchValues.Create("+/-_");
34+
#endif
35+
3136
/// <summary>
3237
/// Decodes a base64url-encoded string.
3338
/// </summary>
@@ -69,9 +74,11 @@ public static byte[] Base64UrlDecode(string input, int offset, int count)
6974

7075
#if NET9_0_OR_GREATER
7176
// Legacy behavior of Base64UrlDecode supports either Base64 or Base64Url input.
72-
// If it doesn't have + or /, it can be treated as Base64Url.
77+
// If it has a - or _, or if it doesn't have + or /, it can be treated as Base64Url.
78+
// Searching for any of them allows us to stop the search as early as we know whether Base64Url should be used.
7379
ReadOnlySpan<char> inputSpan = input.AsSpan(offset, count);
74-
if (!inputSpan.ContainsAny('+', '/'))
80+
int indexOfFirstDifferentiator = inputSpan.IndexOfAny(s_base64vsBase64UrlDifferentiators);
81+
if (indexOfFirstDifferentiator < 0 || inputSpan[indexOfFirstDifferentiator] is '-' or '_')
7582
{
7683
return Base64Url.DecodeFromChars(inputSpan);
7784
}
@@ -124,9 +131,11 @@ public static byte[] Base64UrlDecode(string input, int offset, char[] buffer, in
124131

125132
#if NET9_0_OR_GREATER
126133
// Legacy behavior of Base64UrlDecode supports either Base64 or Base64Url input.
127-
// If it doesn't have + or /, it can be treated as Base64Url.
134+
// If it has a - or _, or if it doesn't have + or /, it can be treated as Base64Url.
135+
// Searching for any of them allows us to stop the search as early as we know Base64Url should be used.
128136
ReadOnlySpan<char> inputSpan = input.AsSpan(offset, count);
129-
if (!inputSpan.ContainsAny('+', '/'))
137+
int indexOfFirstDifferentiator = inputSpan.IndexOfAny(s_base64vsBase64UrlDifferentiators);
138+
if (indexOfFirstDifferentiator < 0 || inputSpan[indexOfFirstDifferentiator] is '-' or '_')
130139
{
131140
return Base64Url.DecodeFromChars(inputSpan);
132141
}

0 commit comments

Comments
 (0)