Skip to content

Commit c0eb23c

Browse files
msbw2pmaytak
andauthored
Use new Base64Url API (#2817)
* Use new Base64Url API * Include Bcl.Memory packge only on non-.NET9 * Don't run Base64Url benchmarks in the build. * Fix naming. --------- Co-authored-by: Peter <[email protected]>
1 parent 0aeeb26 commit c0eb23c

File tree

10 files changed

+215
-321
lines changed

10 files changed

+215
-321
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.Buffers.Text;
6+
using BenchmarkDotNet.Attributes;
7+
using Microsoft.IdentityModel.Tokens;
8+
9+
namespace Microsoft.IdentityModel.Benchmarks
10+
{
11+
// dotnet run -c release -f net9.0 --filter Microsoft.IdentityModel.Benchmarks.Base64UrlEncoderTests*
12+
13+
[MemoryDiagnoser]
14+
public class Base64UrlEncoderTests
15+
{
16+
private const string base64UrlEncodedString = "V1aOrL0rTJZYRxPHmMZdTWR0-ilwg9V9iEoKSn3CYl5vmBNqsM0x4VtvRbK8nCmnCIc2__QE92D4vQDR8AQ2j_BljJUkNY51VrbZ1wBar_7X2NktF_AQLkqDmwuagjhONR9_MIVysq36EAqxoHAwHHJx87XrrOkPDD8kiQ2uZEgPgK-4o02hhjsETU7KWiOKg4nKlLUU2YwuW2ZQxVubPfEv5SrW8BDgvNwPseyXfKrznhNAQHgwUX6sh1lTBm-cQdujkNsG62DeJSA2o9A_IhpKOuyQpaNda6U8jbBh3FGZhmFAm6yxNag3b6jAVlxphRNDvlm6UprgoFbvzcuH8W5ZH60LjNxsSKLH8W3gHIc7jhDA0vH2T8Nf2HEqFmqcsGr6aNm86ilWg1tchS_DlFPWqu8Wm3EEHTSJcd7BxMTvr9syRLICmhVsfHwdgMy1WfKklnyGJ_RT3kvbfCPQ2sSRMiOqCkdwCUECu-CcxS4CiIanlWnIpllmBov6vawcR6o6gmcFuqxhw2rp3815glnF7jNkmr7hsd0DPQ7qRUOHlGkF8_Sgretbgpb61y8a8DlVLlb7nBBQbTFif-lBAH4gfWWeNF9A3RFPQ8e8UKghJ7u_4ua9W_Lk_xpDkyGDXrkAzTYLxOGujRaWexOpwWSOKsXgIqXa94px0HAUIAVwP2Gy_gWcVz47ayedXh1Tcqb3K1hDlzZt4XK6O9eu-lAgy6gBltSrkntumDB-XEkxRabh8FNMln_LeEh_TgwWX4iVBR1-VD-VJw1e_aypVWj_E178TjCeb6Lc9pKD_r2VAieZpVp0c15g3vxznBWPD5mviHnK_NbSiccodSfpzGJbUsBuvKvhK4EFSw4_YlWJFlEXj3XYtiqO60crVynlEEqegLncI6RrjWe8WEfXEm_yeiglH5I-asU5sl0pBdLRdeg1xo1SZfR-CtgJ0dliwGkPDE6HcyGqhddMbIze_5I8ZazQ31PQaShhXtdH3K_cWXe4WhpR-_qYTrwib89ux2zZxePCkb_RXyvd09hv1J1kkmTf9f7q1xXfiBw49Iun90tJaOMru6PeL3Ayixj4d2C-rnwS43jcRJJ_SBiRgpBQo3Gg893UkxY2l2prQa-zU9GdbwlfDF9Htijxm75SuoxOldhTFDcpw6QqKjt1116gfkmgg16hXjvNhV8sCqxmHdKoIM6EOKVy5MAIJcg_-wbAVhbJQ205udIPb49GY1yDePieu2eQa6TU8Pn66YK5Kl4K6kCmOY6NpDdhDk6BwyJ6Z9wz2nF8OwF2mDKpMdP2nkFnq8iq2z9o7s7HwIP8pbr99kvMlw";
17+
// Add padding
18+
private static readonly string base64EncodedString = base64UrlEncodedString.Replace('-', '+').Replace('_', '/') + "==";
19+
// Add "padding" without adding special characters
20+
private static readonly string base64NoSpecialCharsEncodedString = base64UrlEncodedString.Replace('-', 'A').Replace('_', 'B') + "ab";
21+
// Add padding as only special characters (Base64-encoded but could be decoded with Base64Url API)
22+
private static readonly string base64NoSpecialCharsExceptPaddingEncodedString = base64UrlEncodedString.Replace('-', 'A').Replace('_', 'B') + "==";
23+
private static readonly string decodedString = Base64UrlEncoder.Decode(base64UrlEncodedString);
24+
private static readonly byte[] decodedBytes = Base64UrlEncoder.DecodeBytes(base64UrlEncodedString);
25+
26+
[Benchmark]
27+
public void Decode_String_Base64Url() => Base64UrlEncoder.Decode(base64UrlEncodedString);
28+
29+
[Benchmark]
30+
public void Decode_Span_Base64Url() => Base64UrlEncoder.Decode(base64UrlEncodedString.AsSpan());
31+
32+
[Benchmark]
33+
public void DecodeBytes_Base64Url() => Base64UrlEncoder.DecodeBytes(base64UrlEncodedString);
34+
35+
[Benchmark]
36+
public void Decode_Span_Output_Base64Url() => Base64UrlEncoder.Decode(base64UrlEncodedString.AsSpan(), new byte[Base64.GetMaxDecodedFromUtf8Length(base64UrlEncodedString.Length + 2)]);
37+
38+
[Benchmark]
39+
public void Decode_String_Base64() => Base64UrlEncoder.Decode(base64EncodedString);
40+
41+
[Benchmark]
42+
public void Decode_Span_Base64() => Base64UrlEncoder.Decode(base64EncodedString.AsSpan());
43+
44+
[Benchmark]
45+
public void DecodeBytes_Base64() => Base64UrlEncoder.DecodeBytes(base64EncodedString);
46+
47+
[Benchmark]
48+
public void Decode_Span_Output_Base64() => Base64UrlEncoder.Decode(base64EncodedString.AsSpan(), new byte[Base64.GetMaxDecodedFromUtf8Length(base64EncodedString.Length + 2)]);
49+
50+
[Benchmark]
51+
public void Decode_String_Base64NoSpecialChars() => Base64UrlEncoder.Decode(base64NoSpecialCharsEncodedString);
52+
53+
[Benchmark]
54+
public void Decode_Span_Base64NoSpecialChars() => Base64UrlEncoder.Decode(base64NoSpecialCharsEncodedString.AsSpan());
55+
56+
[Benchmark]
57+
public void DecodeBytes_Base64NoSpecialChars() => Base64UrlEncoder.DecodeBytes(base64NoSpecialCharsEncodedString);
58+
59+
[Benchmark]
60+
public void Decode_Span_Output_Base64NoSpecialChars() => Base64UrlEncoder.Decode(base64NoSpecialCharsEncodedString.AsSpan(), new byte[Base64.GetMaxDecodedFromUtf8Length(base64NoSpecialCharsEncodedString.Length + 2)]);
61+
62+
[Benchmark]
63+
public void Decode_String_Base64NoSpecialCharsExceptPadding() => Base64UrlEncoder.Decode(base64NoSpecialCharsExceptPaddingEncodedString);
64+
65+
[Benchmark]
66+
public void Decode_Span_Base64NoSpecialCharsExceptPadding() => Base64UrlEncoder.Decode(base64NoSpecialCharsExceptPaddingEncodedString.AsSpan());
67+
68+
[Benchmark]
69+
public void DecodeBytes_Base64NoSpecialCharsExceptPadding() => Base64UrlEncoder.DecodeBytes(base64NoSpecialCharsExceptPaddingEncodedString);
70+
71+
[Benchmark]
72+
public void Decode_Span_Output_Base64NoSpecialCharsExceptPadding() => Base64UrlEncoder.Decode(base64NoSpecialCharsExceptPaddingEncodedString.AsSpan(), new byte[Base64.GetMaxDecodedFromUtf8Length(base64NoSpecialCharsExceptPaddingEncodedString.Length + 2)]);
73+
74+
[Benchmark]
75+
public void Encode_String_Base64Url() => Base64UrlEncoder.Encode(decodedString);
76+
77+
[Benchmark]
78+
public void Encode_Bytes_Base64Url() => Base64UrlEncoder.Encode(decodedBytes);
79+
80+
[Benchmark]
81+
public void Encode_Span_Base64Url() => Base64UrlEncoder.Encode(decodedBytes, new char[Base64.GetMaxEncodedToUtf8Length(decodedBytes.Length)]);
82+
83+
[Benchmark]
84+
public void Encode_Bytes_Offset_Length_Base64Url() => Base64UrlEncoder.Encode(decodedBytes, decodedBytes.Length / 2, decodedBytes.Length / 2 - 10);
85+
}
86+
}

benchmark/Microsoft.IdentityModel.Benchmarks/identitymodel.benchmarks.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,3 @@ scenarios:
5656
job: benchmarks
5757
variables:
5858
filterArg: "*ValidateTokenAsyncTests*"
59-

build/dependencies.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<SystemMemoryVersion>4.5.5</SystemMemoryVersion>
1212
<SystemSecurityCryptographyCngVersion>4.5.0</SystemSecurityCryptographyCngVersion>
1313
<SystemTextJson>8.0.5</SystemTextJson>
14+
<MicrosoftBclMemory>9.0.0</MicrosoftBclMemory>
1415
</PropertyGroup>
1516

1617

src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -566,15 +566,22 @@ internal JsonClaimSet CreateClaimSet(ReadOnlySpan<char> strSpan, int startIndex,
566566
{
567567
int outputSize = Base64UrlEncoding.ValidateAndGetOutputSize(strSpan, startIndex, length);
568568

569-
byte[] output = ArrayPool<byte>.Shared.Rent(outputSize);
569+
byte[] rented = null;
570+
571+
const int MaxStackallocThreshold = 256;
572+
Span<byte> output = outputSize <= MaxStackallocThreshold
573+
? stackalloc byte[outputSize]
574+
: (rented = ArrayPool<byte>.Shared.Rent(outputSize));
575+
570576
try
571577
{
572578
Base64UrlEncoder.Decode(strSpan.Slice(startIndex, length), output);
573-
return createHeaderClaimSet ? CreateHeaderClaimSet(output.AsSpan()) : CreatePayloadClaimSet(output.AsSpan());
579+
return createHeaderClaimSet ? CreateHeaderClaimSet(output) : CreatePayloadClaimSet(output);
574580
}
575581
finally
576582
{
577-
ArrayPool<byte>.Shared.Return(output, true);
583+
if (rented is not null)
584+
ArrayPool<byte>.Shared.Return(rented, true);
578585
}
579586
}
580587

0 commit comments

Comments
 (0)