SatCat5
lru_cache.h
1 // Copyright 2024-2025 The Aerospace Corporation.
3 // This file is a part of SatCat5, licensed under CERN-OHL-W v2 or later.
5 // Template class implementing a least-recently-used (LRU) cache
6 
7 #pragma once
8 
9 namespace satcat5 {
10  namespace util {
33  template <class T> class LruCache {
34  public:
36  LruCache(T* array, unsigned count) : m_free(array), m_list(0) {
37  // Set pointers to create a linked list of free elements.
38  // Everything else is don't-care.
39  for (unsigned a = 0 ; a < count-1 ; ++a) {
40  array[a].m_next = array + a + 1;
41  }
42  array[count-1].m_next = 0;
43  }
44 
46  void clear() {
47  while (m_list) {
48  T* item = m_list;
49  m_list = item->m_next;
50  item->m_next = m_free;
51  m_free = item;
52  }
53  }
54 
56  inline bool is_empty() const {
57  return !m_list;
58  }
59 
61  unsigned len() const {
62  unsigned count = 0;
63  const T* item = m_list;
64  while (item) {
65  ++count;
66  item = item->m_next;
67  }
68  return count;
69  }
70 
73  T* find(const decltype(T::m_key)& key) {
74  T* ptr = m_list;
75  while (ptr && !(ptr->m_key == key)) {
76  ptr = ptr->m_next;
77  }
78  return ptr;
79  }
80 
84  T* query(const decltype(T::m_key)& key) {
85  // Handling for special cases.
86  if (!m_list) {
87  // Push first item onto an empty list.
88  m_free->m_key = key;
89  return update(0, m_free);
90  } else if (m_list->m_key == key) {
91  // Match on first item is an LRU no-op.
92  return m_list;
93  }
94  // Iterate over the list, from second item to the tail...
95  T** ptr = &m_list->m_next;
96  while (*ptr) {
97  if ((*ptr)->m_key == key) return update(ptr, *ptr);
98  if ((*ptr)->m_next) ptr = &((*ptr)->m_next);
99  else break;
100  }
101  // Reached end of list without finding a match.
102  if (m_free) {
103  // Create a new entry.
104  m_free->m_key = key;
105  return update(0, m_free);
106  } else {
107  // Otherwise, evict by overwriting the tail.
108  (*ptr)->m_key = key;
109  return update(ptr, *ptr);
110  }
111  }
112 
113  private:
114  // Found a match? Given a pointer to the previous element,
115  // reinsert the matching element at the head of the list.
116  T* update(T** prev, T* item) {
117  if (item == m_free) m_free = m_free->m_next;
118  if (prev) *prev = (*prev)->m_next;
119  item->m_next = m_list;
120  m_list = item;
121  return item;
122  }
123 
124  T* m_free; // Linked-list of unused slots.
125  T* m_list; // Linked-list in most-recently-used order.
126  };
127  }
128 };
Template class implementing a least-recently-used (LRU) cache.
Definition: lru_cache.h:33
LruCache(T *array, unsigned count)
Given a backing array, initialize an empty cache.
Definition: lru_cache.h:36
unsigned len() const
Count the number of stored items.
Definition: lru_cache.h:61
T * find(const decltype(T::m_key)&key)
Query the cache without modifying its contents.
Definition: lru_cache.h:73
bool is_empty() const
Is this an empty list?
Definition: lru_cache.h:56
void clear()
Reset this cache to the empty state.
Definition: lru_cache.h:46
T * query(const decltype(T::m_key)&key)
Query the cache, updating the recently-used list.
Definition: lru_cache.h:84