C# BitConverte扩展方法,提供基于大端模式下的数值和字节数组的相互转换

数值转字节数组,以及字节数组转数值,需要注意的是C#的本地字节序是小端模式的,而网络字节序却是(大端模式),所以我用到了IPAddress来进行Host和Network的转换

为了简单以及更好的性能,做了一些取巧的处理方式,尽量避免数组拷贝和反转,看上去很诡异,经过简单测试貌似没啥问题,不敢保证百分百没问题啊,如果有问题,望指出啊,不建议直接拿来用啊

2022-11-03改进net6版

最近开了个net6项目,用到了一些新特性,特意改进了下这个扩展方法库

    /// <summary>
    /// 对BitConverter进行方法扩展
    /// </summary>
    public static class BitConverterExtend {

        #region 将数值的字节数组写入到span中
        public static bool TryWriteInt16(this short num, Span<byte> span, bool bigEndian = true) {
            if (bigEndian) {
                num = IPAddress.HostToNetworkOrder(num);
            }
            return BitConverter.TryWriteBytes(span, num);
        }
        public static bool TryWriteUInt16(this ushort num, Span<byte> span, bool bigEndian = true) {
            return ((short)num).TryWriteInt16(span, bigEndian); //这样比反转数组快
        }
        public static bool TryWriteInt32(this int num, Span<byte> span, bool bigEndian = true) {
            if (bigEndian) {
                num = IPAddress.HostToNetworkOrder(num);
            }
            return BitConverter.TryWriteBytes(span, num);
        }
        public static bool TryWriteUInt32(this uint num, Span<byte> span, bool bigEndian = true) {
            return ((int)num).TryWriteInt32(span, bigEndian);
        }
        public static bool TryWriteInt64(this long num, Span<byte> span, bool bigEndian = true) {
            if (bigEndian) {
                num = IPAddress.HostToNetworkOrder(num);
            }
            return BitConverter.TryWriteBytes(span, num);
        }
        public static bool TryWriteUInt64(this ulong num, Span<byte> span, bool bigEndian = true) {
            return ((long)num).TryWriteInt64(span, bigEndian);
        }
        public static bool TryWriteFloat(this float num, Span<byte> span, bool bigEndian = true) {
            bool b = BitConverter.TryWriteBytes(span, num);
            if (bigEndian) {
                span[..4].Reverse(); //只能手动反转,注意要先切片后才能反转,因为外部传入的span可能很长
            };
            return b;
        }
        public static bool TryWriteDouble(this double num, Span<byte> span, bool bigEndian = true) {
            bool b = BitConverter.TryWriteBytes(span, num);
            if (bigEndian) {
                span[..8].Reverse();
            };
            return b;
        }
        public static bool WriteString(this string value, Span<byte> span, bool bigEndian = true) {
            int length = Encoding.UTF8.GetBytes(value.AsSpan(), span[4..]); //先偏移4个字节写数据
            return length.TryWriteInt32(span, bigEndian); //再写前4个字节的长度
        }
        #endregion

        #region 将span转成具体的数据
        public static short ReadInt16(this byte[] buffer, int startIndex, bool bigEndian = true) {
            short num = BitConverter.ToInt16(buffer, startIndex);
            if (bigEndian) {
                num = IPAddress.NetworkToHostOrder(num);
            }
            return num;
        }
        public static ushort ReadUInt16(this byte[] buffer, int startIndex, bool bigEndian = true) {
            return (ushort)ReadInt16(buffer, startIndex, bigEndian); //因为ushort没有IPAddress.NetworkToHostOrder,所以直接调用ToInt16转换
        }
        public static int ReadInt32(this byte[] buffer, int startIndex, bool bigEndian = true) {
            int num = BitConverter.ToInt32(buffer, startIndex);
            if (bigEndian) {
                num = IPAddress.NetworkToHostOrder(num);
            }
            return num;
        }
        public static uint ReadUInt32(this byte[] buffer, int startIndex, bool bigEndian = true) {
            return (uint)ReadInt32(buffer, startIndex, bigEndian);
        }
        public static long ReadInt64(this byte[] buffer, int startIndex, bool bigEndian = true) {
            long num = BitConverter.ToInt64(buffer, startIndex);
            if (bigEndian) {
                num = IPAddress.NetworkToHostOrder(num);
            }
            return num;
        }
        public static ulong ReadUInt64(this byte[] buffer, int startIndex, bool bigEndian = true) {
            return (ulong)ReadInt64(buffer, startIndex, bigEndian);
        }
        public static float ReadFloat(this byte[] buffer, int startIndex, bool bigEndian = true) {
            float val;
            if (bigEndian) {
                Array.Reverse(buffer, startIndex, 4); //需要先反转顺序,因为本地转换函数是小端的
                val = BitConverter.ToSingle(buffer, startIndex);
                Array.Reverse(buffer, startIndex, 4); //用完,再反转回来,避免破坏数据
            } else {
                val = BitConverter.ToSingle(buffer, startIndex);
            }
            return val;
        }
        public static double ReadDouble(this byte[] buffer, int startIndex, bool bigEndian = true) {
            double val;
            if (bigEndian) {
                Array.Reverse(buffer, startIndex, 8);
                val = BitConverter.ToDouble(buffer, startIndex);
                Array.Reverse(buffer, startIndex, 8);
            } else {
                val = BitConverter.ToDouble(buffer, startIndex);
            }
            return val;
        }
        public static string ReadString(this byte[] buffer, int startIndex, bool bigEndian = true) {
            int length = buffer.ReadInt32(startIndex, bigEndian);
            return Encoding.UTF8.GetString(buffer, startIndex + 4, length);
        }
        #endregion
    }

