// Locale support (codecvt) -*- C++ -*-

// Copyright (C) 2015-2025 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library.  This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.

// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
// <http://www.gnu.org/licenses/>.

#include <codecvt>
#include <cstring>		// std::memcpy, std::memcmp
#include <bits/stl_algobase.h>	// std::min

namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION

  // The standard doesn't define these operators, which is annoying.
  static underlying_type<codecvt_mode>::type
  to_integer(codecvt_mode m)
  { return static_cast<underlying_type<codecvt_mode>::type>(m); }

  static codecvt_mode& operator&=(codecvt_mode& m, codecvt_mode n)
  { return m = codecvt_mode(to_integer(m) & to_integer(n)); }

  static codecvt_mode& operator|=(codecvt_mode& m, codecvt_mode n)
  { return m = codecvt_mode(to_integer(m) | to_integer(n)); }

  static codecvt_mode operator~(codecvt_mode m)
  { return codecvt_mode(~to_integer(m)); }

namespace
{
  // Largest code point that fits in a single UTF-16 code unit.
  const char32_t max_single_utf16_unit = 0xFFFF;

  const char32_t max_code_point = 0x10FFFF;

  // The functions below rely on maxcode < incomplete_mb_character
  // (which is enforced by the codecvt_utf* classes on construction).
  const char32_t incomplete_mb_character = char32_t(-2);
  const char32_t invalid_mb_sequence = char32_t(-1);

  // Utility type for reading and writing code units of type Elem from
  // a range defined by a pair of pointers.
  template<typename Elem, bool Aligned = true>
    struct range
    {
      Elem* next;
      Elem* end;

      // Write a code unit.
      range& operator=(Elem e)
      {
	*next++ = e;
	return *this;
      }

      // Read the next code unit.
      Elem operator*() const { return *next; }

      // Read the Nth code unit.
      Elem operator[](size_t n) const { return next[n]; }

      // Move to the next code unit.
      range& operator++()
      {
	++next;
	return *this;
      }

      // Move to the Nth code unit.
      range& operator+=(size_t n)
      {
	next += n;
	return *this;
      }

      // The number of code units remaining.
      size_t size() const { return end - next; }

      // The number of bytes remaining.
      size_t nbytes() const { return (const char*)end - (const char*)next; }
    };

  // This specialization is used when accessing char16_t values through
  // pointers to char, which might not be correctly aligned for char16_t.
  template<typename Elem>
    struct range<Elem, false>
    {
      using value_type = typename remove_const<Elem>::type;

      using char_pointer = typename
	conditional<is_const<Elem>::value, const char*, char*>::type;

      char_pointer next;
      char_pointer end;

      // Write a code unit.
      range& operator=(Elem e)
      {
	memcpy(next, &e, sizeof(Elem));
	++*this;
	return *this;
      }

      // Read the next code unit.
      Elem operator*() const
      {
	value_type e;
	memcpy(&e, next, sizeof(Elem));
	return e;
      }

      // Read the Nth code unit.
      Elem operator[](size_t n) const
      {
	value_type e;
	memcpy(&e, next + n * sizeof(Elem), sizeof(Elem));
	return e;
      }

      // Move to the next code unit.
      range& operator++()
      {
	next += sizeof(Elem);
	return *this;
      }

      // Move to the Nth code unit.
      range& operator+=(size_t n)
      {
	next += n * sizeof(Elem);
	return *this;
      }

      // The number of code units remaining.
      size_t size() const { return nbytes() / sizeof(Elem); }

      // The number of bytes remaining.
      size_t nbytes() const { return end - next; }
    };

  // Multibyte sequences can have "header" consisting of Byte Order Mark
  const unsigned char utf8_bom[3] = { 0xEF, 0xBB, 0xBF };
  const unsigned char utf16_bom[2] = { 0xFE, 0xFF };
  const unsigned char utf16le_bom[2] = { 0xFF, 0xFE };

  // Write a BOM (space permitting).
  template<typename C, bool A, size_t N>
    bool
    write_bom(range<C, A>& to, const unsigned char (&bom)[N])
    {
      static_assert( (N / sizeof(C)) != 0, "" );
      static_assert( (N % sizeof(C)) == 0, "" );

      if (to.nbytes() < N)
	return false;
      memcpy(to.next, bom, N);
      to += (N / sizeof(C));
      return true;
    }

  // Try to read a BOM.
  template<typename C, bool A, size_t N>
    bool
    read_bom(range<C, A>& from, const unsigned char (&bom)[N])
    {
      static_assert( (N / sizeof(C)) != 0, "" );
      static_assert( (N % sizeof(C)) == 0, "" );

      if (from.nbytes() >= N && !memcmp(from.next, bom, N))
	{
	  from += (N / sizeof(C));
	  return true;
	}
      return false;
    }

  // If generate_header is set in mode write out UTF-8 BOM.
  template<typename C>
  bool
  write_utf8_bom(range<C>& to, codecvt_mode mode)
  {
    if (mode & generate_header)
      return write_bom(to, utf8_bom);
    return true;
  }

  // If generate_header is set in mode write out the UTF-16 BOM indicated
  // by whether little_endian is set in mode.
  template<bool Aligned>
  bool
  write_utf16_bom(range<char16_t, Aligned>& to, codecvt_mode mode)
  {
    if (mode & generate_header)
    {
      if (mode & little_endian)
	return write_bom(to, utf16le_bom);
      else
	return write_bom(to, utf16_bom);
    }
    return true;
  }

  // If consume_header is set in mode update from.next to after any BOM.
  template<typename C>
  void
  read_utf8_bom(range<const C>& from, codecvt_mode mode)
  {
    if (mode & consume_header)
      read_bom(from, utf8_bom);
  }

  // If consume_header is not set in mode, no effects.
  // Otherwise, if *from.next is a UTF-16 BOM increment from.next and then:
  // - if the UTF-16BE BOM was found unset little_endian in mode, or
  // - if the UTF-16LE BOM was found set little_endian in mode.
  template<bool Aligned>
  void
  read_utf16_bom(range<const char16_t, Aligned>& from, codecvt_mode& mode)
  {
    if (mode & consume_header)
      {
	if (read_bom(from, utf16_bom))
	  mode &= ~little_endian;
	else if (read_bom(from, utf16le_bom))
	  mode |= little_endian;
      }
  }

