// xlocbuf internal header (from <locale>) -*-c++-*-
// Copyright 2014-2017 IAR Systems AB.
#ifndef _XLOCBUF_
#define _XLOCBUF_

#ifndef _SYSTEM_BUILD
  #pragma system_include
#endif

#include <xlocale>

namespace std {

// TEMPLATE CLASS wbuffer_convert
template<class _Codecvt,
         class _Elem = wchar_t,
         class _Traits = char_traits<_Elem> >
class wbuffer_convert
  : public basic_streambuf<_Elem, _Traits>
{       // stream buffer associated with a codecvt facet
  enum _Mode : unsigned char {_Unused, _Wrote, _Need, _Got, _Eof};
  enum {_STRING_INC = 8};
public:
  typedef wbuffer_convert<_Codecvt, _Elem, _Traits> _Myt;
  typedef streambuf _Mysb;
  typedef char_traits<char> _Byte_traits;

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

  explicit wbuffer_convert(_Mysb *_Strbuf = 0)
    : _Pcvt(new _Codecvt),
      _Mystrbuf(_Strbuf), _Status(_Unused), _Nback(0)
  {       // construct with byte stream buffer pointer
    static state_type _State0;

    _State = _State0;
    _Loc = locale(_Loc, _Pcvt);
  }

  wbuffer_convert(_Mysb *_Strbuf, const _Codecvt *_Pcvt_arg)
    : _Pcvt(_Pcvt_arg),
      _Mystrbuf(_Strbuf), _Status(_Unused), _Nback(0)
  {       // construct with byte stream buffer pointer and codecvt
    static state_type _State0;

    _State = _State0;
    _Loc = locale(_Loc, _Pcvt);
  }

  wbuffer_convert(_Mysb *_Strbuf,
                  const _Codecvt *_Pcvt_arg, state_type _State_arg)
    : _Pcvt(_Pcvt_arg),
      _Mystrbuf(_Strbuf), _Status(_Unused), _Nback(0)
  {       // construct with byte stream buffer pointer, codecvt, and state
    _State = _State_arg;
    _Loc = locale(_Loc, _Pcvt);
  }

  virtual ~wbuffer_convert() _NOEXCEPT
  {       // destroy the object
    char *_Buf = (char *)_Str.c_str();

    for (; _Status == _Wrote ; )
    {       // put any trailing homing shift
      char *_Dest;

      if (_Str.size() < _STRING_INC)
        _Str.assign(_STRING_INC, '\0');
      switch (_Pcvt->unshift(_State, _Buf, _Buf + _Str.size(), _Dest))
      {       // test result of homing conversion
      case _Codecvt::ok:
        _Status = _Unused;      // homed successfully

      case _Codecvt::partial: // fall through
        {       // put any generated bytes
          int _Count = (int)(_Dest - _Buf);
          if (   0 < _Count
              && _Byte_traits::eq_int_type(
                   _Byte_traits::eof(),
                   (_Byte_traits::int_type)_Mystrbuf->sputn(_Buf, _Count)))
            return; // write failed

          if (_Status == _Wrote && _Count == 0)
            _Str.append(_STRING_INC, '\0'); // try with more space
          break;
        }

      case _Codecvt::noconv:
        return; // nothing to do

      default:
        return; // conversion failed
      }
    }
  }

  _Mysb *rdbuf() const
  {       // return byte stream buffer pointer
    return _Mystrbuf;
  }

  _Mysb *rdbuf(_Mysb *_Strbuf)
  {       // set byte stream buffer pointer
    _Mysb *_Oldstrbuf = _Mystrbuf;
    _Mystrbuf = _Strbuf;
    return _Oldstrbuf;
  }

  state_type state() const
  {       // get state
    return _State;
  }

  wbuffer_convert(const wbuffer_convert&) = delete;
  wbuffer_convert& operator=(const wbuffer_convert&) = delete;

protected:
  virtual int_type overflow(int_type _Meta = _Traits::eof())
  {       // put an element to stream
    if (_Traits::eq_int_type(_Traits::eof(), _Meta))
      return _Traits::not_eof(_Meta);       // EOF, return success code
    else if (   _Mystrbuf == 0 || 0 < _Nback
             || (_Status != _Unused && _Status != _Wrote))
      return _Traits::eof();        // no buffer or reading, fail
    else
    {       // put using codecvt facet
      char *_Buf = (char *)_Str.c_str();
      const _Elem _Ch = _Traits::to_char_type(_Meta);
      const _Elem *_Src;
      char *_Dest;

      if (_Str.size() < _STRING_INC)
        _Str.assign(_STRING_INC, '\0');
      for (_Status = _Wrote; ; )
        switch (_Pcvt->out(_State, &_Ch, &_Ch + 1, _Src,
                           _Buf, _Buf + _Str.size(), _Dest))
        {       // test result of converting one element
        case _Codecvt::partial:
        case _Codecvt::ok:
          {       // converted something, try to put it out
            int _Count = (int)(_Dest - _Buf);
            if (   0 < _Count
                && _Byte_traits::eq_int_type(
                     _Byte_traits::eof(),
                    (_Byte_traits::int_type)_Mystrbuf->sputn(_Buf, _Count)))
              return _Traits::eof();        // write failed

            if (_Src != &_Ch)
              return _Meta; // converted whole element

            if (0 < _Count)
              ;
            else if (_Str.size() < 4 * _STRING_INC)
              _Str.append(_STRING_INC, '\0'); // try with more space
            else
              return _Traits::eof();        // conversion failed
            break;
          }

        case _Codecvt::noconv:
          if (_Traits::eq_int_type(
                _Traits::eof(),
                (typename _Traits::int_type)_Mystrbuf->sputn(
                  (char *)&_Ch,
                  (streamsize)sizeof (_Elem))))
            return _Traits::eof();
          else
            return _Meta; // put native byte order

        default:
          return _Traits::eof();        // conversion failed
        }
    }
  }

  virtual int_type pbackfail(int_type _Meta = _Traits::eof())
  {       // put an element back to stream
    if (   sizeof (_Myback) / sizeof (_Myback[0]) <= _Nback
        || _Status == _Wrote)
      return _Traits::eof();        // nowhere to put back
    else
    {       // enough room, put it back
      if (!_Traits::eq_int_type(_Traits::eof(), _Meta))
        _Myback[_Nback] = _Traits::to_char_type(_Meta);
      ++_Nback;
      if (_Status == _Unused)
        _Status = _Got;
      return _Meta;
    }
  }

  virtual int_type underflow()
  {       // get an element from stream, but don't point past it
    int_type _Meta;

    if (0 < _Nback)
      ;
    else if (_Traits::eq_int_type(_Traits::eof(), _Meta = _Get_elem()))
      return _Meta; // _Get_elem failed, return EOF
    else
      _Myback[_Nback++] = _Traits::to_char_type(_Meta);
    return _Traits::to_int_type(_Myback[_Nback - 1]);
  }

  virtual int_type uflow()
  {       // get an element from stream, point past it
    int_type _Meta;

    if (0 < _Nback)
      ;
    else if (_Traits::eq_int_type(_Traits::eof(), _Meta = _Get_elem()))
      return _Meta; // _Get_elem failed, return EOF
    else
      _Myback[_Nback++] = _Traits::to_char_type(_Meta);
    return _Traits::to_int_type(_Myback[--_Nback]);
  }

  virtual pos_type seekoff(off_type, ios::seekdir,
                           ios::openmode = (ios::openmode)(ios::in | ios::out))
  {       // change position by _Off
    return pos_type(-1);  // always fail
  }

  virtual pos_type seekpos(pos_type,
                           ios::openmode = (ios::openmode)(ios::in | ios::out))
  {       // change position to _Pos
    return pos_type(-1);  // always fail
  }

private:
  int_type _Get_elem()
  {       // compose an element from byte stream buffer
    if (_Mystrbuf != 0 && _Status != _Wrote)
    {       // got buffer, haven't written, try to compose an element
      if (_Status == _Eof)
        ;
      else if (_Str.size() == 0)
        _Status = _Need;
      else
        _Status = _Got;

      for (; _Status != _Eof; )
      {       // get using codecvt facet
        char *_Buf = (char *)_Str.c_str();
        _Elem _Ch, *_Dest;
        const char *_Src;
        int _Meta;

        if (_Status != _Need)
          ;
        else if (_Byte_traits::eq_int_type(_Byte_traits::eof(),
                                           _Meta = _Mystrbuf->sbumpc()))
          _Status = _Eof;
        else
          _Str.append(1, _Byte_traits::to_char_type(_Meta));

        switch (_Pcvt->in(_State, _Buf, _Buf + _Str.size(), _Src,
                          &_Ch, &_Ch + 1, _Dest))
        {       // test result of converting one element
        case _Codecvt::partial:
        case _Codecvt::ok:
          _Str.erase((size_t)0,   // discard any used input
                     (size_t)(_Src - _Buf));
          if (_Dest != &_Ch)
            return _Traits::to_int_type(_Ch);
          break;

        case _Codecvt::noconv:
          if (_Str.size() < sizeof (_Elem))
            break;  // no conversion, but need more chars
          ::memcpy(&_Ch, _Buf, sizeof (_Elem));   // copy raw bytes to element
          _Str.erase((size_t)0, sizeof (_Elem));
          return _Traits::to_int_type(_Ch);     // return result

        default:
          _Status = _Eof; // conversion failed
        }
      }
    }

    return _Traits::eof();
  }

  state_type _State;      // code conversion state
  const _Codecvt *_Pcvt;  // the codecvt facet
  _Mysb *_Mystrbuf;       // pointer to stream buffer
  _Mode _Status;          // buffer read/write status
  size_t _Nback;                  // number of elements in putback buffer
  _Elem _Myback[8];       // putback buffer
  string _Str;    // unconsumed input bytes
  locale _Loc;    // manages reference to codecvt facet
};

// TEMPLATE CLASS wstring_convert
template<class _Codecvt,
         class _Elem = wchar_t,
         class _Walloc = allocator<_Elem>,
         class _Balloc = allocator<char> >
class wstring_convert
{       // converts between _Elem (wide) and char (byte) strings
  enum {_BUF_INC = 8, _BUF_MAX = 16};
  void _Init(const _Codecvt *_Pcvt_arg = new _Codecvt)
  {       // initialize the object
    static state_type _State0;
    _State = _State0;
    _Pcvt = _Pcvt_arg;
    _Loc = locale(_Loc, _Pcvt);
    _Nconv = 0;
  }

public:
  typedef basic_string<char, char_traits<char>, _Balloc> byte_string;
  typedef basic_string<_Elem, char_traits<_Elem>, _Walloc> wide_string;
  typedef typename _Codecvt::state_type state_type;
  typedef typename wide_string::traits_type::int_type int_type;

