本文是《C# Tips:位运算相关》的Update。对《C# Tips:位运算相关》中所定义的类 UInt64MaskHelper,增加了一个实用函数,然后再给出了一个小例子。
这个类设计的用意是方便二进制数据包的 Encoding / Decoding。
先贴出更新后的 UInt64MaskHelper 类:
// -----------------------------------------------------------------------
// <copyright file="UInt64MaskHelper.cs" author="Yaping Xin">
//
//
// File Name : UInt64MaskHelper.cs
// Description : Helper class to support UInt64 bit mask.
// Project Title : UInt64MaskHelper
// Author(s) : Yaping Xin
// Code Review Done :
// Modification History: (1) Created at Aug 10, 2012
// : (2) Add static function bool[] GetBits(ulong value)
// : at Sep 25, 2012
// </copyright>
// -----------------------------------------------------------------------
namespace BitsHelper
{
using System;
using System.Globalization;
/// <summary>
/// Helper class to support UInt64 bit mask.
/// </summary>
public static class UInt64MaskHelper
{
#region Constant definition
/// <summary>
/// Constant definition of max avaiable bytes length.
/// </summary>
public const int MaxBytesLength = 8;
/// <summary>
/// Constant definition bits count within a byte
/// </summary>
public const int ByteBitsCount = 8;
#endregion Constant definition
#region Useful Bytes/Bits static readonly array definition
/// <summary>
/// Array of UInt64 byte mask values.
/// </summary>
private static readonly ulong[] U64ByteMaskValues = new ulong[]
{
0x00000000000000FF,
0x000000000000FF00,
0x0000000000FF0000,
0x00000000FF000000,
0x000000FF00000000,
0x0000FF0000000000,
0x00FF000000000000,
0xFF00000000000000
};
/// <summary>
/// Array of max values storaged in different amount of bytes within UInt64 scope.
/// </summary>
private static readonly ulong[] U64BytesMaxValues = new ulong[]
{
0x00000000000000FF,
0x000000000000FFFF,
0x0000000000FFFFFF,
0x00000000FFFFFFFF,
0x000000FFFFFFFFFF,
0x0000FFFFFFFFFFFF,
0x00FFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF
};
/// <summary>
/// Array of shift values to specific byte lowest bit position.
/// </summary>
private static readonly byte[] BytePositionShiftValues = new byte[]
{
0, 8, 16, 24, 32, 40, 48, 56
};
/// <summary>
/// Array of byte bit mask values.
/// </summary>
private static readonly byte[] ByteBitMaskValues = new byte[]
{
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
};
/// <summary>
/// Array of byte bit mask values.
/// </summary>
private static readonly byte[] ByteBitOffMaskValues = new byte[]
{
0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F
};
#endregion Useful Bytes/Bits static readonly array definition
#region Enumeration definition
/// <summary>
/// Enumeration definition of the error code
/// </summary>
public enum ErrorCode : uint
{
/// <summary>No error occurs</summary>
OK = 0,
/// <summary>Argument is Null</summary>
ArgumentIsNull = 1,
/// <summary>Argument is out of range</summary>
ArgumentOutOfRange = 2,
}
#endregion Enumeration definition
#region Encoding methods (numeric value to bytes/bits)
/// <summary>
/// Gets the bytes array to storage the specific UInt64 value.
/// Notes: the returned array contains MaxBytesLength items.
/// </summary>
/// <param name="value">The specific UInt64 value.</param>
/// <returns>The bytes array which contains MaxBytesLength items.</returns>
public static byte[] GetBytes(ulong value)
{
byte[] result = new byte[MaxBytesLength];
ulong maskResult = value & U64ByteMaskValues[0];
result[0] = (byte)maskResult;
for (int i = 1; i < MaxBytesLength; i++)
{
maskResult = value & U64ByteMaskValues[i];
result[i] = (byte)(maskResult >> BytePositionShiftValues[i]);
}
return result;
}
/// <summary>
/// Gets the bits array to storage the specific UInt64 value.
/// Notes: the returned array contains MaxBytesLength * ByteBitsCount items.
/// </summary>
/// <param name="value">The specific UInt64 value.</param>
/// <returns>The bits array which contains MaxBytesLength * ByteBitsCount items.</returns>
public static bool[] GetBits(ulong value)
{
bool[] bits = new bool[MaxBytesLength * ByteBitsCount];
byte[] bytes = UInt64MaskHelper.GetBytes(value);
byte byteValue;
byte bitValue;
byte mask;
int i = 0;
for (int indexBytes = 0; indexBytes < bytes.Length; indexBytes++)
{
for (byte bitPosition = 0; bitPosition < ByteBitsCount; bitPosition++)
{
byteValue = bytes[indexBytes];
if (bitPosition == 0)
{
bitValue = (byte)(byteValue & ByteBitMaskValues[0]);
}
else
{
mask = (byte)(byteValue & ByteBitMaskValues[bitPosition]);
bitValue = mask >>= bitPosition;
}
bits[i] = bitValue == 1;
i++;
}
}
return bits;
}
/// <summary>
/// Gets the byte at specific position from the UInt64 value.
/// Notes: this function will not return detailed error code while error occurs.
/// </summary>
/// <param name="value">The UInt64 value.</param>
/// <param name="bytePosition">The byte position.</param>
/// <param name="result">The byte to get.</param>
/// <returns>
/// <c>true</c> indicates no error occurs during calculating;
/// <c>fasle</c> indicates error occurs.
/// </returns>
public static bool GetByte(ulong value, int bytePosition, out byte result)
{
ErrorCode errorCode;
return UInt64MaskHelper.GetByte(value, bytePosition, out result, out errorCode);
}
/// <summary>
/// Gets the byte at specific position from the UInt64 value.
/// </summary>
/// <param name="value">The UInt64 value.</param>
/// <param name="bytePosition">The byte position.</param>
/// <param name="result">The byte to get.</param>
/// <param name="errorCode">The error code.
/// Will be set to ErrorCode.OK if no error occurs.</param>
/// <returns>
/// <c>true</c> indicates no error occurs during calculating;
/// <c>fasle</c> indicates error occurs, and detailed error is wrote in errorCode output parameter.
/// </returns>
public static bool GetByte(
ulong value,
int bytePosition,
out byte result,
out ErrorCode errorCode)
{
if (bytePosition < 0 || bytePosition >= MaxBytesLength)
{
result = 0;
errorCode = ErrorCode.ArgumentOutOfRange;
return false;
}
ulong maskResult;
if (bytePosition == 0)
{
maskResult = value & U64ByteMaskValues[0];
goto EXIT_PASS;
}
maskResult = value & U64ByteMaskValues[bytePosition];
maskResult >>= BytePositionShiftValues[bytePosition];
EXIT_PASS:
result = (byte)maskResult;
errorCode = ErrorCode.OK;
return true;
}
/// <summary>
/// Gets the bit at specific position from the byte value.
/// Notes: this function will not return detailed error code while error occurs.
/// </summary>
/// <param name="value">The byte value.</param>
/// <param name="bitPosition">The bit position.</param>
/// <param name="result">The bit (0 or 1) to get.</param>
/// <returns>
/// <c>true</c> indicates no error occurs during calculating;
/// <c>fasle</c> indicates error occurs.
/// </returns>
public static bool GetBit(byte value, int bitPosition, out byte result)
{
ErrorCode errorCode;
return UInt64MaskHelper.GetBit(value, bitPosition, out result, out errorCode);
}
/// <summary>
/// Gets the bit at specific position from the byte value.
/// </summary>
/// <param name="value">The byte value.</param>
/// <param name="bitPosition">The bit position.</param>
/// <param name="result">The bit (0 or 1) to get.</param>
/// <param name="errorCode">The error code.
/// Will be set to ErrorCode.OK if no error occurs.</param>
/// <returns>
/// <c>true</c> indicates no error occurs during calculating;
/// <c>fasle</c> indicates error occurs, and detailed error is wrote in errorCode output parameter.
/// </returns>
public static bool GetBit(
byte value,
int bitPosition,
out byte result,
out ErrorCode errorCode)
{
if (bitPosition < 0 || bitPosition >= ByteBitsCount)
{
result = 0;
errorCode = ErrorCode.ArgumentOutOfRange;
return false;
}
if (bitPosition == 0)
{
result = (byte)(value & ByteBitMaskValues[0]);
errorCode = ErrorCode.OK;
return true;
}
byte maskResult = (byte)(value & ByteBitMaskValues[bitPosition]);
result = maskResult >>= bitPosition;
errorCode = ErrorCode.OK;
return true;
}
#endregion Encoding methods (numeric value to bytes/bits)
#region Decoding methods (bytes/bits to numeric value)
/// <summary>
/// Gets the UInt64 value from bytes array.
/// Notes: this function will not return detailed error code while error occurs.
/// </summary>
/// <param name="bytes">The bytes array.</param>
/// <param name="value">The UInt64 value.</param>
/// <returns>
/// <c>true</c> indicates no error occurs during calculating;
/// <c>fasle</c> indicates error occurs.
/// </returns>
public static bool GetUInt64(byte[] bytes, out ulong value)
{
ErrorCode errorCode;
return UInt64MaskHelper.GetUInt64(bytes, out value, out errorCode);
}
/// <summary>
/// Gets the UInt64 value from bytes array.
/// </summary>
/// <param name="bytes">The bytes array.</param>
/// <param name="value">The UInt64 value.</param>
/// <param name="errorCode">The error code.
/// Will be set to ErrorCode.OK if no error occurs.</param>
/// <returns>
/// <c>true</c> indicates no error occurs during calculating;
/// <c>fasle</c> indicates error occurs, and detailed error is wrote in errorCode output parameter.
/// </returns>
public static bool GetUInt64(byte[] bytes, out ulong value, out ErrorCode errorCode)
{
if (bytes == null || bytes.Length == 0)
{
errorCode = ErrorCode.ArgumentIsNull;
value = 0;
return false;
}
if (bytes.Length > MaxBytesLength)
{
errorCode = ErrorCode.ArgumentOutOfRange;
value = 0;
return false;
}
ulong result = bytes[0];
ulong byteValue;
if (bytes.Length > 1)
{
for (int i = 1; i < bytes.Length; i++)
{
byteValue = bytes[i];
byteValue <<= BytePositionShiftValues[i];
result += byteValue;
}
}
value = result;
errorCode = ErrorCode.OK;
return true;
}
/// <summary>
/// Gets the UInt64 value from text.
/// </summary>
/// <param name="hexText">The hex or decimal text.</param>
/// <param name="result">The UInt64 value.</param>
/// <returns>
/// <c>true</c> indicates no error occurs during calculating;
/// <c>fasle</c> indicates error occurs.
/// </returns>
public static bool GetUInt64(string hexText, out ulong result)
{
if (string.IsNullOrEmpty(hexText))
{
goto EXIT_FAIL;
}
try
{
if (hexText.StartsWith("0x", StringComparison.CurrentCultureIgnoreCase))
{
return ulong.TryParse(
hexText.Substring(2).Trim(),
NumberStyles.AllowHexSpecifier,
null,
out result);
}
else
{
return ulong.TryParse(hexText.Trim(), out result);
}
}
catch
{
goto EXIT_FAIL;
}
EXIT_FAIL:
result = 0;
return false;
}
#endregion Decoding methods (bytes/bits to numeric value)
#region Set bit value
/// <summary>
/// Sets bit value at specific bit position within the raw byte.
/// Notes: this function will not return detailed error code while error occurs.
/// </summary>
/// <param name="rawByte">The raw byte.</param>
/// <param name="bitPosition">The bit position.</param>
/// <param name="bitValue">if set to <c>true</c> [bit value].</param>
/// <returns>
/// <c>true</c> indicates no error occurs during calculating;
/// <c>fasle</c> indicates error occurs.
/// </returns>
public static bool SetBit(ref byte rawByte, int bitPosition, bool bitValue)
{
ErrorCode errorCode;
return UInt64MaskHelper.SetBit(ref rawByte, bitPosition, bitValue, out errorCode);
}
/// <summary>
/// Sets bit value at specific bit position within the raw byte.
/// </summary>
/// <param name="rawByte">The raw byte.</param>
/// <param name="bitPosition">The bit position.</param>
/// <param name="bitValue"><c>true</c> indicates set bit value to 1;
/// otherwise set bit value to 0.</param>
/// <param name="errorCode">The error code.
/// Will be set to ErrorCode.OK if no error occurs.</param>
/// <returns>
/// <c>true</c> indicates no error occurs during calculating;
/// <c>fasle</c> indicates error occurs, and detailed error is wrote in errorCode output parameter.
/// </returns>
public static bool SetBit(ref byte rawByte, int bitPosition, bool bitValue, out ErrorCode errorCode)
{
if (bitPosition < 0 || bitPosition >= ByteBitsCount)
{
errorCode = ErrorCode.ArgumentOutOfRange;
return false;
}
if (bitValue)
{
rawByte |= ByteBitMaskValues[bitPosition];
}
else
{
rawByte &= ByteBitOffMaskValues[bitPosition];
}
errorCode = ErrorCode.OK;
return true;
}
#endregion Set bit value
}
}
应用举例
比如说,我们需要把一个 UInt64 值解析成 byte 数组,那么我们可以用到这个类里面的这个静态方法:
/// <summary>
/// Gets the bytes array to storage the specific UInt64 value.
/// Notes: the returned array contains MaxBytesLength items.
/// </summary>
/// <param name="value">The specific UInt64 value.</param>
/// <returns>The bytes array which contains MaxBytesLength items.</returns>
public static byte[] GetBytes(ulong value)
{
byte[] result = new byte[MaxBytesLength];
ulong maskResult = value & U64ByteMaskValues[0];
result[0] = (byte)maskResult;
for (int i = 1; i < MaxBytesLength; i++)
{
maskResult = value & U64ByteMaskValues[i];
result[i] = (byte)(maskResult >> BytePositionShiftValues[i]);
}
return result;
}
如果需要把一个 UInt64 值解析成 bit 数组,那么我们可以用到这个类里面的这个静态方法(我们用 bool 数组来存放 bit 值):
/// <summary>
/// Gets the bits array to storage the specific UInt64 value.
/// Notes: the returned array contains MaxBytesLength * ByteBitsCount items.
/// </summary>
/// <param name="value">The specific UInt64 value.</param>
/// <returns>The bits array which contains MaxBytesLength * ByteBitsCount items.</returns>
public static bool[] GetBits(ulong value)
{
bool[] bits = new bool[MaxBytesLength * ByteBitsCount];
byte[] bytes = UInt64MaskHelper.GetBytes(value);
byte byteValue;
byte bitValue;
byte mask;
int i = 0;
for (int indexBytes = 0; indexBytes < bytes.Length; indexBytes++)
{
for (byte bitPosition = 0; bitPosition < ByteBitsCount; bitPosition++)
{
byteValue = bytes[indexBytes];
if (bitPosition == 0)
{
bitValue = (byte)(byteValue & ByteBitMaskValues[0]);
}
else
{
mask = (byte)(byteValue & ByteBitMaskValues[bitPosition]);
bitValue = mask >>= bitPosition;
}
bits[i] = bitValue == 1;
i++;
}
}
return bits;
}
Notes:
- 常量 MaxBytesLength 和 ByteBitsCount 的值都是 8。
- public static byte[] GetBytes(ulong value) 返回值是一个长度为 8 的 byte 数组;
- public static bool[] GetBits(ulong value) 返回值是一个长度为 64 的 bool 数组;
测试一下 bool[] GetBits(ulong value) 函数:
测试程序如下:
static void Main(string[] args)
{
ulong value = 0xFF01FF02FF03FF04;
bool[] bits = UInt64MaskHelper.GetBits(value);
if (bits.Length == 64)
{
for (int i = 63; i >= 0; i--)
{
bool bitValue = bits[i];
if (bitValue)
{
Console.Write("1");
}
else
{
Console.Write("0");
}
if (i % 4 == 0)
{
Console.Write(" ");
}
if (i % 32 == 0)
{
Console.WriteLine();
}
}
Console.WriteLine();
}
}
运行结果:
验证:我们用Windows 7自带的计算器来作为验证工具。Windows 7自带的计算器的 Programmer 模式可以显示出一个值的二进制形式:
从上面的截图中我们可以看到,我们的程序输出的结果是正确的。