File: CodeGen\StringTokenMap.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.Collections.Concurrent;
using System.Collections.Generic;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CodeGen;
 
/// <summary>
/// Handles storage of strings referenced via tokens in metadata. When values are stored
/// they are uniquely "associated" with fake tokens, which are basically sequential numbers.
/// IL gen will use these fake tokens during codegen and later, when actual values
/// are known, the method bodies will be patched.
/// </summary>
internal sealed class StringTokenMap(int initialHeapSize)
{
    private readonly ConcurrentDictionary<string, uint> _valueToToken = new ConcurrentDictionary<string, uint>(ReferenceEqualityComparer.Instance);
    private readonly ArrayBuilder<string> _uniqueValues = [];
    private int _heapSize = initialHeapSize;
 
    public bool TryGetOrAddToken(string value, out uint token)
    {
        // NOTE: cannot use GetOrAdd here since _uniqueValues and _valueToToken must be in sync
        // so if we do need to add we have to take a lock and modify both collections.
        if (_valueToToken.TryGetValue(value, out token))
        {
            return true;
        }
 
        lock (_uniqueValues)
        {
            if (_valueToToken.TryGetValue(value, out token))
            {
                return true;
            }
 
            // String can span beyond the heap limit, but must start within the limit.
            if (_heapSize > MetadataHelpers.UserStringHeapCapacity)
            {
                return false;
            }
 
            _heapSize += MetadataHelpers.GetUserStringBlobSize(value);
 
            token = (uint)_uniqueValues.Count;
            _uniqueValues.Add(value);
            _valueToToken.Add(value, token);
 
            return true;
        }
    }
 
    public string GetValue(uint token)
    {
        lock (_uniqueValues)
        {
            return _uniqueValues[(int)token];
        }
    }
 
    public string[] CopyValues()
    {
        lock (_uniqueValues)
        {
            return _uniqueValues.ToArray();
        }
    }
}