  // Read a codepoint from a UTF-8 multibyte sequence.
  // Updates from.next if the codepoint is not greater than maxcode.
  // Returns invalid_mb_sequence, incomplete_mb_character or the code point.
  template<typename C>
  char32_t
  read_utf8_code_point(range<const C>& from, unsigned long maxcode)
  {
    const size_t avail = from.size();
    if (avail == 0)
      return incomplete_mb_character;
    char32_t c1 = (unsigned char) from[0];
    // https://en.wikipedia.org/wiki/UTF-8#Sample_code
    if (c1 < 0x80) [[likely]]
    {
      ++from;
      return c1;
    }
    else if (c1 < 0xC2) [[unlikely]] // continuation or overlong 2-byte sequence
      return invalid_mb_sequence;
    else if (c1 < 0xE0) // 2-byte sequence
    {
      if (avail < 2) [[unlikely]]
	return incomplete_mb_character;
      char32_t c2 = (unsigned char) from[1];
      if ((c2 & 0xC0) != 0x80) [[unlikely]]
	return invalid_mb_sequence;
      char32_t c = (c1 << 6) + c2 - 0x3080;
      if (c <= maxcode)
	from += 2;
      return c;
    }
    else if (c1 < 0xF0) // 3-byte sequence
    {
      if (avail < 2) [[unlikely]]
	return incomplete_mb_character;
      char32_t c2 = (unsigned char) from[1];
      if ((c2 & 0xC0) != 0x80) [[unlikely]]
	return invalid_mb_sequence;
      if (c1 == 0xE0 && c2 < 0xA0) [[unlikely]] // overlong
	return invalid_mb_sequence;
      if (c1 == 0xED && c2 >= 0xA0) [[unlikely]] // surrogate
	return invalid_mb_sequence;
      if (avail < 3) [[unlikely]]
	return incomplete_mb_character;
      char32_t c3 = (unsigned char) from[2];
      if ((c3 & 0xC0) != 0x80) [[unlikely]]
	return invalid_mb_sequence;
      char32_t c = (c1 << 12) + (c2 << 6) + c3 - 0xE2080;
      if (c <= maxcode)
	from += 3;
      return c;
    }
    else if (c1 < 0xF5 && maxcode > 0xFFFF) // 4-byte sequence
    {
      if (avail < 2) [[unlikely]]
	return incomplete_mb_character;
      char32_t c2 = (unsigned char) from[1];
      if ((c2 & 0xC0) != 0x80) [[unlikely]]
	return invalid_mb_sequence;
      if (c1 == 0xF0 && c2 < 0x90) [[unlikely]] // overlong
	return invalid_mb_sequence;
      if (c1 == 0xF4 && c2 >= 0x90) [[unlikely]] // > U+10FFFF
	return invalid_mb_sequence;
      if (avail < 3) [[unlikely]]
	return incomplete_mb_character;
      char32_t c3 = (unsigned char) from[2];
      if ((c3 & 0xC0) != 0x80) [[unlikely]]
	return invalid_mb_sequence;
      if (avail < 4) [[unlikely]]
	return incomplete_mb_character;
      char32_t c4 = (unsigned char) from[3];
      if ((c4 & 0xC0) != 0x80) [[unlikely]]
	return invalid_mb_sequence;
      char32_t c = (c1 << 18) + (c2 << 12) + (c3 << 6) + c4 - 0x3C82080;
      if (c <= maxcode)
	from += 4;
      return c;
    }
    else [[unlikely]] // > U+10FFFF
      return invalid_mb_sequence;
  }

  template<typename C>
  bool
  write_utf8_code_point(range<C>& to, char32_t code_point)
  {
    if (code_point < 0x80)
      {
	if (to.size() < 1) [[unlikely]]
	  return false;
	to = code_point;
      }
    else if (code_point <= 0x7FF)
      {
	if (to.size() < 2) [[unlikely]]
	  return false;
	to = (code_point >> 6) + 0xC0;
	to = (code_point & 0x3F) + 0x80;
      }
    else if (code_point <= 0xFFFF)
      {
	if (to.size() < 3) [[unlikely]]
	  return false;
	to = (code_point >> 12) + 0xE0;
	to = ((code_point >> 6) & 0x3F) + 0x80;
	to = (code_point & 0x3F) + 0x80;
      }
    else if (code_point <= 0x10FFFF)
      {
	if (to.size() < 4) [[unlikely]]
	  return false;
	to = (code_point >> 18) + 0xF0;
	to = ((code_point >> 12) & 0x3F) + 0x80;
	to = ((code_point >> 6) & 0x3F) + 0x80;
	to = (code_point & 0x3F) + 0x80;
      }
    else [[unlikely]]
      return false;
    return true;
  }

  inline char16_t
  adjust_byte_order(char16_t c, codecvt_mode mode)
  {
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
    return (mode & little_endian) ? __builtin_bswap16(c) : c;
#else
    return (mode & little_endian) ? c : __builtin_bswap16(c);
#endif
  }

  // Return true if c is a high-surrogate (aka leading) code point.
  inline bool
  is_high_surrogate(char32_t c)
  {
    return c >= 0xD800 && c <= 0xDBFF;
  }

  // Return true if c is a low-surrogate (aka trailing) code point.
  inline bool
  is_low_surrogate(char32_t c)
  {
    return c >= 0xDC00 && c <= 0xDFFF;
  }

  inline char32_t
  surrogate_pair_to_code_point(char32_t high, char32_t low)
  {
    return (high << 10) + low - 0x35FDC00;
  }

  // Read a codepoint from a UTF-16 multibyte sequence.
  // The sequence's endianness is indicated by (mode & little_endian).
  // Updates from.next if the codepoint is not greater than maxcode.
  // Returns invalid_mb_sequence, incomplete_mb_character or the code point.
  template<bool Aligned>
    char32_t
    read_utf16_code_point(range<const char16_t, Aligned>& from,
			  unsigned long maxcode, codecvt_mode mode)
    {
      const size_t avail = from.size();
      if (avail == 0) [[unlikely]]
	return incomplete_mb_character;
      int inc = 1;
      char32_t c = adjust_byte_order(from[0], mode);
      if (is_high_surrogate(c))
	{
	  if (avail < 2) [[unlikely]]
	    return incomplete_mb_character;
	  const char16_t c2 = adjust_byte_order(from[1], mode);
	  if (is_low_surrogate(c2)) [[likely]]
	    {
	      c = surrogate_pair_to_code_point(c, c2);
	      inc = 2;
	    }
	  else
	    return invalid_mb_sequence;
	}
      else if (is_low_surrogate(c)) [[unlikely]]
	return invalid_mb_sequence;
      if (c <= maxcode)
	from += inc;
      return c;
    }

  template<typename C, bool A>
  bool
  write_utf16_code_point(range<C, A>& to, char32_t codepoint, codecvt_mode mode)
  {
    static_assert(sizeof(C) >= 2, "a code unit must be at least 16-bit");

    if (codepoint <= max_single_utf16_unit)
      {
	if (to.size() > 0)
	  {
	    to = adjust_byte_order(codepoint, mode);
	    return true;
	  }
      }
    else if (to.size() > 1)
      {
	// Algorithm from http://www.unicode.org/faq/utf_bom.html#utf16-4
	const char32_t LEAD_OFFSET = 0xD800 - (0x10000 >> 10);
	char16_t lead = LEAD_OFFSET + (codepoint >> 10);
	char16_t trail = 0xDC00 + (codepoint & 0x3FF);
	to = adjust_byte_order(lead, mode);
	to = adjust_byte_order(trail, mode);
	return true;
      }
    return false;
  }

  // utf8 -> ucs4
  template<typename C>
  codecvt_base::result
  ucs4_in(range<const C>& from, range<char32_t>& to,
          unsigned long maxcode = max_code_point, codecvt_mode mode = {})
  {
    read_utf8_bom(from, mode);
    while (from.size() && to.size())
      {
	const char32_t codepoint = read_utf8_code_point(from, maxcode);
	if (codepoint == incomplete_mb_character) [[unlikely]]
	  return codecvt_base::partial;
	if (codepoint > maxcode) [[unlikely]]
	  return codecvt_base::error;
	to = codepoint;
      }
    return from.size() ? codecvt_base::partial : codecvt_base::ok;
  }

