#if !NET6_0_OR_GREATER // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // Some routines inspired by the Stanford Bit Twiddling Hacks by Sean Eron Anderson: // http://graphics.stanford.edu/~seander/bithacks.html namespace System.Numerics { /// /// Utility methods for intrinsic bit-twiddling operations. /// The methods use hardware intrinsics when available on the underlying platform, /// otherwise they use optimized software fallbacks. /// internal static class BitOperations { // C# no-alloc optimization that directly wraps the data section of the dll (similar to string constants) // https://github.com/dotnet/roslyn/pull/24621 private static ReadOnlySpan TrailingZeroCountDeBruijn => new byte[32] { 00, 01, 28, 02, 29, 14, 24, 03, 30, 22, 20, 15, 25, 17, 04, 08, 31, 27, 13, 23, 21, 19, 16, 07, 26, 12, 18, 06, 11, 05, 10, 09 }; private static ReadOnlySpan Log2DeBruijn => new byte[32] { 00, 09, 01, 10, 13, 21, 02, 29, 11, 14, 16, 18, 22, 25, 03, 30, 08, 12, 20, 28, 15, 17, 24, 07, 19, 27, 23, 06, 26, 05, 04, 31 }; /// /// Count the number of leading zero bits in a mask. /// Similar in behavior to the x86 instruction LZCNT. /// /// The value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LeadingZeroCount(uint value) { // Unguarded fallback contract is 0->31 if (value == 0) { return 32; } return 31 - Log2SoftwareFallback(value); } /// /// Count the number of leading zero bits in a mask. /// Similar in behavior to the x86 instruction LZCNT. /// /// The value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LeadingZeroCount(ulong value) { uint hi = (uint)(value >> 32); if (hi == 0) { return 32 + LeadingZeroCount((uint)value); } return LeadingZeroCount(hi); } /// /// Returns the integer (floor) log of the specified value, base 2. /// Note that by convention, input value 0 returns 0 since Log(0) is undefined. /// /// The value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Log2(uint value) { // Fallback contract is 0->0 return Log2SoftwareFallback(value); } /// /// Returns the integer (floor) log of the specified value, base 2. /// Note that by convention, input value 0 returns 0 since Log(0) is undefined. /// /// The value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Log2(ulong value) { uint hi = (uint)(value >> 32); if (hi == 0) { return Log2((uint)value); } return 32 + Log2(hi); } /// /// Returns the integer (floor) log of the specified value, base 2. /// Note that by convention, input value 0 returns 0 since Log(0) is undefined. /// Does not directly use any hardware intrinsics, nor does it incur branching. /// /// The value. private static int Log2SoftwareFallback(uint value) { // No AggressiveInlining due to large method size // Has conventional contract 0->0 (Log(0) is undefined) // Fill trailing zeros with ones, eg 00010010 becomes 00011111 value |= value >> 01; value |= value >> 02; value |= value >> 04; value |= value >> 08; value |= value >> 16; // uint.MaxValue >> 27 is always in range [0 - 31] so we use Unsafe.AddByteOffset to avoid bounds check return Unsafe.AddByteOffset( // Using deBruijn sequence, k=2, n=5 (2^5=32) : 0b_0000_0111_1100_0100_1010_1100_1101_1101u ref MemoryMarshal.GetReference(Log2DeBruijn), // uint|long -> IntPtr cast on 32-bit platforms does expensive overflow checks not needed here (IntPtr)(int)((value * 0x07C4ACDDu) >> 27)); } /// /// Returns the population count (number of bits set) of a mask. /// Similar in behavior to the x86 instruction POPCNT. /// /// The value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int PopCount(uint value) { const uint c1 = 0x_55555555u; const uint c2 = 0x_33333333u; const uint c3 = 0x_0F0F0F0Fu; const uint c4 = 0x_01010101u; value -= (value >> 1) & c1; value = (value & c2) + ((value >> 2) & c2); value = (((value + (value >> 4)) & c3) * c4) >> 24; return (int)value; } /// /// Returns the population count (number of bits set) of a mask. /// Similar in behavior to the x86 instruction POPCNT. /// /// The value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int PopCount(ulong value) { if (IntPtr.Size == 4) { return PopCount((uint)value) // lo + PopCount((uint)(value >> 32)); // hi } else { const ulong c1 = 0x_55555555_55555555ul; const ulong c2 = 0x_33333333_33333333ul; const ulong c3 = 0x_0F0F0F0F_0F0F0F0Ful; const ulong c4 = 0x_01010101_01010101ul; value -= (value >> 1) & c1; value = (value & c2) + ((value >> 2) & c2); value = (((value + (value >> 4)) & c3) * c4) >> 56; return (int)value; } } /// /// Count the number of trailing zero bits in an integer value. /// Similar in behavior to the x86 instruction TZCNT. /// /// The value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int TrailingZeroCount(int value) => TrailingZeroCount((uint)value); /// /// Count the number of trailing zero bits in an integer value. /// Similar in behavior to the x86 instruction TZCNT. /// /// The value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int TrailingZeroCount(uint value) { // Unguarded fallback contract is 0->0 if (value == 0) { return 32; } // uint.MaxValue >> 27 is always in range [0 - 31] so we use Unsafe.AddByteOffset to avoid bounds check return Unsafe.AddByteOffset( // Using deBruijn sequence, k=2, n=5 (2^5=32) : 0b_0000_0111_0111_1100_1011_0101_0011_0001u ref MemoryMarshal.GetReference(TrailingZeroCountDeBruijn), // uint|long -> IntPtr cast on 32-bit platforms does expensive overflow checks not needed here (IntPtr)(int)(((value & (uint)-(int)value) * 0x077CB531u) >> 27)); // Multi-cast mitigates redundant conv.u8 } /// /// Count the number of trailing zero bits in a mask. /// Similar in behavior to the x86 instruction TZCNT. /// /// The value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int TrailingZeroCount(long value) => TrailingZeroCount((ulong)value); /// /// Count the number of trailing zero bits in a mask. /// Similar in behavior to the x86 instruction TZCNT. /// /// The value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int TrailingZeroCount(ulong value) { uint lo = (uint)value; if (lo == 0) { return 32 + TrailingZeroCount((uint)(value >> 32)); } return TrailingZeroCount(lo); } /// /// Rotates the specified value left by the specified number of bits. /// Similar in behavior to the x86 instruction ROL. /// /// The value to rotate. /// The number of bits to rotate by. /// Any value outside the range [0..31] is treated as congruent mod 32. /// The rotated value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint RotateLeft(uint value, int offset) => (value << offset) | (value >> (32 - offset)); /// /// Rotates the specified value left by the specified number of bits. /// Similar in behavior to the x86 instruction ROL. /// /// The value to rotate. /// The number of bits to rotate by. /// Any value outside the range [0..63] is treated as congruent mod 64. /// The rotated value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong RotateLeft(ulong value, int offset) => (value << offset) | (value >> (64 - offset)); /// /// Rotates the specified value right by the specified number of bits. /// Similar in behavior to the x86 instruction ROR. /// /// The value to rotate. /// The number of bits to rotate by. /// Any value outside the range [0..31] is treated as congruent mod 32. /// The rotated value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint RotateRight(uint value, int offset) => (value >> offset) | (value << (32 - offset)); /// /// Rotates the specified value right by the specified number of bits. /// Similar in behavior to the x86 instruction ROR. /// /// The value to rotate. /// The number of bits to rotate by. /// Any value outside the range [0..63] is treated as congruent mod 64. /// The rotated value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong RotateRight(ulong value, int offset) => (value >> offset) | (value << (64 - offset)); } } #endif