2
2
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3
3
4
4
using System ;
5
+ using System . Buffers ;
5
6
using System . Diagnostics ;
6
7
using System . Globalization ;
7
8
using Microsoft . Extensions . WebEncoders . Sources ;
@@ -189,6 +190,36 @@ public static int GetArraySizeRequiredToDecode(int count)
189
190
return checked ( count + numPaddingCharsToAdd ) ;
190
191
}
191
192
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
+
192
223
/// <summary>
193
224
/// Encodes <paramref name="input"/> using base64url encoding.
194
225
/// </summary>
@@ -220,16 +251,7 @@ public static string Base64UrlEncode(byte[] input, int offset, int count)
220
251
221
252
ValidateParameters ( input . Length , nameof ( input ) , offset , count ) ;
222
253
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 ) ) ;
233
255
}
234
256
235
257
/// <summary>
@@ -280,19 +302,38 @@ public static int Base64UrlEncode(byte[] input, int offset, char[] output, int o
280
302
nameof ( count ) ) ;
281
303
}
282
304
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 )
285
327
{
286
328
return 0 ;
287
329
}
288
330
289
331
// Use base64url encoding with no padding characters. See RFC 4648, Sec. 5.
290
332
291
- // Start with default Base64 encoding.
292
- var numBase64Chars = Convert . ToBase64CharArray ( input , offset , count , output , outputOffset ) ;
333
+ Convert . TryToBase64Chars ( input , output , out int charsWritten ) ;
293
334
294
335
// Fix up '+' -> '-' and '/' -> '_'. Drop padding characters.
295
- for ( var i = outputOffset ; i - outputOffset < numBase64Chars ; i ++ )
336
+ for ( var i = 0 ; i < charsWritten ; i ++ )
296
337
{
297
338
var ch = output [ i ] ;
298
339
if ( ch == '+' )
@@ -306,25 +347,11 @@ public static int Base64UrlEncode(byte[] input, int offset, char[] output, int o
306
347
else if ( ch == '=' )
307
348
{
308
349
// We've reached a padding character; truncate the remainder.
309
- return i - outputOffset ;
350
+ return i ;
310
351
}
311
352
}
312
353
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 ;
328
355
}
329
356
330
357
private static int GetNumBase64PaddingCharsInString ( string str )
0 commit comments