Skip to content

Commit 9ed1765

Browse files
committed
Реализация алгоритма CRC32 BZIP
1 parent 2802688 commit 9ed1765

3 files changed

Lines changed: 200 additions & 72 deletions

File tree

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
using System.Collections.Concurrent;
2+
3+
namespace MathCore.Algorithms.HashSums;
4+
5+
internal static class CRC32
6+
{
7+
/// <summary>Отражение байта</summary>
8+
/// <param name="b">Байт для отражения</param>
9+
/// <returns>Отражённый байт</returns>
10+
private static byte ReflectByte(byte b) =>
11+
(byte)(((b & 0x01) << 7) |
12+
((b & 0x02) << 5) |
13+
((b & 0x04) << 3) |
14+
((b & 0x08) << 1) |
15+
((b & 0x10) >> 1) |
16+
((b & 0x20) >> 3) |
17+
((b & 0x40) >> 5) |
18+
((b & 0x80) >> 7));
19+
20+
/// <summary>Отражение 32-битного значения</summary>
21+
/// <param name="x">Значение для отражения</param>
22+
/// <returns>Отражённое значение</returns>
23+
private static uint ReflectUInt(uint x)
24+
{
25+
x = ((x & 0x55555555) << 1) | ((x & 0xAAAAAAAA) >> 1);
26+
x = ((x & 0x33333333) << 2) | ((x & 0xCCCCCCCC) >> 2);
27+
x = ((x & 0x0F0F0F0F) << 4) | ((x & 0xF0F0F0F0) >> 4);
28+
x = ((x & 0x00FF00FF) << 8) | ((x & 0xFF00FF00) >> 8);
29+
x = ((x & 0x0000FFFF) << 16) | ((x & 0xFFFF0000) >> 16);
30+
return x;
31+
}
32+
33+
/// <summary>Генерирует таблицу коэффициентов для вычисления CRC</summary>
34+
/// <param name="polynomial">Полином для вычисления CRC</param>
35+
/// <param name="RefIn">Отражение входных байтов</param>
36+
/// <returns>Таблица коэффициентов для вычисления CRC</returns>
37+
public static uint[] GenerateCRCTable(uint polynomial, bool RefIn)
38+
{
39+
var table = new uint[256];
40+
for (uint i = 0; i < 256; i++)
41+
{
42+
var entry = i;
43+
if (RefIn)
44+
entry = ReflectUInt(entry);
45+
46+
entry <<= 24;
47+
48+
for (var j = 0; j < 8; j++)
49+
{
50+
if ((entry & 0x80000000) != 0)
51+
entry = (entry << 1) ^ polynomial;
52+
else
53+
entry <<= 1;
54+
}
55+
56+
if (RefIn)
57+
entry = ReflectUInt(entry);
58+
59+
table[i] = entry;
60+
}
61+
62+
for (var i = 0; i < table.Length; i++)
63+
{
64+
if (i > 0 && i % 8 == 0)
65+
Console.WriteLine();
66+
67+
Console.Write($"0x{table[i]:X8}, ");
68+
}
69+
70+
Console.WriteLine();
71+
return table;
72+
}
73+
74+
private static readonly ConcurrentDictionary<(uint Polynomial, bool RefIn), uint[]> __CRCTableCache = [];
75+
76+
/// <summary>Метод-расширение для вычисления CRC-32 для потока</summary>
77+
/// <param name="stream">Поток, для которого вычисляется CRC-32</param>
78+
/// <param name="CRC">Начальное значение</param>
79+
/// <param name="poly">Полином для вычисления CRC-32</param>
80+
/// <param name="RefIn">Отражение входных байтов</param>
81+
/// <param name="RefOut">Отражение выходного значения CRC</param>
82+
/// <param name="XorOut">Значение для выполнения XOR с окончательным CRC</param>
83+
/// <param name="Cancel">Отмена операции</param>
84+
/// <returns>Значение CRC-32</returns>
85+
public static async Task<uint> GetCRC32Async(
86+
this Stream stream,
87+
uint CRC = 0xFFFFFFFF,
88+
uint poly = 0x04C11DB7,
89+
bool RefIn = false,
90+
bool RefOut = false,
91+
uint XorOut = 0xFFFFFFFF,
92+
CancellationToken Cancel = default)
93+
{
94+
stream.NotNull();
95+
96+
var table = __CRCTableCache.GetOrAdd(
97+
(poly, RefIn),
98+
key => GenerateCRCTable(key.Polynomial, key.RefIn));
99+
100+
int bytes_read;
101+
var buffer = new byte[8192];
102+
103+
while ((bytes_read = await stream.ReadAsync(buffer, Cancel).ConfigureAwait(false)) > 0)
104+
if (RefIn)
105+
for (var i = 0; i < bytes_read; i++)
106+
{
107+
var index = ((CRC ^ (uint)(ReflectByte(buffer[i]) << 24)) & 0xFF000000) >> 24;
108+
var lookup = table[index];
109+
CRC = (CRC << 8) ^ lookup;
110+
}
111+
else
112+
for (var i = 0; i < bytes_read; i++)
113+
{
114+
var index = ((CRC ^ (uint)(buffer[i] << 24)) & 0xFF000000) >> 24;
115+
var lookup = table[index];
116+
CRC = (CRC << 8) ^ lookup;
117+
}
118+
119+
if (RefOut)
120+
CRC = ReflectUInt(CRC);
121+
122+
return CRC ^ XorOut;
123+
}
124+
125+
/// <summary>Синхронный метод-расширение для вычисления CRC-32 для потока</summary>
126+
/// <param name="stream">Поток, для которого вычисляется CRC-32.</param>
127+
/// <param name="polynomial">Полином для вычисления CRC-32.</param>
128+
/// <param name="CRC"></param>
129+
/// <param name="RefIn">Отражение входных байтов.</param>
130+
/// <param name="RefOut">Отражение выходного значения CRC.</param>
131+
/// <param name="XOROut">Значение для выполнения XOR с окончательным CRC.</param>
132+
/// <returns>Значение CRC-32.</returns>
133+
public static uint GetCRC32(this Stream stream,
134+
uint polynomial = 0x04C11DB7,
135+
uint CRC = 0xFFFFFF,
136+
bool RefIn = false,
137+
bool RefOut = false,
138+
uint XOROut = 0xFFFFFF)
139+
{
140+
stream.NotNull();
141+
142+
var table = __CRCTableCache.GetOrAdd((polynomial, RefIn), key => GenerateCRCTable(key.Polynomial, key.RefIn));
143+
144+
int bytes_read;
145+
Span<byte> buffer = stackalloc byte[8192];
146+
147+
while ((bytes_read = stream.Read(buffer)) > 0)
148+
if (RefIn)
149+
for (var i = 0; i < bytes_read; i++)
150+
{
151+
var index = ((CRC ^ (uint)(ReflectByte(buffer[i]) << 24)) & 0xFF000000) >> 24;
152+
var lookup = table[index];
153+
CRC = (CRC << 8) ^ lookup;
154+
}
155+
else
156+
for (var i = 0; i < bytes_read; i++)
157+
{
158+
var index = ((CRC ^ (uint)(buffer[i] << 24)) & 0xFF000000) >> 24;
159+
var lookup = table[index];
160+
CRC = (CRC << 8) ^ lookup;
161+
}
162+
163+
if (RefOut)
164+
CRC = ReflectUInt(CRC);
165+
166+
return CRC ^ XOROut;
167+
}
168+
}
Lines changed: 10 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,18 @@
1-
using System.Numerics;
2-
using System.Runtime.Intrinsics;
3-
using System.Linq;
4-
using System.Threading;
5-
using System.Threading.Tasks;
6-
using System.Diagnostics;
1+

