| /*------------------------------------------------------------------------- |
| * 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 |