1 /*-------------------------------------------------------------------------
2  * drawElements Base Portability Library
3  * -------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Memory management.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "deMemory.h"
25 #include "deInt32.h"
26 
27 #include <stdio.h>
28 #include <assert.h>
29 #include <stdlib.h>
30 #include <string.h>
31 
32 #define DE_ALIGNED_MALLOC_POSIX		0
33 #define DE_ALIGNED_MALLOC_WIN32		1
34 #define DE_ALIGNED_MALLOC_GENERIC	2
35 
36 #if (DE_OS == DE_OS_UNIX) || ((DE_OS == DE_OS_ANDROID) && (DE_ANDROID_API >= 21))
37 #	define DE_ALIGNED_MALLOC DE_ALIGNED_MALLOC_POSIX
38 #	include <malloc.h>
39 #elif (DE_OS == DE_OS_WIN32)
40 #	define DE_ALIGNED_MALLOC DE_ALIGNED_MALLOC_WIN32
41 #	include <malloc.h>
42 #else
43 #	define DE_ALIGNED_MALLOC DE_ALIGNED_MALLOC_GENERIC
44 #endif
45 
46 #if defined(DE_VALGRIND_BUILD)
47 #	include <valgrind/valgrind.h>
48 #	if defined(HAVE_VALGRIND_MEMCHECK_H)
49 #		include <valgrind/memcheck.h>
50 #	endif
51 #endif
52 
53 DE_BEGIN_EXTERN_C
54 
55 /*--------------------------------------------------------------------*//*!
56  * \brief Allocate a chunk of memory.
57  * \param numBytes	Number of bytes to allocate.
58  * \return Pointer to the allocated memory (or null on failure).
59  *//*--------------------------------------------------------------------*/
deMalloc(size_t numBytes)60 void* deMalloc (size_t numBytes)
61 {
62 	void* ptr;
63 
64 	DE_ASSERT(numBytes > 0);
65 
66 	ptr = malloc((size_t)numBytes);
67 
68 #if defined(DE_DEBUG)
69 	/* Trash memory in debug builds (if under Valgrind, don't make it think we're initializing data here). */
70 
71 	if (ptr)
72 		memset(ptr, 0xcd, numBytes);
73 
74 #if defined(DE_VALGRIND_BUILD) && defined(HAVE_VALGRIND_MEMCHECK_H)
75 	if (ptr && RUNNING_ON_VALGRIND)
76 	{
77 		VALGRIND_MAKE_MEM_UNDEFINED(ptr, numBytes);
78 	}
79 #endif
80 #endif
81 
82 	return ptr;
83 }
84 
85 /*--------------------------------------------------------------------*//*!
86  * \brief Allocate a chunk of memory and initialize it to zero.
87  * \param numBytes	Number of bytes to allocate.
88  * \return Pointer to the allocated memory (or null on failure).
89  *//*--------------------------------------------------------------------*/
deCalloc(size_t numBytes)90 void* deCalloc (size_t numBytes)
91 {
92 	void* ptr = deMalloc(numBytes);
93 	if (ptr)
94 		deMemset(ptr, 0, numBytes);
95 	return ptr;
96 }
97 
98 /*--------------------------------------------------------------------*//*!
99  * \brief Reallocate a chunk of memory.
100  * \param ptr		Pointer to previously allocated memory block
101  * \param numBytes	New size in bytes
102  * \return Pointer to the reallocated (and possibly moved) memory block
103  *//*--------------------------------------------------------------------*/
deRealloc(void * ptr,size_t numBytes)104 void* deRealloc (void* ptr, size_t numBytes)
105 {
106 	return realloc(ptr, numBytes);
107 }
108 
109 /*--------------------------------------------------------------------*//*!
110  * \brief Free a chunk of memory.
111  * \param ptr	Pointer to memory to free.
112  *//*--------------------------------------------------------------------*/
deFree(void * ptr)113 void deFree (void* ptr)
114 {
115 	free(ptr);
116 }
117 
118 #if (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_GENERIC)
119 
120 typedef struct AlignedAllocHeader_s
121 {
122 	void*	basePtr;
123 	size_t	numBytes;
124 } AlignedAllocHeader;
125 
getAlignedAllocHeader(void * ptr)126 DE_INLINE AlignedAllocHeader* getAlignedAllocHeader (void* ptr)
127 {
128 	const size_t	hdrSize		= sizeof(AlignedAllocHeader);
129 	const deUintptr	hdrAddr		= (deUintptr)ptr - hdrSize;
130 
131 	return (AlignedAllocHeader*)hdrAddr;
132 }
133 
134 #endif
135 
deAlignedMalloc(size_t numBytes,size_t alignBytes)136 void* deAlignedMalloc (size_t numBytes, size_t alignBytes)
137 {
138 #if (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_POSIX)
139 	/* posix_memalign() requires that alignment must be 2^N * sizeof(void*) */
140 	const size_t	ptrAlignedAlign	= deAlignSize(alignBytes, sizeof(void*));
141 	void*			ptr				= DE_NULL;
142 
143 	DE_ASSERT(deIsPowerOfTwoSize(alignBytes) && deIsPowerOfTwoSize(ptrAlignedAlign / sizeof(void*)));
144 
145 	if (posix_memalign(&ptr, ptrAlignedAlign, numBytes) == 0)
146 	{
147 		DE_ASSERT(ptr);
148 		return ptr;
149 	}
150 	else
151 	{
152 		DE_ASSERT(!ptr);
153 		return DE_NULL;
154 	}
155 
156 #elif (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_WIN32)
157 	DE_ASSERT(deIsPowerOfTwoSize(alignBytes));
158 
159 	return _aligned_malloc(numBytes, alignBytes);
160 
161 #elif (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_GENERIC)
162 	void* const	basePtr	= deMalloc(numBytes + alignBytes + sizeof(AlignedAllocHeader));
163 
164 	DE_ASSERT(deIsPowerOfTwoSize(alignBytes));
165 
166 	if (basePtr)
167 	{
168 		void* const					alignedPtr	= deAlignPtr((void*)((deUintptr)basePtr + sizeof(AlignedAllocHeader)), alignBytes);
169 		AlignedAllocHeader* const	hdr			= getAlignedAllocHeader(alignedPtr);
170 
171 		hdr->basePtr	= basePtr;
172 		hdr->numBytes	= numBytes;
173 
174 		return alignedPtr;
175 	}
176 	else
177 		return DE_NULL;
178 #else
179 #	error "Invalid DE_ALIGNED_MALLOC"
180 #endif
181 }
182 
deAlignedRealloc(void * ptr,size_t numBytes,size_t alignBytes)183 void* deAlignedRealloc (void* ptr, size_t numBytes, size_t alignBytes)
184 {
185 #if (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_WIN32)
186 	return _aligned_realloc(ptr, numBytes, alignBytes);
187 
188 #elif (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_GENERIC) || (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_POSIX)
189 	if (ptr)
190 	{
191 		if (numBytes > 0)
192 		{
193 #	if (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_GENERIC)
194 			const size_t				oldSize	= getAlignedAllocHeader(ptr)->numBytes;
195 #	else /* DE_ALIGNED_MALLOC_GENERIC */
196 			const size_t				oldSize	= malloc_usable_size(ptr);
197 #	endif
198 
199 			DE_ASSERT(deIsAlignedPtr(ptr, alignBytes));
200 
201 			if (oldSize < numBytes || oldSize > numBytes*2)
202 			{
203 				/* Create a new alloc if original is smaller, or more than twice the requested size */
204 				void* const	newPtr	= deAlignedMalloc(numBytes, alignBytes);
205 
206 				if (newPtr)
207 				{
208 					const size_t	copyBytes	= numBytes < oldSize ? numBytes : oldSize;
209 
210 					deMemcpy(newPtr, ptr, copyBytes);
211 					deAlignedFree(ptr);
212 
213 					return newPtr;
214 				}
215 				else
216 					return DE_NULL;
217 			}
218 			else
219 				return ptr;
220 		}
221 		else
222 		{
223 			deAlignedFree(ptr);
224 			return DE_NULL;
225 		}
226 	}
227 	else
228 		return deAlignedMalloc(numBytes, alignBytes);
229 
230 #else
231 #	error "Invalid DE_ALIGNED_MALLOC"
232 #endif
233 }
234 
deAlignedFree(void * ptr)235 void deAlignedFree (void* ptr)
236 {
237 #if (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_POSIX)
238 	free(ptr);
239 
240 #elif (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_WIN32)
241 	_aligned_free(ptr);
242 
243 #elif (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_GENERIC)
244 	if (ptr)
245 	{
246 		AlignedAllocHeader* const	hdr	= getAlignedAllocHeader(ptr);
247 
248 		deFree(hdr->basePtr);
249 	}
250 #else
251 #	error "Invalid DE_ALIGNED_MALLOC"
252 #endif
253 }
254 
deStrdup(const char * str)255 char* deStrdup (const char* str)
256 {
257 #if (DE_COMPILER == DE_COMPILER_MSC)
258 	return _strdup(str);
259 #elif (DE_OS == DE_OS_OSX) || (DE_OS == DE_OS_IOS)
260 	/* For some reason Steve doesn't like stdrup(). */
261 	size_t	len		= strlen(str);
262 	char*	copy	= malloc(len+1);
263 	memcpy(copy, str, len);
264 	copy[len] = 0;
265 	return copy;
266 #else
267 	return strdup(str);
268 #endif
269 }
270 
deMemory_selfTest(void)271 void deMemory_selfTest (void)
272 {
273 	static const struct
274 	{
275 		size_t		numBytes;
276 		size_t		alignment;
277 	} s_alignedAllocCases[] =
278 	{
279 		{ 1,		1		},
280 		{ 1,		2		},
281 		{ 1,		256		},
282 		{ 1,		4096	},
283 		{ 547389,	1		},
284 		{ 547389,	2		},
285 		{ 547389,	256		},
286 		{ 547389,	4096	},
287 		{ 52532,	1<<4	},
288 		{ 52532,	1<<10	},
289 		{ 52532,	1<<16	},
290 	};
291 	static const struct
292 	{
293 		size_t		initialSize;
294 		size_t		newSize;
295 		size_t		alignment;
296 	} s_alignedReallocCases[] =
297 	{
298 		{ 1,		1,		1		},
299 		{ 1,		1,		2		},
300 		{ 1,		1,		256		},
301 		{ 1,		1,		4096	},
302 		{ 1,		1241,	1		},
303 		{ 1,		1241,	2		},
304 		{ 1,		1241,	256		},
305 		{ 1,		1241,	4096	},
306 		{ 547389,	234,	1		},
307 		{ 547389,	234,	2		},
308 		{ 547389,	234,	256		},
309 		{ 547389,	234,	4096	},
310 		{ 52532,	421523,	1<<4	},
311 		{ 52532,	421523,	1<<10	},
312 		{ 52532,	421523,	1<<16	},
313 	};
314 
315 	int caseNdx;
316 
317 	for (caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(s_alignedAllocCases); caseNdx++)
318 	{
319 		void* const		ptr		= deAlignedMalloc(s_alignedAllocCases[caseNdx].numBytes, s_alignedAllocCases[caseNdx].alignment);
320 
321 		DE_TEST_ASSERT(ptr);
322 		DE_TEST_ASSERT(deIsAlignedPtr(ptr, s_alignedAllocCases[caseNdx].alignment));
323 
324 		deMemset(ptr, 0xaa, s_alignedAllocCases[caseNdx].numBytes);
325 
326 		deAlignedFree(ptr);
327 	}
328 
329 	for (caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(s_alignedReallocCases); caseNdx++)
330 	{
331 		void* const		ptr		= deAlignedMalloc(s_alignedReallocCases[caseNdx].initialSize, s_alignedReallocCases[caseNdx].alignment);
332 
333 		DE_TEST_ASSERT(ptr);
334 		DE_TEST_ASSERT(deIsAlignedPtr(ptr, s_alignedReallocCases[caseNdx].alignment));
335 
336 		deMemset(ptr, 0xaa, s_alignedReallocCases[caseNdx].initialSize);
337 
338 		{
339 			void* const		newPtr			= deAlignedRealloc(ptr, s_alignedReallocCases[caseNdx].newSize, s_alignedReallocCases[caseNdx].alignment);
340 			const size_t	numPreserved	= s_alignedReallocCases[caseNdx].newSize < s_alignedReallocCases[caseNdx].initialSize
341 											? s_alignedReallocCases[caseNdx].newSize
342 											: s_alignedReallocCases[caseNdx].initialSize;
343 			size_t			off;
344 
345 			DE_TEST_ASSERT(newPtr);
346 			DE_TEST_ASSERT(deIsAlignedPtr(ptr, s_alignedReallocCases[caseNdx].alignment));
347 
348 			for (off = 0; off < numPreserved; off++)
349 				DE_TEST_ASSERT(*((const deUint8*)newPtr + off) == 0xaa);
350 
351 			deAlignedFree(newPtr);
352 		}
353 	}
354 }
355 
356 DE_END_EXTERN_C
357