mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
This change removes the need to to carefully pair MEM_mallocN/MEM_new_for_free with MEM_freeN and MEM_new with MEM_delete. Instead all function are now variations of MEM_new and MEM_delete. * MEM_new_for_free -> MEM_new * MEM_mallocN -> MEM_new_uninitialized * MEM_callocN -> MEM_new_zeroed * MEM_freeN -> MEM_delete (for typed pointers) * MEM_freeN -> MEM_delete_void (for void pointers) * MEM_reallocN -> MEM_realloc_uninitialized * MEM_recallocN -> MEM_realloc_zeroed * MEM_dupallocN -> MEM_new (when possible) * MEM_dupallocN -> MEM_dupalloc (for typed pointers) * MEM_dupallocN -> MEM_dupalloc_void (for void pointers) The MEM_malloc and MEM_calloc functions were renamed to make it clear that they should be paired with MEM_delete and to clarify what they do. The compiler will emit an error if MEM_delete or MEM_delete_void is used on the wrong pointer type. However a remaining risk is casting a MEM_new allocation with a non-trivial destructor to a void pointer (which is the same as before). This is why MEM_delete_void and MEM_dupalloc_void exist as a separate functions, to identify legacy code that has this risk and should be eliminated over time. Note that MEM_new_array only supports trivially destructible types. This avoids the need for an equivalent of the delete [] operator and the associated mistakes that can be made. Instead MEM_delete can be used for everything. For non-trivial types, data structures like Vector should be used instead. MEM_dupalloc is now also more type safe. As before it is only supported on trivially copyable types, and this is now enforced through static asserts to prevent mistakes. It is also templated to remove the need for casts. Pull Request: https://projects.blender.org/blender/blender/pulls/151387
147 lines
2.8 KiB
C++
147 lines
2.8 KiB
C++
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 */
|
|
|
|
#pragma once
|
|
|
|
#include <cstddef>
|
|
|
|
#include "util/defines.h"
|
|
#include "util/guarded_allocator.h"
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
/* Stack allocator for the use with STL. */
|
|
template<int SIZE, typename T> class ccl_try_align(16) StackAllocator {
|
|
public:
|
|
using size_type = size_t;
|
|
using difference_type = ptrdiff_t;
|
|
using pointer = T *;
|
|
using const_pointer = const T *;
|
|
using reference = T &;
|
|
using const_reference = const T &;
|
|
using value_type = T;
|
|
|
|
/* Allocator construction/destruction. */
|
|
|
|
StackAllocator() : pointer_(0), use_stack_(true) {}
|
|
|
|
StackAllocator(const StackAllocator & /*unused*/) : pointer_(0), use_stack_(true) {}
|
|
|
|
template<class U>
|
|
StackAllocator(const StackAllocator<SIZE, U> & /*unused*/) : pointer_(0), use_stack_(false)
|
|
{
|
|
}
|
|
|
|
/* Memory allocation/deallocation. */
|
|
|
|
T *allocate(const size_t n, const void *hint = nullptr)
|
|
{
|
|
(void)hint;
|
|
if (n == 0) {
|
|
return nullptr;
|
|
}
|
|
if (pointer_ + n >= SIZE || use_stack_ == false) {
|
|
size_t size = n * sizeof(T);
|
|
util_guarded_mem_alloc(size);
|
|
T *mem;
|
|
#ifdef WITH_BLENDER_GUARDEDALLOC
|
|
mem = (T *)MEM_new_uninitialized_aligned(size, 16, "Cycles Alloc");
|
|
#else
|
|
mem = (T *)malloc(size);
|
|
#endif
|
|
if (mem == nullptr) {
|
|
throw std::bad_alloc();
|
|
}
|
|
return mem;
|
|
}
|
|
T *mem = &data_[pointer_];
|
|
pointer_ += n;
|
|
return mem;
|
|
}
|
|
|
|
void deallocate(T *p, const size_t n)
|
|
{
|
|
if (p == nullptr) {
|
|
return;
|
|
}
|
|
if (p < data_ || p >= data_ + SIZE) {
|
|
util_guarded_mem_free(n * sizeof(T));
|
|
#ifdef WITH_BLENDER_GUARDEDALLOC
|
|
MEM_delete_void(static_cast<void *>(p));
|
|
#else
|
|
free(p);
|
|
#endif
|
|
return;
|
|
}
|
|
/* We don't support memory free for the stack allocator. */
|
|
}
|
|
|
|
/* Address of an reference. */
|
|
|
|
T *address(T &x) const
|
|
{
|
|
return &x;
|
|
}
|
|
|
|
const T *address(const T &x) const
|
|
{
|
|
return &x;
|
|
}
|
|
|
|
/* Object construction/destruction. */
|
|
|
|
void construct(T *p, const T &val)
|
|
{
|
|
if (p != nullptr) {
|
|
new (p) T(val);
|
|
}
|
|
}
|
|
|
|
void destroy(T *p)
|
|
{
|
|
p->~T();
|
|
}
|
|
|
|
/* Maximum allocation size. */
|
|
|
|
size_t max_size() const
|
|
{
|
|
return size_t(-1);
|
|
}
|
|
|
|
/* Rebind to other type of allocator. */
|
|
|
|
template<class U> struct rebind {
|
|
using other = StackAllocator<SIZE, U>;
|
|
};
|
|
|
|
/* Operators */
|
|
|
|
template<class U> StackAllocator &operator=(const StackAllocator<SIZE, U> & /*unused*/)
|
|
{
|
|
return *this;
|
|
}
|
|
|
|
StackAllocator<SIZE, T> &operator=(const StackAllocator & /*unused*/)
|
|
{
|
|
return *this;
|
|
}
|
|
|
|
bool operator==(const StackAllocator & /*other*/) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool operator!=(const StackAllocator &other) const
|
|
{
|
|
return !operator==(other);
|
|
}
|
|
|
|
private:
|
|
int pointer_;
|
|
bool use_stack_;
|
|
T data_[SIZE];
|
|
};
|
|
|
|
CCL_NAMESPACE_END
|