The main goal of these changes are to improve static (i.e. build-time) checks on whether a given data can be allocated and freed with `malloc` and `free` (C-style), or requires proper C++-style construction and destruction (`new` and `delete`). * Add new `MEM_malloc_arrayN_aligned` API. * Make `MEM_freeN` a template function in C++, which does static assert on type triviality. * Add `MEM_SAFE_DELETE`, similar to `MEM_SAFE_FREE` but calling `MEM_delete`. The changes to `MEM_freeN` was painful and useful, as it allowed to fix a bunch of invalid calls in existing codebase already. It also highlighted a fair amount of places where it is called to free incomplete type pointers, which is likely a sign of badly designed code (there should rather be an API to destroy and free these data then, if the data type is not fully publicly exposed). For now, these are 'worked around' by explicitly casting the freed pointers to `void *` in these cases - which also makes them easy to search for. Some of these will be addressed separately (see blender/blender!134765). Finally, MSVC seems to consider structs defining new/delete operators (e.g. by using the `MEM_CXX_CLASS_ALLOC_FUNCS` macro) as non-trivial. This does not seem to follow the definition of type triviality, so for now static type checking in `MEM_freeN` has been disabled for Windows. We'll likely have to do the same with type-safe `MEM_[cm]allocN` API being worked on in blender/blender!134771 Based on ideas from Brecht in blender/blender!134452 Pull Request: https://projects.blender.org/blender/blender/pulls/134463
149 lines
2.8 KiB
C++
149 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_mallocN_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_freeN(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
|