Skip to content

Commit

Permalink
perf: optimise subsequent GetHash and TryWriteHash calls
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverbooth committed Aug 23, 2023
1 parent 1157e36 commit 50d9cad
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
BigEndian/LittleEndian methods.
- X10D: `Stream.GetHash<>` and `Stream.TryWriteHash<>` now throw ArgumentException in lieu of
TypeInitializationException.
- X10D: `Stream.GetHash<>` and `Stream.TryWriteHash<>` are now more efficient on second and subsequent calls.

### Removed

Expand Down
20 changes: 18 additions & 2 deletions X10D/src/IO/StreamExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Reflection;
using System.Collections.Concurrent;
using System.Reflection;
using System.Security.Cryptography;

namespace X10D.IO;
Expand All @@ -8,6 +9,8 @@ namespace X10D.IO;
/// </summary>
public static partial class StreamExtensions
{
private static readonly ConcurrentDictionary<Type, HashAlgorithm> HashAlgorithmCache = new();

/// <summary>
/// Returns the hash of the current stream as an array of bytes using the specified hash algorithm.
/// </summary>
Expand Down Expand Up @@ -37,6 +40,11 @@ public static byte[] GetHash<T>(this Stream stream)
throw new IOException(ExceptionMessages.StreamDoesNotSupportReading);
}

if (HashAlgorithmCache.TryGetValue(typeof(T), out HashAlgorithm? cachedHashAlgorithm))
{
return cachedHashAlgorithm.ComputeHash(stream);
}

Type type = typeof(T);

MethodInfo? createMethod = type.GetMethods(BindingFlags.Public | BindingFlags.Static)
Expand All @@ -52,6 +60,7 @@ public static byte[] GetHash<T>(this Stream stream)
throw new ArgumentException(ExceptionMessages.HashAlgorithmCreateReturnedNull);
}

HashAlgorithmCache.TryAdd(type, crypt);
return crypt.ComputeHash(stream);
}

Expand Down Expand Up @@ -91,6 +100,13 @@ public static bool TryWriteHash<T>(this Stream stream, Span<byte> destination, o
throw new IOException(ExceptionMessages.StreamDoesNotSupportReading);
}

Span<byte> buffer = stackalloc byte[(int)stream.Length];
if (HashAlgorithmCache.TryGetValue(typeof(T), out HashAlgorithm? cachedHashAlgorithm))
{
_ = stream.Read(buffer); // we don't care about the number of bytes read. we can ignore MustUseReturnValue
return cachedHashAlgorithm.TryComputeHash(buffer, destination, out bytesWritten);
}

Type type = typeof(T);

MethodInfo? createMethod = type.GetMethods(BindingFlags.Public | BindingFlags.Static)
Expand All @@ -111,7 +127,7 @@ public static bool TryWriteHash<T>(this Stream stream, Span<byte> destination, o
throw new ArgumentException(ExceptionMessages.StreamTooLarge);
}

Span<byte> buffer = stackalloc byte[(int)stream.Length];
HashAlgorithmCache.TryAdd(type, crypt);
_ = stream.Read(buffer); // we don't care about the number of bytes read. we can ignore MustUseReturnValue
return crypt.TryComputeHash(buffer, destination, out bytesWritten);
}
Expand Down

0 comments on commit 50d9cad

Please sign in to comment.