From db43799d0f8f9c7e93a5f6017b527bfa04392e55 Mon Sep 17 00:00:00 2001 From: Henrik Gedionsen Date: Mon, 31 Mar 2025 15:51:58 +0200 Subject: [PATCH] Avoid some heap allocations --- ...henticationDeliveryApiSwaggerGenOptions.cs | 2 +- ...SecurityRequirementsOperationFilterBase.cs | 2 +- src/Umbraco.Core/Composing/TypeFinder.cs | 5 +--- .../Exceptions/InvalidCompositionException.cs | 2 +- src/Umbraco.Core/Extensions/IntExtensions.cs | 4 +-- .../Extensions/StringExtensions.cs | 30 ++++++++----------- src/Umbraco.Core/GuidUtils.cs | 2 +- src/Umbraco.Core/HexEncoder.cs | 10 ++++--- .../Strings/DefaultShortStringHelper.cs | 7 +++-- .../Strings/Utf8ToAsciiConverter.cs | 7 ++--- 10 files changed, 33 insertions(+), 38 deletions(-) diff --git a/src/Umbraco.Cms.Api.Delivery/Configuration/ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions.cs b/src/Umbraco.Cms.Api.Delivery/Configuration/ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions.cs index 1c821f9681..5eaa75002c 100644 --- a/src/Umbraco.Cms.Api.Delivery/Configuration/ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions.cs +++ b/src/Umbraco.Cms.Api.Delivery/Configuration/ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions.cs @@ -48,7 +48,7 @@ public class ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions : Id = AuthSchemeName, } }, - new string[] { } + [] } } }; diff --git a/src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilterBase.cs b/src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilterBase.cs index 907b91cdac..e2ff1e609a 100644 --- a/src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilterBase.cs +++ b/src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilterBase.cs @@ -38,7 +38,7 @@ public abstract class BackOfficeSecurityRequirementsOperationFilterBase : IOpera Type = ReferenceType.SecurityScheme, Id = ManagementApiConfiguration.ApiSecurityName } - }, new string[] { } + }, [] } } }; diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs index 6728b2c7a6..2d715ba01b 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -233,10 +233,7 @@ public class TypeFinder : ITypeFinder excludeFromResults = new HashSet(); } - if (exclusionFilter == null) - { - exclusionFilter = new string[] { }; - } + exclusionFilter ??= []; return GetAllAssemblies() .Where(x => excludeFromResults.Contains(x) == false diff --git a/src/Umbraco.Core/Exceptions/InvalidCompositionException.cs b/src/Umbraco.Core/Exceptions/InvalidCompositionException.cs index 9bc51d7b6e..0e67061902 100644 --- a/src/Umbraco.Core/Exceptions/InvalidCompositionException.cs +++ b/src/Umbraco.Core/Exceptions/InvalidCompositionException.cs @@ -35,7 +35,7 @@ public class InvalidCompositionException : Exception /// The added composition alias. /// The property type aliases. public InvalidCompositionException(string contentTypeAlias, string? addedCompositionAlias, string[] propertyTypeAliases) - : this(contentTypeAlias, addedCompositionAlias, propertyTypeAliases, new string[0]) + : this(contentTypeAlias, addedCompositionAlias, propertyTypeAliases, []) { } diff --git a/src/Umbraco.Core/Extensions/IntExtensions.cs b/src/Umbraco.Core/Extensions/IntExtensions.cs index d347993dd0..6bdb3c6435 100644 --- a/src/Umbraco.Core/Extensions/IntExtensions.cs +++ b/src/Umbraco.Core/Extensions/IntExtensions.cs @@ -27,8 +27,8 @@ public static class IntExtensions /// public static Guid ToGuid(this int value) { - var bytes = new byte[16]; - BitConverter.GetBytes(value).CopyTo(bytes, 0); + Span bytes = stackalloc byte[16]; + BitConverter.GetBytes(value).CopyTo(bytes); return new Guid(bytes); } } diff --git a/src/Umbraco.Core/Extensions/StringExtensions.cs b/src/Umbraco.Core/Extensions/StringExtensions.cs index 3d1ea4f83e..b7f066330c 100644 --- a/src/Umbraco.Core/Extensions/StringExtensions.cs +++ b/src/Umbraco.Core/Extensions/StringExtensions.cs @@ -152,14 +152,16 @@ public static class StringExtensions public static string ReplaceNonAlphanumericChars(this string input, char replacement) { - var inputArray = input.ToCharArray(); - var outputArray = new char[input.Length]; - for (var i = 0; i < inputArray.Length; i++) + var chars = input.ToCharArray(); + for (var i = 0; i < chars.Length; i++) { - outputArray[i] = char.IsLetterOrDigit(inputArray[i]) ? inputArray[i] : replacement; + if (!char.IsLetterOrDigit(chars[i])) + { + chars[i] = replacement; + } } - return new string(outputArray); + return new string(chars); } /// @@ -209,7 +211,7 @@ public static class StringExtensions var nonEmpty = queryStrings.Where(x => !x.IsNullOrWhiteSpace()).ToArray(); - if (url.Contains("?")) + if (url.Contains('?')) { return url + string.Join("&", nonEmpty).EnsureStartsWith('&'); } @@ -692,7 +694,7 @@ public static class StringExtensions if (input.Length == 0) { - return Array.Empty(); + return []; } // calc array size - must be groups of 4 @@ -807,7 +809,7 @@ public static class StringExtensions } // replace chars that would cause problems in URLs - var chArray = new char[pos]; + Span chArray = pos <= 1024 ? stackalloc char[pos] : new char[pos]; for (var i = 0; i < pos; i++) { var ch = str[i]; @@ -1293,8 +1295,7 @@ public static class StringExtensions } // most bytes from the hash are copied straight to the bytes of the new GUID (steps 5-7, 9, 11-12) - var newGuid = new byte[16]; - Array.Copy(hash, 0, newGuid, 0, 16); + Span newGuid = hash.AsSpan()[..16]; // set the four most significant bits (bits 12 through 15) of the time_hi_and_version field to the appropriate 4-bit version number from Section 4.1.3 (step 8) newGuid[6] = (byte)((newGuid[6] & 0x0F) | (version << 4)); @@ -1308,7 +1309,7 @@ public static class StringExtensions } // Converts a GUID (expressed as a byte array) to/from network order (MSB-first). - internal static void SwapByteOrder(byte[] guid) + internal static void SwapByteOrder(Span guid) { SwapBytes(guid, 0, 3); SwapBytes(guid, 1, 2); @@ -1316,12 +1317,7 @@ public static class StringExtensions SwapBytes(guid, 6, 7); } - private static void SwapBytes(byte[] guid, int left, int right) - { - var temp = guid[left]; - guid[left] = guid[right]; - guid[right] = temp; - } + private static void SwapBytes(Span guid, int left, int right) => (guid[left], guid[right]) = (guid[right], guid[left]); /// /// Checks if a given path is a full path including drive letter diff --git a/src/Umbraco.Core/GuidUtils.cs b/src/Umbraco.Core/GuidUtils.cs index 290f36cdcf..a549283845 100644 --- a/src/Umbraco.Core/GuidUtils.cs +++ b/src/Umbraco.Core/GuidUtils.cs @@ -63,7 +63,7 @@ public static class GuidUtils // a Guid is 3 blocks + 8 bits // so it turns into a 3*8+2 = 26 chars string - var chars = new char[length]; + Span chars = stackalloc char[length]; var i = 0; var j = 0; diff --git a/src/Umbraco.Core/HexEncoder.cs b/src/Umbraco.Core/HexEncoder.cs index b95376646b..b950f42c29 100644 --- a/src/Umbraco.Core/HexEncoder.cs +++ b/src/Umbraco.Core/HexEncoder.cs @@ -28,7 +28,8 @@ public static class HexEncoder public static string Encode(byte[] bytes) { var length = bytes.Length; - var chars = new char[length * 2]; + int charsLength = length * 2; + Span chars = charsLength <= 1024 ? stackalloc char[charsLength] : new char[charsLength]; var index = 0; for (var i = 0; i < length; i++) @@ -38,7 +39,7 @@ public static class HexEncoder chars[index++] = HexLutLo[byteIndex]; } - return new string(chars, 0, chars.Length); + return new string(chars); } /// @@ -54,7 +55,8 @@ public static class HexEncoder public static string Encode(byte[] bytes, char separator, int blockSize, int blockCount) { var length = bytes.Length; - var chars = new char[(length * 2) + blockCount]; + int charsLength = (length * 2) + blockCount; + Span chars = charsLength <= 1024 ? stackalloc char[charsLength] : new char[charsLength]; var count = 0; var size = 0; var index = 0; @@ -80,6 +82,6 @@ public static class HexEncoder count++; } - return new string(chars, 0, chars.Length); + return new string(chars); } } diff --git a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs index b46c1405e3..581cd168a3 100644 --- a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs +++ b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs @@ -306,7 +306,7 @@ namespace Umbraco.Cms.Core.Strings return text; } - private string RemoveSurrogatePairs(string text) + private static string RemoveSurrogatePairs(string text) { var input = text.AsSpan(); Span output = input.Length <= 1024 ? stackalloc char[input.Length] : new char[text.Length]; @@ -622,7 +622,8 @@ namespace Umbraco.Cms.Core.Strings } var input = text.ToCharArray(); - var output = new char[input.Length * 2]; + int outputLength = input.Length * 2; + Span output = outputLength <= 1024 ? stackalloc char[outputLength] : new char[outputLength]; var opos = 0; var a = input.Length > 0 ? input[0] : char.MinValue; var upos = char.IsUpper(a) ? 1 : 0; @@ -666,7 +667,7 @@ namespace Umbraco.Cms.Core.Strings output[opos++] = a; } - return new string(output, 0, opos); + return new string(output[..opos]); } #endregion diff --git a/src/Umbraco.Core/Strings/Utf8ToAsciiConverter.cs b/src/Umbraco.Core/Strings/Utf8ToAsciiConverter.cs index 74bc2fa9e8..c4bdcba2d9 100644 --- a/src/Umbraco.Core/Strings/Utf8ToAsciiConverter.cs +++ b/src/Umbraco.Core/Strings/Utf8ToAsciiConverter.cs @@ -50,11 +50,10 @@ public static class Utf8ToAsciiConverter // this is faster although it uses more memory // but... we should be filtering short strings only... - var output = new char[input.Length * 3]; // *3 because of things such as OE + int outputLength = input.Length * 3; // *3 because of things such as OE + Span output = outputLength <= 1024 ? stackalloc char[outputLength] : new char[outputLength]; var len = ToAscii(input, output, fail); - var array = new char[len]; - Array.Copy(output, array, len); - return array; + return output[..len].ToArray(); // var temp = new StringBuilder(input.Length + 16); // default is 16, start with at least input length + little extra // ToAscii(input, temp);