| File: Utilities\Checksum.Builder.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.Buffers.Binary; using System.Diagnostics; using System.IO.Hashing; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Microsoft.AspNetCore.Razor.Utilities; internal readonly partial record struct Checksum { internal readonly ref partial struct Builder { private const int XxHash128SizeBytes = 128 / 8; private static readonly XxHash128Pool s_hashPool = XxHash128Pool.Default; private enum TypeKind : byte { Null, Bool, Int32, Int64, String, Checksum, Byte, Char, } // Small (8 byte), per-thread byte array to use as a buffer for appending primitive values to the hash. [ThreadStatic] private static byte[]? s_buffer; private readonly XxHash128 _hash; public Builder() { _hash = s_hashPool.Get(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] static Span<byte> GetBuffer(int length = 8) { Debug.Assert(length <= 8, $"length should never be greater than 8."); var buffer = s_buffer ??= new byte[8]; return buffer.AsSpan(0, length); } public Checksum FreeAndGetChecksum() { Span<byte> hash = stackalloc byte[XxHash128SizeBytes]; _hash.GetHashAndReset(hash); var result = From(hash); s_hashPool.Return(_hash); return result; } private void AppendBuffer(int count) { Debug.Assert(s_buffer is not null); _hash.Append(s_buffer.AsSpan(0, count)); } private void AppendTypeKind(TypeKind kind) { AppendByteValue((byte)kind); } private void AppendBoolValue(bool value) { AppendByteValue((byte)(value ? 1 : 0)); } private void AppendByteValue(byte value) { var buffer = GetBuffer(length: sizeof(byte)); buffer[0] = value; _hash.Append(buffer); } private void AppendCharValue(char value) { var buffer = GetBuffer(length: sizeof(char)); BinaryPrimitives.WriteUInt16LittleEndian(buffer, value); _hash.Append(buffer); } private void AppendInt32Value(int value) { var buffer = GetBuffer(length: sizeof(int)); BinaryPrimitives.WriteInt32LittleEndian(buffer, value); _hash.Append(buffer); } private void AppendInt64Value(long value) { var buffer = GetBuffer(length: sizeof(long)); BinaryPrimitives.WriteInt64LittleEndian(buffer, value); _hash.Append(buffer); } private void AppendStringValue(string value) { _hash.Append(MemoryMarshal.AsBytes(value.AsSpan())); _hash.Append(MemoryMarshal.AsBytes("\0".AsSpan())); } private void AppendChecksumValue(Checksum value) { AppendInt64Value(value.Data1); AppendInt64Value(value.Data2); } public void AppendNull() { AppendTypeKind(TypeKind.Null); } public void Append(bool value) { AppendTypeKind(TypeKind.Bool); AppendBoolValue(value); } public void Append(byte value) { AppendTypeKind(TypeKind.Byte); AppendByteValue(value); } public void Append(char value) { AppendTypeKind(TypeKind.Char); AppendCharValue(value); } public void Append(int value) { AppendTypeKind(TypeKind.Int32); AppendInt32Value(value); } public void Append(long value) { AppendTypeKind(TypeKind.Int64); AppendInt64Value(value); } public void Append(string? value) { if (value is null) { AppendNull(); return; } AppendTypeKind(TypeKind.String); AppendStringValue(value); } public void Append(Checksum value) { AppendTypeKind(TypeKind.Checksum); AppendChecksumValue(value); } public void Append(object? value) { switch (value) { case null: AppendNull(); break; case string s: Append(s); break; case Checksum c: Append(c); break; case bool b: Append(b); break; case int i: Append(i); break; case long l: Append(l); break; case byte b: Append(b); break; case char c: Append(c); break; default: throw new ArgumentException( SR.FormatUnsupported_type_0(value.GetType().FullName), nameof(value)); } } } }