测试代码

    internal class TestBitConverterExtend {
        public static void Test() {
            Console.WriteLine("大端模式#################");
            TestAAA(true);
            Console.WriteLine("小端模式#################");
            TestAAA(false);
        }
        public static void TestAAA(bool bigEndian) {
            short p1 = 123;
            ushort p2 = 456;
            int p3 = 789;
            uint p4 = 101112;
            long p5 = 131415;
            ulong p6 = 161718;
            float p7 = 3.14f;
            double p8 = 2.718281828459045;
            string p9 = "测试啊123";
            byte[] buffer = new byte[1024];
            int offset = 0;
            p1.TryWriteInt16(buffer.AsSpan(offset), bigEndian);
            offset += 2;
            p2.TryWriteUInt16(buffer.AsSpan(offset), bigEndian);
            offset += 2;
            p3.TryWriteInt32(buffer.AsSpan(offset), bigEndian);
            offset += 4;
            p4.TryWriteUInt32(buffer.AsSpan(offset), bigEndian);
            offset += 4;
            p5.TryWriteInt64(buffer.AsSpan(offset), bigEndian);
            offset += 8;
            p6.TryWriteUInt64(buffer.AsSpan(offset), bigEndian);
            offset += 8;
            p7.TryWriteFloat(buffer.AsSpan(offset), bigEndian);
            offset += 4;
            p8.TryWriteDouble(buffer.AsSpan(offset), bigEndian);
            offset += 8;
            p9.WriteString(buffer.AsSpan(offset), bigEndian);

            offset = 0;
            Compare(p1, buffer.ReadInt16(offset, bigEndian), (t1, t2) => t1 == t2);
            offset += 2;
            Compare(p2, buffer.ReadUInt16(offset, bigEndian), (t1, t2) => t1 == t2);
            offset += 2;
            Compare(p3, buffer.ReadInt32(offset, bigEndian), (t1, t2) => t1 == t2);
            offset += 4;
            Compare(p4, buffer.ReadUInt32(offset, bigEndian), (t1, t2) => t1 == t2);
            offset += 4;
            Compare(p5, buffer.ReadInt64(offset, bigEndian), (t1, t2) => t1 == t2);
            offset += 8;
            Compare(p6, buffer.ReadUInt64(offset, bigEndian), (t1, t2) => t1 == t2);
            offset += 8;
            Compare(p7, buffer.ReadFloat(offset, bigEndian), (t1, t2) => t1 == t2);
            offset += 4;
            Compare(p8, buffer.ReadDouble(offset, bigEndian), (t1, t2) => t1 == t2);
            offset += 8;
            Compare(p9, buffer.ReadString(offset, bigEndian), (t1, t2) => t1 == t2);
        }
        static void Compare<T>(T t1, T t2, Func<T, T, bool> func) {
            Console.WriteLine($"相等:{func(t1, t2)} 原参数:{t1} 写入后再读取的参数:{t2} ");
        }
    }

