diff --git a/src/Speckle.Sdk/Common/Sha256.cs b/src/Speckle.Sdk/Common/Sha256.cs
index 953880af..ca08a1f3 100644
--- a/src/Speckle.Sdk/Common/Sha256.cs
+++ b/src/Speckle.Sdk/Common/Sha256.cs
@@ -1,5 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
+using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
#if NET6_0_OR_GREATER
@@ -8,47 +9,58 @@ using System.Runtime.InteropServices;
namespace Speckle.Sdk.Common;
+///
+/// Helpers for hashing data to a hex string
+///
public static class Sha256
{
+ public const string DEFAULT_FORMAT = "x2";
+ public const int HASH_SIZE_CHARS = 64; // SHA256.HashSizeInBytes * sizeof(char)
#if NET6_0_OR_GREATER
/// the value to hash
- /// "x2" for lower case, "X2" for uppercase.
- /// Desired length of the returned string. Must be 2 ≤ Length ≤ 64, and must be a multiple of 2
- ///
- [Pure]
- public static string GetString(
- ReadOnlySpan input,
- [StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format = "x2",
- int length = SHA256.HashSizeInBytes * sizeof(char)
- )
+ /// Output hash; it must have 2 ≤ Length ≤ 64, and must be a multiple of 2
+ /// for upper case, false otherwise
+ public static void Hash(ReadOnlySpan input, bool formatUpperCase, Span destination)
{
ReadOnlySpan inputBytes = MemoryMarshal.AsBytes(input);
+ Hash(inputBytes, formatUpperCase, destination);
+ }
+ public static void Hash(ReadOnlySpan input, bool formatUpperCase, Span destination)
+ {
Span hash = stackalloc byte[SHA256.HashSizeInBytes];
- SHA256.HashData(inputBytes, hash);
+ SHA256.HashData(input, hash);
- Span output = stackalloc char[length];
+ FormatHash(hash, formatUpperCase, destination);
+ }
- for (int i = 0, j = 0; j < length; i += sizeof(byte), j += sizeof(char))
+ public static void Hash(Stream source, bool formatUpperCase, Span destination)
+ {
+ Span hash = stackalloc byte[SHA256.HashSizeInBytes];
+ SHA256.HashData(source, hash);
+
+ FormatHash(hash, formatUpperCase, destination);
+ }
+
+ private static void FormatHash(ReadOnlySpan input, bool formatUpperCase, Span output)
+ {
+ for (int i = 0, j = 0; j < output.Length; i += sizeof(byte), j += sizeof(char))
{
- hash[i].TryFormat(output[j..], out _, format);
+ input[i].TryFormat(output[j..], out _, formatUpperCase ? "X2" : "x2");
}
-
- return new string(output);
}
#endif
/// the value to hash
/// "x2" for lower case, "X2" for uppercase.
- /// Desired length of the returned string
+ /// Desired length of the returned string
/// the hash string
- /// is not a recognised numeric format
///
[Pure]
- public static string GetString(
+ public static string Hash(
string input,
- [StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format = "x2",
- int length = 64
+ [StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format = DEFAULT_FORMAT,
+ int outputLengthChars = HASH_SIZE_CHARS
)
{
var inputBytes = Encoding.Unicode.GetBytes(input);
@@ -59,12 +71,43 @@ public static class Sha256
byte[] hash = sha256.ComputeHash(inputBytes);
#endif
- StringBuilder sb = new(64);
+ StringBuilder sb = new(HASH_SIZE_CHARS);
foreach (byte b in hash)
{
sb.Append(b.ToString(format));
}
- return sb.ToString(0, length);
+ return sb.ToString(0, outputLengthChars);
+ }
+
+ ///
+ [Pure]
+ public static string Hash(
+ Stream input,
+ [StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format = DEFAULT_FORMAT,
+ int outputLengthChars = HASH_SIZE_CHARS
+ )
+ {
+#if NET6_0_OR_GREATER
+ byte[] hash = SHA256.HashData(input);
+#else
+ using var sha256 = SHA256.Create();
+ byte[] hash = sha256.ComputeHash(input);
+#endif
+
+ return FormatHash(hash, format, outputLengthChars);
+ }
+
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static string FormatHash(byte[] hash, string? format, int outputLengthChars)
+ {
+ StringBuilder sb = new(HASH_SIZE_CHARS);
+ foreach (byte b in hash)
+ {
+ sb.Append(b.ToString(format));
+ }
+
+ return sb.ToString(0, outputLengthChars);
}
}
diff --git a/src/Speckle.Sdk/Models/Blob.cs b/src/Speckle.Sdk/Models/Blob.cs
index 638402bf..cf782eb8 100644
--- a/src/Speckle.Sdk/Models/Blob.cs
+++ b/src/Speckle.Sdk/Models/Blob.cs
@@ -14,8 +14,6 @@ public sealed class Blob : Base
private string? _hash;
private bool _isHashExpired = true;
- public Blob() { }
-
[SetsRequiredMembers]
public Blob(string filePath)
{
@@ -32,7 +30,6 @@ public sealed class Blob : Base
_isHashExpired = true;
}
}
-
public required string originalPath { get; set; }
[JsonIgnore]
@@ -51,7 +48,7 @@ public sealed class Blob : Base
{
if ((_isHashExpired || _hash == null))
{
- _hash = HashUtility.HashFile(filePath);
+ _hash = HashUtility.CalculateBlobHash(filePath);
}
return _hash;
diff --git a/src/Speckle.Sdk/Models/HashUtility.cs b/src/Speckle.Sdk/Models/HashUtility.cs
index b4f5497d..7f8832b7 100644
--- a/src/Speckle.Sdk/Models/HashUtility.cs
+++ b/src/Speckle.Sdk/Models/HashUtility.cs
@@ -1,26 +1,39 @@
-using System.Diagnostics.CodeAnalysis;
-using System.Security.Cryptography;
+using System.Diagnostics.Contracts;
+using Speckle.Sdk.Common;
+using Speckle.Sdk.Serialisation;
namespace Speckle.Sdk.Models;
+///
+/// Helper functions for calculating hash based Ids for Speckle core concepts
+///
public static class HashUtility
{
- public enum HashingFunctions
+ public const int HASH_LENGTH_CHARS = 32;
+
+ [Pure]
+ public static Id ComputeObjectId(Json serialized)
{
- SHA256,
- MD5,
+#if NET6_0_OR_GREATER
+ Span hash = stackalloc char[HASH_LENGTH_CHARS];
+ Sha256.Hash(serialized.Value.AsSpan(), false, hash);
+ return new Id(new string(hash));
+#else
+ string hash = Sha256.Hash(serialized.Value, outputLengthChars: HashUtility.HASH_LENGTH_CHARS);
+ return new Id(hash);
+#endif
}
- public const int HASH_LENGTH = 32;
-
- [SuppressMessage("Security", "CA5351:Do Not Use Broken Cryptographic Algorithms")]
- public static string HashFile(string filePath, HashingFunctions func = HashingFunctions.SHA256)
+ [Pure]
+ public static string CalculateBlobHash(string filePath)
{
- using HashAlgorithm hashAlgorithm = func == HashingFunctions.MD5 ? MD5.Create() : SHA256.Create();
-
using var stream = File.OpenRead(filePath);
-
- var hash = hashAlgorithm.ComputeHash(stream);
- return BitConverter.ToString(hash, 0, HASH_LENGTH).Replace("-", "").ToLowerInvariant();
+#if NET6_0_OR_GREATER
+ Span hash = stackalloc char[HASH_LENGTH_CHARS];
+ Sha256.Hash(stream, false, hash);
+ return new(hash);
+#else
+ return Sha256.Hash(stream, "x2", HASH_LENGTH_CHARS);
+#endif
}
}
diff --git a/src/Speckle.Sdk/Serialisation/IdGenerator.cs b/src/Speckle.Sdk/Serialisation/IdGenerator.cs
deleted file mode 100644
index 0d33482e..00000000
--- a/src/Speckle.Sdk/Serialisation/IdGenerator.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System.Diagnostics.Contracts;
-using Speckle.Sdk.Common;
-using Speckle.Sdk.Models;
-
-namespace Speckle.Sdk.Serialisation;
-
-public static class IdGenerator
-{
- [Pure]
- public static Id ComputeId(Json serialized)
- {
-#if NET6_0_OR_GREATER
- string hash = Sha256.GetString(serialized.Value.AsSpan(), length: HashUtility.HASH_LENGTH);
-#else
- string hash = Sha256.GetString(serialized.Value, length: HashUtility.HASH_LENGTH);
-#endif
- return new Id(hash);
- }
-}
diff --git a/src/Speckle.Sdk/Serialisation/SpeckleObjectSerializer.cs b/src/Speckle.Sdk/Serialisation/SpeckleObjectSerializer.cs
index 618f7e6f..d2cc0048 100644
--- a/src/Speckle.Sdk/Serialisation/SpeckleObjectSerializer.cs
+++ b/src/Speckle.Sdk/Serialisation/SpeckleObjectSerializer.cs
@@ -358,7 +358,7 @@ public class SpeckleObjectSerializer
if (writer is SerializerIdWriter serializerIdWriter)
{
(var json, writer) = serializerIdWriter.FinishIdWriter();
- id = IdGenerator.ComputeId(json);
+ id = HashUtility.ComputeObjectId(json);
}
else
{
diff --git a/src/Speckle.Sdk/Serialisation/V2/Send/ObjectSaver.cs b/src/Speckle.Sdk/Serialisation/V2/Send/ObjectSaver.cs
index 57bd09b9..14df1715 100644
--- a/src/Speckle.Sdk/Serialisation/V2/Send/ObjectSaver.cs
+++ b/src/Speckle.Sdk/Serialisation/V2/Send/ObjectSaver.cs
@@ -1,4 +1,5 @@
using Microsoft.Extensions.Logging;
+using Speckle.Sdk.Common;
using Speckle.Sdk.Dependencies;
using Speckle.Sdk.Dependencies.Serialization;
using Speckle.Sdk.SQLite;
@@ -25,7 +26,7 @@ public sealed class ObjectSaver(
IProgress? progress,
ISqLiteJsonCacheManager sqLiteJsonCacheManager,
IServerObjectManager serverObjectManager,
- IServerBlobManager serverBlobManager,
+ IServerBlobManager? serverBlobManager,
ILogger logger,
SerializeProcessOptions options,
CancellationToken cancellationToken
@@ -45,7 +46,10 @@ public sealed class ObjectSaver(
protected override async Task SendBlobToServerInternal(Batch batch)
{
- var objectBatch = batch.Items.Distinct().Select(x => x.Blob).ToList();
+ // Callers should either setup a blob manager, or not try and send blobs
+ serverBlobManager.NotNull("No blob manager was setup to handle sending blobs");
+
+ var objectBatch = batch.Items.Distinct().Select(x => (x.Blob.id.NotNull(), x.Blob.filePath)).ToList();
// var hasObjects = await serverBlobManager
// .HasObjects(objectBatch.Select(x => x.Id.Value).Freeze(), _cancellationTokenSource.Token)
// .ConfigureAwait(false);
@@ -54,9 +58,7 @@ public sealed class ObjectSaver(
{
// Interlocked.Add(ref _uploading, batch.Items.Count);
// progress?.Report(new(ProgressEvent.UploadingObjects, _uploading, null));
- await serverBlobManager
- .UploadBlobs(objectBatch, true, progress, _cancellationTokenSource.Token)
- .ConfigureAwait(false);
+ await serverBlobManager.UploadBlobs(objectBatch, progress, _cancellationTokenSource.Token).ConfigureAwait(false);
}
}
diff --git a/src/Speckle.Sdk/Serialisation/V2/Send/ObjectSerializer.cs b/src/Speckle.Sdk/Serialisation/V2/Send/ObjectSerializer.cs
index 842003cb..3ba47416 100644
--- a/src/Speckle.Sdk/Serialisation/V2/Send/ObjectSerializer.cs
+++ b/src/Speckle.Sdk/Serialisation/V2/Send/ObjectSerializer.cs
@@ -343,7 +343,7 @@ public sealed class ObjectSerializer : IObjectSerializer
if (writer is SerializerIdWriter serializerIdWriter)
{
(var json, writer) = serializerIdWriter.FinishIdWriter();
- id = IdGenerator.ComputeId(json);
+ id = HashUtility.ComputeObjectId(json);
}
else
{
diff --git a/src/Speckle.Sdk/Serialisation/V2/SerializeProcessFactory.cs b/src/Speckle.Sdk/Serialisation/V2/SerializeProcessFactory.cs
index 874ee0c5..6a7e13fe 100644
--- a/src/Speckle.Sdk/Serialisation/V2/SerializeProcessFactory.cs
+++ b/src/Speckle.Sdk/Serialisation/V2/SerializeProcessFactory.cs
@@ -29,13 +29,20 @@ public class SerializeProcessFactory(
var sqLiteJsonCacheManager = sqLiteJsonCacheManagerFactory.CreateFromStream(projectId);
var serverObjectManager = serverObjectManagerFactory.Create(url, projectId, authorizationToken);
var serverBlobManager = serverBlobManagerFactory.Create(url, projectId, authorizationToken);
- return CreateSerializeProcess(sqLiteJsonCacheManager, serverObjectManager, serverBlobManager, progress, cancellationToken, options);
+ return CreateSerializeProcess(
+ sqLiteJsonCacheManager,
+ serverObjectManager,
+ serverBlobManager,
+ progress,
+ cancellationToken,
+ options
+ );
}
public ISerializeProcess CreateSerializeProcess(
ISqLiteJsonCacheManager sqLiteJsonCacheManager,
IServerObjectManager serverObjectManager,
- IServerBlobManager serverBlobManager,
+ IServerBlobManager? serverBlobManager,
IProgress? progress,
CancellationToken cancellationToken,
SerializeProcessOptions? options = null
diff --git a/src/Speckle.Sdk/Serialisation/V2/ServerBlobManagerFactory.cs b/src/Speckle.Sdk/Serialisation/V2/ServerBlobManagerFactory.cs
index 0259af62..541e7833 100644
--- a/src/Speckle.Sdk/Serialisation/V2/ServerBlobManagerFactory.cs
+++ b/src/Speckle.Sdk/Serialisation/V2/ServerBlobManagerFactory.cs
@@ -1,12 +1,10 @@
using Speckle.InterfaceGenerator;
using Speckle.Sdk.Helpers;
-using Speckle.Sdk.Logging;
namespace Speckle.Sdk.Serialisation.V2;
[GenerateAutoInterface]
-public class ServerBlobManagerFactory(ISpeckleHttp speckleHttp, ISdkActivityFactory activityFactory)
- : IServerBlobManagerFactory
+public sealed class ServerBlobManagerFactory(ISpeckleHttp speckleHttp) : IServerBlobManagerFactory
{
public IServerBlobManager Create(
Uri serverUrl,
@@ -17,6 +15,6 @@ public class ServerBlobManagerFactory(ISpeckleHttp speckleHttp, ISdkActivityFact
{
var client = speckleHttp.CreateHttpClient(authorizationToken: authorizationToken);
client.BaseAddress = serverUrl;
- return new ServerBlobManager(client);
+ return new ServerBlobManager(client, projectId);
}
}
diff --git a/src/Speckle.Sdk/Serialisation/V2/ServerObjectBlobManager.cs b/src/Speckle.Sdk/Serialisation/V2/ServerObjectBlobManager.cs
index 6b9fb7c7..af10c94a 100644
--- a/src/Speckle.Sdk/Serialisation/V2/ServerObjectBlobManager.cs
+++ b/src/Speckle.Sdk/Serialisation/V2/ServerObjectBlobManager.cs
@@ -1,22 +1,13 @@
using Speckle.InterfaceGenerator;
-using Speckle.Sdk.Helpers;
using Speckle.Sdk.Transports;
using Speckle.Sdk.Transports.ServerUtils;
namespace Speckle.Sdk.Serialisation.V2;
[GenerateAutoInterface(VisibilityModifier = "public")]
-internal sealed class ServerBlobManager : IServerBlobManager
+internal sealed class ServerBlobManager(HttpClient authorizedClient, string projectId) : IServerBlobManager
{
- private readonly HttpClient _authorizedClient;
-
- public ServerBlobManager(HttpClient authorizedClient)
- {
- _authorizedClient = authorizedClient;
- }
-
public async Task UploadBlobs(
- string projectId,
IReadOnlyCollection<(string blobId, string filePath)> objects,
IProgress? progress,
CancellationToken cancellationToken
@@ -33,9 +24,8 @@ internal sealed class ServerBlobManager : IServerBlobManager
var fileName = Path.GetFileName(filePath);
var stream = File.OpenRead(filePath);
StreamContent fsc = new(stream);
- var hash = id.Split(':')[1];
- multipartFormDataContent.Add(fsc, $"hash:{hash}", fileName);
+ multipartFormDataContent.Add(fsc, $"hash:{id}", fileName);
cancellationToken.ThrowIfCancellationRequested();
}
@@ -44,7 +34,7 @@ internal sealed class ServerBlobManager : IServerBlobManager
message.Method = HttpMethod.Post;
message.Content = new ProgressContent(multipartFormDataContent, progress);
- using var response = await _authorizedClient.SendAsync(message, cancellationToken).ConfigureAwait(false);
+ using var response = await authorizedClient.SendAsync(message, cancellationToken).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
}
diff --git a/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.cs b/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.cs
index 10d79161..6b30eb72 100644
--- a/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.cs
+++ b/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.cs
@@ -59,6 +59,7 @@ public class CancellationTests
new DummySqLiteSendManager(),
new CancellationServerObjectManager(cancellationSource),
null,
+ null,
cancellationSource.Token,
new SerializeProcessOptions(true, true, false, true)
);
@@ -79,6 +80,7 @@ public class CancellationTests
new DummySqLiteSendManager(),
new CancellationServerObjectManager(cancellationSource),
null,
+ null,
cancellationSource.Token,
new SerializeProcessOptions(true, true, false, true)
);
diff --git a/tests/Speckle.Sdk.Serialization.Tests/DataObjectTests.cs b/tests/Speckle.Sdk.Serialization.Tests/DataObjectTests.cs
index 998fa055..6c90db1e 100644
--- a/tests/Speckle.Sdk.Serialization.Tests/DataObjectTests.cs
+++ b/tests/Speckle.Sdk.Serialization.Tests/DataObjectTests.cs
@@ -40,6 +40,7 @@ public class DataObjectTests
new MemoryJsonCacheManager(json),
new DummyServerObjectManager(),
null,
+ null,
default,
new SerializeProcessOptions(false, false, true, true)
);
diff --git a/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.cs b/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.cs
index bc71c3d2..22be9dda 100644
--- a/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.cs
+++ b/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.cs
@@ -37,6 +37,7 @@ public class ExceptionTests
new MemoryJsonCacheManager(objects),
new ExceptionServerObjectManager(),
null,
+ null,
default,
new SerializeProcessOptions(false, false, false, true)
);
@@ -55,6 +56,7 @@ public class ExceptionTests
new ExceptionSendCacheManager(),
new MemoryServerObjectManager(new()),
null,
+ null,
default,
new SerializeProcessOptions(false, false, false, true)
);
@@ -92,6 +94,7 @@ public class ExceptionTests
new ExceptionSendCacheManager(exceptionsAfter: 10),
new MemoryServerObjectManager(new()),
null,
+ null,
default,
new SerializeProcessOptions(false, false, false, true)
{
diff --git a/tests/Speckle.Sdk.Serialization.Tests/SerializationTests.cs b/tests/Speckle.Sdk.Serialization.Tests/SerializationTests.cs
index e7f5f0e7..a3711911 100644
--- a/tests/Speckle.Sdk.Serialization.Tests/SerializationTests.cs
+++ b/tests/Speckle.Sdk.Serialization.Tests/SerializationTests.cs
@@ -146,7 +146,7 @@ public class SerializationTests
jObject.Remove("id");
jObject.Remove("__closure");
var jsonWithoutId = jObject.ToString(Formatting.None);
- var newId = IdGenerator.ComputeId(new Json(jsonWithoutId));
+ var newId = HashUtility.ComputeObjectId(new Json(jsonWithoutId));
id.Should().Be(newId.Value);
}
@@ -227,6 +227,7 @@ public class SerializationTests
SqLiteJsonCacheManager.FromMemory(1),
new MemoryServerObjectManager(newIdToJson),
null,
+ null,
default,
new SerializeProcessOptions(false, false, false, true) { MaxCacheBatchSize = 1, MaxParallelism = concurrency }
)
diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/Blob/BlobApiExceptionalTests.cs b/tests/Speckle.Sdk.Tests.Integration/Api/Blob/BlobApiExceptionalTests.cs
index 05f34a75..33a491b7 100644
--- a/tests/Speckle.Sdk.Tests.Integration/Api/Blob/BlobApiExceptionalTests.cs
+++ b/tests/Speckle.Sdk.Tests.Integration/Api/Blob/BlobApiExceptionalTests.cs
@@ -60,7 +60,7 @@ public class BlobApiExceptionalTests : IAsyncLifetime
{
await writer.WriteLineAsync(PAYLOAD);
}
- string id = HashUtility.HashFile(filePath);
+ string id = HashUtility.CalculateBlobHash(filePath);
var ex = await Assert.ThrowsAsync(async () =>
await _sut.UploadBlobs("non-existent-project", [(id, filePath)], null, CancellationToken.None)
);
diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/Blob/BlobApiTests.cs b/tests/Speckle.Sdk.Tests.Integration/Api/Blob/BlobApiTests.cs
index a89e4c8b..4485afc8 100644
--- a/tests/Speckle.Sdk.Tests.Integration/Api/Blob/BlobApiTests.cs
+++ b/tests/Speckle.Sdk.Tests.Integration/Api/Blob/BlobApiTests.cs
@@ -34,7 +34,7 @@ public class BlobApiTests : IAsyncLifetime
{
await writer.WriteLineAsync(PAYLOAD);
}
- string id = HashUtility.HashFile(filePath);
+ string id = HashUtility.CalculateBlobHash(filePath);
//act
var preDiff = await _blobApi.HasBlobs(_project.id, [id], CancellationToken.None);
diff --git a/tests/Speckle.Sdk.Tests.Performance/Benchmarks/CryptSha256Hash.cs b/tests/Speckle.Sdk.Tests.Performance/Benchmarks/CryptSha256Hash.cs
index 645ce4c9..9c33006e 100644
--- a/tests/Speckle.Sdk.Tests.Performance/Benchmarks/CryptSha256Hash.cs
+++ b/tests/Speckle.Sdk.Tests.Performance/Benchmarks/CryptSha256Hash.cs
@@ -19,12 +19,14 @@ public class CryptSha256Hash
[Benchmark]
public string Sha256()
{
- return Speckle.Sdk.Common.Sha256.GetString(testData);
+ return Speckle.Sdk.Common.Sha256.Hash(testData);
}
[Benchmark]
public string Sha256_Span()
{
- return Speckle.Sdk.Common.Sha256.GetString(testData.AsSpan());
+ Span resultLowerSpan = stackalloc char[Speckle.Sdk.Common.Sha256.HASH_SIZE_CHARS];
+ Speckle.Sdk.Common.Sha256.Hash(testData.AsSpan(), false, resultLowerSpan);
+ return new string(resultLowerSpan);
}
}
diff --git a/tests/Speckle.Sdk.Tests.Unit/Models/UtilitiesTests.cs b/tests/Speckle.Sdk.Tests.Unit/Models/UtilitiesTests.cs
index 6bb6d01f..41c059d6 100644
--- a/tests/Speckle.Sdk.Tests.Unit/Models/UtilitiesTests.cs
+++ b/tests/Speckle.Sdk.Tests.Unit/Models/UtilitiesTests.cs
@@ -69,8 +69,8 @@ public sealed class HashUtilityTests
[MemberData(nameof(SmallTestCasesSha256))]
public void Sha256(string input, string expected, string _, int length)
{
- var resultLower = Speckle.Sdk.Common.Sha256.GetString(input, "x2", length);
- var resultUpper = Speckle.Sdk.Common.Sha256.GetString(input, "X2", length);
+ var resultLower = Speckle.Sdk.Common.Sha256.Hash(input, "x2", length);
+ var resultUpper = Speckle.Sdk.Common.Sha256.Hash(input, "X2", length);
resultLower.Should().Be(new string(expected.ToLower()[..length]));
@@ -86,19 +86,22 @@ public sealed class HashUtilityTests
int length //Span version of the function must have multiple of 2
)
{
- var resultLowerSpan = Speckle.Sdk.Common.Sha256.GetString(input.AsSpan(), "x2", length);
- var resultUpperSpan = Speckle.Sdk.Common.Sha256.GetString(input.AsSpan(), "X2", length);
+ Span resultLowerSpan = stackalloc char[length];
+ Speckle.Sdk.Common.Sha256.Hash(input.AsSpan(), false, resultLowerSpan);
+ Span resultUpperSpan = stackalloc char[length];
+ Speckle.Sdk.Common.Sha256.Hash(input.AsSpan(), true, resultUpperSpan);
- resultLowerSpan.Should().Be(new string(expected.ToLower()[..length]));
+ new string(resultLowerSpan).Should().Be(new string(expected.ToLower()[..length]));
- resultUpperSpan.Should().Be(new string(expected.ToUpper()[..length]));
+ new string(resultUpperSpan).Should().Be(new string(expected.ToUpper()[..length]));
}
[Theory]
[MemberData(nameof(LargeTestCases))]
- public void Sha256_LargeDataTests(string input, string expected)
+ public void Sha256_Span_LargeDataTests(string input, string expected)
{
- var computedHash = Speckle.Sdk.Common.Sha256.GetString(input.AsSpan());
- computedHash.Should().Be(expected);
+ Span output = stackalloc char[Speckle.Sdk.Common.Sha256.HASH_SIZE_CHARS];
+ Speckle.Sdk.Common.Sha256.Hash(input.AsSpan(), false, output);
+ new string(output).Should().Be(expected);
}
}