
// Copyright 2005-2009 Daniel James.
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

// A couple of templates to make using allocators easier.

#ifndef CPPCMS_BOOST_UNORDERED_DETAIL_ALLOCATOR_UTILITIES_HPP_INCLUDED
#define CPPCMS_BOOST_UNORDERED_DETAIL_ALLOCATOR_UTILITIES_HPP_INCLUDED

#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif

#include <cppcms_boost/config.hpp>

#if (defined(CPPCMS_BOOST_NO_STD_ALLOCATOR) || defined(CPPCMS_BOOST_DINKUMWARE_STDLIB)) \
    && !defined(__BORLANDC__)
#  define CPPCMS_BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES
#endif

#if defined(CPPCMS_BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES)
#  include <cppcms_boost/detail/allocator_utilities.hpp>
#endif

namespace cppcms_boost { namespace unordered_detail {

    // rebind_wrap
    //
    // Rebind allocators. For some problematic libraries, use rebind_to
    // from <boost/detail/allocator_utilities.hpp>.

#if defined(CPPCMS_BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES)
    template <class Alloc, class T>
    struct rebind_wrap : ::cppcms_boost::detail::allocator::rebind_to<Alloc, T> {};
#else
    template <class Alloc, class T>
    struct rebind_wrap
    {
        typedef CPPCMS_BOOST_DEDUCED_TYPENAME
            Alloc::CPPCMS_BOOST_NESTED_TEMPLATE rebind<T>::other
            type;
    };
#endif

    // allocator_array_constructor
    //
    // Allocate and construct an array in an exception safe manner, and
    // clean up if an exception is thrown before the container takes charge
    // of it.

    template <class Allocator>
    struct allocator_array_constructor
    {
        typedef CPPCMS_BOOST_DEDUCED_TYPENAME Allocator::pointer pointer;

        Allocator& alloc_;
        pointer ptr_;
        pointer constructed_;
        std::size_t length_;

        allocator_array_constructor(Allocator& a)
            : alloc_(a), ptr_(), constructed_(), length_(0)
        {
            constructed_ = pointer();
            ptr_ = pointer();
        }

        ~allocator_array_constructor() {
            if (ptr_) {
                for(pointer p = ptr_; p != constructed_; ++p)
                    alloc_.destroy(p);

                alloc_.deallocate(ptr_, length_);
            }
        }

        template <class V>
        void construct(V const& v, std::size_t l)
        {
            CPPCMS_BOOST_ASSERT(!ptr_);
            length_ = l;
            ptr_ = alloc_.allocate(length_);
            pointer end = ptr_ + static_cast<std::ptrdiff_t>(length_);
            for(constructed_ = ptr_; constructed_ != end; ++constructed_)
                alloc_.construct(constructed_, v);
        }

        pointer get() const
        {
            return ptr_;
        }

        pointer release()
        {
            pointer p(ptr_);
            ptr_ = pointer();
            return p;
        }
    private:
        allocator_array_constructor(allocator_array_constructor const&);
        allocator_array_constructor& operator=(
            allocator_array_constructor const&);
    };
}}

#if defined(CPPCMS_BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES)
#  undef CPPCMS_BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES
#endif

#endif
