/* Memory utilities for OctaSTD. * * This file is part of OctaSTD. See COPYING.md for futher information. */ #ifndef OCTA_MEMORY_HH #define OCTA_MEMORY_HH #include #include "octa/new.hh" #include "octa/utility.hh" #include "octa/type_traits.hh" namespace octa { /* address of */ template constexpr T *address_of(T &v) { return reinterpret_cast(&const_cast (reinterpret_cast(v))); } /* pointer traits */ namespace detail { template struct HasElement { template static int test(...); template static char test(typename U::Element * = 0); static constexpr bool value = (sizeof(test(0)) == 1); }; template::value> struct PointerElementBase; template struct PointerElementBase { using Type = typename T::Element; }; template class T, typename U, typename ...A> struct PointerElementBase, true> { using Type = typename T::Element; }; template class T, typename U, typename ...A> struct PointerElementBase, false> { using Type = U; }; template struct PointerElementType { using Type = typename PointerElementBase::Type; }; template struct PointerElementType { using Type = T; }; template struct HasDifference { template static int test(...); template static char test(typename U::Difference * = 0); static constexpr bool value = (sizeof(test(0)) == 1); }; template::value> struct PointerDifferenceBase { using Type = octa::Ptrdiff; }; template struct PointerDifferenceBase { using Type = typename T::Difference; }; template struct PointerDifferenceType { using Type = typename PointerDifferenceBase::Type; }; template struct PointerDifferenceType { using Type = octa::Ptrdiff; }; template struct HasRebind { template static int test(...); template static char test( typename V::template Rebind * = 0); static constexpr bool value = (sizeof(test(0)) == 1); }; template::value> struct PointerRebindBase { using Type = typename T::template Rebind; }; template class T, typename U, typename ...A, typename V > struct PointerRebindBase, V, true> { using Type = typename T::template Rebind; }; template class T, typename U, typename ...A, typename V > struct PointerRebindBase, V, false> { using Type = T; }; template struct PointerRebindType { using type = typename PointerRebindBase::Type; }; template struct PointerRebindType { using type = U *; }; template struct PointerPointer { using Type = T; }; template struct PointerPointer { using Type = T *; }; } /*namespace detail */ template using Pointer = typename octa::detail::PointerPointer::Type; template using PointerElement = typename octa::detail::PointerElementType::Type; template using PointerDifference = typename octa::detail::PointerDifferenceType::Type; template using PointerRebind = typename octa::detail::PointerRebindType::Type; /* pointer to */ namespace detail { struct PointerToNat {}; template struct PointerTo { static T pointer_to(octa::Conditional< octa::IsVoid>::value, PointerToNat, PointerElement > &r) { return T::pointer_to(r); } }; template struct PointerTo { static T pointer_to(octa::Conditional< octa::IsVoid::value, PointerToNat, T > &r) { return octa::address_of(r); } }; } template static T pointer_to(octa::Conditional< octa::IsVoid>::value, octa::detail::PointerToNat, PointerElement > &r) { return octa::detail::PointerTo::pointer_to(r); } /* default deleter */ template struct DefaultDelete { constexpr DefaultDelete() = default; template DefaultDelete(const DefaultDelete &) {}; void operator()(T *p) const { delete p; } }; template struct DefaultDelete { constexpr DefaultDelete() = default; template DefaultDelete(const DefaultDelete &) {}; void operator()(T *p) const { delete[] p; } template void operator()(U *) const = delete; }; /* box */ namespace detail { template static int ptr_test(...); template static char ptr_test(typename T::Pointer * = 0); template struct HasPtr: octa::IntegralConstant(0)) == 1) > {}; template::value> struct PointerBase { using Type = typename D::Pointer; }; template struct PointerBase { using Type = T *; }; template struct PointerType { using Type = typename PointerBase>::Type; }; } /* namespace detail */ template> struct Box { using Element = T; using Deleter = D; using Pointer = typename octa::detail::PointerType::Type; private: struct Nat { int x; }; using Dref = RemoveReference &; using Dcref = const RemoveReference &; public: constexpr Box(): p_stor(nullptr, D()) { static_assert(!octa::IsPointer::value, "Box constructed with null fptr deleter"); } constexpr Box(octa::Nullptr): p_stor(nullptr, D()) { static_assert(!octa::IsPointer::value, "Box constructed with null fptr deleter"); } explicit Box(Pointer p): p_stor(p, D()) { static_assert(!octa::IsPointer::value, "Box constructed with null fptr deleter"); } Box(Pointer p, octa::Conditional::value, D, octa::AddLvalueReference > d): p_stor(p, d) {} Box(Pointer p, octa::RemoveReference &&d): p_stor(p, octa::move(d)) { static_assert(!octa::IsReference::value, "rvalue deleter cannot be a ref"); } Box(Box &&u): p_stor(u.release(), octa::forward(u.get_deleter())) {} template Box(Box &&u, octa::EnableIf::value && octa::IsConvertible::Pointer, Pointer>::value && octa::IsConvertible::value && (!octa::IsReference::value || octa::IsSame::value) > = Nat()): p_stor(u.release(), octa::forward
(u.get_deleter())) {} Box &operator=(Box &&u) { reset(u.release()); p_stor.second() = octa::forward(u.get_deleter()); return *this; } template EnableIf::value && octa::IsConvertible::Pointer, Pointer>::value && octa::IsAssignable::value, Box & > operator=(Box &&u) { reset(u.release()); p_stor.second() = octa::forward
(u.get_deleter()); return *this; } Box &operator=(octa::Nullptr) { reset(); return *this; } ~Box() { reset(); } octa::AddLvalueReference operator*() const { return *p_stor.first(); } Pointer operator->() const { return p_stor.first(); } explicit operator bool() const { return p_stor.first() != nullptr; } Pointer get() const { return p_stor.first(); } Dref get_deleter() { return p_stor.second(); } Dcref get_deleter() const { return p_stor.second(); } Pointer release() { Pointer p = p_stor.first(); p_stor.first() = nullptr; return p; } void reset(Pointer p = nullptr) { Pointer tmp = p_stor.first(); p_stor.first() = p; if (tmp) p_stor.second()(tmp); } void swap(Box &u) { p_stor.swap(u.p_stor); } private: octa::detail::CompressedPair p_stor; }; namespace detail { template>, octa::RemoveCv> >::value> struct SameOrLessCvQualifiedBase: octa::IsConvertible {}; template struct SameOrLessCvQualifiedBase: octa::False {}; template::value || octa::IsSame::value || octa::detail::HasElement::value > struct SameOrLessCvQualified: SameOrLessCvQualifiedBase {}; template struct SameOrLessCvQualified: octa::False {}; } /* namespace detail */ template struct Box { using Element = T; using Deleter = D; using Pointer = typename octa::detail::PointerType::Type; private: struct Nat { int x; }; using Dref = RemoveReference &; using Dcref = const RemoveReference &; public: constexpr Box(): p_stor(nullptr, D()) { static_assert(!octa::IsPointer::value, "Box constructed with null fptr deleter"); } constexpr Box(octa::Nullptr): p_stor(nullptr, D()) { static_assert(!octa::IsPointer::value, "Box constructed with null fptr deleter"); } template explicit Box(U p, octa::EnableIf< octa::detail::SameOrLessCvQualified::value, Nat > = Nat()): p_stor(p, D()) { static_assert(!octa::IsPointer::value, "Box constructed with null fptr deleter"); } template Box(U p, octa::Conditional< octa::IsReference::value, D, AddLvalueReference > d, octa::EnableIf::value, Nat> = Nat()): p_stor(p, d) {} Box(octa::Nullptr, octa::Conditional::value, D, AddLvalueReference > d): p_stor(nullptr, d) {} template Box(U p, octa::RemoveReference &&d, octa::EnableIf< octa::detail::SameOrLessCvQualified::value, Nat > = Nat()): p_stor(p, octa::move(d)) { static_assert(!octa::IsReference::value, "rvalue deleter cannot be a ref"); } Box(octa::Nullptr, octa::RemoveReference &&d): p_stor(nullptr, octa::move(d)) { static_assert(!octa::IsReference::value, "rvalue deleter cannot be a ref"); } Box(Box &&u): p_stor(u.release(), octa::forward(u.get_deleter())) {} template Box(Box &&u, EnableIf::value && octa::detail::SameOrLessCvQualified::Pointer, Pointer>::value && octa::IsConvertible::value && (!octa::IsReference::value || octa::IsSame::value)> = Nat() ): p_stor(u.release(), octa::forward
(u.get_deleter())) {} Box &operator=(Box &&u) { reset(u.release()); p_stor.second() = octa::forward(u.get_deleter()); return *this; } template EnableIf::value && octa::detail::SameOrLessCvQualified::Pointer, Pointer>::value && IsAssignable::value, Box & > operator=(Box &&u) { reset(u.release()); p_stor.second() = octa::forward
(u.get_deleter()); return *this; } Box &operator=(octa::Nullptr) { reset(); return *this; } ~Box() { reset(); } octa::AddLvalueReference operator[](octa::Size idx) const { return p_stor.first()[idx]; } explicit operator bool() const { return p_stor.first() != nullptr; } Pointer get() const { return p_stor.first(); } Dref get_deleter() { return p_stor.second(); } Dcref get_deleter() const { return p_stor.second(); } Pointer release() { Pointer p = p_stor.first(); p_stor.first() = nullptr; return p; } template EnableIf< octa::detail::SameOrLessCvQualified::value, void > reset(U p) { Pointer tmp = p_stor.first(); p_stor.first() = p; if (tmp) p_stor.second()(tmp); } void reset(octa::Nullptr) { Pointer tmp = p_stor.first(); p_stor.first() = nullptr; if (tmp) p_stor.second()(tmp); } void reset() { reset(nullptr); } void swap(Box &u) { p_stor.swap(u.p_stor); } private: octa::detail::CompressedPair p_stor; }; namespace detail { template struct BoxIf { using Box = octa::Box; }; template struct BoxIf { using BoxUnknownSize = octa::Box; }; template struct BoxIf { using BoxKnownSize = void; }; } template typename octa::detail::BoxIf::Box make_box(A &&...args) { return Box(new T(octa::forward(args)...)); } template typename octa::detail::BoxIf::BoxUnknownSize make_box(octa::Size n) { return Box(new octa::RemoveExtent[n]()); } template typename octa::detail::BoxIf::BoxKnownSize make_box(A &&...args) = delete; /* allocator */ template struct Allocator; template<> struct Allocator { using Value = void; using Pointer = void *; using ConstPointer = const void *; template using Rebind = Allocator; }; template<> struct Allocator { using Value = const void; using Pointer = const void *; using ConstPointer = const void *; template using Rebind = Allocator; }; template struct Allocator { using Size = octa::Size; using Difference = octa::Ptrdiff; using Value = T; using Reference = T &; using ConstReference = const T &; using Pointer = T *; using ConstPointer = const T *; template using Rebind = Allocator; Allocator() {} template Allocator(const Allocator &) {} Pointer address(Reference v) const { return address_of(v); }; ConstPointer address(ConstReference v) const { return address_of(v); }; Size max_size() const { return Size(~0) / sizeof(T); } Pointer allocate(Size n, Allocator::ConstPointer = nullptr) { return (Pointer) ::new octa::byte[n * sizeof(T)]; } void deallocate(Pointer p, Size) { ::delete[] (octa::byte *) p; } template void construct(U *p, A &&...args) { ::new((void *)p) U(octa::forward(args)...); } void destroy(Pointer p) { p->~T(); } }; template struct Allocator { using Size = octa::Size; using Difference = octa::Ptrdiff; using Value = const T; using Reference = const T &; using ConstReference = const T &; using Pointer = const T *; using ConstPointer = const T *; template using Rebind = Allocator; Allocator() {} template Allocator(const Allocator &) {} ConstPointer address(ConstReference v) const { return address_of(v); }; Size max_size() const { return Size(~0) / sizeof(T); } Pointer allocate(Size n, Allocator::ConstPointer = nullptr) { return (Pointer) ::new octa::byte[n * sizeof(T)]; } void deallocate(Pointer p, Size) { ::delete[] (octa::byte *) p; } template void construct(U *p, A &&...args) { ::new((void *)p) U(octa::forward(args)...); } void destroy(Pointer p) { p->~T(); } }; template bool operator==(const Allocator &, const Allocator &) { return true; } template bool operator!=(const Allocator &, const Allocator &) { return false; } /* allocator traits - modeled after libc++ */ namespace detail { template struct ConstPtrTest { template static char test( typename U::ConstPointer * = 0); template static int test(...); static constexpr bool value = (sizeof(test(0)) == 1); }; template::value> struct ConstPointer { using Type = typename A::ConstPointer; }; template struct ConstPointer { using Type = PointerRebind; }; template struct VoidPtrTest { template static char test( typename U::VoidPointer * = 0); template static int test(...); static constexpr bool value = (sizeof(test(0)) == 1); }; template::value> struct VoidPointer { using Type = typename A::VoidPointer; }; template struct VoidPointer { using Type = PointerRebind; }; template struct ConstVoidPtrTest { template static char test( typename U::ConstVoidPointer * = 0); template static int test(...); static constexpr bool value = (sizeof(test(0)) == 1); }; template::value> struct ConstVoidPointer { using Type = typename A::ConstVoidPointer; }; template struct ConstVoidPointer { using Type = PointerRebind; }; template struct SizeTest { template static char test(typename U::Size * = 0); template static int test(...); static constexpr bool value = (sizeof(test(0)) == 1); }; template::value> struct SizeBase { using Type = octa::MakeUnsigned; }; template struct SizeBase { using Type = typename A::Size; }; } /* namespace detail */ /* allocator type traits */ template using AllocatorType = A; template using AllocatorValue = typename AllocatorType::Value; template using AllocatorPointer = typename octa::detail::PointerType< AllocatorValue, AllocatorType >::Type; template using AllocatorConstPointer = typename octa::detail::ConstPointer< AllocatorValue, AllocatorPointer, AllocatorType >::Type; template using AllocatorVoidPointer = typename octa::detail::VoidPointer< AllocatorPointer, AllocatorType >::Type; template using AllocatorConstVoidPointer = typename octa::detail::ConstVoidPointer< AllocatorPointer, AllocatorType >::Type; /* allocator difference */ namespace detail { template struct DiffTest { template static char test(typename U::Difference * = 0); template static int test(...); static constexpr bool value = (sizeof(test(0)) == 1); }; template::value> struct AllocDifference { using Type = PointerDifference

; }; template struct AllocDifference { using Type = typename A::Difference; }; } template using AllocatorDifference = typename octa::detail::AllocDifference< A, AllocatorPointer >::Type; /* allocator size */ template using AllocatorSize = typename octa::detail::SizeBase< A, AllocatorDifference >::Type; /* allocator rebind */ namespace detail { template::value> struct AllocTraitsRebindType { using Type = typename T::template Rebind; }; template class A, typename T, typename ...Args, typename U > struct AllocTraitsRebindType, U, true> { using Type = typename A::template Rebind; }; template class A, typename T, typename ...Args, typename U > struct AllocTraitsRebindType, U, false> { using Type = A; }; } /* namespace detail */ template using AllocatorRebind = typename octa::detail::AllocTraitsRebindType< AllocatorType, T >::Type; /* allocator propagate on container copy assignment */ namespace detail { template struct PropagateOnContainerCopyAssignmentTest { template static char test( typename U::PropagateOnContainerCopyAssignment * = 0); template static int test(...); static constexpr bool value = (sizeof(test(0)) == 1); }; template::value> struct PropagateOnContainerCopyAssignmentBase { using Type = octa::False; }; template struct PropagateOnContainerCopyAssignmentBase { using Type = typename A::PropagateOnContainerCopyAssignment; }; } /* namespace detail */ template using AllocatorPropagateOnContainerCopyAssignment = typename octa::detail::PropagateOnContainerCopyAssignmentBase::Type; /* allocator propagate on container move assignment */ namespace detail { template struct PropagateOnContainerMoveAssignmentTest { template static char test( typename U::PropagateOnContainerMoveAssignment * = 0); template static int test(...); static constexpr bool value = (sizeof(test(0)) == 1); }; template::value> struct PropagateOnContainerMoveAssignmentBase { using Type = octa::False; }; template struct PropagateOnContainerMoveAssignmentBase { using Type = typename A::PropagateOnContainerMoveAssignment; }; } /* namespace detail */ template using AllocatorPropagateOnContainerMoveAssignment = typename octa::detail::PropagateOnContainerMoveAssignmentBase::Type; /* allocator propagate on container swap */ namespace detail { template struct PropagateOnContainerSwapTest { template static char test( typename U::PropagateOnContainerSwap * = 0); template static int test(...); static constexpr bool value = (sizeof(test(0)) == 1); }; template::value> struct PropagateOnContainerSwapBase { using Type = octa::False; }; template struct PropagateOnContainerSwapBase { using Type = typename A::PropagateOnContainerSwap; }; } /* namespace detail */ template using AllocatorPropagateOnContainerSwap = typename octa::detail::PropagateOnContainerSwapBase::Type; /* allocator is always equal */ namespace detail { template struct IsAlwaysEqualTest { template static char test(typename U::IsAlwaysEqual * = 0); template static int test(...); static constexpr bool value = (sizeof(test(0)) == 1); }; template::value> struct IsAlwaysEqualBase { using Type = typename octa::IsEmpty::Type; }; template struct IsAlwaysEqualBase { using Type = typename A::IsAlwaysEqual; }; } /* namespace detail */ template using AllocatorIsAlwaysEqual = typename octa::detail::IsAlwaysEqualBase::Type; /* allocator allocate */ template inline AllocatorPointer allocator_allocate(A &a, AllocatorSize n) { return a.allocate(n); } namespace detail { template auto allocate_hint_test(A &&a, S &&sz, CVP &&p) -> decltype(a.allocate(sz, p), octa::True()); template auto allocate_hint_test(const A &, S &&, CVP &&) -> octa::False; template struct AllocateHintTest: octa::IntegralConstant(), octa::declval(), octa::declval())), octa::True >::value > {}; template inline AllocatorPointer allocate(A &a, AllocatorSize n, AllocatorConstVoidPointer h, octa::True) { return a.allocate(n, h); } template inline AllocatorPointer allocate(A &a, AllocatorSize n, AllocatorConstVoidPointer, octa::False) { return a.allocate(n); } } /* namespace detail */ template inline AllocatorPointer allocator_allocate(A &a, AllocatorSize n, AllocatorConstVoidPointer h) { return octa::detail::allocate(a, n, h, octa::detail::AllocateHintTest< A, AllocatorSize, AllocatorConstVoidPointer >()); } /* allocator deallocate */ template inline void allocator_deallocate(A &a, AllocatorPointer p, AllocatorSize n) { a.deallocate(p, n); } /* allocator construct */ namespace detail { template auto construct_test(A &&a, T *p, Args &&...args) -> decltype(a.construct(p, octa::forward(args)...), octa::True()); template auto construct_test(const A &, T *, Args &&...) -> octa::False; template struct ConstructTest: octa::IntegralConstant(), octa::declval(), octa::declval()...)), octa::True >::value > {}; template inline void construct(octa::True, A &a, T *p, Args &&...args) { a.construct(p, octa::forward(args)...); } template inline void construct(octa::False, A &, T *p, Args &&...args) { ::new ((void *)p) T(octa::forward(args)...); } } /* namespace detail */ template inline void allocator_construct(A &a, T *p, Args &&...args) { octa::detail::construct(octa::detail::ConstructTest< A, T *, Args... >(), a, p, octa::forward(args)...); } /* allocator destroy */ namespace detail { template auto destroy_test(A &&a, P &&p) -> decltype(a.destroy(p), octa::True()); template auto destroy_test(const A &, P &&) -> octa::False; template struct DestroyTest: octa::IntegralConstant(), octa::declval

())), octa::True >::value > {}; template inline void destroy(octa::True, A &a, T *p) { a.destroy(p); } template inline void destroy(octa::False, A &, T *p) { p->~T(); } } /* namespace detail */ template inline void allocator_destroy(A &a, T *p) { octa::detail::destroy(octa::detail::DestroyTest(), a, p); } /* allocator max size */ namespace detail { template auto alloc_max_size_test(A &&a) -> decltype(a.max_size(), octa::True()); template auto alloc_max_size_test(const A &) -> octa::False; template struct AllocMaxSizeTest: octa::IntegralConstant())), octa::True >::value > {}; template inline AllocatorSize alloc_max_size(octa::True, const A &a) { return a.max_size(); } template inline AllocatorSize alloc_max_size(octa::False, const A &) { return AllocatorSize(~0); } } /* namespace detail */ template inline AllocatorSize allocator_max_size(const A &a) { return octa::detail::alloc_max_size(octa::detail::AllocMaxSizeTest< const A >(), a); } /* allocator container copy */ namespace detail { template auto alloc_copy_test(A &&a) -> decltype(a.container_copy(), octa::True()); template auto alloc_copy_test(const A &) -> octa::False; template struct AllocCopyTest: octa::IntegralConstant())), octa::True >::value > {}; template inline AllocatorType alloc_container_copy(octa::True, const A &a) { return a.container_copy(); } template inline AllocatorType alloc_container_copy(octa::False, const A &a) { return a; } } /* namespace detail */ template inline AllocatorType allocator_container_copy(const A &a) { return octa::detail::alloc_container_copy(octa::detail::AllocCopyTest< const A >(), a); } struct AllocatorArg {}; constexpr AllocatorArg allocator_arg = AllocatorArg(); } /* namespace octa */ #endif