2+
using MathCore.Algorithms.HashSums;
73

8-
using MathCore;
9-
using MathCore.Algorithms.Matrices;
4+
const string test_str = "123456789";
105

11-
double[,] MM =
12-
{
13-
{ 1, 2, 3, },
14-
{ 4, 5, 6, },
15-
{ 7, 8, 8, },
16-
};
17-
18-
var M = Matrix.Create(MM);
19-
var invM = M.GetInverse(out var P);
20-
21-
//var X = new double[11];
6+
var test_bytes = System.Text.Encoding.UTF8.GetBytes(test_str);
227

23-
//for (var i = 0; i < X.Length; i++)
24-
// X[i] = i + 1;
25-
26-
//var A = DoubleVector.Create(X);
27-
28-
//var sum = A.Sum();
8+
var hex = test_bytes.ToStringHex();
299

10+
await using (var stream = new MemoryStream(test_bytes))
11+
{
12+
var crc32 = await stream.GetCRC32Async();
13+
var result = crc32.ToString("X8");
14+
}
3015

3116
Console.WriteLine("End.");
3217
return;
3318

34-
readonly ref struct DoubleVector(double[] array)
35-
{
36-
public static DoubleVector Create(params double[] array) => new(array);
37-
38-
public double[] Items => array;
39-
40-
public int Length => array.Length;
41-
42-
public double Sum()
43-
{
44-
var sum = 0d;
45-
if (!Vector.IsHardwareAccelerated)
46-
{
47-
foreach (var x in array)
48-
sum += x;
49-
50-
return sum;
51-
}
52-
53-
var window_size = Vector<double>.Count;
54-
var ones = Vector<double>.One;
55-
56-
var vector_part_length = array.Length - array.Length % window_size;
57-
for (var i = 0; i < vector_part_length; i += window_size)
58-
sum += Vector.Dot(new(array, i), ones);
59-
60-
for (var i = vector_part_length; i < array.Length; i++)
61-
sum += array[i];
62-
63-
return sum;
64-
}
65-
66-
public DoubleVector ProductTo(DoubleVector other)
67-
{
68-
if (Length != other.Length) throw new InvalidOperationException("Размеры векторов не совпадают");
69-
70-
var result = new double[Length];
71-
72-
73-
return result;
74-
}
75-
76-
77-
public static implicit operator DoubleVector(double[] array) => new(array);
78-
79-
public static implicit operator double[](DoubleVector vector) => vector.Items;
80-
}

