MySQL 26.10.0
Source Code Documentation
ut0prefix_cache.h
Go to the documentation of this file.
1/* Copyright (c) 2026, Oracle and/or its affiliates.
2
3This program is free software; you can redistribute it and/or modify it under
4the terms of the GNU General Public License, version 2.0, as published by the
5Free Software Foundation.
6
7This program is designed to work with certain software (including
8but not limited to OpenSSL) that is licensed under separate terms,
9as designated in a particular file or component or in included license
10documentation. The authors of MySQL hereby grant you an additional
11permission to link the program and your derivative works with the
12separately licensed software that they have either included with
13the program or referenced in the documentation.
14
15This program is distributed in the hope that it will be useful, but WITHOUT
16ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
18for more details.
19
20You should have received a copy of the GNU General Public License along with
21this program; if not, write to the Free Software Foundation, Inc.,
2251 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23*/
24#pragma once
25
26#include <functional>
27#include <list>
28#include <map>
29#include <string>
30#include <string_view>
31#include <utility>
32
33#include "univ.i"
34
35namespace ut {
36
37/** Bounded cache for prefix lookups with MRU/LRU recency tracking.
38
39The main use case is in parsers, which consume a prefix of a buffer, to produce
40a value, where the length of the prefix is not know up-front without parsing.
41This solves the case in which callers need to map owned prefixes to values,
42probe whether a cached prefix matches the beginning of a buffer, and evict old
43entries when the cache reaches a fixed size. Standard containers alone are not
44enough, because they do not combine prefix lookup, stable owned keys, and LRU
45eviction in one reusable abstraction.
46
47Important limitation of this class (met by the parser usecase) is that it can
48work only with prefix-free languages, i.e. that is it doesn't support adding two
49prefixes such that one is a proper prefix of another. */
50template <typename CharType, typename Value>
52 public:
53 using sequence = std::basic_string<CharType>;
54 using sequence_view = std::basic_string_view<CharType>;
55
56 /** Create a cache with at most @p capacity stored prefixes.
57
58 @param[in] capacity The maximum number of entries kept after each insertion */
59 explicit prefix_cache(size_t capacity) : m_capacity(capacity) {}
60
61 prefix_cache(const prefix_cache &) = delete;
63
64 /** Find a cached value whose key is the longest prefix of @p buffer.
65
66 If a match is found, the entry becomes most recently used and the buffer
67 is trimmed to the leftover suffix.
68
69 @param[in,out] buffer
70 The input buffer. Trimmed to the leftover suffix on match.
71 Otherwise kept intact.
72 @return The pointer to cached value or nullptr if no match. It is only safe
73 to dereference it until next call to insert(..). */
75 const It it = try_find(buffer);
76 if (it == m_map.end()) {
77 return nullptr;
78 }
79
80 const size_t matched_len = it->first.size();
81 move_to_front(it);
82 buffer.remove_prefix(matched_len);
83
84 return &it->second.value;
85 }
86
87 /** Insert a new prefix/value pair as the most recently used entry.
88
89 If @p prefix already exists, the cache is left unchanged. After a successful
90 insertion, least recently used entries are evicted until the cache size is
91 within capacity.
92
93 The caller must ensure that the language is prefix-free, i.e. that the prefix
94 is not a proper prefix of any previously inserted one, nor vice-versa.
95
96 Invalidates pointers returned by find_value_for_prefix_of(..), as they might
97 become evicted as a result of this call.
98
99 @param[in] prefix key prefix.
100 @param[in] value value to store.
101 @return true if inserted, false if key already existed */
102 bool insert(sequence_view prefix, Value value) {
103 if (m_map.contains(prefix)) {
104 return false;
105 }
106
107 auto [it, inserted] =
108 m_map.emplace(sequence(prefix), Entry{std::move(value), m_lru.end()});
109 ut_a(inserted);
110#ifdef UNIV_DEBUG
111 /* This class only works for prefix-free languages. If that assumption was
112 violated, i.e. if one key was a proper prefix of another, then there would
113 also exist such a pair where they are next to each other, and it had to form
114 after one of the insert operations. So, we check here both neighbours, to
115 validate this contract is not violated. */
116 if (it != m_map.begin()) {
117 auto prev = it;
118 --prev;
119 ut_ad(!is_prefix(prefix, prev->first));
120 }
121 auto next = it;
122 ++next;
123 if (next != m_map.end()) {
124 ut_ad(!is_prefix(next->first, prefix));
125 }
126#endif /* UNIV_DEBUG */
127
128 link_new_mru(it);
130
131 return true;
132 }
133
134 private:
135 struct Entry;
136 /* We use greater so that if A is a prefix of B, then
137 B is before A in the ordering, and hence lower_bound(B) can find A.
138 This also means it finds the longest prefix. */
139 using Map = std::map<sequence, Entry, std::greater<>>;
140 using It = typename Map::iterator;
141 using Lru_list = std::list<It>;
142 using Lru_it = typename Lru_list::iterator;
143
144 struct Entry {
145 Value value;
147 };
148
150 return buffer.size() >= key.size() &&
152 key.size()) == 0;
153 }
154
156 const It it = m_map.lower_bound(buffer);
157 if (it != m_map.end() && is_prefix(it->first, buffer)) {
158 return it;
159 }
160 return m_map.end();
161 }
162
163 void move_to_front(It it) {
164 ut_ad(it != m_map.end());
165 m_lru.splice(m_lru.begin(), m_lru, it->second.lru_pos);
166 it->second.lru_pos = m_lru.begin();
167 }
168
169 void link_new_mru(It it) {
170 m_lru.push_front(it);
171 it->second.lru_pos = m_lru.begin();
172 }
173
175 while (m_map.size() > m_capacity) {
176 ut_ad(!m_lru.empty());
177 It victim = m_lru.back();
178 m_lru.pop_back();
179 m_map.erase(victim);
180 }
181 }
182
183 private:
187};
188
189} // namespace ut
Definition: mock_resolver.h:43
void * data() const noexcept
Definition: buffer.h:119
size_t size() const noexcept
Definition: buffer.h:120
Bounded cache for prefix lookups with MRU/LRU recency tracking.
Definition: ut0prefix_cache.h:51
bool insert(sequence_view prefix, Value value)
Insert a new prefix/value pair as the most recently used entry.
Definition: ut0prefix_cache.h:102
std::list< It > Lru_list
Definition: ut0prefix_cache.h:141
std::basic_string< CharType > sequence
Definition: ut0prefix_cache.h:53
prefix_cache(const prefix_cache &)=delete
prefix_cache & operator=(const prefix_cache &)=delete
Value * find_value_for_prefix_of(sequence_view &buffer)
Find a cached value whose key is the longest prefix of buffer.
Definition: ut0prefix_cache.h:74
std::map< sequence, Entry, std::greater<> > Map
Definition: ut0prefix_cache.h:139
It try_find(sequence_view buffer)
Definition: ut0prefix_cache.h:155
static bool is_prefix(sequence_view key, sequence_view buffer)
Definition: ut0prefix_cache.h:149
typename Lru_list::iterator Lru_it
Definition: ut0prefix_cache.h:142
Lru_list m_lru
Definition: ut0prefix_cache.h:186
void move_to_front(It it)
Definition: ut0prefix_cache.h:163
size_t m_capacity
Definition: ut0prefix_cache.h:184
Map m_map
Definition: ut0prefix_cache.h:185
void link_new_mru(It it)
Definition: ut0prefix_cache.h:169
std::basic_string_view< CharType > sequence_view
Definition: ut0prefix_cache.h:54
prefix_cache(size_t capacity)
Create a cache with at most capacity stored prefixes.
Definition: ut0prefix_cache.h:59
typename Map::iterator It
Definition: ut0prefix_cache.h:140
void enforce_capacity()
Definition: ut0prefix_cache.h:174
ValueType value(const std::optional< ValueType > &v)
Definition: gtid.h:83
mutable_buffer buffer(void *p, size_t n) noexcept
Definition: buffer.h:418
This file contains a set of libraries providing overloads for regular dynamic allocation routines whi...
Definition: aligned_alloc.h:48
required string key
Definition: replication_asynchronous_connection_failover.proto:60
static int compare(size_t a, size_t b)
Function to compare two size_t integers for their relative order.
Definition: rpl_utility.cc:114
Definition: ut0prefix_cache.h:144
Value value
Definition: ut0prefix_cache.h:145
Lru_it lru_pos
Definition: ut0prefix_cache.h:146
Version control for database, common definitions, and include files.
#define ut_ad(EXPR)
Debug assertion.
Definition: ut0dbg.h:109
#define ut_a(EXPR)
Abort execution if EXPR does not evaluate to nonzero.
Definition: ut0dbg.h:97