40 template <
typename T,
int Al>
46 [[nodiscard]] T &
get() noexcept {
return x; }
49 [[nodiscard]] T
const &
get() const noexcept {
return x; }
73 template <
typename T, AddressSpace AdrSp = Host>
85 template <
typename T, Allocator A = mallocator<>>
87 static_assert(std::is_nothrow_destructible_v<T>,
"nda::mem::handle_heap requires the value_type to have a non-throwing destructor");
97#ifndef NDA_DEBUG_LEAK_CHECK
98 static inline A allocator;
104 mutable std::shared_ptr<void> sptr;
107 using blk_T_t = std::pair<T *, size_t>;
110 static void destruct(blk_T_t b)
noexcept {
114 if (
data ==
nullptr)
return;
118 for (
size_t i = 0; i <
size; ++i)
data[i].~T();
122 allocator.deallocate({(
char *)
data,
size *
sizeof(T)});
128 static void deleter(
void *p)
noexcept {
129 destruct(*((blk_T_t *)p));
148 if (not sptr) sptr.reset(
new blk_T_t{_data, _size}, deleter);
158 if (not sptr and not(
is_null())) destruct({_data, _size});
181 if (not sptr and not(
is_null())) destruct({_data, _size});
186 sptr = std::move(h.sptr);
200 if constexpr (std::is_trivially_copyable_v<T>) {
203 for (
size_t i = 0; i < _size; ++i)
new (_data + i) T(h[i]);
224 template <OwningHandle<value_type> H>
227 if constexpr (std::is_trivially_copyable_v<T>) {
231 "Constructing an nda::mem::handle_heap from a handle of a different address space requires a trivially copyable value_type");
232 for (
size_t i = 0; i < _size; ++i)
new (_data + i) T(h[i]);
243 template <Allocator AS>
254 if (
size == 0)
return;
255 auto b = allocator.allocate(
size *
sizeof(T));
256 if (not b.ptr)
throw std::bad_alloc{};
266 if (
size == 0)
return;
267 auto b = allocator.allocate_zero(
size *
sizeof(T));
268 if (not b.ptr)
throw std::bad_alloc{};
285 if (
size == 0)
return;
288 b = allocator.allocate_zero(
size *
sizeof(T));
290 b = allocator.allocate(
size *
sizeof(T));
291 if (not b.
ptr)
throw std::bad_alloc{};
297 for (
size_t i = 0; i <
size; ++i)
new (_data + i) T();
307 [[nodiscard]] T &
operator[](
long i)
noexcept {
return _data[i]; }
315 [[nodiscard]] T
const &
operator[](
long i)
const noexcept {
return _data[i]; }
324 EXPECTS((_data ==
nullptr) == (_size == 0));
326 return _data ==
nullptr;
333 [[nodiscard]] T *
data() const noexcept {
return _data; }
339 [[nodiscard]]
long size() const noexcept {
return _size; }
350 template <
typename T,
size_t Size>
352 static_assert(std::is_copy_constructible_v<T>,
"nda::mem::handle_stack requires the value_type to be copy constructible");
353 static_assert(std::is_nothrow_destructible_v<T>,
"nda::mem::handle_stack requires the value_type to have a non-throwing destructor");
357 std::array<char,
sizeof(T) * Size> buffer;
371 if constexpr (!std::is_trivial_v<T>) {
373 for (
size_t i = 0; i < Size; ++i)
data()[i].~T();
409 for (
size_t i = 0; i < Size; ++i)
new (
data() + i) T(h[i]);
418 static_assert(std::is_scalar_v<T> or
is_complex_v<T>,
"nda::mem::handle_stack can only be initialized to zero for scalar and complex types");
419 for (
size_t i = 0; i < Size; ++i)
data()[i] = 0;
431 for (
size_t i = 0; i < Size; ++i)
new (
data() + i) T();
455 static constexpr bool is_null() noexcept {
return false; }
461 [[nodiscard]] T *
data() const noexcept {
return (T *)buffer.data(); }
467 static constexpr long size() noexcept {
return Size; }
483 template <
typename T,
size_t Size>
485 static_assert(Size > 0,
"Size == 0 makes no sense in nda::mem::handle_sso");
486 static_assert(std::is_copy_constructible_v<T>,
"nda::mem::handle_sso requires the value_type to be copy constructible");
487 static_assert(std::is_nothrow_destructible_v<T>,
"nda::mem::handle_sso requires the value_type to have a non-throwing destructor");
491 std::array<char,
sizeof(T) * Size> buffer;
500 void clean()
noexcept {
502 if constexpr (!std::is_trivial_v<T>) {
504 for (
size_t i = 0; i < _size; ++i)
data()[i].~T();
539 _data = (T *)buffer.data();
540 for (
size_t i = 0; i < _size; ++i)
new (
data() + i) T(h[i]);
562 _data = (T *)buffer.data();
563 for (
size_t i = 0; i < _size; ++i)
new (_data + i) T(h[i]);
577 if constexpr (std::is_trivially_copyable_v<T>) {
580 for (
size_t i = 0; i < _size; ++i)
new (_data + i) T(h[i]);
591 if (
this == &h)
return *
this;
594 if (_size == 0)
return *
this;
597 if (not b.ptr)
throw std::bad_alloc{};
600 _data = (T *)buffer.data();
602 for (
size_t i = 0; i < _size; ++i)
new (_data + i) T(h[i]);
611 template <OwningHandle<value_type> H>
614 if constexpr (std::is_trivially_copyable_v<T>) {
618 "Constructing an nda::mem::handle_sso from a handle of a different address space requires a trivially copyable value_type");
619 for (
size_t i = 0; i < _size; ++i)
new (_data + i) T(h[i]);
629 if (
size == 0)
return;
632 _data = (T *)buffer.data();
635 if (not b.ptr)
throw std::bad_alloc{};
649 static_assert(std::is_scalar_v<T> or
is_complex_v<T>,
"nda::mem::handle_sso can only be initialized to zero for scalar and complex types");
650 if (
size == 0)
return;
653 _data = (T *)buffer.data();
654 for (
size_t i = 0; i < _size; ++i)
data()[i] = 0;
657 if (not b.ptr)
throw std::bad_alloc{};
673 if (
size == 0)
return;
676 _data = (T *)buffer.data();
683 if (not b.
ptr)
throw std::bad_alloc{};
689 for (
size_t i = 0; i <
size; ++i)
new (_data + i) T();
699 [[nodiscard]] T &
operator[](
long i)
noexcept {
return _data[i]; }
707 [[nodiscard]] T
const &
operator[](
long i)
const noexcept {
return _data[i]; }
713 [[nodiscard]]
bool on_heap()
const {
return _size > Size; }
721 EXPECTS((_data ==
nullptr) == (_size == 0));
723 return _data ==
nullptr;
730 [[nodiscard]] T *
data() const noexcept {
return _data; }
736 [[nodiscard]]
long size() const noexcept {
return _size; }
745 template <
typename T, AddressSpace AdrSp>
747 static_assert(std::is_nothrow_destructible_v<T>,
"nda::mem::handle_shared requires the value_type to have a non-throwing destructor");
757 using blk_t = std::pair<T *, size_t>;
760 std::shared_ptr<void> sptr;
781 : _data(
data), _size(
size), sptr{foreign_handle, foreign_decref} {}
789 template <Allocator A>
792 : _data(h.
data()), _size(h.
size()) {
793 if (not h.is_null()) sptr = h.get_sptr();
802 [[nodiscard]] T &
operator[](
long i)
noexcept {
return _data[i]; }
810 [[nodiscard]] T
const &
operator[](
long i)
const noexcept {
return _data[i]; }
819 EXPECTS((_data ==
nullptr) == (_size == 0));
821 return _data ==
nullptr;
828 [[nodiscard]]
long refcount() const noexcept {
return sptr.use_count(); }
834 [[nodiscard]] T *
data() const noexcept {
return _data; }
840 [[nodiscard]]
long size() const noexcept {
return _size; }
849 template <
typename T, AddressSpace AdrSp = Host>
853 using T0 = std::remove_const_t<T>;
894 requires(address_space == H::address_space and (std::is_const_v<value_type> or !std::is_const_v<typename H::value_type>)
895 and std::is_same_v<const value_type, const typename H::value_type>)
897 if constexpr (std::is_same_v<H, handle_heap<T0>>) _parent = &h;
906 [[nodiscard]] T &
operator[](
long i)
noexcept {
return _data[i]; }
914 [[nodiscard]] T
const &
operator[](
long i)
const noexcept {
return _data[i]; }
920 [[nodiscard]]
bool is_null() const noexcept {
return _data ==
nullptr; }
932 [[nodiscard]] T *
data() const noexcept {
return _data; }
@ Host
Using declaration for the Host address space (see nda::mem::AddressSpace).
Provides custom allocators for the nda library.
Wrap an allocator to check for memory leaks.
static void deallocate(blk_t b) noexcept
Deallocate memory using nda::mem::free.
static blk_t allocate_zero(size_t s) noexcept
Allocate memory and set it to zero.
static blk_t allocate(size_t s) noexcept
Allocate memory using nda::mem::malloc.
Provides concepts for the nda library.
static constexpr bool init_dcmplx
Should we initialize memory for complex double types to zero.
static constexpr do_not_initialize_t do_not_initialize
Instance of nda::mem::do_not_initialize_t.
void memcpy(void *dest, void const *src, size_t count)
Call the correct memcpy function based on the given address spaces.
static constexpr init_zero_t init_zero
Instance of nda::mem::init_zero_t.
constexpr bool is_complex_v
Constexpr variable that is true if type T is a std::complex type.
Macros used in the nda library.
Provides a generic memcpy and memcpy2D function for different address spaces.
Wraps an arbitrary type to have a specified alignment.
T & get() noexcept
Get reference to the wrapped object.
T const & get() const noexcept
Get const reference to the wrapped object.
T x
Wrapped object of type T.
Memory block consisting of a pointer and its size.
char * ptr
Pointer to the memory block.
Tag used in constructors to indicate that the memory should not be initialized.
handle_borrowed(T *ptr) noexcept
Construct a borrowed handle from a pointer to the data.
handle_heap< T0 > const * parent() const
Get a pointer to the parent handle.
handle_borrowed(H const &h, long offset=0) noexcept
Construct a borrowed handle from another handle.
static constexpr auto address_space
handle_borrowed & operator=(handle_borrowed &&)=default
Default move assignment operator.
handle_borrowed & operator=(handle_borrowed const &)=default
Default copy assignment operator.
handle_borrowed()=default
Default constructor leaves the handle in a null state (nullptr).
T & operator[](long i) noexcept
Subscript operator to access the data.
bool is_null() const noexcept
Check if the handle is in a null state.
T const & operator[](long i) const noexcept
Subscript operator to access the data.
handle_borrowed(handle_borrowed const &)=default
Default copy constructor.
T value_type
Value type of the data.
T * data() const noexcept
Get a pointer to the stored data.
A handle for a memory block on the heap.
long size() const noexcept
Get the size of the handle.
~handle_heap() noexcept
Destructor for the handle.
A allocator_type
nda::mem::Allocator type.
handle_heap & operator=(handle_heap const &h)
Copy assignment operator utilizes the copy constructor and move assignment operator to make a deep co...
T const & operator[](long i) const noexcept
Subscript operator to access the data.
handle_heap(handle_heap &&h) noexcept
Move constructor simply copies the pointers and size and resets the source handle to a null state.
handle_heap(long size, init_zero_t)
Construct a handle by allocating memory for the data of a given size and initializing it to zero.
T * data() const noexcept
Get a pointer to the stored data.
handle_heap(long size)
Construct a handle by allocating memory for the data of a given size and initializing it depending on...
handle_heap & operator=(handle_heap< T, AS > const &h)
Assignment operator utilizes another constructor and move assignment to make a deep copy of the data ...
handle_heap(long size, do_not_initialize_t)
Construct a handle by allocating memory for the data of a given size but without initializing it.
T value_type
Value type of the data.
bool is_null() const noexcept
handle_heap(handle_heap const &h)
Copy constructor makes a deep copy of the data from another handle.
T & operator[](long i) noexcept
Subscript operator to access the data.
static constexpr auto address_space
handle_heap & operator=(handle_heap &&h) noexcept
Move assignment operator first releases the resources held by the current handle and then moves the r...
handle_heap()=default
Default constructor leaves the handle in a null state (nullptr and size 0).
std::shared_ptr< void > get_sptr() const
Get a shared pointer to the memory block.
handle_heap(H const &h)
Construct a handle by making a deep copy of the data from another handle.
handle_shared(handle_heap< T, A > const &h) noexcept
Construct a shared handle from an nda::mem::handle_heap.
handle_shared()=default
Default constructor leaves the handle in a null state (nullptr and size 0).
T & operator[](long i) noexcept
Subscript operator to access the data.
static constexpr auto address_space
long refcount() const noexcept
Get the reference count of the shared object.
bool is_null() const noexcept
Check if the handle is in a null state.
T value_type
Value type of the data.
T const & operator[](long i) const noexcept
Subscript operator to access the data.
handle_shared(T *data, size_t size, void *foreign_handle, void(*foreign_decref)(void *)) noexcept
Construct a handle from a shared object from a foreign library.
T * data() const noexcept
long size() const noexcept
static constexpr auto address_space
nda::mem::AddressSpace in which the memory is allocated.
handle_sso()
Default constructor.
long size() const noexcept
Get the size of the handle.
~handle_sso() noexcept
Destructor for the handle.
handle_sso & operator=(handle_sso const &h)
Copy assignment operator first cleans up the current handle and then makes a deep copy of the data fr...
T & operator[](long i) noexcept
Subscript operator to access the data.
bool is_null() const noexcept
Check if the handle is in a null state.
handle_sso & operator=(handle_sso &&h) noexcept
Move assignment operator first releases the resources held by the current handle and then either copi...
handle_sso(long size)
Construct a handle for the data of a given size and initialize it depending on the value type.
handle_sso(handle_sso const &h)
Copy construct a handle by making a deep copy of the data from the source handle.
T value_type
Value type of the data.
T * data() const noexcept
Get a pointer to the stored data.
bool on_heap() const
Check if the data is/should be stored on the heap.
handle_sso(long size, do_not_initialize_t)
Construct a handle for the data of a given size and do not initialize it.
handle_sso(long size, init_zero_t)
Construct a handle for the data of a given size and initialize it to zero (only for scalar and comple...
handle_sso(handle_sso &&h) noexcept
Move constructor either copies the heap pointers or makes a deep copy of the stack data.
handle_sso(H const &h)
Construct a handle by making a deep copy of the data from another owning handle.
T const & operator[](long i) const noexcept
Subscript operator to access the data.
static constexpr auto address_space
nda::mem::AddressSpace in which the memory is allocated (always on Host).
handle_stack()=default
Default constructor leaves the data uninitialized.
handle_stack(long)
Construct a handle and initialize the data depending on the value type.
T & operator[](long i) noexcept
Subscript operator to access the data.
T * data() const noexcept
Get a pointer to the stored data.
handle_stack & operator=(handle_stack const &h)
Copy assignment operator makes a deep copy of the data from the source handle using placement new.
~handle_stack() noexcept
Destructor for the handle.
handle_stack(handle_stack const &h) noexcept
Copy constructor simply calls the copy assignment operator.
handle_stack(long, do_not_initialize_t)
Construct a handle and do not initialize the data.
handle_stack(handle_stack &&h) noexcept
Move constructor simply calls the copy assignment operator.
handle_stack & operator=(handle_stack &&h) noexcept
Move assignment operator simply calls the copy assignment operator.
handle_stack(long, init_zero_t)
Construct a handle and initialize the data to zero (only for scalar and complex types).
static constexpr long size() noexcept
Get the size of the handle.
static constexpr bool is_null() noexcept
Check if the handle is in a null state.
T value_type
Value type of the data.
T const & operator[](long i) const noexcept
Subscript operator to access the data.
Tag used in constructors to indicate that the memory should be initialized to zero.