  wstring_convert()
    : _Has_berr(false), _Has_werr(false), _Has_state(false)
  {       // construct with no error strings
    _Init();
  }

  explicit wstring_convert(const _Codecvt *_Pcvt_arg)
    : _Has_berr(false), _Has_werr(false), _Has_state(false)
  {       // construct with no error strings and codecvt
    _Init(_Pcvt_arg);
  }

  wstring_convert(const _Codecvt *_Pcvt_arg, state_type _State_arg)
    : _Has_berr(false), _Has_werr(false), _Has_state(true)
  {       // construct with no error strings, codecvt, and state
    _Init(_Pcvt_arg);
    _State = _State_arg;
  }

  explicit wstring_convert(const byte_string& _Berr_arg)
    : _Has_berr(true), _Has_werr(false), _Has_state(false),
      _Berr(_Berr_arg)
  {       // construct with byte error string
    _Init();
  }

  wstring_convert(const byte_string& _Berr_arg,
                  const wide_string& _Werr_arg)
    : _Has_berr(true), _Has_werr(true), _Has_state(false),
      _Berr(_Berr_arg), _Werr(_Werr_arg)
  {       // construct with byte and wide error strings
    _Init();
  }

  virtual ~wstring_convert() _NOEXCEPT
  {       // destroy the object
  }

