|
|
|
@ -7,16 +7,21 @@ namespace StabilityMatrix.Core.Helper;
|
|
|
|
|
|
|
|
|
|
public static class FileHash |
|
|
|
|
{ |
|
|
|
|
public static async Task<string> GetHashAsync(HashAlgorithm hashAlgorithm, Stream stream, byte[] buffer, Action<ulong>? progress = default) |
|
|
|
|
public static async Task<string> GetHashAsync( |
|
|
|
|
HashAlgorithm hashAlgorithm, |
|
|
|
|
Stream stream, |
|
|
|
|
byte[] buffer, |
|
|
|
|
Action<ulong>? progress = default |
|
|
|
|
) |
|
|
|
|
{ |
|
|
|
|
ulong totalBytesRead = 0; |
|
|
|
|
|
|
|
|
|
using (hashAlgorithm) |
|
|
|
|
{ |
|
|
|
|
int bytesRead; |
|
|
|
|
while ((bytesRead = await stream.ReadAsync(buffer)) != 0) |
|
|
|
|
while ((bytesRead = await stream.ReadAsync(buffer).ConfigureAwait(false)) != 0) |
|
|
|
|
{ |
|
|
|
|
totalBytesRead += (ulong) bytesRead; |
|
|
|
|
totalBytesRead += (ulong)bytesRead; |
|
|
|
|
hashAlgorithm.TransformBlock(buffer, 0, bytesRead, null, 0); |
|
|
|
|
progress?.Invoke(totalBytesRead); |
|
|
|
|
} |
|
|
|
@ -29,7 +34,7 @@ public static class FileHash
|
|
|
|
|
return BitConverter.ToString(hash).Replace("-", string.Empty).ToLowerInvariant(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static async Task<string> GetSha256Async(string filePath, IProgress<ProgressReport>? progress = default) |
|
|
|
|
{ |
|
|
|
|
if (!File.Exists(filePath)) |
|
|
|
@ -39,22 +44,27 @@ public static class FileHash
|
|
|
|
|
|
|
|
|
|
var totalBytes = Convert.ToUInt64(new FileInfo(filePath).Length); |
|
|
|
|
var shared = ArrayPool<byte>.Shared; |
|
|
|
|
var buffer = shared.Rent((int) FileTransfers.GetBufferSize(totalBytes)); |
|
|
|
|
var buffer = shared.Rent((int)FileTransfers.GetBufferSize(totalBytes)); |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
await using var stream = File.OpenRead(filePath); |
|
|
|
|
|
|
|
|
|
var hash = await GetHashAsync(SHA256.Create(), stream, buffer, totalBytesRead => |
|
|
|
|
{ |
|
|
|
|
progress?.Report(new ProgressReport(totalBytesRead, totalBytes, type: ProgressType.Hashing)); |
|
|
|
|
}).ConfigureAwait(false); |
|
|
|
|
var hash = await GetHashAsync( |
|
|
|
|
SHA256.Create(), |
|
|
|
|
stream, |
|
|
|
|
buffer, |
|
|
|
|
totalBytesRead => |
|
|
|
|
{ |
|
|
|
|
progress?.Report(new ProgressReport(totalBytesRead, totalBytes, type: ProgressType.Hashing)); |
|
|
|
|
} |
|
|
|
|
) |
|
|
|
|
.ConfigureAwait(false); |
|
|
|
|
return hash; |
|
|
|
|
} |
|
|
|
|
finally |
|
|
|
|
{ |
|
|
|
|
shared.Return(buffer); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static async Task<string> GetBlake3Async(string filePath, IProgress<ProgressReport>? progress = default) |
|
|
|
@ -63,23 +73,23 @@ public static class FileHash
|
|
|
|
|
{ |
|
|
|
|
throw new FileNotFoundException($"Could not find file: {filePath}"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var totalBytes = Convert.ToUInt64(new FileInfo(filePath).Length); |
|
|
|
|
var readBytes = 0ul; |
|
|
|
|
var shared = ArrayPool<byte>.Shared; |
|
|
|
|
var buffer = shared.Rent((int) FileTransfers.GetBufferSize(totalBytes)); |
|
|
|
|
var buffer = shared.Rent(GetBufferSize(totalBytes)); |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
await using var stream = File.OpenRead(filePath); |
|
|
|
|
using var hasher = Hasher.New(); |
|
|
|
|
while (true) |
|
|
|
|
{ |
|
|
|
|
var bytesRead = await stream.ReadAsync(buffer); |
|
|
|
|
var bytesRead = await stream.ReadAsync(buffer).ConfigureAwait(false); |
|
|
|
|
if (bytesRead == 0) |
|
|
|
|
{ |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
readBytes += (ulong) bytesRead; |
|
|
|
|
readBytes += (ulong)bytesRead; |
|
|
|
|
hasher.Update(buffer.AsSpan(0, bytesRead)); |
|
|
|
|
progress?.Report(new ProgressReport(readBytes, totalBytes)); |
|
|
|
|
} |
|
|
|
@ -90,4 +100,16 @@ public static class FileHash
|
|
|
|
|
shared.Return(buffer); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// Determines suitable buffer size for hashing based on stream length. |
|
|
|
|
/// </summary> |
|
|
|
|
private static int GetBufferSize(ulong totalBytes) => |
|
|
|
|
totalBytes switch |
|
|
|
|
{ |
|
|
|
|
< Size.MiB => 8 * (int)Size.KiB, |
|
|
|
|
< 500 * Size.MiB => 16 * (int)Size.KiB, |
|
|
|
|
< Size.GiB => 32 * (int)Size.KiB, |
|
|
|
|
_ => 64 * (int)Size.KiB |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|