  // ucs4 -> utf8
  template<typename C>
  codecvt_base::result
  ucs4_out(range<const char32_t>& from, range<C>& to,
           unsigned long maxcode = max_code_point, codecvt_mode mode = {})
  {
    if (!write_utf8_bom(to, mode)) [[unlikely]]
      return codecvt_base::partial;
    while (from.size())
      {
	const char32_t c = from[0];
	if (0xD800 <= c && c <= 0xDFFF) [[unlikely]]
	  return codecvt_base::error;
	if (c > maxcode) [[unlikely]]
	  return codecvt_base::error;
	if (!write_utf8_code_point(to, c)) [[unlikely]]
	  return codecvt_base::partial;
	++from;
      }
    return codecvt_base::ok;
  }

  // utf16 -> ucs4
  codecvt_base::result
  ucs4_in(range<const char16_t, false>& from, range<char32_t>& to,
          unsigned long maxcode = max_code_point, codecvt_mode mode = {})
  {
    read_utf16_bom(from, mode);
    while (from.size() && to.size())
      {
	const char32_t codepoint = read_utf16_code_point(from, maxcode, mode);
	if (codepoint == incomplete_mb_character) [[unlikely]]
	  return codecvt_base::partial;
	if (codepoint > maxcode) [[unlikely]]
	  return codecvt_base::error;
	to = codepoint;
      }
    return from.nbytes() ? codecvt_base::partial : codecvt_base::ok;
  }

  // ucs4 -> utf16
  codecvt_base::result
  ucs4_out(range<const char32_t>& from, range<char16_t, false>& to,
           unsigned long maxcode = max_code_point, codecvt_mode mode = {})
  {
    if (!write_utf16_bom(to, mode)) [[unlikely]]
      return codecvt_base::partial;
    while (from.size())
      {
	const char32_t c = from[0];
	if (0xD800 <= c && c <= 0xDFFF) [[unlikely]]
	  return codecvt_base::error;
	if (c > maxcode) [[unlikely]]
	  return codecvt_base::error;
	if (!write_utf16_code_point(to, c, mode)) [[unlikely]]
	  return codecvt_base::partial;
	++from;
      }
    return codecvt_base::ok;
  }

  // Flag indicating whether to process UTF-16 or UCS2
  enum class surrogates { allowed, disallowed };

  // utf8 -> utf16 (or utf8 -> ucs2 if maxcode <= 0xFFFF)
  template <typename C8, typename C16>
  codecvt_base::result
  utf16_in(range<const C8> &from, range<C16> &to,
	   unsigned long maxcode = max_code_point, codecvt_mode mode = {})
  {
    read_utf8_bom(from, mode);
    while (from.size() && to.size())
      {
	auto orig = from;
	const char32_t codepoint = read_utf8_code_point(from, maxcode);
	if (codepoint == incomplete_mb_character) [[unlikely]]
	  return codecvt_base::partial;
	if (codepoint > maxcode)
	  return codecvt_base::error;
	if (!write_utf16_code_point(to, codepoint, mode)) [[unlikely]]
	  {
	    from = orig; // rewind to previous position
	    return codecvt_base::partial;
	  }
      }
    return from.size() ? codecvt_base::partial : codecvt_base::ok;
  }

  // utf16 -> utf8 (or ucs2 -> utf8 if s == surrogates::disallowed)
  template<typename C16, typename C8>
  codecvt_base::result
  utf16_out(range<const C16>& from, range<C8>& to,
	    unsigned long maxcode = max_code_point, codecvt_mode mode = {},
	    surrogates s = surrogates::allowed)
  {
    if (!write_utf8_bom(to, mode)) [[unlikely]]
      return codecvt_base::partial;
    while (from.size())
      {
	char32_t c = from[0];
	int inc = 1;
	if (is_high_surrogate(c))
	  {
	    if (s == surrogates::disallowed) [[unlikely]]
	      return codecvt_base::error; // No surrogates in UCS-2

	    if (from.size() < 2) [[unlikely]]
	      return codecvt_base::partial; // stop converting at this point

	    const char32_t c2 = from[1];
	    if (is_low_surrogate(c2)) [[likely]]
	      {
		c = surrogate_pair_to_code_point(c, c2);
		inc = 2;
	      }
	    else
	      return codecvt_base::error;
	  }
	else if (is_low_surrogate(c)) [[unlikely]]
	  return codecvt_base::error;
	if (c > maxcode) [[unlikely]]
	  return codecvt_base::error;
	if (!write_utf8_code_point(to, c)) [[unlikely]]
	  return codecvt_base::partial;
	from += inc;
      }
    return codecvt_base::ok;
  }

  // return pos such that [begin,pos) is valid UTF-16 string no longer than max
  template<typename C>
  const C*
  utf16_span(const C* begin, const C* end, size_t max,
	     char32_t maxcode = max_code_point, codecvt_mode mode = {})
  {
    range<const C> from{ begin, end };
    read_utf8_bom(from, mode);
    size_t count = 0;
    while (count+1 < max)
      {
	char32_t c = read_utf8_code_point(from, maxcode);
	if (c > maxcode)
	  return from.next;
	else if (c > max_single_utf16_unit)
	  ++count;
	++count;
      }
    if (count+1 == max) // take one more character if it fits in a single unit
      read_utf8_code_point(from, std::min(max_single_utf16_unit, maxcode));
    return from.next;
  }

  // utf8 -> ucs2
  template<typename C>
  codecvt_base::result
  ucs2_in(range<const C>& from, range<char16_t>& to,
	  char32_t maxcode = max_code_point, codecvt_mode mode = {})
  {
    // UCS-2 only supports characters in the BMP, i.e. one UTF-16 code unit:
    maxcode = std::min(max_single_utf16_unit, maxcode);
    return utf16_in(from, to, maxcode, mode);
  }

  // ucs2 -> utf8
  template<typename C>
  codecvt_base::result
  ucs2_out(range<const char16_t>& from, range<C>& to,
	   char32_t maxcode = max_code_point, codecvt_mode mode = {})
  {
    // UCS-2 only supports characters in the BMP, i.e. one UTF-16 code unit:
    maxcode = std::min(max_single_utf16_unit, maxcode);
    return utf16_out(from, to, maxcode, mode, surrogates::disallowed);
  }

  // ucs2 -> utf16
  codecvt_base::result
  ucs2_out(range<const char16_t>& from, range<char16_t, false>& to,
	   char32_t maxcode = max_code_point, codecvt_mode mode = {})
  {
    if (!write_utf16_bom(to, mode))
      return codecvt_base::partial;
    while (from.size() && to.size())
      {
	char16_t c = from[0];
	if (0xD800 <= c && c <= 0xDFFF)
	  return codecvt_base::error;
	if (c > maxcode)
	  return codecvt_base::error;
	to = adjust_byte_order(c, mode);
	++from;
      }
    return from.size() == 0 ? codecvt_base::ok : codecvt_base::partial;
  }

  // utf16 -> ucs2
  codecvt_base::result
  ucs2_in(range<const char16_t, false>& from, range<char16_t>& to,
	  char32_t maxcode = max_code_point, codecvt_mode mode = {})
  {
    read_utf16_bom(from, mode);
    // UCS-2 only supports characters in the BMP, i.e. one UTF-16 code unit:
    maxcode = std::min(max_single_utf16_unit, maxcode);
    while (from.size() && to.size())
      {
	const char32_t c = read_utf16_code_point(from, maxcode, mode);
	if (c == incomplete_mb_character)
	  return codecvt_base::error; // UCS-2 only supports single units.
	if (c > maxcode)
	  return codecvt_base::error;
	to = c;
      }
    return from.nbytes() == 0 ? codecvt_base::ok : codecvt_base::partial;
  }

