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
+ }
0 commit comments