3 Commits
1.1.2 ... 1.1.3

Author SHA1 Message Date
Matt Nadareski
8face3e4ed Bump version 2024-03-05 13:09:38 -05:00
Matt Nadareski
047463f726 Add byte array variants (fixes #1) 2024-03-05 13:05:42 -05:00
Matt Nadareski
7ab41ce096 Add leave open parameter for stream hashing (fixes #2) 2024-03-05 12:58:06 -05:00
2 changed files with 264 additions and 11 deletions

View File

@@ -46,18 +46,38 @@ namespace SabreTools.Hashing
public static Dictionary<HashType, string?>? GetFileHashes(string filename)
=> GetFileHashesAndSize(filename, out _);
/// <summary>
/// Get hashes from an input file path
/// </summary>
/// <param name="filename">Path to the input file</param>
/// <returns>Dictionary containing hashes on success, null on error</returns>
public static Dictionary<HashType, byte[]?>? GetFileHashArrays(string filename)
=> GetFileHashArraysAndSize(filename, out _);
/// <summary>
/// Get a hash from an input file path
/// </summary>
/// <param name="filename">Path to the input file</param>
/// <param name="hashType">Hash type to get from the file</param>
/// <returns>Dictionary containing hashes on success, null on error</returns>
/// <returns>Hash on success, null on error</returns>
public static string? GetFileHash(string filename, HashType hashType)
{
var hashes = GetFileHashes(filename, [hashType]);
return hashes?[hashType];
}
/// <summary>
/// Get a hash from an input file path
/// </summary>
/// <param name="filename">Path to the input file</param>
/// <param name="hashType">Hash type to get from the file</param>
/// <returns>Hash on success, null on error</returns>
public static byte[]? GetFileHashArray(string filename, HashType hashType)
{
var hashes = GetFileHashArrays(filename, [hashType]);
return hashes?[hashType];
}
/// <summary>
/// Get hashes from an input file path
/// </summary>
@@ -67,6 +87,15 @@ namespace SabreTools.Hashing
public static Dictionary<HashType, string?>? GetFileHashes(string filename, HashType[] hashTypes)
=> GetFileHashesAndSize(filename, hashTypes, out _);
/// <summary>
/// Get hashes from an input file path
/// </summary>
/// <param name="filename">Path to the input file</param>
/// <param name="hashTypes">Array of hash types to get from the file</param>
/// <returns>Dictionary containing hashes on success, null on error</returns>
public static Dictionary<HashType, byte[]?>? GetFileHashArrays(string filename, HashType[] hashTypes)
=> GetFileHashArraysAndSize(filename, hashTypes, out _);
#endregion
#region File Hashes With Size
@@ -85,18 +114,44 @@ namespace SabreTools.Hashing
return GetFileHashesAndSize(filename, hashTypes, out size);
}
/// <summary>
/// Get hashes and size from an input file path
/// </summary>
/// <param name="filename">Path to the input file</param>
/// <returns>Dictionary containing hashes on success, null on error</returns>
public static Dictionary<HashType, byte[]?>? GetFileHashArraysAndSize(string filename, out long size)
{
// Create a hash array for all entries
HashType[] hashTypes = (HashType[])Enum.GetValues(typeof(HashType));
// Return the hashes from the stream
return GetFileHashArraysAndSize(filename, hashTypes, out size);
}
/// <summary>
/// Get a hash and size from an input file path
/// </summary>
/// <param name="filename">Path to the input file</param>
/// <param name="hashType">Hash type to get from the file</param>
/// <returns>Dictionary containing hashes on success, null on error</returns>
/// <returns>Hash and size on success, null on error</returns>
public static string? GetFileHashAndSize(string filename, HashType hashType, out long size)
{
var hashes = GetFileHashesAndSize(filename, [hashType], out size);
return hashes?[hashType];
}
/// <summary>
/// Get a hash and size from an input file path
/// </summary>
/// <param name="filename">Path to the input file</param>
/// <param name="hashType">Hash type to get from the file</param>
/// <returns>Hash and size on success, null on error</returns>
public static byte[]? GetFileHashArrayAndSize(string filename, HashType hashType, out long size)
{
var hashes = GetFileHashArraysAndSize(filename, [hashType], out size);
return hashes?[hashType];
}
/// <summary>
/// Get hashes and size from an input file path
/// </summary>
@@ -122,6 +177,31 @@ namespace SabreTools.Hashing
return GetStreamHashes(input, hashTypes);
}
/// <summary>
/// Get hashes and size from an input file path
/// </summary>
/// <param name="filename">Path to the input file</param>
/// <param name="hashTypes">Array of hash types to get from the file</param>
/// <returns>Dictionary containing hashes on success, null on error</returns>
public static Dictionary<HashType, byte[]?>? GetFileHashArraysAndSize(string filename, HashType[] hashTypes, out long size)
{
// If the file doesn't exist, we can't do anything
if (!File.Exists(filename))
{
size = -1;
return null;
}
// Set the file size
size = new FileInfo(filename).Length;
// Open the input file
var input = File.OpenRead(filename);
// Return the hashes from the stream
return GetStreamHashArrays(input, hashTypes);
}
#endregion
#region Byte Array Hashes
@@ -140,18 +220,44 @@ namespace SabreTools.Hashing
return GetStreamHashes(new MemoryStream(input), hashTypes);
}
/// <summary>
/// Get hashes from an input byte array
/// </summary>
/// <param name="input">Byte array to hash</param>
/// <returns>Dictionary containing hashes on success, null on error</returns>
public static Dictionary<HashType, byte[]?>? GetByteArrayHashArrays(byte[] input)
{
// Create a hash array for all entries
HashType[] hashTypes = (HashType[])Enum.GetValues(typeof(HashType));
// Return the hashes from the stream
return GetStreamHashArrays(new MemoryStream(input), hashTypes);
}
/// <summary>
/// Get a hash from an input byte array
/// </summary>
/// <param name="input">Byte array to hash</param>
/// <param name="hashType">Hash type to get from the file</param>
/// <returns>Dictionary containing hashes on success, null on error</returns>
/// <returns>Hash on success, null on error</returns>
public static string? GetByteArrayHash(byte[] input, HashType hashType)
{
var hashes = GetStreamHashes(new MemoryStream(input), [hashType]);
return hashes?[hashType];
}
/// <summary>
/// Get a hash from an input byte array
/// </summary>
/// <param name="input">Byte array to hash</param>
/// <param name="hashType">Hash type to get from the file</param>
/// <returns>Hash on success, null on error</returns>
public static byte[]? GetByteArrayHashArray(byte[] input, HashType hashType)
{
var hashes = GetStreamHashArrays(new MemoryStream(input), [hashType]);
return hashes?[hashType];
}
/// <summary>
/// Get hashes from an input byte array
/// </summary>
@@ -161,6 +267,15 @@ namespace SabreTools.Hashing
public static Dictionary<HashType, string?>? GetByteArrayHashes(byte[] input, HashType[] hashTypes)
=> GetStreamHashes(new MemoryStream(input), hashTypes);
/// <summary>
/// Get hashes from an input byte array
/// </summary>
/// <param name="input">Byte array to hash</param>
/// <param name="hashTypes">Array of hash types to get from the file</param>
/// <returns>Dictionary containing hashes on success, null on error</returns>
public static Dictionary<HashType, byte[]?>? GetByteArrayHashArrays(byte[] input, HashType[] hashTypes)
=> GetStreamHashArrays(new MemoryStream(input), hashTypes);
#endregion
#region Stream Hashes
@@ -170,13 +285,27 @@ namespace SabreTools.Hashing
/// </summary>
/// <param name="input">Stream to hash</param>
/// <returns>Dictionary containing hashes on success, null on error</returns>
public static Dictionary<HashType, string?>? GetStreamHashes(Stream input)
public static Dictionary<HashType, string?>? GetStreamHashes(Stream input, bool leaveOpen = false)
{
// Create a hash array for all entries
HashType[] hashTypes = (HashType[])Enum.GetValues(typeof(HashType));
// Get the output hashes
return GetStreamHashes(input, hashTypes);
return GetStreamHashes(input, hashTypes, leaveOpen);
}
/// <summary>
/// Get hashes from an input Stream
/// </summary>
/// <param name="input">Stream to hash</param>
/// <returns>Dictionary containing hashes on success, null on error</returns>
public static Dictionary<HashType, byte[]?>? GetStreamHashArrays(Stream input, bool leaveOpen = false)
{
// Create a hash array for all entries
HashType[] hashTypes = (HashType[])Enum.GetValues(typeof(HashType));
// Get the output hashes
return GetStreamHashArrays(input, hashTypes, leaveOpen);
}
/// <summary>
@@ -184,10 +313,22 @@ namespace SabreTools.Hashing
/// </summary>
/// <param name="input">Stream to hash</param>
/// <param name="hashType">Hash type to get from the file</param>
/// <returns>Dictionary containing hashes on success, null on error</returns>
public static string? GetStreamHash(Stream input, HashType hashType)
/// <returns>Hash on success, null on error</returns>
public static string? GetStreamHash(Stream input, HashType hashType, bool leaveOpen = false)
{
var hashes = GetStreamHashes(input, [hashType]);
var hashes = GetStreamHashes(input, [hashType], leaveOpen);
return hashes?[hashType];
}
/// <summary>
/// Get a hash and size from an input Stream
/// </summary>
/// <param name="input">Stream to hash</param>
/// <param name="hashType">Hash type to get from the file</param>
/// <returns>Hash on success, null on error</returns>
public static byte[]? GetStreamHashArray(Stream input, HashType hashType, bool leaveOpen = false)
{
var hashes = GetStreamHashArrays(input, [hashType], leaveOpen);
return hashes?[hashType];
}
@@ -197,7 +338,7 @@ namespace SabreTools.Hashing
/// <param name="input">Stream to hash</param>
/// <param name="hashTypes">Array of hash types to get from the file</param>
/// <returns>Dictionary containing hashes on success, null on error</returns>
public static Dictionary<HashType, string?>? GetStreamHashes(Stream input, HashType[] hashTypes)
public static Dictionary<HashType, string?>? GetStreamHashes(Stream input, HashType[] hashTypes, bool leaveOpen = false)
{
// Create the output dictionary
var hashDict = new Dictionary<HashType, string?>();
@@ -297,7 +438,119 @@ namespace SabreTools.Hashing
}
finally
{
input.Dispose();
if (!leaveOpen)
input.Dispose();
}
}
/// <summary>
/// Get hashes from an input Stream
/// </summary>
/// <param name="input">Stream to hash</param>
/// <param name="hashTypes">Array of hash types to get from the file</param>
/// <returns>Dictionary containing hashes on success, null on error</returns>
private static Dictionary<HashType, byte[]?>? GetStreamHashArrays(Stream input, HashType[] hashTypes, bool leaveOpen = false)
{
// Create the output dictionary
var hashDict = new Dictionary<HashType, byte[]?>();
try
{
// Get a list of hashers to run over the buffer
var hashers = new Dictionary<HashType, HashWrapper>();
// Add hashers based on requested types
foreach (HashType hashType in hashTypes)
{
hashers[hashType] = new HashWrapper(hashType);
}
// Initialize the hashing helpers
var loadBuffer = new ThreadLoadBuffer(input);
int buffersize = 3 * 1024 * 1024;
byte[] buffer0 = new byte[buffersize];
byte[] buffer1 = new byte[buffersize];
/*
Please note that some of the following code is adapted from
RomVault. This is a modified version of how RomVault does
threaded hashing. As such, some of the terminology and code
is the same, though variable names and comments may have
been tweaked to better fit this code base.
*/
// Pre load the first buffer
long refsize = input.Length;
int next = refsize > buffersize ? buffersize : (int)refsize;
input.Read(buffer0, 0, next);
int current = next;
refsize -= next;
bool bufferSelect = true;
while (current > 0)
{
// Trigger the buffer load on the second buffer
next = refsize > buffersize ? buffersize : (int)refsize;
if (next > 0)
loadBuffer.Trigger(bufferSelect ? buffer1 : buffer0, next);
byte[] buffer = bufferSelect ? buffer0 : buffer1;
#if NET20 || NET35
// Run hashers sequentially on each chunk
foreach (var h in hashers)
{
h.Value.Process(buffer, 0, current);
}
#else
// Run hashers in parallel on each chunk
Parallel.ForEach(hashers, h => h.Value.Process(buffer, 0, current));
#endif
// Wait for the load buffer worker, if needed
if (next > 0)
loadBuffer.Wait();
// Setup for the next hashing step
current = next;
refsize -= next;
bufferSelect = !bufferSelect;
}
// Finalize all hashing helpers
loadBuffer.Finish();
#if NET20 || NET35
foreach (var h in hashers)
{
h.Value.Terminate();
}
#else
Parallel.ForEach(hashers, h => h.Value.Terminate());
#endif
// Get the results
foreach (var hasher in hashers)
{
hashDict[hasher.Key] = hasher.Value.CurrentHashBytes;
}
// Dispose of the hashers
loadBuffer.Dispose();
foreach (var hasher in hashers.Values)
{
hasher.Dispose();
}
return hashDict;
}
catch (IOException)
{
return null;
}
finally
{
if (!leaveOpen)
input.Dispose();
}
}

View File

@@ -10,7 +10,7 @@
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>1.1.2</Version>
<Version>1.1.3</Version>
<!-- Package Properties -->
<Authors>Matt Nadareski</Authors>