  const char16_t*
  ucs2_span(range<const char16_t, false>& from, size_t max,
            char32_t maxcode, codecvt_mode mode)
  {
    read_utf16_bom(from, mode);
    // UCS-2 only supports characters in the BMP, i.e. one UTF-16 code unit:
    maxcode = std::min(max_single_utf16_unit, maxcode);
    char32_t c = 0;
    while (max-- && c <= maxcode)
      c = read_utf16_code_point(from, maxcode, mode);
    return reinterpret_cast<const char16_t*>(from.next);
  }

  template<typename C>
  const C*
  ucs2_span(const C* begin, const C* end, size_t max,
            char32_t maxcode, codecvt_mode mode)
  {
    range<const C> from{ begin, end };
    read_utf8_bom(from, mode);
    // UCS-2 only supports characters in the BMP, i.e. one UTF-16 code unit:
    maxcode = std::min(max_single_utf16_unit, maxcode);
    char32_t c = 0;
    while (max-- && c <= maxcode)
      c = read_utf8_code_point(from, maxcode);
    return from.next;
  }

  // return pos such that [begin,pos) is valid UCS-4 string no longer than max
  template<typename C>
  const C*
  ucs4_span(const C* begin, const C* end, size_t max,
            char32_t maxcode = max_code_point, codecvt_mode mode = {})
  {
    range<const C> from{ begin, end };
    read_utf8_bom(from, mode);
    char32_t c = 0;
    while (max-- && c <= maxcode)
      c = read_utf8_code_point(from, maxcode);
    return from.next;
  }

  // return pos such that [begin,pos) is valid UCS-4 string no longer than max
  const char16_t*
  ucs4_span(range<const char16_t, false>& from, size_t max,
            char32_t maxcode = max_code_point, codecvt_mode mode = {})
  {
    read_utf16_bom(from, mode);
    char32_t c = 0;
    while (max-- && c <= maxcode)
      c = read_utf16_code_point(from, maxcode, mode);
    return reinterpret_cast<const char16_t*>(from.next);
  }
}

// Define members of codecvt<char16_t, char, mbstate_t> specialization.
// Converts from UTF-8 to UTF-16.

locale::id codecvt<char16_t, char, mbstate_t>::id;

codecvt<char16_t, char, mbstate_t>::~codecvt() { }

codecvt_base::result
codecvt<char16_t, char, mbstate_t>::
do_out(state_type&,
       const intern_type* __from,
       const intern_type* __from_end, const intern_type*& __from_next,
       extern_type* __to, extern_type* __to_end,
       extern_type*& __to_next) const
{
  range<const char16_t> from{ __from, __from_end };
  range<char> to{ __to, __to_end };
  auto res = utf16_out(from, to);
  __from_next = from.next;
  __to_next = to.next;
  return res;
}

codecvt_base::result
codecvt<char16_t, char, mbstate_t>::
do_unshift(state_type&, extern_type* __to, extern_type*,
	   extern_type*& __to_next) const
{
  __to_next = __to;
  return noconv; // we don't use mbstate_t for the unicode facets
}

codecvt_base::result
codecvt<char16_t, char, mbstate_t>::
do_in(state_type&, const extern_type* __from, const extern_type* __from_end,
      const extern_type*& __from_next,
      intern_type* __to, intern_type* __to_end,
      intern_type*& __to_next) const
{
  range<const char> from{ __from, __from_end };
  range<char16_t> to{ __to, __to_end };
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
  codecvt_mode mode = {};
#else
  codecvt_mode mode = little_endian;
#endif
  auto res = utf16_in(from, to, max_code_point, mode);
  __from_next = from.next;
  __to_next = to.next;
  return res;
}

int
codecvt<char16_t, char, mbstate_t>::do_encoding() const throw()
{ return 0; } // UTF-8 is not a fixed-width encoding

bool
codecvt<char16_t, char, mbstate_t>::do_always_noconv() const throw()
{ return false; }

int
codecvt<char16_t, char, mbstate_t>::
do_length(state_type&, const extern_type* __from,
	  const extern_type* __end, size_t __max) const
{
  __end = utf16_span(__from, __end, __max);
  return __end - __from;
}

int
codecvt<char16_t, char, mbstate_t>::do_max_length() const throw()
{
  // A single character (one or two UTF-16 code units) requires
  // up to four UTF-8 code units.
  return 4;
}

// Define members of codecvt<char32_t, char, mbstate_t> specialization.
// Converts from UTF-8 to UTF-32 (aka UCS-4).

locale::id codecvt<char32_t, char, mbstate_t>::id;

codecvt<char32_t, char, mbstate_t>::~codecvt() { }

codecvt_base::result
codecvt<char32_t, char, mbstate_t>::
do_out(state_type&, const intern_type* __from, const intern_type* __from_end,
       const intern_type*& __from_next,
       extern_type* __to, extern_type* __to_end,
       extern_type*& __to_next) const
{
  range<const char32_t> from{ __from, __from_end };
  range<char> to{ __to, __to_end };
  auto res = ucs4_out(from, to);
  __from_next = from.next;
  __to_next = to.next;
  return res;
}

codecvt_base::result
codecvt<char32_t, char, mbstate_t>::
do_unshift(state_type&, extern_type* __to, extern_type*,
	   extern_type*& __to_next) const
{
  __to_next = __to;
  return noconv;
}

codecvt_base::result
codecvt<char32_t, char, mbstate_t>::
do_in(state_type&, const extern_type* __from, const extern_type* __from_end,
      const extern_type*& __from_next,
      intern_type* __to, intern_type* __to_end,
      intern_type*& __to_next) const
{
  range<const char> from{ __from, __from_end };
  range<char32_t> to{ __to, __to_end };
  auto res = ucs4_in(from, to);
  __from_next = from.next;
  __to_next = to.next;
  return res;
}

int
codecvt<char32_t, char, mbstate_t>::do_encoding() const throw()
{ return 0; } // UTF-8 is not a fixed-width encoding

bool
codecvt<char32_t, char, mbstate_t>::do_always_noconv() const throw()
{ return false; }

int
codecvt<char32_t, char, mbstate_t>::
do_length(state_type&, const extern_type* __from,
	  const extern_type* __end, size_t __max) const
{
  __end = ucs4_span(__from, __end, __max);
  return __end - __from;
}

int
codecvt<char32_t, char, mbstate_t>::do_max_length() const throw()
{
  // A single character (one UTF-32 code unit) requires
  // up to 4 UTF-8 code units.
  return 4;
}

#if defined(_GLIBCXX_USE_CHAR8_T)
// Define members of codecvt<char16_t, char8_t, mbstate_t> specialization.
// Converts from UTF-8 to UTF-16.

locale::id codecvt<char16_t, char8_t, mbstate_t>::id;

codecvt<char16_t, char8_t, mbstate_t>::~codecvt() { }

codecvt_base::result
codecvt<char16_t, char8_t, mbstate_t>::
do_out(state_type&,
       const intern_type* __from,
       const intern_type* __from_end, const intern_type*& __from_next,
       extern_type* __to, extern_type* __to_end,
       extern_type*& __to_next) const
{
  range<const char16_t> from{ __from, __from_end };
  range<char8_t> to{ __to, __to_end };
  auto res = utf16_out(from, to);
  __from_next = from.next;
  __to_next = to.next;
  return res;
}

codecvt_base::result
codecvt<char16_t, char8_t, mbstate_t>::
do_unshift(state_type&, extern_type* __to, extern_type*,
	   extern_type*& __to_next) const
{
  __to_next = __to;
  return noconv; // we don't use mbstate_t for the unicode facets
}

