Files
Umbraco-CMS/src/Umbraco.Core/HashGenerator.cs

152 lines
4.8 KiB
C#
Raw Normal View History

using System;
2017-09-15 18:22:19 +02:00
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Umbraco.Cms.Core
2017-09-15 18:22:19 +02:00
{
/// <summary>
/// Used to generate a string hash using crypto libraries over multiple objects
/// </summary>
/// <remarks>
2017-09-23 10:08:18 +02:00
/// This should be used to generate a reliable hash that survives AppDomain restarts.
/// This will use the crypto libs to generate the hash and will try to ensure that
2017-09-15 18:22:19 +02:00
/// strings, etc... are not re-allocated so it's not consuming much memory.
/// </remarks>
2019-05-20 18:40:13 +02:00
public class HashGenerator : DisposableObjectSlim
2017-09-15 18:22:19 +02:00
{
public HashGenerator()
{
_writer = new StreamWriter(_ms, Encoding.Unicode, 1024, leaveOpen: true);
}
private readonly MemoryStream _ms = new MemoryStream();
private StreamWriter _writer;
2019-05-20 18:40:13 +02:00
public void AddInt(int i)
2017-09-15 18:22:19 +02:00
{
_writer.Write(i);
}
2019-05-20 18:40:13 +02:00
public void AddLong(long i)
2017-09-15 18:22:19 +02:00
{
_writer.Write(i);
}
2019-05-20 18:40:13 +02:00
public void AddObject(object o)
2017-09-15 18:22:19 +02:00
{
_writer.Write(o);
}
2019-05-20 18:40:13 +02:00
public void AddDateTime(DateTime d)
2017-09-15 18:22:19 +02:00
{
2020-02-02 01:43:15 +00:00
_writer.Write(d.Ticks);
2017-09-15 18:22:19 +02:00
}
2019-05-20 18:40:13 +02:00
public void AddString(string s)
2017-09-15 18:22:19 +02:00
{
if (s != null)
_writer.Write(s);
}
2019-05-20 18:40:13 +02:00
public void AddCaseInsensitiveString(string s)
2017-09-15 18:22:19 +02:00
{
//I've tried to no allocate a new string with this which can be done if we use the CompareInfo.GetSortKey method which will create a new
//byte array that we can use to write to the output, however this also allocates new objects so i really don't think the performance
2019-01-22 18:03:39 -05:00
//would be much different. In any case, I'll leave this here for reference. We could write the bytes out based on the sort key,
2017-09-15 18:22:19 +02:00
//this is how we could deal with case insensitivity without allocating another string
//for reference see: https://stackoverflow.com/a/10452967/694494
//we could go a step further and s.Normalize() but we're not really dealing with crazy unicode with this class so far.
if (s != null)
2017-09-23 10:08:18 +02:00
_writer.Write(s.ToUpperInvariant());
2017-09-15 18:22:19 +02:00
}
2019-05-20 18:40:13 +02:00
public void AddFileSystemItem(FileSystemInfo f)
2017-09-15 18:22:19 +02:00
{
//if it doesn't exist, don't proceed.
if (f.Exists == false)
return;
AddCaseInsensitiveString(f.FullName);
AddDateTime(f.CreationTimeUtc);
AddDateTime(f.LastWriteTimeUtc);
2017-09-23 10:08:18 +02:00
//check if it is a file or folder
if (f is FileInfo fileInfo)
2017-09-15 18:22:19 +02:00
{
AddLong(fileInfo.Length);
}
if (f is DirectoryInfo dirInfo)
2017-09-15 18:22:19 +02:00
{
foreach (var d in dirInfo.GetFiles())
{
AddFile(d);
}
foreach (var s in dirInfo.GetDirectories())
{
AddFolder(s);
}
}
}
2019-05-20 18:40:13 +02:00
public void AddFile(FileInfo f)
2017-09-15 18:22:19 +02:00
{
AddFileSystemItem(f);
}
2019-05-20 18:40:13 +02:00
public void AddFolder(DirectoryInfo d)
2017-09-15 18:22:19 +02:00
{
AddFileSystemItem(d);
}
/// <summary>
/// Returns the generated hash output of all added objects
/// </summary>
/// <returns></returns>
2019-05-20 18:40:13 +02:00
public string GenerateHash()
2017-09-15 18:22:19 +02:00
{
//flush,close,dispose the writer,then create a new one since it's possible to keep adding after GenerateHash is called.
_writer.Flush();
_writer.Close();
_writer.Dispose();
_writer = new StreamWriter(_ms, Encoding.UTF8, 1024, leaveOpen: true);
var hashType = CryptoConfig.AllowOnlyFipsAlgorithms ? "SHA1" : "MD5";
//create an instance of the correct hashing provider based on the type passed in
var hasher = HashAlgorithm.Create(hashType);
if (hasher == null) throw new InvalidOperationException("No hashing type found by name " + hashType);
using (hasher)
{
var buffer = _ms.GetBuffer();
//get the hashed values created by our selected provider
var hashedByteArray = hasher.ComputeHash(buffer);
//create a StringBuilder object
var stringBuilder = new StringBuilder();
2019-01-22 18:03:39 -05:00
//loop to each byte
2017-09-15 18:22:19 +02:00
foreach (var b in hashedByteArray)
{
//append it to our StringBuilder
stringBuilder.Append(b.ToString("x2"));
}
//return the hashed value
return stringBuilder.ToString();
}
}
protected override void DisposeResources()
{
_writer.Close();
_writer.Dispose();
_ms.Close();
_ms.Dispose();
}
}
2017-09-23 10:08:18 +02:00
}