diff --git a/PowerKit.Tests/Adler32Tests.cs b/PowerKit.Tests/Adler32Tests.cs new file mode 100644 index 0000000..9b22e0b --- /dev/null +++ b/PowerKit.Tests/Adler32Tests.cs @@ -0,0 +1,59 @@ +using System; +using System.IO; +using FluentAssertions; +using PowerKit; +using Xunit; + +namespace PowerKit.Tests; + +public class Adler32Tests +{ + [Fact] + public void Hash_Stream_Test() + { + // Arrange + using var stream = new MemoryStream("Wikipedia"u8.ToArray()); + + // Act + var hash = Adler32.Hash(stream); + + // Assert + hash.Should().Be(0x11E60398u); + } + + [Fact] + public void Hash_ByteArray_Test() + { + // Arrange + var data = "Wikipedia"u8.ToArray(); + + // Act + var hash = Adler32.Hash(data); + + // Assert + hash.Should().Be(0x11E60398u); + } + + [Fact] + public void Hash_Span_Test() + { + // Arrange + var data = "Wikipedia"u8.ToArray(); + + // Act + var hash = Adler32.Hash((ReadOnlySpan)data); + + // Assert + hash.Should().Be(0x11E60398u); + } + + [Fact] + public void Hash_EmptyData_Test() + { + // Act + var hash = Adler32.Hash(Array.Empty()); + + // Assert + hash.Should().Be(1u); + } +} diff --git a/PowerKit.Tests/Crc32Tests.cs b/PowerKit.Tests/Crc32Tests.cs new file mode 100644 index 0000000..aaa6bf7 --- /dev/null +++ b/PowerKit.Tests/Crc32Tests.cs @@ -0,0 +1,59 @@ +using System; +using System.IO; +using FluentAssertions; +using PowerKit; +using Xunit; + +namespace PowerKit.Tests; + +public class Crc32Tests +{ + [Fact] + public void Hash_Stream_Test() + { + // Arrange + using var stream = new MemoryStream("123456789"u8.ToArray()); + + // Act + var hash = Crc32.Hash(stream); + + // Assert + hash.Should().Be(0xCBF43926u); + } + + [Fact] + public void Hash_ByteArray_Test() + { + // Arrange + var data = "123456789"u8.ToArray(); + + // Act + var hash = Crc32.Hash(data); + + // Assert + hash.Should().Be(0xCBF43926u); + } + + [Fact] + public void Hash_Span_Test() + { + // Arrange + var data = "123456789"u8.ToArray(); + + // Act + var hash = Crc32.Hash((ReadOnlySpan)data); + + // Assert + hash.Should().Be(0xCBF43926u); + } + + [Fact] + public void Hash_EmptyData_Test() + { + // Act + var hash = Crc32.Hash(Array.Empty()); + + // Assert + hash.Should().Be(0u); + } +} diff --git a/PowerKit.Tests/DeflateTests.cs b/PowerKit.Tests/DeflateTests.cs new file mode 100644 index 0000000..0f2d3c4 --- /dev/null +++ b/PowerKit.Tests/DeflateTests.cs @@ -0,0 +1,85 @@ +using System; +using System.IO; +using FluentAssertions; +using PowerKit; +using Xunit; + +namespace PowerKit.Tests; + +public class DeflateTests +{ + [Fact] + public void Compress_Decompress_Stream_Test() + { + // Arrange + var data = "hello world"u8.ToArray(); + using var input = new MemoryStream(data); + using var compressed = new MemoryStream(); + using var decompressed = new MemoryStream(); + + // Act + Deflate.Compress(input, compressed); + compressed.Position = 0; + Deflate.Decompress(compressed, decompressed); + + // Assert + decompressed.ToArray().Should().Equal(data); + } + + [Fact] + public void Compress_Decompress_ByteArray_Test() + { + // Arrange + var data = "hello world"u8.ToArray(); + + // Act + var compressed = Deflate.Compress(data); + var decompressed = Deflate.Decompress(compressed); + + // Assert + decompressed.Should().Equal(data); + } + + [Fact] + public void Compress_Decompress_Span_Test() + { + // Arrange + var data = "hello world"u8.ToArray(); + + // Act + var compressed = Deflate.Compress((ReadOnlySpan)data); + var decompressed = Deflate.Decompress((ReadOnlySpan)compressed); + + // Assert + decompressed.Should().Equal(data); + } + + [Fact] + public void Compress_Decompress_EmptyData_Test() + { + // Arrange + var data = Array.Empty(); + + // Act + var compressed = Deflate.Compress(data); + var decompressed = Deflate.Decompress(compressed); + + // Assert + decompressed.Should().BeEmpty(); + } + + [Fact] + public void Compress_ProducesCompressedOutput_Test() + { + // Arrange + var data = new byte[1000]; + for (var i = 0; i < data.Length; i++) + data[i] = (byte)(i % 4); + + // Act + var compressed = Deflate.Compress(data); + + // Assert + compressed.Should().HaveCountLessThan(data.Length); + } +} diff --git a/PowerKit/Adler32.cs b/PowerKit/Adler32.cs new file mode 100644 index 0000000..59c5f53 --- /dev/null +++ b/PowerKit/Adler32.cs @@ -0,0 +1,66 @@ +using System.IO; +#if !NETFRAMEWORK || NET45_OR_GREATER +using System; +#endif + + +namespace PowerKit; + +/// +/// Provides methods for computing Adler-32 checksums. +/// +public static class Adler32 +{ + private const uint Modulus = 65521; + + /// + /// Computes the Adler-32 checksum of data read from the specified stream. + /// + public static uint Hash(Stream stream) + { + uint a = 1, + b = 0; + + var buffer = new byte[4096]; + int read; + + while ((read = stream.Read(buffer, 0, buffer.Length)) > 0) + { + for (var i = 0; i < read; i++) + { + a = (a + buffer[i]) % Modulus; + b = (b + a) % Modulus; + } + } + + return (b << 16) | a; + } + + /// + /// Computes the Adler-32 checksum of the specified data. + /// + public static uint Hash(byte[] data) + { + using var stream = new MemoryStream(data); + return Hash(stream); + } + +#if !NETFRAMEWORK || NET45_OR_GREATER + /// + /// Computes the Adler-32 checksum of the specified data. + /// + public static uint Hash(ReadOnlySpan data) + { + uint a = 1, + b = 0; + + for (var i = 0; i < data.Length; i++) + { + a = (a + data[i]) % Modulus; + b = (b + a) % Modulus; + } + + return (b << 16) | a; + } +#endif +} diff --git a/PowerKit/Crc32.cs b/PowerKit/Crc32.cs new file mode 100644 index 0000000..04342e6 --- /dev/null +++ b/PowerKit/Crc32.cs @@ -0,0 +1,77 @@ +using System.IO; +#if !NETFRAMEWORK || NET45_OR_GREATER +using System; +#endif + + +namespace PowerKit; + +/// +/// Provides methods for computing CRC-32 checksums. +/// +public static class Crc32 +{ + private static readonly uint[] Table = GenerateTable(); + + private static uint[] GenerateTable() + { + var table = new uint[256]; + + for (var i = 0; i < 256; i++) + { + var entry = (uint)i; + for (var j = 0; j < 8; j++) + { + if ((entry & 1) != 0) + entry = (entry >> 1) ^ 0xEDB88320u; + else + entry >>= 1; + } + + table[i] = entry; + } + + return table; + } + + /// + /// Computes the CRC-32 checksum of data read from the specified stream. + /// + public static uint Hash(Stream stream) + { + var crc = 0xFFFFFFFFu; + + var buffer = new byte[4096]; + int read; + + while ((read = stream.Read(buffer, 0, buffer.Length)) > 0) + { + for (var i = 0; i < read; i++) + crc = (crc >> 8) ^ Table[(byte)(crc ^ buffer[i])]; + } + + return crc ^ 0xFFFFFFFFu; + } + + /// + /// Computes the CRC-32 checksum of the specified data. + /// + public static uint Hash(byte[] data) + { + using var stream = new MemoryStream(data); + return Hash(stream); + } + +#if !NETFRAMEWORK || NET45_OR_GREATER + /// + /// Computes the CRC-32 checksum of the specified data. + /// + public static uint Hash(ReadOnlySpan data) + { + var crc = 0xFFFFFFFFu; + for (var i = 0; i < data.Length; i++) + crc = (crc >> 8) ^ Table[(byte)(crc ^ data[i])]; + return crc ^ 0xFFFFFFFFu; + } +#endif +} diff --git a/PowerKit/Deflate.cs b/PowerKit/Deflate.cs new file mode 100644 index 0000000..ae5e256 --- /dev/null +++ b/PowerKit/Deflate.cs @@ -0,0 +1,75 @@ +using System.IO; +using System.IO.Compression; +#if !NETFRAMEWORK || NET45_OR_GREATER +using System; +#endif + +namespace PowerKit; + +/// +/// Provides methods for compressing and decompressing data using the Deflate algorithm. +/// +public static class Deflate +{ + /// + /// Compresses data from the source stream and writes it to the destination stream. + /// + public static void Compress(Stream source, Stream destination) + { + using (var deflateStream = new DeflateStream(destination, CompressionMode.Compress, true)) + { + var buffer = new byte[4096]; + int read; + while ((read = source.Read(buffer, 0, buffer.Length)) > 0) + deflateStream.Write(buffer, 0, read); + } + } + + /// + /// Decompresses data from the source stream and writes it to the destination stream. + /// + public static void Decompress(Stream source, Stream destination) + { + using (var deflateStream = new DeflateStream(source, CompressionMode.Decompress, true)) + { + var buffer = new byte[4096]; + int read; + while ((read = deflateStream.Read(buffer, 0, buffer.Length)) > 0) + destination.Write(buffer, 0, read); + } + } + + /// + /// Compresses the specified data using the Deflate algorithm. + /// + public static byte[] Compress(byte[] data) + { + using var input = new MemoryStream(data); + using var output = new MemoryStream(); + Compress(input, output); + return output.ToArray(); + } + + /// + /// Decompresses the specified data using the Deflate algorithm. + /// + public static byte[] Decompress(byte[] data) + { + using var input = new MemoryStream(data); + using var output = new MemoryStream(); + Decompress(input, output); + return output.ToArray(); + } + +#if !NETFRAMEWORK || NET45_OR_GREATER + /// + /// Compresses the specified data using the Deflate algorithm. + /// + public static byte[] Compress(ReadOnlySpan data) => Compress(data.ToArray()); + + /// + /// Decompresses the specified data using the Deflate algorithm. + /// + public static byte[] Decompress(ReadOnlySpan data) => Decompress(data.ToArray()); +#endif +}