// wbuffer -- stream buffer for code conversions
 #pragma once
#ifndef _CVT_WBUFFER_
#define _CVT_WBUFFER_
#ifndef RC_INVOKED
#include <streambuf>
#include <string>

 #pragma pack(push,_CRT_PACKING)
 #pragma warning(push,3)
 #pragma push_macro("new")
 #undef new

 #pragma warning(disable: 6385)

/*	Example: to convert from UCS to UTF-8 and write to cout
	wbuffer_convert< codecvt_utf8<wchar_t> >
		mybuf(cout.rdbuf());	// construct wide stream buffer object
	wostream mywcout(&mybuf);	// construct wide ostream object
	mywcout << (wchar_t)0x80;	// writes 0xc2 0x80
 */

namespace stdext {
	namespace cvt {

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

public:
	typedef wbuffer_convert<_Codecvt, _Elem, _Traits> _Myt;
	typedef _STD streambuf _Mysb;
	typedef _STD 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 _Traits::state_type state_type;

	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 = _STD _ADDFAC(_Loc, _Pcvt);
		}

	wbuffer_convert(_Mysb *_Strbuf, _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 = _STD _ADDFAC(_Loc, _Pcvt);
		}

	wbuffer_convert(_Mysb *_Strbuf,
		_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 = _STD _ADDFAC(_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
				size_t _Count = _Dest - _Buf;
				if (0 < _Count
					&& _Byte_traits::eq_int_type(
						_Byte_traits::eof(),
						(typename _Traits::int_type)_Mystrbuf->sputn(
							_Buf,
							(_STD streamsize)_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);
		}

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
					size_t _Count = _Dest - _Buf;
					if (0 < _Count
						&& _Byte_traits::eq_int_type(
							_Byte_traits::eof(),
							(typename _Traits::int_type)_Mystrbuf->sputn(
								_Buf,
								(_STD streamsize)_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,
							(_STD 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,
		_STD ios::seekdir,
		_STD ios::openmode =
			(_STD ios::openmode)(_STD ios::in | _STD ios::out))
		{	// change position by _Off
		return (pos_type(-1));	// always fail
		}

	virtual pos_type seekpos(pos_type,
		_STD ios::openmode =
			(_STD ios::openmode)(_STD ios::in | _STD 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
					_CSTD 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
	_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
	_STD string _Str;	// unconsumed input bytes
	_STD locale _Loc;	// manages reference to codecvt facet
	};
		}	// namespace cvt
	}	// namespace stdext
 #pragma pop_macro("new")
 #pragma warning(pop)
 #pragma pack(pop)

#endif /* RC_INVOKED */
#endif /* _CVT_WBUFFER_ */

/*
 * Copyright (c) 1992-2012 by P.J. Plauger.  ALL RIGHTS RESERVED.
 * Consult your license regarding permissions and restrictions.
V6.00:0009 */