codecvt_base::result
codecvt<char16_t, char8_t, mbstate_t>::
do_in(state_type&, const extern_type* __from, const extern_type* __from_end,
      const extern_type*& __from_next,
      intern_type* __to, intern_type* __to_end,
      intern_type*& __to_next) const
{
  range<const char8_t> from{ __from, __from_end };
  range<char16_t> to{ __to, __to_end };
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
  codecvt_mode mode = {};
#else
  codecvt_mode mode = little_endian;
#endif
  auto res = utf16_in(from, to, max_code_point, mode);
  __from_next = from.next;
  __to_next = to.next;
  return res;
}

int
codecvt<char16_t, char8_t, mbstate_t>::do_encoding() const throw()
{ return 0; } // UTF-8 is not a fixed-width encoding

bool
codecvt<char16_t, char8_t, mbstate_t>::do_always_noconv() const throw()
{ return false; }

int
codecvt<char16_t, char8_t, mbstate_t>::
do_length(state_type&, const extern_type* __from,
	  const extern_type* __end, size_t __max) const
{
  __end = utf16_span(__from, __end, __max);
  return __end - __from;
}

int
codecvt<char16_t, char8_t, mbstate_t>::do_max_length() const throw()
{
  // A single character (one or two UTF-16 code units) requires
  // up to four UTF-8 code units.
  return 4;
}

// Define members of codecvt<char32_t, char8_t, mbstate_t> specialization.
// Converts from UTF-8 to UTF-32 (aka UCS-4).

locale::id codecvt<char32_t, char8_t, mbstate_t>::id;

codecvt<char32_t, char8_t, mbstate_t>::~codecvt() { }

codecvt_base::result
codecvt<char32_t, char8_t, mbstate_t>::
do_out(state_type&, const intern_type* __from, const intern_type* __from_end,
       const intern_type*& __from_next,
       extern_type* __to, extern_type* __to_end,
       extern_type*& __to_next) const
{
  range<const char32_t> from{ __from, __from_end };
  range<char8_t> to{ __to, __to_end };
  auto res = ucs4_out(from, to);
  __from_next = from.next;
  __to_next = to.next;
  return res;
}

codecvt_base::result
codecvt<char32_t, char8_t, mbstate_t>::
do_unshift(state_type&, extern_type* __to, extern_type*,
	   extern_type*& __to_next) const
{
  __to_next = __to;
  return noconv;
}

codecvt_base::result
codecvt<char32_t, char8_t, mbstate_t>::
do_in(state_type&, const extern_type* __from, const extern_type* __from_end,
      const extern_type*& __from_next,
      intern_type* __to, intern_type* __to_end,
      intern_type*& __to_next) const
{
  range<const char8_t> from{ __from, __from_end };
  range<char32_t> to{ __to, __to_end };
  auto res = ucs4_in(from, to);
  __from_next = from.next;
  __to_next = to.next;
  return res;
}

int
codecvt<char32_t, char8_t, mbstate_t>::do_encoding() const throw()
{ return 0; } // UTF-8 is not a fixed-width encoding

bool
codecvt<char32_t, char8_t, mbstate_t>::do_always_noconv() const throw()
{ return false; }

int
codecvt<char32_t, char8_t, mbstate_t>::
do_length(state_type&, const extern_type* __from,
	  const extern_type* __end, size_t __max) const
{
  __end = ucs4_span(__from, __end, __max);
  return __end - __from;
}

int
codecvt<char32_t, char8_t, mbstate_t>::do_max_length() const throw()
{
  // A single character (one UTF-32 code unit) requires
  // up to 4 UTF-8 code units.
  return 4;
}
#endif // _GLIBCXX_USE_CHAR8_T

// Define members of codecvt_utf8<char16_t> base class implementation.
// Converts from UTF-8 to UCS-2.

__codecvt_utf8_base<char16_t>::~__codecvt_utf8_base() { }

codecvt_base::result
__codecvt_utf8_base<char16_t>::
do_out(state_type&, const intern_type* __from, const intern_type* __from_end,
       const intern_type*& __from_next,
       extern_type* __to, extern_type* __to_end,
       extern_type*& __to_next) const
{
  range<const char16_t> from{ __from, __from_end };
  range<char> to{ __to, __to_end };
  auto res = ucs2_out(from, to, _M_maxcode, _M_mode);
  __from_next = from.next;
  __to_next = to.next;
  return res;
}

codecvt_base::result
__codecvt_utf8_base<char16_t>::
do_unshift(state_type&, extern_type* __to, extern_type*,
	   extern_type*& __to_next) const
{
  __to_next = __to;
  return noconv;
}

codecvt_base::result
__codecvt_utf8_base<char16_t>::
do_in(state_type&, const extern_type* __from, const extern_type* __from_end,
      const extern_type*& __from_next,
      intern_type* __to, intern_type* __to_end,
      intern_type*& __to_next) const
{
  range<const char> from{ __from, __from_end };
  range<char16_t> to{ __to, __to_end };
  codecvt_mode mode = codecvt_mode(_M_mode & (consume_header|generate_header));
#if __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__
  mode = codecvt_mode(mode | little_endian);
#endif
  auto res = ucs2_in(from, to, _M_maxcode, mode);
  __from_next = from.next;
  __to_next = to.next;
  return res;
}

int
__codecvt_utf8_base<char16_t>::do_encoding() const throw()
{ return 0; } // UTF-8 is not a fixed-width encoding

bool
__codecvt_utf8_base<char16_t>::do_always_noconv() const throw()
{ return false; }

int
__codecvt_utf8_base<char16_t>::
do_length(state_type&, const extern_type* __from,
	  const extern_type* __end, size_t __max) const
{
  __end = ucs2_span(__from, __end, __max, _M_maxcode, _M_mode);
  return __end - __from;
}

int
__codecvt_utf8_base<char16_t>::do_max_length() const throw()
{
  // A single UCS-2 character requires up to three UTF-8 code units.
  // (UCS-2 cannot represent characters that use four UTF-8 code units).
  int max = 3;
  if (_M_mode & consume_header)
    max += sizeof(utf8_bom);
  return max;
}

// Define members of codecvt_utf8<char32_t> base class implementation.
// Converts from UTF-8 to UTF-32 (aka UCS-4).

__codecvt_utf8_base<char32_t>::~__codecvt_utf8_base() { }

codecvt_base::result
__codecvt_utf8_base<char32_t>::
do_out(state_type&, const intern_type* __from, const intern_type* __from_end,
       const intern_type*& __from_next,
       extern_type* __to, extern_type* __to_end,
       extern_type*& __to_next) const
{
  range<const char32_t> from{ __from, __from_end };
  range<char> to{ __to, __to_end };
  auto res = ucs4_out(from, to, _M_maxcode, _M_mode);
  __from_next = from.next;
  __to_next = to.next;
  return res;
}

codecvt_base::result
__codecvt_utf8_base<char32_t>::
do_unshift(state_type&, extern_type* __to, extern_type*,
	   extern_type*& __to_next) const
{
  __to_next = __to;
  return noconv;
}

codecvt_base::result
__codecvt_utf8_base<char32_t>::
do_in(state_type&, const extern_type* __from, const extern_type* __from_end,
      const extern_type*& __from_next,
      intern_type* __to, intern_type* __to_end,
      intern_type*& __to_next) const
{
  range<const char> from{ __from, __from_end };
  range<char32_t> to{ __to, __to_end };
  auto res = ucs4_in(from, to, _M_maxcode, _M_mode);
  __from_next = from.next;
  __to_next = to.next;
  return res;
}

