File: Symbols\CommonGeneratedNameParser.cs
Web Access
Project: src\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();
    }
}