Skip to content

Commit 5c9192c

Browse files
stephentoubbrentschmaltz
authored andcommitted
Avoid a few more allocations
- JsonWebToken.Audiences can allocate a string[] of exactly the right size, saving on intermediate string[] allocations as well as a List allocation. If empty, it can avoid allocations completely. - ValidateIssuerAsync can avoid a Task allocation in the common case where it completes synchronously. - Also make some static fields readonly and change a few types to be concrete classes rather than interfaces; it helps the JIT generate better code.
1 parent 6602db2 commit 5c9192c

File tree

10 files changed

+38
-38
lines changed

10 files changed

+38
-38
lines changed

src/Microsoft.IdentityModel.JsonWebTokens/ClaimTypeMapping.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ internal static class ClaimTypeMapping
1111
// This is the short to long mapping.
1212
// key is the long claim type
1313
// value is the short claim type
14-
private static Dictionary<string, string> shortToLongClaimTypeMapping = new Dictionary<string, string>
14+
private static readonly Dictionary<string, string> shortToLongClaimTypeMapping = new Dictionary<string, string>
1515
{
1616
{ JwtRegisteredClaimNames.Actort, ClaimTypes.Actor },
1717
{ JwtRegisteredClaimNames.Birthdate, ClaimTypes.DateOfBirth },
@@ -88,8 +88,8 @@ internal static class ClaimTypeMapping
8888
{ "winaccountname", ClaimTypes.WindowsAccountName },
8989
};
9090

91-
private static IDictionary<string, string> longToShortClaimTypeMapping = new Dictionary<string, string>();
92-
private static HashSet<string> inboundClaimFilter = inboundClaimFilter = new HashSet<string>();
91+
private static readonly Dictionary<string, string> longToShortClaimTypeMapping = new Dictionary<string, string>();
92+
private static readonly HashSet<string> inboundClaimFilter = inboundClaimFilter = new HashSet<string>();
9393

9494
/// <summary>
9595
/// Initializes static members of the <see cref="ClaimTypeMapping"/> class.

src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonClaimSet.cs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using System.Collections.Generic;
77
using System.Globalization;
88
using System.Security.Claims;
9-
using System.Text;
109
using System.Text.Json;
1110
using Microsoft.IdentityModel.Logging;
1211
using Microsoft.IdentityModel.Tokens;
@@ -271,19 +270,20 @@ internal T GetValue<T>(string key, bool throwEx, out bool found)
271270

272271
if (typeof(T) == typeof(IList<string>))
273272
{
274-
List<string> list = new();
275273
if (obj is IList iList)
276274
{
277-
foreach (object item in iList)
278-
list.Add(item?.ToString());
275+
string[] arr = new string[iList.Count];
276+
for (int arri = 0; arri < arr.Length; arri++)
277+
{
278+
arr[arri] = iList[arri]?.ToString();
279+
}
279280

280-
return (T)((object)list);
281+
return (T)(object)arr;
281282
}
282283
else
283284
{
284-
list.Add(obj.ToString());
285+
return (T)(object)new string[1] { obj.ToString() };
285286
}
286-
return (T)(object)list;
287287
}
288288

289289
if (typeof(T) == typeof(int) && int.TryParse(obj.ToString(), out int i))
@@ -309,17 +309,20 @@ internal T GetValue<T>(string key, bool throwEx, out bool found)
309309

310310
if (typeof(T) == typeof(IList<object>))
311311
{
312-
List<object> list = new();
313312
if (obj is IList items)
314313
{
315-
foreach (object item in items)
316-
list.Add(item);
314+
object[] arr = new object[items.Count];
315+
for (int arri = 0; arri < arr.Length; arri++)
316+
{
317+
arr[arri] = items[arri];
318+
}
319+
320+
return (T)(object)arr;
317321
}
318322
else
319323
{
320-
list.Add(obj);
324+
return (T)(object)new object[1] { obj };
321325
}
322-
return (T)((object)list);
323326
}
324327

325328
if (typeof(T) == typeof(int[]))

src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Diagnostics;
7+
using System.Linq;
68
using System.Security.Claims;
79
using System.Text;
810
using Microsoft.IdentityModel.Logging;
@@ -23,7 +25,7 @@ public class JsonWebToken : SecurityToken
2325
// Some of the common values are cached in local variables.
2426
private string _act;
2527
private string _alg;
26-
private IList<string> _audiences;
28+
private string[] _audiences;
2729
private string _authenticationTag;
2830
private string _ciphertext;
2931
private string _cty;
@@ -614,17 +616,9 @@ public IEnumerable<string> Audiences
614616
{
615617
lock (_audiencesLock)
616618
{
617-
if (_audiences == null)
618-
{
619-
List<string> tmp = new List<string>();
620-
if (Payload.TryGetValue(JwtRegisteredClaimNames.Aud, out IList<string> audiences))
621-
{
622-
foreach (string str in audiences)
623-
tmp.Add(str);
624-
}
625-
626-
_audiences = tmp;
627-
}
619+
_audiences ??= Payload.TryGetValue(JwtRegisteredClaimNames.Aud, out IList<string> audiences) ?
620+
(audiences is string[] audiencesArray ? audiencesArray : audiences.ToArray()) :
621+
Array.Empty<string>();
628622
}
629623
}
630624

src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public class JsonWebTokenHandler : TokenHandler
3434
/// <summary>
3535
/// Default claim type mapping for inbound claims.
3636
/// </summary>
37-
public static IDictionary<string, string> DefaultInboundClaimTypeMap = new Dictionary<string, string>(ClaimTypeMapping.InboundClaimTypeMap);
37+
public static readonly Dictionary<string, string> DefaultInboundClaimTypeMap = new Dictionary<string, string>(ClaimTypeMapping.InboundClaimTypeMap);
3838

