MEM_guardedalloc: Add template 'type-safe' versions of MEM_mallocN.

Same thing as for `MEM_callocN<T>` and `MEM_freeN<T>` in dd168a35c5,
allows to reduce type verbosity, and increase type safety.
This commit is contained in:
Bastien Montagne
2025-03-05 19:04:09 +01:00
committed by Bastien Montagne
parent 82e2a57dcd
commit 2900cfa50a
5 changed files with 89 additions and 12 deletions

View File

@@ -114,7 +114,7 @@ void *MEM_calloc_arrayN(size_t len,
* Allocate a block of memory of size len, with tag name str. The
* name must be a static, because only a pointer to it is stored!
*/
extern void *(*MEM_mallocN)(size_t len, const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
void *MEM_mallocN(size_t len, const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
/**
@@ -122,9 +122,9 @@ extern void *(*MEM_mallocN)(size_t len, const char *str) /* ATTR_MALLOC */ ATTR_
* aborting in case of integer overflow to prevent vulnerabilities. The
* name must be a static, because only a pointer to it is stored!
*/
extern void *(*MEM_malloc_arrayN)(size_t len,
size_t size,
const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
void *MEM_malloc_arrayN(size_t len,
size_t size,
const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
ATTR_ALLOC_SIZE(1, 2) ATTR_NONNULL(3);
/**
@@ -403,6 +403,55 @@ template<typename T> inline T *MEM_calloc_arrayN(const size_t length, const char
MEM_calloc_arrayN_aligned(length, sizeof(T), alignof(T), allocation_name));
}
/**
* Allocate uninitialized memory for an object of type #T. The constructor of #T is not called,
* therefore this must only be used with trivial types (like all C types).
*
* When allocating an enforced specific amount of bytes, the C version of this function should be
* used instead. While this should be avoided in C++ code, it is still required in some cases, e.g.
* for ID allocation based on #IDTypeInfo::struct_size.
*
* #MEM_freeN must be used to free a pointer returned by this call. Calling #MEM_delete on it is
* illegal.
*/
template<typename T> inline T *MEM_mallocN(const char *allocation_name)
{
# ifdef _MSC_VER
/* MSVC considers C-style types using the DNA_DEFINE_CXX_METHODS as non-trivial (more
* specifically, non-trivially copyable, likely because the default copy constructors are
* deleted). GCC and clang (both on linux, OSX, and clang-cl on Windows on Arm) do not.
*
* So for now, use a more restricted check on MSVC, should still catch most of actual invalid
* cases. */
static_assert(std::is_trivially_constructible_v<T>,
"For non-trivial types, MEM_new must be used.");
# else
static_assert(std::is_trivial_v<T>, "For non-trivial types, MEM_new must be used.");
# endif
return static_cast<T *>(MEM_malloc_arrayN_aligned(1, sizeof(T), alignof(T), allocation_name));
}
/**
* Type-safe version of #MEM_malloc_arrayN/#MEM_malloc_array_alignedN.
*/
template<typename T> inline T *MEM_malloc_arrayN(const size_t length, const char *allocation_name)
{
# ifdef _MSC_VER
/* MSVC considers C-style types using the DNA_DEFINE_CXX_METHODS as non-trivial (more
* specifically, non-trivially copyable, likely because the default copy constructors are
* deleted). GCC and clang (both on linux, OSX, and clang-cl on Windows on Arm) do not.
*
* So for now, use a more restricted check on MSVC, should still catch most of actual invalid
* cases. */
static_assert(std::is_trivially_constructible_v<T>,
"For non-trivial types, MEM_new must be used.");
# else
static_assert(std::is_trivial_v<T>, "For non-trivial types, MEM_new must be used.");
# endif
return static_cast<T *>(
MEM_malloc_arrayN_aligned(length, sizeof(T), alignof(T), allocation_name));
}
/**
* Allocate memory for an object of type #T and memory-copy `other` into it.
* Only applicable for trivial types.

View File

@@ -43,8 +43,10 @@ void *(*mem_guarded::internal::mem_callocN)(size_t len, const char *str) = MEM_l
void *(*mem_guarded::internal::mem_calloc_arrayN)(size_t len,
size_t size,
const char *str) = MEM_lockfree_calloc_arrayN;
void *(*MEM_mallocN)(size_t len, const char *str) = MEM_lockfree_mallocN;
void *(*MEM_malloc_arrayN)(size_t len, size_t size, const char *str) = MEM_lockfree_malloc_arrayN;
void *(*mem_guarded::internal::mem_mallocN)(size_t len, const char *str) = MEM_lockfree_mallocN;
void *(*mem_guarded::internal::mem_malloc_arrayN)(size_t len,
size_t size,
const char *str) = MEM_lockfree_malloc_arrayN;
void *(*mem_guarded::internal::mem_mallocN_aligned_ex)(size_t len,
size_t alignment,
const char *str,
@@ -123,6 +125,16 @@ void *MEM_calloc_arrayN(size_t len, size_t size, const char *str)
return mem_calloc_arrayN(len, size, str);
}
void *MEM_mallocN(size_t len, const char *str)
{
return mem_mallocN(len, str);
}
void *MEM_malloc_arrayN(size_t len, size_t size, const char *str)
{
return mem_malloc_arrayN(len, size, str);
}
void *MEM_mallocN_aligned(size_t len, size_t alignment, const char *str)
{
return mem_mallocN_aligned_ex(len, alignment, str, AllocationType::ALLOC_FREE);
@@ -164,8 +176,8 @@ void MEM_use_lockfree_allocator()
MEM_recallocN_id = MEM_lockfree_recallocN_id;
mem_callocN = MEM_lockfree_callocN;
mem_calloc_arrayN = MEM_lockfree_calloc_arrayN;
MEM_mallocN = MEM_lockfree_mallocN;
MEM_malloc_arrayN = MEM_lockfree_malloc_arrayN;
mem_mallocN = MEM_lockfree_mallocN;
mem_malloc_arrayN = MEM_lockfree_malloc_arrayN;
mem_mallocN_aligned_ex = MEM_lockfree_mallocN_aligned;
MEM_malloc_arrayN_aligned = MEM_lockfree_malloc_arrayN_aligned;
MEM_calloc_arrayN_aligned = MEM_lockfree_calloc_arrayN_aligned;
@@ -200,8 +212,8 @@ void MEM_use_guarded_allocator()
MEM_recallocN_id = MEM_guarded_recallocN_id;
mem_callocN = MEM_guarded_callocN;
mem_calloc_arrayN = MEM_guarded_calloc_arrayN;
MEM_mallocN = MEM_guarded_mallocN;
MEM_malloc_arrayN = MEM_guarded_malloc_arrayN;
mem_mallocN = MEM_guarded_mallocN;
mem_malloc_arrayN = MEM_guarded_malloc_arrayN;
mem_mallocN_aligned_ex = MEM_guarded_mallocN_aligned;
MEM_malloc_arrayN_aligned = MEM_guarded_malloc_arrayN_aligned;
MEM_calloc_arrayN_aligned = MEM_guarded_calloc_arrayN_aligned;

View File

@@ -720,7 +720,7 @@ static void *mem_guarded_malloc_arrayN_aligned(const size_t len,
return nullptr;
}
if (alignment <= MEM_MIN_CPP_ALIGNMENT) {
return mem_callocN(r_bytes_num, str);
return mem_mallocN(r_bytes_num, str);
}
return MEM_mallocN_aligned(r_bytes_num, alignment, str);
}

View File

@@ -36,6 +36,22 @@ extern void *(*mem_calloc_arrayN)(size_t len,
const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
ATTR_ALLOC_SIZE(1, 2) ATTR_NONNULL(3);
/**
* Internal implementation of #MEM_mallocN, exposed because public #MEM_mallocN cannot be a
* function pointer, to allow its overload by C++ template version.
*/
extern void *(*mem_mallocN)(size_t len, const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
/**
* Internal implementation of #MEM_malloc_arrayN, exposed because public #MEM_malloc_arrayN cannot
* be a function pointer, to allow its overload by C++ template version.
*/
extern void *(*mem_malloc_arrayN)(size_t len,
size_t size,
const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
ATTR_ALLOC_SIZE(1, 2) ATTR_NONNULL(3);
/** Internal implementation of #MEM_mallocN_aligned, exposed because #MEM_new needs access to it.
*/
extern void *(*mem_mallocN_aligned_ex)(size_t len,

View File

@@ -474,7 +474,7 @@ static void *mem_lockfree_malloc_arrayN_aligned(const size_t len,
return nullptr;
}
if (alignment <= MEM_MIN_CPP_ALIGNMENT) {
return mem_callocN(r_bytes_num, str);
return mem_mallocN(r_bytes_num, str);
}
void *ptr = MEM_mallocN_aligned(r_bytes_num, alignment, str);
return ptr;