1/*****************************************************************************
2
3Copyright (c) 1994, 2014, Oracle and/or its affiliates. All Rights Reserved.
4Copyright (c) 2017, 2018, MariaDB Corporation.
5
6This program is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free Software
8Foundation; version 2 of the License.
9
10This program is distributed in the hope that it will be useful, but WITHOUT
11ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License along with
15this program; if not, write to the Free Software Foundation, Inc.,
1651 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
17
18*****************************************************************************/
19
20/********************************************************************//**
21@file mem/mem0mem.cc
22The memory management
23
24Created 6/9/1994 Heikki Tuuri
25*************************************************************************/
26
27#include "ha_prototypes.h"
28
29#include "mem0mem.h"
30#include "buf0buf.h"
31#include "srv0srv.h"
32#include <stdarg.h>
33
34/**********************************************************************//**
35Concatenate two strings and return the result, using a memory heap.
36@return own: the result */
37char*
38mem_heap_strcat(
39/*============*/
40 mem_heap_t* heap, /*!< in: memory heap where string is allocated */
41 const char* s1, /*!< in: string 1 */
42 const char* s2) /*!< in: string 2 */
43{
44 char* s;
45 ulint s1_len = strlen(s1);
46 ulint s2_len = strlen(s2);
47
48 s = static_cast<char*>(mem_heap_alloc(heap, s1_len + s2_len + 1));
49
50 memcpy(s, s1, s1_len);
51 memcpy(s + s1_len, s2, s2_len);
52
53 s[s1_len + s2_len] = '\0';
54
55 return(s);
56}
57
58
59/****************************************************************//**
60Helper function for mem_heap_printf.
61@return length of formatted string, including terminating NUL */
62static
63ulint
64mem_heap_printf_low(
65/*================*/
66 char* buf, /*!< in/out: buffer to store formatted string
67 in, or NULL to just calculate length */
68 const char* format, /*!< in: format string */
69 va_list ap) /*!< in: arguments */
70{
71 ulint len = 0;
72
73 while (*format) {
74
75 /* Does this format specifier have the 'l' length modifier. */
76 ibool is_long = FALSE;
77
78 /* Length of one parameter. */
79 size_t plen;
80
81 if (*format++ != '%') {
82 /* Non-format character. */
83
84 len++;
85
86 if (buf) {
87 *buf++ = *(format - 1);
88 }
89
90 continue;
91 }
92
93 if (*format == 'l') {
94 is_long = TRUE;
95 format++;
96 }
97
98 switch (*format++) {
99 case 's':
100 /* string */
101 {
102 char* s = va_arg(ap, char*);
103
104 /* "%ls" is a non-sensical format specifier. */
105 ut_a(!is_long);
106
107 plen = strlen(s);
108 len += plen;
109
110 if (buf) {
111 memcpy(buf, s, plen);
112 buf += plen;
113 }
114 }
115
116 break;
117
118 case 'u':
119 /* unsigned int */
120 {
121 char tmp[32];
122 unsigned long val;
123
124 /* We only support 'long' values for now. */
125 ut_a(is_long);
126
127 val = va_arg(ap, unsigned long);
128
129 plen = size_t(sprintf(tmp, "%lu", val));
130 len += plen;
131
132 if (buf) {
133 memcpy(buf, tmp, plen);
134 buf += plen;
135 }
136 }
137
138 break;
139
140 case '%':
141
142 /* "%l%" is a non-sensical format specifier. */
143 ut_a(!is_long);
144
145 len++;
146
147 if (buf) {
148 *buf++ = '%';
149 }
150
151 break;
152
153 default:
154 ut_error;
155 }
156 }
157
158 /* For the NUL character. */
159 len++;
160
161 if (buf) {
162 *buf = '\0';
163 }
164
165 return(len);
166}
167
168/****************************************************************//**
169A simple sprintf replacement that dynamically allocates the space for the
170formatted string from the given heap. This supports a very limited set of
171the printf syntax: types 's' and 'u' and length modifier 'l' (which is
172required for the 'u' type).
173@return heap-allocated formatted string */
174char*
175mem_heap_printf(
176/*============*/
177 mem_heap_t* heap, /*!< in: memory heap */
178 const char* format, /*!< in: format string */
179 ...)
180{
181 va_list ap;
182 char* str;
183 ulint len;
184
185 /* Calculate length of string */
186 len = 0;
187 va_start(ap, format);
188 len = mem_heap_printf_low(NULL, format, ap);
189 va_end(ap);
190
191 /* Now create it for real. */
192 str = static_cast<char*>(mem_heap_alloc(heap, len));
193 va_start(ap, format);
194 mem_heap_printf_low(str, format, ap);
195 va_end(ap);
196
197 return(str);
198}
199
200#ifdef UNIV_DEBUG
201/** Validates the contents of a memory heap.
202Checks a memory heap for consistency, prints the contents if any error
203is detected. A fatal error is logged if an error is detected.
204@param[in] heap Memory heap to validate. */
205void
206mem_heap_validate(
207 const mem_heap_t* heap)
208{
209 ulint size = 0;
210
211 for (const mem_block_t* block = heap;
212 block != NULL;
213 block = UT_LIST_GET_NEXT(list, block)) {
214
215 mem_block_validate(block);
216
217 switch (block->type) {
218 case MEM_HEAP_DYNAMIC:
219 break;
220 case MEM_HEAP_BUFFER:
221 case MEM_HEAP_BUFFER | MEM_HEAP_BTR_SEARCH:
222 ut_ad(block->len <= srv_page_size);
223 break;
224 default:
225 ut_error;
226 }
227
228 size += block->len;
229 }
230
231 ut_ad(size == heap->total_size);
232}
233#endif /* UNIV_DEBUG */
234
235/***************************************************************//**
236Creates a memory heap block where data can be allocated.
237@return own: memory heap block, NULL if did not succeed (only possible
238for MEM_HEAP_BTR_SEARCH type heaps) */
239mem_block_t*
240mem_heap_create_block_func(
241/*=======================*/
242 mem_heap_t* heap, /*!< in: memory heap or NULL if first block
243 should be created */
244 ulint n, /*!< in: number of bytes needed for user data */
245#ifdef UNIV_DEBUG
246 const char* file_name,/*!< in: file name where created */
247 unsigned line, /*!< in: line where created */
248#endif /* UNIV_DEBUG */
249 ulint type) /*!< in: type of heap: MEM_HEAP_DYNAMIC or
250 MEM_HEAP_BUFFER */
251{
252 buf_block_t* buf_block = NULL;
253 mem_block_t* block;
254 ulint len;
255
256 ut_ad((type == MEM_HEAP_DYNAMIC) || (type == MEM_HEAP_BUFFER)
257 || (type == MEM_HEAP_BUFFER + MEM_HEAP_BTR_SEARCH));
258
259 if (heap != NULL) {
260 mem_block_validate(heap);
261 ut_d(mem_heap_validate(heap));
262 }
263
264 /* In dynamic allocation, calculate the size: block header + data. */
265 len = MEM_BLOCK_HEADER_SIZE + MEM_SPACE_NEEDED(n);
266
267 if (type == MEM_HEAP_DYNAMIC || len < srv_page_size / 2) {
268
269 ut_ad(type == MEM_HEAP_DYNAMIC || n <= MEM_MAX_ALLOC_IN_BUF);
270
271 block = static_cast<mem_block_t*>(ut_malloc_nokey(len));
272 } else {
273 len = srv_page_size;
274
275 if ((type & MEM_HEAP_BTR_SEARCH) && heap) {
276 /* We cannot allocate the block from the
277 buffer pool, but must get the free block from
278 the heap header free block field */
279
280 buf_block = static_cast<buf_block_t*>(heap->free_block);
281 heap->free_block = NULL;
282
283 if (UNIV_UNLIKELY(!buf_block)) {
284
285 return(NULL);
286 }
287 } else {
288 buf_block = buf_block_alloc(NULL);
289 }
290
291 block = (mem_block_t*) buf_block->frame;
292 }
293
294 if (block == NULL) {
295 ib::fatal() << "Unable to allocate memory of size "
296 << len << ".";
297 }
298
299 block->buf_block = buf_block;
300 block->free_block = NULL;
301
302 block->magic_n = MEM_BLOCK_MAGIC_N;
303 ut_d(ut_strlcpy_rev(block->file_name, file_name,
304 sizeof(block->file_name)));
305 ut_d(block->line = line);
306
307 mem_block_set_len(block, len);
308 mem_block_set_type(block, type);
309 mem_block_set_free(block, MEM_BLOCK_HEADER_SIZE);
310 mem_block_set_start(block, MEM_BLOCK_HEADER_SIZE);
311
312 if (UNIV_UNLIKELY(heap == NULL)) {
313 /* This is the first block of the heap. The field
314 total_size should be initialized here */
315 block->total_size = len;
316 } else {
317 /* Not the first allocation for the heap. This block's
318 total_length field should be set to undefined. */
319 ut_d(block->total_size = ULINT_UNDEFINED);
320 UNIV_MEM_INVALID(&block->total_size,
321 sizeof block->total_size);
322
323 heap->total_size += len;
324 }
325
326 /* Poison all available memory. Individual chunks will be unpoisoned on
327 every mem_heap_alloc() call. */
328 compile_time_assert(MEM_BLOCK_HEADER_SIZE >= sizeof *block);
329 UNIV_MEM_FREE(block + 1, len - sizeof *block);
330
331 ut_ad((ulint)MEM_BLOCK_HEADER_SIZE < len);
332
333 return(block);
334}
335
336/***************************************************************//**
337Adds a new block to a memory heap.
338@return created block, NULL if did not succeed (only possible for
339MEM_HEAP_BTR_SEARCH type heaps) */
340mem_block_t*
341mem_heap_add_block(
342/*===============*/
343 mem_heap_t* heap, /*!< in: memory heap */
344 ulint n) /*!< in: number of bytes user needs */
345{
346 mem_block_t* block;
347 mem_block_t* new_block;
348 ulint new_size;
349
350 ut_d(mem_block_validate(heap));
351
352 block = UT_LIST_GET_LAST(heap->base);
353
354 /* We have to allocate a new block. The size is always at least
355 doubled until the standard size is reached. After that the size
356 stays the same, except in cases where the caller needs more space. */
357
358 new_size = 2 * mem_block_get_len(block);
359
360 if (heap->type != MEM_HEAP_DYNAMIC) {
361 /* From the buffer pool we allocate buffer frames */
362 ut_a(n <= MEM_MAX_ALLOC_IN_BUF);
363
364 if (new_size > MEM_MAX_ALLOC_IN_BUF) {
365 new_size = MEM_MAX_ALLOC_IN_BUF;
366 }
367 } else if (new_size > MEM_BLOCK_STANDARD_SIZE) {
368
369 new_size = MEM_BLOCK_STANDARD_SIZE;
370 }
371
372 if (new_size < n) {
373 new_size = n;
374 }
375
376 new_block = mem_heap_create_block(heap, new_size, heap->type,
377 heap->file_name, heap->line);
378 if (new_block == NULL) {
379
380 return(NULL);
381 }
382
383 /* Add the new block as the last block */
384
385 UT_LIST_INSERT_AFTER(heap->base, block, new_block);
386
387 return(new_block);
388}
389
390/******************************************************************//**
391Frees a block from a memory heap. */
392void
393mem_heap_block_free(
394/*================*/
395 mem_heap_t* heap, /*!< in: heap */
396 mem_block_t* block) /*!< in: block to free */
397{
398 ulint type;
399 ulint len;
400 buf_block_t* buf_block;
401
402 buf_block = static_cast<buf_block_t*>(block->buf_block);
403
404 mem_block_validate(block);
405
406 UT_LIST_REMOVE(heap->base, block);
407
408 ut_ad(heap->total_size >= block->len);
409 heap->total_size -= block->len;
410
411 type = heap->type;
412 len = block->len;
413 block->magic_n = MEM_FREED_BLOCK_MAGIC_N;
414
415 if (type == MEM_HEAP_DYNAMIC || len < srv_page_size / 2) {
416 ut_ad(!buf_block);
417 ut_free(block);
418 } else {
419 ut_ad(type & MEM_HEAP_BUFFER);
420 buf_block_free(buf_block);
421 }
422}
423
424/******************************************************************//**
425Frees the free_block field from a memory heap. */
426void
427mem_heap_free_block_free(
428/*=====================*/
429 mem_heap_t* heap) /*!< in: heap */
430{
431 if (UNIV_LIKELY_NULL(heap->free_block)) {
432
433 buf_block_free(static_cast<buf_block_t*>(heap->free_block));
434
435 heap->free_block = NULL;
436 }
437}
438