//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
//
// 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)
//

/**
 * \file boost/process/detail/systembuf.hpp
 *
 * Includes the declaration of the systembuf class.
 */

#ifndef BOOST_PROCESS_DETAIL_SYSTEMBUF_HPP
#define BOOST_PROCESS_DETAIL_SYSTEMBUF_HPP

#include <boost/process/config.hpp>

#if defined(BOOST_POSIX_API)
#   include <sys/types.h>
#   include <unistd.h>
#elif defined(BOOST_WINDOWS_API)
#   include <windows.h>
#else
#   error "Unsupported platform."
#endif

#include <boost/noncopyable.hpp>
#include <boost/scoped_array.hpp>
#include <boost/assert.hpp>
#include <streambuf>
#include <cstddef>

namespace boost {
namespace process {

class postream;

namespace detail {

/**
 * std::streambuf implementation for handles.
 *
 * systembuf provides a std::streambuf implementation for handles.
 * Contrarywise to the handle class, this class does \b not take
 * ownership of the native handle; this should be taken care of
 * somewhere else.
 *
 * This class follows the expected semantics of a std::streambuf object.
 * However, it is not copyable to avoid introducing inconsistences with
 * the on-disk file and the in-memory buffers.
 */
class systembuf : public std::streambuf, public boost::noncopyable
{
    friend class boost::process::postream;

public:
#if defined(BOOST_PROCESS_DOXYGEN)
    /**
     * Opaque name for the native handle type.
     */
    typedef NativeHandleType handle_type;
#elif defined(BOOST_POSIX_API)
    typedef int handle_type;
#elif defined(BOOST_WINDOWS_API)
    typedef HANDLE handle_type;
#endif

    /**
     * Constructs a new systembuf for the given handle.
     *
     * This constructor creates a new systembuf object that reads or
     * writes data from/to the \a h native handle. This handle
     * is \b not owned by the created systembuf object; the code
     * should take care of it externally.
     *
     * This class buffers input and output; the buffer size may be
     * tuned through the \a bufsize parameter, which defaults to 8192
     * bytes.
     *
     * \see pistream and postream
     */
    explicit systembuf(handle_type h, std::size_t bufsize = 8192)
        : handle_(h),
          bufsize_(bufsize),
          read_buf_(new char[bufsize]),
          write_buf_(new char[bufsize])
    {
#if defined(BOOST_POSIX_API)
        BOOST_ASSERT(handle_ >= 0);
#elif defined(BOOST_WINDOWS_API)
        BOOST_ASSERT(handle_ != INVALID_HANDLE_VALUE);
#endif
        BOOST_ASSERT(bufsize_ > 0);
        setp(write_buf_.get(), write_buf_.get() + bufsize_);
    }

protected:
    /**
     * Reads new data from the native handle.
     *
     * This operation is called by input methods when there is no more
     * data in the input buffer. The function fills the buffer with new
     * data, if available.
     *
     * \pre All input positions are exhausted (gptr() >= egptr()).
     * \post The input buffer has new data, if available.
     * \returns traits_type::eof() if a read error occurrs or there are
     *          no more data to be read. Otherwise returns
     *          traits_type::to_int_type(*gptr()).
     */
    virtual int_type underflow()
    {
        BOOST_ASSERT(gptr() >= egptr());

        bool ok;
#if defined(BOOST_POSIX_API)
        ssize_t cnt = read(handle_, read_buf_.get(), bufsize_);
        ok = (cnt != -1 && cnt != 0);
#elif defined(BOOST_WINDOWS_API)
        DWORD cnt;
        BOOL res = ReadFile(handle_, read_buf_.get(), bufsize_, &cnt, NULL);
        ok = (res && cnt > 0);
#endif

        if (!ok)
            return traits_type::eof();
        else
        {
            setg(read_buf_.get(), read_buf_.get(), read_buf_.get() + cnt);
            return traits_type::to_int_type(*gptr());
        }
    }

    /**
     * Makes room in the write buffer for additional data.
     *
     * This operation is called by output methods when there is no more
     * space in the output buffer to hold a new element. The function
     * first flushes the buffer's contents to disk and then clears it to
     * leave room for more characters. The given \a c character is
     * stored at the beginning of the new space.
     *
     * \pre All output positions are exhausted (pptr() >= epptr()).
     * \post The output buffer has more space if no errors occurred
     *       during the write to disk.
     * \post *(pptr() - 1) is \a c.
     * \returns traits_type::eof() if a write error occurrs. Otherwise
     *          returns traits_type::not_eof(c).
     */
    virtual int_type overflow(int c)
    {
        BOOST_ASSERT(pptr() >= epptr());

        if (sync() == -1)
            return traits_type::eof();

        if (!traits_type::eq_int_type(c, traits_type::eof()))
        {
            traits_type::assign(*pptr(), static_cast<traits_type::char_type>(
                c));
            pbump(1);
        }

        return traits_type::not_eof(c);
    }

    /**
     * Flushes the output buffer to disk.
     *
     * Synchronizes the systembuf buffers with the contents of the file
     * associated to this object through the native handle. The output buffer
     * is flushed to disk and cleared to leave new room for more data.
     *
     * \returns 0 on success, -1 if an error occurred.
     */
    virtual int sync()
    {
#if defined(BOOST_POSIX_API)
        ssize_t cnt = pptr() - pbase();
        bool ok = (write(handle_, pbase(), cnt) == cnt);
        if (ok)
            pbump(-cnt);
        return ok ? 0 : -1;
#elif defined(BOOST_WINDOWS_API)
        long cnt = pptr() - pbase();
        DWORD rcnt;
        BOOL res = WriteFile(handle_, pbase(), cnt, &rcnt, NULL);
        bool ok = (res && static_cast<long>(rcnt) == cnt);
        if (ok)
            pbump(-cnt);
        return ok ? 0 : -1;
#endif
    }

private:
    /**
     * Native handle used by the systembuf object.
     */
    handle_type handle_;

    /**
     * Internal buffer size used during read and write operations.
     */
    std::size_t bufsize_;

    /**
     * Internal buffer used during read operations.
     */
    boost::scoped_array<char> read_buf_;

    /**
     * Internal buffer used during write operations.
     */
    boost::scoped_array<char> write_buf_;
};

}
}
}

#endif