3939
/// <summary>
4040
/// Default value for the flag that determines whether or not the InboundClaimTypeMap is used.

src/Microsoft.IdentityModel.Tokens/CryptoProviderFactory.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ namespace Microsoft.IdentityModel.Tokens
1515
public class CryptoProviderFactory
1616
{
1717
private static CryptoProviderFactory _default;
18-
private static ConcurrentDictionary<string, string> _typeToAlgorithmMap = new ConcurrentDictionary<string, string>();
19-
private static object _cacheLock = new object();
18+
private static readonly ConcurrentDictionary<string, string> _typeToAlgorithmMap = new ConcurrentDictionary<string, string>();
19+
private static readonly object _cacheLock = new object();
2020
private static int _defaultSignatureProviderObjectPoolCacheSize = Environment.ProcessorCount * 4;
2121
private int _signatureProviderObjectPoolCacheSize = _defaultSignatureProviderObjectPoolCacheSize;
2222

src/Microsoft.IdentityModel.Tokens/Encryption/SymmetricKeyWrapProvider.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ public class SymmetricKeyWrapProvider : KeyWrapProvider
1515
private static readonly byte[] _defaultIV = new byte[] { 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6 };
1616
private const int _blockSizeInBits = 64;
1717
private const int _blockSizeInBytes = _blockSizeInBits >> 3;
18-
private static object _encryptorLock = new object();
19-
private static object _decryptorLock = new object();
18+
private static readonly object _encryptorLock = new object();
19+
private static readonly object _decryptorLock = new object();
2020

2121
private Lazy<SymmetricAlgorithm> _symmetricAlgorithm;
2222
private ICryptoTransform _symmetricAlgorithmEncryptor;

src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ namespace Microsoft.IdentityModel.Tokens
112112
/// <remarks>The delegate should return a non null string that represents the 'issuer'. If null a default value will be used.
113113
/// <see cref="IssuerValidatorAsync"/> if set, will be called before <see cref="IssuerSigningKeyValidatorUsingConfiguration"/> or <see cref="IssuerSigningKeyValidator"/>
114114
/// </remarks>
115-
internal delegate Task<string> IssuerValidatorAsync(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters);
115+
internal delegate ValueTask<string> IssuerValidatorAsync(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters);
116116

117117
/// <summary>
118118
/// Definition for LifetimeValidator.

src/Microsoft.IdentityModel.Tokens/Validators.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,10 @@ public static string ValidateIssuer(string issuer, SecurityToken securityToken,
215215
/// <remarks>An EXACT match is required.</remarks>
216216
internal static string ValidateIssuer(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters, BaseConfiguration configuration)
217217
{
218-
return ValidateIssuerAsync(issuer, securityToken, validationParameters, configuration).GetAwaiter().GetResult();
218+
ValueTask<string> vt = ValidateIssuerAsync(issuer, securityToken, validationParameters, configuration);
219+
return vt.IsCompletedSuccessfully ?
220+
vt.Result :
221+
vt.AsTask().GetAwaiter().GetResult();
219222
}
220223

221224
/// <summary>
@@ -232,7 +235,7 @@ internal static string ValidateIssuer(string issuer, SecurityToken securityToken
232235
/// <exception cref="SecurityTokenInvalidIssuerException">If <see cref="TokenValidationParameters.ValidIssuer"/> is null or whitespace and <see cref="TokenValidationParameters.ValidIssuers"/> is null and <see cref="BaseConfiguration.Issuer"/> is null.</exception>
233236
/// <exception cref="SecurityTokenInvalidIssuerException">If 'issuer' failed to matched either <see cref="TokenValidationParameters.ValidIssuer"/> or one of <see cref="TokenValidationParameters.ValidIssuers"/> or <see cref="BaseConfiguration.Issuer"/>.</exception>
234237
/// <remarks>An EXACT match is required.</remarks>
235-
internal static async Task<string> ValidateIssuerAsync(
238+
internal static async ValueTask<string> ValidateIssuerAsync(
236239
string issuer,
237240
SecurityToken securityToken,
238241
TokenValidationParameters validationParameters,

src/Microsoft.IdentityModel.Xml/XmlUtil.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace Microsoft.IdentityModel.Xml
1414
/// </summary>
1515
public static class XmlUtil
1616
{
17-
private static Dictionary<byte, string> _hexDictionary = new Dictionary<byte, string>
17+
private static readonly Dictionary<byte, string> _hexDictionary = new Dictionary<byte, string>
1818
{
1919
{ 0, "0" },
2020
{ 1, "1" },

test/Microsoft.IdentityModel.TestUtils/ValidationDelegates.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,9 @@ public static string IssuerValidatorUsingConfigEcho(string issuer, SecurityToken
8787
return issuer;
8888
}
8989

90-
public static Task<string> IssuerValidatorAsync(string issuer, SecurityToken token, TokenValidationParameters validationParameters)
90+
public static ValueTask<string> IssuerValidatorAsync(string issuer, SecurityToken token, TokenValidationParameters validationParameters)
9191
{
92-
return Task.FromResult(issuer);
92+
return new ValueTask<string>(issuer);
9393
}
9494

9595
public static string IssuerValidatorReturnsDifferentIssuer(string issuer, SecurityToken token, TokenValidationParameters validationParameters)

0 commit comments

Comments
 (0)