  size_t converted() const _NOEXCEPT
  {       // get conversion count
    return _Nconv;
  }

  state_type state() const
  {       // get state
    return _State;
  }

  wide_string from_bytes(char _Byte)
  {       // convert a byte to a wide string
    return from_bytes(&_Byte, &_Byte + 1);
  }

  wide_string from_bytes(const char *_Ptr)
  {       // convert a NTBS to a wide string
    return from_bytes(_Ptr, _Ptr + ::strlen(_Ptr));
  }

  wide_string from_bytes(const byte_string& _Bstr)
  {       // convert a byte string to a wide string
    const char *_Ptr = _Bstr.c_str();
    return from_bytes(_Ptr, _Ptr + _Bstr.size());
  }

  wide_string from_bytes(const char *_First, const char *_Last)
  {       // convert byte sequence [_First, _Last) to a wide string
    static state_type _State0;
    wide_string _Wbuf, _Wstr;
    const char *_First_sav = _First;

    if (!_Has_state)
      _State = _State0;       // reset state if not remembered
    _Wbuf.append((::size_t)_BUF_INC, (_Elem)'\0');
    for (_Nconv = 0; _First != _Last; _Nconv = _First - _First_sav)
    {       // convert one or more bytes
      _Elem *_Dest = &*_Wbuf.begin();
      _Elem *_Dnext;

      switch (_Pcvt->in(_State, _First, _Last, _First,
                        _Dest, _Dest + _Wbuf.size(), _Dnext))
      {       // test result of converting one or more bytes
      case _Codecvt::partial:
      case _Codecvt::ok:
        if (_Dest < _Dnext)
          _Wstr.append(_Dest, (::size_t)(_Dnext - _Dest));
        else if (_Wbuf.size() < _BUF_MAX)
          _Wbuf.append((::size_t)_BUF_INC, '\0');
        else if (_Has_werr)
          return _Werr;
        else
          __iar_Raise_conversion();
        break;

      case _Codecvt::noconv:
        for (; _First != _Last; ++_First)
          _Wstr.append((::size_t)1, (_Elem)(unsigned char)*_First);
        break;  // no conversion, just copy code values

      default:
        if (_Has_werr)
          return _Werr;
        else
          __iar_Raise_conversion();
      }
    }
    return _Wstr;
  }

