| File: Utilities\Checksum.cs | Web Access |
| Project: src\roslyn\src\Razor\src\Shared\Microsoft.AspNetCore.Razor.Utilities.Shared\Microsoft.AspNetCore.Razor.Utilities.Shared.csproj (Microsoft.AspNetCore.Razor.Utilities.Shared) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Immutable; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Microsoft.AspNetCore.Razor.Utilities; /// <summary> /// Checksum of data can be used later to see whether two data are same or not /// without actually comparing data itself. /// </summary> [StructLayout(LayoutKind.Explicit, Size = HashSize)] internal readonly partial record struct Checksum( [field: FieldOffset(0)] long Data1, [field: FieldOffset(8)] long Data2) : IComparable<Checksum> { /// <summary> /// The intended size of the <see cref="Checksum"/> in bytes. /// </summary> private const int HashSize = 16; #if !NET // Small (HashSize length), per-thread array to use when converting to base64 on non-.NET. [ThreadStatic] private static byte[]? s_bytes; #endif /// <summary> /// Represents a default/null/invalid Checksum, equivalent to <c>default(Checksum)</c>. This value /// contains all zeroes, which is considered infinitesimally unlikely to ever happen from hashing data /// (including when hashing null/empty/zero data inputs). /// </summary> public static readonly Checksum Null = default; public static Checksum From(byte[] bytes) => From(bytes.AsSpan()); public static Checksum From(ImmutableArray<byte> bytes) => From(bytes.AsSpan()); public static Checksum From(ReadOnlySpan<byte> bytes) { ArgHelper.ThrowIfLessThan(bytes.Length, HashSize); if (!MemoryMarshal.TryRead(bytes, out Checksum result)) { return ThrowHelper.ThrowInvalidOperationException<Checksum>("Could not read hash data"); } return result; } public string ToBase64String() { #if NET Span<byte> bytes = stackalloc byte[HashSize]; WriteTo(bytes); return Convert.ToBase64String(bytes); #else var bytes = s_bytes ??= new byte[HashSize]; WriteTo(bytes.AsSpan()); return Convert.ToBase64String(bytes); #endif } public static Checksum FromBase64String(string value) => From(Convert.FromBase64String(value)); public void WriteTo(Span<byte> destination) { ArgHelper.ThrowIfDestinationTooShort(destination, HashSize); Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), this); } public bool Equals(Checksum other) => Data1 == other.Data1 && Data2 == other.Data2; public override int GetHashCode() { // The checksum is already a hash. Just read a 4-byte value to get a well-distributed hash code. return (int)Data1; } public int CompareTo(Checksum other) { var result = Data1.CompareTo(other.Data1); return result != 0 ? result : Data2.CompareTo(other.Data2); } public override string ToString() => ToBase64String(); }