MySQL 9.1.0
Source Code Documentation
managed_buffer.h
Go to the documentation of this file.
1/* Copyright (c) 2023, 2024, 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#include "mysql/utils/nodiscard.h" // NODISCARD
43
44/// @addtogroup GroupLibsMysqlContainers
45/// @{
46
48
49/// Owned, growable, contiguous memory buffer.
50///
51/// This class provides a single contiguous buffer. Therefore, it may
52/// have to move data when it grows. It is implemented as a Buffer
53/// that is resized using realloc.
54///
55/// The caller can provide a user-defined, pre-allocated buffer, which
56/// will then be used as long as it suffices; a new buffer will be
57/// allocated if not. This can be used to remove the need for
58/// allocation in use cases where the object is small.
59///
60/// Objects have a growable total capacity, which is split into two
61/// parts; the read part and the write part, each represented as a
62/// `Buffer_view`. The intended use case is where the user interacts
63/// with an API that produces data into a user-provided buffer. The
64/// user can then: (1) grow the buffer before invoking the API; (2)
65/// invoke the API to write data to the write part; (3) tell the
66/// Managed_buffer to move the written bytes to the read part.
67///
68/// Generally, std::stringstream or std::vector<char> are safer and
69/// simpler interfaces for buffers and should be preferred when
70/// possible. However they do not fit all use cases:
71///
72/// - std::stringstream is preferrable and more convenient when
73/// appending existing data to the stream. But it is not suitable
74/// for interaction with C-like APIs that produce data in a char*
75/// given by the caller. The user would need to allocate a buffer
76/// outside the stringsteam and then append the buffer to the
77/// stringstream, which would imply unnecessary memory and cpu
78/// overheads.
79///
80/// - When using a C-like API that produces data in a char* given by
81/// the caller, std::vector is often good. The user can reserve as
82/// much memory as needed and then pass the underlying data array to
83/// the API. However, the following properties are sometimes
84/// advantageous for Managed_buffer:
85///
86/// - Vector has no practical way to put an exact bound on the
87/// memory usage. Managed_buffer uses a Grow_calculator which
88/// allows exact control on memory usage, including a maximum
89/// capacity.
90///
91/// - Even for small buffer sizes, vector needs at least one heap
92/// allocation. Managed_buffer allows the use of a default buffer
93/// of fixed size, for example allocated on the stack. This can
94/// reduce the need for heap allocation in use patterns where the
95/// required buffer capacity is *usually* small.
96///
97/// The main drawback of Managed_buffer is that it is non-standard and
98/// has a minimal feature set.
99///
100/// The main difference between Buffer_sequence and Managed_buffer, is
101/// that Managed_buffer keeps data in a contiguous buffer, whereas
102/// Buffer_sequence never copies data.
103///
104/// This class never throws any exception.
105///
106/// @tparam Char_tp The char type; usually char or unsigned char.
107template <class Char_tp = unsigned char>
108class Managed_buffer : public Rw_buffer<Char_tp> {
109 public:
110 using Char_t = Char_tp;
113 using typename Rw_buffer_t::Const_iterator_t;
114 using typename Rw_buffer_t::Iterator_t;
115 using typename Rw_buffer_t::Size_t;
116 // As soon as all platforms support it, change to:
117 // using Allocator_t = std::pmr::polymorphic_allocator<Char_t>;
121
122 /// Construct a new object without a default buffer.
123 // Nolint: clang-tidy does not recognize that m_owns_default_buffer
124 // is initialized, despite it is initialized in the targed
125 // constructor.
126 // NOLINTBEGIN(cppcoreguidelines-pro-type-member-init)
128 const Memory_resource_t &memory_resource = Memory_resource_t())
129 : Managed_buffer(Size_t(0), memory_resource) {}
130 // NOLINTEND(cppcoreguidelines-pro-type-member-init)
131
132 /// Construct a new object that owns a default buffer.
133 ///
134 /// The default buffer is created when needed. Once
135 /// created, it survives calls to @c reset and will only be deleted
136 /// when the Managed_buffer is deleted.
137 ///
138 /// @param default_capacity The capacity of the default buffer.
139 ///
140 /// @param memory_resource Memory_resource used to allocate memory.
142 Size_t default_capacity,
143 const Memory_resource_t &memory_resource = Memory_resource_t())
144 : Rw_buffer<Char_t>(),
145 m_char_allocator(memory_resource),
147 m_default_capacity(default_capacity),
148 m_owns_default_buffer(true) {}
149
150 /// Construct a new object that uses the given default buffer.
151 ///
152 /// The default buffer is owned by the caller, so the caller must
153 /// ensure that it outlives the Managed_buffer.
154 ///
155 /// @param default_buffer The default buffer.
156 ///
157 /// @param memory_resource Memory_resource used to allocate memory.
159 Buffer_view_t default_buffer,
160 const Memory_resource_t &memory_resource = Memory_resource_t())
161 : Rw_buffer<Char_t>(default_buffer),
162 m_char_allocator(memory_resource),
163 m_default_buffer(default_buffer.begin()),
164 m_default_capacity(default_buffer.size()),
165 m_owns_default_buffer(false) {}
166
168 Managed_buffer(Managed_buffer &&other) noexcept = default;
170 Managed_buffer &operator=(Managed_buffer &&other) noexcept = default;
171
172 ~Managed_buffer() override {
173 auto *ptr = this->read_part().begin();
174 bool delete_default_buffer =
176 bool delete_buffer = ptr != nullptr && ptr != m_default_buffer;
177 if (delete_default_buffer)
179 if (delete_buffer) m_char_allocator.deallocate(ptr, this->capacity());
180 }
181
182 /// Reserve space so that the total buffer size is at least the
183 /// given number.
184 ///
185 /// The buffer will be resized if necessary. So, on successful
186 /// return, the caller should call begin() to get the new buffer
187 /// pointer.
188 ///
189 /// @note This may move existing data to a new address; consider any
190 /// existing pointers into the buffer as invalid after this call.
191 ///
192 /// @param requested_size The requested total size of the read part
193 /// and the write part.
194 ///
195 /// @retval Grow_status::success The object now has at least the
196 /// given size; either it was successfully re-allocated, or it
197 /// already had the requested size.
198 ///
199 /// @retval Grow_status::exceeds_max_size if requested_size
200 /// exceeds the configured maximum size.
201 ///
202 /// @retval Grow_status::out_of_memory Memory allocation failed.
205 auto capacity = this->capacity();
206 auto [error, new_capacity] =
209 if (new_capacity > capacity) {
210 if (new_capacity <= m_default_capacity) {
211 // We have capacity < new_capacity <= m_default_capacity.
212 // Since we never allocate capacity less than the default
213 // capacity, this situation only occurs when the capacity is
214 // 0. And since we make use of the default buffer as soon as
215 // we allocate it, it also means that the default buffer is
216 // nullptr.
217 assert(capacity == 0);
218 assert(m_default_buffer == nullptr);
220 if (m_default_buffer == nullptr) return Grow_status::out_of_memory;
222 } else {
223 // Use dynamic buffer.
224 Char_t *new_buffer = allocate_buffer(new_capacity);
225 if (new_buffer == nullptr) return Grow_status::out_of_memory;
226 replace_buffer(new_buffer, new_capacity);
227 }
228 }
230 }
231
232 /// Reserve space so that the write size is at least the given
233 /// number.
234 ///
235 /// @param requested_write_size The requested size of the write
236 /// part.
237 ///
238 /// @retval Grow_status::success The write part now has at least the
239 /// given size; either it was successfully re-allocated, or it
240 /// already had the requested size.
241 ///
242 /// @retval Grow_status::exceeds_max_size if the existing read size
243 /// plus requested_write_size exceeds the max size configured in the
244 /// Grow_calculator.
245 ///
246 /// @retval Grow_status::out_of_memory Memory allocation failed.
247 [[NODISCARD]] Grow_status reserve_write_size(Size_t requested_write_size) {
248 auto read_size = this->read_part().size();
249 if (requested_write_size > std::numeric_limits<Size_t>::max() - read_size)
251 return reserve_total_size(read_size + requested_write_size);
252 }
253
254 /// Reset the buffer.
255 ///
256 /// This makes the read part empty. The write part will point to
257 /// the default buffer if there is one; otherwise the write part
258 /// will be empty.
259 void reset() {
261 auto *rb = this->read_part().begin();
262 if (rb != nullptr && rb != m_default_buffer)
265 if (m_default_buffer == nullptr)
266 this->write_part() = Buffer_view_t();
267 else
269 }
270
271 /// Set the grow calculator.
272 ///
273 /// Details:
274 ///
275 /// - If the new Grow_calculator's maximum size is less than the
276 /// current buffer size, it does not change the existing buffer, but
277 /// subsequent calls to reserve will fail.
278 ///
279 /// - In case the new Grow_calculator's maximum size is less than
280 /// the default capacity, this object will provide capacity equal to
281 /// the default_size, exceeding the Grow_calculator's maximum size.
282 void set_grow_calculator(const Grow_calculator_t &grow_calculator) {
283 m_grow_calculator = grow_calculator;
284 }
285
286 /// Return a const reference to the grow calculator.
288 return m_grow_calculator;
289 }
290
291 /// Return the size of the default buffer.
293
294 private:
295 /// Allocate a new buffer and return it.
296 ///
297 /// This never throws; it returns nullptr on out of memory.
298 ///
299 /// @param new_size The size of the buffer.
300 ///
301 /// @returns the new buffer on success, nullptr on out of memory.
303 try {
304 return m_char_allocator.allocate(new_size);
305 } catch (std::bad_alloc &) {
306 return nullptr;
307 }
308 }
309
310 /// Replace the underlying data buffer by the given one.
311 ///
312 /// @param new_buffer The new buffer. This must be different from
313 /// the old buffer.
314 ///
315 /// @param new_size The size of the new buffer.
316 void replace_buffer(Char_t *new_buffer, Size_t new_size) {
317 assert(new_buffer != this->read_part().data());
318 auto &r = this->read_part();
319 auto &w = this->write_part();
320 auto read_size = r.size();
321 if (read_size) memcpy(new_buffer, r.begin(), read_size);
322 if (r.begin() != m_default_buffer && r.begin() != nullptr)
323 m_char_allocator.deallocate(r.begin(), this->capacity());
324 r = Buffer_view_t(new_buffer, read_size);
325 w = Buffer_view_t(new_buffer + read_size, new_size - read_size);
326 }
327
328 /// Calculator for growing the buffer.
330
331 /// Allocator to grow the buffer.
333
334 /// User-provided, user-owned buffer.
336
337 /// Size of user-provided, user-owned buffer.
339
340 /// If true, the default buffer will be deallocated by the destructor.
342};
343
345 std::size_t(8 * 1024);
346
347/// @see Managed_buffer
348///
349/// This class pre-allocates a fixed-size initial buffer,
350/// which is beneficial in use patterns where managed buffers are
351/// allocated on the stack and are usually only given small amount of
352/// data.
353template <class Char_t = unsigned char,
354 std::size_t preallocated_size =
357 public:
361
363 const Memory_resource_t &memory_resource = Memory_resource_t())
365 Buffer_view_t(m_preallocated_buffer, preallocated_size),
366 memory_resource) {}
367
368 private:
369 /// Preallocated buffer.
370 Char_t m_preallocated_buffer[preallocated_size == 0 ? 1 : preallocated_size];
371};
372
373} // namespace mysql::containers::buffers
374
375/// @}
376
377#endif // MYSQL_CONTAINERS_BUFFERS_MANAGED_BUFFER_H
Kerberos Client Authentication nullptr
Definition: auth_kerberos_client_plugin.cc:251
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:108
void reset()
Reset the buffer.
Definition: managed_buffer.h:259
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:141
Managed_buffer(const Memory_resource_t &memory_resource=Memory_resource_t())
Construct a new object without a default buffer.
Definition: managed_buffer.h:127
Char_allocator_t m_char_allocator
Allocator to grow the buffer.
Definition: managed_buffer.h:332
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:282
mysql::allocators::Memory_resource Memory_resource_t
Definition: managed_buffer.h:118
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:158
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:203
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:247
Char_t * m_default_buffer
User-provided, user-owned buffer.
Definition: managed_buffer.h:335
void replace_buffer(Char_t *new_buffer, Size_t new_size)
Replace the underlying data buffer by the given one.
Definition: managed_buffer.h:316
Char_t * allocate_buffer(Size_t new_size)
Allocate a new buffer and return it.
Definition: managed_buffer.h:302
Buffer_view< Char_t > Buffer_view_t
Definition: managed_buffer.h:111
Char_tp Char_t
Definition: managed_buffer.h:110
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:341
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:338
const Grow_calculator_t & get_grow_calculator() const
Return a const reference to the grow calculator.
Definition: managed_buffer.h:287
Size_t get_default_capacity()
Return the size of the default buffer.
Definition: managed_buffer.h:292
Grow_calculator_t m_grow_calculator
Calculator for growing the buffer.
Definition: managed_buffer.h:329
~Managed_buffer() override
Definition: managed_buffer.h:172
Char_t m_preallocated_buffer[preallocated_size==0 ? 1 :preallocated_size]
Preallocated buffer.
Definition: managed_buffer.h:370
Preallocated_managed_buffer(const Memory_resource_t &memory_resource=Memory_resource_t())
Definition: managed_buffer.h:362
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,...)
Definition: buffer_sequence_view.h:51
constexpr std::size_t default_preallocated_managed_buffer_size
Definition: managed_buffer.h:344
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
#define NODISCARD
The function attribute [[NODISCARD]] is a replacement for [[nodiscard]] to workaround a gcc bug.
Definition: nodiscard.h:47
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