/* Memory utilities for OctaSTD. * * This file is part of OctaSTD. See COPYING.md for futher information. */ #ifndef OSTD_MEMORY_HH #define OSTD_MEMORY_HH #include #include "ostd/new.hh" #include "ostd/utility.hh" #include "ostd/type_traits.hh" namespace ostd { /* address of */ template constexpr T *address_of(T &v) noexcept { 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 = Ptrdiff; }; template struct PointerDifferenceBase { using Type = typename T::Difference; }; template struct PointerDifferenceType { using Type = typename PointerDifferenceBase::Type; }; template struct PointerDifferenceType { using Type = 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< template class T, typename U, typename ...A, typename V > struct PointerRebindBase, V, true> { using Type = typename T::template Rebind; }; template< 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 detail::PointerPointer::Type; template using PointerElement = typename detail::PointerElementType::Type; template using PointerDifference = typename detail::PointerDifferenceType::Type; template using PointerRebind = typename detail::PointerRebindType::Type; /* pointer to */ namespace detail { struct PointerToNat {}; template struct PointerTo { static T pointer_to( Conditional< IsVoid>, PointerToNat, PointerElement > &r ) { return T::pointer_to(r); } }; template struct PointerTo { static T pointer_to(Conditional, PointerToNat, T> &r) { return address_of(r); } }; } template static T pointer_to( Conditional< IsVoid>, detail::PointerToNat, PointerElement > &r ) { return detail::PointerTo::pointer_to(r); } /* default deleter */ template struct DefaultDelete { constexpr DefaultDelete() = default; template DefaultDelete(DefaultDelete const &) noexcept {}; void operator()(T *p) const { delete p; } }; template struct DefaultDelete { constexpr DefaultDelete() = default; template DefaultDelete(DefaultDelete const &) noexcept {}; 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 constexpr bool HasPtr = sizeof(ptr_test(0)) == 1; template> 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 detail::PointerType::Type; private: struct Nat { int x; }; using Dref = RemoveReference &; using Dcref = RemoveReference const &; public: constexpr Box() noexcept: p_stor(nullptr, D()) { static_assert(!IsPointer, "Box constructed with null fptr deleter"); } constexpr Box(Nullptr) noexcept: p_stor(nullptr, D()) { static_assert(!IsPointer, "Box constructed with null fptr deleter"); } explicit Box(Pointer p) noexcept: p_stor(p, D()) { static_assert(!IsPointer, "Box constructed with null fptr deleter"); } Box( Pointer p, Conditional, D, AddLvalueReference> d ) noexcept: p_stor(p, d) {} Box(Pointer p, RemoveReference &&d) noexcept: p_stor(p, move(d)) { static_assert(!IsReference, "rvalue deleter cannot be a ref"); } Box(Box &&u) noexcept: p_stor(u.release(), forward(u.get_deleter())) {} template Box( Box &&u, EnableIf< !IsArray && IsConvertible::Pointer, Pointer> && IsConvertible && (!IsReference || IsSame), Nat > = Nat() ) noexcept: p_stor(u.release(), forward
(u.get_deleter())) {} Box &operator=(Box &&u) noexcept { reset(u.release()); p_stor.second() = forward(u.get_deleter()); return *this; } template EnableIf< !IsArray && IsConvertible::Pointer, Pointer> && IsAssignable, Box & > operator=(Box &&u) noexcept { reset(u.release()); p_stor.second() = forward
(u.get_deleter()); return *this; } Box &operator=(Nullptr) noexcept { reset(); return *this; } ~Box() { reset(); } AddLvalueReference operator*() const { return *p_stor.first(); } Pointer operator->() const noexcept { return p_stor.first(); } explicit operator bool() const noexcept { return p_stor.first() != nullptr; } Pointer get() const noexcept { return p_stor.first(); } Dref get_deleter() noexcept { return p_stor.second(); } Dcref get_deleter() const noexcept { return p_stor.second(); } Pointer release() noexcept { Pointer p = p_stor.first(); p_stor.first() = nullptr; return p; } void reset(Pointer p = nullptr) noexcept { Pointer tmp = p_stor.first(); p_stor.first() = p; if (tmp) { p_stor.second()(tmp); } } void swap(Box &u) noexcept { p_stor.swap(u.p_stor); } private: detail::CompressedPair p_stor; }; namespace detail { template>, RemoveCv> >> constexpr bool SameOrLessCvQualifiedBase = IsConvertible; template constexpr bool SameOrLessCvQualifiedBase = false; template || IsSame || detail::HasElement::value > constexpr bool SameOrLessCvQualified = SameOrLessCvQualifiedBase; template constexpr bool SameOrLessCvQualified = false; } /* namespace detail */ template struct Box { using Element = T; using Deleter = D; using Pointer = typename detail::PointerType::Type; private: struct Nat { int x; }; using Dref = RemoveReference &; using Dcref = RemoveReference const &; public: constexpr Box() noexcept: p_stor(nullptr, D()) { static_assert(!IsPointer, "Box constructed with null fptr deleter"); } constexpr Box(Nullptr) noexcept: p_stor(nullptr, D()) { static_assert(!IsPointer, "Box constructed with null fptr deleter"); } template explicit Box(U p, EnableIf< detail::SameOrLessCvQualified, Nat > = Nat()) noexcept: p_stor(p, D()) { static_assert(!IsPointer, "Box constructed with null fptr deleter"); } template Box(U p, Conditional< IsReference, D, AddLvalueReference > d, EnableIf< detail::SameOrLessCvQualified, Nat > = Nat()) noexcept: p_stor(p, d) {} Box( Nullptr, Conditional, D, AddLvalueReference> d ) noexcept: p_stor(nullptr, d) {} template Box(U p, RemoveReference &&d, EnableIf, Nat> = Nat()) noexcept: p_stor(p, move(d)) { static_assert(!IsReference, "rvalue deleter cannot be a ref"); } Box(Nullptr, RemoveReference &&d) noexcept: p_stor(nullptr, move(d)) { static_assert(!IsReference, "rvalue deleter cannot be a ref"); } Box(Box &&u) noexcept: p_stor(u.release(), forward(u.get_deleter())) {} template Box( Box &&u, EnableIf< IsArray && detail::SameOrLessCvQualified::Pointer,Pointer> && IsConvertible && (!IsReference || IsSame), Nat > = Nat() ) noexcept: p_stor(u.release(), forward
(u.get_deleter())) {} Box &operator=(Box &&u) noexcept { reset(u.release()); p_stor.second() = forward(u.get_deleter()); return *this; } template EnableIf< IsArray && detail::SameOrLessCvQualified::Pointer, Pointer> && IsAssignable, Box & > operator=(Box &&u) noexcept { reset(u.release()); p_stor.second() = forward
(u.get_deleter()); return *this; } Box &operator=(Nullptr) noexcept { reset(); return *this; } ~Box() { reset(); } AddLvalueReference operator[](Size idx) const { return p_stor.first()[idx]; } explicit operator bool() const noexcept { return p_stor.first() != nullptr; } Pointer get() const noexcept { return p_stor.first(); } Dref get_deleter() noexcept { return p_stor.second(); } Dcref get_deleter() const noexcept { return p_stor.second(); } Pointer release() noexcept { Pointer p = p_stor.first(); p_stor.first() = nullptr; return p; } template EnableIf< detail::SameOrLessCvQualified, void > reset(U p) noexcept { Pointer tmp = p_stor.first(); p_stor.first() = p; if (tmp) { p_stor.second()(tmp); } } void reset(Nullptr) noexcept { Pointer tmp = p_stor.first(); p_stor.first() = nullptr; if (tmp) { p_stor.second()(tmp); } } void reset() noexcept { reset(nullptr); } void swap(Box &u) noexcept { p_stor.swap(u.p_stor); } private: detail::CompressedPair p_stor; }; namespace detail { template struct BoxIf { using BoxType = Box; }; template struct BoxIf { using BoxUnknownSize = Box; }; template struct BoxIf { using BoxKnownSize = void; }; } template typename detail::BoxIf::BoxType make_box(A &&...args) { return Box(new T(forward(args)...)); } template typename detail::BoxIf::BoxUnknownSize make_box(Size n) { return Box(new RemoveExtent[n]()); } template typename detail::BoxIf::BoxKnownSize make_box(A &&...args) = delete; /* allocator */ template struct Allocator; template<> struct Allocator { using Value = void; using Pointer = void *; using ConstPointer = void const *; template using Rebind = Allocator; }; template<> struct Allocator { using Value = void const; using Pointer = void const *; using ConstPointer = void const *; template using Rebind = Allocator; }; template struct Allocator { using Size = ostd::Size; using Difference = Ptrdiff; using Value = T; using Reference = T &; using ConstReference = T const &; using Pointer = T *; using ConstPointer = T const *; template using Rebind = Allocator; Allocator() noexcept {} template Allocator(Allocator const &) noexcept {} Pointer address(Reference v) const noexcept { return address_of(v); }; ConstPointer address(ConstReference v) const noexcept { return address_of(v); }; Size max_size() const noexcept { return Size(~0) / sizeof(T); } Pointer allocate(Size n, Allocator::ConstPointer = nullptr) { return reinterpret_cast(::new byte[n * sizeof(T)]); } void deallocate(Pointer p, Size) noexcept { ::delete[] reinterpret_cast(p); } template void construct(U *p, A &&...args) { ::new(p) U(forward(args)...); } void destroy(Pointer p) noexcept { p->~T(); } }; template struct Allocator { using Size = ostd::Size; using Difference = Ptrdiff; using Value = T const; using Reference = T const &; using ConstReference = T const &; using Pointer = T const *; using ConstPointer = T const *; template using Rebind = Allocator; Allocator() noexcept {} template Allocator(Allocator const &) noexcept {} ConstPointer address(ConstReference v) const noexcept { return address_of(v); }; Size max_size() const noexcept { return Size(~0) / sizeof(T); } Pointer allocate(Size n, Allocator::ConstPointer = nullptr) { return reinterpret_cast(::new byte[n * sizeof(T)]); } void deallocate(Pointer p, Size) noexcept { ::delete[] reinterpret_cast(p); } template void construct(U *p, A &&...args) { ::new(p) U(forward(args)...); } void destroy(Pointer p) noexcept { p->~T(); } }; template bool operator==(Allocator const &, Allocator const &) noexcept { return true; } template bool operator!=(Allocator const &, Allocator const &) noexcept { 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 = 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 detail::PointerType< AllocatorValue, AllocatorType >::Type; template using AllocatorConstPointer = typename detail::ConstPointer< AllocatorValue, AllocatorPointer, AllocatorType >::Type; template using AllocatorVoidPointer = typename detail::VoidPointer< AllocatorPointer, AllocatorType >::Type; template using AllocatorConstVoidPointer = typename 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 detail::AllocDifference< A, AllocatorPointer >::Type; /* allocator size */ template using AllocatorSize = typename detail::SizeBase< A, AllocatorDifference >::Type; /* allocator rebind */ namespace detail { template::value> struct AllocTraitsRebindType { using Type = typename T::template Rebind; }; template< template class A, typename T, typename ...Args, typename U > struct AllocTraitsRebindType, U, true> { using Type = typename A::template Rebind; }; template< template class A, typename T, typename ...Args, typename U > struct AllocTraitsRebindType, U, false> { using Type = A; }; } /* namespace detail */ template using AllocatorRebind = typename detail::AllocTraitsRebindType< AllocatorType, T >::Type; /* allocator propagate on container copy assignment */ namespace detail { template struct PropagateOnContainerCopyAssignmentTest { template static char test(decltype(U::PropagateOnContainerCopyAssignment) * = 0); template static int test(...); static constexpr bool value = (sizeof(test(0)) == 1); }; template::value > constexpr bool PropagateOnContainerCopyAssignmentBase = false; template constexpr bool PropagateOnContainerCopyAssignmentBase = A::PropagateOnContainerCopyAssignment; } /* namespace detail */ template constexpr bool AllocatorPropagateOnContainerCopyAssignment = detail::PropagateOnContainerCopyAssignmentBase; /* allocator propagate on container move assignment */ namespace detail { template struct PropagateOnContainerMoveAssignmentTest { template static char test(decltype(U::PropagateOnContainerMoveAssignment) * = 0); template static int test(...); static constexpr bool value = (sizeof(test(0)) == 1); }; template::value > constexpr bool PropagateOnContainerMoveAssignmentBase = false; template constexpr bool PropagateOnContainerMoveAssignmentBase = A::PropagateOnContainerMoveAssignment; } /* namespace detail */ template constexpr bool AllocatorPropagateOnContainerMoveAssignment = detail::PropagateOnContainerMoveAssignmentBase; /* allocator propagate on container swap */ namespace detail { template struct PropagateOnContainerSwapTest { template static char test(decltype(U::PropagateOnContainerSwap) * = 0); template static int test(...); static constexpr bool value = (sizeof(test(0)) == 1); }; template::value> constexpr bool PropagateOnContainerSwapBase = false; template constexpr bool PropagateOnContainerSwapBase = A::PropagateOnContainerSwap; } /* namespace detail */ template constexpr bool AllocatorPropagateOnContainerSwap = detail::PropagateOnContainerSwapBase; /* allocator is always equal */ namespace detail { template struct IsAlwaysEqualTest { template static char test(decltype(U::IsAlwaysEqual) * = 0); template static int test(...); static constexpr bool value = (sizeof(test(0)) == 1); }; template::value> constexpr bool IsAlwaysEqualBase = IsEmpty; template constexpr bool IsAlwaysEqualBase = A::IsAlwaysEqual; } /* namespace detail */ template constexpr bool AllocatorIsAlwaysEqual = detail::IsAlwaysEqualBase; /* 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), True()); template auto allocate_hint_test(A const &, S &&, CVP &&) -> False; template constexpr bool AllocateHintTest = IsSame< decltype(allocate_hint_test( declval(), declval(), declval() )), True >; template inline AllocatorPointer allocate( A &a, AllocatorSize n, AllocatorConstVoidPointer h, True ) { return a.allocate(n, h); } template inline AllocatorPointer allocate( A &a, AllocatorSize n, AllocatorConstVoidPointer, False ) { return a.allocate(n); } } /* namespace detail */ template inline AllocatorPointer allocator_allocate( A &a, AllocatorSize n, AllocatorConstVoidPointer h ) { return detail::allocate( a, n, h, BoolConstant, AllocatorConstVoidPointer >>() ); } /* allocator deallocate */ template inline void allocator_deallocate( A &a, AllocatorPointer p, AllocatorSize n ) noexcept { a.deallocate(p, n); } /* allocator construct */ namespace detail { template auto construct_test(A &&a, T *p, Args &&...args) -> decltype(a.construct(p, forward(args)...), True()); template auto construct_test(A const &, T *, Args &&...) -> False; template constexpr bool ConstructTest = IsSame< decltype(construct_test( declval(), declval(), declval()... )), True >; template inline void construct(True, A &a, T *p, Args &&...args) { a.construct(p, forward(args)...); } template inline void construct(False, A &, T *p, Args &&...args) { ::new (p) T(forward(args)...); } } /* namespace detail */ template inline void allocator_construct(A &a, T *p, Args &&...args) { detail::construct( BoolConstant>(), a, p, forward(args)... ); } /* allocator destroy */ namespace detail { template auto destroy_test(A &&a, P &&p) -> decltype(a.destroy(p), True()); template auto destroy_test(A const &, P &&) -> False; template constexpr bool DestroyTest = IsSame(), declval

())), True>; template inline void destroy(True, A &a, T *p) { a.destroy(p); } template inline void destroy(False, A &, T *p) { p->~T(); } } /* namespace detail */ template inline void allocator_destroy(A &a, T *p) noexcept { detail::destroy(BoolConstant>(), a, p); } /* allocator max size */ namespace detail { template auto alloc_max_size_test(A &&a) -> decltype(a.max_size(), True()); template auto alloc_max_size_test(A const &) -> False; template constexpr bool AllocMaxSizeTest = IsSame())), True>; template inline AllocatorSize alloc_max_size(True, A const &a) { return a.max_size(); } template inline AllocatorSize alloc_max_size(False, A const &) { return AllocatorSize(~0); } } /* namespace detail */ template inline AllocatorSize allocator_max_size(A const &a) noexcept { return detail::alloc_max_size( BoolConstant>(), a ); } /* allocator container copy */ namespace detail { template auto alloc_copy_test(A &&a) -> decltype(a.container_copy(), True()); template auto alloc_copy_test(A const &) -> False; template constexpr bool AllocCopyTest = IsSame())), True>; template inline AllocatorType alloc_container_copy(True, A const &a) { return a.container_copy(); } template inline AllocatorType alloc_container_copy(False, A const &a) { return a; } } /* namespace detail */ template inline AllocatorType allocator_container_copy(A const &a) { return detail::alloc_container_copy( BoolConstant>(), a ); } /* allocator arg */ struct AllocatorArg {}; constexpr AllocatorArg allocator_arg = AllocatorArg(); /* uses allocator */ namespace detail { template struct HasAllocatorType { template static char test(typename U::Allocator *); template static int test(...); static constexpr bool value = (sizeof(test(0)) == 1); }; template::value> constexpr bool UsesAllocatorBase = IsConvertible; template constexpr bool UsesAllocatorBase = false; } template constexpr bool UsesAllocator = detail::UsesAllocatorBase; /* uses allocator ctor */ namespace detail { template struct UsesAllocCtor { static constexpr bool ua = UsesAllocator; static constexpr bool ic = IsConstructible< T, AllocatorArg, A, Args... >; static constexpr int value = ua ? (2 - ic) : 0; }; } template constexpr int UsesAllocatorConstructor = detail::UsesAllocCtor::value; /* util for other classes */ namespace detail { template class AllocatorDestructor { A &p_alloc; Size p_size; public: using Pointer = AllocatorPointer; using Size = ostd::Size; AllocatorDestructor(A &a, Size s) noexcept: p_alloc(a), p_size(s) {} void operator()(Pointer p) noexcept { allocator_deallocate(p_alloc, p, p_size); } }; } } /* namespace ostd */ #endif