Optimize Crc32 calculations

This commit is contained in:
Jason Nelson 2024-04-01 21:59:39 -07:00 committed by BobLd
parent 4ad6bfc74e
commit 0efa68a8e4
2 changed files with 40 additions and 27 deletions

View File

@ -1,12 +1,11 @@
namespace UglyToad.PdfPig.Images.Png
{
using System;
using System.Collections.Generic;
/// <summary>
/// 32-bit Cyclic Redundancy Code used by the PNG for checking the data is intact.
/// </summary>
internal static class Crc32
internal class Crc32
{
private const uint Polynomial = 0xEDB88320;
@ -49,21 +48,6 @@
return crc32 ^ uint.MaxValue;
}
/// <summary>
/// Calculate the CRC32 for data.
/// </summary>
public static uint Calculate(List<byte> data)
{
var crc32 = uint.MaxValue;
for (var i = 0; i < data.Count; i++)
{
var index = (crc32 ^ data[i]) & 0xFF;
crc32 = (crc32 >> 8) ^ Lookup[index];
}
return crc32 ^ uint.MaxValue;
}
/// <summary>
/// Calculate the combined CRC32 for data.
/// </summary>
@ -84,5 +68,30 @@
return crc32 ^ uint.MaxValue;
}
private uint state = uint.MaxValue;
/// <summary>
/// Calculate the CRC32 for data.
/// </summary>
public void Append(ReadOnlySpan<byte> data)
{
for (var i = 0; i < data.Length; i++)
{
var index = (state ^ data[i]) & 0xFF;
state = (state >> 8) ^ Lookup[index];
}
}
public uint GetCurrentHashAsUInt32()
{
return state ^ uint.MaxValue;
}
public void Reset()
{
state = uint.MaxValue;
}
}
}

View File

@ -1,14 +1,13 @@
namespace UglyToad.PdfPig.Images.Png
{
using System;
using System.Collections.Generic;
using System.Buffers.Binary;
using System.IO;
using System.Linq;
internal class PngStreamWriteHelper : Stream
internal sealed class PngStreamWriteHelper : Stream
{
private readonly Stream inner;
private readonly List<byte> written = new List<byte>();
private readonly Crc32 crc = new();
public override bool CanRead => inner.CanRead;
@ -33,7 +32,7 @@
public void WriteChunkHeader(ReadOnlySpan<byte> header)
{
written.Clear();
crc.Reset();
Write(header);
}
@ -50,28 +49,33 @@
public override void Write(byte[] buffer, int offset, int count)
{
written.AddRange(buffer.Skip(offset).Take(count));
crc.Append(buffer.AsSpan(offset, count));
inner.Write(buffer, offset, count);
}
#if NET8_0_OR_GREATER
public override void Write(ReadOnlySpan<byte> buffer)
{
written.AddRange(buffer);
crc.Append(buffer);
inner.Write(buffer);
}
#else
public void Write(ReadOnlySpan<byte> buffer)
{
written.AddRange(buffer.ToArray());
crc.Append(buffer);
inner.Write(buffer);
}
#endif
public void WriteCrc()
{
var result = (int)Crc32.Calculate(written);
StreamHelper.WriteBigEndianInt32(inner, result);
Span<byte> buffer = stackalloc byte[4];
var result = crc.GetCurrentHashAsUInt32();
BinaryPrimitives.WriteUInt32BigEndian(buffer, result);
inner.Write(buffer);
}
}
}