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