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