| File: RestoreCommand\DependencyGraphResolver.DependencyGraphItemIndexer.cs | Web Access |
| Project: src\nuget-client\src\NuGet.Core\NuGet.Commands\NuGet.Commands.csproj (NuGet.Commands) |
// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Concurrent; using NuGet.LibraryModel; namespace NuGet.Commands { internal sealed partial class DependencyGraphResolver { /// <summary> /// Represents a unique number for a library by its name, case-insensitive, so that any libraries with the same name will have the same index. /// </summary> public enum LibraryDependencyIndex : int { /// <summary> /// The index of the root project. /// </summary> Project = 0, } /// <summary> /// Represents a unique number for a library by its library range. This generally means that two libraries with the same name and version will have the same index. /// </summary> public enum LibraryRangeIndex : int { /// <summary> /// The index used as a parent of the root project. /// </summary> None = -1, /// <summary> /// The index of the root project. /// </summary> Project = 0, } /// <summary> /// A class used to translate library dependency names and versions to integers for faster dictionary lookup. /// </summary> /// <remarks> /// Using a dictionary key of a string is relatively slow compared to an integer. This class uses a dictionary and assigned incrementing numbers to the libraries by name and version and stores them. /// </remarks> public sealed class DependencyGraphItemIndexer { /// <summary> /// An object used to <see langword="lock" /> access to the library dependency table. /// </summary> private readonly object _libraryDependencyLockObject = new(); /// <summary> /// An object used to <see langword="lock" /> access to the library range table. /// </summary> private readonly object _libraryRangeLockObject = new(); /// <summary> /// A case-insensitive dictionary that stores a <see cref="LibraryDependencyIndex" /> by the name of a library. /// </summary> private readonly ConcurrentDictionary<string, LibraryDependencyIndex> _libraryDependencyTable = new ConcurrentDictionary<string, LibraryDependencyIndex>(StringComparer.OrdinalIgnoreCase); /// <summary> /// A case-insensitive dictionary that stores a <see cref="LibraryRangeIndex" /> by the <see cref="LibraryRange" /> of a library. This dictionary uses the <see cref="LibraryRangeComparer" /> to compare <see cref="LibraryRange" /> objects. /// </summary> private readonly ConcurrentDictionary<LibraryRange, LibraryRangeIndex> _libraryRangeTable = new(LibraryRangeComparer.Instance); /// <summary> /// Stores the next index to use if a unique library is indexed. /// </summary> private LibraryDependencyIndex _nextLibraryDependencyIndex = LibraryDependencyIndex.Project + 1; /// <summary> /// Stores the next index to use if a unique <see cref="LibraryRange" /> is indexed. /// </summary> private LibraryRangeIndex _nextLibraryRangeIndex = LibraryRangeIndex.Project + 1; /// <summary> /// Initializes a new instance of the <see cref="DependencyGraphItemIndexer" /> class. /// </summary> /// <param name="libraryDependency">A <see cref="LibraryDependency" /> representing the root project.</param> public DependencyGraphItemIndexer(LibraryDependency libraryDependency) { _libraryDependencyTable.TryAdd(libraryDependency.Name, LibraryDependencyIndex.Project); _libraryRangeTable.TryAdd(libraryDependency.LibraryRange, LibraryRangeIndex.Project); } /// <summary> /// Indexes a <see cref="LibraryDependency" /> and returns a <see cref="LibraryDependencyIndex" /> associated with the its name. /// </summary> /// <param name="libraryDependency">The <see cref="LibraryDependency" /> of the library to index.</param> /// <returns>A <see cref="LibraryDependencyIndex" /> representing the unique integer associated with the name of the library.</returns> public LibraryDependencyIndex Index(LibraryDependency libraryDependency) => Index(libraryDependency.Name); /// <summary> /// Indexes a <see cref="CentralPackageVersion" /> and returns a <see cref="LibraryDependencyIndex" /> associated with the its name."/> /// </summary> /// <param name="centralPackageVersion">The <see cref="CentralPackageVersion" /> to index.</param> /// <returns>A <see cref="LibraryDependencyIndex" /> representing the unique integer associated with the name of the library.</returns> public LibraryDependencyIndex Index(CentralPackageVersion centralPackageVersion) => Index(centralPackageVersion.Name); /// <summary> /// Indexes a library by its name and returns a <see cref="LibraryDependencyIndex" /> associated with it. /// </summary> /// <param name="name">The name of the library.</param> /// <returns>A <see cref="LibraryDependencyIndex" /> representing the unique integer associated with the name of the library.</returns> private LibraryDependencyIndex Index(string name) { lock (_libraryDependencyLockObject) { // Attempt to get an existing index value if (_libraryDependencyTable.TryGetValue(name, out LibraryDependencyIndex index)) { return index; } // Increment the index since this is a new library index = _nextLibraryDependencyIndex++; // Store the index in the table so it can be used for equal library names _libraryDependencyTable.TryAdd(name, index); return index; } } /// <summary> /// Indexes a <see cref="LibraryRange" /> and returns a unique <see cref="LibraryRangeIndex" /> associated with it. /// </summary> /// <remarks> /// In general, this method treats version ranges as equal by name and version. However, certain type constraints are treated as different. /// The <see cref="LibraryRangeComparer" /> is used to determine if two <see cref="LibraryRange" /> objects are equal. /// </remarks> /// <param name="libraryRange">The <see cref="LibraryRange" /> to index.</param> /// <returns>A <see cref="LibraryRangeIndex" /> associated with the <see cref="LibraryRange" />.</returns> public LibraryRangeIndex Index(LibraryRange libraryRange) { lock (_libraryRangeLockObject) { // Attempt to get an existing index value if (_libraryRangeTable.TryGetValue(libraryRange, out LibraryRangeIndex index)) { return index; } // Increment the index since this is a new library range index = _nextLibraryRangeIndex++; // Store the index in the table so it can be re-used by equal library ranges _libraryRangeTable.TryAdd(libraryRange, index); return index; } } /// <summary> /// Creates an array containing the parents of a dependency representing its path. /// </summary> /// <param name="existingPath">An existing array of <see cref="LibraryRangeIndex" /> values of the parent.</param> /// <param name="libraryRangeIndex">The <see cref="LibraryRangeIndex" /> of the library range.</param> /// <returns>An array containing the existing path and the specified <see cref="LibraryRangeIndex" />.</returns> public static LibraryRangeIndex[] CreatePathToRef(LibraryRangeIndex[] existingPath, LibraryRangeIndex libraryRangeIndex) { // Create a new array, copy the existing path into it, and add the new library range index LibraryRangeIndex[] newPath = new LibraryRangeIndex[existingPath.Length + 1]; Array.Copy(existingPath, newPath, existingPath.Length); newPath[newPath.Length - 1] = libraryRangeIndex; return newPath; } } } }