int
__codecvt_utf8_base<char32_t>::do_encoding() const throw()
{ return 0; } // UTF-8 is not a fixed-width encoding

bool
__codecvt_utf8_base<char32_t>::do_always_noconv() const throw()
{ return false; }

int
__codecvt_utf8_base<char32_t>::
do_length(state_type&, const extern_type* __from,
	  const extern_type* __end, size_t __max) const
{
  __end = ucs4_span(__from, __end, __max, _M_maxcode, _M_mode);
  return __end - __from;
}

int
__codecvt_utf8_base<char32_t>::do_max_length() const throw()
{
  // A single UCS-4 character requires up to four UTF-8 code units.
  int max = 4;
  if (_M_mode & consume_header)
    max += sizeof(utf8_bom);
  return max;
}

#ifdef _GLIBCXX_USE_WCHAR_T

#if __SIZEOF_WCHAR_T__ == 2
static_assert(sizeof(wchar_t) == sizeof(char16_t), "");
#elif __SIZEOF_WCHAR_T__ == 4
static_assert(sizeof(wchar_t) == sizeof(char32_t), "");
#endif

// Define members of codecvt_utf8<wchar_t> base class implementation.
// Converts from UTF-8 to UCS-2 or UCS-4 depending on sizeof(wchar_t).

__codecvt_utf8_base<wchar_t>::~__codecvt_utf8_base() { }

codecvt_base::result
__codecvt_utf8_base<wchar_t>::
do_out(state_type&, const intern_type* __from, const intern_type* __from_end,
       const intern_type*& __from_next,
       extern_type* __to, extern_type* __to_end,
       extern_type*& __to_next) const
{
  range<char> to{ __to, __to_end };
#if __SIZEOF_WCHAR_T__ == 2
  range<const char16_t> from{
    reinterpret_cast<const char16_t*>(__from),
    reinterpret_cast<const char16_t*>(__from_end)
  };
  auto res = ucs2_out(from, to, _M_maxcode, _M_mode);
#elif __SIZEOF_WCHAR_T__ == 4
  range<const char32_t> from{
    reinterpret_cast<const char32_t*>(__from),
    reinterpret_cast<const char32_t*>(__from_end)
  };
  auto res = ucs4_out(from, to, _M_maxcode, _M_mode);
#else
  return codecvt_base::error;
#endif
  __from_next = reinterpret_cast<const wchar_t*>(from.next);
  __to_next = to.next;
  return res;
}

codecvt_base::result
__codecvt_utf8_base<wchar_t>::
do_unshift(state_type&, extern_type* __to, extern_type*,
	   extern_type*& __to_next) const
{
  __to_next = __to;
  return noconv;
}

codecvt_base::result
__codecvt_utf8_base<wchar_t>::
do_in(state_type&, const extern_type* __from, const extern_type* __from_end,
      const extern_type*& __from_next,
      intern_type* __to, intern_type* __to_end,
      intern_type*& __to_next) const
{
  range<const char> from{ __from, __from_end };
#if __SIZEOF_WCHAR_T__ == 2
  range<char16_t> to{
    reinterpret_cast<char16_t*>(__to),
    reinterpret_cast<char16_t*>(__to_end)
  };
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
  codecvt_mode mode = {};
#else
  codecvt_mode mode = little_endian;
#endif
  auto res = ucs2_in(from, to, _M_maxcode, mode);
#elif __SIZEOF_WCHAR_T__ == 4
  range<char32_t> to{
    reinterpret_cast<char32_t*>(__to),
    reinterpret_cast<char32_t*>(__to_end)
  };
  auto res = ucs4_in(from, to, _M_maxcode, _M_mode);
#else
  return codecvt_base::error;
#endif
  __from_next = from.next;
  __to_next = reinterpret_cast<wchar_t*>(to.next);
  return res;
}

int
__codecvt_utf8_base<wchar_t>::do_encoding() const throw()
{ return 0; } // UTF-8 is not a fixed-width encoding

bool
__codecvt_utf8_base<wchar_t>::do_always_noconv() const throw()
{ return false; }

int
__codecvt_utf8_base<wchar_t>::
do_length(state_type&, const extern_type* __from,
	  const extern_type* __end, size_t __max) const
{
#if __SIZEOF_WCHAR_T__ == 2
  __end = ucs2_span(__from, __end, __max, _M_maxcode, _M_mode);
#elif __SIZEOF_WCHAR_T__ == 4
  __end = ucs4_span(__from, __end, __max, _M_maxcode, _M_mode);
#else
  __end = __from;
#endif
  return __end - __from;
}

int
__codecvt_utf8_base<wchar_t>::do_max_length() const throw()
{
#if __SIZEOF_WCHAR_T__ == 2
  int max = 3; // See __codecvt_utf8_base<char16_t>::do_max_length()
#else
  int max = 4; // See __codecvt_utf8_base<char32_t>::do_max_length()
#endif
  if (_M_mode & consume_header)
    max += sizeof(utf8_bom);
  return max;
}
#endif

// Define members of codecvt_utf16<char16_t> base class implementation.
// Converts from UTF-16 to UCS-2.

__codecvt_utf16_base<char16_t>::~__codecvt_utf16_base() { }

codecvt_base::result
__codecvt_utf16_base<char16_t>::
do_out(state_type&, const intern_type* __from, const intern_type* __from_end,
       const intern_type*& __from_next,
       extern_type* __to, extern_type* __to_end,
       extern_type*& __to_next) const
{
  range<const char16_t> from{ __from, __from_end };
  range<char16_t, false> to{ __to, __to_end };
  auto res = ucs2_out(from, to, _M_maxcode, _M_mode);
  __from_next = from.next;
  __to_next = reinterpret_cast<char*>(to.next);
  return res;
}

codecvt_base::result
__codecvt_utf16_base<char16_t>::
do_unshift(state_type&, extern_type* __to, extern_type*,
	   extern_type*& __to_next) const
{
  __to_next = __to;
  return noconv;
}

codecvt_base::result
__codecvt_utf16_base<char16_t>::
do_in(state_type&, const extern_type* __from, const extern_type* __from_end,
      const extern_type*& __from_next,
      intern_type* __to, intern_type* __to_end,
      intern_type*& __to_next) const
{
  range<const char16_t, false> from{ __from, __from_end };
  range<char16_t> to{ __to, __to_end };
  auto res = ucs2_in(from, to, _M_maxcode, _M_mode);
  __from_next = reinterpret_cast<const char*>(from.next);
  __to_next = to.next;
  return res;
}

int
__codecvt_utf16_base<char16_t>::do_encoding() const throw()
{ return 0; } // UTF-16 is not a fixed-width encoding

bool
__codecvt_utf16_base<char16_t>::do_always_noconv() const throw()
{ return false; }

int
__codecvt_utf16_base<char16_t>::
do_length(state_type&, const extern_type* __from,
	  const extern_type* __end, size_t __max) const
{
  range<const char16_t, false> from{ __from, __end };
  const char16_t* next = ucs2_span(from, __max, _M_maxcode, _M_mode);
  return reinterpret_cast<const char*>(next) - __from;
}

int
__codecvt_utf16_base<char16_t>::do_max_length() const throw()
{
  // A single UCS-2 character requires one UTF-16 code unit (so two chars).
  // (UCS-2 cannot represent characters that use multiple UTF-16 code units).
  int max = 2;
  if (_M_mode & consume_header)
    max += sizeof(utf16_bom);
  return max;
}

