// streambuf standard header -*-c++-*-
// Copyright 2009-2017 IAR Systems AB.
#ifndef _STREAMBUF_
#define _STREAMBUF_

#ifndef _SYSTEM_BUILD
  #pragma system_include
#endif

#include <xiosbase>

/* Module consistency. */
#pragma rtmodel="__dlib_full_locale_support", \
  _STRINGIFY(_DLIB_FULL_LOCALE_SUPPORT)

namespace std {

// TEMPLATE CLASS basic_streambuf
template<class _Elem,
         class _Traits>
class basic_streambuf
{       // control read/write buffers
  typedef basic_streambuf<_Elem, _Traits> _Myt;

protected:
  basic_streambuf()
  #if _DLIB_FULL_LOCALE_SUPPORT
    : _Plocale(new locale)
  #endif
  {       // construct with no buffers
    _Init();
  }

  basic_streambuf(_Uninitialized)
  {       // construct uninitialized
  }

  basic_streambuf(const _Myt& _Right)
  #if _DLIB_FULL_LOCALE_SUPPORT
    : _Plocale(new locale(_Right.getloc()))
  #endif
  {       // construct by copying _Right
    _Init();
    setp(_Right.pbase(), _Right.pptr(), _Right.epptr());
    setg(_Right.eback(), _Right.gptr(), _Right.egptr());
  }

  _Myt& operator=(const _Myt& _Right)
  {       // assign from _Right
    if (this != &_Right)
    {       // different, worth copying
      setp(_Right.pbase(), _Right.pptr(), _Right.epptr());
      setg(_Right.eback(), _Right.gptr(), _Right.egptr());
      #if _DLIB_FULL_LOCALE_SUPPORT
        pubimbue(_Right.getloc());
      #endif
    }
    return *this;
  }

  void swap(_Myt& _Right)
  {       // swap with _Right
    if (this != &_Right)
    {       // different, worth swapping
      _Elem *_Pfirst0 = pbase();
      _Elem *_Pnext0 = pptr();
      _Elem *_Pend = epptr();
      _Elem *_Gfirst0 = eback();
      _Elem *_Gnext0 = gptr();
      _Elem *_Gend = egptr();

      setp(_Right.pbase(), _Right.pptr(), _Right.epptr());
      _Right.setp(_Pfirst0, _Pnext0, _Pend);

      setg(_Right.eback(), _Right.gptr(), _Right.egptr());
      _Right.setg(_Gfirst0, _Gnext0, _Gend);

      #if _DLIB_FULL_LOCALE_SUPPORT
        std::swap(_Plocale, _Right._Plocale);
      #endif
    }
  }

public:
  typedef _Elem char_type;
  typedef _Traits traits_type;

  virtual ~basic_streambuf() _NOEXCEPT
  {       // destroy the object
    #if _DLIB_FULL_LOCALE_SUPPORT
      delete _Plocale;
    #endif
  }

  typedef typename _Traits::int_type int_type;
  typedef typename _Traits::pos_type pos_type;
  typedef typename _Traits::off_type off_type;

  pos_type pubseekoff(off_type _Off,
                      ios_base::seekdir _Way,
                      ios_base::openmode _Mode = ios_base::in | ios_base::out)
  {       // change position by _Off, according to _Way, _Mode
    return seekoff(_Off, _Way, _Mode);
  }

  pos_type pubseekoff(off_type _Off,
                      ios_base::seek_dir _Way,
                      ios_base::open_mode _Mode)
  {       // change position by _Off, according to _Way, _Mode (old style)
    return pubseekoff(_Off, (ios_base::seekdir)_Way,
                      (ios_base::openmode)_Mode);
  }

  pos_type pubseekpos(pos_type _Pos,
                      ios_base::openmode _Mode = ios_base::in | ios_base::out)
  {       // change position to _Pos, according to _Mode
    return seekpos(_Pos, _Mode);
  }

  pos_type pubseekpos(pos_type _Pos,
                      ios_base::open_mode _Mode)
  {       // change position to _Pos, according to _Mode (old style)
    return seekpos(_Pos, (ios_base::openmode)_Mode);
  }

