2023-06-15 13:09:04 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
2021-12-14 18:35:31 +11:00
|
|
|
* \ingroup intern_mem
|
2012-07-14 15:29:45 +00:00
|
|
|
*
|
|
|
|
|
* \brief Read \ref MEMPage
|
2002-10-12 11:37:38 +00:00
|
|
|
*
|
2011-02-22 16:12:12 +00:00
|
|
|
* \page MEMPage Guarded memory(de)allocation
|
2002-10-12 11:37:38 +00:00
|
|
|
*
|
2025-04-30 19:38:23 +02:00
|
|
|
* \section aboutmem C++-style & C-style guarded memory allocation
|
2011-02-21 09:23:34 +00:00
|
|
|
*
|
2025-04-30 19:38:23 +02:00
|
|
|
* \subsection memabout About the MEM allocator module
|
2002-10-12 11:37:38 +00:00
|
|
|
*
|
2025-04-30 19:38:23 +02:00
|
|
|
* MEM provides guarded memory management. All allocated memory is enclosed by pads, to detect
|
|
|
|
|
* out-of-bound writes. All blocks are placed in a linked list, so they remain reachable at all
|
|
|
|
|
* times. There is no back-up in case the linked-list related data is lost.
|
2002-10-12 11:37:38 +00:00
|
|
|
*
|
2025-04-30 19:38:23 +02:00
|
|
|
* It provides C++ template versions of the `new`/`delete` operators (#MEM_new and #MEM_delete),
|
|
|
|
|
* which are the preferred way to create and delete data in new C++ code.
|
|
|
|
|
*
|
|
|
|
|
* It also provides C++ template versions of [cm]alloc and related API, which provides improved
|
|
|
|
|
* type safety, ensures that the allocated types are trivial, and reduces the casting verbosity by
|
|
|
|
|
* directly returning a pointer of the expected type. These are the preferred API when C++ code
|
|
|
|
|
* needs to allocate or free data in a C-compatible way (e.g. because it needs to interact with
|
|
|
|
|
* other 'legacy' code using C-based memory management).
|
|
|
|
|
*
|
|
|
|
|
* Finally, the original C-compatible, type-agnostic allocation API (#MEM_mallocN, #MEM_freeN,
|
|
|
|
|
* etc.) is kept for a few specific use-cases. Its usage should be avoided as much as possible.
|
2019-01-24 16:20:16 +11:00
|
|
|
*
|
2011-02-22 16:12:12 +00:00
|
|
|
* \subsection memdependencies Dependencies
|
2022-01-31 10:51:33 +11:00
|
|
|
* - `stdlib`
|
|
|
|
|
* - `stdio`
|
2019-01-24 16:20:16 +11:00
|
|
|
*
|
2011-02-22 16:12:12 +00:00
|
|
|
* \subsection memdocs API Documentation
|
|
|
|
|
* See \ref MEM_guardedalloc.h
|
2011-02-21 09:23:34 +00:00
|
|
|
*/
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2012-02-17 18:59:41 +00:00
|
|
|
#ifndef __MEM_GUARDEDALLOC_H__
|
|
|
|
|
#define __MEM_GUARDEDALLOC_H__
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2021-03-18 09:35:12 +11:00
|
|
|
/* Needed for uintptr_t and attributes, exception, don't use BLI anywhere else in `MEM_*` */
|
2013-10-10 11:58:01 +00:00
|
|
|
#include "../../source/blender/blenlib/BLI_compiler_attrs.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "../../source/blender/blenlib/BLI_sys_types.h"
|
2012-07-31 15:05:09 +00:00
|
|
|
|
2024-04-12 17:52:27 +02:00
|
|
|
#include <string.h>
|
|
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
#ifdef __cplusplus
|
|
|
|
|
extern "C" {
|
|
|
|
|
#endif
|
|
|
|
|
|
2025-04-18 20:24:14 +02:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/**
|
|
|
|
|
* \name Untyped Allocation API.
|
|
|
|
|
*
|
|
|
|
|
* Defines the 'C-style' part of the API, where memory management is fully untyped (i.e. done with
|
|
|
|
|
* void pointers and explicit size values).
|
|
|
|
|
*
|
|
|
|
|
* This API should usually not be used anymore in C++ code, unless some form of raw memory
|
2025-04-23 03:16:19 +00:00
|
|
|
* management is necessary (e.g. for allocation of various ID types based on their
|
2025-04-18 20:24:14 +02:00
|
|
|
* #IDTypeInfo::struct_size data).
|
|
|
|
|
*
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2021-04-09 18:47:10 +10:00
|
|
|
/**
|
|
|
|
|
* Returns the length of the allocated memory segment pointed at
|
2019-05-01 20:50:02 +10:00
|
|
|
* by vmemh. If the pointer was not previously allocated by this
|
2021-04-09 18:47:10 +10:00
|
|
|
* module, the result is undefined.
|
|
|
|
|
*/
|
2013-10-10 11:58:01 +00:00
|
|
|
extern size_t (*MEM_allocN_len)(const void *vmemh) ATTR_WARN_UNUSED_RESULT;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
/**
|
2025-03-05 16:35:09 +01:00
|
|
|
* Release memory previously allocated by the C-style functions of this module.
|
2024-09-03 12:58:26 +02:00
|
|
|
*
|
|
|
|
|
* It is illegal to call this function with data allocated by #MEM_new.
|
2019-05-01 20:50:02 +10:00
|
|
|
*/
|
2024-07-03 17:23:03 +02:00
|
|
|
void MEM_freeN(void *vmemh);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-05-12 05:54:33 +00:00
|
|
|
#if 0 /* UNUSED */
|
2019-04-17 08:16:53 +02:00
|
|
|
/**
|
2022-06-01 15:19:33 +10:00
|
|
|
* Return zero if memory is not in allocated list
|
|
|
|
|
*/
|
2019-04-17 08:16:53 +02:00
|
|
|
extern short (*MEM_testN)(void *vmemh);
|
2013-05-12 05:54:33 +00:00
|
|
|
#endif
|
2009-11-11 03:45:26 +00:00
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
/**
|
2019-05-01 20:50:02 +10:00
|
|
|
* Duplicates a block of memory, and returns a pointer to the
|
2021-11-16 17:11:45 +01:00
|
|
|
* newly allocated block.
|
|
|
|
|
* NULL-safe; will return NULL when receiving a NULL pointer. */
|
2025-03-05 16:35:09 +01:00
|
|
|
void *MEM_dupallocN(const void *vmemh) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2010-02-08 14:59:59 +00:00
|
|
|
/**
|
2019-05-01 20:50:02 +10:00
|
|
|
* Reallocates a block of memory, and returns pointer to the newly
|
|
|
|
|
* allocated block, the old one is freed. this is not as optimized
|
|
|
|
|
* as a system realloc but just makes a new allocation and copies
|
|
|
|
|
* over from existing memory. */
|
2013-10-10 11:58:01 +00:00
|
|
|
extern void *(*MEM_reallocN_id)(void *vmemh,
|
|
|
|
|
size_t len,
|
|
|
|
|
const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
|
|
|
|
|
ATTR_ALLOC_SIZE(2);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
/**
|
2019-05-01 20:50:02 +10:00
|
|
|
* A variant of realloc which zeros new bytes
|
|
|
|
|
*/
|
2013-10-10 11:58:01 +00:00
|
|
|
extern void *(*MEM_recallocN_id)(void *vmemh,
|
|
|
|
|
size_t len,
|
|
|
|
|
const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
|
|
|
|
|
ATTR_ALLOC_SIZE(2);
|
2012-08-11 12:16:09 +00:00
|
|
|
|
2013-08-04 03:00:04 +00:00
|
|
|
#define MEM_reallocN(vmemh, len) MEM_reallocN_id(vmemh, len, __func__)
|
|
|
|
|
#define MEM_recallocN(vmemh, len) MEM_recallocN_id(vmemh, len, __func__)
|
|
|
|
|
|
2012-08-11 12:16:09 +00:00
|
|
|
/**
|
2019-05-01 20:50:02 +10:00
|
|
|
* Allocate a block of memory of size len, with tag name str. The
|
|
|
|
|
* memory is cleared. The name must be static, because only a
|
2021-04-09 18:47:10 +10:00
|
|
|
* pointer to it is stored!
|
|
|
|
|
*/
|
2025-03-05 16:35:09 +01:00
|
|
|
void *MEM_callocN(size_t len, const char *str) ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1)
|
|
|
|
|
ATTR_NONNULL(2);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-01-14 21:53:32 +01:00
|
|
|
/**
|
2019-05-01 20:50:02 +10:00
|
|
|
* Allocate a block of memory of size (len * size), with tag name
|
|
|
|
|
* str, aborting in case of integer overflows to prevent vulnerabilities.
|
|
|
|
|
* The memory is cleared. The name must be static, because only a
|
2023-09-14 13:25:24 +10:00
|
|
|
* pointer to it is stored! */
|
2025-03-05 16:35:09 +01:00
|
|
|
void *MEM_calloc_arrayN(size_t len,
|
|
|
|
|
size_t size,
|
|
|
|
|
const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
|
2018-01-14 21:53:32 +01:00
|
|
|
ATTR_ALLOC_SIZE(1, 2) ATTR_NONNULL(3);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-07-14 15:29:45 +00:00
|
|
|
/**
|
2019-05-01 20:50:02 +10:00
|
|
|
* Allocate a block of memory of size len, with tag name str. The
|
2023-09-14 13:25:24 +10:00
|
|
|
* name must be a static, because only a pointer to it is stored!
|
2021-01-20 15:15:38 +11:00
|
|
|
*/
|
2025-03-05 19:04:09 +01:00
|
|
|
void *MEM_mallocN(size_t len, const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
|
2013-10-10 11:58:01 +00:00
|
|
|
ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-01-14 21:53:32 +01:00
|
|
|
/**
|
2019-05-01 20:50:02 +10:00
|
|
|
* Allocate a block of memory of size (len * size), with tag name str,
|
|
|
|
|
* aborting in case of integer overflow to prevent vulnerabilities. The
|
2023-09-14 13:25:24 +10:00
|
|
|
* name must be a static, because only a pointer to it is stored!
|
2021-01-20 15:15:38 +11:00
|
|
|
*/
|
2025-03-05 19:04:09 +01:00
|
|
|
void *MEM_malloc_arrayN(size_t len,
|
|
|
|
|
size_t size,
|
|
|
|
|
const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
|
2018-01-14 21:53:32 +01:00
|
|
|
ATTR_ALLOC_SIZE(1, 2) ATTR_NONNULL(3);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-06-14 00:30:13 +06:00
|
|
|
/**
|
2019-05-01 20:50:02 +10:00
|
|
|
* Allocate an aligned block of memory of size len, with tag name str. The
|
2023-09-14 13:25:24 +10:00
|
|
|
* name must be a static, because only a pointer to it is stored!
|
2021-01-20 15:15:38 +11:00
|
|
|
*/
|
2024-07-03 17:23:03 +02:00
|
|
|
void *MEM_mallocN_aligned(size_t len,
|
|
|
|
|
size_t alignment,
|
|
|
|
|
const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
|
2014-06-14 00:30:13 +06:00
|
|
|
ATTR_ALLOC_SIZE(1) ATTR_NONNULL(3);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2025-02-20 10:37:10 +01:00
|
|
|
/**
|
|
|
|
|
* Allocate an aligned block of memory that remains uninitialized.
|
|
|
|
|
*/
|
|
|
|
|
extern void *(*MEM_malloc_arrayN_aligned)(
|
|
|
|
|
size_t len,
|
|
|
|
|
size_t size,
|
|
|
|
|
size_t alignment,
|
|
|
|
|
const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1, 2)
|
|
|
|
|
ATTR_NONNULL(4);
|
|
|
|
|
|
2024-04-15 19:11:06 +02:00
|
|
|
/**
|
|
|
|
|
* Allocate an aligned block of memory that is initialized with zeros.
|
|
|
|
|
*/
|
|
|
|
|
extern void *(*MEM_calloc_arrayN_aligned)(
|
|
|
|
|
size_t len,
|
|
|
|
|
size_t size,
|
|
|
|
|
size_t alignment,
|
|
|
|
|
const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1, 2)
|
|
|
|
|
ATTR_NONNULL(4);
|
|
|
|
|
|
2025-04-18 20:24:14 +02:00
|
|
|
#ifdef __cplusplus
|
2025-04-23 03:16:19 +00:00
|
|
|
/** Implicitly uses the templated, type-safe version of #MEM_freeN<T>, unless `v` is `void *`. */
|
2025-04-18 20:24:14 +02:00
|
|
|
# define MEM_SAFE_FREE(v) \
|
|
|
|
|
do { \
|
|
|
|
|
if (v) { \
|
|
|
|
|
MEM_freeN(v); \
|
|
|
|
|
(v) = nullptr; \
|
|
|
|
|
} \
|
|
|
|
|
} while (0)
|
|
|
|
|
#else
|
|
|
|
|
# define MEM_SAFE_FREE(v) \
|
|
|
|
|
do { \
|
|
|
|
|
void **_v = (void **)&(v); \
|
|
|
|
|
if (*_v) { \
|
|
|
|
|
MEM_freeN(*_v); \
|
|
|
|
|
*_v = NULL; \
|
|
|
|
|
} \
|
|
|
|
|
} while (0)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/**
|
|
|
|
|
* \name Various Helpers.
|
|
|
|
|
*
|
|
|
|
|
* These functions allow to control the behavior of the guarded allocator, and to retrieve (debug)
|
|
|
|
|
* information about allocated memory.
|
|
|
|
|
*/
|
|
|
|
|
|
2022-01-11 18:01:28 +11:00
|
|
|
/**
|
|
|
|
|
* Print a list of the names and sizes of all allocated memory
|
|
|
|
|
* blocks. as a python dict for easy investigation.
|
|
|
|
|
*/
|
|
|
|
|
extern void (*MEM_printmemlist_pydict)(void);
|
|
|
|
|
|
2021-04-09 18:47:10 +10:00
|
|
|
/**
|
|
|
|
|
* Print a list of the names and sizes of all allocated memory blocks.
|
|
|
|
|
*/
|
2013-10-10 11:58:01 +00:00
|
|
|
extern void (*MEM_printmemlist)(void);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-11-10 21:33:53 +00:00
|
|
|
/** calls the function on all allocated memory blocks. */
|
2013-10-10 11:58:01 +00:00
|
|
|
extern void (*MEM_callbackmemlist)(void (*func)(void *));
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2008-02-01 12:14:15 +00:00
|
|
|
/** Print statistics about memory usage */
|
2013-10-10 11:58:01 +00:00
|
|
|
extern void (*MEM_printmemlist_stats)(void);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2005-07-22 16:40:26 +00:00
|
|
|
/** Set the callback function for error output. */
|
2013-10-10 11:58:01 +00:00
|
|
|
extern void (*MEM_set_error_callback)(void (*func)(const char *));
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
/**
|
2019-05-01 20:50:02 +10:00
|
|
|
* Are the start/end block markers still correct ?
|
|
|
|
|
*
|
2021-04-09 18:47:10 +10:00
|
|
|
* \retval true for correct memory, false for corrupted memory.
|
|
|
|
|
*/
|
2018-03-20 16:51:33 +01:00
|
|
|
extern bool (*MEM_consistency_check)(void);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2006-11-27 13:59:55 +00:00
|
|
|
/** Attempt to enforce OSX (or other OS's) to have malloc and stack nonzero */
|
2013-10-10 11:58:01 +00:00
|
|
|
extern void (*MEM_set_memory_debug)(void);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-05-20 00:24:26 +02:00
|
|
|
/** Memory usage stats. */
|
2014-10-14 16:11:20 +02:00
|
|
|
extern size_t (*MEM_get_memory_in_use)(void);
|
2011-02-21 09:23:34 +00:00
|
|
|
/** Get amount of memory blocks in use. */
|
2013-10-10 11:58:01 +00:00
|
|
|
extern unsigned int (*MEM_get_memory_blocks_in_use)(void);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-02-21 09:23:34 +00:00
|
|
|
/** Reset the peak memory statistic to zero. */
|
2013-10-10 11:58:01 +00:00
|
|
|
extern void (*MEM_reset_peak_memory)(void);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-08-30 16:20:07 +10:00
|
|
|
/** Get the peak memory usage in bytes, including `mmap` allocations. */
|
2013-10-10 11:58:01 +00:00
|
|
|
extern size_t (*MEM_get_peak_memory)(void) ATTR_WARN_UNUSED_RESULT;
|
2010-05-04 12:31:24 +00:00
|
|
|
|
2024-04-16 12:27:13 +10:00
|
|
|
/** Overhead for lockfree allocator (use to avoid slop-space). */
|
2013-10-10 18:18:13 +00:00
|
|
|
#define MEM_SIZE_OVERHEAD sizeof(size_t)
|
2025-10-02 12:55:42 -04:00
|
|
|
#define MEM_SIZE_OPTIMAL(size) ((size) - MEM_SIZE_OVERHEAD)
|
2013-10-10 18:18:13 +00:00
|
|
|
|
2010-10-18 00:25:32 +00:00
|
|
|
#ifndef NDEBUG
|
2013-10-10 11:58:01 +00:00
|
|
|
extern const char *(*MEM_name_ptr)(void *vmemh);
|
2022-08-04 11:12:15 +02:00
|
|
|
/**
|
|
|
|
|
* Change the debugging name/string assigned to the memory allocated at \a vmemh. Only affects the
|
|
|
|
|
* guarded allocator. The name must be a static string, because only a pointer to it is stored!
|
|
|
|
|
*
|
|
|
|
|
* Handy when debugging leaking memory allocated by some often called, generic function with a
|
|
|
|
|
* unspecific name. A caller with more info can set a more specific name, and see which call to the
|
|
|
|
|
* generic function allocates the leaking memory.
|
|
|
|
|
*/
|
|
|
|
|
extern void (*MEM_name_ptr_set)(void *vmemh, const char *str) ATTR_NONNULL();
|
2010-10-18 00:25:32 +00:00
|
|
|
#endif
|
2012-06-25 09:14:37 +00:00
|
|
|
|
2021-04-09 18:47:10 +10:00
|
|
|
/**
|
|
|
|
|
* This should be called as early as possible in the program. When it has been called, information
|
|
|
|
|
* about memory leaks will be printed on exit.
|
|
|
|
|
*/
|
2020-08-01 13:02:21 +10:00
|
|
|
void MEM_init_memleak_detection(void);
|
2020-07-24 12:26:11 +02:00
|
|
|
|
2021-04-09 18:47:10 +10:00
|
|
|
/**
|
|
|
|
|
* When this has been called and memory leaks have been detected, the process will have an exit
|
2020-08-26 22:02:02 +02:00
|
|
|
* code that indicates failure. This can be used for when checking for memory leaks with automated
|
2021-04-09 18:47:10 +10:00
|
|
|
* tests.
|
|
|
|
|
*/
|
2020-08-26 22:02:02 +02:00
|
|
|
void MEM_enable_fail_on_memleak(void);
|
|
|
|
|
|
2024-04-16 12:27:13 +10:00
|
|
|
/**
|
|
|
|
|
* Switch allocator to fast mode, with less tracking.
|
2020-11-17 11:33:47 +01:00
|
|
|
*
|
|
|
|
|
* Use in the production code where performance is the priority, and exact details about allocation
|
|
|
|
|
* is not. This allocator keeps track of number of allocation and amount of allocated bytes, but it
|
|
|
|
|
* does not track of names of allocated blocks.
|
|
|
|
|
*
|
2024-04-16 12:27:13 +10:00
|
|
|
* \note The switch between allocator types can only happen before any allocation did happen.
|
|
|
|
|
*/
|
2020-11-17 11:33:47 +01:00
|
|
|
void MEM_use_lockfree_allocator(void);
|
|
|
|
|
|
2024-04-16 12:27:13 +10:00
|
|
|
/**
|
|
|
|
|
* Switch allocator to slow fully guarded mode.
|
2020-11-17 11:33:47 +01:00
|
|
|
*
|
|
|
|
|
* Use for debug purposes. This allocator contains lock section around every allocator call, which
|
|
|
|
|
* makes it slow. What is gained with this is the ability to have list of allocated blocks (in an
|
2020-11-20 11:39:03 +11:00
|
|
|
* addition to the tracking of number of allocations and amount of allocated bytes).
|
2020-11-17 11:33:47 +01:00
|
|
|
*
|
2024-04-16 12:27:13 +10:00
|
|
|
* \note The switch between allocator types can only happen before any allocation did happen.
|
|
|
|
|
*/
|
2013-10-10 11:58:01 +00:00
|
|
|
void MEM_use_guarded_allocator(void);
|
|
|
|
|
|
2025-04-18 20:24:14 +02:00
|
|
|
/** \} */
|
|
|
|
|
|
2020-01-26 16:38:18 +01:00
|
|
|
#ifdef __cplusplus
|
|
|
|
|
}
|
|
|
|
|
#endif /* __cplusplus */
|
|
|
|
|
|
2012-06-25 09:14:37 +00:00
|
|
|
#ifdef __cplusplus
|
2021-12-17 15:38:15 +01:00
|
|
|
|
2024-07-29 11:47:04 +02:00
|
|
|
# include <any>
|
|
|
|
|
# include <memory>
|
2021-12-27 15:41:56 +01:00
|
|
|
# include <new>
|
2021-12-17 15:38:15 +01:00
|
|
|
# include <type_traits>
|
|
|
|
|
# include <utility>
|
|
|
|
|
|
2024-07-03 17:23:03 +02:00
|
|
|
# include "intern/mallocn_intern_function_pointers.hh"
|
|
|
|
|
|
2024-04-16 12:27:13 +10:00
|
|
|
/**
|
|
|
|
|
* Conservative value of memory alignment returned by non-aligned OS-level memory allocation
|
2024-04-12 17:22:46 +02:00
|
|
|
* functions. For alignments smaller than this value, using non-aligned versions of allocator API
|
2024-04-16 12:27:13 +10:00
|
|
|
* functions is okay, allowing use of `calloc`, for example.
|
|
|
|
|
*/
|
2024-04-12 17:22:46 +02:00
|
|
|
# define MEM_MIN_CPP_ALIGNMENT \
|
|
|
|
|
(__STDCPP_DEFAULT_NEW_ALIGNMENT__ < alignof(void *) ? __STDCPP_DEFAULT_NEW_ALIGNMENT__ : \
|
|
|
|
|
alignof(void *))
|
|
|
|
|
|
2025-04-18 20:24:14 +02:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/**
|
|
|
|
|
* \name Type-aware allocation & construction API.
|
|
|
|
|
*
|
|
|
|
|
* Defines some `new`/`delete`-like helpers, which allocate/free memory using `MEM_guardedalloc`,
|
|
|
|
|
* and construct/destruct the objects.
|
|
|
|
|
*
|
2025-04-23 03:16:19 +00:00
|
|
|
* When possible, it is preferred to use these, even on trivial types, as it makes potential
|
2025-04-18 20:24:14 +02:00
|
|
|
* future changes to these types less disruptive, and is overall closer to standard C++ data
|
|
|
|
|
* creation and destruction.
|
|
|
|
|
*
|
|
|
|
|
* However, if the type is trivial, `MEM_[cm]allocN<T>` and related functions can be used to
|
|
|
|
|
* allocate an object that will be managed by external historic code still using C-style
|
|
|
|
|
* allocation/duplication/freeing.
|
|
|
|
|
*
|
|
|
|
|
* \{ */
|
|
|
|
|
|
Allocator: Add `MEM_new_for_free` to allow construction of almost-trivial types.
The data constructed by this call remains in the 'C-alloc' realm, i.e.
it can be `MEM_dupallocN`'ed, and `MEM_freeN`'ed.
This is intended as a temporary API only, to facilitate transition to
full C++ handling of data in Blender. It's primary target is to allow
pseudo-POD types to use default values for their members. See e.g.
!134531.
Unlike !143827 and !138829, it does not change the current rule (`new`
must be paired with `delete`, and `alloc` must be paired with `free`).
Instead, it defines an explicit and temporary API to allow a very
limited form of construction to happen on C-allocated data, provided
that the type is default-constructible, and remains trivial after
construction.
### Notes
* The new API is purposely as restrictive as possible, trying to
only allow the current known needs (init with default member values).
This can easily be extended if needed.
* To try to stay as close as malloc/calloc behavior as possible, and
avoid the 'zero-initialization' gotcha, it does not use
value-initialization, but instead default-initialization on zero-
initialized memory.
_Ideally it would even not allow any user-defined default constructor,
but this does not seem simple to detect._
Pull Request: https://projects.blender.org/blender/blender/pulls/144141
2025-08-11 14:56:11 +02:00
|
|
|
namespace mem_guarded::internal {
|
|
|
|
|
/* Note that we intentionally don't care about a non-trivial default constructor here. */
|
|
|
|
|
template<typename T>
|
|
|
|
|
constexpr bool is_trivial_after_construction = std::is_trivially_copyable_v<T> &&
|
|
|
|
|
std::is_trivially_destructible_v<T>;
|
|
|
|
|
} // namespace mem_guarded::internal
|
|
|
|
|
|
2021-12-17 15:38:15 +01:00
|
|
|
/**
|
2024-09-03 12:58:26 +02:00
|
|
|
* Allocate new memory for an object of type #T, and construct it.
|
|
|
|
|
* #MEM_delete must be used to delete the object. Calling #MEM_freeN on it is illegal.
|
2022-02-04 16:40:55 +01:00
|
|
|
*
|
2025-02-17 16:18:04 +01:00
|
|
|
* Do not assume that this ever zero-initializes memory (even when it does), explicitly initialize.
|
|
|
|
|
*
|
|
|
|
|
* Although calling this without arguments will cause zero-initialization for many types, simple
|
|
|
|
|
* changes to the type can break this. Basic explanation:
|
|
|
|
|
* With no arguments, this will initialize using `T()` (value initialization) not `T` (default
|
|
|
|
|
* initialization). Details are involved, but for "C-style" structs ("Plain old Data" structs or
|
|
|
|
|
* structs with a compiler generated constructor) memory will be zero-initialized. A change like
|
|
|
|
|
* simply adding a custom default constructor would change initialization behavior.
|
|
|
|
|
* See: https://stackoverflow.com/a/4982720, https://stackoverflow.com/a/620402
|
2021-12-17 15:38:15 +01:00
|
|
|
*/
|
|
|
|
|
template<typename T, typename... Args>
|
|
|
|
|
inline T *MEM_new(const char *allocation_name, Args &&...args)
|
|
|
|
|
{
|
2024-07-03 17:23:03 +02:00
|
|
|
void *buffer = mem_guarded::internal::mem_mallocN_aligned_ex(
|
|
|
|
|
sizeof(T), alignof(T), allocation_name, mem_guarded::internal::AllocationType::NEW_DELETE);
|
2021-12-17 15:38:15 +01:00
|
|
|
return new (buffer) T(std::forward<Args>(args)...);
|
|
|
|
|
}
|
|
|
|
|
|
Allocator: Add `MEM_new_for_free` to allow construction of almost-trivial types.
The data constructed by this call remains in the 'C-alloc' realm, i.e.
it can be `MEM_dupallocN`'ed, and `MEM_freeN`'ed.
This is intended as a temporary API only, to facilitate transition to
full C++ handling of data in Blender. It's primary target is to allow
pseudo-POD types to use default values for their members. See e.g.
!134531.
Unlike !143827 and !138829, it does not change the current rule (`new`
must be paired with `delete`, and `alloc` must be paired with `free`).
Instead, it defines an explicit and temporary API to allow a very
limited form of construction to happen on C-allocated data, provided
that the type is default-constructible, and remains trivial after
construction.
### Notes
* The new API is purposely as restrictive as possible, trying to
only allow the current known needs (init with default member values).
This can easily be extended if needed.
* To try to stay as close as malloc/calloc behavior as possible, and
avoid the 'zero-initialization' gotcha, it does not use
value-initialization, but instead default-initialization on zero-
initialized memory.
_Ideally it would even not allow any user-defined default constructor,
but this does not seem simple to detect._
Pull Request: https://projects.blender.org/blender/blender/pulls/144141
2025-08-11 14:56:11 +02:00
|
|
|
/**
|
|
|
|
|
* Allocate new memory for an object of type #T, and construct it with its default constructor.
|
|
|
|
|
* Both #MEM_delete and #MEM_freeN can be used to delete the object.
|
|
|
|
|
*
|
|
|
|
|
* Designed to be used with 'pseudo-POD' types, that are trivially copyable and destructible, but
|
|
|
|
|
* not trivially constructible. Once constructed, this data can be managed as a C-type one (using
|
|
|
|
|
* `MEM_dupallocN`, `MEM_freeN`, safely assigned to a void pointer and freed as such, etc.).
|
|
|
|
|
*
|
|
|
|
|
* The typical use-cases are C-like structs containing only trivial data, that define default
|
|
|
|
|
* values for (some of) their members.
|
|
|
|
|
*
|
|
|
|
|
* \note This function uses 'default initialization' on zero-initialized memory, _not_ 'value
|
|
|
|
|
* initialization'. This means that even if a user-defined default constructor is provided,
|
2025-09-06 09:19:08 +10:00
|
|
|
* non-explicitly initialized data will be zero-initialized. For POD types (e.g. pure C-style
|
|
|
|
|
* structs), its behavior is functionally identical to using `MEM_callocN<T>()`.
|
Allocator: Add `MEM_new_for_free` to allow construction of almost-trivial types.
The data constructed by this call remains in the 'C-alloc' realm, i.e.
it can be `MEM_dupallocN`'ed, and `MEM_freeN`'ed.
This is intended as a temporary API only, to facilitate transition to
full C++ handling of data in Blender. It's primary target is to allow
pseudo-POD types to use default values for their members. See e.g.
!134531.
Unlike !143827 and !138829, it does not change the current rule (`new`
must be paired with `delete`, and `alloc` must be paired with `free`).
Instead, it defines an explicit and temporary API to allow a very
limited form of construction to happen on C-allocated data, provided
that the type is default-constructible, and remains trivial after
construction.
### Notes
* The new API is purposely as restrictive as possible, trying to
only allow the current known needs (init with default member values).
This can easily be extended if needed.
* To try to stay as close as malloc/calloc behavior as possible, and
avoid the 'zero-initialization' gotcha, it does not use
value-initialization, but instead default-initialization on zero-
initialized memory.
_Ideally it would even not allow any user-defined default constructor,
but this does not seem simple to detect._
Pull Request: https://projects.blender.org/blender/blender/pulls/144141
2025-08-11 14:56:11 +02:00
|
|
|
*
|
|
|
|
|
* \warning This function is intended as a temporary work-around during the process of converting
|
|
|
|
|
* Blender data management from C-style (alloc/free) to C++-style (new/delete). It will be removed
|
|
|
|
|
* once not needed anymore (i.e. mainly when there is no more need to dupalloc and free untyped
|
|
|
|
|
* data stored in void pointers).
|
|
|
|
|
*/
|
|
|
|
|
template<typename T> inline T *MEM_new_for_free(const char *allocation_name)
|
|
|
|
|
{
|
|
|
|
|
static_assert(mem_guarded::internal::is_trivial_after_construction<T>,
|
|
|
|
|
"MEM_new_for_free can only construct types that are trivially copyable and "
|
|
|
|
|
"destructible, use MEM_new instead.");
|
|
|
|
|
void *buffer;
|
|
|
|
|
/* There is no lower level #calloc with an alignment parameter, so unless the alignment is less
|
|
|
|
|
* than or equal to what we'd get by default, we have to fall back to #memset unfortunately. */
|
|
|
|
|
if (alignof(T) <= MEM_MIN_CPP_ALIGNMENT) {
|
|
|
|
|
buffer = MEM_callocN(sizeof(T), allocation_name);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
buffer = mem_guarded::internal::mem_mallocN_aligned_ex(
|
|
|
|
|
sizeof(T), alignof(T), allocation_name, mem_guarded::internal::AllocationType::ALLOC_FREE);
|
|
|
|
|
memset(buffer, 0, sizeof(T));
|
|
|
|
|
}
|
|
|
|
|
return new (buffer) T;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-26 18:09:44 +02:00
|
|
|
/**
|
2024-09-03 12:58:26 +02:00
|
|
|
* Destruct and deallocate an object previously allocated and constructed with #MEM_new, or some
|
|
|
|
|
* type-overloaded `new` operators using MEM_guardedalloc as backend.
|
|
|
|
|
*
|
|
|
|
|
* As with the `delete` C++ operator, passing in `nullptr` is allowed and does nothing.
|
|
|
|
|
*
|
2025-03-05 16:35:09 +01:00
|
|
|
* It is illegal to call this function with data allocated by the C-style allocation functions of
|
|
|
|
|
* this module.
|
2022-08-26 18:09:44 +02:00
|
|
|
*/
|
|
|
|
|
template<typename T> inline void MEM_delete(const T *ptr)
|
|
|
|
|
{
|
2024-09-03 12:58:26 +02:00
|
|
|
static_assert(
|
|
|
|
|
!std::is_void_v<T>,
|
|
|
|
|
"MEM_delete on a void pointer is not possible, `static_cast` it to the correct type");
|
2022-08-26 18:09:44 +02:00
|
|
|
if (ptr == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
Allocator: Properly free polymorphic objects
Currently `MEM_delete` frees pointers expecting that they match to the
pointers allocated with `MEM_new`, otherwise it can cause undefined
behavior when freeing memory(using `--debug-memory` flag breaks in
place, if not it can corrupts other data, generating a incorrect back-traces).
However polymorphic objects lifetime can be managed by pointer of their
most derived type or by any pointer in their ancestor tree that defines
a virtual destructor, which sometimes can differ in offset when pointing to
the same object.
This changes ensures the correct pointer is being freed, by using the pointer
to the most derived type (returned by`dynamic_cast<void *>(...);`[0]).
----------
[0] = [dynamic_cast](https://en.cppreference.com/w/cpp/language/dynamic_cast.html): `a) If expression is a pointer to (possibly cv-qualified) void, the result is a pointer to the most derived object pointed to by expression.`
-----------
As an example, given the followings structs:
```c++
struct A {
int a;
virtual ~A() = default;
};
struct B {
int b;
virtual ~B() = default;
};
struct Derived : public A , public B {
int c;
};
std::unique_ptr<A> a_ptr = std::make_unique<Derived>();
std::unique_ptr<B> b_ptr = std::make_unique<Derived>();
```
Using std smart pointers to manage `Derived` objects can be done with `A`
or `B` pointers.
However if a `Derived` object memory is managed with `MEM_delete`,
using a `B` pointer for freeing the memory currently may silently break Blender,
since it don't accounts for the full object memory layout, the `dynamic_cast<void *>(ptr);`
cast gives a more safe pointer for freeing the memory.
Note that object destruction is successfully handled through the virtual destructor.
----------
This instead could be an assert to ensure polymorphic objects to be deleted
as the most derived object type.
Pull Request: https://projects.blender.org/blender/blender/pulls/146269
2025-09-23 16:50:43 +02:00
|
|
|
const void *complete_ptr = [ptr]() {
|
|
|
|
|
if constexpr (std::is_polymorphic_v<T>) {
|
|
|
|
|
/* Polymorphic objects lifetime can be managed with pointers to their most derived type or
|
|
|
|
|
* with pointers to any of their ancestor types in their hierarchy tree that define a virtual
|
|
|
|
|
* destructor, however ancestor pointers may differ in a offset from the same derived object.
|
|
|
|
|
* For freeing the correct memory allocated with #MEM_new, we need to ensure that the given
|
|
|
|
|
* pointer is equal to the pointer to the most derived object, which can be obtained with
|
|
|
|
|
* `dynamic_cast<void *>(ptr)`. */
|
|
|
|
|
return dynamic_cast<const void *>(ptr);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return static_cast<const void *>(ptr);
|
|
|
|
|
}
|
|
|
|
|
}();
|
2024-04-16 12:27:13 +10:00
|
|
|
/* C++ allows destruction of `const` objects, so the pointer is allowed to be `const`. */
|
2022-08-26 18:09:44 +02:00
|
|
|
ptr->~T();
|
Allocator: Properly free polymorphic objects
Currently `MEM_delete` frees pointers expecting that they match to the
pointers allocated with `MEM_new`, otherwise it can cause undefined
behavior when freeing memory(using `--debug-memory` flag breaks in
place, if not it can corrupts other data, generating a incorrect back-traces).
However polymorphic objects lifetime can be managed by pointer of their
most derived type or by any pointer in their ancestor tree that defines
a virtual destructor, which sometimes can differ in offset when pointing to
the same object.
This changes ensures the correct pointer is being freed, by using the pointer
to the most derived type (returned by`dynamic_cast<void *>(...);`[0]).
----------
[0] = [dynamic_cast](https://en.cppreference.com/w/cpp/language/dynamic_cast.html): `a) If expression is a pointer to (possibly cv-qualified) void, the result is a pointer to the most derived object pointed to by expression.`
-----------
As an example, given the followings structs:
```c++
struct A {
int a;
virtual ~A() = default;
};
struct B {
int b;
virtual ~B() = default;
};
struct Derived : public A , public B {
int c;
};
std::unique_ptr<A> a_ptr = std::make_unique<Derived>();
std::unique_ptr<B> b_ptr = std::make_unique<Derived>();
```
Using std smart pointers to manage `Derived` objects can be done with `A`
or `B` pointers.
However if a `Derived` object memory is managed with `MEM_delete`,
using a `B` pointer for freeing the memory currently may silently break Blender,
since it don't accounts for the full object memory layout, the `dynamic_cast<void *>(ptr);`
cast gives a more safe pointer for freeing the memory.
Note that object destruction is successfully handled through the virtual destructor.
----------
This instead could be an assert to ensure polymorphic objects to be deleted
as the most derived object type.
Pull Request: https://projects.blender.org/blender/blender/pulls/146269
2025-09-23 16:50:43 +02:00
|
|
|
mem_guarded::internal::mem_freeN_ex(const_cast<void *>(complete_ptr),
|
2024-07-03 17:23:03 +02:00
|
|
|
mem_guarded::internal::AllocationType::NEW_DELETE);
|
2022-08-26 18:09:44 +02:00
|
|
|
}
|
|
|
|
|
|
2025-02-20 10:37:10 +01:00
|
|
|
/**
|
|
|
|
|
* Helper shortcut to #MEM_delete, that also ensures that the target pointer is set to nullptr
|
|
|
|
|
* after deleting it.
|
|
|
|
|
*/
|
|
|
|
|
# define MEM_SAFE_DELETE(v) \
|
|
|
|
|
do { \
|
|
|
|
|
if (v) { \
|
|
|
|
|
MEM_delete(v); \
|
|
|
|
|
(v) = nullptr; \
|
|
|
|
|
} \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
2025-04-18 20:24:14 +02:00
|
|
|
/** Define overloaded new/delete operators for C++ types. */
|
|
|
|
|
# define MEM_CXX_CLASS_ALLOC_FUNCS(_id) \
|
|
|
|
|
public: \
|
|
|
|
|
void *operator new(size_t num_bytes) \
|
|
|
|
|
{ \
|
|
|
|
|
return mem_guarded::internal::mem_mallocN_aligned_ex( \
|
|
|
|
|
num_bytes, \
|
|
|
|
|
__STDCPP_DEFAULT_NEW_ALIGNMENT__, \
|
|
|
|
|
_id, \
|
|
|
|
|
mem_guarded::internal::AllocationType::NEW_DELETE); \
|
|
|
|
|
} \
|
|
|
|
|
void *operator new(size_t num_bytes, std::align_val_t alignment) \
|
|
|
|
|
{ \
|
|
|
|
|
return mem_guarded::internal::mem_mallocN_aligned_ex( \
|
|
|
|
|
num_bytes, size_t(alignment), _id, mem_guarded::internal::AllocationType::NEW_DELETE); \
|
|
|
|
|
} \
|
|
|
|
|
void operator delete(void *mem) \
|
|
|
|
|
{ \
|
|
|
|
|
if (mem) { \
|
|
|
|
|
mem_guarded::internal::mem_freeN_ex(mem, \
|
|
|
|
|
mem_guarded::internal::AllocationType::NEW_DELETE); \
|
|
|
|
|
} \
|
|
|
|
|
} \
|
|
|
|
|
void *operator new[](size_t num_bytes) \
|
|
|
|
|
{ \
|
|
|
|
|
return mem_guarded::internal::mem_mallocN_aligned_ex( \
|
|
|
|
|
num_bytes, \
|
|
|
|
|
__STDCPP_DEFAULT_NEW_ALIGNMENT__, \
|
|
|
|
|
_id "[]", \
|
|
|
|
|
mem_guarded::internal::AllocationType::NEW_DELETE); \
|
|
|
|
|
} \
|
|
|
|
|
void *operator new[](size_t num_bytes, std::align_val_t alignment) \
|
|
|
|
|
{ \
|
|
|
|
|
return mem_guarded::internal::mem_mallocN_aligned_ex( \
|
|
|
|
|
num_bytes, \
|
|
|
|
|
size_t(alignment), \
|
|
|
|
|
_id "[]", \
|
|
|
|
|
mem_guarded::internal::AllocationType::NEW_DELETE); \
|
|
|
|
|
} \
|
|
|
|
|
void operator delete[](void *mem) \
|
|
|
|
|
{ \
|
|
|
|
|
if (mem) { \
|
|
|
|
|
mem_guarded::internal::mem_freeN_ex(mem, \
|
|
|
|
|
mem_guarded::internal::AllocationType::NEW_DELETE); \
|
|
|
|
|
} \
|
|
|
|
|
} \
|
|
|
|
|
void *operator new(size_t /*count*/, void *ptr) \
|
|
|
|
|
{ \
|
|
|
|
|
return ptr; \
|
|
|
|
|
} \
|
|
|
|
|
/** \
|
|
|
|
|
* This is the matching delete operator to the placement-new operator above. \
|
|
|
|
|
* Both parameters \
|
|
|
|
|
* will have the same value. Without this, we get the warning C4291 on windows. \
|
|
|
|
|
*/ \
|
|
|
|
|
void operator delete(void * /*ptr_to_free*/, void * /*ptr*/) {}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/**
|
|
|
|
|
* \name Type-aware allocation API.
|
|
|
|
|
*
|
|
|
|
|
* Templated, type-safe versions of C-style allocation & freeing API.
|
|
|
|
|
*
|
|
|
|
|
* These functions only allocate or free memory, without any calls to constructors or destructors.
|
|
|
|
|
*
|
|
|
|
|
* \note 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 by this macro). GCC and clang (both on linux, OSX, and clang-cl on Windows on Arm) do
|
|
|
|
|
* not. So for now, `MEM_[cm]allocN<T>` and related templates use slightly more relaxed checks on
|
|
|
|
|
* MSVC. These should still catch most of the real-life invalid cases.
|
|
|
|
|
*
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2021-12-17 15:38:15 +01:00
|
|
|
/**
|
2024-09-03 12:58:26 +02:00
|
|
|
* Allocate zero-initialized memory for an object of type #T. The constructor of #T is not called,
|
2025-03-05 16:35:09 +01:00
|
|
|
* 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.
|
2024-09-03 12:58:26 +02:00
|
|
|
*
|
|
|
|
|
* #MEM_freeN must be used to free a pointer returned by this call. Calling #MEM_delete on it is
|
|
|
|
|
* illegal.
|
2021-12-17 15:38:15 +01:00
|
|
|
*/
|
2025-03-05 16:35:09 +01:00
|
|
|
template<typename T> inline T *MEM_callocN(const char *allocation_name)
|
2021-12-17 15:38:15 +01:00
|
|
|
{
|
2025-03-05 16:35:09 +01:00
|
|
|
# ifdef _MSC_VER
|
|
|
|
|
static_assert(std::is_trivially_constructible_v<T>,
|
|
|
|
|
"For non-trivial types, MEM_new must be used.");
|
|
|
|
|
# else
|
2024-09-03 12:58:26 +02:00
|
|
|
static_assert(std::is_trivial_v<T>, "For non-trivial types, MEM_new must be used.");
|
2025-03-05 16:35:09 +01:00
|
|
|
# endif
|
2024-04-15 19:11:06 +02:00
|
|
|
return static_cast<T *>(MEM_calloc_arrayN_aligned(1, sizeof(T), alignof(T), allocation_name));
|
2021-12-17 15:38:15 +01:00
|
|
|
}
|
|
|
|
|
|
2022-08-26 18:09:44 +02:00
|
|
|
/**
|
2025-03-05 16:35:09 +01:00
|
|
|
* Type-safe version of #MEM_calloc_arrayN/#MEM_calloc_array_alignedN.
|
2025-04-18 20:24:14 +02:00
|
|
|
*
|
|
|
|
|
* It has the same restrictions and limitations as the type-safe version of #MEM_callocN<T>.
|
2022-08-26 18:09:44 +02:00
|
|
|
*/
|
2025-03-05 16:35:09 +01:00
|
|
|
template<typename T> inline T *MEM_calloc_arrayN(const size_t length, const char *allocation_name)
|
2022-08-26 18:09:44 +02:00
|
|
|
{
|
2025-03-05 16:35:09 +01:00
|
|
|
# ifdef _MSC_VER
|
|
|
|
|
static_assert(std::is_trivially_constructible_v<T>,
|
|
|
|
|
"For non-trivial types, MEM_new must be used.");
|
|
|
|
|
# else
|
2024-09-03 12:58:26 +02:00
|
|
|
static_assert(std::is_trivial_v<T>, "For non-trivial types, MEM_new must be used.");
|
2025-03-05 16:35:09 +01:00
|
|
|
# endif
|
2024-04-15 19:11:06 +02:00
|
|
|
return static_cast<T *>(
|
|
|
|
|
MEM_calloc_arrayN_aligned(length, sizeof(T), alignof(T), allocation_name));
|
2025-03-05 19:04:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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
|
|
|
|
|
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.
|
2025-04-18 20:24:14 +02:00
|
|
|
*
|
|
|
|
|
* It has the same restrictions and limitations as the type-safe version of #MEM_mallocN<T>.
|
2025-03-05 19:04:09 +01:00
|
|
|
*/
|
|
|
|
|
template<typename T> inline T *MEM_malloc_arrayN(const size_t length, const char *allocation_name)
|
|
|
|
|
{
|
|
|
|
|
# ifdef _MSC_VER
|
|
|
|
|
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));
|
2022-08-26 18:09:44 +02:00
|
|
|
}
|
|
|
|
|
|
2022-03-16 12:25:44 +01:00
|
|
|
/**
|
2024-09-03 12:58:26 +02:00
|
|
|
* Allocate memory for an object of type #T and memory-copy `other` into it.
|
|
|
|
|
* Only applicable for trivial types.
|
2022-03-16 12:25:44 +01:00
|
|
|
*
|
2024-09-03 12:58:26 +02:00
|
|
|
* This function works around the problem of copy-constructing DNA structs which contains
|
|
|
|
|
* deprecated fields: some compilers will generate access deprecated field warnings in implicitly
|
|
|
|
|
* defined copy constructors.
|
2022-03-16 12:25:44 +01:00
|
|
|
*
|
2025-03-05 16:35:09 +01:00
|
|
|
* This is a better alternative to the C-style implementation of #MEM_dupallocN, unless the source
|
|
|
|
|
* is an array or of a non-fully-defined type.
|
2022-03-16 12:25:44 +01:00
|
|
|
*/
|
2025-03-05 16:35:09 +01:00
|
|
|
template<typename T> inline T *MEM_dupallocN(const char *allocation_name, const T &other)
|
2022-03-16 12:25:44 +01:00
|
|
|
{
|
2025-03-05 16:35:09 +01:00
|
|
|
# ifdef _MSC_VER
|
Allocator: Add `MEM_new_for_free` to allow construction of almost-trivial types.
The data constructed by this call remains in the 'C-alloc' realm, i.e.
it can be `MEM_dupallocN`'ed, and `MEM_freeN`'ed.
This is intended as a temporary API only, to facilitate transition to
full C++ handling of data in Blender. It's primary target is to allow
pseudo-POD types to use default values for their members. See e.g.
!134531.
Unlike !143827 and !138829, it does not change the current rule (`new`
must be paired with `delete`, and `alloc` must be paired with `free`).
Instead, it defines an explicit and temporary API to allow a very
limited form of construction to happen on C-allocated data, provided
that the type is default-constructible, and remains trivial after
construction.
### Notes
* The new API is purposely as restrictive as possible, trying to
only allow the current known needs (init with default member values).
This can easily be extended if needed.
* To try to stay as close as malloc/calloc behavior as possible, and
avoid the 'zero-initialization' gotcha, it does not use
value-initialization, but instead default-initialization on zero-
initialized memory.
_Ideally it would even not allow any user-defined default constructor,
but this does not seem simple to detect._
Pull Request: https://projects.blender.org/blender/blender/pulls/144141
2025-08-11 14:56:11 +02:00
|
|
|
static_assert(std::is_trivially_assignable_v<T &, T> && std::is_trivially_destructible_v<T>,
|
|
|
|
|
"MEM_dupallocN can only duplicate types that are trivially copyable and "
|
|
|
|
|
"destructible, use MEM_new instead.");
|
2025-03-05 16:35:09 +01:00
|
|
|
# else
|
Allocator: Add `MEM_new_for_free` to allow construction of almost-trivial types.
The data constructed by this call remains in the 'C-alloc' realm, i.e.
it can be `MEM_dupallocN`'ed, and `MEM_freeN`'ed.
This is intended as a temporary API only, to facilitate transition to
full C++ handling of data in Blender. It's primary target is to allow
pseudo-POD types to use default values for their members. See e.g.
!134531.
Unlike !143827 and !138829, it does not change the current rule (`new`
must be paired with `delete`, and `alloc` must be paired with `free`).
Instead, it defines an explicit and temporary API to allow a very
limited form of construction to happen on C-allocated data, provided
that the type is default-constructible, and remains trivial after
construction.
### Notes
* The new API is purposely as restrictive as possible, trying to
only allow the current known needs (init with default member values).
This can easily be extended if needed.
* To try to stay as close as malloc/calloc behavior as possible, and
avoid the 'zero-initialization' gotcha, it does not use
value-initialization, but instead default-initialization on zero-
initialized memory.
_Ideally it would even not allow any user-defined default constructor,
but this does not seem simple to detect._
Pull Request: https://projects.blender.org/blender/blender/pulls/144141
2025-08-11 14:56:11 +02:00
|
|
|
static_assert(mem_guarded::internal::is_trivial_after_construction<T>,
|
|
|
|
|
"MEM_dupallocN can only duplicate types that are trivially copyable and "
|
|
|
|
|
"destructible, use MEM_new instead.");
|
2025-03-05 16:35:09 +01:00
|
|
|
# endif
|
2024-06-19 20:11:48 +02:00
|
|
|
T *new_object = static_cast<T *>(MEM_mallocN_aligned(sizeof(T), alignof(T), allocation_name));
|
2022-08-26 18:09:44 +02:00
|
|
|
if (new_object) {
|
|
|
|
|
memcpy(new_object, &other, sizeof(T));
|
2021-12-17 15:38:15 +01:00
|
|
|
}
|
2022-08-26 18:09:44 +02:00
|
|
|
return new_object;
|
2021-12-17 15:38:15 +01:00
|
|
|
}
|
|
|
|
|
|
2025-02-20 10:37:10 +01:00
|
|
|
template<typename T> inline void MEM_freeN(T *ptr)
|
|
|
|
|
{
|
2025-02-21 10:30:29 +01:00
|
|
|
# ifdef _MSC_VER
|
|
|
|
|
static_assert(std::is_trivially_destructible_v<T>,
|
Allocator: Add `MEM_new_for_free` to allow construction of almost-trivial types.
The data constructed by this call remains in the 'C-alloc' realm, i.e.
it can be `MEM_dupallocN`'ed, and `MEM_freeN`'ed.
This is intended as a temporary API only, to facilitate transition to
full C++ handling of data in Blender. It's primary target is to allow
pseudo-POD types to use default values for their members. See e.g.
!134531.
Unlike !143827 and !138829, it does not change the current rule (`new`
must be paired with `delete`, and `alloc` must be paired with `free`).
Instead, it defines an explicit and temporary API to allow a very
limited form of construction to happen on C-allocated data, provided
that the type is default-constructible, and remains trivial after
construction.
### Notes
* The new API is purposely as restrictive as possible, trying to
only allow the current known needs (init with default member values).
This can easily be extended if needed.
* To try to stay as close as malloc/calloc behavior as possible, and
avoid the 'zero-initialization' gotcha, it does not use
value-initialization, but instead default-initialization on zero-
initialized memory.
_Ideally it would even not allow any user-defined default constructor,
but this does not seem simple to detect._
Pull Request: https://projects.blender.org/blender/blender/pulls/144141
2025-08-11 14:56:11 +02:00
|
|
|
"MEM_freeN can only free types that are trivially copyable and destructible, use "
|
|
|
|
|
"MEM_delete instead.");
|
2025-02-21 10:30:29 +01:00
|
|
|
# else
|
Allocator: Add `MEM_new_for_free` to allow construction of almost-trivial types.
The data constructed by this call remains in the 'C-alloc' realm, i.e.
it can be `MEM_dupallocN`'ed, and `MEM_freeN`'ed.
This is intended as a temporary API only, to facilitate transition to
full C++ handling of data in Blender. It's primary target is to allow
pseudo-POD types to use default values for their members. See e.g.
!134531.
Unlike !143827 and !138829, it does not change the current rule (`new`
must be paired with `delete`, and `alloc` must be paired with `free`).
Instead, it defines an explicit and temporary API to allow a very
limited form of construction to happen on C-allocated data, provided
that the type is default-constructible, and remains trivial after
construction.
### Notes
* The new API is purposely as restrictive as possible, trying to
only allow the current known needs (init with default member values).
This can easily be extended if needed.
* To try to stay as close as malloc/calloc behavior as possible, and
avoid the 'zero-initialization' gotcha, it does not use
value-initialization, but instead default-initialization on zero-
initialized memory.
_Ideally it would even not allow any user-defined default constructor,
but this does not seem simple to detect._
Pull Request: https://projects.blender.org/blender/blender/pulls/144141
2025-08-11 14:56:11 +02:00
|
|
|
static_assert(mem_guarded::internal::is_trivial_after_construction<T>,
|
|
|
|
|
"MEM_freeN can only free types that are trivially copyable and destructible, use "
|
|
|
|
|
"MEM_delete instead.");
|
2025-02-20 10:37:10 +01:00
|
|
|
# endif
|
2025-02-20 16:42:22 +01:00
|
|
|
mem_guarded::internal::mem_freeN_ex(const_cast<void *>(static_cast<const void *>(ptr)),
|
|
|
|
|
mem_guarded::internal::AllocationType::ALLOC_FREE);
|
2025-02-20 10:37:10 +01:00
|
|
|
}
|
|
|
|
|
|
2025-04-18 20:24:14 +02:00
|
|
|
/** \} */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-07-29 11:47:04 +02:00
|
|
|
/**
|
|
|
|
|
* Construct a T that will only be destructed after leak detection is run.
|
|
|
|
|
*
|
|
|
|
|
* This call is thread-safe. Calling code should typically keep a reference to that data as a
|
|
|
|
|
* `static thread_local` variable, or use some lock, to prevent concurrent accesses.
|
|
|
|
|
*
|
|
|
|
|
* The returned value should not own any memory allocated with `MEM_*` functions, since these would
|
|
|
|
|
* then be detected as leaked.
|
|
|
|
|
*/
|
|
|
|
|
template<typename T, typename... Args> T &MEM_construct_leak_detection_data(Args &&...args)
|
|
|
|
|
{
|
|
|
|
|
std::shared_ptr<T> data = std::make_shared<T>(std::forward<Args>(args)...);
|
|
|
|
|
std::any any_data = std::make_any<std::shared_ptr<T>>(data);
|
|
|
|
|
mem_guarded::internal::add_memleak_data(any_data);
|
|
|
|
|
return *data;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-14 12:47:49 +00:00
|
|
|
#endif /* __cplusplus */
|
2012-06-25 09:14:37 +00:00
|
|
|
|
2012-07-14 12:47:49 +00:00
|
|
|
#endif /* __MEM_GUARDEDALLOC_H__ */
|