MySQL 9.4.0
Source Code Documentation
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
managed_buffer.h
Go to the documentation of this file.
1/* Copyright (c) 2023, 2025, Oracle and/or its affiliates.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is designed to work with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have either included with
13 the program or referenced in the documentation.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
23
24/// @file
25///
26/// Container class that provides a contiguous memory buffer to
27/// the caller, which the caller can request to grow.
28///
29/// The growth rate is determined by a Grow_calculator.
30
31#ifndef MYSQL_CONTAINERS_BUFFERS_MANAGED_BUFFER_H
32#define MYSQL_CONTAINERS_BUFFERS_MANAGED_BUFFER_H
33
34#include <limits>
35
36#include "mysql/allocators/allocator.h" // Allocator
37#include "mysql/allocators/memory_resource.h" // Memory_resource
38#include "mysql/binlog/event/wrapper_functions.h" // BAPI_TRACE
39#include "mysql/containers/buffers/grow_calculator.h" // Grow_calculator
40#include "mysql/containers/buffers/grow_status.h" // Grow_status
41#include "mysql/containers/buffers/rw_buffer.h" // Rw_buffer
42
43/// @addtogroup GroupLibsMysqlContainers
44/// @{
45
47
48/// Owned, growable, contiguous memory buffer.
49///
50/// This class provides a single contiguous buffer. Therefore, it may
51/// have to move data when it grows. It is implemented as a Buffer
52/// that is resized using realloc.
53///
54/// The caller can provide a user-defined, pre-allocated buffer, which
55/// will then be used as long as it suffices; a new buffer will be
56/// allocated if not. This can be used to remove the need for
57/// allocation in use cases where the object is small.
58///
59/// Objects have a growable total capacity, which is split into two
60/// parts; the read part and the write part, each represented as a
61/// `Buffer_view`. The intended use case is where the user interacts
62/// with an API that produces data into a user-provided buffer. The
63/// user can then: (1) grow the buffer before invoking the API; (2)
64/// invoke the API to write data to the write part; (3) tell the
65/// Managed_buffer to move the written bytes to the read part.
66///
67/// Generally, std::stringstream or std::vector<char> are safer and
68/// simpler interfaces for buffers and should be preferred when
69/// possible. However they do not fit all use cases:
70///
71/// - std::stringstream is preferrable and more convenient when
72/// appending existing data to the stream. But it is not suitable
73/// for interaction with C-like APIs that produce data in a char*
74/// given by the caller. The user would need to allocate a buffer
75/// outside the stringsteam and then append the buffer to the
76/// stringstream, which would imply unnecessary memory and cpu
77/// overheads.
78///
79/// - When using a C-like API that produces data in a char* given by
80/// the caller, std::vector is often good. The user can reserve as
81/// much memory as needed and then pass the underlying data array to
82/// the API. However, the following properties are sometimes
83/// advantageous for Managed_buffer:
84///
85/// - Vector has no practical way to put an exact bound on the
86/// memory usage. Managed_buffer uses a Grow_calculator which
87/// allows exact control on memory usage, including a maximum
88/// capacity.
89///
90/// - Even for small buffer sizes, vector needs at least one heap
91/// allocation. Managed_buffer allows the use of a default buffer
92/// of fixed size, for example allocated on the stack. This can
93/// reduce the need for heap allocation in use patterns where the
94/// required buffer capacity is *usually* small.
95///
96/// The main drawback of Managed_buffer is that it is non-standard and
97/// has a minimal feature set.
98///
99/// The main difference between Buffer_sequence and Managed_buffer, is
100/// that Managed_buffer keeps data in a contiguous buffer, whereas
101/// Buffer_sequence never copies data.
102///
103/// This class never throws any exception.
104///
105/// @tparam Char_tp The char type; usually char or unsigned char.
106template <class Char_tp = unsigned char>
107class Managed_buffer : public Rw_buffer<Char_tp> {
108 public:
109 using Char_t = Char_tp;
112 using typename Rw_buffer_t::Const_iterator_t;
113 using typename Rw_buffer_t::Iterator_t;
114 using typename Rw_buffer_t::Size_t;
115 // As soon as all platforms support it, change to:
116 // using Allocator_t = std::pmr::polymorphic_allocator<Char_t>;
120
121 /// Construct a new object without a default buffer.
122 // Nolint: clang-tidy does not recognize that m_owns_default_buffer
123 // is initialized, despite it is initialized in the targed
124 // constructor.
125 // NOLINTBEGIN(cppcoreguidelines-pro-type-member-init)
127 const Memory_resource_t &memory_resource = Memory_resource_t())
128 : Managed_buffer(Size_t(0), memory_resource) {}
129 // NOLINTEND(cppcoreguidelines-pro-type-member-init)
130
131 /// Construct a new object that owns a default buffer.
132 ///
133 /// The default buffer is created when needed. Once
134 /// created, it survives calls to @c reset and will only be deleted
135 /// when the Managed_buffer is deleted.
136 ///
137 /// @param default_capacity The capacity of the default buffer.
138 ///
139 /// @param memory_resource Memory_resource used to allocate memory.
141 Size_t default_capacity,
142 const Memory_resource_t &memory_resource = Memory_resource_t())
143 : Rw_buffer<Char_t>(),
144 m_char_allocator(memory_resource),
146 m_default_capacity(default_capacity),
147 m_owns_default_buffer(true) {}
148
149 /// Construct a new object that uses the given default buffer.
150 ///
151 /// The default buffer is owned by the caller, so the caller must
152 /// ensure that it outlives the Managed_buffer.
153 ///
154 /// @param default_buffer The default buffer.
155 ///
156 /// @param memory_resource Memory_resource used to allocate memory.
158 Buffer_view_t default_buffer,
159 const Memory_resource_t &memory_resource = Memory_resource_t())
160 : Rw_buffer<Char_t>(default_buffer),
161 m_char_allocator(memory_resource),
162 m_default_buffer(default_buffer.begin()),
163 m_default_capacity(default_buffer.size()),
164 m_owns_default_buffer(false) {}
165
167 Managed_buffer(Managed_buffer &&other) noexcept = default;
169 Managed_buffer &operator=(Managed_buffer &&other) noexcept = default;
170
171 ~Managed_buffer() override {
172 auto *ptr = this->read_part().begin();
173 bool delete_default_buffer =
175 bool delete_buffer = ptr != nullptr && ptr != m_default_buffer;
176 if (delete_default_buffer)
178 if (delete_buffer) m_char_allocator.deallocate(ptr, this->capacity());
179 }
180
181 /// Reserve space so that the total buffer size is at least the
182 /// given number.
183 ///
184 /// The buffer will be resized if necessary. So, on successful
185 /// return, the caller should call begin() to get the new buffer
186 /// pointer.
187 ///
188 /// @note This may move existing data to a new address; consider any
189 /// existing pointers into the buffer as invalid after this call.
190 ///
191 /// @param requested_size The requested total size of the read part
192 /// and the write part.
193 ///
194 /// @retval Grow_status::success The object now has at least the
195 /// given size; either it was successfully re-allocated, or it
196 /// already had the requested size.
197 ///
198 /// @retval Grow_status::exceeds_max_size if requested_size
199 /// exceeds the configured maximum size.
200 ///
201 /// @retval Grow_status::out_of_memory Memory allocation failed.
202 [[nodiscard]] Grow_status reserve_total_size(Size_t requested_size) {
204 auto capacity = this->capacity();
205 auto [error, new_capacity] =
208 if (new_capacity > capacity) {
209 if (new_capacity <= m_default_capacity) {
210 // We have capacity < new_capacity <= m_default_capacity.
211 // Since we never allocate capacity less than the default
212 // capacity, this situation only occurs when the capacity is
213 // 0. And since we make use of the default buffer as soon as
214 // we allocate it, it also means that the default buffer is
215 // nullptr.
216 assert(capacity == 0);
217 assert(m_default_buffer == nullptr);
219 if (m_default_buffer == nullptr) return Grow_status::out_of_memory;
221 } else {
222 // Use dynamic buffer.
223 Char_t *new_buffer = allocate_buffer(new_capacity);
224 if (new_buffer == nullptr) return Grow_status::out_of_memory;
225 replace_buffer(new_buffer, new_capacity);
226 }
227 }
229 }
230
231 /// Reserve space so that the write size is at least the given
232 /// number.
233 ///
234 /// @param requested_write_size The requested size of the write
235 /// part.
236 ///
237 /// @retval Grow_status::success The write part now has at least the
238 /// given size; either it was successfully re-allocated, or it
239 /// already had the requested size.
240 ///
241 /// @retval Grow_status::exceeds_max_size if the existing read size
242 /// plus requested_write_size exceeds the max size configured in the
243 /// Grow_calculator.
244 ///
245 /// @retval Grow_status::out_of_memory Memory allocation failed.
246 [[nodiscard]] Grow_status reserve_write_size(Size_t requested_write_size) {
247 auto read_size = this->read_part().size();
248 if (requested_write_size > std::numeric_limits<Size_t>::max() - read_size)
250 return reserve_total_size(read_size + requested_write_size);
251 }
252
253 /// Reset the buffer.
254 ///
255 /// This makes the read part empty. The write part will point to
256 /// the default buffer if there is one; otherwise the write part
257 /// will be empty.
258 void reset() {
260 auto *rb = this->read_part().begin();
261 if (rb != nullptr && rb != m_default_buffer)
264 if (m_default_buffer == nullptr)
265 this->write_part() = Buffer_view_t();
266 else
268 }
269
270 /// Set the grow calculator.
271 ///
272 /// Details:
273 ///
274 /// - If the new Grow_calculator's maximum size is less than the
275 /// current buffer size, it does not change the existing buffer, but
276 /// subsequent calls to reserve will fail.
277 ///
278 /// - In case the new Grow_calculator's maximum size is less than
279 /// the default capacity, this object will provide capacity equal to
280 /// the default_size, exceeding the Grow_calculator's maximum size.
281 void set_grow_calculator(const Grow_calculator_t &grow_calculator) {
282 m_grow_calculator = grow_calculator;
283 }
284
285 /// Return a const reference to the grow calculator.
287 return m_grow_calculator;
288 }
289
290 /// Return the size of the default buffer.
292
293 private:
294 /// Allocate a new buffer and return it.
295 ///
296 /// This never throws; it returns nullptr on out of memory.
297 ///
298 /// @param new_size The size of the buffer.
299 ///
300 /// @returns the new buffer on success, nullptr on out of memory.
301 [[nodiscard]] Char_t *allocate_buffer(Size_t new_size) {
302 try {
303 return m_char_allocator.allocate(new_size);
304 } catch (std::bad_alloc &) {
305 return nullptr;
306 }
307 }
308
309 /// Replace the underlying data buffer by the given one.
310 ///
311 /// @param new_buffer The new buffer. This must be different from
312 /// the old buffer.
313 ///
314 /// @param new_size The size of the new buffer.
315 void replace_buffer(Char_t *new_buffer, Size_t new_size) {
316 assert(new_buffer != this->read_part().data());
317 auto &r = this->read_part();
318 auto &w = this->write_part();
319 auto read_size = r.size();
320 if (read_size) memcpy(new_buffer, r.begin(), read_size);
321 if (r.begin() != m_default_buffer && r.begin() != nullptr)
322 m_char_allocator.deallocate(r.begin(), this->capacity());
323 r = Buffer_view_t(new_buffer, read_size);
324 w = Buffer_view_t(new_buffer + read_size, new_size - read_size);
325 }
326
327 /// Calculator for growing the buffer.
329
330 /// Allocator to grow the buffer.
332
333 /// User-provided, user-owned buffer.
335
336 /// Size of user-provided, user-owned buffer.
338
339 /// If true, the default buffer will be deallocated by the destructor.
341};
342
344 std::size_t(8 * 1024);
345
346/// @see Managed_buffer
347///
348/// This class pre-allocates a fixed-size initial buffer,
349/// which is beneficial in use patterns where managed buffers are
350/// allocated on the stack and are usually only given small amount of
351/// data.
352template <class Char_t = unsigned char,
353 std::size_t preallocated_size =
356 public:
360
362 const Memory_resource_t &memory_resource = Memory_resource_t())
364 Buffer_view_t(m_preallocated_buffer, preallocated_size),
365 memory_resource) {}
366
367 private:
368 /// Preallocated buffer.
369 Char_t m_preallocated_buffer[preallocated_size == 0 ? 1 : preallocated_size];
370};
371
372} // namespace mysql::containers::buffers
373
374/// @}
375
376#endif // MYSQL_CONTAINERS_BUFFERS_MANAGED_BUFFER_H
Kerberos Client Authentication nullptr
Definition: auth_kerberos_client_plugin.cc:247
constexpr void deallocate(T *p, size_type size)
Use the Memory_resource to deallocate the given pointer.
Definition: allocator.h:95
constexpr T * allocate(size_type n)
Use the Memory_resource to allocate the given number of elements of type T.
Definition: allocator.h:84
Polymorphism-free memory resource class with custom allocator and deallocator functions.
Definition: memory_resource.h:88
Size_t size() const
Return the number of bytes.
Definition: buffer_view.h:112
Iterator_t begin()
Return pointer to the first character of the data.
Definition: buffer_view.h:94
Description of a heuristic to determine how much memory to allocate.
Definition: grow_calculator.h:68
Result_t compute_new_size(Size_t old_size, Size_t requested_size) const
Compute the new size.
Definition: grow_calculator.cpp:39
Owned, growable, contiguous memory buffer.
Definition: managed_buffer.h:107
void reset()
Reset the buffer.
Definition: managed_buffer.h:258
Managed_buffer(Size_t default_capacity, const Memory_resource_t &memory_resource=Memory_resource_t())
Construct a new object that owns a default buffer.
Definition: managed_buffer.h:140
Managed_buffer(const Memory_resource_t &memory_resource=Memory_resource_t())
Construct a new object without a default buffer.
Definition: managed_buffer.h:126
Char_allocator_t m_char_allocator
Allocator to grow the buffer.
Definition: managed_buffer.h:331
Managed_buffer & operator=(Managed_buffer &other)=delete
void set_grow_calculator(const Grow_calculator_t &grow_calculator)
Set the grow calculator.
Definition: managed_buffer.h:281
mysql::allocators::Memory_resource Memory_resource_t
Definition: managed_buffer.h:117
Managed_buffer & operator=(Managed_buffer &&other) noexcept=default
Managed_buffer(Buffer_view_t default_buffer, const Memory_resource_t &memory_resource=Memory_resource_t())
Construct a new object that uses the given default buffer.
Definition: managed_buffer.h:157
Grow_status reserve_total_size(Size_t requested_size)
Reserve space so that the total buffer size is at least the given number.
Definition: managed_buffer.h:202
Grow_status reserve_write_size(Size_t requested_write_size)
Reserve space so that the write size is at least the given number.
Definition: managed_buffer.h:246
Char_t * m_default_buffer
User-provided, user-owned buffer.
Definition: managed_buffer.h:334
void replace_buffer(Char_t *new_buffer, Size_t new_size)
Replace the underlying data buffer by the given one.
Definition: managed_buffer.h:315
Char_t * allocate_buffer(Size_t new_size)
Allocate a new buffer and return it.
Definition: managed_buffer.h:301
Buffer_view< Char_t > Buffer_view_t
Definition: managed_buffer.h:110
Char_tp Char_t
Definition: managed_buffer.h:109
Managed_buffer(Managed_buffer &other)=delete
bool m_owns_default_buffer
If true, the default buffer will be deallocated by the destructor.
Definition: managed_buffer.h:340
Managed_buffer(Managed_buffer &&other) noexcept=default
std::size_t Size_t
Definition: rw_buffer.h:63
Size_t m_default_capacity
Size of user-provided, user-owned buffer.
Definition: managed_buffer.h:337
const Grow_calculator_t & get_grow_calculator() const
Return a const reference to the grow calculator.
Definition: managed_buffer.h:286
Size_t get_default_capacity()
Return the size of the default buffer.
Definition: managed_buffer.h:291
Grow_calculator_t m_grow_calculator
Calculator for growing the buffer.
Definition: managed_buffer.h:328
~Managed_buffer() override
Definition: managed_buffer.h:171
Char_t m_preallocated_buffer[preallocated_size==0 ? 1 :preallocated_size]
Preallocated buffer.
Definition: managed_buffer.h:369
Preallocated_managed_buffer(const Memory_resource_t &memory_resource=Memory_resource_t())
Definition: managed_buffer.h:361
Non-owning read/write memory buffer manager with a fixed size.
Definition: rw_buffer.h:60
Size_t capacity() const
Return the total size of the read part and the write part.
Definition: rw_buffer.h:107
const Buffer_view_t & read_part() const
Return the read part.
Definition: rw_buffer.h:95
Char_t * Iterator_t
Definition: rw_buffer.h:65
const Buffer_view_t & write_part() const
Return the write part.
Definition: rw_buffer.h:101
const Char_t * Const_iterator_t
Definition: rw_buffer.h:66
std::size_t Size_t
Definition: rw_buffer.h:63
Allocator class that uses a polymorphic Memory_resource to allocate memory.
Class that wraps resources in a polymorphic manner.
void error(const char *format,...)
ValueType max(X &&first)
Definition: gtid.h:103
Definition: buffer_sequence_view.h:50
constexpr std::size_t default_preallocated_managed_buffer_size
Definition: managed_buffer.h:343
Grow_status
Error statuses for classes that use Grow_calculator.
Definition: grow_status.h:38
@ exceeds_max_size
A grow operation could not be performed because there is a configured maximum size.
@ success
A grow operation succeeded.
@ out_of_memory
A grow operation failed because memory allocation failed.
const char * begin(const char *const c)
Definition: base64.h:44
size_t size(const char *const c)
Definition: base64.h:46
const mysql_service_registry_t * r
Definition: pfs_example_plugin_employee.cc:86
Non-owning manager for a fixed memory buffer, which is split into a read part and a write part,...
Contains wrapper functions for memory allocation and deallocation.
#define BAPI_TRACE
Definition: wrapper_functions.h:65