  _Myt *pubsetbuf(_Elem *_Buffer, streamsize _Count)
  {       // offer _Buffer to external agent
    return setbuf(_Buffer, _Count);
  }

  #if _DLIB_FULL_LOCALE_SUPPORT
    locale pubimbue(const locale &_Newlocale)
    {       // set locale to argument
      locale _Oldlocale = *_Plocale;
      imbue(_Newlocale);
      *_Plocale = _Newlocale;
      return _Oldlocale;
    }

    locale getloc() const
    {       // get locale
      return *_Plocale;
    }
  #endif

  streamsize in_avail()
  {       // return count of buffered input characters
    streamsize _Res = _Gnavail();
    return 0 < _Res ? _Res : showmanyc();
  }

  int pubsync()
  {       // synchronize with external agent
    return sync();
  }

  int_type sbumpc()
  {       // get a character and point past it
    return 0 < _Gnavail() ? _Traits::to_int_type(*_Gninc()) : uflow();
  }

  int_type sgetc()
  {       // get a character and don't point past it
    return 0 < _Gnavail() ? _Traits::to_int_type(*gptr()) : underflow();
  }

  streamsize sgetn(_Elem *_Ptr,
                   streamsize _Count)
  {       // get up to _Count characters into array beginning at _Ptr
    return xsgetn(_Ptr, _Count);
  }

  int_type snextc()
  {       // point to next character and return it
    return   1 < _Gnavail()
           ? _Traits::to_int_type(*_Gnpreinc())
           : _Traits::eq_int_type(_Traits::eof(), sbumpc())
           ? _Traits::eof() : sgetc();
  }

  int_type sputbackc(_Elem _Ch)
  {       // put back _Ch
    return   gptr() != 0 && eback() < gptr() && _Traits::eq(_Ch, gptr()[-1])
           ? _Traits::to_int_type(*_Gndec())
           : pbackfail(_Traits::to_int_type(_Ch));
  }

  void stossc()
  {       // point past a character
    if (0 < _Gnavail())
      _Gninc();
    else
      uflow();
  }

  int_type sungetc()
  {       // back up one position
    return   gptr() != 0 && eback() < gptr()
           ? _Traits::to_int_type(*_Gndec())
           : pbackfail();
  }

  int_type sputc(_Elem _Ch)
  {       // put a character
    return   0 < _Pnavail()
           ? _Traits::to_int_type(*_Pninc() = _Ch)
           : overflow(_Traits::to_int_type(_Ch));
  }

  streamsize sputn(const _Elem *_Ptr,
                   streamsize _Count)
  {       // put _Count characters from array beginning at _Ptr
    return xsputn(_Ptr, _Count);
  }

  virtual void _Lock()
  {       // set the thread lock (overridden by basic_filebuf)
  }

  virtual void _Unlock()
  {       // clear the thread lock (overridden by basic_filebuf)
  }

protected:
  _Elem *eback() const
  {       // return beginning of read buffer
    return *_IGfirst;
  }

  _Elem *gptr() const
  {       // return current position in read buffer
    return *_IGnext;
  }

  _Elem *pbase() const
  {       // return beginning of write buffer
    return *_IPfirst;
  }

  _Elem *pptr() const
  {       // return current position in write buffer
    return *_IPnext;
  }

  _Elem *egptr() const
  {       // return end of read buffer
    return *_IGlast;
  }

  void gbump(int _Off)
  {       // alter current position in read buffer by _Off
    *_IGnext += _Off;
  }

  void setg(_Elem *_First, _Elem *_Next, _Elem *_Last)
  {       // set pointers for read buffer
    *_IGfirst = _First, *_IGnext = _Next, *_IGlast = _Last;
  }

  _Elem *epptr() const
  {       // return end of write buffer
    return *_IPlast;
  }

  _Elem *_Gndec()
  {       // decrement current position in read buffer
    return --*_IGnext;
  }

  _Elem *_Gninc()
  {       // increment current position in read buffer
    return (*_IGnext)++;
  }

