File: SourceGeneration\GeneratorDriverCache.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 System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Microsoft.CodeAnalysis;
 
namespace Microsoft.CodeAnalysis
{
    internal sealed class GeneratorDriverCache(int maxCacheSize = 100)
    {
        private readonly int MaxCacheSize = maxCacheSize;
 
        private readonly (string cacheKey, GeneratorDriver driver)[] _cachedDrivers = new (string, GeneratorDriver)[maxCacheSize];
 
        private readonly object _cacheLock = new object();
 
        private int _cacheSize = 0;
 
        public GeneratorDriver? TryGetDriver(string cacheKey) => AddOrUpdateMostRecentlyUsed(cacheKey, driver: null);
 
        public void CacheGenerator(string cacheKey, GeneratorDriver driver) => AddOrUpdateMostRecentlyUsed(cacheKey, driver);
 
        public int CacheSize => _cacheSize;
 
        /// <summary>
        /// Attempts to find a driver based on <paramref name="cacheKey"/>. If a matching driver is found in the 
        /// cache, or explicitly passed via <paramref name="driver"/>, the cache is updated so that it is at the
        /// head of the list.
        /// </summary>
        /// <param name="cacheKey">The key to lookup the driver by in the cache</param>
        /// <param name="driver">An optional driver that should be cached, if not already found in the cache</param>
        /// <returns></returns>
        private GeneratorDriver? AddOrUpdateMostRecentlyUsed(string cacheKey, GeneratorDriver? driver)
        {
            lock (_cacheLock)
            {
                // try and find the driver if it's present
                int i = 0;
                for (; i < _cacheSize; i++)
                {
                    if (_cachedDrivers[i].cacheKey == cacheKey)
                    {
                        driver ??= _cachedDrivers[i].driver;
                        break;
                    }
                }
 
                // if we found it (or were passed a new one), update the cache so its at the head of the list
                if (driver is not null)
                {
                    for (i = Math.Min(i, MaxCacheSize - 1); i > 0; i--)
                    {
                        _cachedDrivers[i] = _cachedDrivers[i - 1];
                    }
                    _cachedDrivers[0] = (cacheKey, driver);
                    _cacheSize = Math.Min(MaxCacheSize, _cacheSize + 1);
                }
 
                return driver;
            }
        }
    }
}