Skip to content

Commit 249b565

Browse files
committed
Spanified WebEncoders.Base64UrlEncode with string return
1 parent 37e6ad7 commit 249b565

File tree

1 file changed

+58
-31
lines changed

1 file changed

+58
-31
lines changed

src/Shared/WebEncoders/WebEncoders.cs

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Buffers;
56
using System.Diagnostics;
67
using System.Globalization;
78
using Microsoft.Extensions.WebEncoders.Sources;
@@ -189,6 +190,36 @@ public static int GetArraySizeRequiredToDecode(int count)
189190
return checked(count + numPaddingCharsToAdd);
190191
}
191192

193+
/// <summary>
194+
/// Encodes <paramref name="input"/> using base64url encoding.
195+
/// </summary>
196+
/// <param name="input">The binary input to encode.</param>
197+
/// <returns>The base64url-encoded form of <paramref name="input"/>.</returns>
198+
public static string Base64UrlEncode(ReadOnlySpan<byte> input)
199+
{
200+
if (input.IsEmpty)
201+
{
202+
return string.Empty;
203+
}
204+
205+
int bufferSize = GetArraySizeRequiredToEncode(input.Length);
206+
207+
char[] bufferToReturnToPool = null;
208+
Span<char> buffer = bufferSize <= 128
209+
? stackalloc char[bufferSize]
210+
: bufferToReturnToPool = ArrayPool<char>.Shared.Rent(bufferSize);
211+
212+
var numBase64Chars = Base64UrlEncode(input, buffer);
213+
var base64Url = new string(buffer.Slice(0, numBase64Chars));
214+
215+
if (bufferToReturnToPool != null)
216+
{
217+
ArrayPool<char>.Shared.Return(bufferToReturnToPool);
218+
}
219+
220+
return base64Url;
221+
}
222+
192223
/// <summary>
193224
/// Encodes <paramref name="input"/> using base64url encoding.
194225
/// </summary>
@@ -220,16 +251,7 @@ public static string Base64UrlEncode(byte[] input, int offset, int count)
220251

221252
ValidateParameters(input.Length, nameof(input), offset, count);
222253

223-
// Special-case empty input
224-
if (count == 0)
225-
{
226-
return string.Empty;
227-
}
228-
229-
var buffer = new char[GetArraySizeRequiredToEncode(count)];
230-
var numBase64Chars = Base64UrlEncode(input, offset, buffer, outputOffset: 0, count: count);
231-
232-
return new String(buffer, startIndex: 0, length: numBase64Chars);
254+
return Base64UrlEncode(input.AsSpan(offset, count));
233255
}
234256

235257
/// <summary>
@@ -280,19 +302,38 @@ public static int Base64UrlEncode(byte[] input, int offset, char[] output, int o
280302
nameof(count));
281303
}
282304

283-
// Special-case empty input.
284-
if (count == 0)
305+
return Base64UrlEncode(input.AsSpan(offset, count), output.AsSpan(outputOffset));
306+
}
307+
308+
/// <summary>
309+
/// Get the minimum output <c>char[]</c> size required for encoding <paramref name="count"/>
310+
/// <see cref="byte"/>s with the <see cref="Base64UrlEncode(byte[], int, char[], int, int)"/> method.
311+
/// </summary>
312+
/// <param name="count">The number of characters to encode.</param>
313+
/// <returns>
314+
/// The minimum output <c>char[]</c> size required for encoding <paramref name="count"/> <see cref="byte"/>s.
315+
/// </returns>
316+
public static int GetArraySizeRequiredToEncode(int count)
317+
{
318+
var numWholeOrPartialInputBlocks = checked(count + 2) / 3;
319+
return checked(numWholeOrPartialInputBlocks * 4);
320+
}
321+
322+
private static int Base64UrlEncode(ReadOnlySpan<byte> input, Span<char> output)
323+
{
324+
Debug.Assert(output.Length >= GetArraySizeRequiredToEncode(input.Length));
325+
326+
if (input.IsEmpty)
285327
{
286328
return 0;
287329
}
288330

289331
// Use base64url encoding with no padding characters. See RFC 4648, Sec. 5.
290332

291-
// Start with default Base64 encoding.
292-
var numBase64Chars = Convert.ToBase64CharArray(input, offset, count, output, outputOffset);
333+
Convert.TryToBase64Chars(input, output, out int charsWritten);
293334

294335
// Fix up '+' -> '-' and '/' -> '_'. Drop padding characters.
295-
for (var i = outputOffset; i - outputOffset < numBase64Chars; i++)
336+
for (var i = 0; i < charsWritten; i++)
296337
{
297338
var ch = output[i];
298339
if (ch == '+')
@@ -306,25 +347,11 @@ public static int Base64UrlEncode(byte[] input, int offset, char[] output, int o
306347
else if (ch == '=')
307348
{
308349
// We've reached a padding character; truncate the remainder.
309-
return i - outputOffset;
350+
return i;
310351
}
311352
}
312353

313-
return numBase64Chars;
314-
}
315-
316-
/// <summary>
317-
/// Get the minimum output <c>char[]</c> size required for encoding <paramref name="count"/>
318-
/// <see cref="byte"/>s with the <see cref="Base64UrlEncode(byte[], int, char[], int, int)"/> method.
319-
/// </summary>
320-
/// <param name="count">The number of characters to encode.</param>
321-
/// <returns>
322-
/// The minimum output <c>char[]</c> size required for encoding <paramref name="count"/> <see cref="byte"/>s.
323-
/// </returns>
324-
public static int GetArraySizeRequiredToEncode(int count)
325-
{
326-
var numWholeOrPartialInputBlocks = checked(count + 2) / 3;
327-
return checked(numWholeOrPartialInputBlocks * 4);
354+
return charsWritten;
328355
}
329356

330357
private static int GetNumBase64PaddingCharsInString(string str)

0 commit comments

Comments
 (0)