Skip to content

Commit 8e9aee2

Browse files
committed
[Hi3 Repair] Implement MhyMurmurHash for asset hashing
1 parent 2640e39 commit 8e9aee2

File tree

9 files changed

+242
-51
lines changed

9 files changed

+242
-51
lines changed

CollapseLauncher/Classes/Helper/Hash.FileStream.cs

Lines changed: 84 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ public static byte[] GetCryptoHash<T>(
298298
{
299299
// Do read activity
300300
int read;
301-
while ((read = stream.Read(buffer, 0, bufferLen)) > 0)
301+
while ((read = stream.ReadAtLeast(buffer, bufferLen, false)) > 0)
302302
{
303303
// Throw Cancellation exception if detected
304304
token.ThrowIfCancellationRequested();
@@ -428,12 +428,39 @@ public static ConfiguredTaskAwaitable<byte[]> GetHashAsync<T>(
428428
bool isLongRunning,
429429
CancellationToken token)
430430
where T : NonCryptographicHashAlgorithm, new()
431+
{
432+
// Create hasher instance
433+
NonCryptographicHashAlgorithm hashProvider = CreateHash<T>();
434+
435+
// Calculate hash from the stream
436+
return GetHashAsync(stream, hashProvider, readProgress, isLongRunning, token);
437+
}
438+
439+
/// <summary>
440+
/// Asynchronously computes the non-cryptographic hash of a stream using a specifically provided <see cref="NonCryptographicHashAlgorithm"/> instance.
441+
/// </summary>
442+
/// <param name="stream">The stream to compute the hash for.</param>
443+
/// <param name="hashProvider">A specifically <see cref="NonCryptographicHashAlgorithm"/> instance to use.</param>
444+
/// <param name="readProgress">An action to report the read progress.</param>
445+
/// <param name="isLongRunning">
446+
/// Define where the async method should run for hashing big files.<br/>
447+
/// This to hint the default TaskScheduler to allow more hashing threads to be running at the same time.<br/>
448+
/// Set to <c>true</c> if the data stream is big, otherwise <c>false</c> for small data stream.
449+
/// </param>
450+
/// <param name="token">A cancellation token to observe while waiting for the task to complete.</param>
451+
/// <returns>A task that represents the asynchronous operation. The task result contains the computed hash as a byte array.</returns>
452+
public static ConfiguredTaskAwaitable<byte[]> GetHashAsync(
453+
Stream stream,
454+
NonCryptographicHashAlgorithm hashProvider,
455+
Action<int>? readProgress,
456+
bool isLongRunning,
457+
CancellationToken token)
431458
{
432459
// Create a new task from factory, assign a synchronous method to it with detached thread.
433460
Task<byte[]> task = Task<byte[]>
434461
.Factory
435462
.StartNew(Impl,
436-
(stream, readProgress, token),
463+
(stream, hashProvider, readProgress, token),
437464
token,
438465
isLongRunning ? TaskCreationOptions.LongRunning : TaskCreationOptions.DenyChildAttach,
439466
TaskScheduler.Default);
@@ -443,8 +470,9 @@ public static ConfiguredTaskAwaitable<byte[]> GetHashAsync<T>(
443470

444471
static byte[] Impl(object? state)
445472
{
446-
(Stream stream, Action<int>? readProgress, CancellationToken token) = ((Stream, Action<int>?, CancellationToken))state!;
447-
return GetHash<T>(stream, readProgress, token);
473+
(Stream stream, NonCryptographicHashAlgorithm hashProvider, Action<int>? readProgress, CancellationToken token) =
474+
((Stream, NonCryptographicHashAlgorithm, Action<int>?, CancellationToken))state!;
475+
return GetHash(stream, hashProvider, readProgress, token);
448476
}
449477
}
450478

@@ -482,12 +510,40 @@ public static ConfiguredTaskAwaitable<byte[]> GetHashAsync<T>(
482510
bool isLongRunning,
483511
CancellationToken token)
484512
where T : NonCryptographicHashAlgorithm, new()
513+
{
514+
// Create hasher instance
515+
NonCryptographicHashAlgorithm hashProvider = CreateHash<T>();
516+
517+
// Calculate hash from the stream
518+
return GetHashAsync(streamDelegate, hashProvider, readProgress, isLongRunning, token);
519+
}
520+
521+
522+
/// <summary>
523+
/// Asynchronously computes the non-cryptographic hash of a stream using a specifically provided <see cref="NonCryptographicHashAlgorithm"/> instance.
524+
/// </summary>
525+
/// <param name="streamDelegate">A delegate function which returns the stream to compute the hash for.</param>
526+
/// <param name="hashProvider">A specifically <see cref="NonCryptographicHashAlgorithm"/> instance to use.</param>
527+
/// <param name="readProgress">An action to report the read progress.</param>
528+
/// <param name="isLongRunning">
529+
/// Define where the async method should run for hashing big files.<br/>
530+
/// This to hint the default TaskScheduler to allow more hashing threads to be running at the same time.<br/>
531+
/// Set to <c>true</c> if the data stream is big, otherwise <c>false</c> for small data stream.
532+
/// </param>
533+
/// <param name="token">A cancellation token to observe while waiting for the task to complete.</param>
534+
/// <returns>A task that represents the asynchronous operation. The task result contains the computed hash as a byte array.</returns>
535+
public static ConfiguredTaskAwaitable<byte[]> GetHashAsync(
536+
Func<Stream> streamDelegate,
537+
NonCryptographicHashAlgorithm hashProvider,
538+
Action<int>? readProgress,
539+
bool isLongRunning,
540+
CancellationToken token)
485541
{
486542
// Create a new task from factory, assign a synchronous method to it with detached thread.
487543
Task<byte[]> task = Task<byte[]>
488544
.Factory
489545
.StartNew(Impl,
490-
(streamDelegate, readProgress, token),
546+
(streamDelegate, hashProvider, readProgress, token),
491547
token,
492548
isLongRunning ? TaskCreationOptions.LongRunning : TaskCreationOptions.DenyChildAttach,
493549
TaskScheduler.Default);
@@ -497,9 +553,10 @@ public static ConfiguredTaskAwaitable<byte[]> GetHashAsync<T>(
497553

498554
static byte[] Impl(object? state)
499555
{
500-
(Func<Stream> streamDelegate, Action<int>? readProgress, CancellationToken token) = ((Func<Stream>, Action<int>?, CancellationToken))state!;
556+
(Func<Stream> streamDelegate, NonCryptographicHashAlgorithm hashProvider, Action<int>? readProgress, CancellationToken token) =
557+
((Func<Stream>, NonCryptographicHashAlgorithm, Action<int>?, CancellationToken))state!;
501558
using Stream stream = streamDelegate();
502-
return GetHash<T>(stream, readProgress, token);
559+
return GetHash(stream, hashProvider, readProgress, token);
503560
}
504561
}
505562

@@ -556,9 +613,27 @@ public static byte[] GetHash<T>(
556613
// Create hasher instance
557614
NonCryptographicHashAlgorithm hashProvider = CreateHash<T>();
558615

616+
// Calculate hash from the stream
617+
return GetHash(stream, hashProvider, readProgress, token);
618+
}
619+
620+
/// <summary>
621+
/// Synchronously computes the non-cryptographic hash of a stream using a specifically provided <see cref="NonCryptographicHashAlgorithm"/> instance.
622+
/// </summary>
623+
/// <param name="stream">The stream to compute the hash for.</param>
624+
/// <param name="hashProvider">A specifically <see cref="NonCryptographicHashAlgorithm"/> instance to use.</param>
625+
/// <param name="readProgress">An action to report the read progress.</param>
626+
/// <param name="token">A cancellation token to observe while waiting for the operation to complete.</param>
627+
/// <returns>The computed hash as a byte array.</returns>
628+
public static byte[] GetHash(
629+
Stream stream,
630+
NonCryptographicHashAlgorithm hashProvider,
631+
Action<int>? readProgress = null,
632+
CancellationToken token = default)
633+
{
559634
// Get length based on stream length or at least if bigger, use the default one
560635
long streamLen = GetStreamLength(stream);
561-
int bufferLen = streamLen != -1 && BufferLength > streamLen ? (int)streamLen : BufferLength;
636+
int bufferLen = streamLen != -1 && BufferLength > streamLen ? (int)streamLen : BufferLength;
562637

563638
// Initialize buffer
564639
byte[] buffer = ArrayPool<byte>.Shared.Rent(bufferLen);
@@ -568,7 +643,7 @@ public static byte[] GetHash<T>(
568643
{
569644
// Do read activity
570645
int read;
571-
while ((read = stream.Read(buffer, 0, bufferLen)) > 0)
646+
while ((read = stream.ReadAtLeast(buffer, bufferLen, false)) > 0)
572647
{
573648
// Throw Cancellation exception if detected
574649
token.ThrowIfCancellationRequested();

CollapseLauncher/Classes/Interfaces/Class/ProgressBase.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1152,7 +1152,7 @@ protected virtual byte[] GetHash<T>(
11521152
read => UpdateHashReadProgress(read, updateProgress, updateTotalProgress),
11531153
token);
11541154

1155-
private void UpdateHashReadProgress(int read, bool updateProgress, bool updateTotalProgress)
1155+
protected void UpdateHashReadProgress(int read, bool updateProgress, bool updateTotalProgress)
11561156
{
11571157
// If progress update is not allowed, then return
11581158
if (!updateProgress)
@@ -1200,11 +1200,15 @@ protected virtual async ValueTask RunPatchTask(DownloadClient downloadClient, Do
12001200
await using FileStream patchFileStream = await patchOutputFile.NaivelyOpenFileStreamAsync(FileMode.Open, FileAccess.Read, FileShare.None);
12011201
// Verify the patch file and if it doesn't match, then re-download it
12021202
byte[] patchCrc = await GetCryptoHashAsync<MD5>(patchFileStream, null, true, false, token);
1203+
Array.Reverse(patchCrc);
12031204
if (!IsArrayMatch(patchCrc, patchHash.Span))
12041205
{
12051206
// Revert back the total size
12061207
Interlocked.Add(ref ProgressAllSizeCurrent, -patchSize);
12071208

1209+
// Dispose patch stream before redownloading
1210+
await patchFileStream.DisposeAsync();
1211+
12081212
// Re-download the patch file
12091213
await RunDownloadTask(patchSize, patchOutputFile, patchURL, downloadClient, downloadProgress, token);
12101214
continue;
@@ -1237,6 +1241,10 @@ protected virtual async ValueTask RunPatchTask(DownloadClient downloadClient, Do
12371241
outputFile.MoveTo(inputFile.FullName, true);
12381242
}
12391243
}
1244+
catch (Exception ex)
1245+
{
1246+
LogWriteLine($"Failed while patching file: {inputFile.FullName} -> {outputFile.FullName}\r\n{ex}", LogType.Error, true);
1247+
}
12401248
finally
12411249
{
12421250
// Delete the patch file and unsubscribe the patching progress

0 commit comments

Comments
 (0)