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