diff --git a/.editorconfig b/.editorconfig index 06e698247..0e4883082 100644 --- a/.editorconfig +++ b/.editorconfig @@ -368,8 +368,6 @@ csharp_style_throw_expression = true:suggestion csharp_style_unused_value_expression_statement_preference = discard_variable:silent csharp_style_unused_value_assignment_preference = discard_variable:suggestion -csharp_style_var_for_built_in_types = false:silent +csharp_style_var_for_built_in_types = never csharp_style_var_when_type_is_apparent = true:warning csharp_style_var_elsewhere = false:warning - -csharp_prefer_simple_using_statement = false:silent diff --git a/ImageSharp.sln b/ImageSharp.sln index 40878c575..f1d4afef4 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -334,6 +334,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Tests.ProfilingS EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution + shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{2aa31a1f-142c-43f4-8687-09abca4b3a26}*SharedItemsImports = 5 shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{68a8cc40-6aed-4e96-b524-31b1158fdeea}*SharedItemsImports = 13 EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/shared-infrastructure b/shared-infrastructure index 36b2d55f5..8dfef29f1 160000 --- a/shared-infrastructure +++ b/shared-infrastructure @@ -1 +1 @@ -Subproject commit 36b2d55f5bb0d91024955bd26ba220ee41cc96e5 +Subproject commit 8dfef29f1838da76be9596f1a2f1be6d93e453d3 diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 2c13e469f..a78a75d42 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -34,4 +34,6 @@ + + diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index 68d4f8949..055a6803e 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -52,4 +52,37 @@ + + + + + + + + + + + + + + + + + diff --git a/src/ImageSharp/Common/Extensions/StreamExtensions.cs b/src/ImageSharp/Common/Extensions/StreamExtensions.cs index 971bff322..5d8668257 100644 --- a/src/ImageSharp/Common/Extensions/StreamExtensions.cs +++ b/src/ImageSharp/Common/Extensions/StreamExtensions.cs @@ -2,9 +2,11 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers; using System.IO; using SixLabors.ImageSharp.Memory; +#if !SUPPORTS_SPAN_STREAM +using System.Buffers; +#endif namespace SixLabors.ImageSharp { diff --git a/src/ImageSharp/Common/Helpers/Guard.Numeric.cs b/src/ImageSharp/Common/Helpers/Guard.Numeric.cs new file mode 100644 index 000000000..72262e0b9 --- /dev/null +++ b/src/ImageSharp/Common/Helpers/Guard.Numeric.cs @@ -0,0 +1,1154 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors +{ + /// + /// Provides methods to protect against invalid parameters. + /// + internal static partial class Guard + { + /// + /// Ensures that the specified value is less than a maximum value. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThan(byte value, byte max, string parameterName) + { + if (value >= max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is less than or equal to a maximum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThanOrEqualTo(byte value, byte max, string parameterName) + { + if (value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThan(byte value, byte min, string parameterName) + { + if (value <= min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThanOrEqualTo(byte value, byte min, string parameterName) + { + if (value < min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value and less than + /// or equal to a maximum value and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value of greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeBetweenOrEqualTo(byte value, byte min, byte max, string parameterName) + { + if (value < min || value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); + } + } + + /// + /// Ensures that the specified value is less than a maximum value. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThan(sbyte value, sbyte max, string parameterName) + { + if (value >= max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is less than or equal to a maximum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThanOrEqualTo(sbyte value, sbyte max, string parameterName) + { + if (value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThan(sbyte value, sbyte min, string parameterName) + { + if (value <= min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThanOrEqualTo(sbyte value, sbyte min, string parameterName) + { + if (value < min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value and less than + /// or equal to a maximum value and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value of greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeBetweenOrEqualTo(sbyte value, sbyte min, sbyte max, string parameterName) + { + if (value < min || value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); + } + } + + /// + /// Ensures that the specified value is less than a maximum value. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThan(short value, short max, string parameterName) + { + if (value >= max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is less than or equal to a maximum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThanOrEqualTo(short value, short max, string parameterName) + { + if (value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThan(short value, short min, string parameterName) + { + if (value <= min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThanOrEqualTo(short value, short min, string parameterName) + { + if (value < min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value and less than + /// or equal to a maximum value and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value of greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeBetweenOrEqualTo(short value, short min, short max, string parameterName) + { + if (value < min || value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); + } + } + + /// + /// Ensures that the specified value is less than a maximum value. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThan(ushort value, ushort max, string parameterName) + { + if (value >= max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is less than or equal to a maximum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThanOrEqualTo(ushort value, ushort max, string parameterName) + { + if (value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThan(ushort value, ushort min, string parameterName) + { + if (value <= min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThanOrEqualTo(ushort value, ushort min, string parameterName) + { + if (value < min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value and less than + /// or equal to a maximum value and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value of greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeBetweenOrEqualTo(ushort value, ushort min, ushort max, string parameterName) + { + if (value < min || value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); + } + } + + /// + /// Ensures that the specified value is less than a maximum value. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThan(char value, char max, string parameterName) + { + if (value >= max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is less than or equal to a maximum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThanOrEqualTo(char value, char max, string parameterName) + { + if (value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThan(char value, char min, string parameterName) + { + if (value <= min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThanOrEqualTo(char value, char min, string parameterName) + { + if (value < min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value and less than + /// or equal to a maximum value and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value of greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeBetweenOrEqualTo(char value, char min, char max, string parameterName) + { + if (value < min || value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); + } + } + + /// + /// Ensures that the specified value is less than a maximum value. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThan(int value, int max, string parameterName) + { + if (value >= max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is less than or equal to a maximum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThanOrEqualTo(int value, int max, string parameterName) + { + if (value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThan(int value, int min, string parameterName) + { + if (value <= min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThanOrEqualTo(int value, int min, string parameterName) + { + if (value < min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value and less than + /// or equal to a maximum value and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value of greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeBetweenOrEqualTo(int value, int min, int max, string parameterName) + { + if (value < min || value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); + } + } + + /// + /// Ensures that the specified value is less than a maximum value. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThan(uint value, uint max, string parameterName) + { + if (value >= max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is less than or equal to a maximum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThanOrEqualTo(uint value, uint max, string parameterName) + { + if (value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThan(uint value, uint min, string parameterName) + { + if (value <= min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThanOrEqualTo(uint value, uint min, string parameterName) + { + if (value < min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value and less than + /// or equal to a maximum value and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value of greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeBetweenOrEqualTo(uint value, uint min, uint max, string parameterName) + { + if (value < min || value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); + } + } + + /// + /// Ensures that the specified value is less than a maximum value. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThan(float value, float max, string parameterName) + { + if (value >= max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is less than or equal to a maximum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThanOrEqualTo(float value, float max, string parameterName) + { + if (value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThan(float value, float min, string parameterName) + { + if (value <= min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThanOrEqualTo(float value, float min, string parameterName) + { + if (value < min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value and less than + /// or equal to a maximum value and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value of greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeBetweenOrEqualTo(float value, float min, float max, string parameterName) + { + if (value < min || value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); + } + } + + /// + /// Ensures that the specified value is less than a maximum value. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThan(long value, long max, string parameterName) + { + if (value >= max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is less than or equal to a maximum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThanOrEqualTo(long value, long max, string parameterName) + { + if (value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThan(long value, long min, string parameterName) + { + if (value <= min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThanOrEqualTo(long value, long min, string parameterName) + { + if (value < min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value and less than + /// or equal to a maximum value and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value of greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeBetweenOrEqualTo(long value, long min, long max, string parameterName) + { + if (value < min || value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); + } + } + + /// + /// Ensures that the specified value is less than a maximum value. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThan(ulong value, ulong max, string parameterName) + { + if (value >= max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is less than or equal to a maximum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThanOrEqualTo(ulong value, ulong max, string parameterName) + { + if (value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThan(ulong value, ulong min, string parameterName) + { + if (value <= min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThanOrEqualTo(ulong value, ulong min, string parameterName) + { + if (value < min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value and less than + /// or equal to a maximum value and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value of greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeBetweenOrEqualTo(ulong value, ulong min, ulong max, string parameterName) + { + if (value < min || value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); + } + } + + /// + /// Ensures that the specified value is less than a maximum value. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThan(double value, double max, string parameterName) + { + if (value >= max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is less than or equal to a maximum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThanOrEqualTo(double value, double max, string parameterName) + { + if (value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThan(double value, double min, string parameterName) + { + if (value <= min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThanOrEqualTo(double value, double min, string parameterName) + { + if (value < min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value and less than + /// or equal to a maximum value and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value of greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeBetweenOrEqualTo(double value, double min, double max, string parameterName) + { + if (value < min || value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); + } + } + + /// + /// Ensures that the specified value is less than a maximum value. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThan(decimal value, decimal max, string parameterName) + { + if (value >= max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is less than or equal to a maximum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeLessThanOrEqualTo(decimal value, decimal max, string parameterName) + { + if (value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThan(decimal value, decimal min, string parameterName) + { + if (value <= min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value + /// and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeGreaterThanOrEqualTo(decimal value, decimal min, string parameterName) + { + if (value < min) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); + } + } + + /// + /// Verifies that the specified value is greater than or equal to a minimum value and less than + /// or equal to a maximum value and throws an exception if it is not. + /// + /// The target value, which should be validated. + /// The minimum value. + /// The maximum value. + /// The name of the parameter that is to be checked. + /// + /// is less than the minimum value of greater than the maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MustBeBetweenOrEqualTo(decimal value, decimal min, decimal max, string parameterName) + { + if (value < min || value > max) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); + } + } + } +} diff --git a/src/ImageSharp/Common/Helpers/Guard.cs b/src/ImageSharp/Common/Helpers/Guard.cs index 1d215d286..3ab1b199a 100644 --- a/src/ImageSharp/Common/Helpers/Guard.cs +++ b/src/ImageSharp/Common/Helpers/Guard.cs @@ -22,7 +22,7 @@ namespace SixLabors { if (!value.GetType().GetTypeInfo().IsValueType) { - ThrowArgumentException("Type must be a struct.", parameterName); + ThrowHelper.ThrowArgumentException("Type must be a struct.", parameterName); } } } diff --git a/src/ImageSharp/Formats/Gif/GifConstants.cs b/src/ImageSharp/Formats/Gif/GifConstants.cs index c9d631da0..06c4b3fc6 100644 --- a/src/ImageSharp/Formats/Gif/GifConstants.cs +++ b/src/ImageSharp/Formats/Gif/GifConstants.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Collections.Generic; using System.Text; @@ -21,11 +22,6 @@ namespace SixLabors.ImageSharp.Formats.Gif /// public const string FileVersion = "89a"; - /// - /// The ASCII encoded bytes used to identify the GIF file. - /// - internal static readonly byte[] MagicNumber = Encoding.ASCII.GetBytes(FileType + FileVersion); - /// /// The extension block introducer !. /// @@ -51,11 +47,6 @@ namespace SixLabors.ImageSharp.Formats.Gif /// public const string NetscapeApplicationIdentification = "NETSCAPE2.0"; - /// - /// The ASCII encoded application identification bytes. - /// - internal static readonly byte[] NetscapeApplicationIdentificationBytes = Encoding.ASCII.GetBytes(NetscapeApplicationIdentification); - /// /// The Netscape looping application sub block size. /// @@ -110,5 +101,25 @@ namespace SixLabors.ImageSharp.Formats.Gif /// The collection of file extensions that equate to a Gif. /// public static readonly IEnumerable FileExtensions = new[] { "gif" }; + + /// + /// Gets the ASCII encoded bytes used to identify the GIF file (combining and ). + /// + internal static ReadOnlySpan MagicNumber => new[] + { + (byte)'G', (byte)'I', (byte)'F', + (byte)'8', (byte)'9', (byte)'a' + }; + + /// + /// Gets the ASCII encoded application identification bytes (representing ). + /// + internal static ReadOnlySpan NetscapeApplicationIdentificationBytes => new[] + { + (byte)'N', (byte)'E', (byte)'T', + (byte)'S', (byte)'C', (byte)'A', + (byte)'P', (byte)'E', + (byte)'2', (byte)'.', (byte)'0' + }; } } diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index dcd0be34b..a591eaf3b 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -240,7 +240,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// The stream to write to. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteHeader(Stream stream) => stream.Write(GifConstants.MagicNumber, 0, GifConstants.MagicNumber.Length); + private void WriteHeader(Stream stream) => stream.Write(GifConstants.MagicNumber); /// /// Writes the logical screen descriptor to the stream. diff --git a/src/ImageSharp/Formats/Gif/Sections/GifNetscapeLoopingApplicationExtension.cs b/src/ImageSharp/Formats/Gif/Sections/GifNetscapeLoopingApplicationExtension.cs index 8af5b81c2..5e26370ba 100644 --- a/src/ImageSharp/Formats/Gif/Sections/GifNetscapeLoopingApplicationExtension.cs +++ b/src/ImageSharp/Formats/Gif/Sections/GifNetscapeLoopingApplicationExtension.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Gif buffer[0] = GifConstants.ApplicationBlockSize; // Write NETSCAPE2.0 - GifConstants.NetscapeApplicationIdentificationBytes.AsSpan().CopyTo(buffer.Slice(1, 11)); + GifConstants.NetscapeApplicationIdentificationBytes.CopyTo(buffer.Slice(1, 11)); // Application Data ---- buffer[12] = 3; // Application block length (always 3) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs index 54633a5d7..87b486ea6 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs @@ -1,8 +1,7 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; -using System.Text; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { @@ -12,24 +11,38 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder internal static class ProfileResolver { /// - /// Describes the JFIF specific markers. + /// Gets the JFIF specific markers. /// - public static readonly byte[] JFifMarker = Encoding.ASCII.GetBytes("JFIF\0"); + public static ReadOnlySpan JFifMarker => new[] + { + (byte)'J', (byte)'F', (byte)'I', (byte)'F', (byte)'\0' + }; /// - /// Describes the ICC specific markers. + /// Gets the ICC specific markers. /// - public static readonly byte[] IccMarker = Encoding.ASCII.GetBytes("ICC_PROFILE\0"); + public static ReadOnlySpan IccMarker => new[] + { + (byte)'I', (byte)'C', (byte)'C', (byte)'_', + (byte)'P', (byte)'R', (byte)'O', (byte)'F', + (byte)'I', (byte)'L', (byte)'E', (byte)'\0' + }; /// - /// Describes the EXIF specific markers. + /// Gets the EXIF specific markers. /// - public static readonly byte[] ExifMarker = Encoding.ASCII.GetBytes("Exif\0\0"); + public static ReadOnlySpan ExifMarker => new[] + { + (byte)'E', (byte)'x', (byte)'i', (byte)'f', (byte)'\0', (byte)'\0' + }; /// - /// Describes Adobe specific markers . + /// Gets the Adobe specific markers . /// - public static readonly byte[] AdobeMarker = Encoding.ASCII.GetBytes("Adobe"); + public static ReadOnlySpan AdobeMarker => new[] + { + (byte)'A', (byte)'d', (byte)'o', (byte)'b', (byte)'e' + }; /// /// Returns a value indicating whether the passed bytes are a match to the profile identifier. @@ -43,4 +56,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder && bytesToCheck.Slice(0, profileIdentifier.Length).SequenceEqual(profileIdentifier); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs b/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs index 059e2052b..669abad28 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs @@ -34,12 +34,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components public fixed byte Data[Size]; /// - /// Unzig maps from the zigzag ordering to the natural ordering. For example, - /// unzig[3] is the column and row of the fourth element in zigzag order. The - /// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2). + /// Gets the unzigs map, which maps from the zigzag ordering to the natural ordering. + /// For example, unzig[3] is the column and row of the fourth element in zigzag order. + /// The value is 16, which means first column (16%8 == 0) and third row (16/8 == 2). /// - private static readonly byte[] Unzig = - new byte[Size] + private static ReadOnlySpan Unzig => new byte[] { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, @@ -75,8 +74,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components public static ZigZag CreateUnzigTable() { ZigZag result = default; - byte* unzigPtr = result.Data; - Marshal.Copy(Unzig, 0, (IntPtr)unzigPtr, Size); + ref byte sourceRef = ref MemoryMarshal.GetReference(Unzig); + ref byte destinationRef = ref Unsafe.AsRef(result.Data); + + Unzig.CopyTo(new Span(result.Data, Size)); + return result; } diff --git a/src/ImageSharp/Formats/Png/PngConstants.cs b/src/ImageSharp/Formats/Png/PngConstants.cs index 632460ec4..247bb3c75 100644 --- a/src/ImageSharp/Formats/Png/PngConstants.cs +++ b/src/ImageSharp/Formats/Png/PngConstants.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Collections.Generic; using System.Text; @@ -36,21 +37,6 @@ namespace SixLabors.ImageSharp.Formats.Png /// public static readonly IEnumerable FileExtensions = new[] { "png" }; - /// - /// The header bytes identifying a Png. - /// - public static readonly byte[] HeaderBytes = - { - 0x89, // Set the high bit. - 0x50, // P - 0x4E, // N - 0x47, // G - 0x0D, // Line ending CRLF - 0x0A, // Line ending CRLF - 0x1A, // EOF - 0x0A // LF - }; - /// /// The header bytes as a big-endian coded ulong. /// @@ -77,5 +63,20 @@ namespace SixLabors.ImageSharp.Formats.Png /// The minimum length of a keyword in a text chunk is 1 byte. /// public const int MinTextKeywordLength = 1; + + /// + /// Gets the header bytes identifying a Png. + /// + public static ReadOnlySpan HeaderBytes => new byte[] + { + 0x89, // Set the high bit. + 0x50, // P + 0x4E, // N + 0x47, // G + 0x0D, // Line ending CRLF + 0x0A, // Line ending CRLF + 0x1A, // EOF + 0x0A // LF + }; } } diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 8dbfc25d7..2d8d66c33 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -149,7 +149,7 @@ namespace SixLabors.ImageSharp.Formats.Png IndexedImageFrame quantized = PngEncoderOptionsHelpers.CreateQuantizedFrame(this.options, image); this.bitDepth = PngEncoderOptionsHelpers.CalculateBitDepth(this.options, image, quantized); - stream.Write(PngConstants.HeaderBytes, 0, PngConstants.HeaderBytes.Length); + stream.Write(PngConstants.HeaderBytes); this.WriteHeaderChunk(stream); this.WritePaletteChunk(stream, quantized); diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs b/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs index e8dd8a520..543a1fe30 100644 --- a/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs +++ b/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs @@ -36,12 +36,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib private const int EofSymbol = 256; - // The lengths of the bit length codes are sent in order of decreasing - // probability, to avoid transmitting the lengths for unused bit length codes. - private static readonly int[] BitLengthOrder = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - - private static readonly byte[] Bit4Reverse = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 }; - private static readonly short[] StaticLCodes; private static readonly byte[] StaticLLength; private static readonly short[] StaticDCodes; @@ -128,6 +122,13 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib this.pinnedLiteralBuffer = (short*)this.literalBufferHandle.Pointer; } + /// + /// Gets the lengths of the bit length codes are sent in order of decreasing probability, to avoid transmitting the lengths for unused bit length codes. + /// + private static ReadOnlySpan BitLengthOrder => new byte[] { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + + private static ReadOnlySpan Bit4Reverse => new byte[] { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 }; + /// /// Gets the pending buffer to use. /// @@ -158,6 +159,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib this.Pending.WriteBits(this.literalTree.NumCodes - 257, 5); this.Pending.WriteBits(this.distTree.NumCodes - 1, 5); this.Pending.WriteBits(blTreeCodes - 4, 4); + for (int rank = 0; rank < blTreeCodes; rank++) { this.Pending.WriteBits(this.blTree.Length[BitLengthOrder[rank]], 3); @@ -250,6 +252,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib this.blTree.BuildTree(); int blTreeCodes = 4; + for (int i = 18; i > blTreeCodes; i--) { if (this.blTree.Length[BitLengthOrder[i]] > 0) @@ -363,10 +366,30 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib [MethodImpl(InliningOptions.ShortMethod)] public static short BitReverse(int toReverse) { - return (short)(Bit4Reverse[toReverse & 0xF] << 12 - | Bit4Reverse[(toReverse >> 4) & 0xF] << 8 - | Bit4Reverse[(toReverse >> 8) & 0xF] << 4 - | Bit4Reverse[toReverse >> 12]); + /* Use unsafe offsetting and manually validate the input index to reduce the + * total number of conditional branches. There are two main cases to test here: + * 1. In the first 3, the input value (or some combination of it) is combined + * with & 0xF, which results in a maximum value of 0xF no matter what the + * input value was. That is 15, which is always in range for the target span. + * As a result, no input validation is needed at all in this case. + * 2. There are two cases where the input value might cause an invalid access: + * when it is either negative, or greater than 15 << 12. We can test both + * conditions in a single pass by casting the input value to uint and right + * shifting it by 12, which also preserves the sign. If it is a negative + * value (2-complement), the test will fail as the uint cast will result + * in a much larger value. If the value was simply too high, the test will + * fail as expected. We can't simply check whether the value is lower than + * 15 << 12, because higher values are acceptable in the first 3 accesses. + * Doing this reduces the total number of index checks from 4 down to just 1. */ + int toReverseRightShiftBy12 = toReverse >> 12; + Guard.MustBeLessThanOrEqualTo((uint)toReverseRightShiftBy12, 15, nameof(toReverse)); + + ref byte bit4ReverseRef = ref MemoryMarshal.GetReference(Bit4Reverse); + + return (short)(Unsafe.Add(ref bit4ReverseRef, toReverse & 0xF) << 12 + | Unsafe.Add(ref bit4ReverseRef, (toReverse >> 4) & 0xF) << 8 + | Unsafe.Add(ref bit4ReverseRef, (toReverse >> 8) & 0xF) << 4 + | Unsafe.Add(ref bit4ReverseRef, toReverseRightShiftBy12)); } /// diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index be0e9032b..24d4f4a00 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -37,6 +37,16 @@ + + + True + True + Guard.Numeric.tt + + True True @@ -202,6 +212,10 @@ DefaultPixelBlenders.Generated.cs TextTemplatingFileGenerator + + Guard.Numeric.cs + TextTemplatingFileGenerator + @@ -209,5 +223,4 @@ - diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs index c7112c47a..c58b224e4 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs @@ -1,11 +1,13 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; + namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { internal static class ExifConstants { - public static readonly byte[] LittleEndianByteOrderMarker = + public static ReadOnlySpan LittleEndianByteOrderMarker => new byte[] { (byte)'I', (byte)'I', @@ -13,7 +15,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif 0x00, }; - public static readonly byte[] BigEndianByteOrderMarker = + public static ReadOnlySpan BigEndianByteOrderMarker => new byte[] { (byte)'M', (byte)'M', @@ -21,4 +23,4 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif 0x2A }; } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs index c068461b2..b00813730 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs @@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif int i = 0; // The byte order marker for little-endian, followed by the number 42 and a 0 - ExifConstants.LittleEndianByteOrderMarker.AsSpan().CopyTo(result.AsSpan(start: i)); + ExifConstants.LittleEndianByteOrderMarker.CopyTo(result.AsSpan(start: i)); i += ExifConstants.LittleEndianByteOrderMarker.Length; uint ifdOffset = ((uint)i - startIndex) + 4U; diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs index 94127432b..8184f1577 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs @@ -13,7 +13,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders - /// /// Returns the result of the "NormalSrc" compositing equation. /// @@ -419,7 +418,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return dest; } - /// /// Returns the result of the "MultiplySrc" compositing equation. /// @@ -825,7 +823,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return dest; } - /// /// Returns the result of the "AddSrc" compositing equation. /// @@ -1231,7 +1228,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return dest; } - /// /// Returns the result of the "SubtractSrc" compositing equation. /// @@ -1637,7 +1633,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return dest; } - /// /// Returns the result of the "ScreenSrc" compositing equation. /// @@ -2043,7 +2038,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return dest; } - /// /// Returns the result of the "DarkenSrc" compositing equation. /// @@ -2449,7 +2443,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return dest; } - /// /// Returns the result of the "LightenSrc" compositing equation. /// @@ -2855,7 +2848,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return dest; } - /// /// Returns the result of the "OverlaySrc" compositing equation. /// @@ -3261,7 +3253,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return dest; } - /// /// Returns the result of the "HardLightSrc" compositing equation. /// diff --git a/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs index 433ac4567..ce2e406d4 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs @@ -139,14 +139,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// private sealed class Octree { - /// - /// Mask used when getting the appropriate pixels for a given node. - /// - private static readonly byte[] Mask = new byte[] - { - 0b10000000, 0b1000000, 0b100000, 0b10000, 0b1000, 0b100, 0b10, 0b1 - }; - /// /// The root of the Octree /// @@ -183,6 +175,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization this.previousNode = null; } + /// + /// Gets the mask used when getting the appropriate pixels for a given node. + /// + private static ReadOnlySpan Mask => new byte[] + { + 0b10000000, + 0b1000000, + 0b100000, + 0b10000, + 0b1000, + 0b100, + 0b10, + 0b1 + }; + /// /// Gets or sets the number of leaves in the tree /// @@ -530,8 +537,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization [MethodImpl(InliningOptions.ShortMethod)] private static int GetColorIndex(ref Rgba32 color, int level) { + DebugGuard.MustBeLessThan(level, Mask.Length, nameof(level)); + int shift = 7 - level; - byte mask = Mask[level]; + ref byte maskRef = ref MemoryMarshal.GetReference(Mask); + byte mask = Unsafe.Add(ref maskRef, level); + return ((color.R & mask) >> shift) | ((color.G & mask) >> (shift - 1)) | ((color.B & mask) >> (shift - 2)); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ProfileResolverTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/ProfileResolverTests.cs index c908abc50..fb09065b0 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/ProfileResolverTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/ProfileResolverTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Text; @@ -19,25 +19,25 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [Fact] public void ProfileResolverHasCorrectJFifMarker() { - Assert.Equal(JFifMarker, ProfileResolver.JFifMarker); + Assert.Equal(JFifMarker, ProfileResolver.JFifMarker.ToArray()); } [Fact] public void ProfileResolverHasCorrectExifMarker() { - Assert.Equal(ExifMarker, ProfileResolver.ExifMarker); + Assert.Equal(ExifMarker, ProfileResolver.ExifMarker.ToArray()); } [Fact] public void ProfileResolverHasCorrectIccMarker() { - Assert.Equal(IccMarker, ProfileResolver.IccMarker); + Assert.Equal(IccMarker, ProfileResolver.IccMarker.ToArray()); } [Fact] public void ProfileResolverHasCorrectAdobeMarker() { - Assert.Equal(AdobeMarker, ProfileResolver.AdobeMarker); + Assert.Equal(AdobeMarker, ProfileResolver.AdobeMarker.ToArray()); } [Fact] @@ -76,4 +76,4 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Assert.False(ProfileResolver.IsProfile(AdobeMarker, ProfileResolver.IccMarker)); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs index 0b2274581..9ea6a305c 100644 --- a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs +++ b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Tests this.Collection.AddFrame((ImageFrame)null); }); - Assert.StartsWith("Value cannot be null.", ex.Message); + Assert.StartsWith("Parameter \"frame\" must be not null.", ex.Message); } [Fact] @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests this.Collection.AddFrame(data); }); - Assert.StartsWith("Value cannot be null.", ex.Message); + Assert.StartsWith("Parameter \"source\" must be not null.", ex.Message); } [Fact] @@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.Tests this.Collection.AddFrame(new Rgba32[0]); }); - Assert.StartsWith("Value 0 must be greater than or equal to 100.", ex.Message); + Assert.StartsWith($"Parameter \"data\" ({typeof(int)}) must be greater than or equal to {100}, was {0}", ex.Message); } [Fact] @@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Tests this.Collection.InsertFrame(1, null); }); - Assert.StartsWith("Value cannot be null.", ex.Message); + Assert.StartsWith("Parameter \"frame\" must be not null.", ex.Message); } [Fact] diff --git a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs index b0008d394..08e6f8e1f 100644 --- a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs +++ b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs @@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Tests this.Collection.AddFrame(null); }); - Assert.StartsWith("Value cannot be null.", ex.Message); + Assert.StartsWith("Parameter \"source\" must be not null.", ex.Message); } [Fact] @@ -114,7 +114,7 @@ namespace SixLabors.ImageSharp.Tests this.Collection.InsertFrame(1, null); }); - Assert.StartsWith("Value cannot be null.", ex.Message); + Assert.StartsWith("Parameter \"source\" must be not null.", ex.Message); } [Fact] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs index 79bf76545..7069b0346 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs @@ -394,7 +394,7 @@ namespace SixLabors.ImageSharp.Tests public void ProfileToByteArray() { // Arrange - byte[] exifBytesWithoutExifCode = ExifConstants.LittleEndianByteOrderMarker; + byte[] exifBytesWithoutExifCode = ExifConstants.LittleEndianByteOrderMarker.ToArray(); ExifProfile expectedProfile = CreateExifProfile(); var expectedProfileTags = expectedProfile.Values.Select(x => x.Tag).ToList();