// (C) Copyright 2005 Matthias Troyer



// Use, modification and distribution is subject to 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)



//  Authors: Matthias Troyer



#ifndef BOOST_MPI_PACKED_OPRIMITIVE_HPP

#define BOOST_MPI_PACKED_OPRIMITIVE_HPP



#include <boost/mpi/config.hpp>

#include <iostream>

#include <cstddef> // size_t

#include <boost/config.hpp>



#include <boost/mpi/datatype.hpp>

#include <boost/mpi/exception.hpp>

#include <boost/serialization/detail/get_data.hpp>

#include <boost/serialization/array.hpp>

#include <boost/assert.hpp>

#include <vector>

#include <boost/mpi/allocator.hpp>



namespace boost { namespace mpi {



/// serialization using MPI::Pack



class BOOST_MPI_DECL packed_oprimitive

{

public:

    /// the type of the buffer into which the data is packed upon serialization

    typedef std::vector<char, allocator<char> > buffer_type;



    packed_oprimitive(buffer_type & b, MPI_Comm const & comm)

         : buffer_(b),

           comm(comm)

        {

        }



    void const * address() const

    {

      return &buffer_[0];

    }



    const std::size_t& size() const

    {

      return size_ = buffer_.size();

    }



    void save_binary(void const *address, std::size_t count)

        {

          save_impl(address,MPI_BYTE,count);

        }



    // fast saving of arrays

    template<class T>

    void save_array(serialization::array<T> const& x, unsigned int /* file_version */)

    {

        if (x.count())

          save_impl(x.address(), get_mpi_datatype(*x.address()), x.count());

    }



    typedef is_mpi_datatype<mpl::_1> use_array_optimization;



#ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS

    friend class archive::save_access;

protected:

#else

public:

#endif



    // default saving of primitives.

    template<class T>

    void save(const T & t)

    {

        save_impl(&t, get_mpi_datatype<T>(t), 1);

    }



        void save(const std::string &s)

        {

      unsigned int l = static_cast<unsigned int>(s.size());

      save(l);

      save_impl(s.data(),MPI_CHAR,s.size());

        }



private:



    void save_impl(void const * p, MPI_Datatype t, int l)

        {

          // allocate enough memory

      int memory_needed;

      BOOST_MPI_CHECK_RESULT(MPI_Pack_size,(l,t,comm,&memory_needed));



         int position = buffer_.size();

          buffer_.resize(position + memory_needed);



          // pack the data into the buffer

          BOOST_MPI_CHECK_RESULT(MPI_Pack,

        (const_cast<void*>(p), l, t, boost::serialization::detail::get_data(buffer_), buffer_.size(), &position, comm));



          // reduce the buffer size if needed

          BOOST_ASSERT(std::size_t(position) <= buffer_.size());

          if (std::size_t(position) < buffer_.size())

            buffer_.resize(position);

        }



  buffer_type& buffer_;

  mutable std::size_t size_;

  MPI_Comm comm;

};



} } // end namespace boost::mpi



#endif // BOOST_MPI_PACKED_OPRIMITIVE_HPP

