| File: Workspace\Solution\Checksum.cs | Web Access |
| Project: src\roslyn\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.Collections.Immutable; using System.IO.Pipelines; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; /// <summary> /// Checksum of data can be used later to see whether two data are same or not /// without actually comparing data itself /// </summary> [DataContract, StructLayout(LayoutKind.Explicit, Size = HashSize)] internal readonly partial record struct Checksum( [field: FieldOffset(0)][property: DataMember(Order = 0)] long Data1, [field: FieldOffset(8)][property: DataMember(Order = 1)] long Data2) : IComparable<Checksum> { /// <summary> /// The intended size of the <see cref="Checksum"/> structure. /// </summary> public const int HashSize = 16; /// <summary> /// Represents a default/null/invalid Checksum, equivalent to <c>default(Checksum)</c>. This values contains /// all zeros 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; /// <summary> /// Create Checksum from given byte array. if byte array is bigger than <see cref="HashSize"/>, it will be /// truncated to the size. /// </summary> public static Checksum From(byte[] checksum) => From(checksum.AsSpan()); /// <summary> /// Create Checksum from given byte array. if byte array is bigger than <see cref="HashSize"/>, it will be /// truncated to the size. /// </summary> public static Checksum From(ImmutableArray<byte> checksum) => From(checksum.AsSpan()); public static Checksum From(ReadOnlySpan<byte> checksum) { if (checksum.Length < HashSize) throw new ArgumentException($"checksum must be equal or bigger than the hash size: {HashSize}", nameof(checksum)); Contract.ThrowIfFalse(MemoryMarshal.TryRead(checksum, out Checksum result)); return result; } public string ToBase64String() { #if NET Span<byte> bytes = stackalloc byte[HashSize]; this.WriteTo(bytes); return Convert.ToBase64String(bytes); #else var bytes = new byte[HashSize]; this.WriteTo(bytes.AsSpan()); return Convert.ToBase64String(bytes); #endif } public static Checksum FromBase64String(string value) => From(Convert.FromBase64String(value)); public override string ToString() => ToBase64String(); public void WriteTo(ObjectWriter writer) { writer.WriteInt64(Data1); writer.WriteInt64(Data2); } public void WriteTo(Span<byte> span) { Contract.ThrowIfTrue(span.Length < HashSize); Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(span), this); } public void WriteTo(PipeWriter pipeWriter) { var span = pipeWriter.GetSpan(HashSize); this.WriteTo(span); pipeWriter.Advance(HashSize); } public static Checksum ReadFrom(ObjectReader reader) => new(reader.ReadInt64(), reader.ReadInt64()); public static Func<Checksum, string> GetChecksumLogInfo { get; } = checksum => checksum.ToString(); public static Func<IEnumerable<Checksum>, string> GetChecksumsLogInfo { get; } = checksums => string.Join("|", checksums.Select(c => c.ToString())); // Explicitly implement this method as default jit for records on netfx doesn't properly devirtualize the // standard calls to EqualityComparer<long>.Default.Equals public bool Equals(Checksum other) => this.Data1 == other.Data1 && this.Data2 == other.Data2; // Directly override to any overhead that records add when hashing things like the EqualityContract 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); } } internal static class ChecksumExtensions { public static void AddIfNotNullChecksum(this HashSet<Checksum> checksums, Checksum checksum) { if (checksum != Checksum.Null) checksums.Add(checksum); } }