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
53DE_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 *//*--------------------------------------------------------------------*/
60void* 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 *//*--------------------------------------------------------------------*/
90void* 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 *//*--------------------------------------------------------------------*/
104void* 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 *//*--------------------------------------------------------------------*/
113void deFree (void* ptr)
114{
115	free(ptr);
116}
117
118#if (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_GENERIC)
119
120typedef struct AlignedAllocHeader_s
121{
122	void*	basePtr;
123	size_t	numBytes;
124} AlignedAllocHeader;
125
126DE_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
136void* 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
183void* 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
235void 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
255char* 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
271void 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
356DE_END_EXTERN_C
357