// xlocale internal header (from <locale>) -*-c++-*-
// Copyright 2009-2017 IAR Systems AB.
#ifndef _XLOCALE_
#define _XLOCALE_

#ifndef _SYSTEM_BUILD
  #pragma system_include
#endif

#include <climits>
#include <cstring>
#include <clocale>
#if _DLIB_WIDE_CHARACTERS
#include <cwctype>
#endif
#include <stdexcept>
#include <typeinfo>
#include <xlocinfo>

#include <xctype.h>

#if _DLIB_FULL_LOCALE_SUPPORT

namespace std {

// CLASS _Facet_base
class _Facet_base
{       // code for reference counting a facet
public:
  virtual ~_Facet_base() _NOEXCEPT
  {       // ensure that derived classes can be destroyed properly
  }

  virtual void _Incref() = 0;     // increment use count
  virtual _Facet_base *_Decref() = 0;     // decrement use count
};

template<class _FB>
class _SPtr
{
public:
  _SPtr() : _P(0) {}
  explicit _SPtr(_FB * _ptr)
    : _P(_ptr)
  {
    if (_P)
      _P->_Incref();
  }
  _SPtr(const _SPtr &other)
    : _P(other._P)
  {
    if (_P)
      _P->_Incref();
  }

  void operator=(const _SPtr &right)
  {
    operator=(right._P);
  }
  void operator=(_FB * ptr)
  {
    if (_P)
      delete _P->_Decref();
    _P = ptr;
    if (_P)
      _P->_Incref();
  }

  virtual ~_SPtr()
  {
    if (_P)
      delete _P->_Decref();
  }

  _FB * operator->() const
  {
    return _P;
  }
  _FB * _Get() const
  {
    return _P;
  }

  bool _IsNull() const
  {
    return _P == 0;
  }

  bool operator==(_SPtr const &right) const
  {
    return _P == right._P;
  }

private:
  _FB * _P;
};

// TEMPLATE CLASS _Locbase
template<class _Dummy>
class _Locbase
{       // define templatized category constants, instantiate on demand
public:
  static const int collate = _M_COLLATE;
  static const int ctype = _M_CTYPE;
  static const int monetary = _M_MONETARY;
  static const int numeric = _M_NUMERIC;
  static const int time = _M_TIME;
  static const int messages = _M_MESSAGES;
  static const int all = _M_ALL;
  static const int none = 0;
};

template<class _Dummy>
const int _Locbase<_Dummy>::collate;
template<class _Dummy>
const int _Locbase<_Dummy>::ctype;
template<class _Dummy>
const int _Locbase<_Dummy>::monetary;
template<class _Dummy>
const int _Locbase<_Dummy>::numeric;
template<class _Dummy>
const int _Locbase<_Dummy>::time;
template<class _Dummy>
const int _Locbase<_Dummy>::messages;
template<class _Dummy>
const int _Locbase<_Dummy>::all;
template<class _Dummy>
const int _Locbase<_Dummy>::none;

// CLASS locale
class locale;
template<class _Facet>
const _Facet& use_facet(const locale&);
template<class _Elem>
class collate;

class locale
  : public _Locbase<int>
{       // nonmutable collection of facets that describe a locale
public:
  typedef int category;

  // CLASS id
  class id
  {       // identifier stamp, unique for each distinct kind of facet
  public:
    id(size_t _Val = 0)
      : _Id(_Val)
    {       // construct with specified stamp value
    }

    // Constructor for static initialization
    struct _Static_init_t {};
    id(_Static_init_t const &)
    {
    }

    operator size_t()
    {       // get stamp, with lazy allocation
      if (_Id == 0)
      {       // still zero, allocate stamp
        // Possibly lock thread
        if (_Id == 0)
          _Id = ++_Id_cnt;
      }
      return _Id;
    }

  private:
    size_t _Id;     // the identifier stamp

    static int _Id_cnt;

  public:
    id(const id&) = delete;
    id& operator=(const id&) = delete;
  };

  class _Locimp;

  // CLASS facet
  class facet
    : public _Facet_base
  {       // base class for all locale facets, performs reference counting
  public:
    static size_t _Getcat(const facet ** = 0, const locale * = 0)
    {       // get category value, or -1 if no corresponding C category
      return (size_t)(-1);
    }

    void _AddToLoc(const locale *pLoc, size_t id) const
    {
      pLoc->_Ptr->_Addfac(const_cast<facet *>(this), id);	
    }
		
    virtual void _Incref()
    {   // increment use count
      _MT_INCR(_Mtx, _Myrefs);
    }

    virtual _Facet_base *_Decref()
    {   // decrement use count
      if (_MT_DECR(_Mtx, _Myrefs) == 0)
        return this;
      else
        return 0;
    }

    #if _USE_ATOMIC_OPS
    private:
      _Atomic_counter_t _Myrefs;        // the reference count

    protected:
      explicit facet(size_t _Initrefs = 0)
      {       // construct with initial reference count
        _Init_atomic_counter(_Myrefs, _Initrefs);
      }

      virtual ~facet() _NOEXCEPT
      {       // ensure that derived classes can be destroyed properly
      }

    public:
      bool _Shared() const
      {       // test if more than one owner
        return 1 < _Get_atomic_count(_Myrefs);
      }
    #else /* _USE_ATOMIC_OPS */
    private:
      __int32_t _Myrefs;
      __iar_Rmtx _Mtx;     // the mutex

    protected:
      explicit facet(size_t _Initrefs = 0)
        : _Myrefs((__int32_t)_Initrefs)
      {       // construct
        __iar_Initdynamiclock(&_Mtx);
      }

      virtual ~facet() _NOEXCEPT
      {       // destroy mutex
        __iar_Dstdynamiclock(&_Mtx);
      }

    public:
      bool _Shared() const
      {       // test if more than one owner
        return 1 < _Myrefs;
      }
    #endif /* _USE_ATOMIC_OPS */

  public:
    facet(const facet&) = delete;
    facet& operator=(const facet&) = delete;
  };

  // CLASS _Locimp

  class _Locimp
    : public facet
  {       // reference-counted actual implementation of a locale
  protected:
    ~_Locimp() _NOEXCEPT;   // destroy the object

  private:
    friend class locale;

    static _Locimp *_New_Locimp()
    {       // allocate new _Locimp
      return new _Locimp();
    }

    static _Locimp *_New_Locimp(const _Locimp& _Right)
    {       // allocate new _Locimp by copying
      return new _Locimp(_Right);
    }

    static _Locimp *_New_Locimp(const _Locimp * _Right)
    {       // allocate new _Locimp by copying
      return new _Locimp(*_Right);
    }


    static _Locimp *_New_Locimp(const char *name)
    {       // allocate new _Locimp by copying
      return new _Locimp(name);
    }

    _Locimp();                      // construct from current locale

    _Locimp(const char *name);      // construct from a named locale

    _Locimp(const _Locimp&);        // copy a _Locimp

    void _Addfac(facet *, size_t);  // add a facet

    void _Merge(const char *name, category _Cat);	
    void _Merge(const _Locimp * _PLoc, category _Cat);
    void _Merge(const _Locinfo * _Pobj, category _Cat);

    facet **_Facetvec;      // pointer to vector of facets
    size_t _Facetcount;     // size of vector of facets
    category _Catmask;      // mask describing implemented categories

    char const *_Name() const;
    void _ResetName();
    bool _UpdateName();
    bool _HasName;

    _Locinfo const & _Info() const { return _info; }

    _Locinfo _info;
	
    static _Locimp *_Clocptr;       // pointer to "C" locale object
  };

  template<class _Elem,
           class _Traits,
           class _Alloc>
  bool operator()(const basic_string<_Elem, _Traits, _Alloc>&,
                  const basic_string<_Elem, _Traits, _Alloc>&)
    const;  // compare strings

  template<class _Facet>
  locale combine(const locale& _Loc) const
  {       // combine locale with facet from _Loc
    _Facet *_Facptr;

    _TRY_BEGIN
      _Facptr = (_Facet *)&use_facet<_Facet>(_Loc);
    _CATCH_ALL
      __iar_Raise_locale_facet();
    _CATCH_END

    _Locimp *_Newimp = _Locimp::_New_Locimp(_Ptr._Get());
    _Newimp->_Addfac(_Facptr, _Facet::id);
    _Newimp->_Catmask = none;
    _Newimp->_ResetName();
    return locale(_Newimp);
  }

  template<class _Facet>
  locale(const locale& _Loc, const _Facet *_Facptr)
    : _Ptr(_Locimp::_New_Locimp(_Loc._Ptr._Get()))
  {       // construct from _Loc, replacing facet with *_Facptr
    if (_Facptr != 0)
    {       // replace facet
      _Ptr->_Addfac((_Facet *)_Facptr, _Facet::id);
      _Ptr->_Catmask = none;
      _Ptr->_ResetName();
    }
  }

  locale(_Uninitialized)
  {       // defer construction
  }

  #if _NO_LOCALES
    locale(const locale& _Right) _THROW0()
      : _Ptr(0)
    {       // construct by copying
    }

    locale() _THROW0()
      : _Ptr(0)
    {       // construct from current locale
    }

    ~locale() _NOEXCEPT
    {       // destroy the object
    }

    locale& operator=(const locale& _Right) _THROW0()
    {       // assign a locale
      return *this;
    }
  #else /* _NO_LOCALES */
    locale(const locale& _Right) _THROW0()
      : _Ptr(_Right._Ptr)
    {       // construct by copying
    }

    locale() _THROW0()
      : _Ptr(_Init(false))
    {       // construct from current locale
    }

    locale(const locale& _Loc, const locale& _Other,
           category _Cat)
      : _Ptr(_Locimp::_New_Locimp(_Loc._Ptr._Get()))
    {       // construct a locale by copying named facets
      _Ptr->_Merge(_Other._Ptr._Get(), _Cat);
    }


    explicit locale(const char *_Locname)
      : _Ptr(_Locimp::_New_Locimp(_Locname))
    { // construct a locale with named facets
    }

    locale(const locale& _Loc, const char *_Locname,
           category _Cat)
      : _Ptr(_Locimp::_New_Locimp(_Loc._Ptr._Get()))
    { // construct a locale by copying, replacing named facets
      _Ptr->_Merge(_Locname, _Cat);
    }


    explicit locale(const string& _Str)
      : _Ptr(_Locimp::_New_Locimp(_Str.c_str()))
    {	// construct a locale with named facets
    }

    locale(const locale& _Loc, const string& _Str,
           category _Cat)
      : _Ptr(_Locimp::_New_Locimp(_Loc._Ptr._Get()))
    {	// construct a locale by copying, replacing named facets
      _Ptr->_Merge(_Str.c_str(), _Cat);
    }

    ~locale() _NOEXCEPT
    {	// destroy the object
    }

    locale& operator=(const locale& _Right) _THROW0()
    {	// assign a locale
      _Ptr = _Right._Ptr;
      return *this;
    }

    _Locinfo const &_GetLocinfo() const
    {
      return _Ptr->_info;
    }		
  #endif /* _NO_LOCALES */

  string name() const
  {       // return locale name
    return _Ptr._IsNull() ? string() : _Ptr->_Name();
  }

  const char *c_str() const
  {       // return locale name as NTBS
    return _Ptr._IsNull() ? "" : _Ptr->_Name();
  }

  // Moved to locale0.cpp
  const facet *_Getfacet(size_t _Id) const;
  bool operator==(const locale& _Loc) const;

  bool operator!=(const locale& _Right) const
  {       // test for locale inequality
    return !(*this == _Right);
  }

  static const locale& classic(); // classic "C" locale

  static locale global(const locale&);    // current locale

private:
  locale(_Locimp *_Ptrimp)
    : _Ptr(_Ptrimp)
  {       // construct from _Locimp pointer
  }

  static _Locimp *_Init(
    bool _Do_incref = false);       // initialize locale

  bool _Badname(const _Locinfo& _Lobj)
  {       // test if name is "*"
    return ::strcmp(_Lobj._Getname(), "*") == 0;
  }

//  _Locimp *_Ptr;  // pointer to locale implementation object
  _SPtr<_Locimp> _Ptr;
};

// SUPPORT TEMPLATES

#if _NO_LOCALES
  inline _Locinfo& _Get_locinfo()
  {       // get reference to common locale info
    static _Locinfo _Lobj;
    return _Lobj;
  }

