github.com/web-platform-tests/wpt.fyi@v0.0.0-20240530210107-70cf978996f1/api/query/cache/lru/lru.go (about)

     1  // Copyright 2018 The WPT Dashboard Project. All rights reserved.
     2  // Use of this source code is governed by a BSD-style license that can be
     3  // found in the LICENSE file.
     4  
     5  package lru
     6  
     7  import (
     8  	"math"
     9  	"sort"
    10  	"sync"
    11  	"time"
    12  )
    13  
    14  // LRU is a least recently used collection that supports element acces and
    15  // item eviction. The first access of an unevicted value implicitly adds the
    16  // value to the collection.
    17  type LRU interface {
    18  	// Access stores the current time as the "last accessed" time for the given
    19  	// value in the collection. The first access of an unevicted value implicitly
    20  	// adds the value to the collection.
    21  	Access(value int64)
    22  	// EvictLRU removes and returns a fraction of the collection, based on
    23  	// the passed percentage. It will always remove at least one item. When
    24  	// deciding which items to remove, EvictLRU deletes older values from
    25  	// the collection first. If the collection is empty, nil is returned.
    26  	EvictLRU(percent float64) []int64
    27  }
    28  
    29  type lru struct {
    30  	values map[int64]time.Time
    31  	m      *sync.Mutex
    32  }
    33  
    34  type lruEntry struct {
    35  	int64
    36  	time.Time
    37  }
    38  
    39  type lruEntries []lruEntry
    40  
    41  // Satisfy the Sort interface requirements (https://golang.org/pkg/sort/#Sort)
    42  func (s lruEntries) Len() int           { return len(s) }
    43  func (s lruEntries) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
    44  func (s lruEntries) Less(i, j int) bool { return s[i].Time.Before(s[j].Time) }
    45  
    46  func (l *lru) Access(v int64) {
    47  	l.syncAccess(v)
    48  }
    49  
    50  func (l *lru) EvictLRU(percent float64) []int64 {
    51  	if len(l.values) == 0 {
    52  		return nil
    53  	}
    54  	percent = math.Max(0.0, math.Min(1.0, percent))
    55  	numValuesToDelete := math.Floor(float64(len(l.values)) * percent)
    56  	// Always delete at least one entry.
    57  	return l.syncEvictLRU(int(math.Max(1.0, numValuesToDelete)))
    58  }
    59  
    60  // NewLRU constructs a new empty LRU.
    61  // nolint:ireturn // TODO: Fix ireturn lint error
    62  func NewLRU() LRU {
    63  	return &lru{
    64  		values: make(map[int64]time.Time),
    65  		m:      &sync.Mutex{},
    66  	}
    67  }
    68  
    69  func (l *lru) syncAccess(v int64) {
    70  	l.m.Lock()
    71  	defer l.m.Unlock()
    72  
    73  	l.values[v] = time.Now()
    74  }
    75  
    76  func (l *lru) syncEvictLRU(num int) []int64 {
    77  	l.m.Lock()
    78  	defer l.m.Unlock()
    79  
    80  	vs := make(lruEntries, 0, len(l.values))
    81  	for v, t := range l.values {
    82  		vs = append(vs, lruEntry{v, t})
    83  	}
    84  	sort.Sort(vs)
    85  	toRemove := vs[:num]
    86  	ret := make([]int64, num)
    87  	for i := range toRemove {
    88  		value := toRemove[i].int64
    89  		ret[i] = value
    90  		delete(l.values, value)
    91  	}
    92  
    93  	return ret
    94  }