/* 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 #include #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); } 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 */ /* allocator */ template struct Allocator { using Value = T; Allocator() noexcept {} template Allocator(Allocator const &) noexcept {} Value *allocate(Size n) { return reinterpret_cast(::new byte[n * sizeof(T)]); } void deallocate(Value *p, Size) noexcept { ::delete[] reinterpret_cast(p); } }; 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( std::declval(), std::declval(), std::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, std::forward(args)...), True()); template auto construct_test(A const &, T *, Args &&...) -> False; template constexpr bool ConstructTest = IsSame< decltype(construct_test( std::declval(), std::declval(), std::declval()... )), True >; template inline void construct(True, A &a, T *p, Args &&...args) { a.construct(p, std::forward(args)...); } template inline void construct(False, A &, T *p, Args &&...args) { ::new (p) T(std::forward(args)...); } } /* namespace detail */ template inline void allocator_construct(A &a, T *p, Args &&...args) { detail::construct( BoolConstant>(), a, p, std::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< decltype(destroy_test(std::declval(), std::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 struct AllocatorDestructor { 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); } private: A &p_alloc; Size p_size; }; } } /* namespace ostd */ #endif