2021-09-27 12:42:46 +00:00
|
|
|
using System.Buffers;
|
|
|
|
using System.IO;
|
|
|
|
using System.Threading;
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
using Wabbajack.RateLimiter;
|
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
namespace Wabbajack.Hashing.xxHash64;
|
|
|
|
|
|
|
|
public static class StreamExtensions
|
2021-09-27 12:42:46 +00:00
|
|
|
{
|
2021-10-23 16:51:17 +00:00
|
|
|
public static async Task<Hash> Hash(this Stream stream, CancellationToken token)
|
2021-09-27 12:42:46 +00:00
|
|
|
{
|
2021-10-23 16:51:17 +00:00
|
|
|
return await stream.HashingCopy(Stream.Null, token);
|
|
|
|
}
|
2021-09-27 12:42:46 +00:00
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
public static async Task<Hash> HashingCopy(this Stream inputStream, Stream outputStream,
|
|
|
|
CancellationToken token, IJob? job = null)
|
|
|
|
{
|
|
|
|
using var rented = MemoryPool<byte>.Shared.Rent(1024 * 1024);
|
|
|
|
var buffer = rented.Memory;
|
2021-09-27 12:42:46 +00:00
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
var hasher = new xxHashAlgorithm(0);
|
2021-09-27 12:42:46 +00:00
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
var running = true;
|
|
|
|
ulong finalHash = 0;
|
|
|
|
while (running && !token.IsCancellationRequested)
|
|
|
|
{
|
|
|
|
var totalRead = 0;
|
2021-09-27 12:42:46 +00:00
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
while (totalRead != buffer.Length)
|
|
|
|
{
|
|
|
|
var read = await inputStream.ReadAsync(buffer.Slice(totalRead, buffer.Length - totalRead),
|
|
|
|
token);
|
2021-09-27 12:42:46 +00:00
|
|
|
|
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
if (read == 0)
|
2021-09-27 12:42:46 +00:00
|
|
|
{
|
2021-10-23 16:51:17 +00:00
|
|
|
running = false;
|
|
|
|
break;
|
2021-09-27 12:42:46 +00:00
|
|
|
}
|
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
if (job != null)
|
|
|
|
await job.Report(read, token);
|
|
|
|
|
|
|
|
totalRead += read;
|
|
|
|
}
|
|
|
|
|
|
|
|
var pendingWrite = outputStream.WriteAsync(buffer[..totalRead], token);
|
|
|
|
if (running)
|
|
|
|
{
|
|
|
|
hasher.TransformByteGroupsInternal(buffer.Span);
|
|
|
|
await pendingWrite;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var preSize = (totalRead >> 5) << 5;
|
|
|
|
if (preSize > 0)
|
|
|
|
{
|
|
|
|
hasher.TransformByteGroupsInternal(buffer[..preSize].Span);
|
|
|
|
finalHash = hasher.FinalizeHashValueInternal(buffer[preSize..totalRead].Span);
|
2021-09-27 12:42:46 +00:00
|
|
|
await pendingWrite;
|
|
|
|
break;
|
|
|
|
}
|
2021-10-23 16:51:17 +00:00
|
|
|
|
|
|
|
finalHash = hasher.FinalizeHashValueInternal(buffer[..totalRead].Span);
|
|
|
|
await pendingWrite;
|
|
|
|
break;
|
2021-09-27 12:42:46 +00:00
|
|
|
}
|
2021-10-23 16:51:17 +00:00
|
|
|
}
|
2021-09-27 12:42:46 +00:00
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
await outputStream.FlushAsync(token);
|
2021-09-27 12:42:46 +00:00
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
return new Hash(finalHash);
|
2021-09-27 12:42:46 +00:00
|
|
|
}
|
|
|
|
}
|