MySQL 8.4.0
Source Code Documentation
rw_buffer_sequence.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 rw_buffer_sequence.h
25///
26/// Container class that provides a sequence of buffers to the
27/// caller. This is intended for capturing the output from
28/// compressors.
29
30#ifndef MYSQL_BINLOG_EVENT_COMPRESSION_BUFFER_RW_BUFFER_SEQUENCE_H
31#define MYSQL_BINLOG_EVENT_COMPRESSION_BUFFER_RW_BUFFER_SEQUENCE_H
32
33#include <algorithm> // std::min
34#include <cassert> // assert
35#include <cstring> // std::memcpy
36#include <limits> // std::numeric_limits
37// Use when all platforms support it:
38// #include <memory_resource> // std::pmr::polymorphic_allocator
39#include <list> // std::list
40#include <vector> // std::vector
42
43#include "mysql/binlog/event/compression/buffer/buffer_sequence_view.h" // buffer::Buffer_sequence_view
44#include "mysql/binlog/event/compression/buffer/buffer_view.h" // buffer::Buffer_view
45#include "mysql/binlog/event/compression/buffer/grow_calculator.h" // buffer::Grow_calculator
46#include "mysql/binlog/event/compression/buffer/grow_status.h" // buffer::Grow_status
47#include "mysql/binlog/event/nodiscard.h" // NODISCARD
48
49#include "mysql/binlog/event/wrapper_functions.h" // BAPI_TRACE
50
51/// @addtogroup GroupLibsMysqlBinlogEvent
52/// @{
53
55
56/// Non-owning manager for a fixed sequence of memory buffers, which
57/// is split into a read part and a write part, with a movable split
58/// position.
59///
60/// This has a read/write position (which @c Buffer_sequence does not
61/// have). It does not have functionailty to grow the buffer sequence
62/// (as @c Managed_buffer_sequence has).
63///
64/// Objects have one internal contiguous container (vector or list),
65/// which is split into two parts, each of which is a
66/// Buffer_sequence_view: the first part is the read part and the
67/// second part is the write part. API clients typically write to the
68/// write part and then move the position forwards: this will increase
69/// the read part so that API clients can read what was just written,
70/// and decrease the write part so that next write will happen after
71/// the position that was just written.
72///
73/// The position that defines the end of the read part and the
74/// beginning of the write part has byte granularity. Therefore, it
75/// does not have to be at a buffer boundary. When it is not on a
76/// buffer boundary, a buffer has to be split. When a buffer is
77/// split, the sequence needs one more element than the actual number
78/// of (contiguous) buffers. When the position is moved from a buffer
79/// boundary to a non-boundary, the number of used elements increases
80/// by one. To avoid having to shift all the buffers for the write
81/// part one step right when the container is a vector, there is
82/// *always* one more element than the actual number of contiguous
83/// buffers. So when the position is at a buffer boundary, an unused
84/// "null buffer" is stored between the read part and the write part.
85/// In other words, the buffer sequence has one of the following
86/// forms:
87///
88/// ```
89/// Position at buffer boundary:
90/// Here, there are N buffers where the first R ones are read buffers.
91/// [b_1, ..., b_R, null, b_{R+1}, ..., b_N]
92///
93/// Position not at buffer boundary:
94/// Here there are N buffers where the first R-1 ones are read buffers,
95/// and the R'th one is split into a read part and a write part.
96/// [b_1, ..., b_R[0..x], b_R[x..], b_{R+1}, ..., b_N]
97///```
98///
99/// @tparam Char_tp the type of elements stored in the buffer:
100/// typically unsigned char.
101///
102/// @tparam Container_tp the type of container: either `std::vector`
103/// or `std::list`.
104template <class Char_tp = unsigned char,
105 template <class Element_tp, class Allocator_tp> class Container_tp =
108 public:
112 using Difference_t = std::ptrdiff_t;
121
122 /// Construct a new Rw_buffer_sequence from given endpoint
123 /// iterators, with position 0.
124 ///
125 /// The provided sequence of buffers must start with one null buffer,
126 /// followed by zero or more non-null buffers.
127 ///
128 /// @param begin_arg Iterator to the beginning of the sequence.
129 ///
130 /// @param end_arg Iterator to one-past-the-end of the sequence.
132 : m_read_part(begin_arg, begin_arg),
133 m_write_part(std::next(begin_arg), end_arg) {
134#ifndef NDEBUG
135 assert(begin_arg != end_arg);
136 assert(begin_arg->data() == nullptr);
137 assert(begin_arg->size() == 0);
138 for (auto it = std::next(begin_arg); it != end_arg; ++it) {
139 assert(it->data() != nullptr);
140 assert(it->size() != 0);
141 }
142#endif
143 }
144
145 // Disallow copy/move. We can implement these in the future if we
146 // need them.
151
152 virtual ~Rw_buffer_sequence() = default;
153
154 /// Set the specified absolute position.
155 ///
156 /// "Position" is a synonym for "size of the read part".
157 ///
158 /// @note This may alter the end iterator of the read part, the
159 /// begin iterator of the write part, as well as the begin/end
160 /// iterators of buffers between the current position and the new
161 /// position. The beginning of the read part, the end of the write
162 /// part, and buffers outside the range between the new and old
163 /// position remain unchanged.
164 ///
165 /// @param new_position The new size of the read part. This must be
166 /// within bounds; otherwise an assertion is raised in debug build,
167 /// or the position forced to the end in non-debug build.
168 void set_position(Size_t new_position) {
169 set_position(new_position, this->read_part(), this->write_part());
170 }
171
172 /// Move the position right, relative to the current position.
173 ///
174 /// "Position" is a synonym for "size of the read part".
175 ///
176 /// @note This may alter the end iterator of the read part, the
177 /// begin iterator of the write part, as well as the begin/end
178 /// iterators of buffers between the current position and the new
179 /// position. The beginning of the read part, the end of the write
180 /// part, and buffers outside the range between the new and old
181 /// position remain unchanged.
182 ///
183 /// @param delta The number of bytes to add to the position. The
184 /// resulting size must be within bounds; otherwise an assertion is
185 /// raised in debug build, or the position forced to the
186 /// beginning/end in non-debug build.
188 set_position(read_part().size() + delta);
189 }
190
191 /// Move the position left or right, relative to the current
192 /// position.
193 ///
194 /// "Position" is a synonym for "size of the read part".
195 ///
196 /// @note This may alter the end iterator of the read part, the
197 /// begin iterator of the write part, as well as the begin/end
198 /// iterators of buffers between the current position and the new
199 /// position. The beginning of the read part, the end of the write
200 /// part, and buffers outside the range between the new and old
201 /// position remain unchanged.
202 ///
203 /// @param delta The number of bytes to add to the position. The
204 /// resulting size must be within bounds; otherwise an assertion is
205 /// raised in debug build, or the position forced to the
206 /// beginning/end in non-debug build.
209 Difference_t new_position = Difference_t(read_part().size()) + delta;
210 assert(new_position >= Difference_t(0));
211 new_position = std::max(new_position, Difference_t(0));
212 set_position(Size_t(new_position));
213 }
214
215 /// Return the current size, i.e., total size of all buffers.
216 Size_t capacity() const { return read_part().size() + write_part().size(); }
217
218 /// Return a const reference to the read part.
220
221 /// Return a non-const reference to the read part.
223
224 /// Return a const reference to the write part.
226
227 /// Return a non-const reference to the write part.
229
230 /// In debug mode, return a string that describes the internal
231 /// structure of this object, to use for debugging.
232 ///
233 /// @param show_contents If true, includes the buffer contents.
234 /// Otherwise, just pointers and sizes.
235 ///
236 /// @param indent If 0, put all info on one line. Otherwise, put
237 /// each field on its own line and indent the given number of
238 /// two-space levels.
239 virtual std::string debug_string([[maybe_unused]] bool show_contents,
240 [[maybe_unused]] int indent) const {
241#ifdef NDEBUG
242 return "";
243#else
245 std::string sep;
246 if (indent != 0)
247 sep = std::string(",\n") +
248 std::string(static_cast<std::string::size_type>(indent * 2), ' ');
249 else
250 sep = ", ";
251 int next_indent = (indent != 0) ? indent + 1 : 0;
252 // clang-format off
253 ss << "Rw_buffer_sequence(ptr=" << (const void *)this
254 << sep << "capacity=" << capacity()
255 << sep << "read_part="
256 << read_part().debug_string(show_contents, next_indent)
257 << sep << "between_r_and_w="
259 read_part().end(), write_part().begin())
260 .debug_string(show_contents)
261 << sep << "write_part="
262 << write_part().debug_string(show_contents, next_indent)
263 << ")";
264 // clang-format on
265 return ss.str();
266#endif
267 }
268
269 /// In debug mode, return a string that describes the internal
270 /// structure of this object, to use for debugging.
271 ///
272 /// @param show_contents If true, includes the buffer contents.
273 /// Otherwise, just pointers and sizes.
274 std::string debug_string([[maybe_unused]] bool show_contents = false) const {
275 return debug_string(show_contents, 0);
276 }
277
278 protected:
279 /// Move the position to the given, absolute position.
280 ///
281 /// @note This may alter the end iterator of the left part, the
282 /// begin iterator of the right part, as well as the begin/end
283 /// iterators of buffers between the current position and the new
284 /// position. The beginning of the left part, the end of the right
285 /// part, and buffers outside the range between the new and old
286 /// position remain unchanged.
287 ///
288 /// @param new_position The new position. This must be within
289 /// bounds. Otherwise an assertion is raised in debug build; in
290 /// non-debug build, the position is forced to the end.
291 ///
292 /// @param left The left buffer sequence.
293 ///
294 /// @param right The right buffer sequence.
295 ///
296 /// @note One of the following must hold: (1) the container has one
297 /// element between the left part and the right part, and that
298 /// element is a null buffer; (2) left.end()==right.begin() &&
299 /// left.end().end()==right.begin().begin().
300 static void set_position(Size_t new_position, Buffer_sequence_view_t &left,
301 Buffer_sequence_view_t &right) {
303 Size_t position = left.size();
304 Size_t capacity = position + right.size();
305
306 assert(new_position <= capacity);
307 new_position = std::min(new_position, capacity);
308
309 // If a buffer is split between the read and write parts, merge it.
310 position += merge_if_split(left, right);
311 assert(position >= 0);
312 // Move position left, one buffer at a time, until it becomes less
313 // than or equal to new_position.
314 while (position > new_position)
315 position -= move_position_one_buffer_left(left, right);
316
317 // Move position right, one buffer at a time, until it becomes
318 // equal to new_position. If new_position is not at a buffer
319 // boundary, the function will split the buffer. Therefore, this
320 // never makes position greater than new_position, so finally the
321 // position is equal to new_position.
322 while (position < new_position) {
324 left, right, new_position - position);
325 }
326 assert(position == new_position);
327 }
328
329 /// If a buffer is split between the read and write parts, glue the
330 /// pieces together again and include them in the read part.
331 ///
332 /// If no buffer is split (the position is at a buffer boundary),
333 /// does nothing.
334 ///
335 /// Graphically, the operation is as follows:
336 ///
337 /// ```
338 /// +-----------------+-----------------+
339 /// | b1[0..split] | b1[split..] |
340 /// +-----------------+-----------------+
341 /// ^ read_end
342 /// ^ write_begin
343 /// -->
344 /// +-----------------+-----------------+
345 /// | b1 | null |
346 /// +-----------------+-----------------+
347 /// ^ read_end ^ write_begin
348 /// ```
349 ///
350 /// @param left The left buffer sequence.
351 ///
352 /// @param right The right buffer sequence.
353 ///
354 /// @return The number of bytes that the position was moved left.
356 Buffer_sequence_view_t &right) {
357 auto [read_begin, read_end, read_size] = get_boundaries(left);
358 auto [write_begin, write_end, write_size] = get_boundaries(right);
359 if (read_end == write_begin) {
360 auto delta = write_begin->size();
361 auto before_read_end = read_end;
362 --before_read_end;
363 *before_read_end = Buffer_view_t(before_read_end->data(),
364 before_read_end->size() + delta);
365 *read_end = Buffer_view_t();
366 ++write_begin;
367 left = Buffer_sequence_view_t(read_begin, read_end, read_size + delta);
368 right =
369 Buffer_sequence_view_t(write_begin, write_end, write_size - delta);
370 return delta;
371 }
372 return 0;
373 }
374
375 /// Move the position exactly one buffer left, assuming no buffer is
376 /// split.
377 ///
378 /// Graphically, the operation is as follows:
379 ///
380 /// ```
381 /// +-----------------+-----------------+
382 /// | b1 | null |
383 /// +-----------------+-----------------+
384 /// ^ read_end ^ write_begin
385 /// -->
386 /// +-----------------+-----------------+
387 /// | null | b1 |
388 /// +-----------------+-----------------+
389 /// ^ read_end ^ write_begin
390 /// ```
391 ///
392 /// @param left The left buffer sequence.
393 ///
394 /// @param right The right buffer sequence.
395 ///
396 /// @return The number of bytes that the position was moved left.
398 Buffer_sequence_view_t &right) {
399 auto [read_begin, read_end, read_size] = get_boundaries(left);
400 auto [write_begin, write_end, write_size] = get_boundaries(right);
401 assert(read_end != write_begin);
402 assert(read_end->data() == nullptr);
403 assert(read_end != read_begin);
404 --read_end;
405 --write_begin;
406 *write_begin = *read_end;
407 *read_end = Buffer_view_t();
408 auto delta = write_begin->size();
409 left = Buffer_sequence_view_t(read_begin, read_end, read_size - delta);
410 right = Buffer_sequence_view_t(write_begin, write_end, write_size + delta);
411 return delta;
412 }
413
414 /// Move the position right by whatever is smaller: the given
415 /// number, or one buffer; splits the buffer if the number is
416 /// smaller than the buffer.
417 ///
418 /// @param left The left buffer sequence.
419 ///
420 /// @param right The right buffer sequence.
421 ///
422 /// @param limit Move the position at most this number of bytes right.
423 ///
424 /// @return The number of bytes that the position was moved right.
427 Size_t limit) {
428 auto [read_begin, read_end, read_size] = get_boundaries(left);
429 auto [write_begin, write_end, write_size] = get_boundaries(right);
430 assert(read_end != write_begin);
431 assert(read_end->data() == nullptr);
432 if (write_begin->size() <= limit) {
433 // +-----------------+-----------------+
434 // | null | b1 |
435 // +-----------------+-----------------+
436 // ^ read_end ^ write_begin
437 // -->
438 // +-----------------+-----------------+
439 // | b1 | null |
440 // +-----------------+-----------------+
441 // ^ read_end ^ write_begin
442 auto delta = write_begin->size();
443 *read_end = *write_begin;
444 *write_begin = Buffer_view_t();
445 ++read_end;
446 ++write_begin;
447 left = Buffer_sequence_view_t(read_begin, read_end, read_size + delta);
448 right =
449 Buffer_sequence_view_t(write_begin, write_end, write_size - delta);
450 return delta;
451 }
452 // +-----------------+-----------------+
453 // | null | b1 |
454 // +-----------------+-----------------+
455 // ^ read_end ^ write_begin
456 // -->
457 // +-----------------+-----------------+
458 // | b1[0..limit] | b1[limit..] |
459 // +-----------------+-----------------+
460 // ^ read_end
461 // ^ write_begin
462 *read_end = Buffer_view_t(write_begin->data(), limit);
463 *write_begin =
464 Buffer_view_t(write_begin->data() + limit, write_begin->size() - limit);
465 ++read_end;
466 left = Buffer_sequence_view_t(read_begin, read_end, read_size + limit);
467 right = Buffer_sequence_view_t(write_begin, write_end, write_size - limit);
468 return limit;
469 }
470
471 /// Return the beginning, end, and size of the read and write parts.
472 ///
473 /// @param buffer_sequence_view The buffer sequence view.
474 ///
475 /// @return 3-tuple containing the beginning, size, and end of the
476 /// given buffer_sequence_view.
477 std::tuple<Iterator_t, Iterator_t, Size_t> static get_boundaries(
478 Buffer_sequence_view_t &buffer_sequence_view) {
479 return std::make_tuple(buffer_sequence_view.begin(),
480 buffer_sequence_view.end(),
481 buffer_sequence_view.size());
482 }
483
484 private:
485 /// buffer_sequence_view for the (leading) read segment.
487 /// buffer_sequence_view for the (trailing) write segment.
489};
490
491} // namespace mysql::binlog::event::compression::buffer
492
493/// @}
494
495#endif // MYSQL_BINLOG_EVENT_COMPRESSION_BUFFER_RW_BUFFER_SEQUENCE_H
Container class that provides a sequence of buffers to the caller.
Class that groups a pointer+size as one object, without managing the memory for it.
Sequence of memory buffers.
Definition: buffer_sequence_view.h:72
Container_tp< Buffer_view_t, Buffer_allocator_t > Container_t
Definition: buffer_sequence_view.h:79
Iterator_t begin()
Iterator to the first buffer.
Definition: buffer_sequence_view.h:117
Size_t size() const
Return the total size of all buffers.
Definition: buffer_sequence_view.h:164
Buffer_view< Char_t > Buffer_view_t
Definition: buffer_sequence_view.h:76
typename std::conditional< const_tp, Const_iterator_t, typename Container_t::iterator >::type Iterator_t
Definition: buffer_sequence_view.h:83
std::string debug_string(bool show_contents=false, int indent=0) const
In debug mode, return a string that describes the internal structure of this object,...
Definition: buffer_sequence_view.h:185
Iterator_t end()
Iterator to the last buffer.
Definition: buffer_sequence_view.h:120
std::size_t Size_t
Definition: buffer_sequence_view.h:75
Char_tp Char_t
Definition: buffer_sequence_view.h:74
typename Container_t::const_iterator Const_iterator_t
Definition: buffer_sequence_view.h:80
mysql::binlog::event::resource::Allocator< Buffer_view_t > Buffer_allocator_t
Definition: buffer_sequence_view.h:78
Non-owning manager for a fixed sequence of memory buffers, which is split into a read part and a writ...
Definition: rw_buffer_sequence.h:107
Buffer_sequence_view< Char_tp, Container_tp > Buffer_sequence_view_t
Definition: rw_buffer_sequence.h:109
std::string debug_string(bool show_contents=false) const
In debug mode, return a string that describes the internal structure of this object,...
Definition: rw_buffer_sequence.h:274
Buffer_sequence_view< Char_tp, Container_tp, true > Const_buffer_sequence_view_t
Definition: rw_buffer_sequence.h:120
Rw_buffer_sequence & operator=(Rw_buffer_sequence &&)=delete
const Buffer_sequence_view_t & write_part() const
Return a const reference to the write part.
Definition: rw_buffer_sequence.h:225
Size_t capacity() const
Return the current size, i.e., total size of all buffers.
Definition: rw_buffer_sequence.h:216
typename Buffer_sequence_view_t::Iterator_t Iterator_t
Definition: rw_buffer_sequence.h:117
Buffer_sequence_view_t & write_part()
Return a non-const reference to the write part.
Definition: rw_buffer_sequence.h:228
void move_position(Difference_t delta)
Move the position left or right, relative to the current position.
Definition: rw_buffer_sequence.h:207
void increase_position(Size_t delta)
Move the position right, relative to the current position.
Definition: rw_buffer_sequence.h:187
Rw_buffer_sequence & operator=(Rw_buffer_sequence &)=delete
static Size_t move_position_at_most_one_buffer_right(Buffer_sequence_view_t &left, Buffer_sequence_view_t &right, Size_t limit)
Move the position right by whatever is smaller: the given number, or one buffer; splits the buffer if...
Definition: rw_buffer_sequence.h:425
Buffer_sequence_view_t & read_part()
Return a non-const reference to the read part.
Definition: rw_buffer_sequence.h:222
const Buffer_sequence_view_t & read_part() const
Return a const reference to the read part.
Definition: rw_buffer_sequence.h:219
typename Buffer_sequence_view_t::Buffer_allocator_t Buffer_allocator_t
Definition: rw_buffer_sequence.h:115
void set_position(Size_t new_position)
Set the specified absolute position.
Definition: rw_buffer_sequence.h:168
static Size_t merge_if_split(Buffer_sequence_view_t &left, Buffer_sequence_view_t &right)
If a buffer is split between the read and write parts, glue the pieces together again and include the...
Definition: rw_buffer_sequence.h:355
typename Buffer_sequence_view_t::Char_t Char_t
Definition: rw_buffer_sequence.h:110
typename Buffer_sequence_view_t::Container_t Container_t
Definition: rw_buffer_sequence.h:116
Buffer_sequence_view_t m_read_part
buffer_sequence_view for the (leading) read segment.
Definition: rw_buffer_sequence.h:486
Buffer_sequence_view_t m_write_part
buffer_sequence_view for the (trailing) write segment.
Definition: rw_buffer_sequence.h:488
typename Buffer_sequence_view_t::Size_t Size_t
Definition: rw_buffer_sequence.h:111
static void set_position(Size_t new_position, Buffer_sequence_view_t &left, Buffer_sequence_view_t &right)
Move the position to the given, absolute position.
Definition: rw_buffer_sequence.h:300
static Size_t move_position_one_buffer_left(Buffer_sequence_view_t &left, Buffer_sequence_view_t &right)
Move the position exactly one buffer left, assuming no buffer is split.
Definition: rw_buffer_sequence.h:397
std::ptrdiff_t Difference_t
Definition: rw_buffer_sequence.h:112
typename Buffer_sequence_view_t::Const_iterator_t Const_iterator_t
Definition: rw_buffer_sequence.h:118
typename Buffer_sequence_view_t::Buffer_view_t Buffer_view_t
Definition: rw_buffer_sequence.h:113
Rw_buffer_sequence(Iterator_t begin_arg, Iterator_t end_arg)
Construct a new Rw_buffer_sequence from given endpoint iterators, with position 0.
Definition: rw_buffer_sequence.h:131
static std::tuple< Iterator_t, Iterator_t, Size_t > get_boundaries(Buffer_sequence_view_t &buffer_sequence_view)
Return the beginning, end, and size of the read and write parts.
Definition: rw_buffer_sequence.h:477
virtual std::string debug_string(bool show_contents, int indent) const
In debug mode, return a string that describes the internal structure of this object,...
Definition: rw_buffer_sequence.h:239
Allocator class that uses a polymorphic Memory_resource to allocate memory.
Definition: buffer_sequence_view.h:51
const char * begin(const char *const c)
Definition: base64.h:44
size_t size(const char *const c)
Definition: base64.h:46
Definition: gcs_xcom_synode.h:64
std::basic_ostringstream< char, std::char_traits< char >, ut::allocator< char > > ostringstream
Specialization of basic_ostringstream which uses ut::allocator.
Definition: ut0new.h:2870
std::vector< T, ut::allocator< T > > vector
Specialization of vector which uses allocator.
Definition: ut0new.h:2874
static task_arg end_arg()
Definition: task.h:207
Contains wrapper functions for memory allocation and deallocation.
#define BAPI_TRACE
Definition: wrapper_functions.h:65