Tests/MathCore.Tests/Hash/CRC/CRC64Tests.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,26 @@ public void ISO_Hello_World()
3232

3333
actual_hash1.ToDebug();
3434
}
35+
36+
[TestMethod]
37+
public void TestCRC64WithKnownValues()
38+
{
39+
var data = "123456789"u8.ToArray();
40+
const ulong expected_crc = 0x995DC9BBDF1939FA; // CRC-64/ECMA-182
41+
42+
var actual_crc = CRC64.Hash(data, CRC64.Mode.ECMA, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, RefIn: false, RefOut: false);
43+
44+
Assert.AreEqual(expected_crc, actual_crc, $"Expected: 0x{expected_crc:X16}, Actual: 0x{actual_crc:X16}");
45+
}
46+
47+
[TestMethod]
48+
public void TestCRC64WithOnlineCalculator()
49+
{
50+
var data = "Hello World!"u8.ToArray();
51+
const ulong expected_crc = 0x1F6A1D2C3C9C2D3A; // CRC-64/ECMA-182
52+
53+
var actual_crc = CRC64.Hash(data, CRC64.Mode.ECMA, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, RefIn: false, RefOut: false);
54+
55+
Assert.AreEqual(expected_crc, actual_crc, $"Expected: 0x{expected_crc:X16}, Actual: 0x{actual_crc:X16}");
56+
}
3557
}

0 commit comments

Comments
 (0)