@@ -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 ( ) ;
0 commit comments