using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; namespace RGB.NET.Core; /// /// Offers some methods to create and handle unique identifiers. /// public static class IdGenerator { #region Properties & Fields // ReSharper disable InconsistentNaming private static readonly HashSet _registeredIds = new(); private static readonly Dictionary> _idMappings = new(); private static readonly Dictionary> _counter = new(); // ReSharper restore InconsistentNaming #endregion #region Methods /// /// Makes the specified id unique based on the calling assembly by adding a counter if needed. /// /// The id to make unique. /// The unique id. [MethodImpl(MethodImplOptions.NoInlining)] public static string MakeUnique(string id) => MakeUnique(Assembly.GetCallingAssembly(), id); internal static string MakeUnique(Assembly callingAssembly, string id) { if (!_idMappings.TryGetValue(callingAssembly, out Dictionary? idMapping)) { _idMappings.Add(callingAssembly, idMapping = new Dictionary()); _counter.Add(callingAssembly, new Dictionary()); } Dictionary counterMapping = _counter[callingAssembly]; if (!idMapping.TryGetValue(id, out string? mappedId)) { mappedId = id; int mappingCounter = 1; while (_registeredIds.Contains(mappedId)) mappedId = $"{id} ({++mappingCounter})"; _registeredIds.Add(mappedId); idMapping.Add(id, mappedId); } counterMapping.TryAdd(mappedId, 0); int counter = ++counterMapping[mappedId]; return counter <= 1 ? mappedId : $"{mappedId} ({counter})"; } /// /// Resets the counter used to create unique ids. /// All previous generated ids are not garantueed to stay unique if this is called! /// [MethodImpl(MethodImplOptions.NoInlining)] public static void ResetCounter() => ResetCounter(Assembly.GetCallingAssembly()); internal static void ResetCounter(Assembly callingAssembly) { if (_counter.TryGetValue(callingAssembly, out Dictionary? counter)) counter.Clear(); } #endregion }