MySQL 9.1.0
Source Code Documentation
dyn0buf.h
Go to the documentation of this file.
1/*****************************************************************************
2
3Copyright (c) 2013, 2024, Oracle and/or its affiliates.
4
5This program is free software; you can redistribute it and/or modify it under
6the terms of the GNU General Public License, version 2.0, as published by the
7Free Software Foundation.
8
9This program is designed to work with certain software (including
10but not limited to OpenSSL) that is licensed under separate terms,
11as designated in a particular file or component or in included license
12documentation. The authors of MySQL hereby grant you an additional
13permission to link the program and your derivative works with the
14separately licensed software that they have either included with
15the program or referenced in the documentation.
16
17This program is distributed in the hope that it will be useful, but WITHOUT
18ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
20for more details.
21
22You should have received a copy of the GNU General Public License along with
23this program; if not, write to the Free Software Foundation, Inc.,
2451 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25
26*****************************************************************************/
27
28/** @file include/dyn0buf.h
29 The dynamically allocated buffer implementation
30
31 Created 2013-03-16 Sunny Bains
32 *******************************************************/
33
34#ifndef dyn0buf_h
35#define dyn0buf_h
36
37#include "dyn0types.h"
38#include "mem0mem.h"
39#include "univ.i"
40#include "ut0lst.h"
41
42/** Class that manages dynamic buffers. It uses a UT_LIST of
43dyn_buf_t::block_t instances. We don't use STL containers in
44order to avoid the overhead of heap calls. Using a custom memory
45allocator doesn't solve the problem either because we have to get
46the memory from somewhere. We can't use the block_t::m_data as the
47backend for the custom allocator because we would like the data in
48the blocks to be contiguous. */
49template <size_t SIZE = DYN_ARRAY_DATA_SIZE>
50class dyn_buf_t {
51 public:
52 class block_t;
53
54 typedef UT_LIST_NODE_T(block_t) block_node_t;
55
56 class block_t {
57 public:
59 static_assert(MAX_DATA_SIZE <= (2 << 15));
60 init();
61 }
62
63 ~block_t() = default;
64
65 /**
66 Gets the number of used bytes in a block.
67 @return number of bytes used */
68 [[nodiscard]] ulint used() const {
69 return (static_cast<ulint>(m_used & ~DYN_BLOCK_FULL_FLAG));
70 }
71
72 /**
73 Gets pointer to the start of data.
74 @return pointer to data */
75 [[nodiscard]] byte *start() { return (m_data); }
76
77 /**
78 @return start of data - non const version */
79 [[nodiscard]] byte *begin() { return (m_data); }
80
81 /**
82 @return end of used data - non const version */
83 [[nodiscard]] byte *end() { return (begin() + m_used); }
84
85 /**
86 @return start of data - const version */
87 [[nodiscard]] const byte *begin() const { return (m_data); }
88
89 /**
90 @return end of used data - const version */
91 [[nodiscard]] const byte *end() const { return (begin() + m_used); }
92
93 private:
94 /**
95 @return pointer to start of reserved space */
96 template <typename Type>
97 Type push(uint32_t size) {
98 Type ptr = reinterpret_cast<Type>(end());
99
100 m_used += size;
101 ut_ad(m_used <= static_cast<uint32_t>(MAX_DATA_SIZE));
102
103 return (ptr);
104 }
105
106 /**
107 Grow the stack. */
108 void close(const byte *ptr) {
109 /* Check that it is within bounds */
110 ut_ad(ptr >= begin());
111 ut_ad(ptr <= begin() + m_buf_end);
112
113 /* We have done the boundary check above */
114 m_used = static_cast<uint32_t>(ptr - begin());
115
117 ut_d(m_buf_end = 0);
118 }
119
120 /**
121 Initialise the block */
122 void init() {
123 m_used = 0;
124 ut_d(m_buf_end = 0);
126 }
127
128 private:
129#ifdef UNIV_DEBUG
130 /** If opened then this is the buffer end offset, else 0 */
132
133 /** Magic number (DYN_BLOCK_MAGIC_N) */
135#endif /* UNIV_DEBUG */
136
137 /** SIZE - sizeof(m_node) + sizeof(m_used) */
138 static constexpr auto MAX_DATA_SIZE = SIZE;
139
140 /** Storage */
142
143 /** Doubly linked list node. */
144 block_node_t m_node;
145
146 /** number of data bytes used in this block;
147 DYN_BLOCK_FULL_FLAG is set when the block becomes full */
148 uint32_t m_used;
149
150 friend class dyn_buf_t;
151 };
152 typedef UT_LIST_BASE_NODE_T(block_t, m_node) block_list_t;
153
154 static constexpr auto MAX_DATA_SIZE = block_t::MAX_DATA_SIZE;
155
156 /** Default constructor */
158
159 /** Destructor */
161
162 /** Reset the buffer vector */
163 void erase() {
164 if (m_heap != nullptr) {
166 m_heap = nullptr;
167
168 /* Initialise the list and add the first block. */
169 m_list.clear();
171 } else {
174 }
175
176 m_size = 0;
177 }
178
179 /**
180 Makes room on top and returns a pointer to a buffer in it. After
181 copying the elements, the caller must close the buffer using close().
182 @param size in bytes of the buffer; MUST be <= MAX_DATA_SIZE!
183 @return pointer to the buffer */
184 [[nodiscard]] byte *open(ulint size) {
185 ut_ad(size > 0);
187
188 block_t *block;
189
190 block = has_space(size) ? back() : add_block();
191
192 ut_ad(block->m_used <= MAX_DATA_SIZE);
193 ut_d(block->m_buf_end = block->m_used + size);
194
195 return (block->end());
196 }
197
198 /**
199 Closes the buffer returned by open.
200 @param ptr end of used space */
201 void close(const byte *ptr) {
203 block_t *block = back();
204
205 m_size -= block->used();
206
207 block->close(ptr);
208
209 m_size += block->used();
210 }
211
212 /**
213 Makes room on top and returns a pointer to the added element.
214 The caller must copy the element to the pointer returned.
215 @param size in bytes of the element
216 @return pointer to the element */
217 template <typename Type>
218 Type push(uint32_t size) {
219 ut_ad(size > 0);
221
222 block_t *block;
223
224 block = has_space(size) ? back() : add_block();
225
226 m_size += size;
227
228 /* See ISO C++03 14.2/4 for why "template" is required. */
229
230 return (block->template push<Type>(size));
231 }
232
233 /**
234 Pushes n bytes.
235 @param ptr string to write
236 @param len string length */
237 void push(const byte *ptr, uint32_t len) {
238 while (len > 0) {
239 uint32_t n_copied;
240
241 if (len >= MAX_DATA_SIZE) {
242 n_copied = MAX_DATA_SIZE;
243 } else {
244 n_copied = len;
245 }
246
247 ::memmove(push<byte *>(n_copied), ptr, n_copied);
248
249 ptr += n_copied;
250 len -= n_copied;
251 }
252 }
253
254 /**
255 Returns a pointer to an element in the buffer. const version.
256 @param pos position of element in bytes from start
257 @return pointer to element */
258 template <typename Type>
259 const Type at(ulint pos) const {
260 block_t *block =
261 const_cast<block_t *>(const_cast<dyn_buf_t *>(this)->find(pos));
262
263 return (reinterpret_cast<Type>(block->begin() + pos));
264 }
265
266 /**
267 Returns a pointer to an element in the buffer. non const version.
268 @param pos position of element in bytes from start
269 @return pointer to element */
270 template <typename Type>
271 Type at(ulint pos) {
272 block_t *block = const_cast<block_t *>(find(pos));
273
274 return (reinterpret_cast<Type>(block->begin() + pos));
275 }
276
277 /**
278 Returns the size of the total stored data.
279 @return data size in bytes */
280 [[nodiscard]] ulint size() const {
281#ifdef UNIV_DEBUG
282 ulint total_size = 0;
283
284 for (const block_t *block : m_list) {
285 total_size += block->used();
286 }
287
288 ut_ad(total_size == m_size);
289#endif /* UNIV_DEBUG */
290 return (m_size);
291 }
292
293 /**
294 Iterate over each block and call the functor.
295 @return false if iteration was terminated. */
296 template <typename Functor>
297 bool for_each_block(Functor &functor) const {
298 for (const block_t *block : m_list) {
299 if (!functor(block)) {
300 return (false);
301 }
302 }
303
304 return (true);
305 }
306
307 /**
308 Iterate over all the blocks in reverse and call the iterator
309 @return false if iteration was terminated. */
310 template <typename Functor>
311 bool for_each_block_in_reverse(Functor &functor) const {
312 for (block_t *block = UT_LIST_GET_LAST(m_list); block != nullptr;
313 block = UT_LIST_GET_PREV(m_node, block)) {
314 if (!functor(block)) {
315 return (false);
316 }
317 }
318
319 return (true);
320 }
321
322 /**
323 @return the first block */
324 [[nodiscard]] block_t *front() {
326 return (UT_LIST_GET_FIRST(m_list));
327 }
328
329 /**
330 @return true if m_first_block block was not filled fully */
331 [[nodiscard]] bool is_small() const { return (m_heap == nullptr); }
332
333 private:
334 // Disable copying
335 dyn_buf_t(dyn_buf_t &&) = delete;
336 dyn_buf_t(const dyn_buf_t &) = delete;
338 dyn_buf_t &operator=(const dyn_buf_t &) = delete;
339
340 /**
341 Add the block to the end of the list*/
342 void push_back(block_t *block) {
343 block->init();
344
345 UT_LIST_ADD_LAST(m_list, block);
346 }
347
348 /** @return the last block in the list */
350
351 /*
352 @return true if request can be fulfilled */
353 bool has_space(ulint size) const {
354 return (back()->m_used + size <= MAX_DATA_SIZE);
355 }
356
357 /*
358 @return true if request can be fulfilled */
360 return (back()->m_used + size <= MAX_DATA_SIZE);
361 }
362
363 /** Find the block that contains the pos.
364 @param pos absolute offset, it is updated to make it relative
365 to the block
366 @return the block containing the pos. */
369
370 for (auto block : m_list) {
371 if (pos < block->used()) {
372 return block;
373 }
374
375 pos -= block->used();
376 }
377
378 ut_d(ut_error);
379 ut_o(return nullptr);
380 }
381
382 /**
383 Allocate and add a new block to m_list */
385 block_t *block;
386
387 if (m_heap == nullptr) {
388 m_heap = mem_heap_create(sizeof(*block), UT_LOCATION_HERE);
389 }
390
391 block = reinterpret_cast<block_t *>(mem_heap_alloc(m_heap, sizeof(*block)));
392
393 push_back(block);
394
395 return (block);
396 }
397
398 private:
399 /** Heap to use for memory allocation */
401
402 /** Allocated blocks */
403 block_list_t m_list;
404
405 /** Total size used by all blocks */
407
408 /** The default block, should always be the first element. This
409 is for backwards compatibility and to avoid an extra heap allocation
410 for small REDO log records */
412};
413
415
416/** mtr_buf_t copier */
418 /** The copied buffer */
420
421 /** Append a block to the redo log buffer.
422 @return whether the appending should continue (always true here) */
423 bool operator()(const mtr_buf_t::block_t *block) {
424 byte *buf = m_buf.open(block->used());
425 memcpy(buf, block->begin(), block->used());
426 m_buf.close(buf + block->used());
427 return (true);
428 }
429};
430
431#endif /* dyn0buf_h */
Definition: dyn0buf.h:56
void init()
Initialise the block.
Definition: dyn0buf.h:122
const byte * begin() const
Definition: dyn0buf.h:87
const byte * end() const
Definition: dyn0buf.h:91
ulint m_magic_n
Magic number (DYN_BLOCK_MAGIC_N)
Definition: dyn0buf.h:134
byte * end()
Definition: dyn0buf.h:83
uint32_t m_used
number of data bytes used in this block; DYN_BLOCK_FULL_FLAG is set when the block becomes full
Definition: dyn0buf.h:148
byte m_data[MAX_DATA_SIZE]
Storage.
Definition: dyn0buf.h:141
static constexpr auto MAX_DATA_SIZE
SIZE - sizeof(m_node) + sizeof(m_used)
Definition: dyn0buf.h:138
void close(const byte *ptr)
Grow the stack.
Definition: dyn0buf.h:108
block_t()
Definition: dyn0buf.h:58
block_node_t m_node
Doubly linked list node.
Definition: dyn0buf.h:144
byte * start()
Gets pointer to the start of data.
Definition: dyn0buf.h:75
ulint used() const
Gets the number of used bytes in a block.
Definition: dyn0buf.h:68
byte * begin()
Definition: dyn0buf.h:79
Type push(uint32_t size)
Definition: dyn0buf.h:97
ulint m_buf_end
If opened then this is the buffer end offset, else 0.
Definition: dyn0buf.h:131
Class that manages dynamic buffers.
Definition: dyn0buf.h:50
dyn_buf_t()
Default constructor.
Definition: dyn0buf.h:157
void push_back(block_t *block)
Add the block to the end of the list.
Definition: dyn0buf.h:342
ulint m_size
Total size used by all blocks.
Definition: dyn0buf.h:406
~dyn_buf_t()
Destructor.
Definition: dyn0buf.h:160
void close(const byte *ptr)
Closes the buffer returned by open.
Definition: dyn0buf.h:201
block_t * back()
Definition: dyn0buf.h:349
typedef UT_LIST_BASE_NODE_T(block_t, m_node) block_list_t
ulint size() const
Returns the size of the total stored data.
Definition: dyn0buf.h:280
block_t * find(ulint &pos)
Find the block that contains the pos.
Definition: dyn0buf.h:367
byte * open(ulint size)
Makes room on top and returns a pointer to a buffer in it.
Definition: dyn0buf.h:184
typedef UT_LIST_NODE_T(block_t) block_node_t
void erase()
Reset the buffer vector.
Definition: dyn0buf.h:163
static constexpr auto MAX_DATA_SIZE
Definition: dyn0buf.h:154
dyn_buf_t(dyn_buf_t &&)=delete
mem_heap_t * m_heap
Heap to use for memory allocation.
Definition: dyn0buf.h:400
block_t * front()
Definition: dyn0buf.h:324
Type push(uint32_t size)
Makes room on top and returns a pointer to the added element.
Definition: dyn0buf.h:218
Type at(ulint pos)
Returns a pointer to an element in the buffer.
Definition: dyn0buf.h:271
block_t * add_block()
Allocate and add a new block to m_list.
Definition: dyn0buf.h:384
block_t m_first_block
The default block, should always be the first element.
Definition: dyn0buf.h:411
bool has_space(ulint size) const
Definition: dyn0buf.h:353
void push(const byte *ptr, uint32_t len)
Pushes n bytes.
Definition: dyn0buf.h:237
bool for_each_block_in_reverse(Functor &functor) const
Iterate over all the blocks in reverse and call the iterator.
Definition: dyn0buf.h:311
bool is_small() const
Definition: dyn0buf.h:331
dyn_buf_t & operator=(dyn_buf_t &&)=delete
block_list_t m_list
Allocated blocks.
Definition: dyn0buf.h:403
dyn_buf_t & operator=(const dyn_buf_t &)=delete
bool has_space(ulint size)
Definition: dyn0buf.h:359
bool for_each_block(Functor &functor) const
Iterate over each block and call the functor.
Definition: dyn0buf.h:297
dyn_buf_t(const dyn_buf_t &)=delete
const Type at(ulint pos) const
Returns a pointer to an element in the buffer.
Definition: dyn0buf.h:259
dyn_buf_t< DYN_ARRAY_DATA_SIZE > mtr_buf_t
Definition: dyn0buf.h:414
The dynamically allocated buffer types and constants.
constexpr uint32_t DYN_BLOCK_MAGIC_N
Value of dyn_block_t::magic_n.
Definition: dyn0types.h:41
constexpr uint32_t DYN_BLOCK_FULL_FLAG
Flag for dyn_block_t::used that indicates a full block.
Definition: dyn0types.h:48
The memory management.
static void * mem_heap_alloc(mem_heap_t *heap, ulint n)
Allocates n bytes of memory from a memory heap.
static void mem_heap_free(mem_heap_t *heap)
Frees the space occupied by a memory heap.
static mem_heap_t * mem_heap_create(ulint size, ut::Location loc, ulint type=MEM_HEAP_DYNAMIC)
Creates a memory heap.
Definition: buf0block_hint.cc:30
Type
Definition: resource_group_basic_types.h:33
The info structure stored at the beginning of a heap block.
Definition: mem0mem.h:302
mtr_buf_t copier
Definition: dyn0buf.h:417
bool operator()(const mtr_buf_t::block_t *block)
Append a block to the redo log buffer.
Definition: dyn0buf.h:423
mtr_buf_t m_buf
The copied buffer.
Definition: dyn0buf.h:419
Version control for database, common definitions, and include files.
unsigned long int ulint
Definition: univ.i:406
#define UT_LOCATION_HERE
Definition: ut0core.h:73
#define ut_error
Abort execution.
Definition: ut0dbg.h:101
#define ut_ad(EXPR)
Debug assertion.
Definition: ut0dbg.h:105
#define ut_o(EXPR)
Opposite of ut_d().
Definition: ut0dbg.h:109
#define ut_d(EXPR)
Debug statement.
Definition: ut0dbg.h:107
List utilities.
#define UT_LIST_GET_LEN(BASE)
Alternative macro to get the number of nodes in a two-way list, i.e., its length.
Definition: ut0lst.h:441
#define UT_LIST_GET_FIRST(BASE)
Gets the first node in a two-way list.
Definition: ut0lst.h:446
#define UT_LIST_GET_LAST(BASE)
Gets the last node in a two-way list.
Definition: ut0lst.h:451
#define UT_LIST_GET_PREV(NAME, N)
Gets the previous node in a two-way list.
Definition: ut0lst.h:435
#define UT_LIST_ADD_LAST(LIST, ELEM)
Adds the node as the last element in a two-way linked list.
Definition: ut0lst.h:354