之前的net4.8版本

    /// <summary>
    /// 对BitConverter进行方法扩展
    /// </summary>
    public static class BitConverterExtend {
        /// <summary>
        /// 得到数值的网络字节序(大端模式)的字节数组
        /// </summary> 
        public static byte[] GetBytes_Network(this short num) {
            return BitConverter.GetBytes(IPAddress.HostToNetworkOrder(num));
        }
        public static byte[] GetBytes_Network(this ushort num) {
            return ((short)num).GetBytes_Network();  //这样比反转数组快
        }
        public static byte[] GetBytes_Network(this int num) {
            return BitConverter.GetBytes(IPAddress.HostToNetworkOrder(num));
        }
        public static byte[] GetBytes_Network(this uint num) {
            return ((int)num).GetBytes_Network();
        }
        public static byte[] GetBytes_Network(this long num) {
            return BitConverter.GetBytes(IPAddress.HostToNetworkOrder(num));
        }
        public static byte[] GetBytes_Network(this ulong num) {
            return ((long)num).GetBytes_Network();
        }
        public static byte[] GetBytes_Network(this float num) {
            return BitConverter.GetBytes(num).Reverse().ToArray();
        }
        public static byte[] GetBytes_Network(this double num) {
            return BitConverter.GetBytes(num).Reverse().ToArray();
        }
        public static byte[] GetBytes_UTF8(this string value) {
            return Encoding.UTF8.GetBytes(value);
        }
        /// <summary>
        /// 将网络字节序(大端模式)的字节数组转成本地字节序(小端模式)的数值
        /// </summary> 
        public static short ToInt16_ByNetworkBytes(this byte[] networkBytes, int startIndex) {
            return IPAddress.NetworkToHostOrder(BitConverter.ToInt16(networkBytes, startIndex));  //传入引用和起始索引,减少一个数组拷贝
        }
        public static ushort ToUInt16_ByNetworkBytes(this byte[] networkBytes, int startIndex) {
            return (ushort)((networkBytes[startIndex++] << 8) | networkBytes[startIndex]); //直接自己按位操作,这样最快
        }
        public static int ToInt32_ByNetworkBytes(this byte[] networkBytes, int startIndex) {
            return IPAddress.NetworkToHostOrder(BitConverter.ToInt32(networkBytes, startIndex));
        }
        public static uint ToUInt32_ByNetworkBytes(this byte[] networkBytes, int startIndex) {
            return (uint)((networkBytes[startIndex++] << 24) | (networkBytes[startIndex++] << 16)
                | (networkBytes[startIndex++] << 8) | networkBytes[startIndex]);
        }
        public static long ToInt64_ByNetworkBytes(this byte[] networkBytes, int startIndex) {
            return IPAddress.NetworkToHostOrder(BitConverter.ToInt64(networkBytes, startIndex));
        }
        public static ulong ToUInt64_ByNetworkBytes(this byte[] networkBytes, int startIndex) {
            return (ulong)((networkBytes[startIndex++] << 56) | (networkBytes[startIndex++] << 48)
                | (networkBytes[startIndex++] << 40) | (networkBytes[startIndex++] << 32)
                | (networkBytes[startIndex++] << 24) | (networkBytes[startIndex++] << 16)
                | (networkBytes[startIndex++] << 8) | networkBytes[startIndex]);
        }
        public static float ToFloat_ByNetworkBytes(this byte[] networkBytes, int startIndex) {
            return BitConverter.ToSingle(CopyAndReverse(networkBytes, startIndex, 4), 0);
        }
        /// <summary>
        /// 拷贝反转
        /// </summary> 
        static byte[] CopyAndReverse(byte[] networkBytes, int startIndex, int len) {
            byte[] bs = new byte[len];
            for (int i = 0; i < len; i++) {
                bs[len - 1 - i] = networkBytes[startIndex + i];  //反转拷贝
            }
            return bs;
        }
        public static double ToDouble_ByNetworkBytes(this byte[] networkBytes, int startIndex) {
            return BitConverter.ToDouble(CopyAndReverse(networkBytes, startIndex, 8), 0);
        }
        public static string GetString_UTF8(this byte[] value, int startIndex, int count) {
            return Encoding.UTF8.GetString(value, startIndex, count);
        }
    }

  

  

 

posted @ 2021-06-28 18:12  WmW  阅读(3077)  评论(1编辑  收藏  举报