Skip to content

Commit

Permalink
优化Crc32/Crc16,增加Span计算,测试用例全覆盖;Binary增加Span写入;
Browse files Browse the repository at this point in the history
  • Loading branch information
nnhy committed Dec 12, 2024
1 parent 063270c commit 7f28e80
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 17 deletions.
56 changes: 41 additions & 15 deletions NewLife.Core/Security/Crc16.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ public sealed class Crc16
0x6E17,0x7E36,0x4E55,0x5E74,0x2E93,0x3EB2,0x0ED1,0x1EF0
];

const UInt16 CrcSeed = 0xFFFF;
/// <summary>校验值</summary>
public UInt16 Value { get; set; } = 0xFFFF;
public UInt16 Value { get; set; } = CrcSeed;
#endregion

/// <summary>重置清零</summary>
Expand Down Expand Up @@ -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]);
Expand All @@ -82,28 +82,44 @@ public Crc16 Update(Byte[] buffer, Int32 offset = 0, Int32 count = -1)
return this;
}

/// <summary>添加数据区进行检验</summary>
/// <param name="buffer"></param>
/// <returns></returns>
public Crc16 Update(ReadOnlySpan<Byte> 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;
}

/// <summary>添加数据流进行校验,不查表计算 CRC-16 x16+x15+x2+1 8005 IBM SDLC</summary>
/// <param name="stream"></param>
/// <param name="count">数量</param>
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;

Expand All @@ -122,6 +138,16 @@ public static UInt16 Compute(Byte[] buf, Int32 offset = 0, Int32 count = -1)
return crc.Value;
}

/// <summary>计算校验码</summary>
/// <param name="buffer"></param>
/// <returns></returns>
public static UInt16 Compute(ReadOnlySpan<Byte> buffer)
{
var crc = new Crc16();
crc.Update(buffer);
return crc.Value;
}

/// <summary>计算数据流校验码</summary>
/// <param name="stream"></param>
/// <param name="count"></param>
Expand All @@ -143,7 +169,7 @@ public static UInt16 Compute(Stream stream, Int32 count = -1)
/// <param name="position">如果大于等于0,则表示从该位置开始计算</param>
/// <param name="count">字节数偏移量,一般用负数表示</param>
/// <returns></returns>
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)
{
Expand Down Expand Up @@ -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++)
{
Expand Down
24 changes: 23 additions & 1 deletion NewLife.Core/Security/Crc32.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
namespace NewLife.Security;


/// <summary>CRC32校验</summary>
/// <remarks>
/// Generate a table for a byte-wise 32-bit CRC calculation on the polynomial:
Expand Down Expand Up @@ -100,6 +99,19 @@ public Crc32 Update(Byte[] buffer, Int32 offset = 0, Int32 count = -1)
return this;
}

/// <summary>添加数据区进行检验</summary>
/// <param name="buffer"></param>
/// <returns></returns>
public Crc32 Update(ReadOnlySpan<Byte> buffer)
{
for (var i = 0; i < buffer.Length; i++)
{
crc = Table[(crc ^ buffer[i]) & 0xFF] ^ (crc >> 8);
}

return this;
}

/// <summary>添加数据流进行校验</summary>
/// <param name="stream"></param>
/// <param name="count">数量</param>
Expand Down Expand Up @@ -132,6 +144,16 @@ public static UInt32 Compute(Byte[] buf, Int32 offset = 0, Int32 count = -1)
return crc.Value;
}

/// <summary>计算校验码</summary>
/// <param name="buffer"></param>
/// <returns></returns>
public static UInt32 Compute(ReadOnlySpan<Byte> buffer)
{
var crc = new Crc32();
crc.Update(buffer);
return crc.Value;
}

/// <summary>计算数据流校验码</summary>
/// <param name="stream"></param>
/// <param name="count"></param>
Expand Down
39 changes: 38 additions & 1 deletion NewLife.Core/Serialization/Binary/Binary.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -148,6 +150,41 @@ public virtual void Write(Byte[] buffer, Int32 offset, Int32 count)
Stream.Write(buffer, offset, count);
}

/// <summary>写入数据</summary>
/// <param name="buffer"></param>
public virtual void Write(ReadOnlySpan<Byte> buffer)
{
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
Stream.Write(buffer);
#else
var array = ArrayPool<Byte>.Shared.Rent(buffer.Length);
try
{
buffer.CopyTo(array);

Stream.Write(array, 0, buffer.Length);
}
finally
{
ArrayPool<Byte>.Shared.Return(array);
}
#endif
}

/// <summary>写入数据</summary>
/// <param name="buffer"></param>
public virtual void Write(ReadOnlyMemory<Byte> buffer)
{
if (MemoryMarshal.TryGetArray(buffer, out var segment))
{
Stream.Write(segment.Array!, segment.Offset, segment.Count);

return;
}

Write(buffer.Span);
}

/// <summary>写入大小,如果有FieldSize则返回,否则写入编码的大小并返回-1</summary>
/// <param name="size"></param>
/// <returns></returns>
Expand Down
49 changes: 49 additions & 0 deletions XUnitTest.Core/Security/Crc16Tests.cs
Original file line number Diff line number Diff line change
@@ -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<Byte>(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);
}
}
90 changes: 90 additions & 0 deletions XUnitTest.Core/Security/Crc32Tests.cs
Original file line number Diff line number Diff line change
@@ -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<Byte>(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<Byte>(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);
}
}

0 comments on commit 7f28e80

Please sign in to comment.