Skip to content

Commit 6ddc679

Browse files
committed
[增加]1. 增加Hash算法库
1 parent 74cd1ef commit 6ddc679

12 files changed

+2082
-0
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
namespace GameFrameX.Foundation.Hash;
2+
3+
public static partial class CrcHelper
4+
{
5+
/// <summary>
6+
/// CRC32 算法。
7+
/// </summary>
8+
internal sealed class Crc32
9+
{
10+
private const int TableLength = 256;
11+
private const uint DefaultPolynomial = 0xedb88320;
12+
private const uint DefaultSeed = 0xffffffff;
13+
14+
private readonly uint m_Seed;
15+
private readonly uint[] m_Table;
16+
private uint m_Hash;
17+
18+
public Crc32() : this(DefaultPolynomial, DefaultSeed)
19+
{
20+
}
21+
22+
public Crc32(uint polynomial, uint seed)
23+
{
24+
m_Seed = seed;
25+
m_Table = InitializeTable(polynomial);
26+
m_Hash = seed;
27+
}
28+
29+
public void Initialize()
30+
{
31+
m_Hash = m_Seed;
32+
}
33+
34+
public void HashCore(byte[] bytes, int offset, int length)
35+
{
36+
m_Hash = CalculateHash(m_Table, m_Hash, bytes, offset, length);
37+
}
38+
39+
public uint HashFinal()
40+
{
41+
return ~m_Hash;
42+
}
43+
44+
private static uint CalculateHash(uint[] table, uint value, byte[] bytes, int offset, int length)
45+
{
46+
var last = offset + length;
47+
for (var i = offset; i < last; i++)
48+
{
49+
unchecked
50+
{
51+
value = (value >> 8) ^ table[bytes[i] ^ (value & 0xff)];
52+
}
53+
}
54+
55+
return value;
56+
}
57+
58+
private static uint[] InitializeTable(uint polynomial)
59+
{
60+
var table = new uint[TableLength];
61+
for (var i = 0; i < TableLength; i++)
62+
{
63+
var entry = (uint)i;
64+
for (var j = 0; j < 8; j++)
65+
{
66+
if ((entry & 1) == 1)
67+
{
68+
entry = (entry >> 1) ^ polynomial;
69+
}
70+
else
71+
{
72+
entry >>= 1;
73+
}
74+
}
75+
76+
table[i] = entry;
77+
}
78+
79+
return table;
80+
}
81+
}
82+
}

GameFrameX.Foundation.Hash/CrcHelper.Crc64.cs

