Skip to content

Commit

Permalink
Avoid a few more allocations
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
stephentoub authored and brentschmaltz committed Aug 21, 2023
1 parent 6602db2 commit 5c9192c
Show file tree
Hide file tree
Showing 10 changed files with 38 additions and 38 deletions.
6 changes: 3 additions & 3 deletions src/Microsoft.IdentityModel.JsonWebTokens/ClaimTypeMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal static class ClaimTypeMapping
// This is the short to long mapping.
// key is the long claim type
// value is the short claim type
private static Dictionary<string, string> shortToLongClaimTypeMapping = new Dictionary<string, string>
private static readonly Dictionary<string, string> shortToLongClaimTypeMapping = new Dictionary<string, string>
{
{ JwtRegisteredClaimNames.Actort, ClaimTypes.Actor },
{ JwtRegisteredClaimNames.Birthdate, ClaimTypes.DateOfBirth },
Expand Down Expand Up @@ -88,8 +88,8 @@ internal static class ClaimTypeMapping
{ "winaccountname", ClaimTypes.WindowsAccountName },
};

private static IDictionary<string, string> longToShortClaimTypeMapping = new Dictionary<string, string>();
private static HashSet<string> inboundClaimFilter = inboundClaimFilter = new HashSet<string>();
private static readonly Dictionary<string, string> longToShortClaimTypeMapping = new Dictionary<string, string>();
private static readonly HashSet<string> inboundClaimFilter = inboundClaimFilter = new HashSet<string>();

/// <summary>
/// Initializes static members of the <see cref="ClaimTypeMapping"/> class.
Expand Down
27 changes: 15 additions & 12 deletions src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonClaimSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Collections.Generic;
using System.Globalization;
using System.Security.Claims;
using System.Text;
using System.Text.Json;
using Microsoft.IdentityModel.Logging;
using Microsoft.IdentityModel.Tokens;
Expand Down Expand Up @@ -271,19 +270,20 @@ internal T GetValue<T>(string key, bool throwEx, out bool found)

if (typeof(T) == typeof(IList<string>))
{
List<string> list = new();
if (obj is IList iList)
{
foreach (object item in iList)
list.Add(item?.ToString());
string[] arr = new string[iList.Count];
for (int arri = 0; arri < arr.Length; arri++)
{
arr[arri] = iList[arri]?.ToString();
}

return (T)((object)list);
return (T)(object)arr;
}
else
{
list.Add(obj.ToString());
return (T)(object)new string[1] { obj.ToString() };
}
return (T)(object)list;
}

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

if (typeof(T) == typeof(IList<object>))
{
List<object> list = new();
if (obj is IList items)
{
foreach (object item in items)
list.Add(item);
object[] arr = new object[items.Count];
for (int arri = 0; arri < arr.Length; arri++)
{
arr[arri] = items[arri];
}

return (T)(object)arr;
}
else
{
list.Add(obj);
return (T)(object)new object[1] { obj };
}
return (T)((object)list);
}

