/*
    Copyright 2019 Intel Corporation.

    This software and the related documents are Intel copyrighted materials, and your
    use of them is governed by the express license under which they were provided to
    you ("License"). Unless the License provides otherwise, you may not use, modify,
    copy, publish, distribute, disclose or transmit this software or the related
    documents without Intel's prior written permission.

    This software and the related documents are provided as is, with no express or
    implied warranties, other than those that are expressly stated in the License.



*/

#ifndef __TBB_allocator_traits_H
#define __TBB_allocator_traits_H

#include "../tbb_stddef.h" // true/false_type

#if __TBB_ALLOCATOR_TRAITS_PRESENT
#include <memory> // for allocator_traits
#endif

#if __TBB_CPP11_RVALUE_REF_PRESENT
#include <utility> // for std::move
#endif

// For allocator_swap helper
#include __TBB_STD_SWAP_HEADER

namespace tbb {
namespace internal {

//! Internal implementation of allocator traits, propagate_on_* use internal boolean_constant.
//! In order to avoid code duplication, check what implementation of boolean constant will likely be passed.
#if __TBB_ALLOCATOR_TRAITS_PRESENT
typedef std::true_type traits_true_type;
typedef std::false_type traits_false_type;
#else
typedef tbb::internal::true_type traits_true_type;
typedef tbb::internal::false_type traits_false_type;
#endif

//! Copy assignment implementation for allocator if propagate_on_container_copy_assignment == true_type
//! Noop if pocca == false_type
template <typename MyAlloc, typename OtherAlloc>
inline void allocator_copy_assignment(MyAlloc& my_allocator, OtherAlloc& other_allocator, traits_true_type) {
    my_allocator = other_allocator;
}
template <typename MyAlloc, typename OtherAlloc>
inline void allocator_copy_assignment(MyAlloc&, OtherAlloc&, traits_false_type) { /* NO COPY */}

#if __TBB_CPP11_RVALUE_REF_PRESENT
//! Move assignment implementation for allocator if propagate_on_container_move_assignment == true_type.
//! Noop if pocma == false_type.
template <typename MyAlloc, typename OtherAlloc>
inline void allocator_move_assignment(MyAlloc& my_allocator, OtherAlloc& other_allocator, traits_true_type) {
    my_allocator = std::move(other_allocator);
}
template <typename MyAlloc, typename OtherAlloc>
inline void allocator_move_assignment(MyAlloc&, OtherAlloc&, traits_false_type) { /* NO MOVE */ }
#endif

//! Swap implementation for allocators if propagate_on_container_swap == true_type.
//! Noop if pocs == false_type.
template <typename MyAlloc, typename OtherAlloc>
inline void allocator_swap(MyAlloc& my_allocator, OtherAlloc& other_allocator, traits_true_type) {
    using std::swap;
    swap(my_allocator, other_allocator);
}
template <typename MyAlloc, typename OtherAlloc>
inline void allocator_swap(MyAlloc&, OtherAlloc&, traits_false_type) { /* NO SWAP */ }

#if __TBB_ALLOCATOR_TRAITS_PRESENT
using std::allocator_traits;
#else
//! Internal allocator_traits implementation, which relies on C++03 standard
//! [20.1.5] allocator requirements
template<typename Alloc>
struct allocator_traits {
    // C++03 allocator doesn't have to be assignable or swappable, therefore
    // define these traits as false_type to do not require additional operations
    // that are not supposed to be in.
    typedef tbb::internal::false_type propagate_on_container_move_assignment;
    typedef tbb::internal::false_type propagate_on_container_copy_assignment;
    typedef tbb::internal::false_type propagate_on_container_swap;

    typedef Alloc allocator_type;
    typedef typename allocator_type::value_type value_type;

    typedef typename allocator_type::pointer pointer;
    typedef typename allocator_type::const_pointer const_pointer;
    typedef typename allocator_type::difference_type difference_type;
    typedef typename allocator_type::size_type size_type;

    template <typename U> struct rebind_alloc {
        typedef typename Alloc::template rebind<U>::other other;
    };

    static pointer allocate(Alloc& a, size_type n) {
        return a.allocate(n);
    }

    static void deallocate(Alloc& a, pointer p, size_type n) {
        a.deallocate(p, n);
    }

    template<typename PT>
    static void construct(Alloc&, PT* p) {
        ::new (static_cast<void*>(p)) PT();
    }

    template<typename PT, typename T1>
    static void construct(Alloc&, PT* p, __TBB_FORWARDING_REF(T1) t1) {
        ::new (static_cast<void*>(p)) PT(tbb::internal::forward<T1>(t1));
    }

    template<typename PT, typename T1, typename T2>
    static void construct(Alloc&, PT* p, __TBB_FORWARDING_REF(T1) t1, __TBB_FORWARDING_REF(T2) t2) {
        ::new (static_cast<void*>(p)) PT(tbb::internal::forward<T1>(t1), tbb::internal::forward<T2>(t2));
    }

    template<typename PT, typename T1, typename T2, typename T3>
    static void construct(Alloc&, PT* p, __TBB_FORWARDING_REF(T1) t1,
                          __TBB_FORWARDING_REF(T2) t2, __TBB_FORWARDING_REF(T3) t3) {
        ::new (static_cast<void*>(p)) PT(tbb::internal::forward<T1>(t1), tbb::internal::forward<T2>(t2),
                                         tbb::internal::forward<T3>(t3));
    }

    template<typename T>
    static void destroy(Alloc&, T* p) {
        p->~T();
        tbb::internal::suppress_unused_warning(p);
    }

    static Alloc select_on_container_copy_construction(const Alloc& a) { return a; }
};
#endif // __TBB_ALLOCATOR_TRAITS_PRESENT

//! C++03/C++11 compliant rebind helper, even if no std::allocator_traits available
//! or rebind is not defined for allocator type
template<typename Alloc, typename T>
struct allocator_rebind {
#if __TBB_ALLOCATOR_TRAITS_PRESENT
    typedef typename allocator_traits<Alloc>::template rebind_alloc<T> type;
#else
    typedef typename allocator_traits<Alloc>::template rebind_alloc<T>::other type;
#endif
};

}} // namespace tbb::internal

#endif // __TBB_allocator_traits_H