Lines changed: 592 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
namespace GameFrameX.Foundation.Hash;
2+
3+
/// <summary>
4+
/// CRC校验相关的实用函数。
5+
/// 提供CRC32和CRC64两种校验算法的实现。
6+
/// </summary>
7+
public static partial class CrcHelper
8+
{
9+
/// <summary>
10+
/// 缓存字节数组的长度,用于分块读取大文件
11+
/// </summary>
12+
private const int CachedBytesLength = 0x1000;
13+
14+
/// <summary>
15+
/// 用于缓存读取数据的字节数组
16+
/// </summary>
17+
private static readonly byte[] SCachedBytes = new byte[CachedBytesLength];
18+
19+
/// <summary>
20+
/// CRC32算法的实例
21+
/// </summary>
22+
private static readonly CrcHelper.Crc32 SAlgorithm = new();
23+
24+
/// <summary>
25+
/// CRC64算法的实例
26+
/// </summary>
27+
private static readonly CrcHelper.Crc64 SAlgorithm64 = new();
28+
29+
/// <summary>
30+
/// 计算二进制流的CRC64值
31+
/// </summary>
32+
/// <param name="bytes">要计算的二进制字节数组</param>
33+
/// <returns>计算得到的CRC64校验值</returns>
34+
public static ulong GetCrc64(byte[] bytes)
35+
{
36+
SAlgorithm64.Reset();
37+
SAlgorithm64.Append(bytes);
38+
return SAlgorithm64.GetCurrentHashAsUInt64();
39+
}
40+
41+
/// <summary>
42+
/// 计算流的CRC64值
43+
/// </summary>
44+
/// <param name="stream">要计算的数据流</param>
45+
/// <returns>计算得到的CRC64校验值</returns>
46+
public static ulong GetCrc64(Stream stream)
47+
{
48+
SAlgorithm64.Reset();
49+
SAlgorithm64.Append(stream);
50+
return SAlgorithm64.GetCurrentHashAsUInt64();
51+
}
52+
53+
/// <summary>
54+
/// 计算二进制流的CRC32值
55+
/// </summary>
56+
/// <param name="bytes">要计算的二进制字节数组</param>
57+
/// <returns>计算得到的CRC32校验值</returns>
58+
/// <exception cref="ArgumentNullException">当bytes参数为null时抛出</exception>
59+
public static int GetCrc32(byte[] bytes)
60+
{
61+
if (bytes == null)
62+
{
63+
throw new ArgumentNullException(nameof(bytes), "Bytes is invalid.");
64+
}
65+
66+
return GetCrc32(bytes, 0, bytes.Length);
67+
}
68+
69+
/// <summary>
70+
/// 计算二进制流指定范围的CRC32值
71+
/// </summary>
72+
/// <param name="bytes">要计算的二进制字节数组</param>
73+
/// <param name="offset">起始偏移量</param>
74+
/// <param name="length">要计算的长度</param>
75+
/// <returns>计算得到的CRC32校验值</returns>
76+
/// <exception cref="ArgumentNullException">当bytes参数为null时抛出</exception>
77+
/// <exception cref="ArgumentException">当offset或length参数无效时抛出</exception>
78+
public static int GetCrc32(byte[] bytes, int offset, int length)
79+
{
80+
if (bytes == null)
81+
{
82+
throw new ArgumentNullException(nameof(bytes), "Bytes is invalid.");
83+
}
84+
85+
if (offset < 0 || length < 0 || offset + length > bytes.Length)
86+
{
87+
throw new ArgumentException("Offset or length is invalid.", nameof(offset));
88+
}
89+
90+
SAlgorithm.HashCore(bytes, offset, length);
91+
var result = (int)SAlgorithm.HashFinal();
92+
SAlgorithm.Initialize();
93+
return result;
94+
}
95+
96+
/// <summary>
97+
/// 计算流的CRC32值
98+
/// </summary>
99+
/// <param name="stream">要计算的数据流</param>
100+
/// <returns>计算得到的CRC32校验值</returns>
101+
/// <exception cref="ArgumentNullException">当stream参数为null时抛出</exception>
102+
public static int GetCrc32(Stream stream)
103+
{
104+
if (stream == null)
105+
{
106+
throw new ArgumentNullException(nameof(stream), "Stream is invalid.");
107+
}
108+
109+
while (true)
110+
{
111+
var bytesRead = stream.Read(SCachedBytes, 0, CachedBytesLength);
112+
if (bytesRead > 0)
113+
{
114+
SAlgorithm.HashCore(SCachedBytes, 0, bytesRead);
115+
}
116+
else
117+
{
118+
break;
119+
}
120+
}
121+
122+
var result = (int)SAlgorithm.HashFinal();
123+
SAlgorithm.Initialize();
124+
Array.Clear(SCachedBytes, 0, CachedBytesLength);
125+
return result;
126+
}
127+
128+
/// <summary>
129+
/// 将CRC32值转换为字节数组
130+
/// </summary>
131+
/// <param name="crc32">要转换的CRC32值</param>
132+
/// <returns>转换后的4字节数组,按大端序排列</returns>
133+
public static byte[] GetCrc32Bytes(int crc32)
134+
{
135+
return new[] { (byte)((crc32 >> 24) & 0xff), (byte)((crc32 >> 16) & 0xff), (byte)((crc32 >> 8) & 0xff), (byte)(crc32 & 0xff), };
136+
}
137+
138+
/// <summary>
139+
/// 将CRC32值转换为字节数组并存入指定数组
140+
/// </summary>
141+
/// <param name="crc32">要转换的CRC32值</param>
142+
/// <param name="bytes">存放结果的目标数组</param>
143+
public static void GetCrc32Bytes(int crc32, byte[] bytes)
144+
{
145+
GetCrc32Bytes(crc32, bytes, 0);
146+
}
147+
148+
/// <summary>
149+
/// 将CRC32值转换为字节数组并存入指定数组的指定位置
150+
/// </summary>
151+
/// <param name="crc32">要转换的CRC32值</param>
152+
/// <param name="bytes">存放结果的目标数组</param>
153+
/// <param name="offset">在目标数组中的起始位置</param>
154+
/// <exception cref="ArgumentNullException">当bytes参数为null时抛出</exception>
155+
/// <exception cref="ArgumentException">当offset参数无效或目标数组剩余空间不足4字节时抛出</exception>
156+
public static void GetCrc32Bytes(int crc32, byte[] bytes, int offset)
157+
{
158+
if (bytes == null)
159+
{
160+
throw new ArgumentNullException(nameof(bytes), "Result is invalid.");
161+
}
162+
163+
if (offset < 0 || offset + 4 > bytes.Length)
164+
{
165+
throw new ArgumentException("Offset or length is invalid.", nameof(offset));
166+
}
167+
168+
bytes[offset] = (byte)((crc32 >> 24) & 0xff);
169+
bytes[offset + 1] = (byte)((crc32 >> 16) & 0xff);
170+
bytes[offset + 2] = (byte)((crc32 >> 8) & 0xff);
171+
bytes[offset + 3] = (byte)(crc32 & 0xff);
172+
}
173+
174+
/// <summary>
175+
/// 使用指定编码计算流的CRC32值
176+
/// </summary>
177+
/// <param name="stream">要计算的数据流</param>
178+
/// <param name="code">用于编码的字节数组,将与数据进行XOR运算</param>
179+
/// <param name="length">要计算的字节数,如果为负数或超过流长度则使用整个流</param>
180+
/// <returns>计算得到的CRC32校验值</returns>
181+
/// <exception cref="ArgumentNullException">当stream或code参数为null时抛出</exception>
182+
/// <exception cref="ArgumentException">当code长度小于等于0时抛出</exception>
183+
internal static int GetCrc32(Stream stream, byte[] code, int length)
184+
{
185+
if (stream == null)
186+
{
187+
throw new ArgumentNullException(nameof(stream), "Stream is invalid.");
188+
}
189+
190+
if (code == null)
191+
{
192+
throw new ArgumentNullException(nameof(code), "Code is invalid.");
193+
}
194+
195+
var codeLength = code.Length;
196+
if (codeLength <= 0)
197+
{
198+
throw new ArgumentException("Code length is invalid.", nameof(codeLength));
199+
}
200+
201+
var bytesLength = (int)stream.Length;
202+
if (length < 0 || length > bytesLength)
203+
{
204+
length = bytesLength;
205+
}
206+
207+
var codeIndex = 0;
208+
while (true)
209+
{
210+
var bytesRead = stream.Read(SCachedBytes, 0, CachedBytesLength);
211+
if (bytesRead > 0)
212+
{
213+
if (length > 0)
214+
{
215+
for (var i = 0; i < bytesRead && i < length; i++)
216+
{
217+
SCachedBytes[i] ^= code[codeIndex++];
218+
codeIndex %= codeLength;
219+
}
220+
221+
length -= bytesRead;
222+
}
223+
224+
SAlgorithm.HashCore(SCachedBytes, 0, bytesRead);
225+
}
226+
else
227+
{
228+
break;
229+
}
230+
}
231+
232+
var result = (int)SAlgorithm.HashFinal();
233+
SAlgorithm.Initialize();
234+
Array.Clear(SCachedBytes, 0, CachedBytesLength);
235+
return result;
236+
}
237+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<Import Project="../Version.props" Label="版本号定义"/>
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>disable</Nullable>
7+
<LangVersion>10</LangVersion>
8+
<PublicSign>true</PublicSign>
9+
<SignAssembly>true</SignAssembly>
10+
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
11+
<Title>$(AssemblyName)</Title>
12+
<Copyright>AlianBlank;GameFrameX;Blank</Copyright>
13+
<PackageProjectUrl>https://github.com/GameFrameX/GameFrameX</PackageProjectUrl>
14+
<RepositoryUrl>https://github.com/GameFrameX/GameFrameX</RepositoryUrl>
15+
<RepositoryType>git</RepositoryType>
16+
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
17+
<Description>GameFrameX.Foundation.Hash,GameFrameX 框架的基础设施框架库,提供基础的哈希算法.框架文档主页: https://gameframex.doc.alianblank.com</Description>
18+
<PackageTags>GameFrameX,Lib,Foundation,Foundation.HashServer,GameServer</PackageTags>
19+
<PackageReleaseNotes>https://gameframex.doc.alianblank.com/</PackageReleaseNotes>
20+
<PackageIcon>logo.png</PackageIcon>
21+
<Authors>AlianBlank;Blank</Authors>
22+
<PackageReadmeFile>README.md</PackageReadmeFile>
23+
<IsPackable>true</IsPackable>
24+
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
25+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
26+
<PackageId>$(AssemblyName)</PackageId>
27+
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
28+
<TargetFramework>net8.0</TargetFramework>
29+
<AssemblyOriginatorKeyFile>../gameframex.key.snk</AssemblyOriginatorKeyFile>
30+
<IncludeSymbols>true</IncludeSymbols>
31+
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
32+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
33+
</PropertyGroup>
34+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
35+
<OutputPath>..\bin\app</OutputPath>
36+
</PropertyGroup>
37+
<ItemGroup>
38+
<None Include="..\logo.png">
39+
<Pack>True</Pack>
40+
<PackagePath>\</PackagePath>
41+
<Link>logo.png</Link>
42+
</None>
43+
<None Include="../README.md">
44+
<Pack>True</Pack>
45+
<PackagePath>\</PackagePath>
46+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
47+
</None>
48+
</ItemGroup>
49+
<ItemGroup>
50+
<PackageReference Include="Standart.Hash.xxHash" Version="4.0.5"/>
51+
</ItemGroup>
52+
</Project>

0 commit comments

Comments
 (0)