//
// handler_alloc_helpers.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// 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)
//

#ifndef ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP
#define ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP

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

#include "asio/detail/push_options.hpp"

#include "asio/detail/push_options.hpp"
#include <boost/detail/workaround.hpp>
#include "asio/detail/pop_options.hpp"

#include "asio/handler_alloc_hook.hpp"
#include "asio/detail/noncopyable.hpp"

// Calls to asio_handler_allocate and asio_handler_deallocate must be made from
// a namespace that does not contain any overloads of these functions. The
// asio_handler_alloc_helpers namespace is defined here for that purpose.
namespace asio_handler_alloc_helpers {

template <typename Handler>
inline void* allocate(std::size_t s, Handler* h)
{
#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
  return ::operator new(s);
#else
  using namespace asio;
  return asio_handler_allocate(s, h);
#endif
}

template <typename Handler>
inline void deallocate(void* p, std::size_t s, Handler* h)
{
#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
  ::operator delete(p);
#else
  using namespace asio;
  asio_handler_deallocate(p, s, h);
#endif
}

} // namespace asio_handler_alloc_helpers

namespace asio {
namespace detail {

// Traits for handler allocation.
template <typename Handler, typename Object>
struct handler_alloc_traits
{
  typedef Handler handler_type;
  typedef Object value_type;
  typedef Object* pointer_type;
  BOOST_STATIC_CONSTANT(std::size_t, value_size = sizeof(Object));
};

template <typename Alloc_Traits>
class handler_ptr;

// Helper class to provide RAII on uninitialised handler memory.
template <typename Alloc_Traits>
class raw_handler_ptr
  : private noncopyable
{
public:
  typedef typename Alloc_Traits::handler_type handler_type;
  typedef typename Alloc_Traits::value_type value_type;
  typedef typename Alloc_Traits::pointer_type pointer_type;
  BOOST_STATIC_CONSTANT(std::size_t, value_size = Alloc_Traits::value_size);

  // Constructor allocates the memory.
  raw_handler_ptr(handler_type& handler)
    : handler_(handler),
      pointer_(static_cast<pointer_type>(
            asio_handler_alloc_helpers::allocate(value_size, &handler_)))
  {
  }

  // Destructor automatically deallocates memory, unless it has been stolen by
  // a handler_ptr object.
  ~raw_handler_ptr()
  {
    if (pointer_)
      asio_handler_alloc_helpers::deallocate(
          pointer_, value_size, &handler_);
  }

private:
  friend class handler_ptr<Alloc_Traits>;
  handler_type& handler_;
  pointer_type pointer_;
};

// Helper class to provide RAII on uninitialised handler memory.
template <typename Alloc_Traits>
class handler_ptr
  : private noncopyable
{
public:
  typedef typename Alloc_Traits::handler_type handler_type;
  typedef typename Alloc_Traits::value_type value_type;
  typedef typename Alloc_Traits::pointer_type pointer_type;
  BOOST_STATIC_CONSTANT(std::size_t, value_size = Alloc_Traits::value_size);
  typedef raw_handler_ptr<Alloc_Traits> raw_ptr_type;

  // Take ownership of existing memory.
  handler_ptr(handler_type& handler, pointer_type pointer)
    : handler_(handler),
      pointer_(pointer)
  {
  }

  // Construct object in raw memory and take ownership if construction succeeds.
  handler_ptr(raw_ptr_type& raw_ptr)
    : handler_(raw_ptr.handler_),
      pointer_(new (raw_ptr.pointer_) value_type)
  {
    raw_ptr.pointer_ = 0;
  }

  // Construct object in raw memory and take ownership if construction succeeds.
  template <typename Arg1>
  handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1)
    : handler_(raw_ptr.handler_),
      pointer_(new (raw_ptr.pointer_) value_type(a1))
  {
    raw_ptr.pointer_ = 0;
  }

  // Construct object in raw memory and take ownership if construction succeeds.
  template <typename Arg1, typename Arg2>
  handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2)
    : handler_(raw_ptr.handler_),
      pointer_(new (raw_ptr.pointer_) value_type(a1, a2))
  {
    raw_ptr.pointer_ = 0;
  }

  // Construct object in raw memory and take ownership if construction succeeds.
  template <typename Arg1, typename Arg2, typename Arg3>
  handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3)
    : handler_(raw_ptr.handler_),
      pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3))
  {
    raw_ptr.pointer_ = 0;
  }

  // Construct object in raw memory and take ownership if construction succeeds.
  template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
  handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4)
    : handler_(raw_ptr.handler_),
      pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4))
  {
    raw_ptr.pointer_ = 0;
  }

  // Construct object in raw memory and take ownership if construction succeeds.
  template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
      typename Arg5>
  handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4,
      Arg5& a5)
    : handler_(raw_ptr.handler_),
      pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5))
  {
    raw_ptr.pointer_ = 0;
  }

  // Construct object in raw memory and take ownership if construction succeeds.
  template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
      typename Arg5, typename Arg6>
  handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4,
      Arg5& a5, Arg6& a6)
    : handler_(raw_ptr.handler_),
      pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5, a6))
  {
    raw_ptr.pointer_ = 0;
  }

  // Construct object in raw memory and take ownership if construction succeeds.
  template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
      typename Arg5, typename Arg6, typename Arg7>
  handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4,
      Arg5& a5, Arg6& a6, Arg7& a7)
    : handler_(raw_ptr.handler_),
      pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5, a6, a7))
  {
    raw_ptr.pointer_ = 0;
  }

  // Construct object in raw memory and take ownership if construction succeeds.
  template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
      typename Arg5, typename Arg6, typename Arg7, typename Arg8>
  handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4,
      Arg5& a5, Arg6& a6, Arg7& a7, Arg8& a8)
    : handler_(raw_ptr.handler_),
      pointer_(new (raw_ptr.pointer_) value_type(
            a1, a2, a3, a4, a5, a6, a7, a8))
  {
    raw_ptr.pointer_ = 0;
  }

  // Destructor automatically deallocates memory, unless it has been released.
  ~handler_ptr()
  {
    reset();
  }

  // Get the memory.
  pointer_type get() const
  {
    return pointer_;
  }

  // Release ownership of the memory.
  pointer_type release()
  {
    pointer_type tmp = pointer_;
    pointer_ = 0;
    return tmp;
  }

  // Explicitly destroy and deallocate the memory.
  void reset()
  {
    if (pointer_)
    {
      pointer_->value_type::~value_type();
      asio_handler_alloc_helpers::deallocate(
          pointer_, value_size, &handler_);
      pointer_ = 0;
    }
  }

private:
  handler_type& handler_;
  pointer_type pointer_;
};

} // namespace detail
} // namespace asio

#include "asio/detail/pop_options.hpp"

#endif // ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP
