1 | /***************************************************************************** |
2 | |
3 | Copyright (c) 1994, 2014, Oracle and/or its affiliates. All Rights Reserved. |
4 | Copyright (c) 2017, MariaDB Corporation. |
5 | |
6 | This program is free software; you can redistribute it and/or modify it under |
7 | the terms of the GNU General Public License as published by the Free Software |
8 | Foundation; version 2 of the License. |
9 | |
10 | This program is distributed in the hope that it will be useful, but WITHOUT |
11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
12 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU General Public License along with |
15 | this program; if not, write to the Free Software Foundation, Inc., |
16 | 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA |
17 | |
18 | *****************************************************************************/ |
19 | |
20 | /********************************************************************//** |
21 | @file include/mem0mem.ic |
22 | The memory management |
23 | |
24 | Created 6/8/1994 Heikki Tuuri |
25 | *************************************************************************/ |
26 | |
27 | #include "ut0new.h" |
28 | |
29 | #ifdef UNIV_DEBUG |
30 | # define mem_heap_create_block(heap, n, type, file_name, line) \ |
31 | mem_heap_create_block_func(heap, n, file_name, line, type) |
32 | # define mem_heap_create_at(N, file_name, line) \ |
33 | mem_heap_create_func(N, file_name, line, MEM_HEAP_DYNAMIC) |
34 | #else /* UNIV_DEBUG */ |
35 | # define mem_heap_create_block(heap, n, type, file_name, line) \ |
36 | mem_heap_create_block_func(heap, n, type) |
37 | # define mem_heap_create_at(N, file_name, line) \ |
38 | mem_heap_create_func(N, MEM_HEAP_DYNAMIC) |
39 | #endif /* UNIV_DEBUG */ |
40 | /***************************************************************//** |
41 | Creates a memory heap block where data can be allocated. |
42 | @return own: memory heap block, NULL if did not succeed (only possible |
43 | for MEM_HEAP_BTR_SEARCH type heaps) */ |
44 | mem_block_t* |
45 | mem_heap_create_block_func( |
46 | /*=======================*/ |
47 | mem_heap_t* heap, /*!< in: memory heap or NULL if first block |
48 | should be created */ |
49 | ulint n, /*!< in: number of bytes needed for user data */ |
50 | #ifdef UNIV_DEBUG |
51 | const char* file_name,/*!< in: file name where created */ |
52 | unsigned line, /*!< in: line where created */ |
53 | #endif /* UNIV_DEBUG */ |
54 | ulint type); /*!< in: type of heap: MEM_HEAP_DYNAMIC or |
55 | MEM_HEAP_BUFFER */ |
56 | |
57 | /******************************************************************//** |
58 | Frees a block from a memory heap. */ |
59 | void |
60 | mem_heap_block_free( |
61 | /*================*/ |
62 | mem_heap_t* heap, /*!< in: heap */ |
63 | mem_block_t* block); /*!< in: block to free */ |
64 | |
65 | /******************************************************************//** |
66 | Frees the free_block field from a memory heap. */ |
67 | void |
68 | mem_heap_free_block_free( |
69 | /*=====================*/ |
70 | mem_heap_t* heap); /*!< in: heap */ |
71 | |
72 | /***************************************************************//** |
73 | Adds a new block to a memory heap. |
74 | @param[in] heap memory heap |
75 | @param[in] n number of bytes needed |
76 | @return created block, NULL if did not succeed (only possible for |
77 | MEM_HEAP_BTR_SEARCH type heaps) */ |
78 | mem_block_t* |
79 | mem_heap_add_block( |
80 | mem_heap_t* heap, |
81 | ulint n); |
82 | |
83 | UNIV_INLINE |
84 | void |
85 | mem_block_set_len(mem_block_t* block, ulint len) |
86 | { |
87 | ut_ad(len > 0); |
88 | |
89 | block->len = len; |
90 | } |
91 | |
92 | UNIV_INLINE |
93 | ulint |
94 | mem_block_get_len(mem_block_t* block) |
95 | { |
96 | return(block->len); |
97 | } |
98 | |
99 | UNIV_INLINE |
100 | void |
101 | mem_block_set_type(mem_block_t* block, ulint type) |
102 | { |
103 | ut_ad((type == MEM_HEAP_DYNAMIC) || (type == MEM_HEAP_BUFFER) |
104 | || (type == MEM_HEAP_BUFFER + MEM_HEAP_BTR_SEARCH)); |
105 | |
106 | block->type = type; |
107 | } |
108 | |
109 | UNIV_INLINE |
110 | ulint |
111 | mem_block_get_type(mem_block_t* block) |
112 | { |
113 | return(block->type); |
114 | } |
115 | |
116 | UNIV_INLINE |
117 | void |
118 | mem_block_set_free(mem_block_t* block, ulint free) |
119 | { |
120 | ut_ad(free > 0); |
121 | ut_ad(free <= mem_block_get_len(block)); |
122 | |
123 | block->free = free; |
124 | } |
125 | |
126 | UNIV_INLINE |
127 | ulint |
128 | mem_block_get_free(mem_block_t* block) |
129 | { |
130 | return(block->free); |
131 | } |
132 | |
133 | UNIV_INLINE |
134 | void |
135 | mem_block_set_start(mem_block_t* block, ulint start) |
136 | { |
137 | ut_ad(start > 0); |
138 | |
139 | block->start = start; |
140 | } |
141 | |
142 | UNIV_INLINE |
143 | ulint |
144 | mem_block_get_start(mem_block_t* block) |
145 | { |
146 | return(block->start); |
147 | } |
148 | |
149 | /** Checks that an object is a memory heap block |
150 | @param[in] block Memory block to check. */ |
151 | UNIV_INLINE |
152 | void |
153 | mem_block_validate( |
154 | const mem_block_t* block) |
155 | { |
156 | ut_a(block->magic_n == MEM_BLOCK_MAGIC_N); |
157 | } |
158 | |
159 | /** Allocates and zero-fills n bytes of memory from a memory heap. |
160 | @param[in] heap memory heap |
161 | @param[in] n number of bytes; if the heap is allowed to grow into |
162 | the buffer pool, this must be <= MEM_MAX_ALLOC_IN_BUF |
163 | @return allocated, zero-filled storage */ |
164 | UNIV_INLINE |
165 | void* |
166 | mem_heap_zalloc( |
167 | mem_heap_t* heap, |
168 | ulint n) |
169 | { |
170 | ut_ad(heap); |
171 | ut_ad(!(heap->type & MEM_HEAP_BTR_SEARCH)); |
172 | return(memset(mem_heap_alloc(heap, n), 0, n)); |
173 | } |
174 | |
175 | /** Allocates n bytes of memory from a memory heap. |
176 | @param[in] heap memory heap |
177 | @param[in] n number of bytes; if the heap is allowed to grow into |
178 | the buffer pool, this must be <= MEM_MAX_ALLOC_IN_BUF |
179 | @return allocated storage, NULL if did not succeed (only possible for |
180 | MEM_HEAP_BTR_SEARCH type heaps) */ |
181 | UNIV_INLINE |
182 | void* |
183 | mem_heap_alloc( |
184 | mem_heap_t* heap, |
185 | ulint n) |
186 | { |
187 | mem_block_t* block; |
188 | void* buf; |
189 | ulint free; |
190 | |
191 | ut_d(mem_block_validate(heap)); |
192 | |
193 | block = UT_LIST_GET_LAST(heap->base); |
194 | |
195 | ut_ad(!(block->type & MEM_HEAP_BUFFER) || (n <= MEM_MAX_ALLOC_IN_BUF)); |
196 | |
197 | /* Check if there is enough space in block. If not, create a new |
198 | block to the heap */ |
199 | |
200 | if (mem_block_get_len(block) |
201 | < mem_block_get_free(block) + MEM_SPACE_NEEDED(n)) { |
202 | |
203 | block = mem_heap_add_block(heap, n); |
204 | |
205 | if (block == NULL) { |
206 | |
207 | return(NULL); |
208 | } |
209 | } |
210 | |
211 | free = mem_block_get_free(block); |
212 | |
213 | buf = (byte*) block + free; |
214 | |
215 | mem_block_set_free(block, free + MEM_SPACE_NEEDED(n)); |
216 | |
217 | UNIV_MEM_ALLOC(buf, n); |
218 | return(buf); |
219 | } |
220 | |
221 | /** Returns a pointer to the heap top. |
222 | @param[in] heap memory heap |
223 | @return pointer to the heap top */ |
224 | UNIV_INLINE |
225 | byte* |
226 | mem_heap_get_heap_top( |
227 | mem_heap_t* heap) |
228 | { |
229 | mem_block_t* block; |
230 | byte* buf; |
231 | |
232 | ut_d(mem_block_validate(heap)); |
233 | |
234 | block = UT_LIST_GET_LAST(heap->base); |
235 | |
236 | buf = (byte*) block + mem_block_get_free(block); |
237 | |
238 | return(buf); |
239 | } |
240 | |
241 | /** Frees the space in a memory heap exceeding the pointer given. |
242 | The pointer must have been acquired from mem_heap_get_heap_top. |
243 | The first memory block of the heap is not freed. |
244 | @param[in] heap heap from which to free |
245 | @param[in] old_top pointer to old top of heap */ |
246 | UNIV_INLINE |
247 | void |
248 | mem_heap_free_heap_top( |
249 | mem_heap_t* heap, |
250 | byte* old_top) |
251 | { |
252 | mem_block_t* block; |
253 | mem_block_t* prev_block; |
254 | |
255 | ut_d(mem_heap_validate(heap)); |
256 | |
257 | block = UT_LIST_GET_LAST(heap->base); |
258 | |
259 | while (block != NULL) { |
260 | if (((byte*) block + mem_block_get_free(block) >= old_top) |
261 | && ((byte*) block <= old_top)) { |
262 | /* Found the right block */ |
263 | |
264 | break; |
265 | } |
266 | |
267 | /* Store prev_block value before freeing the current block |
268 | (the current block will be erased in freeing) */ |
269 | |
270 | prev_block = UT_LIST_GET_PREV(list, block); |
271 | |
272 | mem_heap_block_free(heap, block); |
273 | |
274 | block = prev_block; |
275 | } |
276 | |
277 | ut_ad(block); |
278 | |
279 | /* Set the free field of block */ |
280 | mem_block_set_free(block, |
281 | ulint(old_top - reinterpret_cast<byte*>(block))); |
282 | |
283 | ut_ad(mem_block_get_start(block) <= mem_block_get_free(block)); |
284 | UNIV_MEM_FREE(old_top, (byte*) block + block->len - old_top); |
285 | |
286 | /* If free == start, we may free the block if it is not the first |
287 | one */ |
288 | |
289 | if ((heap != block) && (mem_block_get_free(block) |
290 | == mem_block_get_start(block))) { |
291 | mem_heap_block_free(heap, block); |
292 | } |
293 | } |
294 | |
295 | /** Empties a memory heap. |
296 | The first memory block of the heap is not freed. |
297 | @param[in] heap heap to empty */ |
298 | UNIV_INLINE |
299 | void |
300 | mem_heap_empty( |
301 | mem_heap_t* heap) |
302 | { |
303 | mem_heap_free_heap_top(heap, (byte*) heap + mem_block_get_start(heap)); |
304 | |
305 | if (heap->free_block) { |
306 | mem_heap_free_block_free(heap); |
307 | } |
308 | } |
309 | |
310 | /** Returns a pointer to the topmost element in a memory heap. |
311 | The size of the element must be given. |
312 | @param[in] heap memory heap |
313 | @param[in] n size of the topmost element |
314 | @return pointer to the topmost element */ |
315 | UNIV_INLINE |
316 | void* |
317 | mem_heap_get_top( |
318 | mem_heap_t* heap, |
319 | ulint n) |
320 | { |
321 | mem_block_t* block; |
322 | byte* buf; |
323 | |
324 | ut_d(mem_block_validate(heap)); |
325 | |
326 | block = UT_LIST_GET_LAST(heap->base); |
327 | |
328 | buf = (byte*) block + mem_block_get_free(block) - MEM_SPACE_NEEDED(n); |
329 | |
330 | return((void*) buf); |
331 | } |
332 | |
333 | /** Checks if a given chunk of memory is the topmost element stored in the |
334 | heap. If this is the case, then calling mem_heap_free_top() would free |
335 | that element from the heap. |
336 | @param[in] heap memory heap |
337 | @param[in] buf presumed topmost element |
338 | @param[in] buf_sz size of buf in bytes |
339 | @return true if topmost */ |
340 | UNIV_INLINE |
341 | bool |
342 | mem_heap_is_top( |
343 | mem_heap_t* heap, |
344 | const void* buf, |
345 | ulint buf_sz) |
346 | { |
347 | const byte* first_free_byte; |
348 | const byte* presumed_start_of_buf; |
349 | |
350 | ut_d(mem_block_validate(heap)); |
351 | |
352 | first_free_byte = mem_heap_get_heap_top(heap); |
353 | |
354 | presumed_start_of_buf = first_free_byte - MEM_SPACE_NEEDED(buf_sz); |
355 | |
356 | return(presumed_start_of_buf == buf); |
357 | } |
358 | |
359 | /*****************************************************************//** |
360 | Allocate a new chunk of memory from a memory heap, possibly discarding |
361 | the topmost element. If the memory chunk specified with (top, top_sz) |
362 | is the topmost element, then it will be discarded, otherwise it will |
363 | be left untouched and this function will be equivallent to |
364 | mem_heap_alloc(). |
365 | @return allocated storage, NULL if did not succeed (only possible for |
366 | MEM_HEAP_BTR_SEARCH type heaps) */ |
367 | UNIV_INLINE |
368 | void* |
369 | mem_heap_replace( |
370 | /*=============*/ |
371 | mem_heap_t* heap, /*!< in/out: memory heap */ |
372 | const void* top, /*!< in: chunk to discard if possible */ |
373 | ulint top_sz, /*!< in: size of top in bytes */ |
374 | ulint new_sz) /*!< in: desired size of the new chunk */ |
375 | { |
376 | if (mem_heap_is_top(heap, top, top_sz)) { |
377 | mem_heap_free_top(heap, top_sz); |
378 | } |
379 | |
380 | return(mem_heap_alloc(heap, new_sz)); |
381 | } |
382 | |
383 | /*****************************************************************//** |
384 | Allocate a new chunk of memory from a memory heap, possibly discarding |
385 | the topmost element and then copy the specified data to it. If the memory |
386 | chunk specified with (top, top_sz) is the topmost element, then it will be |
387 | discarded, otherwise it will be left untouched and this function will be |
388 | equivallent to mem_heap_dup(). |
389 | @return allocated storage, NULL if did not succeed (only possible for |
390 | MEM_HEAP_BTR_SEARCH type heaps) */ |
391 | UNIV_INLINE |
392 | void* |
393 | mem_heap_dup_replace( |
394 | /*=================*/ |
395 | mem_heap_t* heap, /*!< in/out: memory heap */ |
396 | const void* top, /*!< in: chunk to discard if possible */ |
397 | ulint top_sz, /*!< in: size of top in bytes */ |
398 | const void* data, /*!< in: new data to duplicate */ |
399 | ulint data_sz)/*!< in: size of data in bytes */ |
400 | { |
401 | void* p = mem_heap_replace(heap, top, top_sz, data_sz); |
402 | |
403 | memcpy(p, data, data_sz); |
404 | |
405 | return(p); |
406 | } |
407 | |
408 | /*****************************************************************//** |
409 | Allocate a new chunk of memory from a memory heap, possibly discarding |
410 | the topmost element and then copy the specified string to it. If the memory |
411 | chunk specified with (top, top_sz) is the topmost element, then it will be |
412 | discarded, otherwise it will be left untouched and this function will be |
413 | equivallent to mem_heap_strdup(). |
414 | @return allocated string, NULL if did not succeed (only possible for |
415 | MEM_HEAP_BTR_SEARCH type heaps) */ |
416 | UNIV_INLINE |
417 | char* |
418 | mem_heap_strdup_replace( |
419 | /*====================*/ |
420 | mem_heap_t* heap, /*!< in/out: memory heap */ |
421 | const void* top, /*!< in: chunk to discard if possible */ |
422 | ulint top_sz, /*!< in: size of top in bytes */ |
423 | const char* str) /*!< in: new data to duplicate */ |
424 | { |
425 | return(reinterpret_cast<char*>(mem_heap_dup_replace( |
426 | heap, top, top_sz, str, strlen(str) + 1))); |
427 | } |
428 | |
429 | /*****************************************************************//** |
430 | Frees the topmost element in a memory heap. The size of the element must be |
431 | given. */ |
432 | UNIV_INLINE |
433 | void |
434 | mem_heap_free_top( |
435 | /*==============*/ |
436 | mem_heap_t* heap, /*!< in: memory heap */ |
437 | ulint n) /*!< in: size of the topmost element */ |
438 | { |
439 | mem_block_t* block; |
440 | |
441 | ut_d(mem_block_validate(heap)); |
442 | |
443 | block = UT_LIST_GET_LAST(heap->base); |
444 | |
445 | /* Subtract the free field of block */ |
446 | mem_block_set_free(block, mem_block_get_free(block) |
447 | - MEM_SPACE_NEEDED(n)); |
448 | |
449 | /* If free == start, we may free the block if it is not the first |
450 | one */ |
451 | |
452 | if ((heap != block) && (mem_block_get_free(block) |
453 | == mem_block_get_start(block))) { |
454 | mem_heap_block_free(heap, block); |
455 | } else { |
456 | UNIV_MEM_FREE((byte*) block + mem_block_get_free(block), n); |
457 | } |
458 | } |
459 | |
460 | /** Creates a memory heap. |
461 | NOTE: Use the corresponding macros instead of this function. |
462 | A single user buffer of 'size' will fit in the block. |
463 | 0 creates a default size block. |
464 | @param[in] size Desired start block size. |
465 | @param[in] file_name File name where created |
466 | @param[in] line Line where created |
467 | @param[in] type Heap type |
468 | @return own: memory heap, NULL if did not succeed (only possible for |
469 | MEM_HEAP_BTR_SEARCH type heaps) */ |
470 | UNIV_INLINE |
471 | mem_heap_t* |
472 | mem_heap_create_func( |
473 | ulint size, |
474 | #ifdef UNIV_DEBUG |
475 | const char* file_name, |
476 | unsigned line, |
477 | #endif /* UNIV_DEBUG */ |
478 | ulint type) |
479 | { |
480 | mem_block_t* block; |
481 | |
482 | if (!size) { |
483 | size = MEM_BLOCK_START_SIZE; |
484 | } |
485 | |
486 | block = mem_heap_create_block(NULL, size, type, file_name, line); |
487 | |
488 | if (block == NULL) { |
489 | |
490 | return(NULL); |
491 | } |
492 | |
493 | /* The first block should not be in buffer pool, |
494 | because it might be relocated to resize buffer pool. */ |
495 | ut_ad(block->buf_block == NULL); |
496 | |
497 | UT_LIST_INIT(block->base, &mem_block_t::list); |
498 | |
499 | /* Add the created block itself as the first block in the list */ |
500 | UT_LIST_ADD_FIRST(block->base, block); |
501 | |
502 | return(block); |
503 | } |
504 | |
505 | /** Frees the space occupied by a memory heap. |
506 | NOTE: Use the corresponding macro instead of this function. |
507 | @param[in] heap Heap to be freed */ |
508 | UNIV_INLINE |
509 | void |
510 | mem_heap_free( |
511 | mem_heap_t* heap) |
512 | { |
513 | mem_block_t* block; |
514 | mem_block_t* prev_block; |
515 | |
516 | ut_d(mem_block_validate(heap)); |
517 | |
518 | block = UT_LIST_GET_LAST(heap->base); |
519 | |
520 | if (heap->free_block) { |
521 | mem_heap_free_block_free(heap); |
522 | } |
523 | |
524 | while (block != NULL) { |
525 | /* Store the contents of info before freeing current block |
526 | (it is erased in freeing) */ |
527 | |
528 | prev_block = UT_LIST_GET_PREV(list, block); |
529 | |
530 | mem_heap_block_free(heap, block); |
531 | |
532 | block = prev_block; |
533 | } |
534 | } |
535 | |
536 | /*****************************************************************//** |
537 | Returns the space in bytes occupied by a memory heap. */ |
538 | UNIV_INLINE |
539 | ulint |
540 | mem_heap_get_size( |
541 | /*==============*/ |
542 | mem_heap_t* heap) /*!< in: heap */ |
543 | { |
544 | ulint size = 0; |
545 | |
546 | ut_d(mem_block_validate(heap)); |
547 | |
548 | size = heap->total_size; |
549 | |
550 | if (heap->free_block) { |
551 | size += srv_page_size; |
552 | } |
553 | |
554 | return(size); |
555 | } |
556 | |
557 | /**********************************************************************//** |
558 | Duplicates a NUL-terminated string. |
559 | @return own: a copy of the string, must be deallocated with ut_free */ |
560 | UNIV_INLINE |
561 | char* |
562 | mem_strdup( |
563 | /*=======*/ |
564 | const char* str) /*!< in: string to be copied */ |
565 | { |
566 | ulint len = strlen(str) + 1; |
567 | return(static_cast<char*>(memcpy(ut_malloc_nokey(len), str, len))); |
568 | } |
569 | |
570 | /**********************************************************************//** |
571 | Makes a NUL-terminated copy of a nonterminated string. |
572 | @return own: a copy of the string, must be deallocated with ut_free */ |
573 | UNIV_INLINE |
574 | char* |
575 | mem_strdupl( |
576 | /*========*/ |
577 | const char* str, /*!< in: string to be copied */ |
578 | ulint len) /*!< in: length of str, in bytes */ |
579 | { |
580 | char* s = static_cast<char*>(ut_malloc_nokey(len + 1)); |
581 | s[len] = 0; |
582 | return(static_cast<char*>(memcpy(s, str, len))); |
583 | } |
584 | |