Skip to content

Commit e50a28f

Browse files
author
msftbot[bot]
authored
New BitHelper APIs (#3362)
## 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. -->
2 parents a7f8976 + 8382313 commit e50a28f

File tree

2 files changed

+143
-0
lines changed

2 files changed

+143
-0
lines changed

Microsoft.Toolkit.HighPerformance/Helpers/BitHelper.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,68 @@ public static bool HasLookupFlag(uint table, int x, int min = 0)
103103
return valid;
104104
}
105105

106+
/// <summary>
107+
/// Checks whether the given value has any bytes that are set to 0.
108+
/// That is, given a <see cref="uint"/> value, which has a total of 4 bytes,
109+
/// it checks whether any of those have all the bits set to 0.
110+
/// </summary>
111+
/// <param name="value">The input value to check.</param>
112+
/// <returns>Whether <paramref name="value"/> has any bytes set to 0.</returns>
113+
/// <remarks>
114+
/// This method contains no branches.
115+
/// For more background on this subject, see <see href="https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord"/>.
116+
/// </remarks>
117+
[Pure]
118+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
119+
public static bool HasZeroByte(uint value)
120+
{
121+
return ((value - 0x0101_0101u) & ~value & 0x8080_8080u) != 0;
122+
}
123+
124+
/// <summary>
125+
/// Checks whether the given value has any bytes that are set to 0.
126+
/// This method mirrors <see cref="HasZeroByte(uint)"/>, but with <see cref="ulong"/> values.
127+
/// </summary>
128+
/// <param name="value">The input value to check.</param>
129+
/// <returns>Whether <paramref name="value"/> has any bytes set to 0.</returns>
130+
[Pure]
131+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
132+
public static bool HasZeroByte(ulong value)
133+
{
134+
return ((value - 0x0101_0101_0101_0101ul) & ~value & 0x8080_8080_8080_8080ul) != 0;
135+
}
136+
137+
/// <summary>
138+
/// Checks whether a byte in the input <see cref="uint"/> value matches a target value.
139+
/// </summary>
140+
/// <param name="value">The input value to check.</param>
141+
/// <param name="target">The target byte to look for.</param>
142+
/// <returns>Whether <paramref name="value"/> has any bytes set to <paramref name="target"/>.</returns>
143+
/// <remarks>
144+
/// This method contains no branches.
145+
/// For more info, see <see href="https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord"/>.
146+
/// </remarks>
147+
[Pure]
148+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
149+
public static bool HasByteEqualTo(uint value, byte target)
150+
{
151+
return HasZeroByte(value ^ (0x0101_0101u * target));
152+
}
153+
154+
/// <summary>
155+
/// Checks whether a byte in the input <see cref="uint"/> value matches a target value.
156+
/// This method mirrors <see cref="HasByteEqualTo(uint,byte)"/>, but with <see cref="ulong"/> values.
157+
/// </summary>
158+
/// <param name="value">The input value to check.</param>
159+
/// <param name="target">The target byte to look for.</param>
160+
/// <returns>Whether <paramref name="value"/> has any bytes set to <paramref name="target"/>.</returns>
161+
[Pure]
162+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
163+
public static bool HasByteEqualTo(ulong value, byte target)
164+
{
165+
return HasZeroByte(value ^ (0x0101_0101_0101_0101u * target));
166+
}
167+
106168
/// <summary>
107169
/// Sets a bit to a specified value.
108170
/// </summary>

UnitTests/UnitTests.HighPerformance.Shared/Helpers/Test_BitHelper.cs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,87 @@ public void Test_BitHelper_HasLookupFlag32_WithMin(int x, bool flag)
7777
Assert.AreEqual(flag, BitHelper.HasLookupFlag(mask, x, 40));
7878
}
7979

