//////////////////////////////////////////////////////////////////////////////
//
// (C) Copyright Ion Gaztanaga 2005-2012. 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)
//
// See http://www.boost.org/libs/interprocess for documentation.
//
//////////////////////////////////////////////////////////////////////////////
//
// This file comes from SGI's sstream file. Modified by Ion Gaztanaga 2005-2012.
// Changed internal SGI string to a buffer. Added efficient
// internal buffer get/set/swap functions, so that we can obtain/establish the
// internal buffer without any reallocation or copy. Kill those temporaries!
///////////////////////////////////////////////////////////////////////////////
/*
 * Copyright (c) 1998
 * Silicon Graphics Computer Systems, Inc.
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  Silicon Graphics makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 */

//!\file
//!This file defines basic_bufferbuf, basic_ibufferstream,
//!basic_obufferstream, and basic_bufferstream classes. These classes
//!represent streamsbufs and streams whose sources or destinations
//!are fixed size character buffers.

#ifndef BOOST_INTERPROCESS_BUFFERSTREAM_HPP
#define BOOST_INTERPROCESS_BUFFERSTREAM_HPP

#include <boost/interprocess/detail/config_begin.hpp>
#include <boost/interprocess/detail/workaround.hpp>

#include <iosfwd>
#include <ios>
#include <istream>
#include <ostream>
#include <string>    // char traits
#include <cstddef>   // ptrdiff_t
#include <boost/assert.hpp>
#include <boost/interprocess/interprocess_fwd.hpp>

