michael@0: /* michael@0: ****************************************************************************** michael@0: * michael@0: * Copyright (C) 1997-2012, International Business Machines michael@0: * Corporation and others. All Rights Reserved. michael@0: * michael@0: ****************************************************************************** michael@0: * michael@0: * File CMEMORY.H michael@0: * michael@0: * Contains stdlib.h/string.h memory functions michael@0: * michael@0: * @author Bertrand A. Damiba michael@0: * michael@0: * Modification History: michael@0: * michael@0: * Date Name Description michael@0: * 6/20/98 Bertrand Created. michael@0: * 05/03/99 stephen Changed from functions to macros. michael@0: * michael@0: ****************************************************************************** michael@0: */ michael@0: michael@0: #ifndef CMEMORY_H michael@0: #define CMEMORY_H michael@0: michael@0: #include "unicode/utypes.h" michael@0: michael@0: #include michael@0: #include michael@0: #include "unicode/localpointer.h" michael@0: michael@0: #if U_DEBUG && defined(UPRV_MALLOC_COUNT) michael@0: #include michael@0: #endif michael@0: michael@0: #if U_DEBUG michael@0: michael@0: /* michael@0: * The C++ standard requires that the source pointer for memcpy() & memmove() michael@0: * is valid, not NULL, and not at the end of an allocated memory block. michael@0: * In debug mode, we read one byte from the source point to verify that it's michael@0: * a valid, readable pointer. michael@0: */ michael@0: michael@0: U_CAPI void uprv_checkValidMemory(const void *p, size_t n); michael@0: michael@0: #define uprv_memcpy(dst, src, size) ( \ michael@0: uprv_checkValidMemory(src, 1), \ michael@0: U_STANDARD_CPP_NAMESPACE memcpy(dst, src, size)) michael@0: #define uprv_memmove(dst, src, size) ( \ michael@0: uprv_checkValidMemory(src, 1), \ michael@0: U_STANDARD_CPP_NAMESPACE memmove(dst, src, size)) michael@0: michael@0: #else michael@0: michael@0: #define uprv_memcpy(dst, src, size) U_STANDARD_CPP_NAMESPACE memcpy(dst, src, size) michael@0: #define uprv_memmove(dst, src, size) U_STANDARD_CPP_NAMESPACE memmove(dst, src, size) michael@0: michael@0: #endif /* U_DEBUG */ michael@0: michael@0: #define uprv_memset(buffer, mark, size) U_STANDARD_CPP_NAMESPACE memset(buffer, mark, size) michael@0: #define uprv_memcmp(buffer1, buffer2, size) U_STANDARD_CPP_NAMESPACE memcmp(buffer1, buffer2,size) michael@0: michael@0: U_CAPI void * U_EXPORT2 michael@0: uprv_malloc(size_t s) U_MALLOC_ATTR U_ALLOC_SIZE_ATTR(1); michael@0: michael@0: U_CAPI void * U_EXPORT2 michael@0: uprv_realloc(void *mem, size_t size) U_ALLOC_SIZE_ATTR(2); michael@0: michael@0: U_CAPI void U_EXPORT2 michael@0: uprv_free(void *mem); michael@0: michael@0: U_CAPI void * U_EXPORT2 michael@0: uprv_calloc(size_t num, size_t size) U_MALLOC_ATTR U_ALLOC_SIZE_ATTR2(1,2); michael@0: michael@0: /** michael@0: * This should align the memory properly on any machine. michael@0: * This is very useful for the safeClone functions. michael@0: */ michael@0: typedef union { michael@0: long t1; michael@0: double t2; michael@0: void *t3; michael@0: } UAlignedMemory; michael@0: michael@0: /** michael@0: * Get the least significant bits of a pointer (a memory address). michael@0: * For example, with a mask of 3, the macro gets the 2 least significant bits, michael@0: * which will be 0 if the pointer is 32-bit (4-byte) aligned. michael@0: * michael@0: * ptrdiff_t is the most appropriate integer type to cast to. michael@0: * size_t should work too, since on most (or all?) platforms it has the same michael@0: * width as ptrdiff_t. michael@0: */ michael@0: #define U_POINTER_MASK_LSB(ptr, mask) (((ptrdiff_t)(char *)(ptr)) & (mask)) michael@0: michael@0: /** michael@0: * Get the amount of bytes that a pointer is off by from michael@0: * the previous UAlignedMemory-aligned pointer. michael@0: */ michael@0: #define U_ALIGNMENT_OFFSET(ptr) U_POINTER_MASK_LSB(ptr, sizeof(UAlignedMemory) - 1) michael@0: michael@0: /** michael@0: * Get the amount of bytes to add to a pointer michael@0: * in order to get the next UAlignedMemory-aligned address. michael@0: */ michael@0: #define U_ALIGNMENT_OFFSET_UP(ptr) (sizeof(UAlignedMemory) - U_ALIGNMENT_OFFSET(ptr)) michael@0: michael@0: /** michael@0: * Indicate whether the ICU allocation functions have been used. michael@0: * This is used to determine whether ICU is in an initial, unused state. michael@0: */ michael@0: U_CFUNC UBool michael@0: cmemory_inUse(void); michael@0: michael@0: /** michael@0: * Heap clean up function, called from u_cleanup() michael@0: * Clears any user heap functions from u_setMemoryFunctions() michael@0: * Does NOT deallocate any remaining allocated memory. michael@0: */ michael@0: U_CFUNC UBool michael@0: cmemory_cleanup(void); michael@0: michael@0: /** michael@0: * A function called by uhash_remove, michael@0: * uhash_close, or uhash_put to delete michael@0: * an existing key or value. michael@0: * @param obj A key or value stored in a hashtable michael@0: * @see uprv_deleteUObject michael@0: */ michael@0: typedef void U_CALLCONV UObjectDeleter(void* obj); michael@0: michael@0: /** michael@0: * Deleter for UObject instances. michael@0: * Works for all subclasses of UObject because it has a virtual destructor. michael@0: */ michael@0: U_CAPI void U_EXPORT2 michael@0: uprv_deleteUObject(void *obj); michael@0: michael@0: #ifdef __cplusplus michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: /** michael@0: * "Smart pointer" class, deletes memory via uprv_free(). michael@0: * For most methods see the LocalPointerBase base class. michael@0: * Adds operator[] for array item access. michael@0: * michael@0: * @see LocalPointerBase michael@0: */ michael@0: template michael@0: class LocalMemory : public LocalPointerBase { michael@0: public: michael@0: /** michael@0: * Constructor takes ownership. michael@0: * @param p simple pointer to an array of T items that is adopted michael@0: */ michael@0: explicit LocalMemory(T *p=NULL) : LocalPointerBase(p) {} michael@0: /** michael@0: * Destructor deletes the memory it owns. michael@0: */ michael@0: ~LocalMemory() { michael@0: uprv_free(LocalPointerBase::ptr); michael@0: } michael@0: /** michael@0: * Deletes the array it owns, michael@0: * and adopts (takes ownership of) the one passed in. michael@0: * @param p simple pointer to an array of T items that is adopted michael@0: */ michael@0: void adoptInstead(T *p) { michael@0: uprv_free(LocalPointerBase::ptr); michael@0: LocalPointerBase::ptr=p; michael@0: } michael@0: /** michael@0: * Deletes the array it owns, allocates a new one and reset its bytes to 0. michael@0: * Returns the new array pointer. michael@0: * If the allocation fails, then the current array is unchanged and michael@0: * this method returns NULL. michael@0: * @param newCapacity must be >0 michael@0: * @return the allocated array pointer, or NULL if the allocation failed michael@0: */ michael@0: inline T *allocateInsteadAndReset(int32_t newCapacity=1); michael@0: /** michael@0: * Deletes the array it owns and allocates a new one, copying length T items. michael@0: * Returns the new array pointer. michael@0: * If the allocation fails, then the current array is unchanged and michael@0: * this method returns NULL. michael@0: * @param newCapacity must be >0 michael@0: * @param length number of T items to be copied from the old array to the new one; michael@0: * must be no more than the capacity of the old array, michael@0: * which the caller must track because the LocalMemory does not track it michael@0: * @return the allocated array pointer, or NULL if the allocation failed michael@0: */ michael@0: inline T *allocateInsteadAndCopy(int32_t newCapacity=1, int32_t length=0); michael@0: /** michael@0: * Array item access (writable). michael@0: * No index bounds check. michael@0: * @param i array index michael@0: * @return reference to the array item michael@0: */ michael@0: T &operator[](ptrdiff_t i) const { return LocalPointerBase::ptr[i]; } michael@0: }; michael@0: michael@0: template michael@0: inline T *LocalMemory::allocateInsteadAndReset(int32_t newCapacity) { michael@0: if(newCapacity>0) { michael@0: T *p=(T *)uprv_malloc(newCapacity*sizeof(T)); michael@0: if(p!=NULL) { michael@0: uprv_memset(p, 0, newCapacity*sizeof(T)); michael@0: uprv_free(LocalPointerBase::ptr); michael@0: LocalPointerBase::ptr=p; michael@0: } michael@0: return p; michael@0: } else { michael@0: return NULL; michael@0: } michael@0: } michael@0: michael@0: michael@0: template michael@0: inline T *LocalMemory::allocateInsteadAndCopy(int32_t newCapacity, int32_t length) { michael@0: if(newCapacity>0) { michael@0: T *p=(T *)uprv_malloc(newCapacity*sizeof(T)); michael@0: if(p!=NULL) { michael@0: if(length>0) { michael@0: if(length>newCapacity) { michael@0: length=newCapacity; michael@0: } michael@0: uprv_memcpy(p, LocalPointerBase::ptr, length*sizeof(T)); michael@0: } michael@0: uprv_free(LocalPointerBase::ptr); michael@0: LocalPointerBase::ptr=p; michael@0: } michael@0: return p; michael@0: } else { michael@0: return NULL; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Simple array/buffer management class using uprv_malloc() and uprv_free(). michael@0: * Provides an internal array with fixed capacity. Can alias another array michael@0: * or allocate one. michael@0: * michael@0: * The array address is properly aligned for type T. It might not be properly michael@0: * aligned for types larger than T (or larger than the largest subtype of T). michael@0: * michael@0: * Unlike LocalMemory and LocalArray, this class never adopts michael@0: * (takes ownership of) another array. michael@0: */ michael@0: template michael@0: class MaybeStackArray { michael@0: public: michael@0: /** michael@0: * Default constructor initializes with internal T[stackCapacity] buffer. michael@0: */ michael@0: MaybeStackArray() : ptr(stackArray), capacity(stackCapacity), needToRelease(FALSE) {} michael@0: /** michael@0: * Destructor deletes the array (if owned). michael@0: */ michael@0: ~MaybeStackArray() { releaseArray(); } michael@0: /** michael@0: * Returns the array capacity (number of T items). michael@0: * @return array capacity michael@0: */ michael@0: int32_t getCapacity() const { return capacity; } michael@0: /** michael@0: * Access without ownership change. michael@0: * @return the array pointer michael@0: */ michael@0: T *getAlias() const { return ptr; } michael@0: /** michael@0: * Returns the array limit. Simple convenience method. michael@0: * @return getAlias()+getCapacity() michael@0: */ michael@0: T *getArrayLimit() const { return getAlias()+capacity; } michael@0: // No "operator T *() const" because that can make michael@0: // expressions like mbs[index] ambiguous for some compilers. michael@0: /** michael@0: * Array item access (const). michael@0: * No index bounds check. michael@0: * @param i array index michael@0: * @return reference to the array item michael@0: */ michael@0: const T &operator[](ptrdiff_t i) const { return ptr[i]; } michael@0: /** michael@0: * Array item access (writable). michael@0: * No index bounds check. michael@0: * @param i array index michael@0: * @return reference to the array item michael@0: */ michael@0: T &operator[](ptrdiff_t i) { return ptr[i]; } michael@0: /** michael@0: * Deletes the array (if owned) and aliases another one, no transfer of ownership. michael@0: * If the arguments are illegal, then the current array is unchanged. michael@0: * @param otherArray must not be NULL michael@0: * @param otherCapacity must be >0 michael@0: */ michael@0: void aliasInstead(T *otherArray, int32_t otherCapacity) { michael@0: if(otherArray!=NULL && otherCapacity>0) { michael@0: releaseArray(); michael@0: ptr=otherArray; michael@0: capacity=otherCapacity; michael@0: needToRelease=FALSE; michael@0: } michael@0: } michael@0: /** michael@0: * Deletes the array (if owned) and allocates a new one, copying length T items. michael@0: * Returns the new array pointer. michael@0: * If the allocation fails, then the current array is unchanged and michael@0: * this method returns NULL. michael@0: * @param newCapacity can be less than or greater than the current capacity; michael@0: * must be >0 michael@0: * @param length number of T items to be copied from the old array to the new one michael@0: * @return the allocated array pointer, or NULL if the allocation failed michael@0: */ michael@0: inline T *resize(int32_t newCapacity, int32_t length=0); michael@0: /** michael@0: * Gives up ownership of the array if owned, or else clones it, michael@0: * copying length T items; resets itself to the internal stack array. michael@0: * Returns NULL if the allocation failed. michael@0: * @param length number of T items to copy when cloning, michael@0: * and capacity of the clone when cloning michael@0: * @param resultCapacity will be set to the returned array's capacity (output-only) michael@0: * @return the array pointer; michael@0: * caller becomes responsible for deleting the array michael@0: */ michael@0: inline T *orphanOrClone(int32_t length, int32_t &resultCapacity); michael@0: private: michael@0: T *ptr; michael@0: int32_t capacity; michael@0: UBool needToRelease; michael@0: T stackArray[stackCapacity]; michael@0: void releaseArray() { michael@0: if(needToRelease) { michael@0: uprv_free(ptr); michael@0: } michael@0: } michael@0: /* No comparison operators with other MaybeStackArray's. */ michael@0: bool operator==(const MaybeStackArray & /*other*/) {return FALSE;} michael@0: bool operator!=(const MaybeStackArray & /*other*/) {return TRUE;} michael@0: /* No ownership transfer: No copy constructor, no assignment operator. */ michael@0: MaybeStackArray(const MaybeStackArray & /*other*/) {} michael@0: void operator=(const MaybeStackArray & /*other*/) {} michael@0: michael@0: // No heap allocation. Use only on the stack. michael@0: // (Declaring these functions private triggers a cascade of problems: michael@0: // MSVC insists on exporting an instantiation of MaybeStackArray, which michael@0: // requires that all functions be defined. michael@0: // An empty implementation of new() is rejected, it must return a value. michael@0: // Returning NULL is rejected by gcc for operator new. michael@0: // The expedient thing is just not to override operator new. michael@0: // While relatively pointless, heap allocated instances will function. michael@0: // static void * U_EXPORT2 operator new(size_t size); michael@0: // static void * U_EXPORT2 operator new[](size_t size); michael@0: #if U_HAVE_PLACEMENT_NEW michael@0: // static void * U_EXPORT2 operator new(size_t, void *ptr); michael@0: #endif michael@0: }; michael@0: michael@0: template michael@0: inline T *MaybeStackArray::resize(int32_t newCapacity, int32_t length) { michael@0: if(newCapacity>0) { michael@0: #if U_DEBUG && defined(UPRV_MALLOC_COUNT) michael@0: ::fprintf(::stderr,"MaybeStacArray (resize) alloc %d * %lu\n", newCapacity,sizeof(T)); michael@0: #endif michael@0: T *p=(T *)uprv_malloc(newCapacity*sizeof(T)); michael@0: if(p!=NULL) { michael@0: if(length>0) { michael@0: if(length>capacity) { michael@0: length=capacity; michael@0: } michael@0: if(length>newCapacity) { michael@0: length=newCapacity; michael@0: } michael@0: uprv_memcpy(p, ptr, length*sizeof(T)); michael@0: } michael@0: releaseArray(); michael@0: ptr=p; michael@0: capacity=newCapacity; michael@0: needToRelease=TRUE; michael@0: } michael@0: return p; michael@0: } else { michael@0: return NULL; michael@0: } michael@0: } michael@0: michael@0: template michael@0: inline T *MaybeStackArray::orphanOrClone(int32_t length, int32_t &resultCapacity) { michael@0: T *p; michael@0: if(needToRelease) { michael@0: p=ptr; michael@0: } else if(length<=0) { michael@0: return NULL; michael@0: } else { michael@0: if(length>capacity) { michael@0: length=capacity; michael@0: } michael@0: p=(T *)uprv_malloc(length*sizeof(T)); michael@0: #if U_DEBUG && defined(UPRV_MALLOC_COUNT) michael@0: ::fprintf(::stderr,"MaybeStacArray (orphan) alloc %d * %lu\n", length,sizeof(T)); michael@0: #endif michael@0: if(p==NULL) { michael@0: return NULL; michael@0: } michael@0: uprv_memcpy(p, ptr, length*sizeof(T)); michael@0: } michael@0: resultCapacity=length; michael@0: ptr=stackArray; michael@0: capacity=stackCapacity; michael@0: needToRelease=FALSE; michael@0: return p; michael@0: } michael@0: michael@0: /** michael@0: * Variant of MaybeStackArray that allocates a header struct and an array michael@0: * in one contiguous memory block, using uprv_malloc() and uprv_free(). michael@0: * Provides internal memory with fixed array capacity. Can alias another memory michael@0: * block or allocate one. michael@0: * The stackCapacity is the number of T items in the internal memory, michael@0: * not counting the H header. michael@0: * Unlike LocalMemory and LocalArray, this class never adopts michael@0: * (takes ownership of) another memory block. michael@0: */ michael@0: template michael@0: class MaybeStackHeaderAndArray { michael@0: public: michael@0: /** michael@0: * Default constructor initializes with internal H+T[stackCapacity] buffer. michael@0: */ michael@0: MaybeStackHeaderAndArray() : ptr(&stackHeader), capacity(stackCapacity), needToRelease(FALSE) {} michael@0: /** michael@0: * Destructor deletes the memory (if owned). michael@0: */ michael@0: ~MaybeStackHeaderAndArray() { releaseMemory(); } michael@0: /** michael@0: * Returns the array capacity (number of T items). michael@0: * @return array capacity michael@0: */ michael@0: int32_t getCapacity() const { return capacity; } michael@0: /** michael@0: * Access without ownership change. michael@0: * @return the header pointer michael@0: */ michael@0: H *getAlias() const { return ptr; } michael@0: /** michael@0: * Returns the array start. michael@0: * @return array start, same address as getAlias()+1 michael@0: */ michael@0: T *getArrayStart() const { return reinterpret_cast(getAlias()+1); } michael@0: /** michael@0: * Returns the array limit. michael@0: * @return array limit michael@0: */ michael@0: T *getArrayLimit() const { return getArrayStart()+capacity; } michael@0: /** michael@0: * Access without ownership change. Same as getAlias(). michael@0: * A class instance can be used directly in expressions that take a T *. michael@0: * @return the header pointer michael@0: */ michael@0: operator H *() const { return ptr; } michael@0: /** michael@0: * Array item access (writable). michael@0: * No index bounds check. michael@0: * @param i array index michael@0: * @return reference to the array item michael@0: */ michael@0: T &operator[](ptrdiff_t i) { return getArrayStart()[i]; } michael@0: /** michael@0: * Deletes the memory block (if owned) and aliases another one, no transfer of ownership. michael@0: * If the arguments are illegal, then the current memory is unchanged. michael@0: * @param otherArray must not be NULL michael@0: * @param otherCapacity must be >0 michael@0: */ michael@0: void aliasInstead(H *otherMemory, int32_t otherCapacity) { michael@0: if(otherMemory!=NULL && otherCapacity>0) { michael@0: releaseMemory(); michael@0: ptr=otherMemory; michael@0: capacity=otherCapacity; michael@0: needToRelease=FALSE; michael@0: } michael@0: } michael@0: /** michael@0: * Deletes the memory block (if owned) and allocates a new one, michael@0: * copying the header and length T array items. michael@0: * Returns the new header pointer. michael@0: * If the allocation fails, then the current memory is unchanged and michael@0: * this method returns NULL. michael@0: * @param newCapacity can be less than or greater than the current capacity; michael@0: * must be >0 michael@0: * @param length number of T items to be copied from the old array to the new one michael@0: * @return the allocated pointer, or NULL if the allocation failed michael@0: */ michael@0: inline H *resize(int32_t newCapacity, int32_t length=0); michael@0: /** michael@0: * Gives up ownership of the memory if owned, or else clones it, michael@0: * copying the header and length T array items; resets itself to the internal memory. michael@0: * Returns NULL if the allocation failed. michael@0: * @param length number of T items to copy when cloning, michael@0: * and array capacity of the clone when cloning michael@0: * @param resultCapacity will be set to the returned array's capacity (output-only) michael@0: * @return the header pointer; michael@0: * caller becomes responsible for deleting the array michael@0: */ michael@0: inline H *orphanOrClone(int32_t length, int32_t &resultCapacity); michael@0: private: michael@0: H *ptr; michael@0: int32_t capacity; michael@0: UBool needToRelease; michael@0: // stackHeader must precede stackArray immediately. michael@0: H stackHeader; michael@0: T stackArray[stackCapacity]; michael@0: void releaseMemory() { michael@0: if(needToRelease) { michael@0: uprv_free(ptr); michael@0: } michael@0: } michael@0: /* No comparison operators with other MaybeStackHeaderAndArray's. */ michael@0: bool operator==(const MaybeStackHeaderAndArray & /*other*/) {return FALSE;} michael@0: bool operator!=(const MaybeStackHeaderAndArray & /*other*/) {return TRUE;} michael@0: /* No ownership transfer: No copy constructor, no assignment operator. */ michael@0: MaybeStackHeaderAndArray(const MaybeStackHeaderAndArray & /*other*/) {} michael@0: void operator=(const MaybeStackHeaderAndArray & /*other*/) {} michael@0: michael@0: // No heap allocation. Use only on the stack. michael@0: // (Declaring these functions private triggers a cascade of problems; michael@0: // see the MaybeStackArray class for details.) michael@0: // static void * U_EXPORT2 operator new(size_t size); michael@0: // static void * U_EXPORT2 operator new[](size_t size); michael@0: #if U_HAVE_PLACEMENT_NEW michael@0: // static void * U_EXPORT2 operator new(size_t, void *ptr); michael@0: #endif michael@0: }; michael@0: michael@0: template michael@0: inline H *MaybeStackHeaderAndArray::resize(int32_t newCapacity, michael@0: int32_t length) { michael@0: if(newCapacity>=0) { michael@0: #if U_DEBUG && defined(UPRV_MALLOC_COUNT) michael@0: ::fprintf(::stderr,"MaybeStackHeaderAndArray alloc %d + %d * %ul\n", sizeof(H),newCapacity,sizeof(T)); michael@0: #endif michael@0: H *p=(H *)uprv_malloc(sizeof(H)+newCapacity*sizeof(T)); michael@0: if(p!=NULL) { michael@0: if(length<0) { michael@0: length=0; michael@0: } else if(length>0) { michael@0: if(length>capacity) { michael@0: length=capacity; michael@0: } michael@0: if(length>newCapacity) { michael@0: length=newCapacity; michael@0: } michael@0: } michael@0: uprv_memcpy(p, ptr, sizeof(H)+length*sizeof(T)); michael@0: releaseMemory(); michael@0: ptr=p; michael@0: capacity=newCapacity; michael@0: needToRelease=TRUE; michael@0: } michael@0: return p; michael@0: } else { michael@0: return NULL; michael@0: } michael@0: } michael@0: michael@0: template michael@0: inline H *MaybeStackHeaderAndArray::orphanOrClone(int32_t length, michael@0: int32_t &resultCapacity) { michael@0: H *p; michael@0: if(needToRelease) { michael@0: p=ptr; michael@0: } else { michael@0: if(length<0) { michael@0: length=0; michael@0: } else if(length>capacity) { michael@0: length=capacity; michael@0: } michael@0: #if U_DEBUG && defined(UPRV_MALLOC_COUNT) michael@0: ::fprintf(::stderr,"MaybeStackHeaderAndArray (orphan) alloc %ul + %d * %lu\n", sizeof(H),length,sizeof(T)); michael@0: #endif michael@0: p=(H *)uprv_malloc(sizeof(H)+length*sizeof(T)); michael@0: if(p==NULL) { michael@0: return NULL; michael@0: } michael@0: uprv_memcpy(p, ptr, sizeof(H)+length*sizeof(T)); michael@0: } michael@0: resultCapacity=length; michael@0: ptr=&stackHeader; michael@0: capacity=stackCapacity; michael@0: needToRelease=FALSE; michael@0: return p; michael@0: } michael@0: michael@0: U_NAMESPACE_END michael@0: michael@0: #endif /* __cplusplus */ michael@0: #endif /* CMEMORY_H */