classes/LFUCache.class.ps1
using namespace System.Collections.Generic class LFUCacheEntry { [object]$Key [object]$Item [int]$Hits LFUCacheEntry($key,$item) { $this.Key = $key $this.Item = $item $this.Hits = 1 } LFUCacheEntry($key,$item,$hits) { $this.Key = $key $this.Item = $item $this.Hits = $hits } Hit() { $this.Hits++ } Tick() { if($this.Hits -gt 0){ $this.Hits-- } } } class LFUCache : PSObjectCache { hidden [IDictionary[int,LinkedList[LFUCacheEntry]]] $FrequencyTable hidden [int] $gc [int]$Capacity [int]$MinHits LFUCache([scriptblock]$Fetcher,[int]$Capacity) : base($Fetcher) { $this.Capacity = $Capacity $this.FrequencyTable = [Dictionary[int,LinkedList[LFUCacheEntry]]]::new($Capacity) } hidden [void] Promote([LinkedListNode[LFUCacheEntry]]$Node) { $list = $this.FrequencyTable[$Node.Value.Hits] $list.Remove($Node) if($list.Count -lt 1){ $this.FrequencyTable.Remove($Node.Value.Hits) } if($this.MinHits -eq $Node.Value.Hits){ $this.MinHits++ } $Node.Value.Hit() if(-not $this.FrequencyTable.ContainsKey($Node.Value.Hits)){ $this.FrequencyTable[$Node.Value.Hits] = [LinkedList[LFUCacheEntry]]::new() } $this.FrequencyTable[$Node.Value.Hits].AddFirst($Node) } [psobject] Get($Key){ if($this.LookupTable.Contains($Key)){ $this.Promote($this.LookupTable[$Key]) return $this.LookupTable[$Key].Value.Item } else{ try{ $copy = & $this.Fetcher $Key } catch{ throw $_ return $null } $this.Add($key,$copy) return $copy } } [void] Add($key, $val) { if($this.LookupTable.Contains($key)){ $this.LookupTable[$key].Value = $val } else{ while($this.LookupTable.Count -ge $this.Capacity){ $hitList = $this.FrequencyTable[$this.MinHits] $lfuNode = $hitList.Last $hitList.RemoveLast() if($hitList.Count -lt 1){ $this.FrequencyTable.Remove($this.MinHits) } $this.LookupTable.Remove($lfuNode.Value.Key) } $newEntry = [LFUCacheEntry]::new($key, $val) $this.MinHits = $newEntry.Hits if(-not $this.FrequencyTable.ContainsKey($newEntry.Hits)){ $this.FrequencyTable[$newEntry.Hits] = [LinkedList[LFUCacheEntry]]::new() } $newNode = $this.FrequencyTable[$newEntry.Hits].AddFirst($newEntry) $this.LookupTable[$key] = $newNode } } } |