本文是《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; }
/// <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; }
测试一下 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 模式可以显示出一个值的二进制形式:
从上面的截图中我们可以看到,我们的程序输出的结果是正确的。