A seemingly usable binary serialization help class

Keywords: encoding less

This is only a helper method help class, which can help small partners to write binary serialization efficiency. The code is also in the past

Before I start, I need to say that if it's not necessary, don't use binary serialization. Because it's hard to achieve version compatibility, if you write wrong, you don't know where it's written wrong, so debugging is very difficult. But for the performance improvement, it's not big

    /// <summary>
    ///Binary serialization
    /// </summary>
    interface IBinarySerializable
    {
        void Serialize(BinaryWriter binaryWriter);
        void Deserialize(BinaryReader binaryReader);
    }

This interface is used for object inheritance. If the object inherits, it is convenient for serialization

Here are some auxiliary methods

Without reflection, you need to write the conversion code manually. Pay attention to the order

    static class BinarySerialize
    {
        /// <summary>
        ///Write uin32 list
        /// </summary>
        /// <param name="binaryWriter"></param>
        /// <param name="list"></param>
        public static void WriteUint32List(this BinaryWriter binaryWriter, List<uint> list)
        {
            // Format first writes the length of the list, then writes the contents in turn
            binaryWriter.Write(list.Count);

            foreach (var n in list)
            {
                binaryWriter.Write(n);
            }
        }

        /// <summary>
        ///Read uint32 list
        /// </summary>
        /// <param name="binaryReader"></param>
        /// <returns></returns>
        public static List<uint> ReadUint32List(this BinaryReader binaryReader)
        {
            // Read length
            var count = binaryReader.ReadInt32();

            List<uint> list = new List<uint>(count);
            for (int i = 0; i < count; i++)
            {
                list.Add(binaryReader.ReadUInt32());
            }

            return list;
        }

        /// <summary>
        ///Write string list
        /// </summary>
        /// <param name="binaryWriter"></param>
        /// <param name="stringList"></param>
        public static void WriteStringList(this BinaryWriter binaryWriter, List<string> stringList)
        {
            // Format writes the list length first, then the string in turn
            binaryWriter.Write(stringList.Count);

            foreach (var str in stringList)
            {
                binaryWriter.Write(str);
            }
        }

        /// <summary>
        ///Write a list of serializable classes
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="binaryWriter"></param>
        /// <param name="list"></param>
        public static void WriteList<T>(this BinaryWriter binaryWriter, List<T> list) where T : IBinarySerializable
        {
            // Write length first
            binaryWriter.Write(list.Count);
            foreach (var binarySerializable in list)
            {
                binarySerializable.Serialize(binaryWriter);
            }
        }

        /// <summary>
        ///Read the list of serializable classes
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="binaryReader"></param>
        /// <returns></returns>
        public static List<T> ReadList<T>(this BinaryReader binaryReader) where T : IBinarySerializable, new()
        {
            // Read length
            var count = binaryReader.ReadInt32();
            List<T> list = new List<T>(count);
            for (int i = 0; i < count; i++)
            {
                T t = new T();
                t.Deserialize(binaryReader);
                list.Add(t);
            }

            return list;
        }

        /// <summary>
        ///Read string list
        /// </summary>
        /// <param name="binaryReader"></param>
        /// <returns></returns>
        public static List<string> ReadStringList(this BinaryReader binaryReader)
        {
            // First read the length of the list, then the string
            var count = binaryReader.ReadInt32();
            List<string> stringList = new List<string>(count);
            for (int i = 0; i < count; i++)
            {
                var str = binaryReader.ReadString();
                stringList.Add(str);
            }

            return stringList;
        }

        /// <summary>
        ///Write header information. It needs fixed length by default
        /// </summary>
        /// <param name="binaryWriter"></param>
        /// <param name="head"></param>
        /// <param name="headLength"></param>
        public static void WriteHead(this BinaryWriter binaryWriter, string head, int headLength)
        {
            var headByte = StringToByteList(head);
            binaryWriter.WriteByteList(headByte, headLength);
        }

        /// <summary>
        ///Convert string to byte where string is prefixed with length
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static byte[] StringToByteList(string str)
        {
            var strByteList = Utf8.GetBytes(str);
            ushort byteLength = (ushort)strByteList.Length;
            var newLength = sizeof(ushort) + strByteList.Length;

            var byteList = new byte[newLength];
            var binaryWriter = new BinaryWriter(new MemoryStream(byteList));
            binaryWriter.Write(byteLength);
            binaryWriter.Write(strByteList);
            return byteList;
        }

        /// <summary>
        ///Write binary write fixed length
        /// </summary>
        /// <param name="binaryWriter"></param>
        /// <param name="source"></param>
        /// <param name="byteCount"></param>
        public static void WriteByteList(this BinaryWriter binaryWriter, byte[] source, int byteCount)
        {
            var byteList = new byte[byteCount];
            Array.Copy(source, 0, byteList, 0, Math.Min(source.Length, byteCount));

            binaryWriter.Write(byteList, 0, byteCount);
        }

        private static Encoding Utf8 => Encoding.UTF8;

        /// <summary>
        ///Read fixed length head
        /// </summary>
        /// <param name="binaryReader"></param>
        /// <param name="headLength"></param>
        /// <returns></returns>
        public static string ReadHead(this BinaryReader binaryReader, int headLength)
        {
            var strByteLength = (ushort)binaryReader.ReadInt16();
            var strByteList = binaryReader.ReadBytes(strByteLength);
            var head = Utf8.GetString(strByteList);

            var readLength = headLength - sizeof(ushort) - strByteLength;
            binaryReader.ReadBytes(readLength);

            return head;
        }
    }

