// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace Tools.DotNETCommon
{
///
/// Stores the hash value for a piece of content as a byte array, allowing it to be used as a dictionary key
///
public class ContentHash : IEquatable
{
///
/// The bytes compromising this hash
///
public byte[] Bytes
{
get;
private set;
}
///
/// Constructor
///
/// The hash data
public ContentHash(byte[] Bytes)
{
this.Bytes = Bytes;
}
///
/// Compares two content hashes for equality
///
/// The object to compare against
/// True if the hashes are equal, false otherwise
public override bool Equals(object Other)
{
return Equals(Other as ContentHash);
}
///
/// Compares two content hashes for equality
///
/// The hash to compare against
/// True if the hashes are equal, false otherwise
public bool Equals(ContentHash Other)
{
if((object)Other == null)
{
return false;
}
if(Bytes.Length != Other.Bytes.Length)
{
return false;
}
for(int Idx = 0; Idx < Bytes.Length; Idx++)
{
if(Bytes[Idx] != Other.Bytes[Idx])
{
return false;
}
}
return true;
}
///
/// Compares two content hash objects for equality
///
/// The first hash to compare
/// The second has to compare
/// True if the objects are equal, false otherwise
public static bool operator==(ContentHash A, ContentHash B)
{
if((object)A == null)
{
return ((object)B == null);
}
else
{
return A.Equals(B);
}
}
///
/// Compares two content hash objects for inequality
///
/// The first hash to compare
/// The second has to compare
/// True if the objects are not equal, false otherwise
public static bool operator!=(ContentHash A, ContentHash B)
{
return !(A == B);
}
///
/// Creates a content hash for a block of data, using a given algorithm.
///
/// Data to compute the hash for
/// Algorithm to use to create the hash
/// New content hash instance containing the hash of the data
public static ContentHash Compute(byte[] Data, HashAlgorithm Algorithm)
{
return new ContentHash(Algorithm.ComputeHash(Data));
}
///
/// Creates a content hash for a string, using a given algorithm.
///
/// Text to compute a hash for
/// Algorithm to use to create the hash
/// New content hash instance containing the hash of the text
public static ContentHash Compute(string Text, HashAlgorithm Algorithm)
{
return new ContentHash(Algorithm.ComputeHash(Encoding.Unicode.GetBytes(Text)));
}
///
/// Creates a content hash for a file, using a given algorithm.
///
/// File to compute a hash for
/// Algorithm to use to create the hash
/// New content hash instance containing the hash of the file
public static ContentHash Compute(FileReference Location, HashAlgorithm Algorithm)
{
using(FileStream Stream = FileReference.Open(Location, FileMode.Open, FileAccess.Read, FileShare.Read))
{
return new ContentHash(Algorithm.ComputeHash(Stream));
}
}
///
/// Creates a content hash for a block of data using MD5
///
/// Data to compute the hash for
/// New content hash instance containing the hash of the data
public static ContentHash MD5(byte[] Data)
{
using(MD5 Algorithm = System.Security.Cryptography.MD5.Create())
{
return Compute(Data, Algorithm);
}
}
///
/// Creates a content hash for a string using MD5.
///
/// Text to compute a hash for
/// New content hash instance containing the hash of the text
public static ContentHash MD5(string Text)
{
using(MD5 Algorithm = System.Security.Cryptography.MD5.Create())
{
return Compute(Text, Algorithm);
}
}
///
/// Creates a content hash for a file, using a given algorithm.
///
/// File to compute a hash for
/// New content hash instance containing the hash of the file
public static ContentHash MD5(FileReference Location)
{
using(MD5 Algorithm = System.Security.Cryptography.MD5.Create())
{
return Compute(Location, Algorithm);
}
}
///
/// Creates a content hash for a block of data using SHA1
///
/// Data to compute the hash for
/// New content hash instance containing the hash of the data
public static ContentHash SHA1(byte[] Data)
{
using(SHA1 Algorithm = System.Security.Cryptography.SHA1.Create())
{
return Compute(Data, Algorithm);
}
}
///
/// Creates a content hash for a string using SHA1.
///
/// Text to compute a hash for
/// New content hash instance containing the hash of the text
public static ContentHash SHA1(string Text)
{
using(SHA1 Algorithm = System.Security.Cryptography.SHA1.Create())
{
return Compute(Text, Algorithm);
}
}
///
/// Creates a content hash for a file using SHA1.
///
/// File to compute a hash for
/// New content hash instance containing the hash of the file
public static ContentHash SHA1(FileReference Location)
{
using(SHA1 Algorithm = System.Security.Cryptography.SHA1.Create())
{
return Compute(Location, Algorithm);
}
}
///
/// Computes a hash code for this digest
///
/// Integer value to use as a hash code
public override int GetHashCode()
{
int HashCode = Bytes[0];
for(int Idx = 1; Idx < Bytes.Length; Idx++)
{
HashCode = (HashCode * 31) + Bytes[Idx];
}
return HashCode;
}
///
/// Formats this hash as a string
///
/// The hashed value
public override string ToString()
{
return StringUtils.FormatHexString(Bytes);
}
}
///
/// Utility methods for serializing ContentHash objects
///
public static class ContentHashExtensionMethods
{
///
/// Writes a ContentHash to a binary archive
///
/// The writer to output data to
/// The hash to write
public static void WriteContentHash(this BinaryArchiveWriter Writer, ContentHash Hash)
{
if(Hash == null)
{
Writer.WriteByteArray(null);
}
else
{
Writer.WriteByteArray(Hash.Bytes);
}
}
///
/// Reads a ContentHash from a binary archive
///
/// Reader to serialize data from
/// New hash instance
public static ContentHash ReadContentHash(this BinaryArchiveReader Reader)
{
byte[] Data = Reader.ReadByteArray();
if(Data == null)
{
return null;
}
else
{
return new ContentHash(Data);
}
}
}
}