File: Symbols\FileIdentifier.cs
Web Access
Project: src\roslyn\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp)
// 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.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.CodeAnalysis.CSharp;

internal sealed class FileIdentifier
{
    private class FileIdentifierData(string? encoderFallbackErrorMessage, string displayFilePath, ImmutableArray<byte> filePathChecksumOpt)
    {
        public readonly string? EncoderFallbackErrorMessage = encoderFallbackErrorMessage;
        public readonly string DisplayFilePath = displayFilePath;
        public readonly ImmutableArray<byte> FilePathChecksumOpt = filePathChecksumOpt;
    }

    private static readonly Encoding s_encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);

    private readonly string _filePath;
    private FileIdentifierData? _data;

    private FileIdentifier(string filePath)
    {
        _filePath = filePath;
    }

    private FileIdentifier(ImmutableArray<byte> filePathChecksumOpt, string displayFilePath)
    {
        _data = new FileIdentifierData(encoderFallbackErrorMessage: null, displayFilePath, filePathChecksumOpt);
        _filePath = string.Empty;
    }

    [MemberNotNull(nameof(_data))]
    private void EnsureInitialized()
    {
        if (_data is null)
        {
            string? encoderFallbackErrorMessage = null;
            ImmutableArray<byte> hash = default;
            try
            {
                var encodedFilePath = s_encoding.GetBytes(_filePath);
                using var hashAlgorithm = SourceHashAlgorithms.CreateDefaultInstance();
                hash = hashAlgorithm.ComputeHash(encodedFilePath).ToImmutableArray();
            }
            catch (EncoderFallbackException ex)
            {
                encoderFallbackErrorMessage = ex.Message;
            }

            var displayFilePath = GeneratedNames.GetDisplayFilePath(_filePath);

            _data = new FileIdentifierData(encoderFallbackErrorMessage, displayFilePath, hash);
        }
    }

    public string DisplayFilePath
    {
        get
        {
            EnsureInitialized();

            return _data.DisplayFilePath;
        }
    }

    public string? EncoderFallbackErrorMessage
    {
        get
        {
            EnsureInitialized();

            return _data.EncoderFallbackErrorMessage;
        }
    }

    public ImmutableArray<byte> FilePathChecksumOpt
    {
        get
        {
            EnsureInitialized();

            return _data.FilePathChecksumOpt;
        }
    }

    public static FileIdentifier Create(SyntaxTree syntaxTree, SourceReferenceResolver? resolver)
        => new FileIdentifier(syntaxTree.GetNormalizedPath(resolver));

    public static FileIdentifier Create(string normalizedFilePath)
        => new FileIdentifier(normalizedFilePath);

    public static FileIdentifier Create(ImmutableArray<byte> filePathChecksumOpt, string displayFilePath)
        => new FileIdentifier(filePathChecksumOpt, displayFilePath);
}