From 7f28e80b1e31496abc100f56e04ae3273c384db7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E7=9F=B3=E5=A4=B4?= Date: Thu, 12 Dec 2024 18:01:14 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96Crc32/Crc16=EF=BC=8C=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0Span=E8=AE=A1=E7=AE=97=EF=BC=8C=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=94=A8=E4=BE=8B=E5=85=A8=E8=A6=86=E7=9B=96=EF=BC=9BBinary?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0Span=E5=86=99=E5=85=A5=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NewLife.Core/Security/Crc16.cs | 56 +++++++++---- NewLife.Core/Security/Crc32.cs | 24 +++++- NewLife.Core/Serialization/Binary/Binary.cs | 39 ++++++++- XUnitTest.Core/Security/Crc16Tests.cs | 49 +++++++++++ XUnitTest.Core/Security/Crc32Tests.cs | 90 +++++++++++++++++++++ 5 files changed, 241 insertions(+), 17 deletions(-) create mode 100644 XUnitTest.Core/Security/Crc16Tests.cs create mode 100644 XUnitTest.Core/Security/Crc32Tests.cs diff --git a/NewLife.Core/Security/Crc16.cs b/NewLife.Core/Security/Crc16.cs index ff04d3c05..eff0069ed 100644 --- a/NewLife.Core/Security/Crc16.cs +++ b/NewLife.Core/Security/Crc16.cs @@ -42,8 +42,9 @@ public sealed class Crc16 0x6E17,0x7E36,0x4E55,0x5E74,0x2E93,0x3EB2,0x0ED1,0x1EF0 ]; + const UInt16 CrcSeed = 0xFFFF; /// 校验值 - public UInt16 Value { get; set; } = 0xFFFF; + public UInt16 Value { get; set; } = CrcSeed; #endregion /// 重置清零 @@ -71,8 +72,7 @@ public Crc16 Update(Byte[] buffer, Int32 offset = 0, Int32 count = -1) if (count < 0) count = buffer.Length; if (offset < 0 || offset + count > buffer.Length) throw new ArgumentOutOfRangeException(nameof(offset)); - var crc = Value; - crc ^= crc; + var crc = (UInt16)(Value ^ CrcSeed); for (var i = 0; i < count; i++) { crc = (UInt16)((crc << 8) ^ CrcTable[(crc >> 8 ^ buffer[offset + i]) & 0xFF]); @@ -82,28 +82,44 @@ public Crc16 Update(Byte[] buffer, Int32 offset = 0, Int32 count = -1) return this; } + /// 添加数据区进行检验 + /// + /// + public Crc16 Update(ReadOnlySpan buffer) + { + var crc = (UInt16)(Value ^ CrcSeed); + for (var i = 0; i < buffer.Length; i++) + { + crc = (UInt16)((crc << 8) ^ CrcTable[(crc >> 8 ^ buffer[i]) & 0xFF]); + } + Value = crc; + + return this; + } + /// 添加数据流进行校验,不查表计算 CRC-16 x16+x15+x2+1 8005 IBM SDLC /// /// 数量 public Crc16 Update(Stream stream, Int64 count = -1) { if (stream == null) throw new ArgumentNullException(nameof(stream)); - if (count <= 0) count = Int64.MaxValue; + if (count <= 0) count = stream.Length - stream.Position; - var crc = Value; + var crc = (UInt16)(Value ^ CrcSeed); while (--count >= 0) { var b = stream.ReadByte(); if (b == -1) break; - crc ^= (Byte)b; - for (var i = 0; i < 8; i++) - { - if ((crc & 0x0001) != 0) - crc = (UInt16)((crc >> 1) ^ 0xa001); - else - crc = (UInt16)(crc >> 1); - } + crc = (UInt16)((crc << 8) ^ CrcTable[(crc >> 8 ^ b) & 0xFF]); + //crc ^= (Byte)b; + //for (var i = 0; i < 8; i++) + //{ + // if ((crc & 0x0001) != 0) + // crc = (UInt16)((crc >> 1) ^ 0xa001); + // else + // crc = (UInt16)(crc >> 1); + //} } Value = crc; @@ -122,6 +138,16 @@ public static UInt16 Compute(Byte[] buf, Int32 offset = 0, Int32 count = -1) return crc.Value; } + /// 计算校验码 + /// + /// + public static UInt16 Compute(ReadOnlySpan buffer) + { + var crc = new Crc16(); + crc.Update(buffer); + return crc.Value; + } + /// 计算数据流校验码 /// /// @@ -143,7 +169,7 @@ public static UInt16 Compute(Stream stream, Int32 count = -1) /// 如果大于等于0,则表示从该位置开始计算 /// 字节数偏移量,一般用负数表示 /// - public static UInt16 Compute(Stream stream, Int64 position = -1, Int32 count = -1) + public static UInt16 Compute(Stream stream, Int64 position, Int32 count) { if (position >= 0) { @@ -172,7 +198,7 @@ public static UInt16 ComputeModbus(Byte[] data, Int32 offset, Int32 count = -1) UInt16 u = 0xFFFF; Byte b; - if (count == 0) count = data.Length - offset; + if (count <= 0) count = data.Length - offset; for (var i = offset; i < count; i++) { diff --git a/NewLife.Core/Security/Crc32.cs b/NewLife.Core/Security/Crc32.cs index cc8179a77..e9b9a3861 100644 --- a/NewLife.Core/Security/Crc32.cs +++ b/NewLife.Core/Security/Crc32.cs @@ -1,6 +1,5 @@ namespace NewLife.Security; - /// CRC32校验 /// /// Generate a table for a byte-wise 32-bit CRC calculation on the polynomial: @@ -100,6 +99,19 @@ public Crc32 Update(Byte[] buffer, Int32 offset = 0, Int32 count = -1) return this; } + /// 添加数据区进行检验 + /// + /// + public Crc32 Update(ReadOnlySpan buffer) + { + for (var i = 0; i < buffer.Length; i++) + { + crc = Table[(crc ^ buffer[i]) & 0xFF] ^ (crc >> 8); + } + + return this; + } + /// 添加数据流进行校验 /// /// 数量 @@ -132,6 +144,16 @@ public static UInt32 Compute(Byte[] buf, Int32 offset = 0, Int32 count = -1) return crc.Value; } + /// 计算校验码 + /// + /// + public static UInt32 Compute(ReadOnlySpan buffer) + { + var crc = new Crc32(); + crc.Update(buffer); + return crc.Value; + } + /// 计算数据流校验码 /// /// diff --git a/NewLife.Core/Serialization/Binary/Binary.cs b/NewLife.Core/Serialization/Binary/Binary.cs index 1729baaf4..08d81194c 100644 --- a/NewLife.Core/Serialization/Binary/Binary.cs +++ b/NewLife.Core/Serialization/Binary/Binary.cs @@ -1,4 +1,6 @@ -using System.Reflection; +using System.Buffers; +using System.Reflection; +using System.Runtime.InteropServices; using NewLife.Collections; using NewLife.Data; using NewLife.Reflection; @@ -148,6 +150,41 @@ public virtual void Write(Byte[] buffer, Int32 offset, Int32 count) Stream.Write(buffer, offset, count); } + /// 写入数据 + /// + public virtual void Write(ReadOnlySpan buffer) + { +#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER + Stream.Write(buffer); +#else + var array = ArrayPool.Shared.Rent(buffer.Length); + try + { + buffer.CopyTo(array); + + Stream.Write(array, 0, buffer.Length); + } + finally + { + ArrayPool.Shared.Return(array); + } +#endif + } + + /// 写入数据 + /// + public virtual void Write(ReadOnlyMemory buffer) + { + if (MemoryMarshal.TryGetArray(buffer, out var segment)) + { + Stream.Write(segment.Array!, segment.Offset, segment.Count); + + return; + } + + Write(buffer.Span); + } + /// 写入大小,如果有FieldSize则返回,否则写入编码的大小并返回-1 /// /// diff --git a/XUnitTest.Core/Security/Crc16Tests.cs b/XUnitTest.Core/Security/Crc16Tests.cs new file mode 100644 index 000000000..3d5fd11e1 --- /dev/null +++ b/XUnitTest.Core/Security/Crc16Tests.cs @@ -0,0 +1,49 @@ +using NewLife.Security; +using Xunit; + +namespace XUnitTest.Security; + +public class Crc16Tests +{ + [Fact] + public void TestComputeWithByteArray() + { + var data = "123456789"u8.ToArray(); + var crc = Crc16.Compute(data); + Assert.Equal(0x31C3, crc); + } + + [Fact] + public void TestComputeWithStream() + { + var data = "123456789"u8.ToArray(); + using var stream = new MemoryStream(data); + var crc = Crc16.Compute(stream, -1); + Assert.Equal(0x31C3, crc); + } + + [Fact] + public void TestComputeWithReadOnlySpan() + { + var data = "123456789"u8.ToArray(); + var crc = Crc16.Compute(new ReadOnlySpan(data)); + Assert.Equal(0x31C3, crc); + } + + [Fact] + public void TestComputeModbusWithByteArray() + { + var data = "123456789"u8.ToArray(); + var crc = Crc16.ComputeModbus(data, 0); + Assert.Equal(0x4B37, crc); + } + + [Fact] + public void TestComputeModbusWithStream() + { + var data = "123456789"u8.ToArray(); + using var stream = new MemoryStream(data); + var crc = Crc16.ComputeModbus(stream); + Assert.Equal(0x4B37, crc); + } +} diff --git a/XUnitTest.Core/Security/Crc32Tests.cs b/XUnitTest.Core/Security/Crc32Tests.cs new file mode 100644 index 000000000..8c960cd91 --- /dev/null +++ b/XUnitTest.Core/Security/Crc32Tests.cs @@ -0,0 +1,90 @@ +using NewLife.Security; +using Xunit; + +namespace XUnitTest.Security; + +public class Crc32Tests +{ + [Fact] + public void Compute_ByteArray_ReturnsExpectedCrc() + { + Byte[] data = { 1, 2, 3, 4, 5 }; + var expectedCrc = 0x470B99F4u; + + var actualCrc = Crc32.Compute(data); + + Assert.Equal(expectedCrc, actualCrc); + } + + [Fact] + public void Compute_ReadOnlySpan_ReturnsExpectedCrc() + { + Byte[] data = { 1, 2, 3, 4, 5 }; + var expectedCrc = 0x470B99F4u; + + var actualCrc = Crc32.Compute(new ReadOnlySpan(data)); + + Assert.Equal(expectedCrc, actualCrc); + } + + [Fact] + public void Compute_Stream_ReturnsExpectedCrc() + { + Byte[] data = { 1, 2, 3, 4, 5 }; + var expectedCrc = 0x470B99F4u; + + using var stream = new MemoryStream(data); + var actualCrc = Crc32.Compute(stream); + + Assert.Equal(expectedCrc, actualCrc); + } + + [Fact] + public void ComputeRange_Stream_ReturnsExpectedCrc() + { + Byte[] data = { 1, 2, 3, 4, 5 }; + var expectedCrc = 0x470B99F4u; + + using var stream = new MemoryStream(data); + var actualCrc = Crc32.ComputeRange(stream, 0, data.Length); + + Assert.Equal(expectedCrc, actualCrc); + } + + [Fact] + public void Update_ByteArray_ReturnsExpectedCrc() + { + Byte[] data = { 1, 2, 3, 4, 5 }; + var expectedCrc = 0x470B99F4u; + + var crc32 = new Crc32(); + crc32.Update(data); + + Assert.Equal(expectedCrc, crc32.Value); + } + + [Fact] + public void Update_ReadOnlySpan_ReturnsExpectedCrc() + { + Byte[] data = { 1, 2, 3, 4, 5 }; + var expectedCrc = 0x470B99F4u; + + var crc32 = new Crc32(); + crc32.Update(new ReadOnlySpan(data)); + + Assert.Equal(expectedCrc, crc32.Value); + } + + [Fact] + public void Update_Stream_ReturnsExpectedCrc() + { + Byte[] data = { 1, 2, 3, 4, 5 }; + var expectedCrc = 0x470B99F4u; + + using var stream = new MemoryStream(data); + var crc32 = new Crc32(); + crc32.Update(stream); + + Assert.Equal(expectedCrc, crc32.Value); + } +}