diff --git a/src/libraries/Common/tests/Tests/System/Collections/Generic/ArrayBuilderTests.cs b/src/libraries/Common/tests/Tests/System/Collections/Generic/ArrayBuilderTests.cs index 5a6dbcd32d1ff1..14a80e17d6af8a 100644 --- a/src/libraries/Common/tests/Tests/System/Collections/Generic/ArrayBuilderTests.cs +++ b/src/libraries/Common/tests/Tests/System/Collections/Generic/ArrayBuilderTests.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Linq; +using System.Numerics; using Xunit; namespace System.Collections.Generic.Tests @@ -205,29 +206,7 @@ private static int CalculateExpectedCapacity(int count) // Then allocate arrays of size 4, 8, 16, etc. count = Math.Max(count, 4); - return NextPowerOfTwo(count); - } - - private static int NextPowerOfTwo(int value) - { - // Taken from https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 - - Debug.Assert(value >= 0); - - // If the number is already a power of 2, we want to round to itself. - value--; - - // Propogate 1-bits right: if the highest bit set is @ position n, - // then all of the bits to the right of position n will become set. - value |= value >> 1; - value |= value >> 2; - value |= value >> 4; - value |= value >> 8; - value |= value >> 16; - - // This yields a number of the form 2^N - 1. - // Add 1 to get a power of 2 with the bit set @ position n + 1. - return value + 1; + return (int)BitOperations.RoundUpToPowerOf2((uint)count); } } diff --git a/src/libraries/System.Buffers/tests/ArrayPool/UnitTests.cs b/src/libraries/System.Buffers/tests/ArrayPool/UnitTests.cs index a917fd3af41c7a..7456e1b46fe68e 100644 --- a/src/libraries/System.Buffers/tests/ArrayPool/UnitTests.cs +++ b/src/libraries/System.Buffers/tests/ArrayPool/UnitTests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics.Tracing; using System.Linq; +using System.Numerics; using System.Threading.Tasks; using Microsoft.DotNet.RemoteExecutor; using Xunit; @@ -278,23 +279,11 @@ public static void CanRentManySizedBuffers(ArrayPool pool) for (int i = 1; i < 10000; i++) { byte[] buffer = pool.Rent(i); - Assert.Equal(i <= 16 ? 16 : RoundUpToPowerOf2(i), buffer.Length); + Assert.Equal(i <= 16 ? 16 : (int)BitOperations.RoundUpToPowerOf2((uint)i), buffer.Length); pool.Return(buffer); } } - private static int RoundUpToPowerOf2(int i) - { - // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 - --i; - i |= i >> 1; - i |= i >> 2; - i |= i >> 4; - i |= i >> 8; - i |= i >> 16; - return i + 1; - } - [Theory] [InlineData(1, 16)] [InlineData(15, 16)] diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs index 826a129be7f854..49fbb26ff4ca48 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs @@ -71,18 +71,55 @@ public static class BitOperations [CLSCompliant(false)] public static bool IsPow2(ulong value) => (value & (value - 1)) == 0 && value != 0; - /// Round the given integral value up to a power of 2. + /// + /// Round the given integral value up to a power of 2. + /// /// The value. - internal static uint RoundUpToPowerOf2(uint value) + /// + /// The smallest power of 2 which is greater than or equal to . + /// If is 0 or the result overflows, returns 0. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static uint RoundUpToPowerOf2(uint value) { - // TODO: https://github.com/dotnet/runtime/issues/43135 - // When this is exposed publicly, decide on the behavior for the boundary cases... - // the accelerated and fallback paths differ. - Debug.Assert(value > 0 && value <= (uint.MaxValue / 2) + 1); - if (Lzcnt.IsSupported || ArmBase.IsSupported || X86Base.IsSupported) { - return 1u << (32 - LeadingZeroCount(value - 1)); +#if TARGET_64BIT + return (uint)(0x1_0000_0000ul >> LeadingZeroCount(value - 1)); +#else + int shift = 32 - LeadingZeroCount(value - 1); + return (1u ^ (uint)(shift >> 5)) << shift; +#endif + } + + // Based on https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + --value; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + return value + 1; + } + + + /// + /// Round the given integral value up to a power of 2. + /// + /// The value. + /// + /// The smallest power of 2 which is greater than or equal to . + /// If is 0 or the result overflows, returns 0. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static ulong RoundUpToPowerOf2(ulong value) + { + if (Lzcnt.X64.IsSupported || ArmBase.Arm64.IsSupported) + { + int shift = 64 - LeadingZeroCount(value - 1); + return (1ul ^ (ulong)(shift >> 6)) << shift; } // Based on https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 @@ -92,6 +129,7 @@ internal static uint RoundUpToPowerOf2(uint value) value |= value >> 4; value |= value >> 8; value |= value >> 16; + value |= value >> 32; return value + 1; } diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Numerics/BitOperationsTests.cs b/src/libraries/System.Runtime.Extensions/tests/System/Numerics/BitOperationsTests.cs index f59d90b7c51450..7836538dbe7480 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Numerics/BitOperationsTests.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Numerics/BitOperationsTests.cs @@ -388,5 +388,53 @@ public static void BitOps_RotateRight_ulong() Assert.Equal(value, BitOperations.RotateRight(value, int.MinValue)); // % 64 = 0 Assert.Equal(BitOperations.RotateLeft(value, 63), BitOperations.RotateRight(value, int.MaxValue)); // % 64 = 63 } + + [Theory] + [InlineData(0u, 0u)] + [InlineData(1u, 1u)] + [InlineData(2u, 2u)] + [InlineData(0x0096u, 0x0100u)] + [InlineData(0x05CDu, 0x0800u)] + [InlineData(0x0932u, 0x1000u)] + [InlineData(0x0004_C911u, 0x0008_0000u)] + [InlineData(0x00E0_A2E2u, 0x0100_0000u)] + [InlineData(0x0988_0713u, 0x1000_0000u)] + [InlineData(0x30A4_9649u, 0x4000_0000u)] + [InlineData(0x7FFF_FFFFu, 0x8000_0000u)] + [InlineData(0x8000_0000u, 0x8000_0000u)] + [InlineData(0x8000_0001u, 0ul)] + [InlineData(0xFFFF_FFFFu, 0ul)] + public static void BitOps_RoundUpToPow2_uint(uint value, uint expected) + { + Assert.Equal(expected, BitOperations.RoundUpToPowerOf2(value)); + } + + [Theory] + [InlineData(0ul, 0ul)] + [InlineData(1ul, 1ul)] + [InlineData(2ul, 2ul)] + [InlineData(0x0096ul, 0x0100ul)] + [InlineData(0x05cdul, 0x0800ul)] + [InlineData(0x0932ul, 0x1000ul)] + [InlineData(0x0004_c911ul, 0x0008_0000ul)] + [InlineData(0x00e0_a2b2ul, 0x0100_0000ul)] + [InlineData(0x0988_0713ul, 0x1000_0000ul)] + [InlineData(0x30a4_9649ul, 0x4000_0000ul)] + [InlineData(0x7FFF_FFFFul, 0x8000_0000ul)] + [InlineData(0x8000_0000ul, 0x8000_0000ul)] + [InlineData(0x8000_0001ul, 0x1_0000_0000ul)] + [InlineData(0xFFFF_FFFFul, 0x1_0000_0000ul)] + [InlineData(0x0000_0003_343B_0D81ul, 0x0000_0004_0000_0000ul)] + [InlineData(0x0000_0D87_5EE2_8F19ul, 0x0000_1000_0000_0000ul)] + [InlineData(0x0006_2A08_4A7A_3A2Dul, 0x0008_0000_0000_0000ul)] + [InlineData(0x0101_BF76_4398_F791ul, 0x0200_0000_0000_0000ul)] + [InlineData(0x7FFF_FFFF_FFFF_FFFFul, 0x8000_0000_0000_0000ul)] + [InlineData(0x8000_0000_0000_0000ul, 0x8000_0000_0000_0000ul)] + [InlineData(0x8000_0000_0000_0001ul, 0ul)] + [InlineData(0xFFFF_FFFF_FFFF_FFFFul, 0ul)] + public static void BitOps_RoundUpToPow2_ulong(ulong value, ulong expected) + { + Assert.Equal(expected, BitOperations.RoundUpToPowerOf2(value)); + } } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 5c77a9476c8bb9..da83529a5da361 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -8196,6 +8196,10 @@ public static partial class BitOperations public static uint RotateRight(uint value, int offset) { throw null; } [System.CLSCompliantAttribute(false)] public static ulong RotateRight(ulong value, int offset) { throw null; } + [System.CLSCompliantAttribute(false)] + public static uint RoundUpToPowerOf2(uint value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static ulong RoundUpToPowerOf2(ulong value) { throw null; } public static int TrailingZeroCount(int value) { throw null; } public static int TrailingZeroCount(long value) { throw null; } [System.CLSCompliantAttribute(false)]