The read and write Head here may not be used by the business of the partners. I mainly write the version number in this way

This is the code of unit test, only the main use method of test. There is no test on the boundary

    [TestClass]
    public class BinarySerializeTests
    {
        private class FakeBinarySerialize : IBinarySerializable, IEquatable<FakeBinarySerialize>
        {
            /// <inheritdoc />
            public void Serialize(BinaryWriter binaryWriter)
            {
                binaryWriter.Write(F1);
                binaryWriter.Write(F2);
            }

            /// <inheritdoc />
            public void Deserialize(BinaryReader binaryReader)
            {
                F1 = binaryReader.ReadString();
                F2 = binaryReader.ReadInt32();
            }

            public string F1 { set; get; }
            public int F2 { set; get; }

            /// <inheritdoc />
            public bool Equals(FakeBinarySerialize other)
            {
                if (ReferenceEquals(null, other)) return false;
                if (ReferenceEquals(this, other)) return true;
                return F1 == other.F1 && F2 == other.F2;
            }

            /// <inheritdoc />
            public override bool Equals(object obj)
            {
                if (ReferenceEquals(null, obj)) return false;
                if (ReferenceEquals(this, obj)) return true;
                if (obj.GetType() != this.GetType()) return false;
                return Equals((FakeBinarySerialize)obj);
            }

            /// <inheritdoc />
            public override int GetHashCode()
            {
                unchecked
                {
                    return ((F1 != null ? F1.GetHashCode() : 0) * 397) ^ F2;
                }
            }
        }

        [ContractTestCase]
        public void WriteUint32List()
        {
            "write in uint List, you can read the contents of the list".Test(() =>
            {
                // Arrange
                var memoryStream = new MemoryStream();
                var binaryWriter = new BinaryWriter(memoryStream);
                var fakeBinarySerializeList = new List<uint>();
                for (int i = 0; i < 100; i++)
                {
                    fakeBinarySerializeList.Add((uint)i);
                }

                // Action
                binaryWriter.WriteUint32List(fakeBinarySerializeList);

                // Assert
                memoryStream.Seek(0, SeekOrigin.Begin);
                var binaryReader = new BinaryReader(memoryStream);
                var readList = binaryReader.ReadUint32List();

                Equal(fakeBinarySerializeList, readList);
            });
        }

        [ContractTestCase]
        public void WriteList()
        {
            "Write the contents of the list and read the list".Test(() =>
            {
                // Arrange
                var memoryStream = new MemoryStream();
                var binaryWriter = new BinaryWriter(memoryStream);
                var fakeBinarySerializeList = new List<FakeBinarySerialize>();
                for (int i = 0; i < 100; i++)
                {
                    fakeBinarySerializeList.Add(new FakeBinarySerialize()
                    {
                        F1 = i.ToString(),
                        F2 = i,
                    });
                }

                // Action
                binaryWriter.WriteList(fakeBinarySerializeList);

                // Assert
                memoryStream.Seek(0, SeekOrigin.Begin);
                var binaryReader = new BinaryReader(memoryStream);
                var readList = binaryReader.ReadList<FakeBinarySerialize>();
                Equal(fakeBinarySerializeList, readList);
            });
        }

        private void Equal<T>(List<T> a, List<T> b)
        {
            Assert.AreEqual(a.Count, b.Count);
            for (int i = 0; i < a.Count; i++)
            {
                Assert.AreEqual(a[i], b[i]);
            }
        }

        [ContractTestCase]
        public void WriteStringList()
        {
            "Write the empty string list, and you can read the empty list".Test(() =>
            {
                // Arrange
                var memoryStream = new MemoryStream();
                var binaryWriter = new BinaryWriter(memoryStream);
                var head = "Font Data 1.0.0";
                var length = 20;
                var test = (byte)0xF1;

                var stringList = new List<string>();

                // Action
                binaryWriter.WriteHead(head, length);
                binaryWriter.Write(test);
                binaryWriter.WriteStringList(stringList);

                // Assert

                memoryStream.Seek(0, SeekOrigin.Begin);
                var binaryReader = new BinaryReader(memoryStream);
                var str = binaryReader.ReadHead(length);
                var b = binaryReader.ReadByte();
                var readList = binaryReader.ReadStringList();

                Assert.AreEqual(0, readList.Count);
            });

            "Write string list to read the written value".Test(() =>
            {
                // Arrange
                var memoryStream = new MemoryStream();
                var binaryWriter = new BinaryWriter(memoryStream);
                var head = "Font Data 1.0.0";
                var length = 20;
                var test = (byte)0xF1;

                var stringList = new List<string>()
                {
                    "lindexi","doubi"
                };

                // Action
                binaryWriter.WriteHead(head, length);
                binaryWriter.Write(test);
                binaryWriter.WriteStringList(stringList);

                // Assert
                memoryStream.Seek(0, SeekOrigin.Begin);
                var binaryReader = new BinaryReader(memoryStream);
                var str = binaryReader.ReadHead(length);
                var b = binaryReader.ReadByte();
                var readList = binaryReader.ReadStringList();
            });
        }

        [ContractTestCase]
        public void WriteHead()
        {
            "Attempts to write a header with a length less than the specified length, which can be written and read".Test(() =>
            {
                // Arrange
                var memoryStream = new MemoryStream();
                var binaryWriter = new BinaryWriter(memoryStream);
                var head = "Font Data 1.0.0";
                var length = 20;
                var test = (byte)0xF1;
                // Action
                binaryWriter.WriteHead(head, length);
                binaryWriter.Write(test);

                // Assert
                memoryStream.Seek(0, SeekOrigin.Begin);
                var binaryReader = new BinaryReader(memoryStream);
                var str = binaryReader.ReadHead(length);
                var b = binaryReader.ReadByte();

                Assert.AreEqual(head, str);
                Assert.AreEqual(test, b);
            });
        }
    }

The above code needs to be used CUnit Only with the support of the library can Chinese writing conditions be used in unit tests

        <PackageReference Include="MSTestEnhancer" Version="1.6.0" />

I built my own blog https://blog.lindexi.com/ Welcome to visit, there are many new blogs. Only when I see that the blog is mature will it be put in csdn or blog Park, but once it is published, it will not be updated

If you see anything you don't understand in the blog, welcome to communicate, I have set up dotnet vocational and Technical College Welcome to join

If you have any questions that are not convenient to comment on the blog, you can add QQ 2844808902 to communicate


This work adopts Knowledge sharing Attribution - non-commercial use - sharing 4.0 international license agreement in the same way Licensing. You are welcome to reprint, use and republish, but you must keep the signature of the article Lin Dexi (includes links: http://blog.csdn.net/lindexi_gd ), shall not be used for commercial purposes, and the revised works based on this article must be released with the same license. If you have any questions, please contact me contact.

Posted by void_function on Thu, 11 Jun 2020 01:15:48 -0700