  _Elem *_Gnpreinc()
  {       // preincrement current position in read buffer
    return ++(*_IGnext);
  }

  streamsize _Gnavail() const
  {       // count number of available elements in read buffer
    return (streamsize)(*_IGnext != 0 ? *_IGlast - *_IGnext : 0);
  }

  void pbump(int _Off)
  {       // alter current position in write buffer by _Off
    *_IPnext += _Off;
  }

  void setp(_Elem *_First, _Elem *_Last)
  {       // set pointers for write buffer
    *_IPfirst = _First;
    *_IPnext = _First;
    *_IPlast = _Last;
  }

  void setp(_Elem *_First, _Elem *_Next, _Elem *_Last)
  {       // set pointers for write buffer, extended version
    *_IPfirst = _First;
    *_IPnext = _Next;
    *_IPlast = _Last;
  }

  _Elem *_Pninc()
  {       // increment current position in write buffer
    return (*_IPnext)++;
  }

  streamsize _Pnavail() const
  {       // count number of available positions in write buffer
    return (streamsize)(*_IPnext != 0 ? *_IPlast - *_IPnext : 0);
  }

  void _Init()
  {       // initialize buffer parameters for no buffers
    _IGfirst = &_Gfirst;
    _IPfirst = &_Pfirst;
    _IGnext = &_Gnext;
    _IPnext = &_Pnext;
    _IGlast = &_Glast;
    _IPlast = &_Plast;
    setp(0, 0);
    setg(0, 0, 0);
  }

  void _Init(_Elem **_Gf, _Elem **_Gn, _Elem **_Gl,
             _Elem **_Pf, _Elem **_Pn, _Elem **_Pl)
  {       // initialize buffer parameters as specified
    _IGfirst = _Gf;
    _IPfirst = _Pf;
    _IGnext = _Gn;
    _IPnext = _Pn;
    _IGlast = _Gl;
    _IPlast = _Pl;
  }

  virtual int_type overflow(int_type = _Traits::eof())
  {       // put a character to stream (always fail)
    return _Traits::eof();
  }

  virtual int_type pbackfail(int_type = _Traits::eof())
  {       // put a character back to stream (always fail)
    return _Traits::eof();
  }

  virtual streamsize showmanyc()
  {       // return count of input characters
    return 0;
  }

  virtual int_type underflow()
  {       // get a character from stream, but don't point past it
    return _Traits::eof();
  }

  virtual int_type uflow()
  {       // get a character from stream, point past it
    return   _Traits::eq_int_type(_Traits::eof(), underflow())
           ? _Traits::eof()
           : _Traits::to_int_type(*_Gninc());
  }

  virtual streamsize xsgetn(_Elem * _Ptr, streamsize _Count)
  {       // get _Count characters from stream
    int_type _Meta;
    streamsize _Size, _Copied;

    for (_Copied = 0; 0 < _Count; )
      if (0 < (_Size = _Gnavail()))
      {       // copy from read buffer
        if (_Count < _Size)
          _Size = _Count;
        _Traits::copy(_Ptr, gptr(), (size_t)_Size);
        _Ptr += _Size;
        _Copied += _Size;
        _Count -= _Size;
        gbump((int)_Size);
      }
      else if (_Traits::eq_int_type(_Traits::eof(), _Meta = uflow()))
        break;  // end of file, quit
      else
      {       // get a single character
        *_Ptr++ = _Traits::to_char_type(_Meta);
        ++_Copied;
        --_Count;
      }

    return _Copied;
  }

  virtual streamsize xsputn(const _Elem *_Ptr, streamsize _Count)
  {       // put _Count characters to stream
    streamsize _Size, _Copied;

    for (_Copied = 0; 0 < _Count; )
      if (0 < (_Size = _Pnavail()))
      {       // copy to write buffer
        if (_Count < _Size)
          _Size = _Count;
        _Traits::copy(pptr(), _Ptr, (size_t)_Size);
        _Ptr += _Size;
        _Copied += _Size;
        _Count -= _Size;
        pbump((int)_Size);
      }
      else if (_Traits::eq_int_type(_Traits::eof(),
                                    overflow(_Traits::to_int_type(*_Ptr))))
        break;  // single character put failed, quit
      else
      {       // count character successfully put
        ++_Ptr;
        ++_Copied;
        --_Count;
      }

    return _Copied;
  }

