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