1 /*
2    Copyright (c) 2000, 2013, Oracle and/or its affiliates
3    Copyright (c) 2009, 2014, SkySQL Ab
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; version 2 of the License.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
17 
18 #include "mysys_priv.h"
19 #include "mysys_err.h"
20 #include <m_string.h>
21 
22 /* If we have our own safemalloc (for debugging) */
23 #if defined(SAFEMALLOC)
24 #define MALLOC_SIZE_AND_FLAG(p,b) sf_malloc_usable_size(p,b)
25 #define MALLOC_PREFIX_SIZE 0
26 #define MALLOC_STORE_SIZE(a,b,c,d)
27 #define MALLOC_FIX_POINTER_FOR_FREE(a) a
28 #else
29 /*
30  *   We use double as prefix size as this guarantees the correct
31  *   alignment on all platforms and will optimize things for
32  *   memcpy(), memcmp() etc.
33  */
34 #define MALLOC_PREFIX_SIZE (sizeof(double))
35 #define MALLOC_SIZE(p) (*(size_t*) ((char*)(p) - MALLOC_PREFIX_SIZE))
36 #define MALLOC_STORE_SIZE(p, type_of_p, size, flag)      \
37 {\
38   *(size_t*) p= (size) | (flag);    \
39   (p)= (type_of_p) (((char*) (p)) + MALLOC_PREFIX_SIZE); \
40 }
malloc_size_and_flag(void * p,my_bool * is_thread_specific)41 static inline size_t malloc_size_and_flag(void *p, my_bool *is_thread_specific)
42 {
43   size_t size= MALLOC_SIZE(p);
44   *is_thread_specific= (size & 1);
45   return size & ~ (ulonglong) 1;
46 }
47 #define MALLOC_SIZE_AND_FLAG(p,b) malloc_size_and_flag(p, b);
48 #define MALLOC_FIX_POINTER_FOR_FREE(p) (((char*) (p)) - MALLOC_PREFIX_SIZE)
49 #endif /* SAFEMALLOC */
50 
51 
52 /**
53   Inform application that memory usage has changed
54 
55   @param size	Size of memory segment allocated or freed
56   @param flag   1 if thread specific (allocated by MY_THREAD_SPECIFIC),
57                 0 if system specific.
58 
59   The type os size is long long, to be able to handle negative numbers to
60   decrement the memory usage
61 
62   @return 0 - ok
63           1 - failure, abort the allocation
64 */
dummy(long long size,my_bool is_thread_specific)65 static void dummy(long long size __attribute__((unused)),
66                   my_bool is_thread_specific __attribute__((unused)))
67 {}
68 
69 static MALLOC_SIZE_CB update_malloc_size= dummy;
70 
set_malloc_size_cb(MALLOC_SIZE_CB func)71 void set_malloc_size_cb(MALLOC_SIZE_CB func)
72 {
73   update_malloc_size= func ? func : dummy;
74 }
75 
76 
77 /**
78   Allocate a sized block of memory.
79 
80   @param size   The size of the memory block in bytes.
81   @param flags  Failure action modifiers (bitmasks).
82 
83   @return A pointer to the allocated memory block, or NULL on failure.
84 */
my_malloc(size_t size,myf my_flags)85 void *my_malloc(size_t size, myf my_flags)
86 {
87   void* point;
88   DBUG_ENTER("my_malloc");
89   DBUG_PRINT("my",("size: %lu  my_flags: %lu", (ulong) size, my_flags));
90   compile_time_assert(sizeof(size_t) <= sizeof(double));
91 
92   if (!(my_flags & (MY_WME | MY_FAE)))
93     my_flags|= my_global_flags;
94 
95   /* Safety */
96   if (!size)
97     size=1;
98 
99   /* We have to align size to be able to store markers in it */
100   size= ALIGN_SIZE(size);
101   point= sf_malloc(size + MALLOC_PREFIX_SIZE, my_flags);
102 
103   if (point == NULL)
104   {
105     my_errno=errno;
106     if (my_flags & MY_FAE)
107       error_handler_hook=fatal_error_handler_hook;
108     if (my_flags & (MY_FAE+MY_WME))
109       my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_ERROR_LOG+ME_FATAL),size);
110     if (my_flags & MY_FAE)
111       abort();
112   }
113   else
114   {
115     MALLOC_STORE_SIZE(point, void*, size,
116                       MY_TEST(my_flags & MY_THREAD_SPECIFIC));
117     update_malloc_size(size + MALLOC_PREFIX_SIZE,
118                        MY_TEST(my_flags & MY_THREAD_SPECIFIC));
119     TRASH_ALLOC(point, size);
120     DBUG_EXECUTE_IF("simulate_out_of_memory",
121                     {
122                       /* my_free() handles memory accounting */
123                       my_free(point);
124                       point= NULL;
125                     });
126     if (my_flags & MY_ZEROFILL)
127       bzero(point, size);
128   }
129   DBUG_PRINT("exit",("ptr: %p", point));
130   DBUG_RETURN(point);
131 }
132 
133 
134 /**
135    @brief wrapper around realloc()
136 
137    @param  oldpoint        pointer to currently allocated area
138    @param  size            new size requested, must be >0
139    @param  my_flags        flags
140 
141    @note if size==0 realloc() may return NULL; my_realloc() treats this as an
142    error which is not the intention of realloc()
143 */
my_realloc(void * oldpoint,size_t size,myf my_flags)144 void *my_realloc(void *oldpoint, size_t size, myf my_flags)
145 {
146   void *point;
147   size_t old_size;
148   my_bool old_flags;
149   DBUG_ENTER("my_realloc");
150   DBUG_PRINT("my",("ptr: %p  size: %lu  my_flags: %lu", oldpoint,
151                    (ulong) size, my_flags));
152 
153   DBUG_ASSERT(size > 0);
154   if (!oldpoint && (my_flags & MY_ALLOW_ZERO_PTR))
155     DBUG_RETURN(my_malloc(size, my_flags));
156 
157   size= ALIGN_SIZE(size);
158   old_size= MALLOC_SIZE_AND_FLAG(oldpoint, &old_flags);
159   /*
160     Test that the new and old area are the same, if not MY_THREAD_MOVE is
161     given
162   */
163   DBUG_ASSERT((MY_TEST(my_flags & MY_THREAD_SPECIFIC) == old_flags) ||
164               (my_flags & MY_THREAD_MOVE));
165   if ((point= sf_realloc(MALLOC_FIX_POINTER_FOR_FREE(oldpoint),
166                          size + MALLOC_PREFIX_SIZE, my_flags)) == NULL)
167   {
168     if (my_flags & MY_FREE_ON_ERROR)
169     {
170       /* my_free will take care of size accounting */
171       my_free(oldpoint);
172       oldpoint= 0;
173     }
174     if (my_flags & MY_HOLD_ON_ERROR)
175       DBUG_RETURN(oldpoint);
176     my_errno=errno;
177     if (my_flags & (MY_FAE+MY_WME))
178       my_error(EE_OUTOFMEMORY, MYF(ME_BELL + ME_FATAL), size);
179   }
180   else
181   {
182     MALLOC_STORE_SIZE(point, void*, size,
183                       MY_TEST(my_flags & MY_THREAD_SPECIFIC));
184     if (MY_TEST(my_flags & MY_THREAD_SPECIFIC) != old_flags)
185     {
186       /* memory moved between system and thread specific */
187       update_malloc_size(-(longlong) old_size - MALLOC_PREFIX_SIZE, old_flags);
188       update_malloc_size((longlong) size + MALLOC_PREFIX_SIZE,
189                          MY_TEST(my_flags & MY_THREAD_SPECIFIC));
190     }
191     else
192       update_malloc_size((longlong)size - (longlong)old_size, old_flags);
193   }
194 
195   DBUG_PRINT("exit",("ptr: %p", point));
196   DBUG_RETURN(point);
197 }
198 
199 
200 /**
201   Free memory allocated with my_malloc.
202 
203   @param ptr Pointer to the memory allocated by my_malloc.
204 */
my_free(void * ptr)205 void my_free(void *ptr)
206 {
207   DBUG_ENTER("my_free");
208   DBUG_PRINT("my",("ptr: %p", ptr));
209   if (ptr)
210   {
211     size_t old_size;
212     my_bool old_flags;
213     old_size= MALLOC_SIZE_AND_FLAG(ptr, &old_flags);
214     update_malloc_size(- (longlong) old_size - MALLOC_PREFIX_SIZE, old_flags);
215 #ifndef SAFEMALLOC
216     /*
217       Trash memory if not safemalloc. We don't have to do this if safemalloc
218       is used as safemalloc will also do trashing
219     */
220     TRASH_FREE(ptr, old_size);
221 #endif
222     sf_free(MALLOC_FIX_POINTER_FOR_FREE(ptr));
223   }
224   DBUG_VOID_RETURN;
225 }
226 
227 
my_memdup(const void * from,size_t length,myf my_flags)228 void *my_memdup(const void *from, size_t length, myf my_flags)
229 {
230   void *ptr;
231   DBUG_ENTER("my_memdup");
232 
233   if ((ptr= my_malloc(length,my_flags)) != 0)
234     memcpy(ptr, from, length);
235   DBUG_RETURN(ptr);
236 }
237 
238 
my_strdup(const char * from,myf my_flags)239 char *my_strdup(const char *from, myf my_flags)
240 {
241   char *ptr;
242   size_t length= strlen(from)+1;
243   DBUG_ENTER("my_strdup");
244 
245   if ((ptr= (char*) my_malloc(length, my_flags)))
246     memcpy(ptr, from, length);
247   DBUG_RETURN(ptr);
248 }
249 
250 
my_strndup(const char * from,size_t length,myf my_flags)251 char *my_strndup(const char *from, size_t length, myf my_flags)
252 {
253   char *ptr;
254   DBUG_ENTER("my_strndup");
255 
256   if ((ptr= (char*) my_malloc(length+1, my_flags)))
257   {
258     memcpy(ptr, from, length);
259     ptr[length]= 0;
260   }
261   DBUG_RETURN(ptr);
262 }
263 
264