// Define members of codecvt_utf16<char32_t> base class implementation.
// Converts from UTF-16 to UTF-32 (aka UCS-4).

__codecvt_utf16_base<char32_t>::~__codecvt_utf16_base() { }

codecvt_base::result
__codecvt_utf16_base<char32_t>::
do_out(state_type&, const intern_type* __from, const intern_type* __from_end,
       const intern_type*& __from_next,
       extern_type* __to, extern_type* __to_end,
       extern_type*& __to_next) const
{
  range<const char32_t> from{ __from, __from_end };
  range<char16_t, false> to{ __to, __to_end };
  auto res = ucs4_out(from, to, _M_maxcode, _M_mode);
  __from_next = from.next;
  __to_next = reinterpret_cast<char*>(to.next);
  return res;
}

codecvt_base::result
__codecvt_utf16_base<char32_t>::
do_unshift(state_type&, extern_type* __to, extern_type*,
	   extern_type*& __to_next) const
{
  __to_next = __to;
  return noconv;
}

codecvt_base::result
__codecvt_utf16_base<char32_t>::
do_in(state_type&, const extern_type* __from, const extern_type* __from_end,
      const extern_type*& __from_next,
      intern_type* __to, intern_type* __to_end,
      intern_type*& __to_next) const
{
  range<const char16_t, false> from{ __from, __from_end };
  range<char32_t> to{ __to, __to_end };
  auto res = ucs4_in(from, to, _M_maxcode, _M_mode);
  __from_next = reinterpret_cast<const char*>(from.next);
  __to_next = to.next;
  return res;
}

int
__codecvt_utf16_base<char32_t>::do_encoding() const throw()
{ return 0; } // UTF-16 is not a fixed-width encoding

bool
__codecvt_utf16_base<char32_t>::do_always_noconv() const throw()
{ return false; }

int
__codecvt_utf16_base<char32_t>::
do_length(state_type&, const extern_type* __from,
	  const extern_type* __end, size_t __max) const
{
  range<const char16_t, false> from{ __from, __end };
  const char16_t* next = ucs4_span(from, __max, _M_maxcode, _M_mode);
  return reinterpret_cast<const char*>(next) - __from;
}

int
__codecvt_utf16_base<char32_t>::do_max_length() const throw()
{
  // A single UCS-4 character requires one or two UTF-16 code units
  // (so up to four chars).
  int max = 4;
  if (_M_mode & consume_header)
    max += sizeof(utf16_bom);
  return max;
}

#ifdef _GLIBCXX_USE_WCHAR_T
// Define members of codecvt_utf16<wchar_t> base class implementation.
// Converts from UTF-8 to UCS-2 or UCS-4 depending on sizeof(wchar_t).

__codecvt_utf16_base<wchar_t>::~__codecvt_utf16_base() { }

codecvt_base::result
__codecvt_utf16_base<wchar_t>::
do_out(state_type&, const intern_type* __from, const intern_type* __from_end,
       const intern_type*& __from_next,
       extern_type* __to, extern_type* __to_end,
       extern_type*& __to_next) const
{
  range<char16_t, false> to{ __to, __to_end };
#if __SIZEOF_WCHAR_T__ == 2
  range<const char16_t> from{
    reinterpret_cast<const char16_t*>(__from),
    reinterpret_cast<const char16_t*>(__from_end),
  };
  auto res = ucs2_out(from, to, _M_maxcode, _M_mode);
#elif __SIZEOF_WCHAR_T__ == 4
  range<const char32_t> from{
    reinterpret_cast<const char32_t*>(__from),
    reinterpret_cast<const char32_t*>(__from_end),
  };
  auto res = ucs4_out(from, to, _M_maxcode, _M_mode);
#else
  return codecvt_base::error;
#endif
  __from_next = reinterpret_cast<const wchar_t*>(from.next);
  __to_next = reinterpret_cast<char*>(to.next);
  return res;
}

codecvt_base::result
__codecvt_utf16_base<wchar_t>::
do_unshift(state_type&, extern_type* __to, extern_type*,
	   extern_type*& __to_next) const
{
  __to_next = __to;
  return noconv;
}

codecvt_base::result
__codecvt_utf16_base<wchar_t>::
do_in(state_type&, const extern_type* __from, const extern_type* __from_end,
      const extern_type*& __from_next,
      intern_type* __to, intern_type* __to_end,
      intern_type*& __to_next) const
{
  range<const char16_t, false> from{ __from, __from_end };
#if __SIZEOF_WCHAR_T__ == 2
  range<char16_t> to{
    reinterpret_cast<char16_t*>(__to),
    reinterpret_cast<char16_t*>(__to_end),
  };
  auto res = ucs2_in(from, to, _M_maxcode, _M_mode);
#elif __SIZEOF_WCHAR_T__ == 4
  range<char32_t> to{
    reinterpret_cast<char32_t*>(__to),
    reinterpret_cast<char32_t*>(__to_end),
  };
  auto res = ucs4_in(from, to, _M_maxcode, _M_mode);
#else
  return codecvt_base::error;
#endif
  __from_next = reinterpret_cast<const char*>(from.next);
  __to_next = reinterpret_cast<wchar_t*>(to.next);
  return res;
}

int
__codecvt_utf16_base<wchar_t>::do_encoding() const throw()
{ return 0; } // UTF-16 is not a fixed-width encoding

bool
__codecvt_utf16_base<wchar_t>::do_always_noconv() const throw()
{ return false; }

int
__codecvt_utf16_base<wchar_t>::
do_length(state_type&, const extern_type* __from,
	  const extern_type* __end, size_t __max) const
{
  range<const char16_t, false> from{ __from, __end };
#if __SIZEOF_WCHAR_T__ == 2
  const char16_t* next = ucs2_span(from, __max, _M_maxcode, _M_mode);
#elif __SIZEOF_WCHAR_T__ == 4
  const char16_t* next = ucs4_span(from, __max, _M_maxcode, _M_mode);
#endif
  return reinterpret_cast<const char*>(next) - __from;
}

int
__codecvt_utf16_base<wchar_t>::do_max_length() const throw()
{
#if __SIZEOF_WCHAR_T__ == 2
  int max = 2; // See __codecvt_utf16_base<char16_t>::do_max_length()
#else
  int max = 4; // See __codecvt_utf16_base<char32_t>::do_max_length()
#endif
  if (_M_mode & consume_header)
    max += sizeof(utf16_bom);
  return max;
}
#endif

// Define members of codecvt_utf8_utf16<char16_t> base class implementation.
// Converts from UTF-8 to UTF-16.

__codecvt_utf8_utf16_base<char16_t>::~__codecvt_utf8_utf16_base() { }

codecvt_base::result
__codecvt_utf8_utf16_base<char16_t>::
do_out(state_type&, const intern_type* __from, const intern_type* __from_end,
       const intern_type*& __from_next,
       extern_type* __to, extern_type* __to_end,
       extern_type*& __to_next) const
{
  range<const char16_t> from{ __from, __from_end };
  range<char> to{ __to, __to_end };
  auto res = utf16_out(from, to, _M_maxcode, _M_mode);
  __from_next = from.next;
  __to_next = to.next;
  return res;
}

codecvt_base::result
__codecvt_utf8_utf16_base<char16_t>::
do_unshift(state_type&, extern_type* __to, extern_type*,
	   extern_type*& __to_next) const
{
  __to_next = __to;
  return noconv;
}

