/*------------------------------------------------------------------------- * drawElements Base Portability Library * ------------------------------------- * * Copyright 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Memory management. *//*--------------------------------------------------------------------*/ #include "deMemory.h" #include "deInt32.h" #include <stdio.h> #include <assert.h> #include <stdlib.h> #include <string.h> #define DE_ALIGNED_MALLOC_POSIX 0 #define DE_ALIGNED_MALLOC_WIN32 1 #define DE_ALIGNED_MALLOC_GENERIC 2 #if (DE_OS == DE_OS_UNIX) || ((DE_OS == DE_OS_ANDROID) && (DE_ANDROID_API >= 21)) # define DE_ALIGNED_MALLOC DE_ALIGNED_MALLOC_POSIX # include <malloc.h> #elif (DE_OS == DE_OS_WIN32) # define DE_ALIGNED_MALLOC DE_ALIGNED_MALLOC_WIN32 # include <malloc.h> #else # define DE_ALIGNED_MALLOC DE_ALIGNED_MALLOC_GENERIC #endif #if defined(DE_VALGRIND_BUILD) # include <valgrind/valgrind.h> # if defined(HAVE_VALGRIND_MEMCHECK_H) # include <valgrind/memcheck.h> # endif #endif DE_BEGIN_EXTERN_C /*--------------------------------------------------------------------*//*! * \brief Allocate a chunk of memory. * \param numBytes Number of bytes to allocate. * \return Pointer to the allocated memory (or null on failure). *//*--------------------------------------------------------------------*/ void* deMalloc (size_t numBytes) { void* ptr; DE_ASSERT(numBytes > 0); ptr = malloc((size_t)numBytes); #if defined(DE_DEBUG) /* Trash memory in debug builds (if under Valgrind, don't make it think we're initializing data here). */ if (ptr) memset(ptr, 0xcd, numBytes); #if defined(DE_VALGRIND_BUILD) && defined(HAVE_VALGRIND_MEMCHECK_H) if (ptr && RUNNING_ON_VALGRIND) { VALGRIND_MAKE_MEM_UNDEFINED(ptr, numBytes); } #endif #endif return ptr; } /*--------------------------------------------------------------------*//*! * \brief Allocate a chunk of memory and initialize it to zero. * \param numBytes Number of bytes to allocate. * \return Pointer to the allocated memory (or null on failure). *//*--------------------------------------------------------------------*/ void* deCalloc (size_t numBytes) { void* ptr = deMalloc(numBytes); if (ptr) deMemset(ptr, 0, numBytes); return ptr; } /*--------------------------------------------------------------------*//*! * \brief Reallocate a chunk of memory. * \param ptr Pointer to previously allocated memory block * \param numBytes New size in bytes * \return Pointer to the reallocated (and possibly moved) memory block *//*--------------------------------------------------------------------*/ void* deRealloc (void* ptr, size_t numBytes) { return realloc(ptr, numBytes); } /*--------------------------------------------------------------------*//*! * \brief Free a chunk of memory. * \param ptr Pointer to memory to free. *//*--------------------------------------------------------------------*/ void deFree (void* ptr) { free(ptr); } #if (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_GENERIC) typedef struct AlignedAllocHeader_s { void* basePtr; size_t numBytes; } AlignedAllocHeader; DE_INLINE AlignedAllocHeader* getAlignedAllocHeader (void* ptr) { const size_t hdrSize = sizeof(AlignedAllocHeader); const deUintptr hdrAddr = (deUintptr)ptr - hdrSize; return (AlignedAllocHeader*)hdrAddr; } #endif void* deAlignedMalloc (size_t numBytes, size_t alignBytes) { #if (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_POSIX) /* posix_memalign() requires that alignment must be 2^N * sizeof(void*) */ const size_t ptrAlignedAlign = deAlignSize(alignBytes, sizeof(void*)); void* ptr = DE_NULL; DE_ASSERT(deIsPowerOfTwoSize(alignBytes) && deIsPowerOfTwoSize(ptrAlignedAlign / sizeof(void*))); if (posix_memalign(&ptr, ptrAlignedAlign, numBytes) == 0) { DE_ASSERT(ptr); return ptr; } else { DE_ASSERT(!ptr); return DE_NULL; } #elif (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_WIN32) DE_ASSERT(deIsPowerOfTwoSize(alignBytes)); return _aligned_malloc(numBytes, alignBytes); #elif (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_GENERIC) void* const basePtr = deMalloc(numBytes + alignBytes + sizeof(AlignedAllocHeader)); DE_ASSERT(deIsPowerOfTwoSize(alignBytes)); if (basePtr) { void* const alignedPtr = deAlignPtr((void*)((deUintptr)basePtr + sizeof(AlignedAllocHeader)), alignBytes); AlignedAllocHeader* const hdr = getAlignedAllocHeader(alignedPtr); hdr->basePtr = basePtr; hdr->numBytes = numBytes; return alignedPtr; } else return DE_NULL; #else # error "Invalid DE_ALIGNED_MALLOC" #endif } void* deAlignedRealloc (void* ptr, size_t numBytes, size_t alignBytes) { #if (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_WIN32) return _aligned_realloc(ptr, numBytes, alignBytes); #elif (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_GENERIC) || (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_POSIX) if (ptr) { if (numBytes > 0) { # if (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_GENERIC) const size_t oldSize = getAlignedAllocHeader(ptr)->numBytes; # else /* DE_ALIGNED_MALLOC_GENERIC */ const size_t oldSize = malloc_usable_size(ptr); # endif DE_ASSERT(deIsAlignedPtr(ptr, alignBytes)); if (oldSize < numBytes || oldSize > numBytes*2) { /* Create a new alloc if original is smaller, or more than twice the requested size */ void* const newPtr = deAlignedMalloc(numBytes, alignBytes); if (newPtr) { const size_t copyBytes = numBytes < oldSize ? numBytes : oldSize; deMemcpy(newPtr, ptr, copyBytes); deAlignedFree(ptr); return newPtr; } else return DE_NULL; } else return ptr; } else { deAlignedFree(ptr); return DE_NULL; } } else return deAlignedMalloc(numBytes, alignBytes); #else # error "Invalid DE_ALIGNED_MALLOC" #endif } void deAlignedFree (void* ptr) { #if (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_POSIX) free(ptr); #elif (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_WIN32) _aligned_free(ptr); #elif (DE_ALIGNED_MALLOC == DE_ALIGNED_MALLOC_GENERIC) if (ptr) { AlignedAllocHeader* const hdr = getAlignedAllocHeader(ptr); deFree(hdr->basePtr); } #else # error "Invalid DE_ALIGNED_MALLOC" #endif } char* deStrdup (const char* str) { #if (DE_COMPILER == DE_COMPILER_MSC) return _strdup(str); #elif (DE_OS == DE_OS_OSX) || (DE_OS == DE_OS_IOS) /* For some reason Steve doesn't like stdrup(). */ size_t len = strlen(str); char* copy = malloc(len+1); memcpy(copy, str, len); copy[len] = 0; return copy; #else return strdup(str); #endif } void deMemory_selfTest (void) { static const struct { size_t numBytes; size_t alignment; } s_alignedAllocCases[] = { { 1, 1 }, { 1, 2 }, { 1, 256 }, { 1, 4096 }, { 547389, 1 }, { 547389, 2 }, { 547389, 256 }, { 547389, 4096 }, { 52532, 1<<4 }, { 52532, 1<<10 }, { 52532, 1<<16 }, }; static const struct { size_t initialSize; size_t newSize; size_t alignment; } s_alignedReallocCases[] = { { 1, 1, 1 }, { 1, 1, 2 }, { 1, 1, 256 }, { 1, 1, 4096 }, { 1, 1241, 1 }, { 1, 1241, 2 }, { 1, 1241, 256 }, { 1, 1241, 4096 }, { 547389, 234, 1 }, { 547389, 234, 2 }, { 547389, 234, 256 }, { 547389, 234, 4096 }, { 52532, 421523, 1<<4 }, { 52532, 421523, 1<<10 }, { 52532, 421523, 1<<16 }, }; int caseNdx; for (caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(s_alignedAllocCases); caseNdx++) { void* const ptr = deAlignedMalloc(s_alignedAllocCases[caseNdx].numBytes, s_alignedAllocCases[caseNdx].alignment); DE_TEST_ASSERT(ptr); DE_TEST_ASSERT(deIsAlignedPtr(ptr, s_alignedAllocCases[caseNdx].alignment)); deMemset(ptr, 0xaa, s_alignedAllocCases[caseNdx].numBytes); deAlignedFree(ptr); } for (caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(s_alignedReallocCases); caseNdx++) { void* const ptr = deAlignedMalloc(s_alignedReallocCases[caseNdx].initialSize, s_alignedReallocCases[caseNdx].alignment); DE_TEST_ASSERT(ptr); DE_TEST_ASSERT(deIsAlignedPtr(ptr, s_alignedReallocCases[caseNdx].alignment)); deMemset(ptr, 0xaa, s_alignedReallocCases[caseNdx].initialSize); { void* const newPtr = deAlignedRealloc(ptr, s_alignedReallocCases[caseNdx].newSize, s_alignedReallocCases[caseNdx].alignment); const size_t numPreserved = s_alignedReallocCases[caseNdx].newSize < s_alignedReallocCases[caseNdx].initialSize ? s_alignedReallocCases[caseNdx].newSize : s_alignedReallocCases[caseNdx].initialSize; size_t off; DE_TEST_ASSERT(newPtr); DE_TEST_ASSERT(deIsAlignedPtr(ptr, s_alignedReallocCases[caseNdx].alignment)); for (off = 0; off < numPreserved; off++) DE_TEST_ASSERT(*((const deUint8*)newPtr + off) == 0xaa); deAlignedFree(newPtr); } } } DE_END_EXTERN_C