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