  template<class _Facet>
  class _Wrap_facet
    : public _Facet
  {       // makes a facet destructible and shareable
  public:
    _Wrap_facet()
      : _Facet(_Get_locinfo())
    {       // construct from locale object
    }
  };
#endif /* _NO_LOCALES */

#define _ADDFAC(loc, pfac)     locale(loc, pfac)       /* add facet to locale */

#define _USE(loc, fac)                                          \
  use_facet< fac >(loc)   /* get facet reference from locale */

template<class _Facet> inline
const _Facet& use_facet(const locale& _Loc)

{       // get facet reference from locale
  #if _NO_LOCALES
    static _Wrap_facet<_Facet> _Wfac;
    return *(_Facet *)&_Wfac;
  #else /* _NO_LOCALES */
    // Possibly lock thread

    size_t _Id = _Facet::id;
    const locale::facet *_Pf = _Loc._Getfacet(_Id);

    if (_Pf == 0)
    {
      if (_Facet::_Getcat(&_Pf, &_Loc) != (size_t)(-1))
      {
        _Pf->_AddToLoc(&_Loc, _Id);
      }
      else
      {
        __iar_Raise_bad_cast();
      }
    }

    return (const _Facet&)(*_Pf); // should be dynamic_cast
  #endif /* _NO_LOCALES */
}       // end of use_facet body

// TEMPLATE FUNCTION _Getloctxt
template<class _Elem,
         class _InIt> inline
int _Getloctxt(_InIt& _First, _InIt& _Last, size_t _Numfields,
               const _Elem *_Ptr)
{       // find field at _Ptr that matches longest in [_First, _Last)
  for (size_t _Off = 0; _Ptr[_Off] != (_Elem)0; ++_Off)
    if (_Ptr[_Off] == _Ptr[0])
      ++_Numfields;   // add fields with leading mark to initial count
  string _Str(_Numfields, '\0');  // one column counter for each field

  int _Ans = -2;  // no candidates so far
  for (size_t _Column = 1; ; ++_Column, ++_First, _Ans = -1)
  {       // test each element against all viable fields
    bool _Prefix = false;   // seen at least one valid prefix
    size_t _Off = 0;        // offset into fields
    size_t _Field = 0;      // current field number

    for (; _Field < _Numfields; ++_Field)
    {       // test element at _Column in field _Field
      for (; _Ptr[_Off] != (_Elem)0 && _Ptr[_Off] != _Ptr[0]; ++_Off)
        ;       // find beginning of field

      if (_Str[_Field] != '\0')
        _Off += _Str[_Field];   // skip tested columns in field
      else if (_Ptr[_Off += _Column] == _Ptr[0]
               || _Ptr[_Off] == (_Elem)0)
      {       // matched all of field, save as possible answer
        _Str[_Field] = (char)(_Column < 127 ? _Column : 127);
                                            // save skip count if small enough
        _Ans = (int)_Field;     // save answer
      }
      else if (_First == _Last || _Ptr[_Off] != *_First)
        _Str[_Field] = (char)(_Column < 127 ? _Column : 127);
                                             // no match, just save skip count
      else
        _Prefix = true; // still a valid prefix
    }

    if (!_Prefix || _First == _Last)
      break;  // no pending prefixes or no input, give up
  }
  return _Ans;  // return field number or negative value on failure
}

// TEMPLATE FUNCTION _Maklocbyte
#define _MAKLOCBYTE(Elem, chr, cvt)                             \
  _Maklocbyte((_Elem)chr, cvt)    /* convert Elem to char */

template<class _Elem> inline
char _Maklocbyte(_Elem _Char,
                 const _Locinfo::_Cvtvec&)
{       // convert _Elem to char using _Cvtvec
  return (char)(unsigned char)_Char;
}

template<> inline
char _Maklocbyte(wchar_t _Char,
                 const _Locinfo::_Cvtvec& _Cvt)
{       // convert wchar_t to char using _Cvtvec
  char _Byte = '\0';
  _Mbstinit(_Mbst1);
  _Wcrtomb(&_Byte, _Char, &_Mbst1, &_Cvt);
  return _Byte;
}

// TEMPLATE FUNCTION _Maklocchr
#define _MAKLOCCHR(Elem, chr, cvt)                              \
  _Maklocchr(chr, (Elem *)0, cvt) /* convert char to Elem */

template<class _Elem> inline
_Elem _Maklocchr(char _Byte, _Elem *,
                 const _Locinfo::_Cvtvec&)
{       // convert char to _Elem using _Cvtvec
  return (_Elem)(unsigned char)_Byte;
}

template<> inline
wchar_t _Maklocchr(char _Byte, wchar_t *,
                   const _Locinfo::_Cvtvec& _Cvt)
{       // convert char to wchar_t using _Cvtvec
  wchar_t _Wc = L'\0';
  _Mbstinit(_Mbst1);
  _Mbrtowc(&_Wc, &_Byte, 1, &_Mbst1, &_Cvt);
  return _Wc;
}

// TEMPLATE FUNCTION _Maklocstr
#define _MAKLOCSTR(Elem, str, cvt)                                      \
  _Maklocstr(str, (Elem *)0, cvt) /* convert C string to Elem sequence */

template<class _Elem> inline
_Elem *_Maklocstr(const char *_Ptr, _Elem *,
                  const _Locinfo::_Cvtvec&)
{       // convert C string to _Elem sequence using _Cvtvec
  size_t _Count = ::strlen(_Ptr) + 1;
  _Elem *_Ptrdest = (_Elem *)::malloc(_Count);
  if (!_Ptrdest)
    __iar_Raise_bad_alloc();

  for (_Elem *_Ptrnext = _Ptrdest; 0 < _Count; --_Count, ++_Ptrnext, ++_Ptr)
    *_Ptrnext = (_Elem)(unsigned char)*_Ptr;
  return _Ptrdest;
}

template<> inline
wchar_t *_Maklocstr(const char *_Ptr, wchar_t *,
                    const _Locinfo::_Cvtvec& _Cvt)
{       // convert C string to wchar_t sequence using _Cvtvec
  size_t _Count, _Count1;
  size_t _Wchars;
  const char *_Ptr1;
  int _Bytes;
  wchar_t _Wc;
  _Mbstinit(_Mbst1);

  _Count1 = ::strlen(_Ptr) + 1;
  for (_Count = _Count1, _Wchars = 0, _Ptr1 = _Ptr;
       0 < _Count;
       _Count -= _Bytes, _Ptr1 += _Bytes, ++_Wchars)
    if ((_Bytes = _Mbrtowc(&_Wc, _Ptr1, _Count, &_Mbst1, &_Cvt)) <= 0)
      break;
  ++_Wchars;      // count terminating nul
 
  wchar_t *_Ptrdest = (wchar_t *)::malloc(_Wchars * sizeof (wchar_t));
  if (!_Ptrdest)
    __iar_Raise_bad_alloc();
  wchar_t *_Ptrnext = _Ptrdest;
  _Mbstinit(_Mbst2);

  for (;
       0 < _Wchars;
       _Count1 -= _Bytes, _Ptr += _Bytes, --_Wchars, ++_Ptrnext)
    if ((_Bytes = _Mbrtowc(_Ptrnext, _Ptr, _Count1, &_Mbst2, &_Cvt)) <= 0)
      break;
  *_Ptrnext = L'\0';
  return _Ptrdest;
}

// STRUCT codecvt_base

class codecvt_base
  : public locale::facet
{       // base class for codecvt
public:
  enum
  {       // constants for different parse states
    ok, partial, error, noconv};
  typedef int result;

  codecvt_base(size_t _Refs = 0)
    : locale::facet(_Refs)
  {       // default constructor
  }

  bool always_noconv() const _THROW0()
  {       // return true if conversions never change input (from codecvt)
    return do_always_noconv();
  }

  int max_length() const _THROW0()
  {       // return maximum length required for a conversion (from codecvt)
    return do_max_length();
  }

  int encoding() const _THROW0()
  {       // return length of code sequence (from codecvt)
    return do_encoding();
  }

  ~codecvt_base() _NOEXCEPT
  {       // destroy the object
  }

protected:
  virtual bool do_always_noconv() const _THROW0()
  {       // return true if conversions never change input (from codecvt)
    return false;
  }

  virtual int do_max_length() const _THROW0()
  {       // return maximum length required for a conversion (from codecvt)
    return 1;
  }

  virtual int do_encoding() const _THROW0()
  {       // return length of code sequence (from codecvt)
    return 1;     // -1 ==> state dependent, 0 ==> varying length
  }
};

// TEMPLATE CLASS codecvt
template<class _Elem,
         class _Byte,
         class _Statype>
class codecvt
  : public codecvt_base
{       // facet for converting between _Elem and char (_Byte) sequences
public:
  typedef _Elem intern_type;
  typedef _Byte extern_type;
  typedef _Statype state_type;

  result in(_Statype& _State,
            const _Byte *_First1, const _Byte *_Last1, const _Byte *& _Mid1,
            _Elem *_First2, _Elem *_Last2, _Elem *& _Mid2) const
  {       // convert bytes [_First1, _Last1) to [_First2, _Last)
    return do_in(_State, _First1, _Last1, _Mid1, _First2, _Last2, _Mid2);
  }

  result out(_Statype& _State,
             const _Elem *_First1, const _Elem *_Last1, const _Elem *& _Mid1,
             _Byte *_First2, _Byte *_Last2, _Byte *& _Mid2) const
  {       // convert [_First1, _Last1) to bytes [_First2, _Last2)
    return do_out(_State, _First1, _Last1, _Mid1, _First2, _Last2, _Mid2);
  }

  result unshift(_Statype& _State,
                 _Byte *_First2, _Byte *_Last2, _Byte *& _Mid2) const
  {       // generate bytes to return to default shift state
    return do_unshift(_State, _First2, _Last2, _Mid2);
  }

  int length(_Statype& _State, const _Byte *_First1,
             const _Byte *_Last1, size_t _Count) const
  {       // return min(_Count, converted length of bytes [_First1, _Last1))
    return do_length(_State, _First1, _Last1, _Count);
  }

  static locale::id id;   // unique facet id

  explicit codecvt(size_t _Refs = 0)
    : codecvt_base(_Refs)
  {       // construct from current locale
    _Init(_Locinfo());
  }

  codecvt(const _Locinfo& _Lobj, size_t _Refs = 0)
    : codecvt_base(_Refs)
  {       // construct from specified locale
    _Init(_Lobj);
  }

  static size_t _Getcat(const locale::facet **_Ppf = 0,
                        const locale *_Ploc = 0)
  {       // return locale category mask and construct standard facet
    if (_Ppf != 0 && *_Ppf == 0)
      *_Ppf = new codecvt<_Elem, _Byte, _Statype>(
          _Ploc->_GetLocinfo());
    return _X_CTYPE;
  }

protected:
  virtual ~codecvt() _NOEXCEPT
  {       // destroy the object
  }

  void _Init(const _Locinfo&)
  {       // initialize from _Locinfo object
  }

  virtual bool do_always_noconv() const _THROW0()
  {       // return true if conversions never change input (from codecvt)
    return is_same<_Byte, _Elem>::value;
  }

  virtual result do_in(_Statype&,
                       const _Byte *_First1, const _Byte *_Last1,
                       const _Byte *& _Mid1, _Elem *_First2, _Elem *_Last2,
                       _Elem *& _Mid2) const
  {       // convert bytes [_First1, _Last1) to [_First2, _Last)
    _Mid1 = _First1, _Mid2 = _First2;
    if (is_same<_Byte, _Elem>::value)
      return noconv;        // convert nothing
    else
    {       // types differ, copy one for one
      for (; _Mid1 != _Last1 && _Mid2 != _Last2; ++_Mid1, ++_Mid2)
        *_Mid2 = (_Elem)*_Mid1;
      return ok;
    }
  }

  virtual result do_out(_Statype&,
                        const _Elem *_First1, const _Elem *_Last1,
                        const _Elem *& _Mid1, _Byte *_First2, _Byte *_Last2,
                        _Byte *& _Mid2) const
  {       // convert [_First1, _Last1) to bytes [_First2, _Last)
    _Mid1 = _First1, _Mid2 = _First2;
    if (is_same<_Byte, _Elem>::value)
      return noconv;        // convert nothing
    else
    {       // types differ, copy one for one
      for (; _Mid1 != _Last1 && _Mid2 != _Last2; ++_Mid1, ++_Mid2)
        *_Mid2 = (_Byte)*_Mid1;
      return ok;
    }
  }

  virtual result do_unshift(_Statype&,
                            _Byte *_First2, _Byte *, _Byte *&_Mid2) const
  {       // generate bytes to return to default shift state
    _Mid2 = _First2;
    return ok;    // convert nothing
  }

  virtual int do_length(_Statype&, const _Byte *_First1,
                        const _Byte *_Last1, size_t _Count) const
  {       // return min(_Count, converted length of bytes [_First1, _Last1))
    return (int)(  _Count < (size_t)(_Last1 - _First1)
                 ? _Count
                 : _Last1 - _First1);  // assume 1-to-1 conversion
  }
};

// STATIC codecvt::id OBJECT
template<class _Elem,
         class _Byte,
         class _Statype>
locale::id codecvt<_Elem, _Byte, _Statype>::id;

// ENUM _Codecvt_mode
enum _Codecvt_mode : unsigned char {
  _Consume_header = 4,
  _Generate_header = 2
};

// CLASS codecvt<char16_t, char, _Mbstatet>
template<>
class codecvt<char16_t, char, _Mbstatet>
  : public codecvt_base
{       // facet for converting between char16_t and UTF-8 byte sequences
public:
  typedef codecvt<char16_t, char, _Mbstatet> _Mybase;
  typedef char16_t _Elem;
  typedef char _Byte;
  typedef _Mbstatet _Statype;
  typedef _Elem intern_type;
  typedef _Byte extern_type;
  typedef _Statype state_type;

  result in(_Statype& _State,
            const _Byte *_First1, const _Byte *_Last1, const _Byte *& _Mid1,
            _Elem *_First2, _Elem *_Last2, _Elem *& _Mid2) const
  {       // convert bytes [_First1, _Last1) to [_First2, _Last)
    return do_in(_State, _First1, _Last1, _Mid1, _First2, _Last2, _Mid2);
  }

  result out(_Statype& _State,
             const _Elem *_First1, const _Elem *_Last1, const _Elem *& _Mid1,
             _Byte *_First2, _Byte *_Last2, _Byte *& _Mid2) const
  {       // convert [_First1, _Last1) to bytes [_First2, _Last)
    return do_out(_State, _First1, _Last1, _Mid1, _First2, _Last2, _Mid2);
  }

  result unshift(_Statype& _State,
                 _Byte *_First2, _Byte *_Last2, _Byte *& _Mid2) const
  {       // generate bytes to return to default shift state
    return do_unshift(_State, _First2, _Last2, _Mid2);
  }

  int length(_Statype& _State, const _Byte *_First1,
             const _Byte *_Last1, size_t _Count) const
  {       // return min(_Count, converted length of bytes [_First1, _Last1))
    return do_length(_State, _First1, _Last1, _Count);
  }

  static locale::id id;

  explicit codecvt(size_t _Refs = 0)
    : codecvt_base(_Refs), _Maxcode(0x10ffff), _Mode(_Consume_header)
  {       // construct from current locale
    // Possibly lock thread
    _Locinfo _Lobj;
    _Init(_Lobj);
  }

  codecvt(const _Locinfo& _Lobj, size_t _Refs = 0)
    : codecvt_base(_Refs), _Maxcode(0x10ffff), _Mode(_Consume_header)
  {       // construct from specified locale
    _Init(_Lobj);
  }

  codecvt(const _Locinfo& _Lobj, __uint32_t _Maxcode_arg,
          _Codecvt_mode _Mode_arg, size_t _Refs = 0)
    : codecvt_base(_Refs), _Maxcode(_Maxcode_arg), _Mode(_Mode_arg)
  {       // construct from specified locale and parameters
    _Init(_Lobj);
  }

  static size_t _Getcat(const locale::facet **_Ppf = 0,
                        const locale *_Ploc = 0)
  {       // return locale category mask and construct standard facet
    if (_Ppf != 0 && *_Ppf == 0)
      *_Ppf = new codecvt<_Elem, _Byte, _Statype>(
        _Ploc->_GetLocinfo());
    return _X_CTYPE;
  }

protected:
  virtual ~codecvt() _NOEXCEPT
  {       // destroy the object
  }

  void _Init(const _Locinfo&)
  {       // initialize
  }

  virtual result do_in(_Statype& _State,
                       const _Byte *_First1, const _Byte *_Last1,
                       const _Byte *& _Mid1, _Elem *_First2, _Elem *_Last2,
                       _Elem *& _Mid2) const
  {       // convert bytes [_First1, _Last1) to [_First2, _Last2)
    unsigned short *_Pstate = (unsigned short *)&_State;
    _Mid1 = _First1;
    _Mid2 = _First2;

    for (; _Mid1 != _Last1 && _Mid2 != _Last2; )
    {       // convert a multibyte sequence
      unsigned char _By = (unsigned char)*_Mid1;
      __uint32_t _Ch;
      int _Nextra, _Nskip;

      if (*_Pstate <= 1)
        ;       // no leftover word
      else if (_By < 0x80 || 0xc0 <= _By)
        return _Mybase::error;        // not continuation byte
      else
      {       // deliver second half of two-word value
        ++_Mid1;
        *_Mid2++ = (_Elem)(*_Pstate | (_By & 0x3f));
        *_Pstate = 1;
        continue;
      }

      if (_By < 0x80)
        _Ch = _By, _Nextra = 0;
      else if (_By < 0xc0)
      {       // 0x80-0xdf not first byte
        ++_Mid1;
        return _Mybase::error;
      }
      else if (_By < 0xe0)
        _Ch = _By & 0x1f, _Nextra = 1;
      else if (_By < 0xf0)
        _Ch = _By & 0x0f, _Nextra = 2;
      else if (_By < 0xf8)
        _Ch = _By & 0x07, _Nextra = 3;
      else
        _Ch = _By & 0x03, _Nextra = _By < 0xfc ? 4 : 5;

      _Nskip = _Nextra < 3 ? 0 : 1;   // leave a byte for 2nd word
      _First1 = _Mid1;        // roll back point

      if (_Nextra == 0)
        ++_Mid1;
      else if (_Last1 - _Mid1 < _Nextra + 1 - _Nskip)
        break;  // not enough input
      else
        for (++_Mid1; _Nskip < _Nextra; --_Nextra, ++_Mid1)
          if ((_By = (unsigned char)*_Mid1) < 0x80 || 0xc0 <= _By)
            return _Mybase::error;        // not continuation byte
          else
            _Ch = _Ch << 6 | (_By & 0x3f);
      if (0 < _Nskip)
        _Ch <<= 6;      // get last byte on next call

      if ((_Maxcode < 0x10ffff ? _Maxcode : 0x10ffff) < _Ch)
        return _Mybase::error;        // value too large
      else if (0xffff < _Ch)
      {       // deliver first half of two-word value, save second word
        unsigned short _Ch0 =
          (unsigned short)(0xd800 | (_Ch >> 10) - 0x0040);

        *_Mid2++ = (_Elem)_Ch0;
        *_Pstate = (unsigned short)(0xdc00 | (_Ch & 0x03ff));
        continue;
      }

      if (_Nskip == 0)
        ;
      else if (_Mid1 == _Last1)
      {       // not enough bytes, noncanonical value
        _Mid1 = _First1;
        break;
      }
      else if ((_By = (unsigned char)*_Mid1++) < 0x80 || 0xc0 <= _By)
        return _Mybase::error;        // not continuation byte
      else
        _Ch |= _By & 0x3f;      // complete noncanonical value

      if (*_Pstate == 0)
      {       // first time, maybe look for and consume header
        *_Pstate = 1;

        if ((_Mode & _Consume_header) != 0 && _Ch == 0xfeff)
        {       // drop header and retry
          result _Ans = do_in(_State, _Mid1, _Last1, _Mid1,
                              _First2, _Last2, _Mid2);

          if (_Ans == _Mybase::partial)
          {       // roll back header determination
            *_Pstate = 0;
            _Mid1 = _First1;
          }
          return _Ans;
        }
      }

      *_Mid2++ = (_Elem)_Ch;
    }

    return _First1 == _Mid1 ? _Mybase::partial : _Mybase::ok;
  }

  virtual result do_out(_Statype& _State,
                        const _Elem *_First1, const _Elem *_Last1,
                        const _Elem *& _Mid1, _Byte *_First2, _Byte *_Last2,
                        _Byte *& _Mid2) const
  {       // convert [_First1, _Last1) to bytes [_First2, _Last)
    unsigned short *_Pstate = (unsigned short *)&_State;
    _Mid1 = _First1;
    _Mid2 = _First2;

    for (; _Mid1 != _Last1 && _Mid2 != _Last2; )
    {       // convert and put a wide char
      __uint32_t _Ch;
      unsigned short _Ch1 = (unsigned short)*_Mid1;
      bool _Save = false;

      if (1 < *_Pstate)
      {       // get saved MS 11 bits from *_Pstate
        if (_Ch1 < 0xdc00 || 0xe000 <= _Ch1)
          return _Mybase::error;        // bad second word
        _Ch = (*_Pstate << 10) | (_Ch1 - 0xdc00);
      }
      else if (0xd800 <= _Ch1 && _Ch1 < 0xdc00)
      {       // get new first word
        _Ch = (_Ch1 - 0xd800 + 0x0040) << 10;
        _Save = true;   // put only first byte, rest with second word
      }
      else
        _Ch = _Ch1;     // not first word, just put it

      _Byte _By;
      int _Nextra;

      if (_Ch < 0x0080)
        _By = (_Byte)_Ch, _Nextra = 0;
      else if (_Ch < 0x0800)
        _By = (_Byte)(0xc0 | _Ch >> 6), _Nextra = 1;
      else if (_Ch < 0x10000)
        _By = (_Byte)(0xe0 | _Ch >> 12), _Nextra = 2;
      else
        _By = (_Byte)(0xf0 | _Ch >> 18), _Nextra = 3;

      int _Nput = _Nextra < 3 ? _Nextra + 1 : _Save ? 1 : 3;

      if (_Last2 - _Mid2 < _Nput)
        break;  // not enough room, even without header
      else if (*_Pstate != 0 || (_Mode & _Generate_header) == 0)
        ;       // no header to put
      else if (_Last2 - _Mid2 < 3 + _Nput)
        break;  // not enough room for header + output
      else
      {       // prepend header
        *_Mid2++ = (_Byte)(unsigned char)0xef;
        *_Mid2++ = (_Byte)(unsigned char)0xbb;
        *_Mid2++ = (_Byte)(unsigned char)0xbf;
      }

      ++_Mid1;
      if (_Save || _Nextra < 3)
      {       // put first byte of sequence, if not already put
        *_Mid2++ = _By;
        --_Nput;
      }
      for (; 0 < _Nput; --_Nput)
        *_Mid2++ = (_Byte)((_Ch >> 6 * --_Nextra & 0x3f) | 0x80);

      *_Pstate = (unsigned short)(_Save ? _Ch >> 10 : 1);
    }

    return _First1 == _Mid1 ? _Mybase::partial : _Mybase::ok;
  }

  virtual result do_unshift(_Statype& _State,
                            _Byte *_First2, _Byte *, _Byte *& _Mid2) const
  {       // generate bytes to return to default shift state
    unsigned short *_Pstate = (unsigned short *)&_State;
    _Mid2 = _First2;

    return (1 < *_Pstate ? _Mybase::error : _Mybase::ok);
                                                 // fail if trailing first word
  }

  virtual int do_length(_Statype& _State, const _Byte *_First1,
                        const _Byte *_Last1, size_t _Count) const
  {       // return min(_Count, converted length of bytes [_First1, _Last1))
    size_t _Wchars = 0;
    _Statype _Mystate = _State;

    for (; _Wchars < _Count && _First1 != _Last1; )
    {       // convert another wide character
      const _Byte *_Mid1;
      _Elem *_Mid2;
      _Elem _Ch;

      switch (do_in(_Mystate, _First1, _Last1, _Mid1,
                    &_Ch, &_Ch + 1, _Mid2))
      {       // test result of single wide-char conversion
      case _Mybase::noconv:
        return (int)(_Wchars + (_Last1 - _First1));

      case _Mybase::ok:
        if (_Mid2 == &_Ch + 1)
          ++_Wchars;      // replacement do_in might not convert one
        _First1 = _Mid1;
        break;

      default:
        return (int)_Wchars;  // error or partial
      }
    }

    return (int)_Wchars;
  }

  virtual bool do_always_noconv() const _THROW0()
  {       // return true if conversions never change input
    return false;
  }

  virtual int do_max_length() const _THROW0()
  {       // return maximum length required for a conversion
    return   (_Mode & _Consume_header) != 0
           ? 9 : (_Mode & _Generate_header) != 0    // header + max input
           ? 7                                      // header + max output
           : 6;             // 6-byte max input sequence, no 3-byte header
  }

  virtual int do_encoding() const _THROW0()
  {       // return length of code sequence (from codecvt)
    return 0;     // 0 => varying length
  }

private:
  __uint32_t    _Maxcode; // default: 0x10ffff
  _Codecvt_mode _Mode;    // default: _Consume_header
};

// CLASS codecvt<char32_t, char, _Mbstatet>
template<>
class codecvt<char32_t, char, _Mbstatet>
  : public codecvt_base
{       // facet for converting between char32_t and UTF-8 byte sequences
public:
  typedef codecvt<char32_t, char, _Mbstatet> _Mybase;
  typedef char32_t _Elem;
  typedef char _Byte;
  typedef _Mbstatet _Statype;
  typedef _Elem intern_type;
  typedef _Byte extern_type;
  typedef _Statype state_type;

  result in(_Statype& _State,
            const _Byte *_First1, const _Byte *_Last1, const _Byte *& _Mid1,
            _Elem *_First2, _Elem *_Last2, _Elem *& _Mid2) const
  {       // convert bytes [_First1, _Last1) to [_First2, _Last)
    return do_in(_State, _First1, _Last1, _Mid1, _First2, _Last2, _Mid2);
  }

  result out(_Statype& _State,
             const _Elem *_First1, const _Elem *_Last1, const _Elem *& _Mid1,
             _Byte *_First2, _Byte *_Last2, _Byte *& _Mid2) const
  {       // convert [_First1, _Last1) to bytes [_First2, _Last)
    return do_out(_State, _First1, _Last1, _Mid1, _First2, _Last2, _Mid2);
  }

  result unshift(_Statype& _State,
                 _Byte *_First2, _Byte *_Last2, _Byte *& _Mid2) const
  {       // generate bytes to return to default shift state
    return do_unshift(_State, _First2, _Last2, _Mid2);
  }

  int length(_Statype& _State, const _Byte *_First1,
             const _Byte *_Last1, size_t _Count) const
  {       // return min(_Count, converted length of bytes [_First1, _Last1))
    return do_length(_State, _First1, _Last1, _Count);
  }

  static locale::id id;

  explicit codecvt(size_t _Refs = 0)
    : codecvt_base(_Refs), _Maxcode(0xffffffff), _Mode(_Consume_header)
  {       // construct from current locale
    // Possibly lock thread
    _Locinfo _Lobj;
    _Init(_Lobj);
  }

  codecvt(const _Locinfo& _Lobj, size_t _Refs = 0)
    : codecvt_base(_Refs), _Maxcode(0xffffffff), _Mode(_Consume_header)
  {       // construct from specified locale
    _Init(_Lobj);
  }

  codecvt(const _Locinfo& _Lobj, __uint32_t _Maxcode_arg,
          _Codecvt_mode _Mode_arg, size_t _Refs = 0)
    : codecvt_base(_Refs), _Maxcode(_Maxcode_arg), _Mode(_Mode_arg)
  {       // construct from specified locale and parameters
    _Init(_Lobj);
  }

  static size_t _Getcat(const locale::facet **_Ppf = 0,
                        const locale *_Ploc = 0)
  {       // return locale category mask and construct standard facet
    if (_Ppf != 0 && *_Ppf == 0)
      *_Ppf = new codecvt<_Elem, _Byte, _Statype>(
        _Ploc->_GetLocinfo());
    return _X_CTYPE;
  }

protected:
  virtual ~codecvt() _NOEXCEPT
  {       // destroy the object
  }

  void _Init(const _Locinfo&)
  {       // initialize
  }

  virtual result do_in(_Statype& _State,
                       const _Byte *_First1, const _Byte *_Last1,
                       const _Byte *& _Mid1, _Elem *_First2, _Elem *_Last2,
                       _Elem *& _Mid2) const
  {       // convert bytes [_First1, _Last1) to [_First2, _Last)
    char *_Pstate = (char *)&_State;
    _Mid1 = _First1;
    _Mid2 = _First2;

    for (; _Mid1 != _Last1 && _Mid2 != _Last2; )
    {       // convert a multibyte sequence
      unsigned char _By = (unsigned char)*_Mid1;
      __uint32_t _Ch;
      int _Nextra;

      if (_By < 0x80)
        _Ch = _By, _Nextra = 0;
      else if (_By < 0xc0)
      {       // 0x80-0xdf not first byte
        ++_Mid1;
        return _Mybase::error;
      }
      else if (_By < 0xe0)
        _Ch = _By & 0x1f, _Nextra = 1;
      else if (_By < 0xf0)
        _Ch = _By & 0x0f, _Nextra = 2;
      else if (_By < 0xf8)
        _Ch = _By & 0x07, _Nextra = 3;
      else
        _Ch = _By & 0x03, _Nextra = _By < 0xfc ? 4 : 5;

      if (_Nextra == 0)
        ++_Mid1;
      else if (_Last1 - _Mid1 < _Nextra + 1)
        break;  // not enough input
      else
        for (++_Mid1; 0 < _Nextra; --_Nextra, ++_Mid1)
          if ((_By = (unsigned char)*_Mid1) < 0x80 || 0xc0 <= _By)
            return _Mybase::error;        // not continuation byte
          else
            _Ch = _Ch << 6 | (_By & 0x3f);

      if (*_Pstate == 0)
      {       // first time, maybe look for and consume header
        *_Pstate = 1;

        if ((_Mode & _Consume_header) != 0 && _Ch == 0xfeff)
        {       // drop header and retry
          result _Ans = do_in(_State, _Mid1, _Last1, _Mid1,
                              _First2, _Last2, _Mid2);

          if (_Ans == _Mybase::partial)
          {       // roll back header determination
            *_Pstate = 0;
            _Mid1 = _First1;
          }
          return _Ans;
        }
      }

      if (_Maxcode < _Ch)
        return _Mybase::error;        // code too large
      *_Mid2++ = (_Elem)_Ch;
    }

    return _First1 == _Mid1 ? _Mybase::partial : _Mybase::ok;
  }

  virtual result do_out(_Statype& _State,
                        const _Elem *_First1, const _Elem *_Last1,
                        const _Elem *& _Mid1, _Byte *_First2, _Byte *_Last2,
                        _Byte *& _Mid2) const
  {       // convert [_First1, _Last1) to bytes [_First2, _Last)
    char *_Pstate = (char *)&_State;
    _Mid1 = _First1;
    _Mid2 = _First2;

    for (; _Mid1 != _Last1 && _Mid2 != _Last2; )
    {       // convert and put a wide char
      _Byte _By;
      int _Nextra;
      __uint32_t _Ch = (unsigned long)*_Mid1;

      if (_Maxcode < _Ch)
        return _Mybase::error;

      if (_Ch < 0x0080)
        _By = (_Byte)_Ch, _Nextra = 0;
      else if (_Ch < 0x0800)
        _By = (_Byte)(0xc0 | _Ch >> 6), _Nextra = 1;
      else if (_Ch < 0x00010000)
        _By = (_Byte)(0xe0 | _Ch >> 12), _Nextra = 2;
      else if (_Ch < 0x00200000)
        _By = (_Byte)(0xf0 | _Ch >> 18), _Nextra = 3;
      else if (_Ch < 0x04000000)
        _By = (_Byte)(0xf8 | _Ch >> 24), _Nextra = 4;
      else
        _By = (_Byte)(0xfc | (_Ch >> 30 & 0x03)), _Nextra = 5;

      if (*_Pstate == 0)
      {       // first time, maybe generate header
        *_Pstate = 1;
        if ((_Mode & _Generate_header) == 0)
          ;
        else if (_Last2 - _Mid2 < 3 + 1 + _Nextra)
          return _Mybase::partial;      // not enough room for both
        else
        {       // prepend header
          *_Mid2++ = (_Byte)(unsigned char)0xef;
          *_Mid2++ = (_Byte)(unsigned char)0xbb;
          *_Mid2++ = (_Byte)(unsigned char)0xbf;
        }
      }

      if (_Last2 - _Mid2 < 1 + _Nextra)
        break;  // not enough room for output

      ++_Mid1;
      for (*_Mid2++ = _By; 0 < _Nextra; )
        *_Mid2++ = (_Byte)((_Ch >> 6 * --_Nextra & 0x3f) | 0x80);
    }
    return _First1 == _Mid1 ? _Mybase::partial : _Mybase::ok;
  }

  virtual result do_unshift(_Statype&,
                            _Byte *_First2, _Byte *, _Byte *& _Mid2) const
  {       // generate bytes to return to default shift state
    _Mid2 = _First2;
    return (_Mybase::ok);
  }

  virtual int do_length(_Statype& _State, const _Byte *_First1,
                        const _Byte *_Last1, size_t _Count) const _THROW0()
  {       // return min(_Count, converted length of bytes [_First1, _Last1))
    int _Wchars = 0;
    _Statype _Mystate = _State;

    for (; (size_t)_Wchars < _Count && _First1 != _Last1; )
    {       // convert another wide character
      const _Byte *_Mid1;
      _Elem *_Mid2;
      _Elem _Ch;

      switch (do_in(_Mystate, _First1, _Last1, _Mid1,
                    &_Ch, &_Ch + 1, _Mid2))
      {       // test result of single wide-char conversion
      case _Mybase::noconv:
        return (int)(_Wchars + (int)(_Last1 - _First1));

      case _Mybase::ok:
        if (_Mid2 == &_Ch + 1)
          ++_Wchars;      // replacement do_in might not convert one
        _First1 = _Mid1;
        break;

      default:
        return (int)_Wchars;  // error or partial
      }
    }

    return (int)_Wchars;
  }

  virtual bool do_always_noconv() const _THROW0()
  {       // return true if conversions never change input
    return false;
  }

  virtual int do_max_length() const _THROW0()
  {       // return maximum length required for a conversion
    return (_Mode & (_Consume_header | _Generate_header)) != 0 ? 9 : 6;
  }

  virtual int do_encoding() const _THROW0()
  {       // return length of code sequence (from codecvt)
    return (_Mode & (_Consume_header | _Generate_header)) != 0 ? -1 : 0;
                                 // -1 => state dependent, 0 => varying length
  }

private:
  __uint32_t    _Maxcode; // default: 0xffffffff
  _Codecvt_mode _Mode;    // default: _Consume_header
};

// CLASS codecvt<wchar_t, char, _Mbstatet>
template<>
class codecvt<wchar_t, char, _Mbstatet>
  : public codecvt_base
{       // facet for converting between wchar_t and char (_Byte) sequences
public:
  typedef wchar_t _Elem;
  typedef char _Byte;
  typedef _Mbstatet _Statype;
  typedef _Elem intern_type;
  typedef _Byte extern_type;
  typedef _Statype state_type;

  result in(_Statype& _State,
            const _Byte *_First1, const _Byte *_Last1, const _Byte *& _Mid1,
            _Elem *_First2, _Elem *_Last2, _Elem *& _Mid2) const
  {       // convert bytes [_First1, _Last1) to [_First2, _Last)
    return do_in(_State, _First1, _Last1, _Mid1, _First2, _Last2, _Mid2);
  }

  result out(_Statype& _State,
             const _Elem *_First1, const _Elem *_Last1, const _Elem *& _Mid1,
             _Byte *_First2, _Byte *_Last2, _Byte *& _Mid2) const
  {       // convert [_First1, _Last1) to bytes [_First2, _Last)
    return do_out(_State, _First1, _Last1, _Mid1, _First2, _Last2, _Mid2);
  }

  result unshift(_Statype& _State,
                 _Byte *_First2, _Byte *_Last2, _Byte *& _Mid2) const
  {       // generate bytes to return to default shift state
    return do_unshift(_State, _First2, _Last2, _Mid2);
  }

  int length(_Statype& _State, const _Byte *_First1,
             const _Byte *_Last1, size_t _Count) const
  {       // return min(_Count, converted length of bytes [_First1, _Last1))
    return do_length(_State, _First1, _Last1, _Count);
  }

  static locale::id id;

  explicit codecvt(size_t _Refs = 0)
    : codecvt_base(_Refs)
  {       // construct from current locale
    // Possibly lock thread
    _Locinfo _Lobj;
    _Init(_Lobj);
  }

  codecvt(const _Locinfo& _Lobj, size_t _Refs = 0)
    : codecvt_base(_Refs)
  {       // construct from specified locale
    _Init(_Lobj);
  }

  static size_t _Getcat(const locale::facet **_Ppf = 0,
                        const locale *_Ploc = 0)
  {       // return locale category mask and construct standard facet
    if (_Ppf != 0 && *_Ppf == 0)
      *_Ppf = new codecvt<_Elem, _Byte, _Statype>(
        _Ploc->_GetLocinfo());
    return _X_CTYPE;
  }

protected:
  virtual ~codecvt() _NOEXCEPT
  {       // destroy the object
  }

  void _Init(const _Locinfo& _Lobj)
  {       // initialize from _Lobj
    _Cvt = _Lobj._Getcvt();
  }

  virtual result do_in(_Statype& _State,
                       const _Byte *_First1, const _Byte *_Last1,
                       const _Byte *& _Mid1, _Elem *_First2, _Elem *_Last2,
                       _Elem *& _Mid2) const
  {       // convert bytes [_First1, _Last1) to [_First2, _Last)
    _DEBUG_RANGE(_First1, _Last1);
    _DEBUG_RANGE(_First2, _Last2);
    _Mid1 = _First1, _Mid2 = _First2;
    result _Ans = _Mid1 == _Last1 ? ok : partial;
    int _Bytes;

    while (_Mid1 != _Last1 && _Mid2 != _Last2)
      switch (_Bytes = _Mbrtowc(_Mid2, _Mid1, _Last1 - _Mid1,
                                &_State, &_Cvt))
      {       // test result of locale-specific mbrtowc call
      case -2:        // partial conversion
        _Mid1 = _Last1;
        return _Ans;

      case -1:        // failed conversion
        return error;

      case 0: // may have converted null character
        if (*_Mid2 == (_Elem)0)
          _Bytes = (int)::strlen(_Mid1) + 1;
        // fall through

      default:        // converted _Bytes bytes to a wchar_t
        if (_Bytes == -3)
          _Bytes = 0;     // wchar_t generated from state info
        _Mid1 += _Bytes;
        ++_Mid2;
        _Ans = ok;
      }
    return _Ans;
  }

  virtual result do_out(_Statype& _State,
                        const _Elem *_First1, const _Elem *_Last1,
                        const _Elem *& _Mid1, _Byte *_First2, _Byte *_Last2,
                        _Byte *& _Mid2) const
  {       // convert [_First1, _Last1) to bytes [_First2, _Last)
    _DEBUG_RANGE(_First1, _Last1);
    _DEBUG_RANGE(_First2, _Last2);
    _Mid1 = _First1, _Mid2 = _First2;
    int _Bytes;

    while (_Mid1 != _Last1 && _Mid2 != _Last2)
      if ((int)MB_LEN_MAX <= _Last2 - _Mid2)
        if ((_Bytes = _Wcrtomb(_Mid2, *_Mid1,
                               &_State, &_Cvt)) < 0)
          return error; // locale-specific wcrtomb failed
        else
          ++_Mid1, _Mid2 += _Bytes;
      else
      {       // destination too small, convert into buffer
        _Byte _Buf[MB_LEN_MAX];
        _Statype _Stsave = _State;

        if ((_Bytes = _Wcrtomb(_Buf, *_Mid1,
                               &_State, &_Cvt)) < 0)
          return error; // locale-specific wcrtomb failed
        else if (_Last2 - _Mid2 < _Bytes)
        {       // converted too many, roll back and return previous
          _State = _Stsave;
          break;
        }
        else
        {       // copy converted bytes from buffer
          ::memcpy(_Mid2, _Buf, _Bytes);
          ++_Mid1, _Mid2 += _Bytes;
        }
      }
    return _Mid1 == _Last1 ? ok : partial;
  }

  virtual result do_unshift(_Statype& _State,
                            _Byte *_First2, _Byte *_Last2, _Byte *& _Mid2) const
  {       // generate bytes to return to default shift state
    _DEBUG_RANGE(_First2, _Last2);
    _Mid2 = _First2;
    result _Ans = ok;
    int _Bytes;
    _Byte _Buf[MB_LEN_MAX];
    _Statype _Stsave = _State;

    if ((_Bytes = _Wcrtomb(_Buf, L'\0', &_State, &_Cvt)) <= 0)
      _Ans = error;   // locale-specific wcrtomb failed
    else if (_Last2 - _Mid2 < --_Bytes)
    {       // converted too many, roll back and return
      _State = _Stsave;
      _Ans = partial;
    }
    else if (0 < _Bytes)
    {       // copy converted bytes from buffer
      ::memcpy(_Mid2, _Buf, _Bytes);
      _Mid2 += _Bytes;
    }
    return _Ans;
  }

  virtual int do_length(_Statype& _State, const _Byte *_First1,
                        const _Byte *_Last1, size_t _Count) const
  {       // return min(_Count, converted length of bytes [_First1, _Last1))
    _DEBUG_RANGE(_First1, _Last1);
    int _Wchars;
    const _Byte *_Mid1;
    _Statype _Mystate = _State;

    for (_Wchars = 0, _Mid1 = _First1;
         (size_t)_Wchars < _Count && _Mid1 != _Last1; )
    {       // convert another wchar_t
      int _Bytes;
      _Elem _Ch;

      switch (_Bytes = _Mbrtowc(&_Ch, _Mid1, _Last1 - _Mid1,
                                &_Mystate, &_Cvt))
      {       // test result of locale-specific mbrtowc call
      case -2:        // partial conversion
        return _Wchars;

      case -1:        // failed conversion
        return _Wchars;

      case 0: // may have converted null character
        if (_Ch == (_Elem)0)
          _Bytes = (int)::strlen(_Mid1) + 1;
        // fall through

      default:        // converted _Bytes bytes to a wchar_t
        if (_Bytes == -3)
          _Bytes = 0;     // wchar_t generated from state info
        _Mid1 += _Bytes;
        ++_Wchars;
      }
    }
    return _Wchars;
  }

  virtual bool do_always_noconv() const _THROW0()
  {       // return true if conversions never change input
    return false;
  }

  virtual int do_max_length() const _THROW0()
  {       // return maximum length required for a conversion (from codecvt)
    return MB_LEN_MAX;
  }

  virtual int do_encoding() const _THROW0()
  {       // return length of code sequence (from codecvt)
    return 0;     // 0 => varying length
  }

private:
  _Locinfo::_Cvtvec _Cvt; // locale info passed to _Mbrtowc, _Wcrtomb
};

// TEMPLATE CLASS codecvt_byname
template<class _Elem,
         class _Byte,
         class _Statype>
class codecvt_byname
  : public codecvt<_Elem, _Byte, _Statype>
{       // codecvt for named locale
public:
  explicit codecvt_byname(const char *_Locname, size_t _Refs = 0)
    : codecvt<_Elem, _Byte, _Statype>(_Locinfo(_Locname), _Refs)
  {       // construct for named locale
  }

  explicit codecvt_byname(const string& _Str, size_t _Refs = 0)
    : codecvt<_Elem, _Byte, _Statype>(_Locinfo(_Str.c_str()), _Refs)
  {       // construct for named locale
  }

protected:
  virtual ~codecvt_byname() _NOEXCEPT
  {       // destroy the object
  }
};


// STRUCT ctype_base
struct ctype_base
  : public locale::facet
{       // base for ctype
  typedef _CtypeMask mask;

  // constants for character classifications
  static const mask space  = _ctype_space;
  static const mask print  = _ctype_print;
  static const mask cntrl  = _ctype_cntrl;
  static const mask upper  = _ctype_upper;
  static const mask lower  = _ctype_lower;
  static const mask alpha  = _ctype_alpha;
  static const mask digit  = _ctype_digit;
  static const mask punct  = _ctype_punct;
  static const mask xdigit = _ctype_xdigit;
  static const mask blank  = _ctype_blank;
  static const mask alnum  = alpha | digit;
  static const mask graph  = alnum | punct;

  ctype_base(size_t _Refs = 0)
    : locale::facet(_Refs)
  {       // default constructor
  }

  ~ctype_base() _NOEXCEPT
  {       // destroy the object
  }
};

// TEMPLATE CLASS ctype
template<class _Elem>
class ctype
  : public ctype_base
{       // facet for classifying elements, converting cases
public:
  typedef _Elem char_type;

  bool is(mask _Maskval, _Elem _Ch) const
  {       // test if element fits any mask classifications
    return do_is(_Maskval, _Ch);
  }

  const _Elem *is(const _Elem *_First, const _Elem *_Last,
                  mask *_Dest) const
  {       // get mask sequence for elements in [_First, _Last)
    return do_is(_First, _Last, _Dest);
  }

  const _Elem *scan_is(mask _Maskval, const _Elem *_First,
                       const _Elem *_Last) const
  {       // find first in [_First, _Last) that fits mask classification
    return do_scan_is(_Maskval, _First, _Last);
  }

  const _Elem *scan_not(mask _Maskval, const _Elem *_First,
                        const _Elem *_Last) const
  {       // find first in [_First, _Last) not fitting mask classification
    return do_scan_not(_Maskval, _First, _Last);
  }

  _Elem tolower(_Elem _Ch) const
  {       // convert element to lower case
    return do_tolower(_Ch);
  }

  const _Elem *tolower(_Elem *_First, const _Elem *_Last) const
  {       // convert [_First, _Last) in place to lower case
    return do_tolower(_First, _Last);
  }

  _Elem toupper(_Elem _Ch) const
  {       // convert element to upper case
    return do_toupper(_Ch);
  }

  const _Elem *toupper(_Elem *_First, const _Elem *_Last) const
  {       // convert [_First, _Last) in place to upper case
    return do_toupper(_First, _Last);
  }

  _Elem widen(char _Byte) const
  {       // widen char
    return do_widen(_Byte);
  }

  const char *widen(const char *_First, const char *_Last,
                    _Elem *_Dest) const
  {       // widen chars in [_First, _Last)
    return do_widen(_First, _Last, _Dest);
  }

  char narrow(_Elem _Ch, char _Dflt = '\0') const
  {       // narrow element to char
    return do_narrow(_Ch, _Dflt);
  }

  const _Elem *narrow(const _Elem *_First, const _Elem *_Last,
                      char _Dflt, char *_Dest) const
  {       // narrow elements in [_First, _Last) to chars
    return do_narrow(_First, _Last, _Dflt, _Dest);
  }

  static locale::id id;

  explicit ctype(size_t _Refs = 0)
    : ctype_base(_Refs)
  {       // construct from current locale
    // Possibly lock thread
    _Locinfo _Lobj;
    _Init(_Lobj);
  }

  ctype(const _Locinfo& _Lobj, size_t _Refs = 0)
    : ctype_base(_Refs)
  {       // construct from specified locale
    _Init(_Lobj);
  }

  static size_t _Getcat(const locale::facet **_Ppf = 0,
                        const locale *_Ploc = 0)
  {       // return locale category mask and construct standard facet
    if (_Ppf != 0 && *_Ppf == 0)
      *_Ppf = new ctype<_Elem>(
        _Ploc->_GetLocinfo());
    return _X_CTYPE;
  }

protected:
  virtual ~ctype() _NOEXCEPT
  {       // destroy the object
    if (_Ctype._Delfl)
      ::free((void *)_Ctype._Table);
  }

  void _Init(const _Locinfo& _Lobj)
  {       // initialize from _Lobj
    _Ctype = _Lobj._Getctype();
    _Cvt = _Lobj._Getcvt();
  }

  virtual bool do_is(mask _Maskval, _Elem _Ch) const
  {       // test if element fits any mask classifications
    return (_Ctype._Table[(unsigned char)narrow(_Ch)] & _Maskval) != 0;
  }

  virtual const _Elem *do_is(const _Elem *_First, const _Elem *_Last,
                             mask *_Dest) const
  {       // get mask sequence for elements in [_First, _Last)
    _DEBUG_RANGE_PTR(_First, _Last, _Dest);
    for (; _First != _Last; ++_First, ++_Dest)
      *_Dest = _Ctype._Table[(unsigned char)narrow(*_First)];
    return _First;
  }

  virtual const _Elem *do_scan_is(mask _Maskval,
                                  const _Elem *_First, const _Elem *_Last) const
  {       // find first in [_First, _Last) that fits mask classification
    _DEBUG_RANGE(_First, _Last);
    for (; _First != _Last && !is(_Maskval, *_First); ++_First)
      ;
    return _First;
  }

  virtual const _Elem *do_scan_not(mask _Maskval,
                                  const _Elem *_First, const _Elem *_Last) const
  {       // find first in [_First, _Last) not fitting mask classification
    _DEBUG_RANGE(_First, _Last);
    for (; _First != _Last && is(_Maskval, *_First); ++_First)
      ;
    return _First;
  }

  virtual _Elem do_tolower(_Elem _Ch) const
  {       // convert element to lower case
    unsigned char _Byte = (unsigned char)narrow(_Ch, '\0');
    if (_Byte == '\0')
      return _Ch;
    else
      return widen((char)_Tolower(_Byte, &_Ctype));
  }

  virtual const _Elem *do_tolower(_Elem *_First,
                                  const _Elem *_Last) const
  {       // convert [_First, _Last) in place to lower case
    _DEBUG_RANGE((const _Elem *)_First, _Last);
    for (; _First != _Last; ++_First)
    {       // convert *_First to lower case
      unsigned char _Byte = (unsigned char)narrow(*_First, '\0');
      if (_Byte != '\0')
        *_First = (widen((char)_Tolower(_Byte, &_Ctype)));
    }
    return (const _Elem *)_First;
  }

  virtual _Elem do_toupper(_Elem _Ch) const
  {       // convert element to upper case
    unsigned char _Byte = (unsigned char)narrow(_Ch, '\0');
    if (_Byte == '\0')
      return _Ch;
    else
      return widen((char)_Toupper(_Byte, &_Ctype));
  }

  virtual const _Elem *do_toupper(_Elem *_First,
                                  const _Elem *_Last) const
  {       // convert [_First, _Last) in place to upper case
    _DEBUG_RANGE((const _Elem *)_First, _Last);
    for (; _First != _Last; ++_First)
    {       // convert *_First to upper case
      unsigned char _Byte = (unsigned char)narrow(*_First, '\0');
      if (_Byte != '\0')
        *_First = (widen((char)_Toupper(_Byte, &_Ctype)));
    }
    return (const _Elem *)_First;
  }

  virtual _Elem do_widen(char _Byte) const
  {       // widen char
    return _MAKLOCCHR(_Elem, _Byte, _Cvt);
  }

  virtual const char *do_widen(const char *_First,
                               const char *_Last, _Elem *_Dest) const
  {       // widen chars in [_First, _Last)
    _DEBUG_RANGE_PTR(_First, _Last, _Dest);
    for (; _First != _Last; ++_First, ++_Dest)
      *_Dest = _MAKLOCCHR(_Elem, *_First, _Cvt);
    return _First;
  }

  char _Donarrow(_Elem _Ch, char _Dflt) const
  {       // narrow element to char
    char _Byte;
    if (_Ch == (_Elem)0)
      return '\0';
    else if ((_Byte = _MAKLOCBYTE(_Elem, _Ch, _Cvt)) == '\0')
      return _Dflt;
    else
      return _Byte;
  }

  virtual char do_narrow(_Elem _Ch, char _Dflt) const
  {       // narrow element to char
    return _Donarrow(_Ch, _Dflt);
  }

  virtual const _Elem *do_narrow(const _Elem *_First,
                              const _Elem *_Last, char _Dflt, char *_Dest) const
  {       // narrow elements in [_First, _Last) to chars
    _DEBUG_RANGE_PTR(_First, _Last, _Dest);
    for (; _First != _Last; ++_First, ++_Dest)
      *_Dest = _Donarrow(*_First, _Dflt);
    return _First;
  }

private:
  _Locinfo::_Ctypevec _Ctype;     // locale info passed to _Tolower, etc.
  _Locinfo::_Cvtvec _Cvt;         // conversion information
};

// STATIC ctype::id OBJECT
template<class _Elem>
locale::id ctype<_Elem>::id;

// CLASS ctype<char>
template<>
class ctype<char>
  : public ctype_base
{       // facet for classifying char elements, converting cases
  typedef ctype<char> _Myt;

public:
  typedef char _Elem;
  typedef _Elem char_type;

  bool is(mask _Maskval, _Elem _Ch) const
  {       // test if element fits any mask classifications
    return (_Ctype._Table[(unsigned char)_Ch] & _Maskval) != 0;
  }

  const _Elem *is(const _Elem *_First,
                  const _Elem *_Last, mask *_Dest) const
  {       // get mask sequence for elements in [_First, _Last)
    _DEBUG_RANGE_PTR(_First, _Last, _Dest);
    for (; _First != _Last; ++_First, ++_Dest)
      *_Dest = _Ctype._Table[(unsigned char)*_First];
    return _First;
  }

  const _Elem *scan_is(mask _Maskval,
                       const _Elem *_First, const _Elem *_Last) const
  {       // find first in [_First, _Last) that fits mask classification
    _DEBUG_RANGE(_First, _Last);
    for (; _First != _Last && !is(_Maskval, *_First); ++_First)
      ;
    return _First;
  }

  const _Elem *scan_not(mask _Maskval,
                        const _Elem *_First, const _Elem *_Last) const
  {       // find first in [_First, _Last) not fitting mask classification
    _DEBUG_RANGE(_First, _Last);
    for (; _First != _Last && is(_Maskval, *_First); ++_First)
      ;
    return _First;
  }

  _Elem tolower(_Elem _Ch) const
  {       // convert element to lower case
    return do_tolower(_Ch);
  }

  const _Elem *tolower(_Elem *_First, const _Elem *_Last) const
  {       // convert [_First, _Last) in place to lower case
    return do_tolower(_First, _Last);
  }

  _Elem toupper(_Elem _Ch) const
  {       // convert element to upper case
    return do_toupper(_Ch);
  }

  const _Elem *toupper(_Elem *_First, const _Elem *_Last) const
  {       // convert [_First, _Last) in place to upper case
    return do_toupper(_First, _Last);
  }

  _Elem widen(char _Byte) const
  {       // widen char
    return do_widen(_Byte);
  }

  const _Elem *widen(const char *_First, const char *_Last,
                     _Elem *_Dest) const
  {       // widen chars in [_First, _Last)
    return do_widen(_First, _Last, _Dest);
  }

  _Elem narrow(_Elem _Ch, char _Dflt = '\0') const
  {       // narrow element to char
    return do_narrow(_Ch, _Dflt);
  }

  const _Elem *narrow(const _Elem *_First, const _Elem *_Last,
                      char _Dflt, char *_Dest) const
  {       // narrow elements in [_First, _Last) to chars
    return do_narrow(_First, _Last, _Dflt, _Dest);
  }

  static locale::id id;

  explicit ctype(const mask *_Table = 0,
                 bool _Deletetable = false,
                 size_t _Refs = 0)
    : ctype_base(_Refs)
  {       // construct with specified table and delete flag for table
      // Possibly lock thread
     _Locinfo _Lobj;
     _Init(_Lobj);

    _Tidy();        // free existing table, as needed
    if (_Table != 0)
    {       // replace existing char to mask table
      _Ctype._Table = _Table;
      _Ctype._Delfl = _Deletetable ? -1 : 0;
    }
    else
    {       // use classic table
      _Ctype._Table = classic_table();
      _Ctype._Delfl = 0;
    }
  }

  ctype(const _Locinfo& _Lobj, size_t _Refs = 0)
    : ctype_base(_Refs)
  {       // construct from current locale
    _Init(_Lobj);
  }

  static size_t _Getcat(const locale::facet **_Ppf = 0,
                        const locale *_Ploc = 0)
  {       // return locale category mask and construct standard facet
    if (_Ppf != 0 && *_Ppf == 0)
      *_Ppf = new ctype<_Elem>(
        _Ploc->_GetLocinfo());
    return _X_CTYPE;
  }

  const mask *table() const _THROW0()
  {       // return address of char to mask table
    return _Ctype._Table != 0 ? _Ctype._Table : _Ctype.LocaleTable();
  }

  static const mask *classic_table() _THROW0()
  {       // return address of char to mask table for "C" locale
    const _Myt& _Ctype_fac = use_facet< _Myt >(locale::classic());
    return _Ctype_fac.table();
  }

  static const size_t table_size; // size of _Ctype._Table

protected:
  virtual ~ctype() _NOEXCEPT
  {       // destroy the object
    _Tidy();
  }

  void _Init(const _Locinfo& _Lobj)
  {       // initialize from _Lobj
    _Ctype = _Lobj._Getctype();
  }

  void _Tidy()
  {       // free any allocated storage
    if (0 < _Ctype._Delfl)
      ::free((void *)_Ctype._Table);
    else if (_Ctype._Delfl < 0)
      delete[] _Ctype._Table;
  }

  virtual _Elem do_tolower(_Elem _Ch) const
  {       // convert element to lower case
    return (_Elem)_Tolower((unsigned char)_Ch, &_Ctype);
  }

  virtual const _Elem *do_tolower(_Elem *_First,
                                  const _Elem *_Last) const
  {       // convert [_First, _Last) in place to lower case
    _DEBUG_RANGE((const _Elem *)_First, _Last);
    for (; _First != _Last; ++_First)
      *_First = (_Elem)_Tolower((unsigned char)*_First, &_Ctype);
    return (const _Elem *)_First;
  }

  virtual _Elem do_toupper(_Elem _Ch) const
  {       // convert element to upper case
    return (_Elem)_Toupper((unsigned char)_Ch, &_Ctype);
  }

  virtual const _Elem *do_toupper(_Elem *_First,
                                  const _Elem *_Last) const
  {       // convert [_First, _Last) in place to upper case
    _DEBUG_RANGE((const _Elem *)_First, _Last);
    for (; _First != _Last; ++_First)
      *_First = (_Elem)_Toupper((unsigned char)*_First, &_Ctype);
    return (const _Elem *)_First;
  }

  virtual _Elem do_widen(char _Byte) const
  {       // widen char
    return _Byte;
  }

  virtual const _Elem *do_widen(const char *_First,
                                const char *_Last, _Elem *_Dest) const
  {       // widen chars in [_First, _Last)
    _DEBUG_RANGE_PTR(_First, _Last, _Dest);
    ::memcpy(_Dest, _First, _Last - _First);
    return _Last;
  }

  virtual _Elem do_narrow(_Elem _Ch, char) const
  {       // narrow char
    return _Ch;
  }

  virtual const _Elem *do_narrow(const _Elem *_First,
                                 const _Elem *_Last, char, char *_Dest) const
  {       // narrow elements in [_First, _Last) to chars
    _DEBUG_RANGE_PTR(_First, _Last, _Dest);
    ::memcpy(_Dest, _First, _Last - _First);
    return _Last;
  }

private:
  _Locinfo::_Ctypevec _Ctype;     // information
};

// CLASS ctype<wchar_t>
template<>
class ctype<wchar_t>
  : public ctype_base
{       // facet for classifying wchar_t elements, converting cases
  typedef ctype<wchar_t> _Myt;

public:
  typedef wchar_t _Elem;
  typedef _Elem char_type;

  bool is(mask _Maskval, _Elem _Ch) const
  {       // test if element fits any mask classifications
    return do_is(_Maskval, _Ch);
  }

  const _Elem *is(const _Elem *_First, const _Elem *_Last,
                  mask *_Dest) const
  {       // get mask sequence for elements in [_First, _Last)
    return do_is(_First, _Last, _Dest);
  }

  const _Elem *scan_is(mask _Maskval, const _Elem *_First,
                       const _Elem *_Last) const
  {       // find first in [_First, _Last) that fits mask classification
    return do_scan_is(_Maskval, _First, _Last);
  }

  const _Elem *scan_not(mask _Maskval, const _Elem *_First,
                        const _Elem *_Last) const
  {       // find first in [_First, _Last) not fitting mask classification
    return do_scan_not(_Maskval, _First, _Last);
  }

  _Elem tolower(_Elem _Ch) const
  {       // convert element to lower case
    return do_tolower(_Ch);
  }

  const _Elem *tolower(_Elem *_First, const _Elem *_Last) const
  {       // convert [_First, _Last) in place to lower case
    return do_tolower(_First, _Last);
  }

  _Elem toupper(_Elem _Ch) const
  {       // convert element to upper case
    return do_toupper(_Ch);
  }

  const _Elem *toupper(_Elem *_First, const _Elem *_Last) const
  {       // convert [_First, _Last) in place to upper case
    return do_toupper(_First, _Last);
  }

  _Elem widen(char _Byte) const
  {       // widen char
    return do_widen(_Byte);
  }

  const char *widen(const char *_First, const char *_Last,
                    _Elem *_Dest) const
  {       // widen chars in [_First, _Last)
    return do_widen(_First, _Last, _Dest);
  }

  char narrow(_Elem _Ch, char _Dflt = '\0') const
  {       // narrow element to char
    return do_narrow(_Ch, _Dflt);
  }

  const _Elem *narrow(const _Elem *_First, const _Elem *_Last,
                      char _Dflt, char *_Dest) const
  {       // narrow elements in [_First, _Last) to chars
    return do_narrow(_First, _Last, _Dflt, _Dest);
  }

  static locale::id id;

  explicit ctype(size_t _Refs = 0)
    : ctype_base(_Refs)
  {       // construct from current locale
    // Possibly lock thread
    _Locinfo _Lobj;
    _Init(_Lobj);
  }

  ctype(const _Locinfo& _Lobj, size_t _Refs = 0)
    : ctype_base(_Refs)
  {       // construct from specified locale
    _Init(_Lobj);
  }

  static size_t _Getcat(const locale::facet **_Ppf = 0,
                        const locale *_Ploc = 0)
  {       // return locale category mask and construct standard facet
    if (_Ppf != 0 && *_Ppf == 0)
      *_Ppf = new ctype<_Elem>(
        _Ploc->_GetLocinfo());
    return _X_CTYPE;
  }

protected:
  virtual ~ctype() _NOEXCEPT
  {       // destroy the object
    if (_Ctype._Delfl)
      ::free((void *)_Ctype._Table);
  }

  void _Init(const _Locinfo& _Lobj)
  {       // initialize from _Lobj
    _Ctype = _Lobj._Getctype();
    _Cvt = _Lobj._Getcvt();
  }

  virtual bool do_is(mask _Maskval, _Elem _Ch) const
  { // test if element fits any mask classifications
    return __iar_isw(_Ch, _Maskval);
  }

  virtual const _Elem *do_is(const _Elem *_First,
                             const _Elem *_Last, mask *_Dest) const
  {       // get mask sequence for elements in [_First, _Last)
    _DEBUG_RANGE_PTR(_First, _Last, _Dest);
    for (; _First != _Last; ++_First, ++_Dest)
      *_Dest = __iar_getw(*_First);
    return _First;
  }

  virtual const _Elem *do_scan_is(mask _Maskval,
                                  const _Elem *_First, const _Elem *_Last) const
  {       // find first in [_First, _Last) that fits mask classification
    _DEBUG_RANGE(_First, _Last);
    for (; _First != _Last && !is(_Maskval, *_First); ++_First)
      ;
    return _First;
  }

  virtual const _Elem *do_scan_not(mask _Maskval,
                                  const _Elem *_First, const _Elem *_Last) const
  {       // find first in [_First, _Last) not fitting mask classification
    _DEBUG_RANGE(_First, _Last);
    for (; _First != _Last && is(_Maskval, *_First); ++_First)
      ;
    return _First;
  }

  virtual _Elem do_tolower(_Elem _Ch) const
  { // convert element to lower case
    return towlower(_Ch);
  }

  virtual const _Elem *do_tolower(_Elem *_First,
                                  const _Elem *_Last) const
  {       // convert [_First, _Last) in place to lower case
    _DEBUG_RANGE((const _Elem *)_First, _Last);
    for (; _First != _Last; ++_First)
    { // convert *_First to lower case
      *_First = towlower(*_First);
    }
    return (const _Elem *)_First;
  }

  virtual _Elem do_toupper(_Elem _Ch) const
  {       // convert element to upper case
    return towupper(_Ch);
  }

  virtual const _Elem *do_toupper(_Elem *_First,
                                  const _Elem *_Last) const
  {       // convert [_First, _Last) in place to upper case
    _DEBUG_RANGE((const _Elem *)_First, _Last);
    for (; _First != _Last; ++_First)
    {       // convert *_First to upper case
      *_First = towupper(*_First);
    }
    return (const _Elem *)_First;
  }

  _Elem _Dowiden(char _Byte) const
  {       // widen char
    _Mbstinit(_Mbst);
    wchar_t _Wc;
    return _Mbrtowc(&_Wc, &_Byte, 1, &_Mbst, &_Cvt) < 0 ? (wchar_t)WEOF : _Wc;
  }

  virtual _Elem do_widen(char _Byte) const
  {       // widen char
    return _Dowiden(_Byte);
  }

  virtual const char *do_widen(const char *_First,
                               const char *_Last, _Elem *_Dest) const
  {       // widen chars in [_First, _Last)
    _DEBUG_RANGE_PTR(_First, _Last, _Dest);
    for (; _First != _Last; ++_First, ++_Dest)
      *_Dest = _Dowiden(*_First);
    return _First;
  }

  char _Donarrow(_Elem _Ch, char _Dflt) const
  {       // narrow element to char
    char _Buf[MB_LEN_MAX];
    _Mbstinit(_Mbst);
    return _Wcrtomb(_Buf, _Ch, &_Mbst, &_Cvt) != 1 ? _Dflt : _Buf[0];
  }

  virtual char do_narrow(_Elem _Ch, char _Dflt) const
  {       // narrow element to char
    return _Donarrow(_Ch, _Dflt);
  }

  virtual const _Elem *do_narrow(const _Elem *_First,
                              const _Elem *_Last, char _Dflt, char *_Dest) const
  {       // narrow elements in [_First, _Last) to chars
    _DEBUG_RANGE_PTR(_First, _Last, _Dest);
    for (; _First != _Last; ++_First, ++_Dest)
      *_Dest = _Donarrow(*_First, _Dflt);
    return _First;
  }

private:
  _Locinfo::_Ctypevec _Ctype;     // locale info passed to _Tolower, etc.
  _Locinfo::_Cvtvec _Cvt;         // conversion information
};

// TEMPLATE CLASS ctype_byname
template<class _Elem>
class ctype_byname
  : public ctype<_Elem>
{       // ctype for named locale
public:
  explicit ctype_byname(const char *_Locname, size_t _Refs = 0)
    : ctype<_Elem>(_Locinfo(_Locname), _Refs)
  {       // construct for named locale
  }

  explicit ctype_byname(const string& _Str, size_t _Refs = 0)
    : ctype<_Elem>(_Locinfo(_Str.c_str()), _Refs)
  {       // construct for named locale
  }

protected:
  virtual ~ctype_byname() _NOEXCEPT
  {       // destroy the object
  }
};

// TEMPLATE CLASS ctype_byname<char>
template<>
class ctype_byname<char>
  : public ctype<char>
{       // ctype_byname<char> for named locale
public:
  explicit ctype_byname(const char *_Locname, size_t _Refs = 0)
    : ctype<char>(_Locinfo(_Locname), _Refs)
  {       // construct for named locale
  }

  explicit ctype_byname(const string& _Str, size_t _Refs = 0)
    : ctype<char>(_Locinfo(_Str.c_str()), _Refs)
  {       // construct for named locale
  }

protected:
  virtual ~ctype_byname() _NOEXCEPT
  {       // destroy the object
  }
};

} /* namespace std */
#else /* !_DLIB_FULL_LOCALE_SUPPORT */
namespace std {
template<class _Facet>
class _Wrap_facet
  : public _Facet
{ // makes a facet destructible and shareable
public:
  _Wrap_facet()
    : _Facet()
  {
  }
};

template<class _Facet> inline
const _Facet& use_facet()
{
    static _Wrap_facet<_Facet> _MyFacet;
    return (*(_Facet *)&_MyFacet);
//  static const _Facet _MyFacet;
//  return _MyFacet;
}

// TEMPLATE FUNCTION _Getloctxt
template<class _Elem,
         class _InIt> inline
int _Getloctxt(_InIt& _First, _InIt& _Last, size_t _Numfields,
               const _Elem *_Ptr)
{       // find field at _Ptr that matches longest in [_First, _Last)
  for (size_t _Off = 0; _Ptr[_Off] != (_Elem)0; ++_Off)
    if (_Ptr[_Off] == _Ptr[0])
      ++_Numfields;   // add fields with leading mark to initial count
  string _Str(_Numfields, '\0');  // one column counter for each field

  int _Ans = -2;  // no candidates so far
  for (size_t _Column = 1; ; ++_Column, ++_First, _Ans = -1)
  {       // test each element against all viable fields
    bool _Prefix = false;   // seen at least one valid prefix
    size_t _Off = 0;        // offset into fields
    size_t _Field = 0;      // current field number

    for (; _Field < _Numfields; ++_Field)
    {       // test element at _Column in field _Field
      for (; _Ptr[_Off] != (_Elem)0 && _Ptr[_Off] != _Ptr[0]; ++_Off)
        ;       // find beginning of field

      if (_Str[_Field] != '\0')
        _Off += _Str[_Field];   // skip tested columns in field
      else if (_Ptr[_Off += _Column] == _Ptr[0]
               || _Ptr[_Off] == (_Elem)0)
      {       // matched all of field, save as possible answer
        _Str[_Field] = (char)(_Column < 127
                      ? _Column : 127);       // save skip count if small enough
        _Ans = (int)_Field;     // save answer
      }
      else if (_First == _Last || _Ptr[_Off] != *_First)
        _Str[_Field] = (char)(_Column < 127
                       ? _Column : 127);       // no match, just save skip count
      else
        _Prefix = true; // still a valid prefix
    }

    if (!_Prefix || _First == _Last)
      break;  // no pending prefixes or no input, give up
  }
  return _Ans;  // return field number or negative value on failure
}

// TEMPLATE FUNCTION _Maklocbyte
#define _MAKLOCBYTE(Elem, chr, cvt)                             \
  _Maklocbyte((_Elem)chr)    /* convert Elem to char */

template<class _Elem> inline
char _Maklocbyte(_Elem _Char)
{       // convert _Elem to char
  return (char)(unsigned char)_Char;
}

#if _DLIB_WIDE_CHARACTERS
template<> inline
char _Maklocbyte(wchar_t _Char)
{       // convert wchar_t to char
  char _Byte = '\0';
  _Mbstinit(_Mbst1);
  _Wcrtomb(&_Byte, _Char, &_Mbst1);
  return _Byte;
}
#endif

// TEMPLATE FUNCTION _Maklocchr
#define _MAKLOCCHR(Elem, chr, cvt)                              \
  _Maklocchr(chr, (Elem *)0) /* convert char to Elem */

template<class _Elem> inline
_Elem _Maklocchr(char _Byte, _Elem *)
{       // convert char to _Elem using _Cvtvec
  return (_Elem)(unsigned char)_Byte;
}

#if _DLIB_WIDE_CHARACTERS
template<> inline
wchar_t _Maklocchr(char _Byte, wchar_t *)
{       // convert char to wchar_t using _Cvtvec
  wchar_t _Wc = L'\0';
  _Mbstinit(_Mbst1);
  _Mbrtowc(&_Wc, &_Byte, 1, &_Mbst1);
  return _Wc;
}
#endif

// TEMPLATE FUNCTION _Maklocstr
#define _MAKLOCSTR(Elem, str, cvt)                                      \
  _Maklocstr(str, (Elem *)0) /* convert C string to Elem sequence */

template<class _Elem> inline
_Elem *_Maklocstr(const char *_Ptr, _Elem *)
{       // convert C string to _Elem sequence
  size_t _Count = ::strlen(_Ptr) + 1;
  _Elem *_Ptrdest = (_Elem *)::malloc(_Count);
  if (!_Ptrdest)
    __iar_Raise_bad_alloc();

  for (_Elem *_Ptrnext = _Ptrdest; 0 < _Count; --_Count, ++_Ptrnext, ++_Ptr)
    *_Ptrnext = (_Elem)(unsigned char)*_Ptr;
  return _Ptrdest;
}

#if _DLIB_WIDE_CHARACTERS
#pragma no_arith_checks
template<> inline
wchar_t *_Maklocstr(const char *_Ptr, wchar_t *)
{       // convert C string to wchar_t sequence using _Cvtvec
  size_t _Count, _Count1;
  size_t _Wchars;
  const char *_Ptr1;
  int _Bytes;
  wchar_t _Wc;
  _Mbstinit(_Mbst1);

  _Count1 = ::strlen(_Ptr) + 1;
  for (_Count = _Count1, _Wchars = 0, _Ptr1 = _Ptr; 0 < _Count;
       _Count -= _Bytes, _Ptr1 += _Bytes, ++_Wchars)
    if ((_Bytes = _Mbrtowc(&_Wc, _Ptr1, _Count, &_Mbst1)) <= 0)
      break;
  ++_Wchars;      // count terminating nul

  wchar_t *_Ptrdest = (wchar_t *)::malloc(_Wchars * sizeof (wchar_t));
  if (!_Ptrdest)
    __iar_Raise_bad_alloc();
  wchar_t *_Ptrnext = _Ptrdest;
  _Mbstinit(_Mbst2);

  for (; 0 < _Wchars;
       _Count -= _Bytes, _Ptr += _Bytes, --_Wchars, ++_Ptrnext)
    if ((_Bytes = _Mbrtowc(_Ptrnext, _Ptr, _Count1, &_Mbst2)) <= 0)
      break;
  *_Ptrnext = L'\0';
  return _Ptrdest;
}
#endif
                /* ctype_base code bits */
#define _XB             0x400 /* extra blank */
#define _XA             0x200 /* extra alphabetic */
#define _XS             0x100 /* extra space */
#define _BB             0x80 /* BEL, BS, etc. */
#define _CN             0x40 /* CR, FF, HT, NL, VT */
#define _DI             0x20 /* '0'-'9' */
#define _LO             0x10 /* 'a'-'z' */
#define _PU             0x08 /* punctuation */
#define _SP             0x04 /* space */
#define _UP             0x02 /* 'A'-'Z' */
#define _XD             0x01 /* '0'-'9', 'A'-'F', 'a'-'f' */

// STRUCT ctype_base
struct ctype_base
{       // base for ctype
  enum
  {       // constants for character classifications
    alnum = _DI | _LO | _UP | _XA, alpha = _LO | _UP | _XA,
    cntrl = _BB, digit = _DI, graph = _DI | _LO | _PU | _UP | _XA,
    lower = _LO, print = _DI | _LO | _PU | _SP | _UP | _XA | _XD,
    punct = _PU, space = _CN | _SP | _XS, upper = _UP,
    xdigit = _XD, blank = _CN | _SP | _XS | _XB};
  typedef short mask;     // to match <ctype.h>