  byte_string to_bytes(_Elem _Char)
  {       // convert a wide char to a byte string
    return to_bytes(&_Char, &_Char + 1);
  }

  byte_string to_bytes(const _Elem *_Wptr)
  {       // convert a NTWCS to a byte string
    const _Elem *_Next = _Wptr;
    for (; (int_type)*_Next != 0; ++_Next)
      ;
    return to_bytes(_Wptr, _Next);
  }

  byte_string to_bytes(const wide_string& _Wstr)
  {       // convert a wide string to a byte string
    const _Elem *_Wptr = _Wstr.c_str();
    return to_bytes(_Wptr, _Wptr + _Wstr.size());
  }

  byte_string to_bytes(const _Elem *_First, const _Elem *_Last)
  {       // convert wide sequence [_First, _Last) to a byte string
    static state_type _State0;
    byte_string _Bbuf, _Bstr;
    const _Elem *_First_sav = _First;

    if (!_Has_state)
      _State = _State0;       // reset state if not remembered
    _Bbuf.append((::size_t)_BUF_INC, '\0');
    for (_Nconv = 0; _First != _Last; _Nconv = _First - _First_sav)
    {       // convert one or more wide chars
      char *_Dest = &*_Bbuf.begin();
      char *_Dnext;

      switch (_Pcvt->out(_State, _First, _Last, _First,
                         _Dest, _Dest + _Bbuf.size(), _Dnext))
      {       // test result of converting one or more wide chars
      case _Codecvt::partial:
      case _Codecvt::ok:
        if (_Dest < _Dnext)
          _Bstr.append(_Dest, (::size_t)(_Dnext - _Dest));
        else if (_Bbuf.size() < _BUF_MAX)
          _Bbuf.append((::size_t)_BUF_INC, '\0');
        else if (_Has_berr)
          return _Berr;
        else
          __iar_Raise_conversion();
        break;

      case _Codecvt::noconv:
        for (; _First != _Last; ++_First)
          _Bstr.append((::size_t)1, (char)(int_type)*_First);
        break;  // no conversion, just copy code values

      default:
        if (_Has_berr)
          return _Berr;
        else
          __iar_Raise_conversion();
      }
    }
    return _Bstr;
  }

  wstring_convert(const wstring_convert&) = delete;
  wstring_convert& operator=(const wstring_convert&) = delete;

private:
  const _Codecvt *_Pcvt;  // the codecvt facet
  locale _Loc;    // manages reference to codecvt facet
  byte_string _Berr;
  wide_string _Werr;
  state_type _State;      // the remembered state
  bool _Has_state;
  bool _Has_berr;
  bool _Has_werr;
  size_t _Nconv;
};

} /* namespace std */

#endif /* _XLOCBUF_ */

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