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