// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE_list file. package internal import "time" // Entry is an LRU Entry type Entry[K comparable, V any] struct { // Next and previous pointers in the doubly-linked list of elements. // To simplify the implementation, internally a list l is implemented // as a ring, such that &l.root is both the next element of the last // list element (l.Back()) and the previous element of the first list // element (l.Front()). next, prev *Entry[K, V] // The list to which this element belongs. list *LruList[K, V] // The LRU Key of this element. Key K // The Value stored with this element. Value V // The time this element would be cleaned up, optional ExpiresAt time.Time // The expiry bucket item was put in, optional ExpireBucket uint8 } // PrevEntry returns the previous list element or nil. func (e *Entry[K, V]) PrevEntry() *Entry[K, V] { if p := e.prev; e.list != nil && p != &e.list.root { return p } return nil } // LruList represents a doubly linked list. // The zero Value for LruList is an empty list ready to use. type LruList[K comparable, V any] struct { root Entry[K, V] // sentinel list element, only &root, root.prev, and root.next are used len int // current list Length excluding (this) sentinel element } // Init initializes or clears list l. func (l *LruList[K, V]) Init() *LruList[K, V] { l.root.next = &l.root l.root.prev = &l.root l.len = 0 return l } // NewList returns an initialized list. func NewList[K comparable, V any]() *LruList[K, V] { return new(LruList[K, V]).Init() } // Length returns the number of elements of list l. // The complexity is O(1). func (l *LruList[K, V]) Length() int { return l.len } // Back returns the last element of list l or nil if the list is empty. func (l *LruList[K, V]) Back() *Entry[K, V] { if l.len == 0 { return nil } return l.root.prev } // lazyInit lazily initializes a zero List Value. func (l *LruList[K, V]) lazyInit() { if l.root.next == nil { l.Init() } } // insert inserts e after at, increments l.len, and returns e. func (l *LruList[K, V]) insert(e, at *Entry[K, V]) *Entry[K, V] { e.prev = at e.next = at.next e.prev.next = e e.next.prev = e e.list = l l.len++ return e } // insertValue is a convenience wrapper for insert(&Entry{Value: v, ExpiresAt: ExpiresAt}, at). func (l *LruList[K, V]) insertValue(k K, v V, expiresAt time.Time, at *Entry[K, V]) *Entry[K, V] { return l.insert(&Entry[K, V]{Value: v, Key: k, ExpiresAt: expiresAt}, at) } // Remove removes e from its list, decrements l.len func (l *LruList[K, V]) Remove(e *Entry[K, V]) V { e.prev.next = e.next e.next.prev = e.prev e.next = nil // avoid memory leaks e.prev = nil // avoid memory leaks e.list = nil l.len-- return e.Value } // move moves e to next to at. func (l *LruList[K, V]) move(e, at *Entry[K, V]) { if e == at { return } e.prev.next = e.next e.next.prev = e.prev e.prev = at e.next = at.next e.prev.next = e e.next.prev = e } // PushFront inserts a new element e with value v at the front of list l and returns e. func (l *LruList[K, V]) PushFront(k K, v V) *Entry[K, V] { l.lazyInit() return l.insertValue(k, v, time.Time{}, &l.root) } // PushFrontExpirable inserts a new expirable element e with Value v at the front of list l and returns e. func (l *LruList[K, V]) PushFrontExpirable(k K, v V, expiresAt time.Time) *Entry[K, V] { l.lazyInit() return l.insertValue(k, v, expiresAt, &l.root) } // MoveToFront moves element e to the front of list l. // If e is not an element of l, the list is not modified. // The element must not be nil. func (l *LruList[K, V]) MoveToFront(e *Entry[K, V]) { if e.list != l || l.root.next == e { return } // see comment in List.Remove about initialization of l l.move(e, &l.root) }