if (typeof(T) == typeof(int[]))
Expand Down
18 changes: 6 additions & 12 deletions src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Logging;
Expand All @@ -23,7 +25,7 @@ public class JsonWebToken : SecurityToken
// Some of the common values are cached in local variables.
private string _act;
private string _alg;
private IList<string> _audiences;
private string[] _audiences;
private string _authenticationTag;
private string _ciphertext;
private string _cty;
Expand Down Expand Up @@ -614,17 +616,9 @@ public IEnumerable<string> Audiences
{
lock (_audiencesLock)
{
if (_audiences == null)
{
List<string> tmp = new List<string>();
if (Payload.TryGetValue(JwtRegisteredClaimNames.Aud, out IList<string> audiences))
{
foreach (string str in audiences)
tmp.Add(str);
}

_audiences = tmp;
}
_audiences ??= Payload.TryGetValue(JwtRegisteredClaimNames.Aud, out IList<string> audiences) ?
(audiences is string[] audiencesArray ? audiencesArray : audiences.ToArray()) :
Array.Empty<string>();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class JsonWebTokenHandler : TokenHandler
/// <summary>
/// Default claim type mapping for inbound claims.
/// </summary>
public static IDictionary<string, string> DefaultInboundClaimTypeMap = new Dictionary<string, string>(ClaimTypeMapping.InboundClaimTypeMap);
public static readonly Dictionary<string, string> DefaultInboundClaimTypeMap = new Dictionary<string, string>(ClaimTypeMapping.InboundClaimTypeMap);

/// <summary>
/// Default value for the flag that determines whether or not the InboundClaimTypeMap is used.
Expand Down
4 changes: 2 additions & 2 deletions src/Microsoft.IdentityModel.Tokens/CryptoProviderFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ namespace Microsoft.IdentityModel.Tokens
public class CryptoProviderFactory
{
private static CryptoProviderFactory _default;
private static ConcurrentDictionary<string, string> _typeToAlgorithmMap = new ConcurrentDictionary<string, string>();
private static object _cacheLock = new object();
private static readonly ConcurrentDictionary<string, string> _typeToAlgorithmMap = new ConcurrentDictionary<string, string>();
private static readonly object _cacheLock = new object();
private static int _defaultSignatureProviderObjectPoolCacheSize = Environment.ProcessorCount * 4;
private int _signatureProviderObjectPoolCacheSize = _defaultSignatureProviderObjectPoolCacheSize;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ public class SymmetricKeyWrapProvider : KeyWrapProvider
private static readonly byte[] _defaultIV = new byte[] { 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6 };
private const int _blockSizeInBits = 64;
private const int _blockSizeInBytes = _blockSizeInBits >> 3;
private static object _encryptorLock = new object();
private static object _decryptorLock = new object();
private static readonly object _encryptorLock = new object();
private static readonly object _decryptorLock = new object();

private Lazy<SymmetricAlgorithm> _symmetricAlgorithm;
private ICryptoTransform _symmetricAlgorithmEncryptor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ namespace Microsoft.IdentityModel.Tokens
/// <remarks>The delegate should return a non null string that represents the 'issuer'. If null a default value will be used.
/// <see cref="IssuerValidatorAsync"/> if set, will be called before <see cref="IssuerSigningKeyValidatorUsingConfiguration"/> or <see cref="IssuerSigningKeyValidator"/>
/// </remarks>
internal delegate Task<string> IssuerValidatorAsync(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters);
internal delegate ValueTask<string> IssuerValidatorAsync(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters);

/// <summary>
/// Definition for LifetimeValidator.
Expand Down
7 changes: 5 additions & 2 deletions src/Microsoft.IdentityModel.Tokens/Validators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,10 @@ public static string ValidateIssuer(string issuer, SecurityToken securityToken,
/// <remarks>An EXACT match is required.</remarks>
internal static string ValidateIssuer(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters, BaseConfiguration configuration)
{
return ValidateIssuerAsync(issuer, securityToken, validationParameters, configuration).GetAwaiter().GetResult();
ValueTask<string> vt = ValidateIssuerAsync(issuer, securityToken, validationParameters, configuration);
return vt.IsCompletedSuccessfully ?
vt.Result :
vt.AsTask().GetAwaiter().GetResult();
}

/// <summary>
Expand All @@ -232,7 +235,7 @@ internal static string ValidateIssuer(string issuer, SecurityToken securityToken
/// <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>
/// <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>
/// <remarks>An EXACT match is required.</remarks>
internal static async Task<string> ValidateIssuerAsync(
internal static async ValueTask<string> ValidateIssuerAsync(
string issuer,
SecurityToken securityToken,
TokenValidationParameters validationParameters,
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.IdentityModel.Xml/XmlUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace Microsoft.IdentityModel.Xml
/// </summary>
public static class XmlUtil
{
private static Dictionary<byte, string> _hexDictionary = new Dictionary<byte, string>
private static readonly Dictionary<byte, string> _hexDictionary = new Dictionary<byte, string>
{
{ 0, "0" },
{ 1, "1" },
Expand Down
4 changes: 2 additions & 2 deletions test/Microsoft.IdentityModel.TestUtils/ValidationDelegates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ public static string IssuerValidatorUsingConfigEcho(string issuer, SecurityToken
return issuer;
}

public static Task<string> IssuerValidatorAsync(string issuer, SecurityToken token, TokenValidationParameters validationParameters)
public static ValueTask<string> IssuerValidatorAsync(string issuer, SecurityToken token, TokenValidationParameters validationParameters)
{
return Task.FromResult(issuer);
return new ValueTask<string>(issuer);
}

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

0 comments on commit 5c9192c

Please sign in to comment.