File: Symbols\CommonGeneratedNameParser.cs
Web Access
Project: src\src\roslyn\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis)
// 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 Microsoft.CodeAnalysis.CodeGen;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Symbols;

internal static partial class CommonGeneratedNames
{
    /// <summary>
    /// Parses one or two debug ids that the specified <paramref name="metadataNameSuffix"/> ends with.
    /// 
    /// Returns true if <paramref name="metadataNameSuffix"/> ends with one or two well-formed debug ids.
    /// If two ids are present in the name then the first is <paramref name="methodId"/> and the second is <paramref name="entityId"/>.
    /// Otherwise, if <paramref name="isMethodIdOptional"/> is true then the single parsed id is returned in <paramref name="entityId"/>,
    /// otherwise in <paramref name="methodId"/>.
    /// </summary>
    /// <param name="metadataNameSuffix">Suffix of the metadata name following the suffix separator.</param>
    public static bool TryParseDebugIds(ReadOnlySpan<char> metadataNameSuffix, char idSeparator, bool isMethodIdOptional, out DebugId methodId, out DebugId entityId)
    {
        methodId = entityId = default;

        int generation = -1;
        long power = 1;
        long value = 0;

        DebugId? seenId = null;

        for (int i = metadataNameSuffix.Length - 1; i >= -1; i--)
        {
            var c = (i >= 0) ? metadataNameSuffix[i] : '\0';
            switch (c)
            {
                case >= '0' and <= '9':
                    value += (c - '0') * power;
                    if (value > int.MaxValue)
                    {
                        return false;
                    }

                    power *= 10;
                    break;

                case GenerationSeparator:
                    if (generation >= 0 || power == 1)
                    {
                        // bad format:
                        return false;
                    }

                    generation = (int)value;
                    value = 0;
                    power = 1;
                    break;

                default:
                    if (power == 1)
                    {
                        // no digits parsed
                        return false;
                    }

                    var id = new DebugId((int)value, (generation >= 0) ? generation : 0);
                    generation = -1;
                    value = 0;
                    power = 1;

                    if (seenId == null)
                    {
                        if (c == idSeparator)
                        {
                            // continue parsing, we have another id
                            seenId = id;
                            break;
                        }

                        // only a single id is present
                        if (isMethodIdOptional)
                        {
                            entityId = id;
                        }
                        else
                        {
                            methodId = id;
                        }

                        return true;
                    }

                    if (c == idSeparator)
                    {
                        // bad format - only two ids allowed
                        return false;
                    }

                    // two ids are present:
                    methodId = id;
                    entityId = seenId.Value;
                    return true;
            }
        }

        throw ExceptionUtilities.Unreachable();
    }
}