mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-02-15 05:26:01 +00:00
312 lines
11 KiB
C#
312 lines
11 KiB
C#
// Licensed to the .NET Foundation under one or more agreements.
|
|
// The .NET Foundation licenses this file to you under the MIT license.
|
|
|
|
#if !NETCOREAPP3_0_OR_GREATER
|
|
|
|
using System.Runtime.CompilerServices;
|
|
using static SharpCompress.Compressors.ZStandard.UnsafeHelper;
|
|
|
|
// Some routines inspired by the Stanford Bit Twiddling Hacks by Sean Eron Anderson:
|
|
// http://graphics.stanford.edu/~seander/bithacks.html
|
|
|
|
namespace System.Numerics
|
|
{
|
|
/// <summary>
|
|
/// Utility methods for intrinsic bit-twiddling operations.
|
|
/// The methods use hardware intrinsics when available on the underlying platform,
|
|
/// otherwise they use optimized software fallbacks.
|
|
/// </summary>
|
|
public static unsafe class BitOperations
|
|
{
|
|
// hack: should be public because of inline
|
|
public static readonly byte* TrailingZeroCountDeBruijn = GetArrayPointer(
|
|
new byte[]
|
|
{
|
|
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,
|
|
}
|
|
);
|
|
|
|
// hack: should be public because of inline
|
|
public static readonly byte* Log2DeBruijn = GetArrayPointer(
|
|
new byte[]
|
|
{
|
|
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,
|
|
}
|
|
);
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <param name="value">The value.</param>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static int Log2(uint value)
|
|
{
|
|
// The 0->0 contract is fulfilled by setting the LSB to 1.
|
|
// Log(1) is 0, and setting the LSB for values > 1 does not change the log2 result.
|
|
value |= 1;
|
|
|
|
// value lzcnt actual expected
|
|
// ..0001 31 31-31 0
|
|
// ..0010 30 31-30 1
|
|
// 0010.. 2 31-2 29
|
|
// 0100.. 1 31-1 30
|
|
// 1000.. 0 31-0 31
|
|
|
|
// Fallback contract is 0->0
|
|
// 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 Log2DeBruijn[
|
|
// Using deBruijn sequence, k=2, n=5 (2^5=32) : 0b_0000_0111_1100_0100_1010_1100_1101_1101u
|
|
(int)((value * 0x07C4ACDDu) >> 27)
|
|
];
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <param name="value">The value.</param>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static int Log2(ulong value)
|
|
{
|
|
value |= 1;
|
|
|
|
uint hi = (uint)(value >> 32);
|
|
|
|
if (hi == 0)
|
|
{
|
|
return Log2((uint)value);
|
|
}
|
|
|
|
return 32 + Log2(hi);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Count the number of trailing zero bits in an integer value.
|
|
/// Similar in behavior to the x86 instruction TZCNT.
|
|
/// </summary>
|
|
/// <param name="value">The value.</param>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static int TrailingZeroCount(int value) => TrailingZeroCount((uint)value);
|
|
|
|
/// <summary>
|
|
/// Count the number of trailing zero bits in an integer value.
|
|
/// Similar in behavior to the x86 instruction TZCNT.
|
|
/// </summary>
|
|
/// <param name="value">The value.</param>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static int TrailingZeroCount(uint value)
|
|
{
|
|
// Unguarded fallback contract is 0->0, BSF contract is 0->undefined
|
|
if (value == 0)
|
|
{
|
|
return 32;
|
|
}
|
|
|
|
// uint.MaxValue >> 27 is always in range [0 - 31] so we use Unsafe.AddByteOffset to avoid bounds check
|
|
return TrailingZeroCountDeBruijn[
|
|
// Using deBruijn sequence, k=2, n=5 (2^5=32) : 0b_0000_0111_0111_1100_1011_0101_0011_0001u
|
|
(int)(((value & (uint)-(int)value) * 0x077CB531u) >> 27)
|
|
]; // Multi-cast mitigates redundant conv.u8
|
|
}
|
|
|
|
/// <summary>
|
|
/// Count the number of trailing zero bits in a mask.
|
|
/// Similar in behavior to the x86 instruction TZCNT.
|
|
/// </summary>
|
|
/// <param name="value">The value.</param>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static int TrailingZeroCount(long value) => TrailingZeroCount((ulong)value);
|
|
|
|
/// <summary>
|
|
/// Count the number of trailing zero bits in a mask.
|
|
/// Similar in behavior to the x86 instruction TZCNT.
|
|
/// </summary>
|
|
/// <param name="value">The value.</param>
|
|
[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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rotates the specified value left by the specified number of bits.
|
|
/// Similar in behavior to the x86 instruction ROL.
|
|
/// </summary>
|
|
/// <param name="value">The value to rotate.</param>
|
|
/// <param name="offset">The number of bits to rotate by.
|
|
/// Any value outside the range [0..31] is treated as congruent mod 32.</param>
|
|
/// <returns>The rotated value.</returns>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static uint RotateLeft(uint value, int offset) =>
|
|
(value << offset) | (value >> (32 - offset));
|
|
|
|
/// <summary>
|
|
/// Rotates the specified value left by the specified number of bits.
|
|
/// Similar in behavior to the x86 instruction ROL.
|
|
/// </summary>
|
|
/// <param name="value">The value to rotate.</param>
|
|
/// <param name="offset">The number of bits to rotate by.
|
|
/// Any value outside the range [0..63] is treated as congruent mod 64.</param>
|
|
/// <returns>The rotated value.</returns>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static ulong RotateLeft(ulong value, int offset) =>
|
|
(value << offset) | (value >> (64 - offset));
|
|
|
|
/// <summary>
|
|
/// Rotates the specified value right by the specified number of bits.
|
|
/// Similar in behavior to the x86 instruction ROR.
|
|
/// </summary>
|
|
/// <param name="value">The value to rotate.</param>
|
|
/// <param name="offset">The number of bits to rotate by.
|
|
/// Any value outside the range [0..31] is treated as congruent mod 32.</param>
|
|
/// <returns>The rotated value.</returns>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static uint RotateRight(uint value, int offset) =>
|
|
(value >> offset) | (value << (32 - offset));
|
|
|
|
/// <summary>
|
|
/// Rotates the specified value right by the specified number of bits.
|
|
/// Similar in behavior to the x86 instruction ROR.
|
|
/// </summary>
|
|
/// <param name="value">The value to rotate.</param>
|
|
/// <param name="offset">The number of bits to rotate by.
|
|
/// Any value outside the range [0..63] is treated as congruent mod 64.</param>
|
|
/// <returns>The rotated value.</returns>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static ulong RotateRight(ulong value, int offset) =>
|
|
(value >> offset) | (value << (64 - offset));
|
|
|
|
/// <summary>
|
|
/// Count the number of leading zero bits in a mask.
|
|
/// Similar in behavior to the x86 instruction LZCNT.
|
|
/// </summary>
|
|
/// <param name="value">The value.</param>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static int LeadingZeroCount(uint value)
|
|
{
|
|
// Unguarded fallback contract is 0->31, BSR contract is 0->undefined
|
|
if (value == 0)
|
|
{
|
|
return 32;
|
|
}
|
|
|
|
// 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 31
|
|
^ Log2DeBruijn[
|
|
// uint|long -> IntPtr cast on 32-bit platforms does expensive overflow checks not needed here
|
|
(int)((value * 0x07C4ACDDu) >> 27)
|
|
];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Count the number of leading zero bits in a mask.
|
|
/// Similar in behavior to the x86 instruction LZCNT.
|
|
/// </summary>
|
|
/// <param name="value">The value.</param>
|
|
[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);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|