1 /* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
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 #undef EXTRA_DEBUG
34 #define EXTRA_DEBUG
35
36
37 /*
38 Initialize memory root
39
40 SYNOPSIS
41 init_alloc_root()
42 mem_root - memory root to initialize
43 block_size - size of chunks (blocks) used for memory allocation
44 (It is external size of chunk i.e. it should include
45 memory required for internal structures, thus it
46 should be no less than ALLOC_ROOT_MIN_BLOCK_SIZE)
47 pre_alloc_size - if non-0, then size of block that should be
48 pre-allocated during memory root initialization.
49
50 DESCRIPTION
51 This function prepares memory root for further use, sets initial size of
52 chunk for memory allocation and pre-allocates first block if specified.
53 Altough error can happen during execution of this function if
54 pre_alloc_size is non-0 it won't be reported. Instead it will be
55 reported as error in first alloc_root() on this memory root.
56 */
57
init_alloc_root(MEM_ROOT * mem_root,size_t block_size,size_t pre_alloc_size MY_ATTRIBUTE ((unused)))58 void init_alloc_root(MEM_ROOT *mem_root, size_t block_size,
59 size_t pre_alloc_size MY_ATTRIBUTE((unused)))
60 {
61 DBUG_ENTER("init_alloc_root");
62 DBUG_PRINT("enter",("root: 0x%lx", (long) mem_root));
63
64 mem_root->free= mem_root->used= mem_root->pre_alloc= 0;
65 mem_root->min_malloc= 32;
66 mem_root->block_size= block_size - ALLOC_ROOT_MIN_BLOCK_SIZE;
67 mem_root->error_handler= 0;
68 mem_root->block_num= 4; /* We shift this with >>2 */
69 mem_root->first_block_usage= 0;
70
71 #if !(defined(HAVE_purify) && defined(EXTRA_DEBUG))
72 if (pre_alloc_size)
73 {
74 if ((mem_root->free= mem_root->pre_alloc=
75 (USED_MEM*) my_malloc(pre_alloc_size+ ALIGN_SIZE(sizeof(USED_MEM)),
76 MYF(0))))
77 {
78 mem_root->free->size= pre_alloc_size+ALIGN_SIZE(sizeof(USED_MEM));
79 mem_root->free->left= pre_alloc_size;
80 mem_root->free->next= 0;
81 }
82 }
83 #endif
84 DBUG_VOID_RETURN;
85 }
86
87 /** This is a no-op unless the build is debug or for Valgrind. */
88 #define TRASH_MEM(X) TRASH(((char*)(X) + ((X)->size-(X)->left)), (X)->left)
89
90
91 /*
92 SYNOPSIS
93 reset_root_defaults()
94 mem_root memory root to change defaults of
95 block_size new value of block size. Must be greater or equal
96 than ALLOC_ROOT_MIN_BLOCK_SIZE (this value is about
97 68 bytes and depends on platform and compilation flags)
98 pre_alloc_size new size of preallocated block. If not zero,
99 must be equal to or greater than block size,
100 otherwise means 'no prealloc'.
101 DESCRIPTION
102 Function aligns and assigns new value to block size; then it tries to
103 reuse one of existing blocks as prealloc block, or malloc new one of
104 requested size. If no blocks can be reused, all unused blocks are freed
105 before allocation.
106 */
107
reset_root_defaults(MEM_ROOT * mem_root,size_t block_size,size_t pre_alloc_size MY_ATTRIBUTE ((unused)))108 void reset_root_defaults(MEM_ROOT *mem_root, size_t block_size,
109 size_t pre_alloc_size MY_ATTRIBUTE((unused)))
110 {
111 DBUG_ASSERT(alloc_root_inited(mem_root));
112
113 mem_root->block_size= block_size - ALLOC_ROOT_MIN_BLOCK_SIZE;
114 #if !(defined(HAVE_purify) && defined(EXTRA_DEBUG))
115 if (pre_alloc_size)
116 {
117 size_t size= pre_alloc_size + ALIGN_SIZE(sizeof(USED_MEM));
118 if (!mem_root->pre_alloc || mem_root->pre_alloc->size != size)
119 {
120 USED_MEM *mem, **prev= &mem_root->free;
121 /*
122 Free unused blocks, so that consequent calls
123 to reset_root_defaults won't eat away memory.
124 */
125 while (*prev)
126 {
127 mem= *prev;
128 if (mem->size == size)
129 {
130 /* We found a suitable block, no need to do anything else */
131 mem_root->pre_alloc= mem;
132 return;
133 }
134 if (mem->left + ALIGN_SIZE(sizeof(USED_MEM)) == mem->size)
135 {
136 /* remove block from the list and free it */
137 *prev= mem->next;
138 {
139 mem->left= mem->size;
140 TRASH_MEM(mem);
141 my_free(mem);
142 }
143 }
144 else
145 prev= &mem->next;
146 }
147 /* Allocate new prealloc block and add it to the end of free list */
148 if ((mem= (USED_MEM *) my_malloc(size, MYF(0))))
149 {
150 mem->size= size;
151 mem->left= pre_alloc_size;
152 mem->next= *prev;
153 *prev= mem_root->pre_alloc= mem;
154 }
155 else
156 {
157 mem_root->pre_alloc= 0;
158 }
159 }
160 }
161 else
162 #endif
163 mem_root->pre_alloc= 0;
164 }
165
166
alloc_root(MEM_ROOT * mem_root,size_t length)167 void *alloc_root(MEM_ROOT *mem_root, size_t length)
168 {
169 #if defined(HAVE_purify) && defined(EXTRA_DEBUG)
170 reg1 USED_MEM *next;
171 DBUG_ENTER("alloc_root");
172 DBUG_PRINT("enter",("root: 0x%lx", (long) mem_root));
173
174 DBUG_ASSERT(alloc_root_inited(mem_root));
175
176 DBUG_EXECUTE_IF("simulate_out_of_memory",
177 {
178 if (mem_root->error_handler)
179 (*mem_root->error_handler)();
180 DBUG_SET("-d,simulate_out_of_memory");
181 DBUG_RETURN((void*) 0); /* purecov: inspected */
182 });
183
184 length+=ALIGN_SIZE(sizeof(USED_MEM));
185 if (!(next = (USED_MEM*) my_malloc(length,MYF(MY_WME | ME_FATALERROR))))
186 {
187 if (mem_root->error_handler)
188 (*mem_root->error_handler)();
189 DBUG_RETURN((uchar*) 0); /* purecov: inspected */
190 }
191 next->next= mem_root->used;
192 next->size= length;
193 mem_root->used= next;
194 DBUG_PRINT("exit",("ptr: 0x%lx", (long) (((char*) next)+
195 ALIGN_SIZE(sizeof(USED_MEM)))));
196 DBUG_RETURN((uchar*) (((char*) next)+ALIGN_SIZE(sizeof(USED_MEM))));
197 #else
198 size_t get_size, block_size;
199 uchar* point;
200 reg1 USED_MEM *next= 0;
201 reg2 USED_MEM **prev;
202 DBUG_ENTER("alloc_root");
203 DBUG_PRINT("enter",("root: 0x%lx", (long) mem_root));
204 DBUG_ASSERT(alloc_root_inited(mem_root));
205
206 DBUG_EXECUTE_IF("simulate_out_of_memory",
207 {
208 /* Avoid reusing an already allocated block */
209 if (mem_root->error_handler)
210 (*mem_root->error_handler)();
211 DBUG_SET("-d,simulate_out_of_memory");
212 DBUG_RETURN((void*) 0); /* purecov: inspected */
213 });
214 length= ALIGN_SIZE(length);
215 if ((*(prev= &mem_root->free)) != NULL)
216 {
217 if ((*prev)->left < length &&
218 mem_root->first_block_usage++ >= ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP &&
219 (*prev)->left < ALLOC_MAX_BLOCK_TO_DROP)
220 {
221 next= *prev;
222 *prev= next->next; /* Remove block from list */
223 next->next= mem_root->used;
224 mem_root->used= next;
225 mem_root->first_block_usage= 0;
226 }
227 for (next= *prev ; next && next->left < length ; next= next->next)
228 prev= &next->next;
229 }
230 if (! next)
231 { /* Time to alloc new block */
232 block_size= mem_root->block_size * (mem_root->block_num >> 2);
233 get_size= length+ALIGN_SIZE(sizeof(USED_MEM));
234 get_size= MY_MAX(get_size, block_size);
235
236 if (!(next = (USED_MEM*) my_malloc(get_size,MYF(MY_WME | ME_FATALERROR))))
237 {
238 if (mem_root->error_handler)
239 (*mem_root->error_handler)();
240 DBUG_RETURN((void*) 0); /* purecov: inspected */
241 }
242 mem_root->block_num++;
243 next->next= *prev;
244 next->size= get_size;
245 next->left= get_size-ALIGN_SIZE(sizeof(USED_MEM));
246 *prev=next;
247 }
248
249 point= (uchar*) ((char*) next+ (next->size-next->left));
250 /*TODO: next part may be unneded due to mem_root->first_block_usage counter*/
251 if ((next->left-= length) < mem_root->min_malloc)
252 { /* Full block */
253 *prev= next->next; /* Remove block from list */
254 next->next= mem_root->used;
255 mem_root->used= next;
256 mem_root->first_block_usage= 0;
257 }
258 DBUG_PRINT("exit",("ptr: 0x%lx", (ulong) point));
259 DBUG_RETURN((void*) point);
260 #endif
261 }
262
263
264 /*
265 Allocate many pointers at the same time.
266
267 DESCRIPTION
268 ptr1, ptr2, etc all point into big allocated memory area.
269
270 SYNOPSIS
271 multi_alloc_root()
272 root Memory root
273 ptr1, length1 Multiple arguments terminated by a NULL pointer
274 ptr2, length2 ...
275 ...
276 NULL
277
278 RETURN VALUE
279 A pointer to the beginning of the allocated memory block
280 in case of success or NULL if out of memory.
281 */
282
multi_alloc_root(MEM_ROOT * root,...)283 void *multi_alloc_root(MEM_ROOT *root, ...)
284 {
285 va_list args;
286 char **ptr, *start, *res;
287 size_t tot_length, length;
288 DBUG_ENTER("multi_alloc_root");
289
290 va_start(args, root);
291 tot_length= 0;
292 while ((ptr= va_arg(args, char **)))
293 {
294 length= va_arg(args, uint);
295 tot_length+= ALIGN_SIZE(length);
296 }
297 va_end(args);
298
299 if (!(start= (char*) alloc_root(root, tot_length)))
300 DBUG_RETURN(0); /* purecov: inspected */
301
302 va_start(args, root);
303 res= start;
304 while ((ptr= va_arg(args, char **)))
305 {
306 *ptr= res;
307 length= va_arg(args, uint);
308 res+= ALIGN_SIZE(length);
309 }
310 va_end(args);
311 DBUG_RETURN((void*) start);
312 }
313
314 /* Mark all data in blocks free for reusage */
315
mark_blocks_free(MEM_ROOT * root)316 static inline void mark_blocks_free(MEM_ROOT* root)
317 {
318 reg1 USED_MEM *next;
319 reg2 USED_MEM **last;
320
321 /* iterate through (partially) free blocks, mark them free */
322 last= &root->free;
323 for (next= root->free; next; next= *(last= &next->next))
324 {
325 next->left= next->size - ALIGN_SIZE(sizeof(USED_MEM));
326 TRASH_MEM(next);
327 }
328
329 /* Combine the free and the used list */
330 *last= next=root->used;
331
332 /* now go through the used blocks and mark them free */
333 for (; next; next= next->next)
334 {
335 next->left= next->size - ALIGN_SIZE(sizeof(USED_MEM));
336 TRASH_MEM(next);
337 }
338
339 /* Now everything is set; Indicate that nothing is used anymore */
340 root->used= 0;
341 root->first_block_usage= 0;
342 }
343
344
345 /*
346 Deallocate everything used by alloc_root or just move
347 used blocks to free list if called with MY_USED_TO_FREE
348
349 SYNOPSIS
350 free_root()
351 root Memory root
352 MyFlags Flags for what should be freed:
353
354 MY_MARK_BLOCKS_FREED Don't free blocks, just mark them free
355 MY_KEEP_PREALLOC If this is not set, then free also the
356 preallocated block
357
358 NOTES
359 One can call this function either with root block initialised with
360 init_alloc_root() or with a zero()-ed block.
361 It's also safe to call this multiple times with the same mem_root.
362 */
363
free_root(MEM_ROOT * root,myf MyFlags)364 void free_root(MEM_ROOT *root, myf MyFlags)
365 {
366 reg1 USED_MEM *next,*old;
367 DBUG_ENTER("free_root");
368 DBUG_PRINT("enter",("root: 0x%lx flags: %u", (long) root, (uint) MyFlags));
369
370 if (MyFlags & MY_MARK_BLOCKS_FREE)
371 {
372 mark_blocks_free(root);
373 DBUG_VOID_RETURN;
374 }
375 if (!(MyFlags & MY_KEEP_PREALLOC))
376 root->pre_alloc=0;
377
378 for (next=root->used; next ;)
379 {
380 old=next; next= next->next ;
381 if (old != root->pre_alloc)
382 {
383 old->left= old->size;
384 TRASH_MEM(old);
385 my_free(old);
386 }
387 }
388 for (next=root->free ; next ;)
389 {
390 old=next; next= next->next;
391 if (old != root->pre_alloc)
392 {
393 old->left= old->size;
394 TRASH_MEM(old);
395 my_free(old);
396 }
397 }
398 root->used=root->free=0;
399 if (root->pre_alloc)
400 {
401 root->free=root->pre_alloc;
402 root->free->left=root->pre_alloc->size-ALIGN_SIZE(sizeof(USED_MEM));
403 TRASH_MEM(root->pre_alloc);
404 root->free->next=0;
405 }
406 root->block_num= 4;
407 root->first_block_usage= 0;
408 DBUG_VOID_RETURN;
409 }
410
411 /*
412 Find block that contains an object and set the pre_alloc to it
413 */
414
set_prealloc_root(MEM_ROOT * root,char * ptr)415 void set_prealloc_root(MEM_ROOT *root, char *ptr)
416 {
417 USED_MEM *next;
418 for (next=root->used; next ; next=next->next)
419 {
420 if ((char*) next <= ptr && (char*) next + next->size > ptr)
421 {
422 root->pre_alloc=next;
423 return;
424 }
425 }
426 for (next=root->free ; next ; next=next->next)
427 {
428 if ((char*) next <= ptr && (char*) next + next->size > ptr)
429 {
430 root->pre_alloc=next;
431 return;
432 }
433 }
434 }
435
436
strdup_root(MEM_ROOT * root,const char * str)437 char *strdup_root(MEM_ROOT *root, const char *str)
438 {
439 return strmake_root(root, str, strlen(str));
440 }
441
442
strmake_root(MEM_ROOT * root,const char * str,size_t len)443 char *strmake_root(MEM_ROOT *root, const char *str, size_t len)
444 {
445 char *pos;
446 if ((pos=alloc_root(root,len+1)))
447 {
448 memcpy(pos,str,len);
449 pos[len]=0;
450 }
451 return pos;
452 }
453
454
memdup_root(MEM_ROOT * root,const void * str,size_t len)455 void *memdup_root(MEM_ROOT *root, const void *str, size_t len)
456 {
457 char *pos;
458 if ((pos=alloc_root(root,len)))
459 memcpy(pos,str,len);
460 return pos;
461 }
462