File: Workspace\Solution\Checksum_Factory.cs
Web Access
Project: ..\..\..\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;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Serialization;
using Roslyn.Utilities;
using System.Diagnostics;
 
namespace Microsoft.CodeAnalysis
{
    // various factory methods. all these are just helper methods
    internal partial class Checksum
    {
        private static readonly ObjectPool<IncrementalHash> s_incrementalHashPool =
            new(() => IncrementalHash.CreateHash(HashAlgorithmName.SHA256), size: 20);
 
        public static Checksum Create(IEnumerable<string> values)
        {
            using var pooledHash = s_incrementalHashPool.GetPooledObject();
            using var pooledBuffer = SharedPools.ByteArray.GetPooledObject();
            var hash = pooledHash.Object;
 
            foreach (var value in values)
            {
                AppendData(hash, pooledBuffer.Object, value);
                AppendData(hash, pooledBuffer.Object, "\0");
            }
 
            return From(hash.GetHashAndReset());
        }
 
        public static Checksum Create(string value)
        {
            using var pooledHash = s_incrementalHashPool.GetPooledObject();
            using var pooledBuffer = SharedPools.ByteArray.GetPooledObject();
            var hash = pooledHash.Object;
 
            AppendData(hash, pooledBuffer.Object, value);
 
            return From(hash.GetHashAndReset());
        }
 
        public static Checksum Create(Stream stream)
        {
            using var pooledHash = s_incrementalHashPool.GetPooledObject();
            using var pooledBuffer = SharedPools.ByteArray.GetPooledObject();
 
            var hash = pooledHash.Object;
 
            var buffer = pooledBuffer.Object;
            var bufferLength = buffer.Length;
            int bytesRead;
            do
            {
                bytesRead = stream.Read(buffer, 0, bufferLength);
                if (bytesRead > 0)
                {
                    hash.AppendData(buffer, 0, bytesRead);
                }
            }
            while (bytesRead > 0);
 
            var bytes = hash.GetHashAndReset();
 
            // if bytes array is bigger than certain size, checksum
            // will truncate it to predetermined size. for more detail,
            // see the Checksum type
            //
            // the truncation can happen since different hash algorithm or 
            // same algorithm on different platform can have different hash size
            // which might be bigger than the Checksum HashSize.
            //
            // hash algorithm used here should remain functionally correct even
            // after the truncation
            return From(bytes);
        }
 
        public static Checksum Create(IObjectWritable @object)
        {
            using var stream = SerializableBytes.CreateWritableStream();
 
            using (var objectWriter = new ObjectWriter(stream, leaveOpen: true))
            {
                @object.WriteTo(objectWriter);
            }
 
            stream.Position = 0;
            return Create(stream);
        }
 
        public static Checksum Create(Checksum checksum1, Checksum checksum2)
        {
            using var stream = SerializableBytes.CreateWritableStream();
 
            using (var writer = new ObjectWriter(stream, leaveOpen: true))
            {
                checksum1.WriteTo(writer);
                checksum2.WriteTo(writer);
            }
 
            stream.Position = 0;
            return Create(stream);
        }
 
        public static Checksum Create(Checksum checksum1, Checksum checksum2, Checksum checksum3)
        {
            using var stream = SerializableBytes.CreateWritableStream();
 
            using (var writer = new ObjectWriter(stream, leaveOpen: true))
            {
                checksum1.WriteTo(writer);
                checksum2.WriteTo(writer);
                checksum3.WriteTo(writer);
            }
 
            stream.Position = 0;
            return Create(stream);
        }
 
        public static Checksum Create(IEnumerable<Checksum> checksums)
        {
            using var stream = SerializableBytes.CreateWritableStream();
 
            using (var writer = new ObjectWriter(stream, leaveOpen: true))
            {
                foreach (var checksum in checksums)
                    checksum.WriteTo(writer);
            }
 
            stream.Position = 0;
            return Create(stream);
        }
 
        public static Checksum Create(ImmutableArray<byte> bytes)
        {
            using var stream = SerializableBytes.CreateWritableStream();
 
            using (var writer = new ObjectWriter(stream, leaveOpen: true))
            {
                for (var i = 0; i < bytes.Length; i++)
                    writer.WriteByte(bytes[i]);
            }
 
            stream.Position = 0;
            return Create(stream);
        }
 
        public static Checksum Create<T>(T value, ISerializerService serializer)
        {
            using var stream = SerializableBytes.CreateWritableStream();
            using var context = new SolutionReplicationContext();
 
            using (var objectWriter = new ObjectWriter(stream, leaveOpen: true))
            {
                serializer.Serialize(value!, objectWriter, context, CancellationToken.None);
            }
 
            stream.Position = 0;
            return Create(stream);
        }
 
        public static Checksum Create(ParseOptions value, ISerializerService serializer)
        {
            using var stream = SerializableBytes.CreateWritableStream();
 
            using (var objectWriter = new ObjectWriter(stream, leaveOpen: true))
            {
                serializer.SerializeParseOptions(value, objectWriter);
            }
 
            stream.Position = 0;
            return Create(stream);
        }
 
        private static void AppendData(IncrementalHash hash, byte[] buffer, string value)
        {
            var stringBytes = MemoryMarshal.AsBytes(value.AsSpan());
            Debug.Assert(stringBytes.Length == value.Length * 2);
 
            var index = 0;
            while (index < stringBytes.Length)
            {
                var remaining = stringBytes.Length - index;
                var toCopy = Math.Min(remaining, buffer.Length);
 
                stringBytes.Slice(index, toCopy).CopyTo(buffer);
                hash.AppendData(buffer, 0, toCopy);
 
                index += toCopy;
            }
        }
    }
}