< Summary

Information
Class: SwitchBlade.Core.LruRegexCache
Assembly: SwitchBlade
File(s): D:\a\switchblade\switchblade\Core\LruRegexCache.cs
Tag: 203_23722840422
Line coverage
100%
Covered lines: 49
Uncovered lines: 0
Coverable lines: 49
Total lines: 101
Line coverage: 100%
Branch coverage
100%
Covered branches: 12
Total branches: 12
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%22100%
GetOrCreate(...)100%1010100%
Clear()100%11100%
get_Count()100%11100%

File(s)

D:\a\switchblade\switchblade\Core\LruRegexCache.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Text.RegularExpressions;
 4
 5namespace SwitchBlade.Core
 6{
 7    /// <summary>
 8    /// Thread-safe LRU cache for compiled regex patterns.
 9    /// Extracted from MainViewModel to follow Single Responsibility Principle.
 10    /// </summary>
 11    public class LruRegexCache : IRegexCache
 12    {
 2613        private readonly Dictionary<string, (Regex Regex, LinkedListNode<string> Node)> _cache = new();
 2614        private readonly LinkedList<string> _lruList = new();
 2615        private readonly object _lock = new();
 16        private readonly int _maxSize;
 17
 18        /// <summary>
 19        /// Creates a new LRU regex cache with the specified capacity.
 20        /// </summary>
 21        /// <param name="maxSize">Maximum number of patterns to cache.</param>
 2622        public LruRegexCache(int maxSize = 50)
 2623        {
 2624            if (maxSize <= 0)
 225                throw new ArgumentOutOfRangeException(nameof(maxSize), "Max size must be positive.");
 26
 2427            _maxSize = maxSize;
 2428        }
 29
 30        /// <inheritdoc />
 31        public Regex? GetOrCreate(string pattern)
 2132        {
 2133            if (string.IsNullOrEmpty(pattern))
 234                return null;
 35
 1936            lock (_lock)
 1937            {
 1938                if (_cache.TryGetValue(pattern, out var entry))
 339                {
 40                    // Move to front (LRU update) - O(1)
 341                    _lruList.Remove(entry.Node);
 342                    _lruList.AddFirst(entry.Node);
 343                    return entry.Regex;
 44                }
 45
 46                try
 1647                {
 48                    // NonBacktracking prevents ReDoS for user-provided patterns
 49                    // Available in .NET 7+
 1650                    var regex = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture | RegexOptions
 51
 52                    // Add to cache - O(1)
 1453                    var node = _lruList.AddFirst(pattern);
 1454                    _cache[pattern] = (regex, node);
 55
 56                    // Evict if exceeded capacity
 1657                    while (_cache.Count > _maxSize && _lruList.Count > 0)
 258                    {
 259                        var last = _lruList.Last;
 260                        if (last != null)
 261                        {
 262                            _cache.Remove(last.Value);
 263                            _lruList.RemoveLast();
 264                        }
 265                    }
 66
 1467                    return regex;
 68                }
 269                catch (ArgumentException)
 270                {
 71                    // Invalid regex pattern
 272                    return null;
 73                }
 74            }
 2175        }
 76
 77        /// <inheritdoc />
 78        public void Clear()
 179        {
 180            lock (_lock)
 181            {
 182                _cache.Clear();
 183                _lruList.Clear();
 184            }
 185        }
 86
 87        /// <summary>
 88        /// Gets the current number of cached patterns.
 89        /// </summary>
 90        public int Count
 91        {
 92            get
 293            {
 294                lock (_lock)
 295                {
 296                    return _cache.Count;
 97                }
 298            }
 99        }
 100    }
 101}