53 template <
typename T,
int Al>
59 [[nodiscard]] T &
get() noexcept {
return x; }
62 [[nodiscard]] T
const &
get() const noexcept {
return x; }
86 template <
typename T, AddressSpace AdrSp = Host>
98 template <
typename T, Allocator A = mallocator<>>
100 static_assert(std::is_nothrow_destructible_v<T>,
"nda::mem::handle_heap requires the value_type to have a non-throwing destructor");
110#ifndef NDA_DEBUG_LEAK_CHECK
111 static inline A allocator;
117 mutable std::shared_ptr<void> sptr;
120 using blk_T_t = std::pair<T *, size_t>;
123 static void destruct(blk_T_t b)
noexcept {
124 auto [data, size] = b;
127 if (data ==
nullptr)
return;
131 for (
size_t i = 0; i < size; ++i) data[i].~T();
135 allocator.deallocate({(
char *)data, size *
sizeof(T)});
139 static void deleter(
void *p)
noexcept { destruct(*((blk_T_t *)p)); }
149 static constexpr auto address_space = allocator_type::address_space;
156 if (not sptr) sptr.reset(
new blk_T_t{_data, _size}, deleter);
166 if (not sptr and not(is_null())) destruct({_data, _size});
189 if (not sptr and not(is_null())) destruct({_data, _size});
194 sptr = std::move(h.sptr);
207 if (is_null())
return;
208 if constexpr (std::is_trivially_copyable_v<T>) {
209 memcpy<address_space, address_space>(_data, h.
data(), h.
size() *
sizeof(T));
211 for (
size_t i = 0; i < _size; ++i)
new (_data + i) T(h[i]);
232 template <OwningHandle<value_type> H>
234 if (is_null())
return;
235 if constexpr (std::is_trivially_copyable_v<T>) {
236 memcpy<address_space, H::address_space>((
void *)_data, (
void *)h.data(), _size *
sizeof(T));
238 static_assert(address_space == H::address_space,
239 "Constructing an nda::mem::handle_heap from a handle of a different address space requires a trivially copyable value_type");
240 for (
size_t i = 0; i < _size; ++i)
new (_data + i) T(h[i]);
251 template <Allocator AS>
262 if (size == 0)
return;
263 auto b = allocator.allocate(size *
sizeof(T));
264 if (not b.ptr)
throw std::bad_alloc{};
274 if (size == 0)
return;
275 auto b = allocator.allocate_zero(size *
sizeof(T));
276 if (not b.ptr)
throw std::bad_alloc{};
293 if (size == 0)
return;
296 b = allocator.allocate_zero(size *
sizeof(T));
298 b = allocator.allocate(size *
sizeof(T));
299 if (not b.
ptr)
throw std::bad_alloc{};
304 if constexpr (!std::is_trivial_v<T> and !is_complex_v<T>) {
305 for (
size_t i = 0; i < size; ++i)
new (_data + i) T();
315 [[nodiscard]] T &
operator[](
long i)
noexcept {
return _data[i]; }
323 [[nodiscard]] T
const &
operator[](
long i)
const noexcept {
return _data[i]; }
332 EXPECTS((_data ==
nullptr) == (_size == 0));
334 return _data ==
nullptr;
341 [[nodiscard]] T *
data() const noexcept {
return _data; }
347 [[nodiscard]]
long size() const noexcept {
return _size; }
358 template <
typename T,
size_t Size>
360 static_assert(std::is_copy_constructible_v<T>,
"nda::mem::handle_stack requires the value_type to be copy constructible");
361 static_assert(std::is_nothrow_destructible_v<T>,
"nda::mem::handle_stack requires the value_type to have a non-throwing destructor");
365 std::array<char,
sizeof(T) * Size> buffer;
372 static constexpr auto address_space = Host;
379 if constexpr (!std::is_trivial_v<T>) {
381 for (
size_t i = 0; i < Size; ++i) data()[i].~T();
417 for (
size_t i = 0; i < Size; ++i)
new (data() + i) T(h[i]);
426 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");
427 for (
size_t i = 0; i < Size; ++i) data()[i] = 0;
438 if constexpr (!std::is_trivial_v<T> and !is_complex_v<T>) {
439 for (
size_t i = 0; i < Size; ++i)
new (data() + i) T();
449 [[nodiscard]] T &
operator[](
long i)
noexcept {
return data()[i]; }
457 [[nodiscard]] T
const &
operator[](
long i)
const noexcept {
return data()[i]; }
463 static constexpr bool is_null() noexcept {
return false; }
469 [[nodiscard]] T *
data() const noexcept {
return (T *)buffer.data(); }
475 static constexpr long size() noexcept {
return Size; }
491 template <
typename T,
size_t Size>
493 static_assert(Size > 0,
"Size == 0 makes no sense in nda::mem::handle_sso");
494 static_assert(std::is_copy_constructible_v<T>,
"nda::mem::handle_sso requires the value_type to be copy constructible");
495 static_assert(std::is_nothrow_destructible_v<T>,
"nda::mem::handle_sso requires the value_type to have a non-throwing destructor");
499 std::array<char,
sizeof(T) * Size> buffer;
508 void clean()
noexcept {
509 if (is_null())
return;
510 if constexpr (!std::is_trivial_v<T>) {
512 for (
size_t i = 0; i < _size; ++i) data()[i].~T();
524 static constexpr auto address_space = Host;
547 _data = (T *)buffer.data();
548 for (
size_t i = 0; i < _size; ++i)
new (data() + i) T(h[i]);
570 _data = (T *)buffer.data();
571 for (
size_t i = 0; i < _size; ++i)
new (_data + i) T(h[i]);
584 if (is_null())
return;
585 if constexpr (std::is_trivially_copyable_v<T>) {
586 memcpy<address_space, address_space>((
void *)_data, (
void *)h.
data(), _size *
sizeof(T));
588 for (
size_t i = 0; i < _size; ++i)
new (_data + i) T(h[i]);
599 if (
this == &h)
return *
this;
602 if (_size == 0)
return *
this;
605 if (not b.ptr)
throw std::bad_alloc{};
608 _data = (T *)buffer.data();
610 for (
size_t i = 0; i < _size; ++i)
new (_data + i) T(h[i]);
619 template <OwningHandle<value_type> H>
621 if (is_null())
return;
622 if constexpr (std::is_trivially_copyable_v<T>) {
623 memcpy<address_space, H::address_space>((
void *)_data, (
void *)h.data(), _size *
sizeof(T));
625 static_assert(address_space == H::address_space,
626 "Constructing an nda::mem::handle_sso from a handle of a different address space requires a trivially copyable value_type");
627 for (
size_t i = 0; i < _size; ++i)
new (_data + i) T(h[i]);
637 if (size == 0)
return;
640 _data = (T *)buffer.data();
643 if (not b.ptr)
throw std::bad_alloc{};
657 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");
658 if (size == 0)
return;
661 _data = (T *)buffer.data();
662 for (
size_t i = 0; i < _size; ++i) data()[i] = 0;
665 if (not b.ptr)
throw std::bad_alloc{};
681 if (size == 0)
return;
684 _data = (T *)buffer.data();
691 if (not b.
ptr)
throw std::bad_alloc{};
696 if constexpr (!std::is_trivial_v<T> and !is_complex_v<T>) {
697 for (
size_t i = 0; i < size; ++i)
new (_data + i) T();
707 [[nodiscard]] T &
operator[](
long i)
noexcept {
return _data[i]; }
715 [[nodiscard]] T
const &
operator[](
long i)
const noexcept {
return _data[i]; }
721 [[nodiscard]]
bool on_heap()
const {
return _size > Size; }
729 EXPECTS((_data ==
nullptr) == (_size == 0));
731 return _data ==
nullptr;
738 [[nodiscard]] T *
data() const noexcept {
return _data; }
744 [[nodiscard]]
long size() const noexcept {
return _size; }
753 template <
typename T, AddressSpace AdrSp>
755 static_assert(std::is_nothrow_destructible_v<T>,
"nda::mem::handle_shared requires the value_type to have a non-throwing destructor");
765 using blk_t = std::pair<T *, size_t>;
768 std::shared_ptr<void> sptr;
775 static constexpr auto address_space = AdrSp;
788 handle_shared(T *data,
size_t size,
void *foreign_handle,
void (*foreign_decref)(
void *)) noexcept
789 : _data(data), _size(size), sptr{foreign_handle, foreign_decref} {}
797 template <Allocator A>
799 requires(A::address_space == address_space)
800 : _data(h.data()), _size(h.size()) {
801 if (not h.is_null()) sptr = h.get_sptr();
810 [[nodiscard]] T &
operator[](
long i)
noexcept {
return _data[i]; }
818 [[nodiscard]] T
const &
operator[](
long i)
const noexcept {
return _data[i]; }
827 EXPECTS((_data ==
nullptr) == (_size == 0));
829 return _data ==
nullptr;
836 [[nodiscard]]
long refcount() const noexcept {
return sptr.use_count(); }
842 [[nodiscard]] T *
data() const noexcept {
return _data; }
848 [[nodiscard]]
long size() const noexcept {
return _size; }
857 template <
typename T, AddressSpace AdrSp = Host>
861 using T0 = std::remove_const_t<T>;
874 static constexpr auto address_space = AdrSp;
902 requires(address_space == H::address_space and (std::is_const_v<value_type> or !std::is_const_v<typename H::value_type>)
903 and std::is_same_v<const value_type, const typename H::value_type>)
905 if constexpr (std::is_same_v<H, handle_heap<T0>>) _parent = &h;
914 [[nodiscard]] T &
operator[](
long i)
noexcept {
return _data[i]; }
922 [[nodiscard]] T
const &
operator[](
long i)
const noexcept {
return _data[i]; }
928 [[nodiscard]]
bool is_null() const noexcept {
return _data ==
nullptr; }
940 [[nodiscard]] T *
data() const noexcept {
return _data; }
Provides custom allocators for the nda library.
Wrap an allocator to check for memory leaks.
Custom allocator that uses nda::mem::malloc to allocate memory.
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.
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.
A non-owning handle for a memory block on the heap.
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 a another handle.
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
Check if the handle is in a null state.
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.
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.
A handle for a memory block on the heap with shared ownership.
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.
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
Get a pointer to the stored data.
long size() const noexcept
Get the size of the handle.
A handle for a memory block on the heap or stack depending on the size of the data.
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.
A handle for a memory block on the stack.
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.