Files
SabreTools.Hashing/HashWrapper.cs
2024-03-03 22:18:32 -05:00

206 lines
5.7 KiB
C#

using System;
#if NET462_OR_GREATER || NETCOREAPP
using System.IO.Hashing;
#endif
#if NET35_OR_GREATER || NETCOREAPP
using System.Linq;
#endif
using System.Security.Cryptography;
using Aaru.Checksums;
using Aaru.CommonTypes.Interfaces;
namespace SabreTools.Hashing
{
/// <summary>
/// Wrapper for a single hashing algorithm
/// </summary>
public class HashWrapper : IDisposable
{
#region Properties
/// <summary>
/// Hash type associated with the current state
/// </summary>
#if NETFRAMEWORK || NETCOREAPP3_1
public HashType HashType { get; private set; }
#else
public HashType HashType { get; init; }
#endif
/// <summary>
/// Current hash in bytes
/// </summary>
public byte[]? CurrentHashBytes
{
get
{
return _hasher switch
{
HashAlgorithm ha => ha.Hash,
IChecksum ic => ic.Final(),
#if NET462_OR_GREATER || NETCOREAPP
NonCryptographicHashAlgorithm ncha => ncha.GetCurrentHash().Reverse().ToArray(),
#endif
_ => null,
};
}
}
/// <summary>
/// Current hash as a string
/// </summary>
public string? CurrentHashString
{
get
{
return _hasher switch
{
IChecksum ic => ic.End(),
_ => ByteArrayToString(CurrentHashBytes),
};
}
}
#endregion
#region Private Fields
/// <summary>
/// Internal hasher being used for processing
/// </summary>
/// <remarks>May be either a HashAlgorithm or NonCryptographicHashAlgorithm</remarks>
private object? _hasher;
#endregion
#region Constructors
/// <summary>
/// Constructor
/// </summary>
/// <param name="hashType">Hash type to instantiate</param>
public HashWrapper(HashType hashType)
{
this.HashType = hashType;
GetHasher();
}
/// <summary>
/// Generate the correct hashing class based on the hash type
/// </summary>
private void GetHasher()
{
_hasher = HashType switch
{
#if NET462_OR_GREATER || NETCOREAPP
HashType.CRC32 => new Crc32(),
HashType.CRC64 => new Crc64(),
#else
HashType.CRC32 => new OptimizedCRC.OptimizedCRC(),
#endif
HashType.MD5 => MD5.Create(),
HashType.SHA1 => SHA1.Create(),
HashType.SHA256 => SHA256.Create(),
HashType.SHA384 => SHA384.Create(),
HashType.SHA512 => SHA512.Create(),
HashType.SpamSum => new SpamSumContext(),
#if NET462_OR_GREATER || NETCOREAPP
HashType.XxHash32 => new XxHash32(),
HashType.XxHash64 => new XxHash64(),
HashType.XxHash3 => new XxHash3(),
HashType.XxHash128 => new XxHash128(),
#endif
_ => null,
};
}
/// <inheritdoc/>
public void Dispose()
{
if (_hasher is IDisposable disposable)
disposable.Dispose();
}
#endregion
#region Hashing
/// <summary>
/// Process a buffer of some length with the internal hash algorithm
/// </summary>
public void Process(byte[] buffer, int size)
{
switch (_hasher)
{
case HashAlgorithm ha:
ha.TransformBlock(buffer, 0, size, null, 0);
break;
case IChecksum ic:
ic.Update(buffer);
break;
#if NET462_OR_GREATER || NETCOREAPP
case NonCryptographicHashAlgorithm ncha:
var bufferSpan = new ReadOnlySpan<byte>(buffer, 0, size);
ncha.Append(bufferSpan);
break;
#else
case OptimizedCRC.OptimizedCRC oc:
oc.Update(buffer, 0, size);
break;
#endif
}
}
/// <summary>
/// Finalize the internal hash algorigthm
/// </summary>
/// <remarks>NonCryptographicHashAlgorithm implementations do not need finalization</remarks>
public void Terminate()
{
byte[] emptyBuffer = [];
switch (_hasher)
{
case HashAlgorithm ha:
ha.TransformFinalBlock(emptyBuffer, 0, 0);
break;
#if NET20_OR_GREATER || NETCOREAPP3_1 || NET5_0
case OptimizedCRC.OptimizedCRC oc:
oc.Update([], 0, 0);
break;
#endif
}
}
#endregion
#region Helpers
/// <summary>
/// Convert a byte array to a hex string
/// </summary>
/// <param name="bytes">Byte array to convert</param>
/// <returns>Hex string representing the byte array</returns>
/// <link>http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa</link>
private static string? ByteArrayToString(byte[]? bytes)
{
// If we get null in, we send null out
if (bytes == null)
return null;
try
{
string hex = BitConverter.ToString(bytes);
return hex.Replace("-", string.Empty).ToLowerInvariant();
}
catch
{
return null;
}
}
#endregion
}
}