Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheKeyIndex.ps1

<#
.SYNOPSIS
Returns a key-to-item index for an in-memory cache list.
 
.DESCRIPTION
Add-CacheItem and Get-CacheItem used to find a key by scanning the whole cache, which made
filling a cache item by item quadratic. This function keeps one dictionary per cache type, keyed
case-insensitively like PowerShell's -eq, in $Global:AzDoCacheKeyIndex.
 
The index records the list it was built from and how many items that list held. It is rebuilt
whenever the global has been replaced with a different list or its count no longer matches,
so code that changes a cache without going through Add-CacheItem cannot leave a stale index.
 
A cache holding the same key twice cannot be indexed one-to-one. The function returns $null
for it, and callers fall back to scanning the list.
 
.PARAMETER Type
The cache type the list belongs to.
 
.PARAMETER Cache
The live cache list, as returned by Get-CacheObject.
 
.OUTPUTS
System.Collections.Generic.Dictionary[string, CacheItem], or $null.
 
.EXAMPLE
$index = Get-CacheKeyIndex -Type 'LiveProjects' -Cache $cache
 
.NOTES
This function is private and should not be used directly.
#>

function Get-CacheKeyIndex
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $Type,

        [Parameter(Mandatory = $true)]
        [AllowEmptyCollection()]
        [System.Collections.Generic.List[CacheItem]]
        $Cache
    )

    if ($Global:AzDoCacheKeyIndex -isnot [hashtable])
    {
        $Global:AzDoCacheKeyIndex = @{}
    }

    $entry = $Global:AzDoCacheKeyIndex[$Type]
    if (($null -ne $entry) -and [object]::ReferenceEquals($entry.List, $Cache) -and ($entry.Map.Count -eq $Cache.Count))
    {
        return ,$entry.Map
    }

    Write-Verbose "[Get-CacheKeyIndex] Building the key index for cache '$Type' ($($Cache.Count) items)."

    $map = [System.Collections.Generic.Dictionary[string, CacheItem]]::new([System.StringComparer]::OrdinalIgnoreCase)
    foreach ($item in $Cache)
    {
        if ($null -eq $item -or $map.ContainsKey($item.Key))
        {
            # A duplicate (or a null entry) means the list cannot be indexed one-to-one.
            $null = $Global:AzDoCacheKeyIndex.Remove($Type)
            return $null
        }
        $map[$item.Key] = $item
    }

    $Global:AzDoCacheKeyIndex[$Type] = @{ List = $Cache; Map = $map }
    return ,$map
}