// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Linq; namespace UnrealBuildTool { /// /// Maps a unique string to an integer /// public class UniqueStringRegistry { // protect the InstanceMap private object LockObject = new object(); // holds a mapping of string name to single instance private Dictionary StringToInstanceMap = new Dictionary(StringComparer.OrdinalIgnoreCase); // holds a mapping of string aliases to original string name private Dictionary AliasToStringMap = new Dictionary(StringComparer.OrdinalIgnoreCase); /// /// Default constructor /// public UniqueStringRegistry() { } /// /// See if string is in registry /// /// True if string is present in registry public bool HasString(string Name) { return StringToInstanceMap.ContainsKey(Name); } /// /// Add string if missing and/or lookup /// /// String instance id public int FindOrAddByName(string Name) { // look for existing one int Instance = -1; if (!StringToInstanceMap.TryGetValue(Name, out Instance)) { lock (LockObject) { if (!StringToInstanceMap.TryGetValue(Name, out Instance)) { // copy over the dictionary and add, so that other threads can keep reading out of the old one until it's time Dictionary NewStringToInstanceMap = new Dictionary(StringToInstanceMap, StringComparer.OrdinalIgnoreCase); // make and add a new instance number Instance = StringToInstanceMap.Count; NewStringToInstanceMap[Name] = Instance; // replace the class's map StringToInstanceMap = NewStringToInstanceMap; } } } return Instance; } /// /// Get list of strings /// /// String list public string[] GetStringNames() { return StringToInstanceMap.Keys.ToArray(); } /// /// Get list of string ids /// /// String id list public int[] GetStringIds() { return StringToInstanceMap.Values.ToArray(); } /// /// Get string given instance id /// /// String public string GetStringForId(int Id) { return StringToInstanceMap.First(x => x.Value == Id).Key; } /// /// See if alias exists in registry /// /// True if alias exists public bool HasAlias(string Alias) { return AliasToStringMap.ContainsKey(Alias); } /// /// Get instance id of alias /// /// Instance id of alias public int FindExistingAlias(string Alias) { string? Name; if (!AliasToStringMap.TryGetValue(Alias, out Name) || Name == null) { throw new BuildException($"Alias {Alias} not found"); } return FindOrAddByName(Name); } /// /// Add alias if missing and/or lookup /// /// Instance id of alias public int FindOrAddAlias(string Alias, string OriginalName) { string? Name; if (!AliasToStringMap.TryGetValue(Alias, out Name)) { lock (LockObject) { // copy over the dictionary and add, so that other threads can keep reading out of the old one until it's time Dictionary NewAliasToStringMap = new Dictionary(AliasToStringMap, StringComparer.OrdinalIgnoreCase); // add a new alias NewAliasToStringMap[Alias] = OriginalName; // replace the instance map AliasToStringMap = NewAliasToStringMap; } Name = OriginalName; } else { if (Name != OriginalName || Name == null) { throw new BuildException($"{Alias} is already an alias for {Name}. Can't associate with {OriginalName}"); } } return FindOrAddByName(Name); } } }