/* ****************************************************************************** * * Copyright (C) 2002-2012, International Business Machines * Corporation and others. All Rights Reserved. * ****************************************************************************** * * File cmemory.c ICU Heap allocation. * All ICU heap allocation, both for C and C++ new of ICU * class types, comes through these functions. * * If you have a need to replace ICU allocation, this is the * place to do it. * * Note that uprv_malloc(0) returns a non-NULL pointer, and * that a subsequent free of that pointer value is a NOP. * ****************************************************************************** */ #include "unicode/uclean.h" #include "cmemory.h" #include "putilimp.h" #include "uassert.h" #include /* uprv_malloc(0) returns a pointer to this read-only data. */ static const int32_t zeroMem[] = {0, 0, 0, 0, 0, 0}; /* Function Pointers for user-supplied heap functions */ static const void *pContext; static UMemAllocFn *pAlloc; static UMemReallocFn *pRealloc; static UMemFreeFn *pFree; /* Flag indicating whether any heap allocations have happened. * Used to prevent changing out the heap functions after allocations have been made */ static UBool gHeapInUse; #if U_DEBUG && defined(UPRV_MALLOC_COUNT) #include static int n=0; static long b=0; #endif #if U_DEBUG static char gValidMemorySink = 0; U_CAPI void uprv_checkValidMemory(const void *p, size_t n) { /* * Access the memory to ensure that it's all valid. * Load and save a computed value to try to ensure that the compiler * does not throw away the whole loop. * A thread analyzer might complain about un-mutexed access to gValidMemorySink * which is true but harmless because no one ever uses the value in gValidMemorySink. */ const char *s = (const char *)p; char c = gValidMemorySink; size_t i; U_ASSERT(p != NULL); for(i = 0; i < n; ++i) { c ^= s[i]; } gValidMemorySink = c; } #endif /* U_DEBUG */ U_CAPI void * U_EXPORT2 uprv_malloc(size_t s) { #if U_DEBUG && defined(UPRV_MALLOC_COUNT) #if 1 putchar('>'); fflush(stdout); #else fprintf(stderr,"MALLOC\t#%d\t%ul bytes\t%ul total\n", ++n,s,(b+=s)); fflush(stderr); #endif #endif if (s > 0) { gHeapInUse = TRUE; if (pAlloc) { return (*pAlloc)(pContext, s); } else { return uprv_default_malloc(s); } } else { return (void *)zeroMem; } } U_CAPI void * U_EXPORT2 uprv_realloc(void * buffer, size_t size) { #if U_DEBUG && defined(UPRV_MALLOC_COUNT) putchar('~'); fflush(stdout); #endif if (buffer == zeroMem) { return uprv_malloc(size); } else if (size == 0) { if (pFree) { (*pFree)(pContext, buffer); } else { uprv_default_free(buffer); } return (void *)zeroMem; } else { gHeapInUse = TRUE; if (pRealloc) { return (*pRealloc)(pContext, buffer, size); } else { return uprv_default_realloc(buffer, size); } } } U_CAPI void U_EXPORT2 uprv_free(void *buffer) { #if U_DEBUG && defined(UPRV_MALLOC_COUNT) putchar('<'); fflush(stdout); #endif if (buffer != zeroMem) { if (pFree) { (*pFree)(pContext, buffer); } else { uprv_default_free(buffer); } } } U_CAPI void * U_EXPORT2 uprv_calloc(size_t num, size_t size) { void *mem = NULL; size *= num; mem = uprv_malloc(size); if (mem) { uprv_memset(mem, 0, size); } return mem; } U_CAPI void U_EXPORT2 u_setMemoryFunctions(const void *context, UMemAllocFn *a, UMemReallocFn *r, UMemFreeFn *f, UErrorCode *status) { if (U_FAILURE(*status)) { return; } if (a==NULL || r==NULL || f==NULL) { *status = U_ILLEGAL_ARGUMENT_ERROR; return; } if (gHeapInUse) { *status = U_INVALID_STATE_ERROR; return; } pContext = context; pAlloc = a; pRealloc = r; pFree = f; } U_CFUNC UBool cmemory_cleanup(void) { pContext = NULL; pAlloc = NULL; pRealloc = NULL; pFree = NULL; gHeapInUse = FALSE; return TRUE; } /* * gHeapInUse * Return True if ICU has allocated any memory. * Used by u_SetMutexFunctions() and similar to verify that ICU has not * been used, that it is in a pristine initial state. */ U_CFUNC UBool cmemory_inUse() { return gHeapInUse; }