mirror of
https://github.com/oliverbooth/X10D
synced 2024-11-26 06:08:48 +00:00
parent
ae24d94da2
commit
cfa27e0b4f
@ -10,6 +10,7 @@
|
||||
- Added time-related extension methods (`Ticks`, `Milliseconds`, `Seconds`, `Minutes`, `Hours`, `Days`, and `Weeks`) to all built-in numeric types
|
||||
- Added `TimeSpan.Ago()` and `TimeSpan.FromNow()`
|
||||
- Added `FileInfo.GetHash<T>()`
|
||||
- Added `FileInfo.TryWriteHash<T>(Span<byte>, out int)`
|
||||
- Added `IEnumerable<TSource>.Product()` and `IEnumerable<TSource>.Product<TResult>(Func<TSource>, TResult)` for all built-in numeric types, computing the product of all (transformed) elements
|
||||
- Added `IEnumerable<T>.Shuffled([Random])`, which wraps `IList<T>.Shuffle([Random])` and returns the result
|
||||
- Added `T.AsArray()`
|
||||
@ -126,6 +127,8 @@
|
||||
- Added `Type.SelectFromCustomAttribute<TAttribute, TReturn>()`
|
||||
- Added `DateTimeOffset` extensions which supersede `DateTime` extensions
|
||||
- Added `Endianness` enum
|
||||
- Added `Stream.GetHash<T>()`
|
||||
- Added `Stream.TryWriteHash<T>(Span<byte>, out int)`
|
||||
- Added `Stream.ReadInt16([Endian])`
|
||||
- Added `Stream.ReadInt32([Endian])`
|
||||
- Added `Stream.ReadInt64([Endian])`
|
||||
|
@ -1,48 +0,0 @@
|
||||
using System.Security.Cryptography;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace X10D.Tests.Core;
|
||||
|
||||
[TestClass]
|
||||
public class FileInfoTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void GetHash()
|
||||
{
|
||||
string fileName = $"temp.{DateTimeOffset.Now.ToUnixTimeSeconds()}.bin";
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
Assert.Fail("Temporary file already exists");
|
||||
}
|
||||
|
||||
const string expectedHash = "0A4D55A8D778E5022FAB701977C5D840BBC486D0"; // SHA-1
|
||||
File.WriteAllText(fileName, "Hello World");
|
||||
Assert.IsTrue(File.Exists(fileName), $"File.Exists(\"{fileName}\")");
|
||||
|
||||
byte[] hash = new FileInfo(fileName).GetHash<SHA1>();
|
||||
string actualHash = BitConverter.ToString(hash).Replace("-", "").ToUpperInvariant();
|
||||
Assert.AreEqual(expectedHash, actualHash);
|
||||
|
||||
File.Delete(fileName); // cleanup is important
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetHash_NullFileInfo()
|
||||
{
|
||||
// any HashAlgorithm will do, but SHA1 is used above. so to remain consistent, we use it here
|
||||
Assert.ThrowsException<ArgumentNullException>(() => ((FileInfo?)null)!.GetHash<SHA1>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetHash_InvalidFile()
|
||||
{
|
||||
string fileName = $"temp.{DateTimeOffset.Now.ToUnixTimeSeconds()}.bin";
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
Assert.Fail("Temporary file already exists");
|
||||
}
|
||||
|
||||
// any HashAlgorithm will do, but SHA1 is used above. so to remain consistent, we use it here
|
||||
Assert.ThrowsException<FileNotFoundException>(() => new FileInfo(fileName).GetHash<SHA1>());
|
||||
}
|
||||
}
|
93
X10D.Tests/src/IO/FileInfoTests.cs
Normal file
93
X10D.Tests/src/IO/FileInfoTests.cs
Normal file
@ -0,0 +1,93 @@
|
||||
using System.Security.Cryptography;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using X10D.IO;
|
||||
|
||||
namespace X10D.Tests.IO;
|
||||
|
||||
[TestClass]
|
||||
public class FileInfoTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void GetHashSha1ShouldBeCorrect()
|
||||
{
|
||||
string fileName = $"temp.{DateTimeOffset.Now.ToUnixTimeSeconds()}.bin";
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
Assert.Fail("Temporary file already exists");
|
||||
}
|
||||
|
||||
File.WriteAllText(fileName, "Hello World");
|
||||
Assert.IsTrue(File.Exists(fileName));
|
||||
|
||||
// SHA-1
|
||||
byte[] expectedHash =
|
||||
{
|
||||
0x0A, 0x4D, 0x55, 0xA8, 0xD7, 0x78, 0xE5, 0x02, 0x2F, 0xAB, 0x70, 0x19, 0x77, 0xC5, 0xD8, 0x40, 0xBB, 0xC4, 0x86,
|
||||
0xD0
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
byte[] hash = new FileInfo(fileName).GetHash<SHA1>();
|
||||
CollectionAssert.AreEqual(expectedHash, hash);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(fileName); // cleanup is important
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TryWriteHashSha1ShouldBeCorrect()
|
||||
{
|
||||
string fileName = $"temp.{DateTimeOffset.Now.ToUnixTimeSeconds()}.bin";
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
Assert.Fail("Temporary file already exists");
|
||||
}
|
||||
|
||||
File.WriteAllText(fileName, "Hello World");
|
||||
Assert.IsTrue(File.Exists(fileName));
|
||||
|
||||
// SHA-1
|
||||
byte[] expectedHash =
|
||||
{
|
||||
0x0A, 0x4D, 0x55, 0xA8, 0xD7, 0x78, 0xE5, 0x02, 0x2F, 0xAB, 0x70, 0x19, 0x77, 0xC5, 0xD8, 0x40, 0xBB, 0xC4, 0x86,
|
||||
0xD0
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
Span<byte> hash = stackalloc byte[20];
|
||||
new FileInfo(fileName).TryWriteHash<SHA1>(hash, out int bytesWritten);
|
||||
Assert.AreEqual(expectedHash.Length, bytesWritten);
|
||||
CollectionAssert.AreEqual(expectedHash, hash.ToArray());
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(fileName); // cleanup is important
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetHashNullShouldThrow()
|
||||
{
|
||||
// any HashAlgorithm will do, but SHA1 is used above. so to remain consistent, we use it here
|
||||
Assert.ThrowsException<ArgumentNullException>(() => ((FileInfo?)null)!.GetHash<SHA1>());
|
||||
Assert.ThrowsException<ArgumentNullException>(() => ((FileInfo?)null)!.TryWriteHash<SHA1>(Span<byte>.Empty, out _));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetHashInvalidFileShouldThrow()
|
||||
{
|
||||
string fileName = $"temp.{DateTimeOffset.Now.ToUnixTimeSeconds()}.bin";
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
Assert.Fail("Temporary file already exists");
|
||||
}
|
||||
|
||||
// any HashAlgorithm will do, but SHA1 is used above. so to remain consistent, we use it here
|
||||
Assert.ThrowsException<FileNotFoundException>(() => new FileInfo(fileName).GetHash<SHA1>());
|
||||
Assert.ThrowsException<FileNotFoundException>(() => new FileInfo(fileName).TryWriteHash<SHA1>(Span<byte>.Empty, out _));
|
||||
}
|
||||
}
|
59
X10D.Tests/src/IO/StreamTests.cs
Normal file
59
X10D.Tests/src/IO/StreamTests.cs
Normal file
@ -0,0 +1,59 @@
|
||||
using System.Diagnostics;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using X10D.IO;
|
||||
|
||||
namespace X10D.Tests.IO;
|
||||
|
||||
[TestClass]
|
||||
public class StreamTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void GetHashSha1ShouldBeCorrect()
|
||||
{
|
||||
// SHA-1
|
||||
byte[] expectedHash =
|
||||
{
|
||||
0x0A, 0x4D, 0x55, 0xA8, 0xD7, 0x78, 0xE5, 0x02, 0x2F, 0xAB, 0x70, 0x19, 0x77, 0xC5, 0xD8, 0x40, 0xBB, 0xC4, 0x86,
|
||||
0xD0
|
||||
};
|
||||
|
||||
using var stream = new MemoryStream();
|
||||
stream.Write(Encoding.UTF8.GetBytes("Hello World"));
|
||||
stream.Position = 0;
|
||||
|
||||
byte[] hash = stream.GetHash<SHA1>();
|
||||
Trace.WriteLine($"Hash: {BitConverter.ToString(hash)}");
|
||||
Trace.WriteLine($"Expected: {BitConverter.ToString(expectedHash)}");
|
||||
CollectionAssert.AreEqual(expectedHash, hash);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetHashNullShouldThrow()
|
||||
{
|
||||
// any HashAlgorithm will do, but SHA1 is used above. so to remain consistent, we use it here
|
||||
Assert.ThrowsException<ArgumentNullException>(() => ((Stream?)null)!.GetHash<SHA1>());
|
||||
Assert.ThrowsException<ArgumentNullException>(() => ((Stream?)null)!.TryWriteHash<SHA1>(Span<byte>.Empty, out _));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TryWriteHashSha1ShouldBeCorrect()
|
||||
{
|
||||
// SHA-1
|
||||
byte[] expectedHash =
|
||||
{
|
||||
0x0A, 0x4D, 0x55, 0xA8, 0xD7, 0x78, 0xE5, 0x02, 0x2F, 0xAB, 0x70, 0x19, 0x77, 0xC5, 0xD8, 0x40, 0xBB, 0xC4, 0x86,
|
||||
0xD0
|
||||
};
|
||||
|
||||
using var stream = new MemoryStream();
|
||||
stream.Write(Encoding.UTF8.GetBytes("Hello World"));
|
||||
stream.Position = 0;
|
||||
|
||||
Span<byte> hash = stackalloc byte[20];
|
||||
stream.TryWriteHash<SHA1>(hash, out int bytesWritten);
|
||||
Assert.AreEqual(expectedHash.Length, bytesWritten);
|
||||
CollectionAssert.AreEqual(expectedHash, hash.ToArray());
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace X10D;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="FileInfo" />.
|
||||
/// </summary>
|
||||
public static class FileInfoExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes the hash of a file using the specified hash algorithm.
|
||||
/// </summary>
|
||||
/// <param name="value">The file whose hash to compute.</param>
|
||||
/// <typeparam name="T">A <see cref="HashAlgorithm" /> derived type.</typeparam>
|
||||
/// <returns>A <see cref="byte" /> array representing the hash of the file.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="value" /> is <see langword="null" />.</exception>
|
||||
/// <exception cref="FileNotFoundException">The specified file was not found.</exception>
|
||||
/// <exception cref="IOException">The opened file stream cannot be read.</exception>
|
||||
/// <exception cref="TypeInitializationException">
|
||||
/// The specified <see cref="HashAlgorithm" /> does not offer a public, static. parameterless <c>Create</c> method, or its
|
||||
/// <c>Create</c> method returns a type that is not assignable to <typeparamref name="T" />.
|
||||
/// </exception>
|
||||
/// <exception cref="ObjectDisposedException">The stream has already been disposed.</exception>
|
||||
public static byte[] GetHash<T>(this FileInfo value)
|
||||
where T : HashAlgorithm
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
using FileStream stream = value.OpenRead();
|
||||
return stream.GetHash<T>();
|
||||
}
|
||||
}
|
74
X10D/src/IO/FileInfoExtensions.cs
Normal file
74
X10D/src/IO/FileInfoExtensions.cs
Normal file
@ -0,0 +1,74 @@
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace X10D.IO;
|
||||
|
||||
/// <summary>
|
||||
/// IO-related extension methods for <see cref="FileInfo" />.
|
||||
/// </summary>
|
||||
public static class FileInfoExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes the hash of a file using the specified hash algorithm.
|
||||
/// </summary>
|
||||
/// <param name="value">The file whose hash to compute.</param>
|
||||
/// <typeparam name="T">
|
||||
/// The type of the <see cref="HashAlgorithm" /> whose <see cref="HashAlgorithm.ComputeHash(Stream)" /> is to be used for
|
||||
/// computing the hash.
|
||||
/// </typeparam>
|
||||
/// <returns>The hash of <paramref name="stream" /> represented as an array of bytes.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="value" /> is <see langword="null" />.</exception>
|
||||
/// <exception cref="FileNotFoundException">The specified file was not found.</exception>
|
||||
/// <exception cref="IOException">The opened file stream cannot be read.</exception>
|
||||
/// <exception cref="TypeInitializationException">
|
||||
/// The specified <see cref="HashAlgorithm" /> does not offer a public, static. parameterless <c>Create</c> method, or its
|
||||
/// <c>Create</c> method returns a type that is not assignable to <typeparamref name="T" />.
|
||||
/// </exception>
|
||||
/// <exception cref="ObjectDisposedException">The stream has already been disposed.</exception>
|
||||
public static byte[] GetHash<T>(this FileInfo value)
|
||||
where T : HashAlgorithm
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
using FileStream stream = value.OpenRead();
|
||||
return stream.GetHash<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the hash of a file using the specified hash algorithm.
|
||||
/// </summary>
|
||||
/// <param name="value">The file whose hash to compute.</param>
|
||||
/// <param name="destination">When this method returns, contains the computed hash of <paramref name="value" />.</param>
|
||||
/// <param name="bytesWritten">
|
||||
/// When this method returns, the total number of bytes written into destination. This parameter is treated as
|
||||
/// uninitialized.
|
||||
/// </param>
|
||||
/// <typeparam name="T">
|
||||
/// The type of the <see cref="HashAlgorithm" /> whose <see cref="HashAlgorithm.ComputeHash(Stream)" /> is to be used for
|
||||
/// computing the hash.
|
||||
/// </typeparam>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if the destination is long enough to receive the hash; otherwise, <see langword="false" />.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="value" /> is <see langword="null" />.</exception>
|
||||
/// <exception cref="FileNotFoundException">The specified file was not found.</exception>
|
||||
/// <exception cref="IOException">The opened file stream cannot be read.</exception>
|
||||
/// <exception cref="TypeInitializationException">
|
||||
/// The specified <see cref="HashAlgorithm" /> does not offer a public, static. parameterless <c>Create</c> method, or its
|
||||
/// <c>Create</c> method returns a type that is not assignable to <typeparamref name="T" />.
|
||||
/// </exception>
|
||||
/// <exception cref="ObjectDisposedException">The stream has already been disposed.</exception>
|
||||
public static bool TryWriteHash<T>(this FileInfo value, Span<byte> destination, out int bytesWritten)
|
||||
where T : HashAlgorithm
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
using FileStream stream = value.OpenRead();
|
||||
return stream.TryWriteHash<T>(destination, out bytesWritten);
|
||||
}
|
||||
}
|
122
X10D/src/IO/StreamExtensions.cs
Normal file
122
X10D/src/IO/StreamExtensions.cs
Normal file
@ -0,0 +1,122 @@
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace X10D.IO;
|
||||
|
||||
/// <summary>
|
||||
/// IO-related extension methods for <see cref="Stream" />.
|
||||
/// </summary>
|
||||
public static class StreamExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the hash of the current stream as an array of bytes using the specified hash algorithm.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream whose hash is to be computed.</param>
|
||||
/// <typeparam name="T">
|
||||
/// The type of the <see cref="HashAlgorithm" /> whose <see cref="HashAlgorithm.ComputeHash(Stream)" /> is to be used for
|
||||
/// computing the hash.
|
||||
/// </typeparam>
|
||||
/// <returns>The hash of <paramref name="stream" /> represented as an array of bytes.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="stream" /> is <see langword="null" /></exception>
|
||||
/// <exception cref="IOException"><paramref name="stream" /> does not support reading.</exception>
|
||||
/// <exception cref="TypeInitializationException">
|
||||
/// The specified <see cref="HashAlgorithm" /> does not offer a public, static. parameterless <c>Create</c> method, or its
|
||||
/// <c>Create</c> method returns a type that is not assignable to <typeparamref name="T" />.
|
||||
/// </exception>
|
||||
/// <exception cref="ObjectDisposedException">The stream has already been disposed.</exception>
|
||||
public static byte[] GetHash<T>(this Stream stream)
|
||||
where T : HashAlgorithm
|
||||
{
|
||||
if (stream is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
}
|
||||
|
||||
if (!stream.CanRead)
|
||||
{
|
||||
throw new IOException(ExceptionMessages.StreamDoesNotSupportReading);
|
||||
}
|
||||
|
||||
Type type = typeof(T);
|
||||
|
||||
MethodInfo? createMethod = type.GetMethods(BindingFlags.Public | BindingFlags.Static)
|
||||
.FirstOrDefault(c => c.Name == "Create" && c.GetParameters().Length == 0);
|
||||
if (createMethod is null)
|
||||
{
|
||||
throw new TypeInitializationException(type.FullName,
|
||||
new ArgumentException(ExceptionMessages.HashAlgorithmNoCreateMethod));
|
||||
}
|
||||
|
||||
using var crypt = createMethod.Invoke(null, null) as T;
|
||||
if (crypt is null)
|
||||
{
|
||||
throw new TypeInitializationException(type.FullName,
|
||||
new ArgumentException(ExceptionMessages.HashAlgorithmCreateReturnedNull));
|
||||
}
|
||||
|
||||
return crypt.ComputeHash(stream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the hash of the current stream as an array of bytes using the specified hash algorithm.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream whose hash is to be computed.</param>
|
||||
/// <param name="destination">When this method returns, contains the computed hash of <paramref name="stream" />.</param>
|
||||
/// <param name="bytesWritten">
|
||||
/// When this method returns, the total number of bytes written into destination. This parameter is treated as
|
||||
/// uninitialized.
|
||||
/// </param>
|
||||
/// <typeparam name="T">
|
||||
/// The type of the <see cref="HashAlgorithm" /> whose <see cref="HashAlgorithm.ComputeHash(Stream)" /> is to be used for
|
||||
/// computing the hash.
|
||||
/// </typeparam>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if the destination is long enough to receive the hash; otherwise, <see langword="false" />.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="stream" /> is <see langword="null" /></exception>
|
||||
/// <exception cref="IOException"><paramref name="stream" /> does not support reading.</exception>
|
||||
/// <exception cref="TypeInitializationException">
|
||||
/// The specified <see cref="HashAlgorithm" /> does not offer a public, static. parameterless <c>Create</c> method, or its
|
||||
/// <c>Create</c> method returns a type that is not assignable to <typeparamref name="T" />.
|
||||
/// </exception>
|
||||
/// <exception cref="ObjectDisposedException">The stream has already been disposed.</exception>
|
||||
public static bool TryWriteHash<T>(this Stream stream, Span<byte> destination, out int bytesWritten)
|
||||
where T : HashAlgorithm
|
||||
{
|
||||
if (stream is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
}
|
||||
|
||||
if (!stream.CanRead)
|
||||
{
|
||||
throw new IOException(ExceptionMessages.StreamDoesNotSupportReading);
|
||||
}
|
||||
|
||||
Type type = typeof(T);
|
||||
|
||||
MethodInfo? createMethod = type.GetMethods(BindingFlags.Public | BindingFlags.Static)
|
||||
.FirstOrDefault(c => c.Name == "Create" && c.GetParameters().Length == 0);
|
||||
if (createMethod is null)
|
||||
{
|
||||
throw new TypeInitializationException(type.FullName,
|
||||
new ArgumentException(ExceptionMessages.HashAlgorithmNoCreateMethod));
|
||||
}
|
||||
|
||||
using var crypt = createMethod.Invoke(null, null) as T;
|
||||
if (crypt is null)
|
||||
{
|
||||
throw new TypeInitializationException(type.FullName,
|
||||
new ArgumentException(ExceptionMessages.HashAlgorithmCreateReturnedNull));
|
||||
}
|
||||
|
||||
if (stream.Length > int.MaxValue)
|
||||
{
|
||||
throw new ArgumentException(ExceptionMessages.StreamTooLarge);
|
||||
}
|
||||
|
||||
Span<byte> buffer = stackalloc byte[(int)stream.Length];
|
||||
_ = stream.Read(buffer); // we don't care about the number of bytes read. we can ignore MustUseReturnValue
|
||||
return crypt.TryComputeHash(buffer, destination, out bytesWritten);
|
||||
}
|
||||
}
|
@ -1,6 +1,4 @@
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Buffers.Binary;
|
||||
using System.Buffers.Binary;
|
||||
|
||||
namespace X10D;
|
||||
|
||||
@ -12,53 +10,6 @@ public static partial class StreamExtensions
|
||||
private static readonly Endianness DefaultEndianness =
|
||||
BitConverter.IsLittleEndian ? Endianness.LittleEndian : Endianness.BigEndian;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the hash of a stream using the specified hash algorithm.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">A <see cref="HashAlgorithm" /> derived type.</typeparam>
|
||||
/// <param name="stream">The stream whose hash is to be computed.</param>
|
||||
/// <returns>A <see cref="byte" /> array representing the hash of the stream.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="stream" /> is <see langword="null" />.</exception>
|
||||
/// <exception cref="IOException">The stream does not support reading.</exception>
|
||||
/// <exception cref="TypeInitializationException">
|
||||
/// The specified <see cref="HashAlgorithm" /> does not offer a public, static. parameterless <c>Create</c> method, or its
|
||||
/// <c>Create</c> method returns a type that is not assignable to <typeparamref name="T" />.
|
||||
/// </exception>
|
||||
/// <exception cref="ObjectDisposedException">The stream has already been disposed.</exception>
|
||||
/// <remarks>This method consumes the stream from its current position!.</remarks>
|
||||
public static byte[] GetHash<T>(this Stream stream)
|
||||
where T : HashAlgorithm
|
||||
{
|
||||
if (stream is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
}
|
||||
|
||||
if (!stream.CanRead)
|
||||
{
|
||||
throw new IOException(ExceptionMessages.StreamDoesNotSupportReading);
|
||||
}
|
||||
|
||||
Type type = typeof(T);
|
||||
|
||||
MethodInfo? createMethod = type.GetMethods(BindingFlags.Public | BindingFlags.Static)
|
||||
.FirstOrDefault(c => c.Name == "Create" && c.GetParameters().Length == 0);
|
||||
if (createMethod is null)
|
||||
{
|
||||
throw new TypeInitializationException(type.FullName,
|
||||
new ArgumentException(ExceptionMessages.HashAlgorithmNoCreateMethod));
|
||||
}
|
||||
|
||||
using var crypt = createMethod.Invoke(null, null) as T;
|
||||
if (crypt is null)
|
||||
{
|
||||
throw new TypeInitializationException(type.FullName,
|
||||
new ArgumentException(ExceptionMessages.HashAlgorithmCreateReturnedNull));
|
||||
}
|
||||
|
||||
return crypt.ComputeHash(stream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a decimal value from the current stream using the system's default endian encoding, and advances the stream
|
||||
/// position by sixteen bytes.
|
||||
|
Loading…
Reference in New Issue
Block a user