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
{
///
/// Wrapper for a single hashing algorithm
///
public class HashWrapper : IDisposable
{
#region Properties
///
/// Hash type associated with the current state
///
#if NETFRAMEWORK || NETCOREAPP3_1
public HashType HashType { get; private set; }
#else
public HashType HashType { get; init; }
#endif
///
/// Current hash in bytes
///
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,
};
}
}
///
/// Current hash as a string
///
public string? CurrentHashString
{
get
{
return _hasher switch
{
IChecksum ic => ic.End(),
_ => ByteArrayToString(CurrentHashBytes),
};
}
}
#endregion
#region Private Fields
///
/// Internal hasher being used for processing
///
/// May be either a HashAlgorithm or NonCryptographicHashAlgorithm
private object? _hasher;
#endregion
#region Constructors
///
/// Constructor
///
/// Hash type to instantiate
public HashWrapper(HashType hashType)
{
this.HashType = hashType;
GetHasher();
}
///
/// Generate the correct hashing class based on the hash type
///
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,
};
}
///
public void Dispose()
{
if (_hasher is IDisposable disposable)
disposable.Dispose();
}
#endregion
#region Hashing
///
/// Process a buffer of some length with the internal hash algorithm
///
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(buffer, 0, size);
ncha.Append(bufferSpan);
break;
#else
case OptimizedCRC.OptimizedCRC oc:
oc.Update(buffer, 0, size);
break;
#endif
}
}
///
/// Finalize the internal hash algorigthm
///
/// NonCryptographicHashAlgorithm implementations do not need finalization
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
///
/// Convert a byte array to a hex string
///
/// Byte array to convert
/// Hex string representing the byte array
/// http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa
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
}
}