  virtual pos_type seekoff(off_type,
                           ios_base::seekdir,
                           ios_base::openmode = ios_base::in | ios_base::out)
  {       // change position by offset, according to way and mode
    return streampos(_BADOFF);
  }

  virtual pos_type seekpos(pos_type,
                           ios_base::openmode = ios_base::in | ios_base::out)
  {       // change to specified position, according to mode
    return streampos(_BADOFF);
  }

  virtual _Myt *setbuf(_Elem *, streamsize)
  {       // offer buffer to external agent (do nothing)
    return this;
  }

  virtual int sync()
  {       // synchronize with external agent (do nothing)
    return 0;
  }

  #if _DLIB_FULL_LOCALE_SUPPORT
    virtual void imbue(const locale&)
    {       // set locale to argument (do nothing)
    }
  #endif

private:
  _Elem *_Gfirst; // beginning of read buffer
  _Elem *_Pfirst; // beginning of write buffer
  _Elem **_IGfirst;       // pointer to beginning of read buffer
  _Elem **_IPfirst;       // pointer to beginning of write buffer
  _Elem *_Gnext;  // current position in read buffer
  _Elem *_Pnext;  // current position in write buffer
  _Elem **_IGnext;        // pointer to current position in read buffer
  _Elem **_IPnext;        // pointer to current position in write buffer