codecvt_base::result
__codecvt_utf8_utf16_base<char16_t>::
do_in(state_type&, const extern_type* __from, const extern_type* __from_end,
      const extern_type*& __from_next,
      intern_type* __to, intern_type* __to_end,
      intern_type*& __to_next) const
{
  range<const char> from{ __from, __from_end };
  range<char16_t> to{ __to, __to_end };
  codecvt_mode mode = codecvt_mode(_M_mode & (consume_header|generate_header));
#if __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__
  mode = codecvt_mode(mode | little_endian);
#endif
  auto res = utf16_in(from, to, _M_maxcode, mode);
  __from_next = from.next;
  __to_next = to.next;
  return res;
}

int
__codecvt_utf8_utf16_base<char16_t>::do_encoding() const throw()
{ return 0; } // UTF-8 is not a fixed-width encoding

bool
__codecvt_utf8_utf16_base<char16_t>::do_always_noconv() const throw()
{ return false; }

int
__codecvt_utf8_utf16_base<char16_t>::
do_length(state_type&, const extern_type* __from,
	  const extern_type* __end, size_t __max) const
{
  __end = utf16_span(__from, __end, __max, _M_maxcode, _M_mode);
  return __end - __from;
}

int
__codecvt_utf8_utf16_base<char16_t>::do_max_length() const throw()
{
  // A single character can be 1 or 2 UTF-16 code units,
  // requiring up to 4 UTF-8 code units.
  int max = 4;
  if (_M_mode & consume_header)
    max += sizeof(utf8_bom);
  return max;
}

// Define members of codecvt_utf8_utf16<char32_t> base class implementation.
// Converts from UTF-8 to UTF-16.

__codecvt_utf8_utf16_base<char32_t>::~__codecvt_utf8_utf16_base() { }

codecvt_base::result
__codecvt_utf8_utf16_base<char32_t>::
do_out(state_type&, const intern_type* __from, const intern_type* __from_end,
       const intern_type*& __from_next,
       extern_type* __to, extern_type* __to_end,
       extern_type*& __to_next) const
{
  range<const char32_t> from{ __from, __from_end };
  range<char> to{ __to, __to_end };
  auto res = utf16_out(from, to, _M_maxcode, _M_mode);
  __from_next = from.next;
  __to_next = to.next;
  return res;
}

codecvt_base::result
__codecvt_utf8_utf16_base<char32_t>::
do_unshift(state_type&, extern_type* __to, extern_type*,
	   extern_type*& __to_next) const
{
  __to_next = __to;
  return noconv;
}

codecvt_base::result
__codecvt_utf8_utf16_base<char32_t>::
do_in(state_type&, const extern_type* __from, const extern_type* __from_end,
      const extern_type*& __from_next,
      intern_type* __to, intern_type* __to_end,
      intern_type*& __to_next) const
{
  range<const char> from{ __from, __from_end };
  range<char32_t> to{ __to, __to_end };
  codecvt_mode mode = codecvt_mode(_M_mode & (consume_header|generate_header));
#if __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__
  mode = codecvt_mode(mode | little_endian);
#endif
  auto res = utf16_in(from, to, _M_maxcode, mode);
  __from_next = from.next;
  __to_next = to.next;
  return res;
}

int
__codecvt_utf8_utf16_base<char32_t>::do_encoding() const throw()
{ return 0; } // UTF-8 is not a fixed-width encoding

bool
__codecvt_utf8_utf16_base<char32_t>::do_always_noconv() const throw()
{ return false; }

int
__codecvt_utf8_utf16_base<char32_t>::
do_length(state_type&, const extern_type* __from,
	  const extern_type* __end, size_t __max) const
{
  __end = utf16_span(__from, __end, __max, _M_maxcode, _M_mode);
  return __end - __from;
}

int
__codecvt_utf8_utf16_base<char32_t>::do_max_length() const throw()
{
  // A single character can be 1 or 2 UTF-16 code units,
  // requiring up to 4 UTF-8 code units.
  int max = 4;
  if (_M_mode & consume_header)
    max += sizeof(utf8_bom);
  return max;
}

#ifdef _GLIBCXX_USE_WCHAR_T
// Define members of codecvt_utf8_utf16<wchar_t> base class implementation.
// Converts from UTF-8 to UTF-16.

__codecvt_utf8_utf16_base<wchar_t>::~__codecvt_utf8_utf16_base() { }

codecvt_base::result
__codecvt_utf8_utf16_base<wchar_t>::
do_out(state_type&, const intern_type* __from, const intern_type* __from_end,
       const intern_type*& __from_next,
       extern_type* __to, extern_type* __to_end,
       extern_type*& __to_next) const
{
  range<const wchar_t> from{ __from, __from_end };
  range<char> to{ __to, __to_end };
  auto res = utf16_out(from, to, _M_maxcode, _M_mode);
  __from_next = from.next;
  __to_next = to.next;
  return res;
}

codecvt_base::result
__codecvt_utf8_utf16_base<wchar_t>::
do_unshift(state_type&, extern_type* __to, extern_type*,
	   extern_type*& __to_next) const
{
  __to_next = __to;
  return noconv;
}

codecvt_base::result
__codecvt_utf8_utf16_base<wchar_t>::
do_in(state_type&, const extern_type* __from, const extern_type* __from_end,
      const extern_type*& __from_next,
      intern_type* __to, intern_type* __to_end,
      intern_type*& __to_next) const
{
  range<const char> from{ __from, __from_end };
  range<wchar_t> to{ __to, __to_end };
  codecvt_mode mode = codecvt_mode(_M_mode & (consume_header|generate_header));
#if __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__
  mode = codecvt_mode(mode | little_endian);
#endif
  auto res = utf16_in(from, to, _M_maxcode, mode);
  __from_next = from.next;
  __to_next = to.next;
  return res;
}

int
__codecvt_utf8_utf16_base<wchar_t>::do_encoding() const throw()
{ return 0; } // UTF-8 is not a fixed-width encoding

bool
__codecvt_utf8_utf16_base<wchar_t>::do_always_noconv() const throw()
{ return false; }

int
__codecvt_utf8_utf16_base<wchar_t>::
do_length(state_type&, const extern_type* __from,
	  const extern_type* __end, size_t __max) const
{
  __end = utf16_span(__from, __end, __max, _M_maxcode, _M_mode);
  return __end - __from;
}

int
__codecvt_utf8_utf16_base<wchar_t>::do_max_length() const throw()
{
  // A single character can be 1 or 2 UTF-16 code units,
  // requiring up to 4 UTF-8 code units.
  int max = 4;
  if (_M_mode & consume_header)
    max += sizeof(utf8_bom);
  return max;
}
#endif

inline template class __codecvt_abstract_base<char16_t, char, mbstate_t>;
inline template class __codecvt_abstract_base<char32_t, char, mbstate_t>;
template class codecvt_byname<char16_t, char, mbstate_t>;
template class codecvt_byname<char32_t, char, mbstate_t>;

#if defined(_GLIBCXX_USE_CHAR8_T)
inline template class __codecvt_abstract_base<char16_t, char8_t, mbstate_t>;
inline template class __codecvt_abstract_base<char32_t, char8_t, mbstate_t>;
template class codecvt_byname<char16_t, char8_t, mbstate_t>;
template class codecvt_byname<char32_t, char8_t, mbstate_t>;
#endif

_GLIBCXX_END_NAMESPACE_VERSION
}
