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