  ctype_base()
  {       // default constructor
  }

  ~ctype_base() _NOEXCEPT
  {       // destroy the object
  }
};

// TEMPLATE CLASS ctype
template<class _Elem>
class ctype
  : public ctype_base
{       // facet for classifying elements, converting cases
public:
  typedef _Elem char_type;

  bool is(mask _Maskval, _Elem _Ch) const
  {       // test if element fits any mask classifications
    // Only implemented for space.
    return isspace((unsigned char)narrow(_Ch));
  }

  _Elem widen(char _Byte) const
  {       // widen char
    return _MAKLOCCHR(_Elem, _Byte, _Cvt);
  }

  char const *widen(char const *_First, char const *_Last, _Elem *_Dest) const
  {
    _DEBUG_RANGE_PTR(_First, _Last, _Dest);
    for (; _First != _Last; ++_First, ++_Dest)
      *_Dest = _MAKLOCCHR(_Elem, *_First, _Cvt);
    return _Last;
  }

  char narrow(_Elem _Ch, char _Dflt = '\0') const
  {       // narrow element to char
    return _Donarrow(_Ch, _Dflt);
  }

  _Elem const *narrow(_Elem const *_First, _Elem const *_Last,
                      char _Dflt, char *_Dest) const
  {
    _DEBUG_RANGE_PTR(_First, _Last, _Dest);
    for (; _First != _Last; ++_First, ++_Dest)
      *_Dest = _Donarrow(*_First, _Dflt);
    return _Last;
  }

  ctype()
  {
  }

protected:
  virtual ~ctype() _NOEXCEPT
  {       // destroy the object
  }

  char _Donarrow(_Elem _Ch, char _Dflt) const
  {       // narrow element to char
    char _Byte;
    if (_Ch == (_Elem)0)
      return '\0';
    else if ((_Byte = _MAKLOCBYTE(_Elem, _Ch, _Cvt)) == '\0')
      return _Dflt;
    else
      return _Byte;
  }
};

// CLASS ctype<char>
template<>
class ctype<char>
  : public ctype_base
{       // facet for classifying char elements, converting cases
  typedef ctype<char> _Myt;

public:
  typedef char _Elem;
  typedef _Elem char_type;

  bool is(mask _Maskval, _Elem _Ch) const
  {       // test if element fits any mask classifications
    // Only implemented for space.
    return isspace((unsigned char)narrow(_Ch));
  }

  _Elem widen(char _Byte) const
  {       // widen char
    return _Byte;
  }

  char const *widen(char const *_First, char const *_Last, _Elem *_Dest) const
  {
    _DEBUG_RANGE_PTR(_First, _Last, _Dest);
    for (; _First != _Last; ++_First, ++_Dest)
      *_Dest = *_First;
    return _Last;
  }

  _Elem narrow(_Elem _Ch, char _Dflt = '\0') const
  {       // narrow element to char
    return _Ch;
  }

  _Elem const *narrow(_Elem const *_First, _Elem const *_Last,
                      char _Dflt, char *_Dest) const
  {
    _DEBUG_RANGE_PTR(_First, _Last, _Dest);
    for (; _First != _Last; ++_First, ++_Dest)
      *_Dest = *_First;
    return _Last;
  }

  ctype()
  {
  }

protected:
  virtual ~ctype() _NOEXCEPT
  {       // destroy the object
  }
};

#if _DLIB_WIDE_CHARACTERS
// CLASS ctype<wchar_t>
template<>
class ctype<wchar_t>
  : public ctype_base
{       // facet for classifying wchar_t elements, converting cases
  typedef ctype<wchar_t> _Myt;

public:
  typedef wchar_t _Elem;
  typedef _Elem char_type;

  bool is(mask _Maskval, _Elem _Ch) const
  {       // test if element fits any mask classifications
    // Only implemented for space.
    return isspace((unsigned char)narrow(_Ch));
  }

  _Elem widen(char _Byte) const
  {       // widen char
    return _Dowiden(_Byte);
  }

  char const *widen(char const *_First, char const *_Last, _Elem *_Dest) const
  {
    _DEBUG_RANGE_PTR(_First, _Last, _Dest);
    for (; _First != _Last; ++_First, ++_Dest)
      *_Dest = _Dowiden(*_First);
    return _Last;
  }

  char narrow(_Elem _Ch, char _Dflt = '\0') const
  {       // narrow element to char
    return _Donarrow(_Ch, _Dflt);
  }

  _Elem const *narrow(_Elem const *_First, _Elem const *_Last,
                      char _Dflt, char *_Dest) const
  {
    _DEBUG_RANGE_PTR(_First, _Last, _Dest);
    for (; _First != _Last; ++_First, ++_Dest)
      *_Dest = _Donarrow(*_First, _Dflt);
    return _Last;
  }

  ctype()
  {
  }

protected:
  virtual ~ctype() _NOEXCEPT
  {       // destroy the object
  }

  _Elem _Dowiden(char _Byte) const
  {       // widen char
    _Mbstinit(_Mbst);
    wchar_t _Wc;
    return _Mbrtowc(&_Wc, &_Byte, 1, &_Mbst) < 0 ? (wchar_t)WEOF : _Wc;
  }

  char _Donarrow(_Elem _Ch, char _Dflt) const
  {       // narrow element to char
    char _Buf[MB_LEN_MAX];
    _Mbstinit(_Mbst);
    return _Wcrtomb(_Buf, _Ch, &_Mbst) != 1 ? _Dflt : _Buf[0];
  }
};
#endif

} /* namespace std */

#endif /* _DLIB_FULL_LOCALE_SUPPORT */

#endif /* _XLOCALE_ */

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