Skip to content

Commit

Permalink
New BitHelper APIs (#3362)
Browse files Browse the repository at this point in the history
## PR Type
What kind of change does this PR introduce?
<!-- Please uncomment one or more that apply to this PR. -->

<!-- - Bugfix -->
 - Feature
<!-- - Code style update (formatting) -->
<!-- - Refactoring (no functional changes, no api changes) -->
<!-- - Build or CI related changes -->
<!-- - Documentation content changes -->
<!-- - Sample app changes -->
<!-- - Other... Please describe: -->


## What is the new behavior?
<!-- Describe how was this issue resolved or changed? -->
This PR adds a few new APIs to the `BitHelper` class.
Follow up from this original thread on Twitter: https://twitter.com/SergioPedri/status/1275556765363568641.

These are the new APIs being included in this PR:

```csharp
namespace Microsoft.Toolkit.HighPerformance.Helpers
{
    public static class BitHelper
    {
        public static bool HasZeroByte(uint value);
        public static bool HasZeroByte(ulong value);
        public static bool HasByteEqualTo(uint value, byte target);
        public static bool HasByteEqualTo(ulong value, byte target);
    }
}
```

## PR Checklist

Please check if your PR fulfills the following requirements:

- [X] Tested code with current [supported SDKs](../readme.md#supported)
- [ ] ~~Pull Request has been submitted to the documentation repository [instructions](..\contributing.md#docs). Link: <!-- docs PR link -->~~
- [ ] ~~Sample in sample app has been added / updated (for bug fixes / features)~~
    - [ ] ~~Icon has been created (if new sample) following the [Thumbnail Style Guide and templates](https://github.com/windows-toolkit/WindowsCommunityToolkit-design-assets)~~
- [X] Tests for the changes have been added (for bug fixes / features) (if applicable)
- [X] Header has been added to all new source files (run *build/UpdateHeaders.bat*)
- [X] Contains **NO** breaking changes

<!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below. 
     Please note that breaking changes are likely to be rejected. -->
  • Loading branch information
msftbot[bot] authored Sep 22, 2020
2 parents a7f8976 + 8382313 commit e50a28f
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 0 deletions.
62 changes: 62 additions & 0 deletions Microsoft.Toolkit.HighPerformance/Helpers/BitHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,68 @@ public static bool HasLookupFlag(uint table, int x, int min = 0)
return valid;
}

/// <summary>
/// Checks whether the given value has any bytes that are set to 0.
/// That is, given a <see cref="uint"/> value, which has a total of 4 bytes,
/// it checks whether any of those have all the bits set to 0.
/// </summary>
/// <param name="value">The input value to check.</param>
/// <returns>Whether <paramref name="value"/> has any bytes set to 0.</returns>
/// <remarks>
/// This method contains no branches.
/// For more background on this subject, see <see href="https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord"/>.
/// </remarks>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool HasZeroByte(uint value)
{
return ((value - 0x0101_0101u) & ~value & 0x8080_8080u) != 0;
}

/// <summary>
/// Checks whether the given value has any bytes that are set to 0.
/// This method mirrors <see cref="HasZeroByte(uint)"/>, but with <see cref="ulong"/> values.
/// </summary>
/// <param name="value">The input value to check.</param>
/// <returns>Whether <paramref name="value"/> has any bytes set to 0.</returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool HasZeroByte(ulong value)
{
return ((value - 0x0101_0101_0101_0101ul) & ~value & 0x8080_8080_8080_8080ul) != 0;
}

/// <summary>
/// Checks whether a byte in the input <see cref="uint"/> value matches a target value.
/// </summary>
/// <param name="value">The input value to check.</param>
/// <param name="target">The target byte to look for.</param>
/// <returns>Whether <paramref name="value"/> has any bytes set to <paramref name="target"/>.</returns>
/// <remarks>
/// This method contains no branches.
/// For more info, see <see href="https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord"/>.
/// </remarks>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool HasByteEqualTo(uint value, byte target)
{
return HasZeroByte(value ^ (0x0101_0101u * target));
}

/// <summary>
/// Checks whether a byte in the input <see cref="uint"/> value matches a target value.
/// This method mirrors <see cref="HasByteEqualTo(uint,byte)"/>, but with <see cref="ulong"/> values.
/// </summary>
/// <param name="value">The input value to check.</param>
/// <param name="target">The target byte to look for.</param>
/// <returns>Whether <paramref name="value"/> has any bytes set to <paramref name="target"/>.</returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool HasByteEqualTo(ulong value, byte target)
{
return HasZeroByte(value ^ (0x0101_0101_0101_0101u * target));
}

/// <summary>
/// Sets a bit to a specified value.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,87 @@ public void Test_BitHelper_HasLookupFlag32_WithMin(int x, bool flag)
Assert.AreEqual(flag, BitHelper.HasLookupFlag(mask, x, 40));
}

[TestCategory("BitHelper")]
[TestMethod]
[DataRow(0x0000_0000u, true)]
[DataRow(0x0000_0001u, true)]
[DataRow(0x0000_0011u, true)]
[DataRow(0x0011_1111u, true)]
[DataRow(0x1100_1111u, true)]
[DataRow(0x1011_0011u, true)]
[DataRow(0x1755_0055u, true)]
[DataRow(0x0055_B255u, true)]
[DataRow(0x1755_B200u, true)]
[DataRow(0x1111_1111u, false)]
[DataRow(0x1755_B255u, false)]
[DataRow(0x1705_B255u, false)]
[DataRow(0x1755_B055u, false)]
public void Test_BitHelper_HasZeroByte_UInt32(uint x, bool result)
{
Assert.AreEqual(result, BitHelper.HasZeroByte(x));
}

[TestCategory("BitHelper")]
[TestMethod]
[DataRow(0x0000_0000_0000_0000ul, true)]
[DataRow(0x0000_0000_0000_0001ul, true)]
[DataRow(0x0000_0000_0000_0011ul, true)]
[DataRow(0x0011_1111_1111_1111ul, true)]
[DataRow(0x1111_0011_1111_1111ul, true)]
[DataRow(0x7234_AB00_DEAD_BEEFul, true)]
[DataRow(0x7234_A542_DEAD_BEEFul, false)]
[DataRow(0x1111_1111_1111_1111ul, false)]
[DataRow(0x7234_A542_DEAD_B0EFul, false)]
[DataRow(0x7030_A040_0E0D_B0E0ul, false)]
public void Test_BitHelper_HasZeroByte_UInt64(ulong x, bool result)
{
Assert.AreEqual(result, BitHelper.HasZeroByte(x));
}

[TestCategory("BitHelper")]
[TestMethod]
[DataRow(0x0000_0000u, 0x7B, false)]
[DataRow(0x0000_0001u, 0x7B, false)]
[DataRow(0x0000_1010u, 0x7B, false)]
[DataRow(0x0111_7A00u, 0x7B, false)]
[DataRow(0x0000_07B0u, 0x7B, false)]
[DataRow(0x1111_1111u, 0x7B, false)]
[DataRow(0x0000_FEFEu, 0xFF, false)]
[DataRow(0xF00F_0FF0u, 0xFF, false)]
[DataRow(0x0000_0000u, 0x00, true)]
[DataRow(0x0000_007Bu, 0x7B, true)]
[DataRow(0x0000_7B7Bu, 0x7B, true)]
[DataRow(0x7B00_0110u, 0x7B, true)]
[DataRow(0x00FF_0000u, 0xFF, true)]
[DataRow(0xFFFF_FFFFu, 0xFF, true)]
[DataRow(0x1515_1515u, 0x15, true)]
public void Test_BitHelper_HasByteEqualTo_UInt32(uint x, int target, bool result)
{
Assert.AreEqual(result, BitHelper.HasByteEqualTo(x, unchecked((byte)target)));
}

[TestCategory("BitHelper")]
[TestMethod]
[DataRow(0x0000_0000_0000_0000u, 0x7B, false)]
[DataRow(0x0000_0000_0000_0001u, 0x7B, false)]
[DataRow(0x0000_0000_0000_1010u, 0x7B, false)]
[DataRow(0x0111_0000_0000_7A00u, 0x7B, false)]
[DataRow(0x0000_0000_0000_07B0u, 0x7B, false)]
[DataRow(0x1111_1111_0000_0000u, 0x7B, false)]
[DataRow(0x0000_FEFE_0000_0000u, 0xFF, false)]
[DataRow(0xF00F_0000_0000_0FF0u, 0xFF, false)]
[DataRow(0x0000_0000_0000_0000u, 0x00, true)]
[DataRow(0x0000_0000_0000_007Bu, 0x7B, true)]
[DataRow(0x0000_7B7B_0000_0000u, 0x7B, true)]
[DataRow(0x7B00_0110_0000_0000u, 0x7B, true)]
[DataRow(0x00FF_0000_0000_0000u, 0xFF, true)]
[DataRow(0xFFFF_FFFF_FFFF_FFFFu, 0xFF, true)]
[DataRow(0x1515_1515_1515_1515u, 0x15, true)]
public void Test_BitHelper_HasByteEqualTo_UInt64(ulong x, int target, bool result)
{
Assert.AreEqual(result, BitHelper.HasByteEqualTo(x, unchecked((byte)target)));
}

[TestCategory("BitHelper")]
[TestMethod]
public void Test_BitHelper_SetFlag_UInt32()
Expand Down

0 comments on commit e50a28f

Please sign in to comment.