diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f31c88..65394b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/X10D/src/IO/StreamExtensions.cs b/X10D/src/IO/StreamExtensions.cs index 15e50e0..4bf3df8 100644 --- a/X10D/src/IO/StreamExtensions.cs +++ b/X10D/src/IO/StreamExtensions.cs @@ -1,4 +1,5 @@ -using System.Reflection; +using System.Collections.Concurrent; +using System.Reflection; using System.Security.Cryptography; namespace X10D.IO; @@ -8,6 +9,8 @@ namespace X10D.IO; /// public static partial class StreamExtensions { + private static readonly ConcurrentDictionary HashAlgorithmCache = new(); + /// /// Returns the hash of the current stream as an array of bytes using the specified hash algorithm. /// @@ -37,6 +40,11 @@ public static partial class StreamExtensions 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) @@ -52,6 +60,7 @@ public static partial class StreamExtensions throw new ArgumentException(ExceptionMessages.HashAlgorithmCreateReturnedNull); } + HashAlgorithmCache.TryAdd(type, crypt); return crypt.ComputeHash(stream); } @@ -91,6 +100,13 @@ public static partial class StreamExtensions throw new IOException(ExceptionMessages.StreamDoesNotSupportReading); } + Span 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) @@ -111,7 +127,7 @@ public static partial class StreamExtensions throw new ArgumentException(ExceptionMessages.StreamTooLarge); } - Span 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); }