namespace boost {  namespace interprocess {

//!A streambuf class that controls the transmission of elements to and from
//!a basic_xbufferstream. The elements are transmitted from a to a fixed
//!size buffer
template <class CharT, class CharTraits>
class basic_bufferbuf
   : public std::basic_streambuf<CharT, CharTraits>
{
   public:
   typedef CharT                                         char_type;
   typedef typename CharTraits::int_type                 int_type;
   typedef typename CharTraits::pos_type                 pos_type;
   typedef typename CharTraits::off_type                 off_type;
   typedef CharTraits                                    traits_type;
   typedef std::basic_streambuf<char_type, traits_type>  base_t;

   public:
   //!Constructor.
   //!Does not throw.
   explicit basic_bufferbuf(std::ios_base::openmode mode
                            = std::ios_base::in | std::ios_base::out)
      :  base_t(), m_mode(mode), m_buffer(0), m_length(0)
      {}

   //!Constructor. Assigns formatting buffer.
   //!Does not throw.
   explicit basic_bufferbuf(CharT *buf, std::size_t length,
                            std::ios_base::openmode mode
                              = std::ios_base::in | std::ios_base::out)
      :  base_t(), m_mode(mode), m_buffer(buf), m_length(length)
      {  this->set_pointers();   }

   virtual ~basic_bufferbuf(){}

   public:
   //!Returns the pointer and size of the internal buffer.
   //!Does not throw.
   std::pair<CharT *, std::size_t> buffer() const
      { return std::pair<CharT *, std::size_t>(m_buffer, m_length); }

   //!Sets the underlying buffer to a new value
   //!Does not throw.
   void buffer(CharT *buf, std::size_t length)
      {  m_buffer = buf;   m_length = length;   this->set_pointers();   }

   /// @cond
   private:
   void set_pointers()
   {
      // The initial read position is the beginning of the buffer.
      if(m_mode & std::ios_base::in)
         this->setg(m_buffer, m_buffer, m_buffer + m_length);

      // The initial write position is the beginning of the buffer.
      if(m_mode & std::ios_base::out)
         this->setp(m_buffer, m_buffer + m_length);
   }

   protected:
   virtual int_type underflow()
   {
      // Precondition: gptr() >= egptr(). Returns a character, if available.
      return this->gptr() != this->egptr() ?
         CharTraits::to_int_type(*this->gptr()) : CharTraits::eof();
   }

   virtual int_type pbackfail(int_type c = CharTraits::eof())
   {
      if(this->gptr() != this->eback()) {
         if(!CharTraits::eq_int_type(c, CharTraits::eof())) {
            if(CharTraits::eq(CharTraits::to_char_type(c), this->gptr()[-1])) {
               this->gbump(-1);
               return c;
            }
            else if(m_mode & std::ios_base::out) {
               this->gbump(-1);
               *this->gptr() = c;
               return c;
            }
            else
               return CharTraits::eof();
         }
         else {
            this->gbump(-1);
            return CharTraits::not_eof(c);
         }
      }
      else
         return CharTraits::eof();
   }

   virtual int_type overflow(int_type c = CharTraits::eof())
   {
      if(m_mode & std::ios_base::out) {
         if(!CharTraits::eq_int_type(c, CharTraits::eof())) {
//            if(!(m_mode & std::ios_base::in)) {
//               if(this->pptr() != this->epptr()) {
//                  *this->pptr() = CharTraits::to_char_type(c);
//                  this->pbump(1);
//                  return c;
//               }
//               else
//                  return CharTraits::eof();
//            }
//            else {
               if(this->pptr() == this->epptr()) {
                  //We can't append to a static buffer
                  return CharTraits::eof();
               }
               else {
                  *this->pptr() = CharTraits::to_char_type(c);
                  this->pbump(1);
                  return c;
               }
//            }
         }
         else  // c is EOF, so we don't have to do anything
            return CharTraits::not_eof(c);
      }
      else     // Overflow always fails if it's read-only.
         return CharTraits::eof();
   }

   virtual pos_type seekoff(off_type off, std::ios_base::seekdir dir,
                              std::ios_base::openmode mode
                                 = std::ios_base::in | std::ios_base::out)
   {
      bool in  = false;
      bool out = false;

      const std::ios_base::openmode inout =
         std::ios_base::in | std::ios_base::out;

      if((mode & inout) == inout) {
         if(dir == std::ios_base::beg || dir == std::ios_base::end)
            in = out = true;
      }
      else if(mode & std::ios_base::in)
         in = true;
      else if(mode & std::ios_base::out)
         out = true;

      if(!in && !out)
         return pos_type(off_type(-1));
      else if((in  && (!(m_mode & std::ios_base::in) || this->gptr() == 0)) ||
               (out && (!(m_mode & std::ios_base::out) || this->pptr() == 0)))
         return pos_type(off_type(-1));

      std::streamoff newoff;
      switch(dir) {
         case std::ios_base::beg:
            newoff = 0;
         break;
         case std::ios_base::end:
            newoff = static_cast<std::streamoff>(m_length);
         break;
         case std::ios_base::cur:
            newoff = in ? static_cast<std::streamoff>(this->gptr() - this->eback())
                        : static_cast<std::streamoff>(this->pptr() - this->pbase());
         break;
         default:
            return pos_type(off_type(-1));
      }

      off += newoff;

      if(in) {
         std::ptrdiff_t n = this->egptr() - this->eback();

         if(off < 0 || off > n)
            return pos_type(off_type(-1));
         else
            this->setg(this->eback(), this->eback() + off, this->eback() + n);
      }

      if(out) {
         std::ptrdiff_t n = this->epptr() - this->pbase();

         if(off < 0 || off > n)
            return pos_type(off_type(-1));
         else {
            this->setp(this->pbase(), this->pbase() + n);
            this->pbump(off);
         }
      }

      return pos_type(off);
   }

   virtual pos_type seekpos(pos_type pos, std::ios_base::openmode mode
                                 = std::ios_base::in | std::ios_base::out)
   {  return seekoff(pos - pos_type(off_type(0)), std::ios_base::beg, mode);  }

   private:
   std::ios_base::openmode m_mode;
   CharT *                 m_buffer;
   std::size_t             m_length;
   /// @endcond
};

//!A basic_istream class that uses a fixed size character buffer
//!as its formatting buffer.
template <class CharT, class CharTraits>
class basic_ibufferstream
   : public std::basic_istream<CharT, CharTraits>
{
   public:                         // Typedefs
   typedef typename std::basic_ios
      <CharT, CharTraits>::char_type          char_type;
   typedef typename std::basic_ios<char_type, CharTraits>::int_type     int_type;
   typedef typename std::basic_ios<char_type, CharTraits>::pos_type     pos_type;
   typedef typename std::basic_ios<char_type, CharTraits>::off_type     off_type;
   typedef typename std::basic_ios<char_type, CharTraits>::traits_type  traits_type;

   private:
   typedef std::basic_ios<char_type, CharTraits>                basic_ios_t;
   typedef std::basic_istream<char_type, CharTraits>            base_t;

   public:
   //!Constructor.
   //!Does not throw.
   basic_ibufferstream(std::ios_base::openmode mode = std::ios_base::in)
      :  basic_ios_t(), base_t(0), m_buf(mode | std::ios_base::in)
      {  basic_ios_t::init(&m_buf); }

   //!Constructor. Assigns formatting buffer.
   //!Does not throw.
   basic_ibufferstream(const CharT *buf, std::size_t length,
                       std::ios_base::openmode mode = std::ios_base::in)
      :  basic_ios_t(), base_t(0),
         m_buf(const_cast<CharT*>(buf), length, mode | std::ios_base::in)
      {  basic_ios_t::init(&m_buf); }

   ~basic_ibufferstream(){};

   public:
   //!Returns the address of the stored
   //!stream buffer.
   basic_bufferbuf<CharT, CharTraits>* rdbuf() const
      { return const_cast<basic_bufferbuf<CharT, CharTraits>*>(&m_buf); }

   //!Returns the pointer and size of the internal buffer.
   //!Does not throw.
   std::pair<const CharT *, std::size_t> buffer() const
      { return m_buf.buffer(); }

   //!Sets the underlying buffer to a new value. Resets
   //!stream position. Does not throw.
   void buffer(const CharT *buf, std::size_t length)
      {  m_buf.buffer(const_cast<CharT*>(buf), length);  }

   /// @cond
   private:
   basic_bufferbuf<CharT, CharTraits> m_buf;
   /// @endcond
};

//!A basic_ostream class that uses a fixed size character buffer
//!as its formatting buffer.
template <class CharT, class CharTraits>
class basic_obufferstream
   : public std::basic_ostream<CharT, CharTraits>
{
   public:
   typedef typename std::basic_ios
      <CharT, CharTraits>::char_type          char_type;
   typedef typename std::basic_ios<char_type, CharTraits>::int_type     int_type;
   typedef typename std::basic_ios<char_type, CharTraits>::pos_type     pos_type;
   typedef typename std::basic_ios<char_type, CharTraits>::off_type     off_type;
   typedef typename std::basic_ios<char_type, CharTraits>::traits_type  traits_type;

   /// @cond
   private:
   typedef std::basic_ios<char_type, CharTraits>      basic_ios_t;
   typedef std::basic_ostream<char_type, CharTraits>  base_t;
   /// @endcond
   public:
   //!Constructor.
   //!Does not throw.
   basic_obufferstream(std::ios_base::openmode mode = std::ios_base::out)
      :  basic_ios_t(), base_t(0), m_buf(mode | std::ios_base::out)
      {  basic_ios_t::init(&m_buf); }

   //!Constructor. Assigns formatting buffer.
   //!Does not throw.
   basic_obufferstream(CharT *buf, std::size_t length,
                       std::ios_base::openmode mode = std::ios_base::out)
      :  basic_ios_t(), base_t(0),
         m_buf(buf, length, mode | std::ios_base::out)
      {  basic_ios_t::init(&m_buf); }

   ~basic_obufferstream(){}

   public:
   //!Returns the address of the stored
   //!stream buffer.
   basic_bufferbuf<CharT, CharTraits>* rdbuf() const
      { return const_cast<basic_bufferbuf<CharT, CharTraits>*>(&m_buf); }

   //!Returns the pointer and size of the internal buffer.
   //!Does not throw.
   std::pair<CharT *, std::size_t> buffer() const
      { return m_buf.buffer(); }

   //!Sets the underlying buffer to a new value. Resets
   //!stream position. Does not throw.
   void buffer(CharT *buf, std::size_t length)
      {  m_buf.buffer(buf, length);  }

   /// @cond
   private:
   basic_bufferbuf<CharT, CharTraits> m_buf;
   /// @endcond
};


//!A basic_iostream class that uses a fixed size character buffer
//!as its formatting buffer.
template <class CharT, class CharTraits>
class basic_bufferstream
   : public std::basic_iostream<CharT, CharTraits>

{
   public:                         // Typedefs
   typedef typename std::basic_ios
      <CharT, CharTraits>::char_type          char_type;
   typedef typename std::basic_ios<char_type, CharTraits>::int_type     int_type;
   typedef typename std::basic_ios<char_type, CharTraits>::pos_type     pos_type;
   typedef typename std::basic_ios<char_type, CharTraits>::off_type     off_type;
   typedef typename std::basic_ios<char_type, CharTraits>::traits_type  traits_type;

   /// @cond
   private:
   typedef std::basic_ios<char_type, CharTraits>                 basic_ios_t;
   typedef std::basic_iostream<char_type, CharTraits>            base_t;
   /// @endcond

   public:
   //!Constructor.
   //!Does not throw.
   basic_bufferstream(std::ios_base::openmode mode
                      = std::ios_base::in | std::ios_base::out)
      :  basic_ios_t(), base_t(0), m_buf(mode)
      {  basic_ios_t::init(&m_buf); }

   //!Constructor. Assigns formatting buffer.
   //!Does not throw.
   basic_bufferstream(CharT *buf, std::size_t length,
                      std::ios_base::openmode mode
                        = std::ios_base::in | std::ios_base::out)
      :  basic_ios_t(), base_t(0), m_buf(buf, length, mode)
      {  basic_ios_t::init(&m_buf); }

   ~basic_bufferstream(){}

   public:
   //!Returns the address of the stored
   //!stream buffer.
   basic_bufferbuf<CharT, CharTraits>* rdbuf() const
      { return const_cast<basic_bufferbuf<CharT, CharTraits>*>(&m_buf); }

   //!Returns the pointer and size of the internal buffer.
   //!Does not throw.
   std::pair<CharT *, std::size_t> buffer() const
      { return m_buf.buffer(); }

   //!Sets the underlying buffer to a new value. Resets
   //!stream position. Does not throw.
   void buffer(CharT *buf, std::size_t length)
      {  m_buf.buffer(buf, length);  }

   /// @cond
   private:
   basic_bufferbuf<CharT, CharTraits> m_buf;
   /// @endcond
};

//Some typedefs to simplify usage
typedef basic_bufferbuf<char>        bufferbuf;
typedef basic_bufferstream<char>     bufferstream;
typedef basic_ibufferstream<char>    ibufferstream;
typedef basic_obufferstream<char>    obufferstream;

typedef basic_bufferbuf<wchar_t>     wbufferbuf;
typedef basic_bufferstream<wchar_t>  wbufferstream;
typedef basic_ibufferstream<wchar_t> wibufferstream;
typedef basic_obufferstream<wchar_t> wobufferstream;


}} //namespace boost {  namespace interprocess {

#include <boost/interprocess/detail/config_end.hpp>

#endif /* BOOST_INTERPROCESS_BUFFERSTREAM_HPP */
