#ifdef USE_API_MAXON Error #elif !defined(C4DMISC_DEFAULTALLOCATOR_H__) #define C4DMISC_DEFAULTALLOCATOR_H__ #include "../general.h" #include namespace maxon { // Can't include c4d_memory.h because this would create a circular dependency to operatingsystem.h, therefore use the following helpers void* _DefaultAllocNC(Int size, Int32 line, const Char* file); void* _DefaultAlloc(Int size, Int32 line, const Char* file); void* _DefaultReallocNC(void* data, Int size, Int32 line, const Char* file); void _DefaultFree(void** data_ptr); //---------------------------------------------------------------------------------------- /// Default implementation of an allocator. /// An allocator is used by arrays, lists and other data structurs to allocate and release /// memory. By default this implementation of an allocator is used. /// /// There might be rare cases when you need a special memory alignment, a different resize /// strategy or have to use a special memory area (stack, shared memory, ...). This can be /// done by writing a custom allocator and specifying it as parameter upon array construction. /// A custom allocator must implement the ComputeArraySize(), Alloc(), AllocClear(), Realloc() /// and Free() methods, but it doesn't (and usually shouldn't) inherit from DefaultAllocator. /// The allocator methods don't have to be static if your allocator requires member /// variables, but the DefaultAllocator doesn't and therefore uses static methods for /// better performance. /// /// Please note that an allocator is copied upon array construction - it would be /// a bad idea if your custom allocator object would consist of more than a few variables. /// /// THREADSAFE //---------------------------------------------------------------------------------------- class DefaultAllocator { public: //---------------------------------------------------------------------------------------- /// Computes the new size for a growing array. /// @param[in] current_size Current number of elements. /// @param[in] increment Number of elements to be added (>= 1) /// @param[in] min_chunk_size The minimum number of elements upon array creation. /// THREADSAFE. /// @return New number of elements. //---------------------------------------------------------------------------------------- static Int ComputeArraySize(Int current_size, Int increment, Int min_chunk_size) { Int new_size = (current_size + increment) * 3 / 2; // increase required size by 50 % for future allocations Int remainder = new_size % min_chunk_size; if (remainder || new_size == 0) new_size += min_chunk_size - remainder; return new_size; } //---------------------------------------------------------------------------------------- /// Allocates a memory block. /// The memory is not cleared, it may contain a certain byte pattern in debug mode. /// @param[in] s Block size in bytes (values < 0 will return nullptr) /// THREADSAFE. /// @return Memory block address or nullptr. //---------------------------------------------------------------------------------------- static inline void* Alloc(Int32 s, C4D_MISC_ALLOC_LOCATION_DECLARATION) { return _DefaultAllocNC((Int)s, (Int32)C4D_MISC_ALLOC_LOCATION_FORWARD.GetLine(), C4D_MISC_ALLOC_LOCATION_FORWARD.GetFile()); } //---------------------------------------------------------------------------------------- /// Allocates a memory block. /// The memory is not cleared, it may contain a certain byte pattern in debug mode. /// @param[in] s Block size in bytes (values < 0 will return nullptr) /// THREADSAFE. /// @return Memory block address or nullptr. //---------------------------------------------------------------------------------------- static inline void* Alloc(Int64 s, C4D_MISC_ALLOC_LOCATION_DECLARATION) { if (s!=Int(s)) { DebugStop(); return nullptr; } return _DefaultAllocNC((Int)s, (Int32)C4D_MISC_ALLOC_LOCATION_FORWARD.GetLine(), C4D_MISC_ALLOC_LOCATION_FORWARD.GetFile()); } //---------------------------------------------------------------------------------------- /// Allocates a memory block and clears it. /// @param[in] s Block size in bytes (values < 0 will return nullptr) /// THREADSAFE. /// @return Memory block address or nullptr. //---------------------------------------------------------------------------------------- static inline void* AllocClear(Int32 s, C4D_MISC_ALLOC_LOCATION_DECLARATION) { return _DefaultAlloc((Int)s, (Int32)C4D_MISC_ALLOC_LOCATION_FORWARD.GetLine(), C4D_MISC_ALLOC_LOCATION_FORWARD.GetFile()); } //---------------------------------------------------------------------------------------- /// Allocates a memory block and clears it. /// @param[in] s Block size in bytes (values < 0 will return nullptr) /// THREADSAFE. /// @return Memory block address or nullptr. //---------------------------------------------------------------------------------------- static inline void* AllocClear(Int64 s, C4D_MISC_ALLOC_LOCATION_DECLARATION) { if (s!=Int(s)) { DebugStop(); return nullptr; } return _DefaultAlloc((Int)s, (Int32)C4D_MISC_ALLOC_LOCATION_FORWARD.GetLine(), C4D_MISC_ALLOC_LOCATION_FORWARD.GetFile()); } //---------------------------------------------------------------------------------------- /// Resizes a memory block. /// The additional memory is not cleared, it may contain a certain byte pattern in debug mode. /// @param[in] p Current memory block (can be nullptr) /// @param[in] n New block size in bytes (values < 0 will return nullptr) /// THREADSAFE. /// @return Memory block address or nullptr if resize is not possible (p is still valid in this case) //---------------------------------------------------------------------------------------- static inline void* Realloc(void* p, Int n, C4D_MISC_ALLOC_LOCATION_DECLARATION) { return _DefaultReallocNC(p, (Int)n, (Int32)C4D_MISC_ALLOC_LOCATION_FORWARD.GetLine(), C4D_MISC_ALLOC_LOCATION_FORWARD.GetFile()); } //---------------------------------------------------------------------------------------- /// Frees a memory block. /// @param[in,out] p Memory block address (can be nullptr, will be nullptr after return) /// THREADSAFE //---------------------------------------------------------------------------------------- template static inline void Free(T*& p) { void* tmp = (void*) p; _DefaultFree(&tmp); p = nullptr; } }; //---------------------------------------------------------------------------------------- /// Creates an object with constructor parameters (using new or T::Alloc, depending on the type). This does not throw any kind of /// exception, it returns null if the allocation fails. /// Usage: MyType* x = NewObj(MyType, optional constructor parameters); /// @param[in] T Type of object. /// @param[in] ... Additional constructor parameters. /// THREADSAFE. /// @return Pointer to object (null if allocation failed) //---------------------------------------------------------------------------------------- // The placement new operator does not throw (is noexcept). Therefore placement new has to make // a null check before it calls the constructor and there is no need us to check the return value // of DefaultAllocator::Alloc(). All compilers behave as expected (gcc, llvm, msvc and icc) and // this according to the standard (ยง3.7.4.1). #define NewObj(T, ...) new (maxon::DefaultAllocator::Alloc(SIZEOF(T), C4D_MISC_ALLOC_LOCATION)) T(__VA_ARGS__) #define NewObjClear(T, ...) new (maxon::DefaultAllocator::AllocClear(SIZEOF(T), C4D_MISC_ALLOC_LOCATION)) T(__VA_ARGS__) #ifdef _MSC_VER #define ALLOC_TYPENAME #else #define ALLOC_TYPENAME typename #endif //---------------------------------------------------------------------------------------- /// Deletes an object. This expects a pointer as argument, the object is deleted using the destructor or T::Free, /// depending on the type of the object. /// THREADSAFE. /// @param[in,out] obj Object pointer (can be nullptr, will be nullptr after return) //---------------------------------------------------------------------------------------- // originally we used auto*& objectToDelete, but that doesn't work as the Intel Compiler has a bug that shows lots of warnings and MSVC has a bug where it doesn't link anymore #define DeleteObj(obj) \ do { \ ALLOC_TYPENAME std::remove_reference::type* objectToDelete = &(obj); \ if (*objectToDelete) { \ typedef ALLOC_TYPENAME std::remove_pointer::type>::type CtorType; \ ((CtorType*) (*objectToDelete))->~CtorType(); \ maxon::DefaultAllocator::Free(*objectToDelete); \ *objectToDelete = nullptr; \ } \ } while (false) // while ensures that the user needs to set a semicolon after DeleteObj //---------------------------------------------------------------------------------------- /// Deletes an object. This calls the destructor and frees memory afterwards. /// Normally you should use DeleteObj(). This variant should be used only in the rare case /// when the object pointer is a constant so that it can't be set to nullptr. /// @param[in] o Object pointer (can be nullptr) /// THREADSAFE //---------------------------------------------------------------------------------------- template void DeleteConstPtrObj(T* o) { if (o) { o->~T(); DefaultAllocator::Free(o); } } //---------------------------------------------------------------------------------------- /// Allocates raw memory: no constructors are called! /// @param[in] T Type of item (e.g. Char, Float) /// @param[in] cnt Number of items. /// THREADSAFE. /// @return Pointer to memory block or nullptr. //---------------------------------------------------------------------------------------- #define NewMem(T, cnt) ((T*) maxon::DefaultAllocator::Alloc((maxon::Int64)SIZEOF(T) * (maxon::Int64)(cnt), C4D_MISC_ALLOC_LOCATION)) //---------------------------------------------------------------------------------------- /// Allocates raw memory and clears it: no constructors are called! /// @param[in] T Type of item (e.g. Char, Float) /// @param[in] cnt Number of items. /// THREADSAFE. /// @return Pointer to memory block or nullptr. //---------------------------------------------------------------------------------------- #define NewMemClear(T, cnt) ((T*) maxon::DefaultAllocator::AllocClear((maxon::Int64)SIZEOF(T) * (maxon::Int64)(cnt), C4D_MISC_ALLOC_LOCATION)) //---------------------------------------------------------------------------------------- /// Resizes a raw memory block: no constructors are called! /// @param[in] p Previous memory block (can be nullptr) /// @param[in] cnt New number of items (size of the memory block is n * sizeof(T)) /// THREADSAFE. /// @return Pointer to memory block or nullptr. //---------------------------------------------------------------------------------------- template inline T* _ReallocMem(T* p, Int cnt, C4D_MISC_ALLOC_LOCATION_DECLARATION) { Int64 size = SIZEOF(T) * Int64(cnt); #ifndef __C4D_64BIT if (size > maxon::LIMIT::MAX) // overflow? { DebugStop(); return nullptr; } #endif return (T*) DefaultAllocator::Realloc(p, Int(size), C4D_MISC_ALLOC_LOCATION_FORWARD); } //---------------------------------------------------------------------------------------- /// Resizes a raw memory block: no constructors are called! /// @param[in] p Previous memory block (can be nullptr) /// @param[in] cnt New number of items (size of the memory block is n * sizeof(T)) /// THREADSAFE. /// @return Pointer to memory block or nullptr. //---------------------------------------------------------------------------------------- #define ReallocMem(p, cnt) _ReallocMem(p, cnt, C4D_MISC_ALLOC_LOCATION) //---------------------------------------------------------------------------------------- /// Frees a raw memory block: no destructors are called! /// @param[in] p Memory block (can be nullptr, will be nullptr after return) /// THREADSAFE //---------------------------------------------------------------------------------------- #define DeleteMem(p) (maxon::DefaultAllocator::Free(p)) //---------------------------------------------------------------------------------------- /// Clears memory of a datatype with pattern, if size >0. /// THREADSAFE. /// @param[out] data_ptr Address of the datatype. /// @param[in] cnt Number of elements t be filled with pattern (>1 e.g. for arrays), can be 0. /// @param[in] value (optional) fill value. //---------------------------------------------------------------------------------------- template inline void ClearMemType(T* data_ptr, Int cnt, UChar value = 0) { ::memset(data_ptr, value, size_t(cnt * sizeof(T))); } } // C4D_MISC_END #endif