Files
sharpcompress/src/SharpCompress/Crypto/Crc32Stream.cs
2026-02-13 14:43:56 +00:00

135 lines
3.5 KiB
C#

using System;
using System.IO;
namespace SharpCompress.Crypto;
[CLSCompliant(false)]
public sealed class Crc32Stream : Stream
{
private readonly Stream stream;
private readonly uint[] _table;
private uint seed;
public const uint DEFAULT_POLYNOMIAL = 0xedb88320u;
public const uint DEFAULT_SEED = 0xffffffffu;
private static uint[]? _defaultTable;
public Crc32Stream(Stream stream)
: this(stream, DEFAULT_POLYNOMIAL, DEFAULT_SEED) { }
public Crc32Stream(Stream stream, uint polynomial, uint seed)
{
this.stream = stream;
_table = InitializeTable(polynomial);
this.seed = seed;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
public Stream WrappedStream => stream;
public override void Flush() => stream.Flush();
public override int Read(byte[] buffer, int offset, int count) =>
throw new NotSupportedException();
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
public override void SetLength(long value) => throw new NotSupportedException();
#if !LEGACY_DOTNET
public override void Write(ReadOnlySpan<byte> buffer)
{
stream.Write(buffer);
seed = CalculateCrc(_table, seed, buffer);
}
#endif
public override void Write(byte[] buffer, int offset, int count)
{
stream.Write(buffer, offset, count);
seed = CalculateCrc(_table, seed, buffer.AsSpan(offset, count));
}
public override void WriteByte(byte value)
{
stream.WriteByte(value);
seed = CalculateCrc(_table, seed, value);
}
public override bool CanRead => stream.CanRead;
public override bool CanSeek => false;
public override bool CanWrite => stream.CanWrite;
public override long Length => throw new NotSupportedException();
public override long Position
{
get => throw new NotSupportedException();
set => throw new NotSupportedException();
}
public uint Crc => ~seed;
public static uint Compute(byte[] buffer) => Compute(DEFAULT_SEED, buffer);
public static uint Compute(uint seed, byte[] buffer) =>
Compute(DEFAULT_POLYNOMIAL, seed, buffer);
public static uint Compute(uint polynomial, uint seed, ReadOnlySpan<byte> buffer) =>
~CalculateCrc(InitializeTable(polynomial), seed, buffer);
private static uint[] InitializeTable(uint polynomial)
{
if (polynomial == DEFAULT_POLYNOMIAL && _defaultTable != null)
{
return _defaultTable;
}
var createTable = new uint[256];
for (var i = 0; i < 256; i++)
{
var entry = (uint)i;
for (var j = 0; j < 8; j++)
{
if ((entry & 1) == 1)
{
entry = (entry >> 1) ^ polynomial;
}
else
{
entry >>= 1;
}
}
createTable[i] = entry;
}
if (polynomial == DEFAULT_POLYNOMIAL)
{
_defaultTable = createTable;
}
return createTable;
}
private static uint CalculateCrc(uint[] table, uint crc, ReadOnlySpan<byte> buffer)
{
unchecked
{
for (var i = 0; i < buffer.Length; i++)
{
crc = CalculateCrc(table, crc, buffer[i]);
}
}
return crc;
}
private static uint CalculateCrc(uint[] table, uint crc, byte b) =>
(crc >> 8) ^ table[(crc ^ b) & 0xFF];
}