1 /* Copyright (c) 2000, 2021, 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 also distributed 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 included with MySQL.
13
14 Without limiting anything contained in the foregoing, this file,
15 which is part of C Driver for MySQL (Connector/C), is also subject to the
16 Universal FOSS Exception, version 1.0, a copy of which can be found at
17 http://oss.oracle.com/licenses/universal-foss-exception.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License, version 2.0, for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
27
28 /* Routines to handle mallocing of results which will be freed the same time */
29
30 #include <my_global.h>
31 #include <my_sys.h>
32 #include <m_string.h>
33 #include "mysys_err.h"
34
35 static inline my_bool is_mem_available(MEM_ROOT *mem_root, size_t size);
36
37 /*
38 For instrumented code: don't preallocate memory in alloc_root().
39 This gives a lot more memory chunks, each with a red-zone around them.
40 */
41 #if !defined(HAVE_VALGRIND) && !defined(HAVE_ASAN)
42 #define PREALLOCATE_MEMORY_CHUNKS
43 #endif
44
45
46 /*
47 Initialize memory root
48
49 SYNOPSIS
50 init_alloc_root()
51 mem_root - memory root to initialize
52 block_size - size of chunks (blocks) used for memory allocation
53 (It is external size of chunk i.e. it should include
54 memory required for internal structures, thus it
55 should be no less than ALLOC_ROOT_MIN_BLOCK_SIZE)
56 pre_alloc_size - if non-0, then size of block that should be
57 pre-allocated during memory root initialization.
58
59 DESCRIPTION
60 This function prepares memory root for further use, sets initial size of
61 chunk for memory allocation and pre-allocates first block if specified.
62 Altough error can happen during execution of this function if
63 pre_alloc_size is non-0 it won't be reported. Instead it will be
64 reported as error in first alloc_root() on this memory root.
65 */
66
init_alloc_root(PSI_memory_key key,MEM_ROOT * mem_root,size_t block_size,size_t pre_alloc_size MY_ATTRIBUTE ((unused)))67 void init_alloc_root(PSI_memory_key key,
68 MEM_ROOT *mem_root, size_t block_size,
69 size_t pre_alloc_size MY_ATTRIBUTE((unused)))
70 {
71 DBUG_ENTER("init_alloc_root");
72 DBUG_PRINT("enter",("root: 0x%lx", (long) mem_root));
73
74 mem_root->free= mem_root->used= mem_root->pre_alloc= 0;
75 mem_root->min_malloc= 32;
76 mem_root->block_size= block_size - ALLOC_ROOT_MIN_BLOCK_SIZE;
77 mem_root->error_handler= 0;
78 mem_root->block_num= 4; /* We shift this with >>2 */
79 mem_root->first_block_usage= 0;
80 mem_root->m_psi_key= key;
81 mem_root->max_capacity= 0;
82 mem_root->allocated_size= 0;
83 mem_root->error_for_capacity_exceeded= FALSE;
84
85 #if defined(PREALLOCATE_MEMORY_CHUNKS)
86 if (pre_alloc_size)
87 {
88 if ((mem_root->free= mem_root->pre_alloc=
89 (USED_MEM*) my_malloc(key,
90 pre_alloc_size+ ALIGN_SIZE(sizeof(USED_MEM)),
91 MYF(0))))
92 {
93 mem_root->free->size= (uint)(pre_alloc_size+ALIGN_SIZE(sizeof(USED_MEM)));
94 mem_root->free->left= (uint)pre_alloc_size;
95 mem_root->free->next= 0;
96 mem_root->allocated_size+= pre_alloc_size+ ALIGN_SIZE(sizeof(USED_MEM));
97 }
98 }
99 #endif
100 DBUG_VOID_RETURN;
101 }
102
103 /** This is a no-op unless the build is debug or for Valgrind. */
104 #define TRASH_MEM(X) TRASH(((char*)(X) + ((X)->size-(X)->left)), (X)->left)
105
106
107 /*
108 SYNOPSIS
109 reset_root_defaults()
110 mem_root memory root to change defaults of
111 block_size new value of block size. Must be greater or equal
112 than ALLOC_ROOT_MIN_BLOCK_SIZE (this value is about
113 68 bytes and depends on platform and compilation flags)
114 pre_alloc_size new size of preallocated block. If not zero,
115 must be equal to or greater than block size,
116 otherwise means 'no prealloc'.
117 DESCRIPTION
118 Function aligns and assigns new value to block size; then it tries to
119 reuse one of existing blocks as prealloc block, or malloc new one of
120 requested size. If no blocks can be reused, all unused blocks are freed
121 before allocation.
122 */
123
reset_root_defaults(MEM_ROOT * mem_root,size_t block_size,size_t pre_alloc_size MY_ATTRIBUTE ((unused)))124 void reset_root_defaults(MEM_ROOT *mem_root, size_t block_size,
125 size_t pre_alloc_size MY_ATTRIBUTE((unused)))
126 {
127 assert(alloc_root_inited(mem_root));
128
129 mem_root->block_size= block_size - ALLOC_ROOT_MIN_BLOCK_SIZE;
130 #if defined(PREALLOCATE_MEMORY_CHUNKS)
131 if (pre_alloc_size)
132 {
133 size_t size= pre_alloc_size + ALIGN_SIZE(sizeof(USED_MEM));
134 if (!mem_root->pre_alloc || mem_root->pre_alloc->size != size)
135 {
136 USED_MEM *mem, **prev= &mem_root->free;
137 /*
138 Free unused blocks, so that consequent calls
139 to reset_root_defaults won't eat away memory.
140 */
141 while (*prev)
142 {
143 mem= *prev;
144 if (mem->size == (uint)size)
145 {
146 /* We found a suitable block, no need to do anything else */
147 mem_root->pre_alloc= mem;
148 return;
149 }
150 if (mem->left + ALIGN_SIZE(sizeof(USED_MEM)) == mem->size)
151 {
152 /* remove block from the list and free it */
153 *prev= mem->next;
154 {
155 mem->left= mem->size;
156 mem_root->allocated_size-= mem->size;
157 TRASH_MEM(mem);
158 my_free(mem);
159 }
160 }
161 else
162 prev= &mem->next;
163 }
164 /* Allocate new prealloc block and add it to the end of free list */
165 if (is_mem_available(mem_root, size) &&
166 (mem= (USED_MEM *) my_malloc(mem_root->m_psi_key,
167 size, MYF(0))))
168 {
169 mem->size= (uint)size;
170 mem->left= (uint)pre_alloc_size;
171 mem->next= *prev;
172 *prev= mem_root->pre_alloc= mem;
173 mem_root->allocated_size+= size;
174 }
175 else
176 {
177 mem_root->pre_alloc= 0;
178 }
179 }
180 }
181 else
182 #endif
183 mem_root->pre_alloc= 0;
184 }
185
186
187 /**
188 Function allocates the requested memory in the mem_root specified.
189 If max_capacity is defined for the mem_root, it only allocates
190 if the requested size can be allocated without exceeding the limit.
191 However, when error_for_capacity_exceeded is set, an error is flagged
192 (see set_error_reporting), but allocation is still performed.
193
194 @param mem_root memory root to allocate memory from
195 @length size to be allocated
196
197 @retval
198 void * Pointer to the memory thats been allocated
199 @retval
200 NULL Memory is not available.
201 */
202
alloc_root(MEM_ROOT * mem_root,size_t length)203 void *alloc_root(MEM_ROOT *mem_root, size_t length)
204 {
205 #if !defined(PREALLOCATE_MEMORY_CHUNKS)
206 USED_MEM *next;
207 DBUG_ENTER("alloc_root");
208 DBUG_PRINT("enter",("root: 0x%lx", (long) mem_root));
209
210 assert(alloc_root_inited(mem_root));
211
212 DBUG_EXECUTE_IF("simulate_out_of_memory",
213 {
214 if (mem_root->error_handler)
215 (*mem_root->error_handler)();
216 DBUG_SET("-d,simulate_out_of_memory");
217 DBUG_RETURN((void*) 0); /* purecov: inspected */
218 });
219
220 length+=ALIGN_SIZE(sizeof(USED_MEM));
221 if (!is_mem_available(mem_root, length))
222 {
223 if (mem_root->error_for_capacity_exceeded)
224 my_error(EE_CAPACITY_EXCEEDED, MYF(0),
225 (ulonglong) mem_root->max_capacity);
226 else
227 DBUG_RETURN(NULL);
228 }
229 if (!(next = (USED_MEM*) my_malloc(mem_root->m_psi_key,
230 length,MYF(MY_WME | ME_FATALERROR))))
231 {
232 if (mem_root->error_handler)
233 (*mem_root->error_handler)();
234 DBUG_RETURN((uchar*) 0); /* purecov: inspected */
235 }
236 mem_root->allocated_size+= length;
237 next->next= mem_root->used;
238 next->size= (uint)length;
239 next->left= (uint)(length - ALIGN_SIZE(sizeof(USED_MEM)));
240 mem_root->used= next;
241 DBUG_PRINT("exit",("ptr: 0x%lx", (long) (((char*) next)+
242 ALIGN_SIZE(sizeof(USED_MEM)))));
243 DBUG_RETURN((uchar*) (((char*) next)+ALIGN_SIZE(sizeof(USED_MEM))));
244 #else
245 size_t get_size, block_size;
246 uchar* point;
247 USED_MEM *next= 0;
248 USED_MEM **prev;
249 DBUG_ENTER("alloc_root");
250 DBUG_PRINT("enter",("root: 0x%lx", (long) mem_root));
251 assert(alloc_root_inited(mem_root));
252
253 DBUG_EXECUTE_IF("simulate_out_of_memory",
254 {
255 /* Avoid reusing an already allocated block */
256 if (mem_root->error_handler)
257 (*mem_root->error_handler)();
258 DBUG_SET("-d,simulate_out_of_memory");
259 DBUG_RETURN((void*) 0); /* purecov: inspected */
260 });
261 length= ALIGN_SIZE(length);
262 if ((*(prev= &mem_root->free)) != NULL)
263 {
264 if ((*prev)->left < length &&
265 mem_root->first_block_usage++ >= ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP &&
266 (*prev)->left < ALLOC_MAX_BLOCK_TO_DROP)
267 {
268 next= *prev;
269 *prev= next->next; /* Remove block from list */
270 next->next= mem_root->used;
271 mem_root->used= next;
272 mem_root->first_block_usage= 0;
273 }
274 for (next= *prev ; next && next->left < length ; next= next->next)
275 prev= &next->next;
276 }
277 if (! next)
278 { /* Time to alloc new block */
279 block_size= mem_root->block_size * (mem_root->block_num >> 2);
280 get_size= length+ALIGN_SIZE(sizeof(USED_MEM));
281 get_size= MY_MAX(get_size, block_size);
282
283 if (!is_mem_available(mem_root, get_size))
284 {
285 if (mem_root->error_for_capacity_exceeded)
286 my_error(EE_CAPACITY_EXCEEDED, MYF(0),
287 (ulonglong) mem_root->max_capacity);
288 else
289 DBUG_RETURN(NULL);
290 }
291 if (!(next = (USED_MEM*) my_malloc(mem_root->m_psi_key,
292 get_size,MYF(MY_WME | ME_FATALERROR))))
293 {
294 if (mem_root->error_handler)
295 (*mem_root->error_handler)();
296 DBUG_RETURN((void*) 0); /* purecov: inspected */
297 }
298 mem_root->allocated_size+= get_size;
299 mem_root->block_num++;
300 next->next= *prev;
301 next->size= (uint)get_size;
302 next->left= (uint)(get_size-ALIGN_SIZE(sizeof(USED_MEM)));
303 *prev=next;
304 }
305
306 point= (uchar*) ((char*) next+ (next->size-next->left));
307 /*TODO: next part may be unneded due to mem_root->first_block_usage counter*/
308 if ((next->left-= (uint)length) < mem_root->min_malloc)
309 { /* Full block */
310 *prev= next->next; /* Remove block from list */
311 next->next= mem_root->used;
312 mem_root->used= next;
313 mem_root->first_block_usage= 0;
314 }
315 DBUG_PRINT("exit",("ptr: 0x%lx", (ulong) point));
316 DBUG_RETURN((void*) point);
317 #endif
318 }
319
320
321 /*
322 Allocate many pointers at the same time.
323
324 DESCRIPTION
325 ptr1, ptr2, etc all point into big allocated memory area.
326
327 SYNOPSIS
328 multi_alloc_root()
329 root Memory root
330 ptr1, length1 Multiple arguments terminated by a NULL pointer
331 ptr2, length2 ...
332 ...
333 NULL
334
335 RETURN VALUE
336 A pointer to the beginning of the allocated memory block
337 in case of success or NULL if out of memory.
338 */
339
multi_alloc_root(MEM_ROOT * root,...)340 void *multi_alloc_root(MEM_ROOT *root, ...)
341 {
342 va_list args;
343 char **ptr, *start, *res;
344 size_t tot_length, length;
345 DBUG_ENTER("multi_alloc_root");
346
347 va_start(args, root);
348 tot_length= 0;
349 while ((ptr= va_arg(args, char **)))
350 {
351 length= va_arg(args, uint);
352 tot_length+= ALIGN_SIZE(length);
353 }
354 va_end(args);
355
356 if (!(start= (char*) alloc_root(root, tot_length)))
357 DBUG_RETURN(0); /* purecov: inspected */
358
359 va_start(args, root);
360 res= start;
361 while ((ptr= va_arg(args, char **)))
362 {
363 *ptr= res;
364 length= va_arg(args, uint);
365 res+= ALIGN_SIZE(length);
366 }
367 va_end(args);
368 DBUG_RETURN((void*) start);
369 }
370
371 /* Mark all data in blocks free for reusage */
372
mark_blocks_free(MEM_ROOT * root)373 static inline void mark_blocks_free(MEM_ROOT* root)
374 {
375 USED_MEM *next;
376 USED_MEM **last;
377
378 /* iterate through (partially) free blocks, mark them free */
379 last= &root->free;
380 for (next= root->free; next; next= *(last= &next->next))
381 {
382 next->left= next->size - (uint)ALIGN_SIZE(sizeof(USED_MEM));
383 TRASH_MEM(next);
384 }
385
386 /* Combine the free and the used list */
387 *last= next=root->used;
388
389 /* now go through the used blocks and mark them free */
390 for (; next; next= next->next)
391 {
392 next->left= next->size - (uint)ALIGN_SIZE(sizeof(USED_MEM));
393 TRASH_MEM(next);
394 }
395
396 /* Now everything is set; Indicate that nothing is used anymore */
397 root->used= 0;
398 root->first_block_usage= 0;
399 }
400
claim_root(MEM_ROOT * root)401 void claim_root(MEM_ROOT *root)
402 {
403 USED_MEM *next,*old;
404 DBUG_ENTER("claim_root");
405 DBUG_PRINT("enter",("root: 0x%lx", (long) root));
406
407 for (next=root->used; next ;)
408 {
409 old=next; next= next->next ;
410 my_claim(old);
411 }
412
413 for (next=root->free ; next ;)
414 {
415 old=next; next= next->next;
416 my_claim(old);
417 }
418
419 DBUG_VOID_RETURN;
420 }
421
422
423 /*
424 Deallocate everything used by alloc_root or just move
425 used blocks to free list if called with MY_USED_TO_FREE
426
427 SYNOPSIS
428 free_root()
429 root Memory root
430 MyFlags Flags for what should be freed:
431
432 MY_MARK_BLOCKS_FREED Don't free blocks, just mark them free
433 MY_KEEP_PREALLOC If this is not set, then free also the
434 preallocated block
435
436 NOTES
437 One can call this function either with root block initialised with
438 init_alloc_root() or with a zero()-ed block.
439 It's also safe to call this multiple times with the same mem_root.
440 */
441
free_root(MEM_ROOT * root,myf MyFlags)442 void free_root(MEM_ROOT *root, myf MyFlags)
443 {
444 USED_MEM *next,*old;
445 DBUG_ENTER("free_root");
446 DBUG_PRINT("enter",("root: 0x%lx flags: %u", (long) root, (uint) MyFlags));
447
448 if (MyFlags & MY_MARK_BLOCKS_FREE)
449 {
450 mark_blocks_free(root);
451 DBUG_VOID_RETURN;
452 }
453 if (!(MyFlags & MY_KEEP_PREALLOC))
454 root->pre_alloc=0;
455
456 for (next=root->used; next ;)
457 {
458 old=next; next= next->next ;
459 if (old != root->pre_alloc)
460 {
461 old->left= old->size;
462 TRASH_MEM(old);
463 my_free(old);
464 }
465 }
466 for (next=root->free ; next ;)
467 {
468 old=next; next= next->next;
469 if (old != root->pre_alloc)
470 {
471 old->left= old->size;
472 TRASH_MEM(old);
473 my_free(old);
474 }
475 }
476 root->used=root->free=0;
477 if (root->pre_alloc)
478 {
479 root->free=root->pre_alloc;
480 root->free->left=root->pre_alloc->size-(uint)ALIGN_SIZE(sizeof(USED_MEM));
481 root->allocated_size= root->pre_alloc->size;
482 TRASH_MEM(root->pre_alloc);
483 root->free->next=0;
484 }
485 else
486 root->allocated_size= 0;
487 root->block_num= 4;
488 root->first_block_usage= 0;
489 DBUG_VOID_RETURN;
490 }
491
492
strdup_root(MEM_ROOT * root,const char * str)493 char *strdup_root(MEM_ROOT *root, const char *str)
494 {
495 return strmake_root(root, str, strlen(str));
496 }
497
498
strmake_root(MEM_ROOT * root,const char * str,size_t len)499 char *strmake_root(MEM_ROOT *root, const char *str, size_t len)
500 {
501 char *pos;
502 if ((pos=alloc_root(root,len+1)))
503 {
504 memcpy(pos,str,len);
505 pos[len]=0;
506 }
507 return pos;
508 }
509
510
memdup_root(MEM_ROOT * root,const void * str,size_t len)511 void *memdup_root(MEM_ROOT *root, const void *str, size_t len)
512 {
513 char *pos;
514 if ((pos=alloc_root(root,len)))
515 memcpy(pos,str,len);
516 return pos;
517 }
518
519 /**
520 Check if the set max_capacity is exceeded if the requested
521 size is allocated
522
523 @param mem_root memory root to check for allocation
524 @param size requested size to be allocated
525
526 @retval
527 1 Memory is available
528 @retval
529 0 Memory is not available
530 */
is_mem_available(MEM_ROOT * mem_root,size_t size)531 static inline my_bool is_mem_available(MEM_ROOT *mem_root, size_t size)
532 {
533 if (mem_root->max_capacity)
534 {
535 if ((mem_root->allocated_size + size) > mem_root->max_capacity)
536 return 0;
537 }
538 return 1;
539 }
540
541 /**
542 set max_capacity for this mem_root. Should be called after init_alloc_root.
543 If the max_capacity specified is less than what is already pre_alloced
544 in init_alloc_root, only the future allocations are affected.
545
546 @param mem_root memory root to set the max capacity
547 @param max_value Maximum capacity this mem_root can hold
548 */
set_memroot_max_capacity(MEM_ROOT * mem_root,size_t max_value)549 void set_memroot_max_capacity(MEM_ROOT *mem_root, size_t max_value)
550 {
551 assert(alloc_root_inited(mem_root));
552 mem_root->max_capacity= max_value;
553 }
554
555 /**
556 Enable/disable error reporting for exceeding max_capacity. If error
557 reporting is enabled, an error is flagged to indicate that the capacity
558 is exceeded. However allocation will still happen for the requested memory.
559
560 @param mem_root memory root
561 @param report_eroor set to true if error should be reported
562 else set to false
563 */
set_memroot_error_reporting(MEM_ROOT * mem_root,my_bool report_error)564 void set_memroot_error_reporting(MEM_ROOT *mem_root, my_bool report_error)
565 {
566 assert(alloc_root_inited(mem_root));
567 mem_root->error_for_capacity_exceeded= report_error;
568 }
569
570