80+
[TestCategory("BitHelper")]
81+
[TestMethod]
82+
[DataRow(0x0000_0000u, true)]
83+
[DataRow(0x0000_0001u, true)]
84+
[DataRow(0x0000_0011u, true)]
85+
[DataRow(0x0011_1111u, true)]
86+
[DataRow(0x1100_1111u, true)]
87+
[DataRow(0x1011_0011u, true)]
88+
[DataRow(0x1755_0055u, true)]
89+
[DataRow(0x0055_B255u, true)]
90+
[DataRow(0x1755_B200u, true)]
91+
[DataRow(0x1111_1111u, false)]
92+
[DataRow(0x1755_B255u, false)]
93+
[DataRow(0x1705_B255u, false)]
94+
[DataRow(0x1755_B055u, false)]
95+
public void Test_BitHelper_HasZeroByte_UInt32(uint x, bool result)
96+
{
97+
Assert.AreEqual(result, BitHelper.HasZeroByte(x));
98+
}
99+
100+
[TestCategory("BitHelper")]
101+
[TestMethod]
102+
[DataRow(0x0000_0000_0000_0000ul, true)]
103+
[DataRow(0x0000_0000_0000_0001ul, true)]
104+
[DataRow(0x0000_0000_0000_0011ul, true)]
105+
[DataRow(0x0011_1111_1111_1111ul, true)]
106+
[DataRow(0x1111_0011_1111_1111ul, true)]
107+
[DataRow(0x7234_AB00_DEAD_BEEFul, true)]
108+
[DataRow(0x7234_A542_DEAD_BEEFul, false)]
109+
[DataRow(0x1111_1111_1111_1111ul, false)]
110+
[DataRow(0x7234_A542_DEAD_B0EFul, false)]
111+
[DataRow(0x7030_A040_0E0D_B0E0ul, false)]
112+
public void Test_BitHelper_HasZeroByte_UInt64(ulong x, bool result)
113+
{
114+
Assert.AreEqual(result, BitHelper.HasZeroByte(x));
115+
}
116+
117+
[TestCategory("BitHelper")]
118+
[TestMethod]
119+
[DataRow(0x0000_0000u, 0x7B, false)]
120+
[DataRow(0x0000_0001u, 0x7B, false)]
121+
[DataRow(0x0000_1010u, 0x7B, false)]
122+
[DataRow(0x0111_7A00u, 0x7B, false)]
123+
[DataRow(0x0000_07B0u, 0x7B, false)]
124+
[DataRow(0x1111_1111u, 0x7B, false)]
125+
[DataRow(0x0000_FEFEu, 0xFF, false)]
126+
[DataRow(0xF00F_0FF0u, 0xFF, false)]
127+
[DataRow(0x0000_0000u, 0x00, true)]
128+
[DataRow(0x0000_007Bu, 0x7B, true)]
129+
[DataRow(0x0000_7B7Bu, 0x7B, true)]
130+
[DataRow(0x7B00_0110u, 0x7B, true)]
131+
[DataRow(0x00FF_0000u, 0xFF, true)]
132+
[DataRow(0xFFFF_FFFFu, 0xFF, true)]
133+
[DataRow(0x1515_1515u, 0x15, true)]
134+
public void Test_BitHelper_HasByteEqualTo_UInt32(uint x, int target, bool result)
135+
{
136+
Assert.AreEqual(result, BitHelper.HasByteEqualTo(x, unchecked((byte)target)));
137+
}
138+
139+
[TestCategory("BitHelper")]
140+
[TestMethod]
141+
[DataRow(0x0000_0000_0000_0000u, 0x7B, false)]
142+
[DataRow(0x0000_0000_0000_0001u, 0x7B, false)]
143+
[DataRow(0x0000_0000_0000_1010u, 0x7B, false)]
144+
[DataRow(0x0111_0000_0000_7A00u, 0x7B, false)]
145+
[DataRow(0x0000_0000_0000_07B0u, 0x7B, false)]
146+
[DataRow(0x1111_1111_0000_0000u, 0x7B, false)]
147+
[DataRow(0x0000_FEFE_0000_0000u, 0xFF, false)]
148+
[DataRow(0xF00F_0000_0000_0FF0u, 0xFF, false)]
149+
[DataRow(0x0000_0000_0000_0000u, 0x00, true)]
150+
[DataRow(0x0000_0000_0000_007Bu, 0x7B, true)]
151+
[DataRow(0x0000_7B7B_0000_0000u, 0x7B, true)]
152+
[DataRow(0x7B00_0110_0000_0000u, 0x7B, true)]
153+
[DataRow(0x00FF_0000_0000_0000u, 0xFF, true)]
154+
[DataRow(0xFFFF_FFFF_FFFF_FFFFu, 0xFF, true)]
155+
[DataRow(0x1515_1515_1515_1515u, 0x15, true)]
156+
public void Test_BitHelper_HasByteEqualTo_UInt64(ulong x, int target, bool result)
157+
{
158+
Assert.AreEqual(result, BitHelper.HasByteEqualTo(x, unchecked((byte)target)));
159+
}
160+
80161
[TestCategory("BitHelper")]
81162
[TestMethod]
82163
public void Test_BitHelper_SetFlag_UInt32()

0 commit comments

Comments
 (0)