  _Elem *_Glast;  // end of read buffer
  _Elem *_Plast;  // end of write buffer
  _Elem **_IGlast;        // pointer to end of read buffer
  _Elem **_IPlast;        // pointer to end of write buffer

protected:
  #if _DLIB_FULL_LOCALE_SUPPORT
    locale *_Plocale;       // pointer to imbued locale object
  #endif
};

// TEMPLATE CLASS istreambuf_iterator
template<class _Elem,
         class _Traits>
class istreambuf_iterator
  : public iterator<input_iterator_tag,
                    _Elem, typename _Traits::off_type, const _Elem *, _Elem>
{       // wrap stream buffer as input iterator
  typedef istreambuf_iterator<_Elem, _Traits> _Myt;
public:
  typedef _Elem char_type;
  typedef _Traits traits_type;
  typedef basic_streambuf<_Elem, _Traits> streambuf_type;
  typedef basic_istream<_Elem, _Traits> istream_type;

  typedef typename traits_type::int_type int_type;
  typedef const _Elem *pointer;

  constexpr istreambuf_iterator(streambuf_type *_Sb = 0) _THROW0()
    : _Strbuf(_Sb), _Got(_Sb == 0), _Val()
  {       // construct from stream buffer _Sb
  }

  istreambuf_iterator(istream_type& _Istr) _THROW0()
  : _Strbuf(_Istr.rdbuf()), _Got(_Istr.rdbuf() == 0)
  {       // construct from stream buffer in istream _Istr
  }

  _Elem operator*() const
  {       // return designated value
    if (!_Got)
      _Peek();

    #if _ITERATOR_DEBUG_LEVEL == 2
      if (_Strbuf == 0)
        _DEBUG_ERROR("istreambuf_iterator is not dereferencable");
    #endif

    return _Val;
  }

  const _Elem *operator->() const
  {       // return pointer to class object
    return _POINTER_TO(**this);
  }

  _Myt& operator++()
  {       // preincrement
    #if _ITERATOR_DEBUG_LEVEL == 2
      if (_Strbuf == 0)
        _DEBUG_ERROR("istreambuf_iterator is not incrementable");
    #endif

    _Inc();
    return *this;
  }

  _Myt operator++(int)
  {       // postincrement
    if (!_Got)
      _Peek();
    _Myt _Tmp = *this;
    ++*this;
    return _Tmp;
  }

  bool equal(const _Myt& _Right) const
  {       // test for equality
    if (!_Got)
      _Peek();
    if (!_Right._Got)
      _Right._Peek();
    return    (_Strbuf == 0 && _Right._Strbuf == 0)
           || (_Strbuf != 0 && _Right._Strbuf != 0);
  }

private:
  void _Inc()
  {       // skip to next input element
    if (   _Strbuf == 0
        || traits_type::eq_int_type(traits_type::eof(), _Strbuf->sbumpc()))
      _Strbuf = 0, _Got = true;
    else
      _Got = false;
  }

  _Elem _Peek() const
  {       // peek at next input element
    int_type _Meta;
    if (   _Strbuf == 0
        || traits_type::eq_int_type(traits_type::eof(),
                                    _Meta = _Strbuf->sgetc()))
      _Strbuf = 0;
    else
      _Val = traits_type::to_char_type(_Meta);
    _Got = true;
    return _Val;
  }

  mutable streambuf_type *_Strbuf;        // the wrapped stream buffer
  mutable bool _Got;      // true if _Val is valid
  mutable _Elem _Val;     // next element to deliver
};

template<class _Elem,
         class _Traits>
struct _Is_checked_helper<istreambuf_iterator<_Elem, _Traits> >
  : public true_type
{       // mark istreambuf_iterator as checked
};

// istreambuf_iterator TEMPLATE OPERATORS
template<class _Elem,
         class _Traits> inline
bool operator==(
  const istreambuf_iterator<_Elem, _Traits>& _Left,
  const istreambuf_iterator<_Elem, _Traits>& _Right)
{       // test for istreambuf_iterator equality
  return _Left.equal(_Right);
}

template<class _Elem,
         class _Traits> inline
bool operator!=(
  const istreambuf_iterator<_Elem, _Traits>& _Left,
  const istreambuf_iterator<_Elem, _Traits>& _Right)
{       // test for istreambuf_iterator inequality
  return !(_Left == _Right);
}

// TEMPLATE CLASS ostreambuf_iterator
template<class _Elem,
         class _Traits>
class ostreambuf_iterator
  : public _Outit
{       // wrap stream buffer as output iterator
  typedef ostreambuf_iterator<_Elem, _Traits> _Myt;
public:
  typedef _Elem char_type;
  typedef _Traits traits_type;
  typedef basic_streambuf<_Elem, _Traits> streambuf_type;
  typedef basic_ostream<_Elem, _Traits> ostream_type;

  ostreambuf_iterator(streambuf_type *_Sb) _THROW0()
  : _Failed(false), _Strbuf(_Sb)
  {       // construct from stream buffer _Sb
  }

  ostreambuf_iterator(ostream_type& _Ostr) _THROW0()
  : _Failed(false), _Strbuf(_Ostr.rdbuf())
  {       // construct from stream buffer in _Ostr
  }

  _Myt& operator=(_Elem _Right)
  {       // store element and increment
    if (   _Strbuf == 0
        || traits_type::eq_int_type(_Traits::eof(), _Strbuf->sputc(_Right)))
      _Failed = true;
    return *this;
  }

  _Myt& operator*()
  {       // pretend to get designated element
    return *this;
  }

  _Myt& operator++()
  {       // pretend to preincrement
    return *this;
  }

  _Myt& operator++(int)
  {       // pretend to postincrement
    return *this;
  }

  bool failed() const _THROW0()
  {       // return true if any stores failed
    return _Failed;
  }

private:
  bool _Failed;   // true if any stores have failed
  streambuf_type *_Strbuf;        // the wrapped stream buffer
};

template<class _Elem,
         class _Traits>
struct _Is_checked_helper<ostreambuf_iterator<_Elem, _Traits> >
  : public true_type
{       // mark ostreambuf_iterator as checked
};

} /* namespace std */

#endif /* _STREAMBUF_ */

/*
 * Copyright (c) by P.J. Plauger. All rights reserved.
 * Consult your license regarding permissions and restrictions.
V6.50:0576 */
