// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Buffers;
using System.Security.Cryptography;
using System.Text;
using System.Buffers.Binary;
namespace EpicGames.Core
{
///
/// Struct representing a weakly typed hash value. Counterpart to - a strongly typed digest.
///
public struct Digest
{
///
/// Memory storing the digest data
///
public ReadOnlyMemory Memory;
///
/// Accessor for the span of memory storing the data
///
public ReadOnlySpan Span => Memory.Span;
///
/// Constructor
///
/// Memory to construct from
public Digest(ReadOnlyMemory Memory)
{
this.Memory = Memory;
}
///
/// Creates a content hash for a block of data, using a given algorithm.
///
/// Data to compute the hash for
/// New content hash instance containing the hash of the data
public static Digest Compute(byte[] Data) where T : DigestTraits, new()
{
using HashAlgorithm Algorithm = Digest.Traits.CreateAlgorithm();
return Algorithm.ComputeHash(Data);
}
///
/// Creates a content hash for a block of data, using a given algorithm.
///
/// Text to compute the hash for
/// New content hash instance containing the hash of the data
public static Digest Compute(string Text) where T : DigestTraits, new()
{
return Compute(Encoding.UTF8.GetBytes(Text));
}
///
/// Creates a content hash for a block of data, using a given algorithm.
///
/// Data to compute the hash for
/// New content hash instance containing the hash of the data
public static Digest Compute(ReadOnlySpan Data) where T : DigestTraits, new()
{
byte[] Value = new byte[Digest.Traits.Length];
using (HashAlgorithm Algorithm = Digest.Traits.CreateAlgorithm())
{
if (!Algorithm.TryComputeHash(Data, Value, out int Written) || Written != Value.Length)
{
throw new InvalidOperationException("Unable to compute hash for buffer");
}
}
return new Digest(Value);
}
///
/// Parses a digest from the given hex string
///
///
///
public static Digest Parse(string Text)
{
return new Digest(StringUtils.ParseHexString(Text));
}
///
/// Parses a digest from the given hex string
///
///
///
public static Digest Parse(string Text) where T : DigestTraits, new()
{
return new Digest(StringUtils.ParseHexString(Text));
}
///
public override bool Equals(object? Obj) => (Obj is Digest Digest) && Digest.Span.SequenceEqual(Span);
///
public override int GetHashCode() => BinaryPrimitives.ReadInt32LittleEndian(Span);
///
public override string ToString() => StringUtils.FormatHexString(Memory.Span);
///
/// Test two hash values for equality
///
///
///
///
public static bool operator ==(Digest A, Digest B)
{
return A.Memory.Span.SequenceEqual(B.Memory.Span);
}
///
/// Test two hash values for equality
///
///
///
///
public static bool operator !=(Digest A, Digest B)
{
return !(A == B);
}
///
/// Implicit conversion operator from memory objects
///
///
public static implicit operator Digest(ReadOnlyMemory Memory)
{
return new Digest(Memory);
}
///
/// Implicit conversion operator from byte arrays
///
///
public static implicit operator Digest(byte[] Memory)
{
return new Digest(Memory);
}
}
///
/// Traits for a hashing algorithm
///
public abstract class DigestTraits
{
///
/// Length of the produced hash
///
public int Length { get; }
///
/// Constructor
///
///
public DigestTraits(int Length)
{
this.Length = Length;
}
///
/// Creates a HashAlgorithm object
///
///
public abstract HashAlgorithm CreateAlgorithm();
}
///
/// Traits for the MD5 hash algorithm
///
public class Md5 : DigestTraits
{
///
/// Length of the produced digest
///
public new const int Length = 16;
///
/// Constructor
///
public Md5()
: base(Length)
{
}
///
public override HashAlgorithm CreateAlgorithm() => MD5.Create();
}
///
/// Traits for the SHA1 hash algorithm
///
public class Sha1 : DigestTraits
{
///
/// Length of the produced digest
///
public new const int Length = 20;
///
/// Constructor
///
public Sha1()
: base(Length)
{
}
///
public override HashAlgorithm CreateAlgorithm() => SHA1.Create();
}
///
/// Traits for the SHA1 hash algorithm
///
public class Sha256 : DigestTraits
{
///
/// Length of the produced digest
///
public new const int Length = 32;
///
/// Constructor
///
public Sha256()
: base(Length)
{
}
///
public override HashAlgorithm CreateAlgorithm() => SHA256.Create();
}
///
/// Generic HashValue implementation
///
public struct Digest where T : DigestTraits, new()
{
///
/// Traits instance
///
public static T Traits { get; } = new T();
///
/// Length of a hash value
///
public static int Length => Traits.Length;
///
/// Zero digest value
///
public static Digest Zero => new Digest(new byte[Traits.Length]);
///
/// Memory storing the digest data
///
public ReadOnlyMemory Memory;
///
/// Accessor for the span of memory storing the data
///
public ReadOnlySpan Span => Memory.Span;
///
/// Constructor
///
/// Memory to construct from
public Digest(ReadOnlyMemory Memory)
{
this.Memory = Memory;
}
///
public override bool Equals(object? Obj) => (Obj is Digest Hash) && Hash.Span.SequenceEqual(Span);
///
public override int GetHashCode() => BinaryPrimitives.ReadInt32LittleEndian(Span);
///
public override string ToString() => StringUtils.FormatHexString(Memory.Span);
///
/// Test two hash values for equality
///
///
///
///
public static bool operator ==(Digest A, Digest B) => A.Span.SequenceEqual(B.Span);
///
/// Test two hash values for equality
///
///
///
///
public static bool operator !=(Digest A, Digest B) => !A.Span.SequenceEqual(B.Span);
///
/// Implicit conversion operator from memory objects
///
///
public static implicit operator Digest(ReadOnlyMemory Memory)
{
return new Digest(Memory);
}
///
/// Implicit conversion operator from byte arrays
///
///
public static implicit operator Digest(byte[] Memory)
{
return new Digest(Memory);
}
}
///
/// Extension methods for dealing with digests
///
public static class DigestExtensions
{
///
/// Read a digest from a memory reader
///
///
///
public static Digest ReadDigest(this MemoryReader Reader)
{
return new Digest(Reader.ReadVariableLengthBytes());
}
///
/// Read a strongly-typed digest from a memory reader
///
///
///
///
public static Digest ReadDigest(this MemoryReader Reader) where T : DigestTraits, new()
{
return new Digest(Reader.ReadFixedLengthBytes(Digest.Traits.Length));
}
///
/// Write a digest to a memory writer
///
///
///
public static void WriteDigest(this MemoryWriter Writer, Digest Digest)
{
Writer.WriteVariableLengthBytes(Digest.Span);
}
///
/// Write a strongly typed digest to a memory writer
///
///
///
///
public static void WriteDigest(this MemoryWriter Writer, Digest Digest) where T : DigestTraits, new()
{
Writer.WriteFixedLengthBytes(Digest.Span);
}
}
}