libstdc++: testsuite: Add char8_t to codecvt_unicode

Message ID AM0PR04MB54127FF670644B5182B84459ACD89@AM0PR04MB5412.eurprd04.prod.outlook.com
State Accepted
Headers
Series libstdc++: testsuite: Add char8_t to codecvt_unicode |

Checks

Context Check Description
snail/gcc-patch-check success Github commit url

Commit Message

Dimitrij Mijoski Feb. 8, 2023, 11:38 p.m. UTC
  libstdc++-v3/ChangeLog:

	* testsuite/22_locale/codecvt/codecvt_unicode.cc: Rename
	  functions.
	* testsuite/22_locale/codecvt/codecvt_unicode.h: Make more
	  generic so it accepts char8_t.
	* testsuite/22_locale/codecvt/codecvt_unicode_wchar_t.cc: Rename
	  functions.
	* testsuite/22_locale/codecvt/codecvt_unicode_char8_t.cc: New test.
---
 .../22_locale/codecvt/codecvt_unicode.cc      |  16 +-
 .../22_locale/codecvt/codecvt_unicode.h       | 807 +++++++++---------
 .../codecvt/codecvt_unicode_char8_t.cc        |  53 ++
 .../codecvt/codecvt_unicode_wchar_t.cc        |   6 +-
 4 files changed, 484 insertions(+), 398 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode_char8_t.cc
  

Comments

Hans-Peter Nilsson Feb. 11, 2023, 1:48 a.m. UTC | #1
Casual observation from a random reader that's sometimes hit by 
testresults acting up:

On Thu, 9 Feb 2023, Dimitrij Mijoski via Gcc-patches wrote:

> libstdc++-v3/ChangeLog:
> 
> 	* testsuite/22_locale/codecvt/codecvt_unicode.cc: Rename
> 	  functions.
> 	* testsuite/22_locale/codecvt/codecvt_unicode.h: Make more
> 	  generic so it accepts char8_t.
> 	* testsuite/22_locale/codecvt/codecvt_unicode_wchar_t.cc: Rename
> 	  functions.
> 	* testsuite/22_locale/codecvt/codecvt_unicode_char8_t.cc: New test.

(Second line indents to the "*", not the text after it.  TL;DR: 
See other entries.)

But above that, please never *modify* existing tests (unless 
they're bad and wrong), add new ones instead.  TL;DR: Bisecting 
when reghunting gets messy.

brgds, H-P

> ---
>  .../22_locale/codecvt/codecvt_unicode.cc      |  16 +-
>  .../22_locale/codecvt/codecvt_unicode.h       | 807 +++++++++---------
>  .../codecvt/codecvt_unicode_char8_t.cc        |  53 ++
>  .../codecvt/codecvt_unicode_wchar_t.cc        |   6 +-
>  4 files changed, 484 insertions(+), 398 deletions(-)
>  create mode 100644 libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode_char8_t.cc
> 
> diff --git a/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode.cc b/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode.cc
> index df1a2b4cc..eafb53a8c 100644
> --- a/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode.cc
> +++ b/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode.cc
> @@ -27,38 +27,38 @@ void
>  test_utf8_utf32_codecvts ()
>  {
>    using codecvt_c32 = codecvt<char32_t, char, mbstate_t>;
> -  auto loc_c = locale::classic ();
> +  auto &loc_c = locale::classic ();
>    VERIFY (has_facet<codecvt_c32> (loc_c));
>  
>    auto &cvt = use_facet<codecvt_c32> (loc_c);
> -  test_utf8_utf32_codecvts (cvt);
> +  test_utf8_utf32_cvt (cvt);
>  
>    codecvt_utf8<char32_t> cvt2;
> -  test_utf8_utf32_codecvts (cvt2);
> +  test_utf8_utf32_cvt (cvt2);
>  }
>  
>  void
>  test_utf8_utf16_codecvts ()
>  {
>    using codecvt_c16 = codecvt<char16_t, char, mbstate_t>;
> -  auto loc_c = locale::classic ();
> +  auto &loc_c = locale::classic ();
>    VERIFY (has_facet<codecvt_c16> (loc_c));
>  
>    auto &cvt = use_facet<codecvt_c16> (loc_c);
> -  test_utf8_utf16_cvts (cvt);
> +  test_utf8_utf16_cvt (cvt);
>  
>    codecvt_utf8_utf16<char16_t> cvt2;
> -  test_utf8_utf16_cvts (cvt2);
> +  test_utf8_utf16_cvt (cvt2);
>  
>    codecvt_utf8_utf16<char32_t> cvt3;
> -  test_utf8_utf16_cvts (cvt3);
> +  test_utf8_utf16_cvt (cvt3);
>  }
>  
>  void
>  test_utf8_ucs2_codecvts ()
>  {
>    codecvt_utf8<char16_t> cvt;
> -  test_utf8_ucs2_cvts (cvt);
> +  test_utf8_ucs2_cvt (cvt);
>  }
>  
>  int
> diff --git a/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode.h b/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode.h
> index fbdc7a35b..690c07215 100644
> --- a/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode.h
> +++ b/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode.h
> @@ -42,33 +42,33 @@ auto constexpr array_size (const T (&)[N]) -> size_t
>    return N;
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -utf8_to_utf32_in_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +utf8_to_utf32_in_ok (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    using namespace std;
>    // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
> -  const char in[] = "b?\uAAAA\U0010AAAA";
> -  const char32_t exp_literal[] = U"b?\uAAAA\U0010AAAA";
> -  CharT exp[array_size (exp_literal)] = {};
> -  std::copy (begin (exp_literal), end (exp_literal), begin (exp));
> -
> -  static_assert (array_size (in) == 11, "");
> -  static_assert (array_size (exp_literal) == 5, "");
> -  static_assert (array_size (exp) == 5, "");
> -  VERIFY (char_traits<char>::length (in) == 10);
> -  VERIFY (char_traits<char32_t>::length (exp_literal) == 4);
> -  VERIFY (char_traits<CharT>::length (exp) == 4);
> +  const unsigned char input[] = "b?\uAAAA\U0010AAAA";
> +  const char32_t expected[] = U"b?\uAAAA\U0010AAAA";
> +  static_assert (array_size (input) == 11, "");
> +  static_assert (array_size (expected) == 5, "");
> +
> +  ExternT in[array_size (input)];
> +  InternT exp[array_size (expected)];
> +  copy (begin (input), end (input), begin (in));
> +  copy (begin (expected), end (expected), begin (exp));
> +  VERIFY (char_traits<ExternT>::length (in) == 10);
> +  VERIFY (char_traits<InternT>::length (exp) == 4);
>  
>    test_offsets_ok offsets[] = {{0, 0}, {1, 1}, {3, 2}, {6, 3}, {10, 4}};
>    for (auto t : offsets)
>      {
> -      CharT out[array_size (exp) - 1] = {};
> +      InternT out[array_size (exp) - 1] = {};
>        VERIFY (t.in_size <= array_size (in));
>        VERIFY (t.out_size <= array_size (out));
>        auto state = mbstate_t{};
> -      auto in_next = (const char *) nullptr;
> -      auto out_next = (CharT *) nullptr;
> +      auto in_next = (const ExternT *) nullptr;
> +      auto out_next = (InternT *) nullptr;
>        auto res = codecvt_base::result ();
>  
>        res = cvt.in (state, in, in + t.in_size, in_next, out, out + t.out_size,
> @@ -76,19 +76,19 @@ utf8_to_utf32_in_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
>        VERIFY (res == cvt.ok);
>        VERIFY (in_next == in + t.in_size);
>        VERIFY (out_next == out + t.out_size);
> -      VERIFY (char_traits<CharT>::compare (out, exp, t.out_size) == 0);
> +      VERIFY (char_traits<InternT>::compare (out, exp, t.out_size) == 0);
>        if (t.out_size < array_size (out))
>  	VERIFY (out[t.out_size] == 0);
>      }
>  
>    for (auto t : offsets)
>      {
> -      CharT out[array_size (exp)] = {};
> +      InternT out[array_size (exp)] = {};
>        VERIFY (t.in_size <= array_size (in));
>        VERIFY (t.out_size <= array_size (out));
>        auto state = mbstate_t{};
> -      auto in_next = (const char *) nullptr;
> -      auto out_next = (CharT *) nullptr;
> +      auto in_next = (const ExternT *) nullptr;
> +      auto out_next = (InternT *) nullptr;
>        auto res = codecvt_base::result ();
>  
>        res
> @@ -96,29 +96,29 @@ utf8_to_utf32_in_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
>        VERIFY (res == cvt.ok);
>        VERIFY (in_next == in + t.in_size);
>        VERIFY (out_next == out + t.out_size);
> -      VERIFY (char_traits<CharT>::compare (out, exp, t.out_size) == 0);
> +      VERIFY (char_traits<InternT>::compare (out, exp, t.out_size) == 0);
>        if (t.out_size < array_size (out))
>  	VERIFY (out[t.out_size] == 0);
>      }
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -utf8_to_utf32_in_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +utf8_to_utf32_in_partial (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    using namespace std;
>    // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
> -  const char in[] = "b?\uAAAA\U0010AAAA";
> -  const char32_t exp_literal[] = U"b?\uAAAA\U0010AAAA";
> -  CharT exp[array_size (exp_literal)] = {};
> -  std::copy (begin (exp_literal), end (exp_literal), begin (exp));
> -
> -  static_assert (array_size (in) == 11, "");
> -  static_assert (array_size (exp_literal) == 5, "");
> -  static_assert (array_size (exp) == 5, "");
> -  VERIFY (char_traits<char>::length (in) == 10);
> -  VERIFY (char_traits<char32_t>::length (exp_literal) == 4);
> -  VERIFY (char_traits<CharT>::length (exp) == 4);
> +  const unsigned char input[] = "b?\uAAAA\U0010AAAA";
> +  const char32_t expected[] = U"b?\uAAAA\U0010AAAA";
> +  static_assert (array_size (input) == 11, "");
> +  static_assert (array_size (expected) == 5, "");
> +
> +  ExternT in[array_size (input)];
> +  InternT exp[array_size (expected)];
> +  copy (begin (input), end (input), begin (in));
> +  copy (begin (expected), end (expected), begin (exp));
> +  VERIFY (char_traits<ExternT>::length (in) == 10);
> +  VERIFY (char_traits<InternT>::length (exp) == 4);
>  
>    test_offsets_partial offsets[] = {
>      {1, 0, 0, 0}, // no space for first CP
> @@ -144,14 +144,14 @@ utf8_to_utf32_in_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
>  
>    for (auto t : offsets)
>      {
> -      CharT out[array_size (exp) - 1] = {};
> +      InternT out[array_size (exp) - 1] = {};
>        VERIFY (t.in_size <= array_size (in));
>        VERIFY (t.out_size <= array_size (out));
>        VERIFY (t.expected_in_next <= t.in_size);
>        VERIFY (t.expected_out_next <= t.out_size);
>        auto state = mbstate_t{};
> -      auto in_next = (const char *) nullptr;
> -      auto out_next = (CharT *) nullptr;
> +      auto in_next = (const ExternT *) nullptr;
> +      auto out_next = (InternT *) nullptr;
>        auto res = codecvt_base::result ();
>  
>        res = cvt.in (state, in, in + t.in_size, in_next, out, out + t.out_size,
> @@ -159,37 +159,38 @@ utf8_to_utf32_in_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
>        VERIFY (res == cvt.partial);
>        VERIFY (in_next == in + t.expected_in_next);
>        VERIFY (out_next == out + t.expected_out_next);
> -      VERIFY (char_traits<CharT>::compare (out, exp, t.expected_out_next) == 0);
> +      VERIFY (char_traits<InternT>::compare (out, exp, t.expected_out_next)
> +	      == 0);
>        if (t.expected_out_next < array_size (out))
>  	VERIFY (out[t.expected_out_next] == 0);
>      }
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -utf8_to_utf32_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +utf8_to_utf32_in_error (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    using namespace std;
>    // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
> -  const char valid_in[] = "b?\uAAAA\U0010AAAA";
> -  const char32_t exp_literal[] = U"b?\uAAAA\U0010AAAA";
> -  CharT exp[array_size (exp_literal)] = {};
> -  std::copy (begin (exp_literal), end (exp_literal), begin (exp));
> +  const unsigned char input[] = "b?\uAAAA\U0010AAAA";
> +  const char32_t expected[] = U"b?\uAAAA\U0010AAAA";
> +  static_assert (array_size (input) == 11, "");
> +  static_assert (array_size (expected) == 5, "");
>  
> -  static_assert (array_size (valid_in) == 11, "");
> -  static_assert (array_size (exp_literal) == 5, "");
> -  static_assert (array_size (exp) == 5, "");
> -  VERIFY (char_traits<char>::length (valid_in) == 10);
> -  VERIFY (char_traits<char32_t>::length (exp_literal) == 4);
> -  VERIFY (char_traits<CharT>::length (exp) == 4);
> +  ExternT in[array_size (input)];
> +  InternT exp[array_size (expected)];
> +  copy (begin (input), end (input), begin (in));
> +  copy (begin (expected), end (expected), begin (exp));
> +  VERIFY (char_traits<ExternT>::length (in) == 10);
> +  VERIFY (char_traits<InternT>::length (exp) == 4);
>  
> -  test_offsets_error<char> offsets[] = {
> +  test_offsets_error<unsigned char> offsets[] = {
>  
>      // replace leading byte with invalid byte
> -    {1, 4, 0, 0, '\xFF', 0},
> -    {3, 4, 1, 1, '\xFF', 1},
> -    {6, 4, 3, 2, '\xFF', 3},
> -    {10, 4, 6, 3, '\xFF', 6},
> +    {1, 4, 0, 0, 0xFF, 0},
> +    {3, 4, 1, 1, 0xFF, 1},
> +    {6, 4, 3, 2, 0xFF, 3},
> +    {10, 4, 6, 3, 0xFF, 6},
>  
>      // replace first trailing byte with ASCII byte
>      {3, 4, 1, 1, 'z', 2},
> @@ -197,21 +198,21 @@ utf8_to_utf32_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
>      {10, 4, 6, 3, 'z', 7},
>  
>      // replace first trailing byte with invalid byte
> -    {3, 4, 1, 1, '\xFF', 2},
> -    {6, 4, 3, 2, '\xFF', 4},
> -    {10, 4, 6, 3, '\xFF', 7},
> +    {3, 4, 1, 1, 0xFF, 2},
> +    {6, 4, 3, 2, 0xFF, 4},
> +    {10, 4, 6, 3, 0xFF, 7},
>  
>      // replace second trailing byte with ASCII byte
>      {6, 4, 3, 2, 'z', 5},
>      {10, 4, 6, 3, 'z', 8},
>  
>      // replace second trailing byte with invalid byte
> -    {6, 4, 3, 2, '\xFF', 5},
> -    {10, 4, 6, 3, '\xFF', 8},
> +    {6, 4, 3, 2, 0xFF, 5},
> +    {10, 4, 6, 3, 0xFF, 8},
>  
>      // replace third trailing byte
>      {10, 4, 6, 3, 'z', 9},
> -    {10, 4, 6, 3, '\xFF', 9},
> +    {10, 4, 6, 3, 0xFF, 9},
>  
>      // replace first trailing byte with ASCII byte, also incomplete at end
>      {5, 4, 3, 2, 'z', 4},
> @@ -219,30 +220,29 @@ utf8_to_utf32_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
>      {9, 4, 6, 3, 'z', 7},
>  
>      // replace first trailing byte with invalid byte, also incomplete at end
> -    {5, 4, 3, 2, '\xFF', 4},
> -    {8, 4, 6, 3, '\xFF', 7},
> -    {9, 4, 6, 3, '\xFF', 7},
> +    {5, 4, 3, 2, 0xFF, 4},
> +    {8, 4, 6, 3, 0xFF, 7},
> +    {9, 4, 6, 3, 0xFF, 7},
>  
>      // replace second trailing byte with ASCII byte, also incomplete at end
>      {9, 4, 6, 3, 'z', 8},
>  
>      // replace second trailing byte with invalid byte, also incomplete at end
> -    {9, 4, 6, 3, '\xFF', 8},
> +    {9, 4, 6, 3, 0xFF, 8},
>    };
>    for (auto t : offsets)
>      {
> -      char in[array_size (valid_in)] = {};
> -      CharT out[array_size (exp) - 1] = {};
> +      InternT out[array_size (exp) - 1] = {};
>        VERIFY (t.in_size <= array_size (in));
>        VERIFY (t.out_size <= array_size (out));
>        VERIFY (t.expected_in_next <= t.in_size);
>        VERIFY (t.expected_out_next <= t.out_size);
> -      char_traits<char>::copy (in, valid_in, array_size (valid_in));
> +      auto old_char = in[t.replace_pos];
>        in[t.replace_pos] = t.replace_char;
>  
>        auto state = mbstate_t{};
> -      auto in_next = (const char *) nullptr;
> -      auto out_next = (CharT *) nullptr;
> +      auto in_next = (const ExternT *) nullptr;
> +      auto out_next = (InternT *) nullptr;
>        auto res = codecvt_base::result ();
>  
>        res = cvt.in (state, in, in + t.in_size, in_next, out, out + t.out_size,
> @@ -250,48 +250,51 @@ utf8_to_utf32_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
>        VERIFY (res == cvt.error);
>        VERIFY (in_next == in + t.expected_in_next);
>        VERIFY (out_next == out + t.expected_out_next);
> -      VERIFY (char_traits<CharT>::compare (out, exp, t.expected_out_next) == 0);
> +      VERIFY (char_traits<InternT>::compare (out, exp, t.expected_out_next)
> +	      == 0);
>        if (t.expected_out_next < array_size (out))
>  	VERIFY (out[t.expected_out_next] == 0);
> +
> +      in[t.replace_pos] = old_char;
>      }
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -utf8_to_utf32_in (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +utf8_to_utf32_in (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    utf8_to_utf32_in_ok (cvt);
>    utf8_to_utf32_in_partial (cvt);
>    utf8_to_utf32_in_error (cvt);
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -utf32_to_utf8_out_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +utf32_to_utf8_out_ok (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    using namespace std;
>    // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
> -  const char32_t in_literal[] = U"b?\uAAAA\U0010AAAA";
> -  const char exp[] = "b?\uAAAA\U0010AAAA";
> -  CharT in[array_size (in_literal)] = {};
> -  copy (begin (in_literal), end (in_literal), begin (in));
> -
> -  static_assert (array_size (in_literal) == 5, "");
> -  static_assert (array_size (in) == 5, "");
> -  static_assert (array_size (exp) == 11, "");
> -  VERIFY (char_traits<char32_t>::length (in_literal) == 4);
> -  VERIFY (char_traits<CharT>::length (in) == 4);
> -  VERIFY (char_traits<char>::length (exp) == 10);
> +  const char32_t input[] = U"b?\uAAAA\U0010AAAA";
> +  const unsigned char expected[] = "b?\uAAAA\U0010AAAA";
> +  static_assert (array_size (input) == 5, "");
> +  static_assert (array_size (expected) == 11, "");
> +
> +  InternT in[array_size (input)];
> +  ExternT exp[array_size (expected)];
> +  copy (begin (input), end (input), begin (in));
> +  copy (begin (expected), end (expected), begin (exp));
> +  VERIFY (char_traits<InternT>::length (in) == 4);
> +  VERIFY (char_traits<ExternT>::length (exp) == 10);
>  
>    const test_offsets_ok offsets[] = {{0, 0}, {1, 1}, {2, 3}, {3, 6}, {4, 10}};
>    for (auto t : offsets)
>      {
> -      char out[array_size (exp) - 1] = {};
> +      ExternT out[array_size (exp) - 1] = {};
>        VERIFY (t.in_size <= array_size (in));
>        VERIFY (t.out_size <= array_size (out));
>        auto state = mbstate_t{};
> -      auto in_next = (const CharT *) nullptr;
> -      auto out_next = (char *) nullptr;
> +      auto in_next = (const InternT *) nullptr;
> +      auto out_next = (ExternT *) nullptr;
>        auto res = codecvt_base::result ();
>  
>        res = cvt.out (state, in, in + t.in_size, in_next, out, out + t.out_size,
> @@ -299,29 +302,29 @@ utf32_to_utf8_out_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
>        VERIFY (res == cvt.ok);
>        VERIFY (in_next == in + t.in_size);
>        VERIFY (out_next == out + t.out_size);
> -      VERIFY (char_traits<char>::compare (out, exp, t.out_size) == 0);
> +      VERIFY (char_traits<ExternT>::compare (out, exp, t.out_size) == 0);
>        if (t.out_size < array_size (out))
>  	VERIFY (out[t.out_size] == 0);
>      }
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -utf32_to_utf8_out_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +utf32_to_utf8_out_partial (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    using namespace std;
>    // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
> -  const char32_t in_literal[] = U"b?\uAAAA\U0010AAAA";
> -  const char exp[] = "b?\uAAAA\U0010AAAA";
> -  CharT in[array_size (in_literal)] = {};
> -  copy (begin (in_literal), end (in_literal), begin (in));
> -
> -  static_assert (array_size (in_literal) == 5, "");
> -  static_assert (array_size (in) == 5, "");
> -  static_assert (array_size (exp) == 11, "");
> -  VERIFY (char_traits<char32_t>::length (in_literal) == 4);
> -  VERIFY (char_traits<CharT>::length (in) == 4);
> -  VERIFY (char_traits<char>::length (exp) == 10);
> +  const char32_t input[] = U"b?\uAAAA\U0010AAAA";
> +  const unsigned char expected[] = "b?\uAAAA\U0010AAAA";
> +  static_assert (array_size (input) == 5, "");
> +  static_assert (array_size (expected) == 11, "");
> +
> +  InternT in[array_size (input)];
> +  ExternT exp[array_size (expected)];
> +  copy (begin (input), end (input), begin (in));
> +  copy (begin (expected), end (expected), begin (exp));
> +  VERIFY (char_traits<InternT>::length (in) == 4);
> +  VERIFY (char_traits<ExternT>::length (exp) == 10);
>  
>    const test_offsets_partial offsets[] = {
>      {1, 0, 0, 0}, // no space for first CP
> @@ -340,14 +343,14 @@ utf32_to_utf8_out_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
>    };
>    for (auto t : offsets)
>      {
> -      char out[array_size (exp) - 1] = {};
> +      ExternT out[array_size (exp) - 1] = {};
>        VERIFY (t.in_size <= array_size (in));
>        VERIFY (t.out_size <= array_size (out));
>        VERIFY (t.expected_in_next <= t.in_size);
>        VERIFY (t.expected_out_next <= t.out_size);
>        auto state = mbstate_t{};
> -      auto in_next = (const CharT *) nullptr;
> -      auto out_next = (char *) nullptr;
> +      auto in_next = (const InternT *) nullptr;
> +      auto out_next = (ExternT *) nullptr;
>        auto res = codecvt_base::result ();
>  
>        res = cvt.out (state, in, in + t.in_size, in_next, out, out + t.out_size,
> @@ -355,44 +358,49 @@ utf32_to_utf8_out_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
>        VERIFY (res == cvt.partial);
>        VERIFY (in_next == in + t.expected_in_next);
>        VERIFY (out_next == out + t.expected_out_next);
> -      VERIFY (char_traits<char>::compare (out, exp, t.expected_out_next) == 0);
> +      VERIFY (char_traits<ExternT>::compare (out, exp, t.expected_out_next)
> +	      == 0);
>        if (t.expected_out_next < array_size (out))
>  	VERIFY (out[t.expected_out_next] == 0);
>      }
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -utf32_to_utf8_out_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +utf32_to_utf8_out_error (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    using namespace std;
> -  const char32_t valid_in[] = U"b?\uAAAA\U0010AAAA";
> -  const char exp[] = "b?\uAAAA\U0010AAAA";
> -
> -  static_assert (array_size (valid_in) == 5, "");
> -  static_assert (array_size (exp) == 11, "");
> -  VERIFY (char_traits<char32_t>::length (valid_in) == 4);
> -  VERIFY (char_traits<char>::length (exp) == 10);
> -
> -  test_offsets_error<CharT> offsets[] = {{4, 10, 0, 0, 0x00110000, 0},
> -					 {4, 10, 1, 1, 0x00110000, 1},
> -					 {4, 10, 2, 3, 0x00110000, 2},
> -					 {4, 10, 3, 6, 0x00110000, 3}};
> +  // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
> +  const char32_t input[] = U"b?\uAAAA\U0010AAAA";
> +  const unsigned char expected[] = "b?\uAAAA\U0010AAAA";
> +  static_assert (array_size (input) == 5, "");
> +  static_assert (array_size (expected) == 11, "");
> +
> +  InternT in[array_size (input)];
> +  ExternT exp[array_size (expected)];
> +  copy (begin (input), end (input), begin (in));
> +  copy (begin (expected), end (expected), begin (exp));
> +  VERIFY (char_traits<InternT>::length (in) == 4);
> +  VERIFY (char_traits<ExternT>::length (exp) == 10);
> +
> +  test_offsets_error<InternT> offsets[] = {{4, 10, 0, 0, 0x00110000, 0},
> +					   {4, 10, 1, 1, 0x00110000, 1},
> +					   {4, 10, 2, 3, 0x00110000, 2},
> +					   {4, 10, 3, 6, 0x00110000, 3}};
>  
>    for (auto t : offsets)
>      {
> -      CharT in[array_size (valid_in)] = {};
> -      char out[array_size (exp) - 1] = {};
> +      ExternT out[array_size (exp) - 1] = {};
>        VERIFY (t.in_size <= array_size (in));
>        VERIFY (t.out_size <= array_size (out));
>        VERIFY (t.expected_in_next <= t.in_size);
>        VERIFY (t.expected_out_next <= t.out_size);
> -      copy (begin (valid_in), end (valid_in), begin (in));
> +      auto old_char = in[t.replace_pos];
>        in[t.replace_pos] = t.replace_char;
>  
>        auto state = mbstate_t{};
> -      auto in_next = (const CharT *) nullptr;
> -      auto out_next = (char *) nullptr;
> +      auto in_next = (const InternT *) nullptr;
> +      auto out_next = (ExternT *) nullptr;
>        auto res = codecvt_base::result ();
>  
>        res = cvt.out (state, in, in + t.in_size, in_next, out, out + t.out_size,
> @@ -400,56 +408,59 @@ utf32_to_utf8_out_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
>        VERIFY (res == cvt.error);
>        VERIFY (in_next == in + t.expected_in_next);
>        VERIFY (out_next == out + t.expected_out_next);
> -      VERIFY (char_traits<char>::compare (out, exp, t.expected_out_next) == 0);
> +      VERIFY (char_traits<ExternT>::compare (out, exp, t.expected_out_next)
> +	      == 0);
>        if (t.expected_out_next < array_size (out))
>  	VERIFY (out[t.expected_out_next] == 0);
> +
> +      in[t.replace_pos] = old_char;
>      }
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -utf32_to_utf8_out (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +utf32_to_utf8_out (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    utf32_to_utf8_out_ok (cvt);
>    utf32_to_utf8_out_partial (cvt);
>    utf32_to_utf8_out_error (cvt);
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -test_utf8_utf32_codecvts (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +test_utf8_utf32_cvt (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    utf8_to_utf32_in (cvt);
>    utf32_to_utf8_out (cvt);
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -utf8_to_utf16_in_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +utf8_to_utf16_in_ok (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    using namespace std;
>    // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
> -  const char in[] = "b?\uAAAA\U0010AAAA";
> -  const char16_t exp_literal[] = u"b?\uAAAA\U0010AAAA";
> -  CharT exp[array_size (exp_literal)] = {};
> -  copy (begin (exp_literal), end (exp_literal), begin (exp));
> -
> -  static_assert (array_size (in) == 11, "");
> -  static_assert (array_size (exp_literal) == 6, "");
> -  static_assert (array_size (exp) == 6, "");
> -  VERIFY (char_traits<char>::length (in) == 10);
> -  VERIFY (char_traits<char16_t>::length (exp_literal) == 5);
> -  VERIFY (char_traits<CharT>::length (exp) == 5);
> +  const unsigned char input[] = "b?\uAAAA\U0010AAAA";
> +  const char16_t expected[] = u"b?\uAAAA\U0010AAAA";
> +  static_assert (array_size (input) == 11, "");
> +  static_assert (array_size (expected) == 6, "");
> +
> +  ExternT in[array_size (input)];
> +  InternT exp[array_size (expected)];
> +  copy (begin (input), end (input), begin (in));
> +  copy (begin (expected), end (expected), begin (exp));
> +  VERIFY (char_traits<ExternT>::length (in) == 10);
> +  VERIFY (char_traits<InternT>::length (exp) == 5);
>  
>    test_offsets_ok offsets[] = {{0, 0}, {1, 1}, {3, 2}, {6, 3}, {10, 5}};
>    for (auto t : offsets)
>      {
> -      CharT out[array_size (exp) - 1] = {};
> +      InternT out[array_size (exp) - 1] = {};
>        VERIFY (t.in_size <= array_size (in));
>        VERIFY (t.out_size <= array_size (out));
>        auto state = mbstate_t{};
> -      auto in_next = (const char *) nullptr;
> -      auto out_next = (CharT *) nullptr;
> +      auto in_next = (const ExternT *) nullptr;
> +      auto out_next = (InternT *) nullptr;
>        auto res = codecvt_base::result ();
>  
>        res = cvt.in (state, in, in + t.in_size, in_next, out, out + t.out_size,
> @@ -457,19 +468,19 @@ utf8_to_utf16_in_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
>        VERIFY (res == cvt.ok);
>        VERIFY (in_next == in + t.in_size);
>        VERIFY (out_next == out + t.out_size);
> -      VERIFY (char_traits<CharT>::compare (out, exp, t.out_size) == 0);
> +      VERIFY (char_traits<InternT>::compare (out, exp, t.out_size) == 0);
>        if (t.out_size < array_size (out))
>  	VERIFY (out[t.out_size] == 0);
>      }
>  
>    for (auto t : offsets)
>      {
> -      CharT out[array_size (exp)] = {};
> +      InternT out[array_size (exp)] = {};
>        VERIFY (t.in_size <= array_size (in));
>        VERIFY (t.out_size <= array_size (out));
>        auto state = mbstate_t{};
> -      auto in_next = (const char *) nullptr;
> -      auto out_next = (CharT *) nullptr;
> +      auto in_next = (const ExternT *) nullptr;
> +      auto out_next = (InternT *) nullptr;
>        auto res = codecvt_base::result ();
>  
>        res
> @@ -477,29 +488,29 @@ utf8_to_utf16_in_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
>        VERIFY (res == cvt.ok);
>        VERIFY (in_next == in + t.in_size);
>        VERIFY (out_next == out + t.out_size);
> -      VERIFY (char_traits<CharT>::compare (out, exp, t.out_size) == 0);
> +      VERIFY (char_traits<InternT>::compare (out, exp, t.out_size) == 0);
>        if (t.out_size < array_size (out))
>  	VERIFY (out[t.out_size] == 0);
>      }
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -utf8_to_utf16_in_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +utf8_to_utf16_in_partial (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    using namespace std;
>    // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
> -  const char in[] = "b?\uAAAA\U0010AAAA";
> -  const char16_t exp_literal[] = u"b?\uAAAA\U0010AAAA";
> -  CharT exp[array_size (exp_literal)] = {};
> -  copy (begin (exp_literal), end (exp_literal), begin (exp));
> -
> -  static_assert (array_size (in) == 11, "");
> -  static_assert (array_size (exp_literal) == 6, "");
> -  static_assert (array_size (exp) == 6, "");
> -  VERIFY (char_traits<char>::length (in) == 10);
> -  VERIFY (char_traits<char16_t>::length (exp_literal) == 5);
> -  VERIFY (char_traits<CharT>::length (exp) == 5);
> +  const unsigned char input[] = "b?\uAAAA\U0010AAAA";
> +  const char16_t expected[] = u"b?\uAAAA\U0010AAAA";
> +  static_assert (array_size (input) == 11, "");
> +  static_assert (array_size (expected) == 6, "");
> +
> +  ExternT in[array_size (input)];
> +  InternT exp[array_size (expected)];
> +  copy (begin (input), end (input), begin (in));
> +  copy (begin (expected), end (expected), begin (exp));
> +  VERIFY (char_traits<ExternT>::length (in) == 10);
> +  VERIFY (char_traits<InternT>::length (exp) == 5);
>  
>    test_offsets_partial offsets[] = {
>      {1, 0, 0, 0}, // no space for first CP
> @@ -530,14 +541,14 @@ utf8_to_utf16_in_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
>  
>    for (auto t : offsets)
>      {
> -      CharT out[array_size (exp) - 1] = {};
> +      InternT out[array_size (exp) - 1] = {};
>        VERIFY (t.in_size <= array_size (in));
>        VERIFY (t.out_size <= array_size (out));
>        VERIFY (t.expected_in_next <= t.in_size);
>        VERIFY (t.expected_out_next <= t.out_size);
>        auto state = mbstate_t{};
> -      auto in_next = (const char *) nullptr;
> -      auto out_next = (CharT *) nullptr;
> +      auto in_next = (const ExternT *) nullptr;
> +      auto out_next = (InternT *) nullptr;
>        auto res = codecvt_base::result ();
>  
>        res = cvt.in (state, in, in + t.in_size, in_next, out, out + t.out_size,
> @@ -545,36 +556,38 @@ utf8_to_utf16_in_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
>        VERIFY (res == cvt.partial);
>        VERIFY (in_next == in + t.expected_in_next);
>        VERIFY (out_next == out + t.expected_out_next);
> -      VERIFY (char_traits<CharT>::compare (out, exp, t.expected_out_next) == 0);
> +      VERIFY (char_traits<InternT>::compare (out, exp, t.expected_out_next)
> +	      == 0);
>        if (t.expected_out_next < array_size (out))
>  	VERIFY (out[t.expected_out_next] == 0);
>      }
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -utf8_to_utf16_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +utf8_to_utf16_in_error (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    using namespace std;
> -  const char valid_in[] = "b?\uAAAA\U0010AAAA";
> -  const char16_t exp_literal[] = u"b?\uAAAA\U0010AAAA";
> -  CharT exp[array_size (exp_literal)] = {};
> -  copy (begin (exp_literal), end (exp_literal), begin (exp));
> +  // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
> +  const unsigned char input[] = "b?\uAAAA\U0010AAAA";
> +  const char16_t expected[] = u"b?\uAAAA\U0010AAAA";
> +  static_assert (array_size (input) == 11, "");
> +  static_assert (array_size (expected) == 6, "");
>  
> -  static_assert (array_size (valid_in) == 11, "");
> -  static_assert (array_size (exp_literal) == 6, "");
> -  static_assert (array_size (exp) == 6, "");
> -  VERIFY (char_traits<char>::length (valid_in) == 10);
> -  VERIFY (char_traits<char16_t>::length (exp_literal) == 5);
> -  VERIFY (char_traits<CharT>::length (exp) == 5);
> +  ExternT in[array_size (input)];
> +  InternT exp[array_size (expected)];
> +  copy (begin (input), end (input), begin (in));
> +  copy (begin (expected), end (expected), begin (exp));
> +  VERIFY (char_traits<ExternT>::length (in) == 10);
> +  VERIFY (char_traits<InternT>::length (exp) == 5);
>  
> -  test_offsets_error<char> offsets[] = {
> +  test_offsets_error<unsigned char> offsets[] = {
>  
>      // replace leading byte with invalid byte
> -    {1, 5, 0, 0, '\xFF', 0},
> -    {3, 5, 1, 1, '\xFF', 1},
> -    {6, 5, 3, 2, '\xFF', 3},
> -    {10, 5, 6, 3, '\xFF', 6},
> +    {1, 5, 0, 0, 0xFF, 0},
> +    {3, 5, 1, 1, 0xFF, 1},
> +    {6, 5, 3, 2, 0xFF, 3},
> +    {10, 5, 6, 3, 0xFF, 6},
>  
>      // replace first trailing byte with ASCII byte
>      {3, 5, 1, 1, 'z', 2},
> @@ -582,21 +595,21 @@ utf8_to_utf16_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
>      {10, 5, 6, 3, 'z', 7},
>  
>      // replace first trailing byte with invalid byte
> -    {3, 5, 1, 1, '\xFF', 2},
> -    {6, 5, 3, 2, '\xFF', 4},
> -    {10, 5, 6, 3, '\xFF', 7},
> +    {3, 5, 1, 1, 0xFF, 2},
> +    {6, 5, 3, 2, 0xFF, 4},
> +    {10, 5, 6, 3, 0xFF, 7},
>  
>      // replace second trailing byte with ASCII byte
>      {6, 5, 3, 2, 'z', 5},
>      {10, 5, 6, 3, 'z', 8},
>  
>      // replace second trailing byte with invalid byte
> -    {6, 5, 3, 2, '\xFF', 5},
> -    {10, 5, 6, 3, '\xFF', 8},
> +    {6, 5, 3, 2, 0xFF, 5},
> +    {10, 5, 6, 3, 0xFF, 8},
>  
>      // replace third trailing byte
>      {10, 5, 6, 3, 'z', 9},
> -    {10, 5, 6, 3, '\xFF', 9},
> +    {10, 5, 6, 3, 0xFF, 9},
>  
>      // replace first trailing byte with ASCII byte, also incomplete at end
>      {5, 5, 3, 2, 'z', 4},
> @@ -604,30 +617,29 @@ utf8_to_utf16_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
>      {9, 5, 6, 3, 'z', 7},
>  
>      // replace first trailing byte with invalid byte, also incomplete at end
> -    {5, 5, 3, 2, '\xFF', 4},
> -    {8, 5, 6, 3, '\xFF', 7},
> -    {9, 5, 6, 3, '\xFF', 7},
> +    {5, 5, 3, 2, 0xFF, 4},
> +    {8, 5, 6, 3, 0xFF, 7},
> +    {9, 5, 6, 3, 0xFF, 7},
>  
>      // replace second trailing byte with ASCII byte, also incomplete at end
>      {9, 5, 6, 3, 'z', 8},
>  
>      // replace second trailing byte with invalid byte, also incomplete at end
> -    {9, 5, 6, 3, '\xFF', 8},
> +    {9, 5, 6, 3, 0xFF, 8},
>    };
>    for (auto t : offsets)
>      {
> -      char in[array_size (valid_in)] = {};
> -      CharT out[array_size (exp) - 1] = {};
> +      InternT out[array_size (exp) - 1] = {};
>        VERIFY (t.in_size <= array_size (in));
>        VERIFY (t.out_size <= array_size (out));
>        VERIFY (t.expected_in_next <= t.in_size);
>        VERIFY (t.expected_out_next <= t.out_size);
> -      char_traits<char>::copy (in, valid_in, array_size (valid_in));
> +      auto old_char = in[t.replace_pos];
>        in[t.replace_pos] = t.replace_char;
>  
>        auto state = mbstate_t{};
> -      auto in_next = (const char *) nullptr;
> -      auto out_next = (CharT *) nullptr;
> +      auto in_next = (const ExternT *) nullptr;
> +      auto out_next = (InternT *) nullptr;
>        auto res = codecvt_base::result ();
>  
>        res = cvt.in (state, in, in + t.in_size, in_next, out, out + t.out_size,
> @@ -635,48 +647,51 @@ utf8_to_utf16_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
>        VERIFY (res == cvt.error);
>        VERIFY (in_next == in + t.expected_in_next);
>        VERIFY (out_next == out + t.expected_out_next);
> -      VERIFY (char_traits<CharT>::compare (out, exp, t.expected_out_next) == 0);
> +      VERIFY (char_traits<InternT>::compare (out, exp, t.expected_out_next)
> +	      == 0);
>        if (t.expected_out_next < array_size (out))
>  	VERIFY (out[t.expected_out_next] == 0);
> +
> +      in[t.replace_pos] = old_char;
>      }
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -utf8_to_utf16_in (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +utf8_to_utf16_in (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    utf8_to_utf16_in_ok (cvt);
>    utf8_to_utf16_in_partial (cvt);
>    utf8_to_utf16_in_error (cvt);
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -utf16_to_utf8_out_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +utf16_to_utf8_out_ok (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    using namespace std;
>    // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
> -  const char16_t in_literal[] = u"b?\uAAAA\U0010AAAA";
> -  const char exp[] = "b?\uAAAA\U0010AAAA";
> -  CharT in[array_size (in_literal)];
> -  copy (begin (in_literal), end (in_literal), begin (in));
> -
> -  static_assert (array_size (in_literal) == 6, "");
> -  static_assert (array_size (exp) == 11, "");
> -  static_assert (array_size (in) == 6, "");
> -  VERIFY (char_traits<char16_t>::length (in_literal) == 5);
> -  VERIFY (char_traits<char>::length (exp) == 10);
> -  VERIFY (char_traits<CharT>::length (in) == 5);
> +  const char16_t input[] = u"b?\uAAAA\U0010AAAA";
> +  const unsigned char expected[] = "b?\uAAAA\U0010AAAA";
> +  static_assert (array_size (input) == 6, "");
> +  static_assert (array_size (expected) == 11, "");
> +
> +  InternT in[array_size (input)];
> +  ExternT exp[array_size (expected)];
> +  copy (begin (input), end (input), begin (in));
> +  copy (begin (expected), end (expected), begin (exp));
> +  VERIFY (char_traits<InternT>::length (in) == 5);
> +  VERIFY (char_traits<ExternT>::length (exp) == 10);
>  
>    const test_offsets_ok offsets[] = {{0, 0}, {1, 1}, {2, 3}, {3, 6}, {5, 10}};
>    for (auto t : offsets)
>      {
> -      char out[array_size (exp) - 1] = {};
> +      ExternT out[array_size (exp) - 1] = {};
>        VERIFY (t.in_size <= array_size (in));
>        VERIFY (t.out_size <= array_size (out));
>        auto state = mbstate_t{};
> -      auto in_next = (const CharT *) nullptr;
> -      auto out_next = (char *) nullptr;
> +      auto in_next = (const InternT *) nullptr;
> +      auto out_next = (ExternT *) nullptr;
>        auto res = codecvt_base::result ();
>  
>        res = cvt.out (state, in, in + t.in_size, in_next, out, out + t.out_size,
> @@ -684,29 +699,29 @@ utf16_to_utf8_out_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
>        VERIFY (res == cvt.ok);
>        VERIFY (in_next == in + t.in_size);
>        VERIFY (out_next == out + t.out_size);
> -      VERIFY (char_traits<char>::compare (out, exp, t.out_size) == 0);
> +      VERIFY (char_traits<ExternT>::compare (out, exp, t.out_size) == 0);
>        if (t.out_size < array_size (out))
>  	VERIFY (out[t.out_size] == 0);
>      }
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -utf16_to_utf8_out_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +utf16_to_utf8_out_partial (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    using namespace std;
>    // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
> -  const char16_t in_literal[] = u"b?\uAAAA\U0010AAAA";
> -  const char exp[] = "b?\uAAAA\U0010AAAA";
> -  CharT in[array_size (in_literal)];
> -  copy (begin (in_literal), end (in_literal), begin (in));
> -
> -  static_assert (array_size (in_literal) == 6, "");
> -  static_assert (array_size (exp) == 11, "");
> -  static_assert (array_size (in) == 6, "");
> -  VERIFY (char_traits<char16_t>::length (in_literal) == 5);
> -  VERIFY (char_traits<char>::length (exp) == 10);
> -  VERIFY (char_traits<CharT>::length (in) == 5);
> +  const char16_t input[] = u"b?\uAAAA\U0010AAAA";
> +  const unsigned char expected[] = "b?\uAAAA\U0010AAAA";
> +  static_assert (array_size (input) == 6, "");
> +  static_assert (array_size (expected) == 11, "");
> +
> +  InternT in[array_size (input)];
> +  ExternT exp[array_size (expected)];
> +  copy (begin (input), end (input), begin (in));
> +  copy (begin (expected), end (expected), begin (exp));
> +  VERIFY (char_traits<InternT>::length (in) == 5);
> +  VERIFY (char_traits<ExternT>::length (exp) == 10);
>  
>    const test_offsets_partial offsets[] = {
>      {1, 0, 0, 0}, // no space for first CP
> @@ -732,14 +747,14 @@ utf16_to_utf8_out_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
>    };
>    for (auto t : offsets)
>      {
> -      char out[array_size (exp) - 1] = {};
> +      ExternT out[array_size (exp) - 1] = {};
>        VERIFY (t.in_size <= array_size (in));
>        VERIFY (t.out_size <= array_size (out));
>        VERIFY (t.expected_in_next <= t.in_size);
>        VERIFY (t.expected_out_next <= t.out_size);
>        auto state = mbstate_t{};
> -      auto in_next = (const CharT *) nullptr;
> -      auto out_next = (char *) nullptr;
> +      auto in_next = (const InternT *) nullptr;
> +      auto out_next = (ExternT *) nullptr;
>        auto res = codecvt_base::result ();
>  
>        res = cvt.out (state, in, in + t.in_size, in_next, out, out + t.out_size,
> @@ -747,26 +762,32 @@ utf16_to_utf8_out_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
>        VERIFY (res == cvt.partial);
>        VERIFY (in_next == in + t.expected_in_next);
>        VERIFY (out_next == out + t.expected_out_next);
> -      VERIFY (char_traits<char>::compare (out, exp, t.expected_out_next) == 0);
> +      VERIFY (char_traits<ExternT>::compare (out, exp, t.expected_out_next)
> +	      == 0);
>        if (t.expected_out_next < array_size (out))
>  	VERIFY (out[t.expected_out_next] == 0);
>      }
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -utf16_to_utf8_out_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +utf16_to_utf8_out_error (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    using namespace std;
> -  const char16_t valid_in[] = u"b?\uAAAA\U0010AAAA";
> -  const char exp[] = "b?\uAAAA\U0010AAAA";
> -
> -  static_assert (array_size (valid_in) == 6, "");
> -  static_assert (array_size (exp) == 11, "");
> -  VERIFY (char_traits<char16_t>::length (valid_in) == 5);
> -  VERIFY (char_traits<char>::length (exp) == 10);
> -
> -  test_offsets_error<CharT> offsets[] = {
> +  // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
> +  const char16_t input[] = u"b?\uAAAA\U0010AAAA";
> +  const unsigned char expected[] = "b?\uAAAA\U0010AAAA";
> +  static_assert (array_size (input) == 6, "");
> +  static_assert (array_size (expected) == 11, "");
> +
> +  InternT in[array_size (input)];
> +  ExternT exp[array_size (expected)];
> +  copy (begin (input), end (input), begin (in));
> +  copy (begin (expected), end (expected), begin (exp));
> +  VERIFY (char_traits<InternT>::length (in) == 5);
> +  VERIFY (char_traits<ExternT>::length (exp) == 10);
> +
> +  test_offsets_error<InternT> offsets[] = {
>      {5, 10, 0, 0, 0xD800, 0},
>      {5, 10, 0, 0, 0xDBFF, 0},
>      {5, 10, 0, 0, 0xDC00, 0},
> @@ -796,18 +817,17 @@ utf16_to_utf8_out_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
>  
>    for (auto t : offsets)
>      {
> -      CharT in[array_size (valid_in)] = {};
> -      char out[array_size (exp) - 1] = {};
> +      ExternT out[array_size (exp) - 1] = {};
>        VERIFY (t.in_size <= array_size (in));
>        VERIFY (t.out_size <= array_size (out));
>        VERIFY (t.expected_in_next <= t.in_size);
>        VERIFY (t.expected_out_next <= t.out_size);
> -      copy (begin (valid_in), end (valid_in), begin (in));
> +      auto old_char = in[t.replace_pos];
>        in[t.replace_pos] = t.replace_char;
>  
>        auto state = mbstate_t{};
> -      auto in_next = (const CharT *) nullptr;
> -      auto out_next = (char *) nullptr;
> +      auto in_next = (const InternT *) nullptr;
> +      auto out_next = (ExternT *) nullptr;
>        auto res = codecvt_base::result ();
>  
>        res = cvt.out (state, in, in + t.in_size, in_next, out, out + t.out_size,
> @@ -815,56 +835,59 @@ utf16_to_utf8_out_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
>        VERIFY (res == cvt.error);
>        VERIFY (in_next == in + t.expected_in_next);
>        VERIFY (out_next == out + t.expected_out_next);
> -      VERIFY (char_traits<char>::compare (out, exp, t.expected_out_next) == 0);
> +      VERIFY (char_traits<ExternT>::compare (out, exp, t.expected_out_next)
> +	      == 0);
>        if (t.expected_out_next < array_size (out))
>  	VERIFY (out[t.expected_out_next] == 0);
> +
> +      in[t.replace_pos] = old_char;
>      }
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -utf16_to_utf8_out (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +utf16_to_utf8_out (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    utf16_to_utf8_out_ok (cvt);
>    utf16_to_utf8_out_partial (cvt);
>    utf16_to_utf8_out_error (cvt);
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -test_utf8_utf16_cvts (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +test_utf8_utf16_cvt (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    utf8_to_utf16_in (cvt);
>    utf16_to_utf8_out (cvt);
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -utf8_to_ucs2_in_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +utf8_to_ucs2_in_ok (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    using namespace std;
>    // UTF-8 string of 1-byte CP, 2-byte CP and 3-byte CP
> -  const char in[] = "b?\uAAAA";
> -  const char16_t exp_literal[] = u"b?\uAAAA";
> -  CharT exp[array_size (exp_literal)] = {};
> -  copy (begin (exp_literal), end (exp_literal), begin (exp));
> -
> -  static_assert (array_size (in) == 7, "");
> -  static_assert (array_size (exp_literal) == 4, "");
> -  static_assert (array_size (exp) == 4, "");
> -  VERIFY (char_traits<char>::length (in) == 6);
> -  VERIFY (char_traits<char16_t>::length (exp_literal) == 3);
> -  VERIFY (char_traits<CharT>::length (exp) == 3);
> +  const unsigned char input[] = "b?\uAAAA";
> +  const char16_t expected[] = u"b?\uAAAA";
> +  static_assert (array_size (input) == 7, "");
> +  static_assert (array_size (expected) == 4, "");
> +
> +  ExternT in[array_size (input)];
> +  InternT exp[array_size (expected)];
> +  copy (begin (input), end (input), begin (in));
> +  copy (begin (expected), end (expected), begin (exp));
> +  VERIFY (char_traits<ExternT>::length (in) == 6);
> +  VERIFY (char_traits<InternT>::length (exp) == 3);
>  
>    test_offsets_ok offsets[] = {{0, 0}, {1, 1}, {3, 2}, {6, 3}};
>    for (auto t : offsets)
>      {
> -      CharT out[array_size (exp) - 1] = {};
> +      InternT out[array_size (exp) - 1] = {};
>        VERIFY (t.in_size <= array_size (in));
>        VERIFY (t.out_size <= array_size (out));
>        auto state = mbstate_t{};
> -      auto in_next = (const char *) nullptr;
> -      auto out_next = (CharT *) nullptr;
> +      auto in_next = (const ExternT *) nullptr;
> +      auto out_next = (InternT *) nullptr;
>        auto res = codecvt_base::result ();
>  
>        res = cvt.in (state, in, in + t.in_size, in_next, out, out + t.out_size,
> @@ -872,19 +895,19 @@ utf8_to_ucs2_in_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
>        VERIFY (res == cvt.ok);
>        VERIFY (in_next == in + t.in_size);
>        VERIFY (out_next == out + t.out_size);
> -      VERIFY (char_traits<CharT>::compare (out, exp, t.out_size) == 0);
> +      VERIFY (char_traits<InternT>::compare (out, exp, t.out_size) == 0);
>        if (t.out_size < array_size (out))
>  	VERIFY (out[t.out_size] == 0);
>      }
>  
>    for (auto t : offsets)
>      {
> -      CharT out[array_size (exp)] = {};
> +      InternT out[array_size (exp)] = {};
>        VERIFY (t.in_size <= array_size (in));
>        VERIFY (t.out_size <= array_size (out));
>        auto state = mbstate_t{};
> -      auto in_next = (const char *) nullptr;
> -      auto out_next = (CharT *) nullptr;
> +      auto in_next = (const ExternT *) nullptr;
> +      auto out_next = (InternT *) nullptr;
>        auto res = codecvt_base::result ();
>  
>        res
> @@ -892,29 +915,29 @@ utf8_to_ucs2_in_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
>        VERIFY (res == cvt.ok);
>        VERIFY (in_next == in + t.in_size);
>        VERIFY (out_next == out + t.out_size);
> -      VERIFY (char_traits<CharT>::compare (out, exp, t.out_size) == 0);
> +      VERIFY (char_traits<InternT>::compare (out, exp, t.out_size) == 0);
>        if (t.out_size < array_size (out))
>  	VERIFY (out[t.out_size] == 0);
>      }
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -utf8_to_ucs2_in_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +utf8_to_ucs2_in_partial (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    using namespace std;
>    // UTF-8 string of 1-byte CP, 2-byte CP and 3-byte CP
> -  const char in[] = "b?\uAAAA";
> -  const char16_t exp_literal[] = u"b?\uAAAA";
> -  CharT exp[array_size (exp_literal)] = {};
> -  copy (begin (exp_literal), end (exp_literal), begin (exp));
> -
> -  static_assert (array_size (in) == 7, "");
> -  static_assert (array_size (exp_literal) == 4, "");
> -  static_assert (array_size (exp) == 4, "");
> -  VERIFY (char_traits<char>::length (in) == 6);
> -  VERIFY (char_traits<char16_t>::length (exp_literal) == 3);
> -  VERIFY (char_traits<CharT>::length (exp) == 3);
> +  const unsigned char input[] = "b?\uAAAA";
> +  const char16_t expected[] = u"b?\uAAAA";
> +  static_assert (array_size (input) == 7, "");
> +  static_assert (array_size (expected) == 4, "");
> +
> +  ExternT in[array_size (input)];
> +  InternT exp[array_size (expected)];
> +  copy (begin (input), end (input), begin (in));
> +  copy (begin (expected), end (expected), begin (exp));
> +  VERIFY (char_traits<ExternT>::length (in) == 6);
> +  VERIFY (char_traits<InternT>::length (exp) == 3);
>  
>    test_offsets_partial offsets[] = {
>      {1, 0, 0, 0}, // no space for first CP
> @@ -932,14 +955,14 @@ utf8_to_ucs2_in_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
>  
>    for (auto t : offsets)
>      {
> -      CharT out[array_size (exp) - 1] = {};
> +      InternT out[array_size (exp) - 1] = {};
>        VERIFY (t.in_size <= array_size (in));
>        VERIFY (t.out_size <= array_size (out));
>        VERIFY (t.expected_in_next <= t.in_size);
>        VERIFY (t.expected_out_next <= t.out_size);
>        auto state = mbstate_t{};
> -      auto in_next = (const char *) nullptr;
> -      auto out_next = (CharT *) nullptr;
> +      auto in_next = (const ExternT *) nullptr;
> +      auto out_next = (InternT *) nullptr;
>        auto res = codecvt_base::result ();
>  
>        res = cvt.in (state, in, in + t.in_size, in_next, out, out + t.out_size,
> @@ -947,36 +970,37 @@ utf8_to_ucs2_in_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
>        VERIFY (res == cvt.partial);
>        VERIFY (in_next == in + t.expected_in_next);
>        VERIFY (out_next == out + t.expected_out_next);
> -      VERIFY (char_traits<CharT>::compare (out, exp, t.expected_out_next) == 0);
> +      VERIFY (char_traits<InternT>::compare (out, exp, t.expected_out_next)
> +	      == 0);
>        if (t.expected_out_next < array_size (out))
>  	VERIFY (out[t.expected_out_next] == 0);
>      }
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -utf8_to_ucs2_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +utf8_to_ucs2_in_error (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    using namespace std;
> -  const char valid_in[] = "b?\uAAAA\U0010AAAA";
> -  const char16_t exp_literal[] = u"b?\uAAAA\U0010AAAA";
> -  CharT exp[array_size (exp_literal)] = {};
> -  copy (begin (exp_literal), end (exp_literal), begin (exp));
> +  const unsigned char input[] = "b?\uAAAA\U0010AAAA";
> +  const char16_t expected[] = u"b?\uAAAA\U0010AAAA";
> +  static_assert (array_size (input) == 11, "");
> +  static_assert (array_size (expected) == 6, "");
>  
> -  static_assert (array_size (valid_in) == 11, "");
> -  static_assert (array_size (exp_literal) == 6, "");
> -  static_assert (array_size (exp) == 6, "");
> -  VERIFY (char_traits<char>::length (valid_in) == 10);
> -  VERIFY (char_traits<char16_t>::length (exp_literal) == 5);
> -  VERIFY (char_traits<CharT>::length (exp) == 5);
> +  ExternT in[array_size (input)];
> +  InternT exp[array_size (expected)];
> +  copy (begin (input), end (input), begin (in));
> +  copy (begin (expected), end (expected), begin (exp));
> +  VERIFY (char_traits<ExternT>::length (in) == 10);
> +  VERIFY (char_traits<InternT>::length (exp) == 5);
>  
> -  test_offsets_error<char> offsets[] = {
> +  test_offsets_error<unsigned char> offsets[] = {
>  
>      // replace leading byte with invalid byte
> -    {1, 5, 0, 0, '\xFF', 0},
> -    {3, 5, 1, 1, '\xFF', 1},
> -    {6, 5, 3, 2, '\xFF', 3},
> -    {10, 5, 6, 3, '\xFF', 6},
> +    {1, 5, 0, 0, 0xFF, 0},
> +    {3, 5, 1, 1, 0xFF, 1},
> +    {6, 5, 3, 2, 0xFF, 3},
> +    {10, 5, 6, 3, 0xFF, 6},
>  
>      // replace first trailing byte with ASCII byte
>      {3, 5, 1, 1, 'z', 2},
> @@ -984,21 +1008,21 @@ utf8_to_ucs2_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
>      {10, 5, 6, 3, 'z', 7},
>  
>      // replace first trailing byte with invalid byte
> -    {3, 5, 1, 1, '\xFF', 2},
> -    {6, 5, 3, 2, '\xFF', 4},
> -    {10, 5, 6, 3, '\xFF', 7},
> +    {3, 5, 1, 1, 0xFF, 2},
> +    {6, 5, 3, 2, 0xFF, 4},
> +    {10, 5, 6, 3, 0xFF, 7},
>  
>      // replace second trailing byte with ASCII byte
>      {6, 5, 3, 2, 'z', 5},
>      {10, 5, 6, 3, 'z', 8},
>  
>      // replace second trailing byte with invalid byte
> -    {6, 5, 3, 2, '\xFF', 5},
> -    {10, 5, 6, 3, '\xFF', 8},
> +    {6, 5, 3, 2, 0xFF, 5},
> +    {10, 5, 6, 3, 0xFF, 8},
>  
>      // replace third trailing byte
>      {10, 5, 6, 3, 'z', 9},
> -    {10, 5, 6, 3, '\xFF', 9},
> +    {10, 5, 6, 3, 0xFF, 9},
>  
>      // When we see a leading byte of 4-byte CP, we should return error, no
>      // matter if it is incomplete at the end or has errors in the trailing
> @@ -1020,36 +1044,35 @@ utf8_to_ucs2_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
>      {5, 5, 3, 2, 'z', 4},
>  
>      // replace first trailing byte with invalid byte, also incomplete at end
> -    {5, 5, 3, 2, '\xFF', 4},
> +    {5, 5, 3, 2, 0xFF, 4},
>  
>      // replace first trailing byte with ASCII byte, also incomplete at end
>      {8, 5, 6, 3, 'z', 7},
>      {9, 5, 6, 3, 'z', 7},
>  
>      // replace first trailing byte with invalid byte, also incomplete at end
> -    {8, 5, 6, 3, '\xFF', 7},
> -    {9, 5, 6, 3, '\xFF', 7},
> +    {8, 5, 6, 3, 0xFF, 7},
> +    {9, 5, 6, 3, 0xFF, 7},
>  
>      // replace second trailing byte with ASCII byte, also incomplete at end
>      {9, 5, 6, 3, 'z', 8},
>  
>      // replace second trailing byte with invalid byte, also incomplete at end
> -    {9, 5, 6, 3, '\xFF', 8},
> +    {9, 5, 6, 3, 0xFF, 8},
>    };
>    for (auto t : offsets)
>      {
> -      char in[array_size (valid_in)] = {};
> -      CharT out[array_size (exp) - 1] = {};
> +      InternT out[array_size (exp) - 1] = {};
>        VERIFY (t.in_size <= array_size (in));
>        VERIFY (t.out_size <= array_size (out));
>        VERIFY (t.expected_in_next <= t.in_size);
>        VERIFY (t.expected_out_next <= t.out_size);
> -      char_traits<char>::copy (in, valid_in, array_size (valid_in));
> +      auto old_char = in[t.replace_pos];
>        in[t.replace_pos] = t.replace_char;
>  
>        auto state = mbstate_t{};
> -      auto in_next = (const char *) nullptr;
> -      auto out_next = (CharT *) nullptr;
> +      auto in_next = (const ExternT *) nullptr;
> +      auto out_next = (InternT *) nullptr;
>        auto res = codecvt_base::result ();
>  
>        res = cvt.in (state, in, in + t.in_size, in_next, out, out + t.out_size,
> @@ -1057,48 +1080,51 @@ utf8_to_ucs2_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
>        VERIFY (res == cvt.error);
>        VERIFY (in_next == in + t.expected_in_next);
>        VERIFY (out_next == out + t.expected_out_next);
> -      VERIFY (char_traits<CharT>::compare (out, exp, t.expected_out_next) == 0);
> +      VERIFY (char_traits<InternT>::compare (out, exp, t.expected_out_next)
> +	      == 0);
>        if (t.expected_out_next < array_size (out))
>  	VERIFY (out[t.expected_out_next] == 0);
> +
> +      in[t.replace_pos] = old_char;
>      }
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -utf8_to_ucs2_in (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +utf8_to_ucs2_in (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    utf8_to_ucs2_in_ok (cvt);
>    utf8_to_ucs2_in_partial (cvt);
>    utf8_to_ucs2_in_error (cvt);
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -ucs2_to_utf8_out_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +ucs2_to_utf8_out_ok (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    using namespace std;
>    // UTF-8 string of 1-byte CP, 2-byte CP and 3-byte CP
> -  const char16_t in_literal[] = u"b?\uAAAA";
> -  const char exp[] = "b?\uAAAA";
> -  CharT in[array_size (in_literal)] = {};
> -  copy (begin (in_literal), end (in_literal), begin (in));
> -
> -  static_assert (array_size (in_literal) == 4, "");
> -  static_assert (array_size (exp) == 7, "");
> -  static_assert (array_size (in) == 4, "");
> -  VERIFY (char_traits<char16_t>::length (in_literal) == 3);
> -  VERIFY (char_traits<char>::length (exp) == 6);
> -  VERIFY (char_traits<CharT>::length (in) == 3);
> +  const char16_t input[] = u"b?\uAAAA";
> +  const unsigned char expected[] = "b?\uAAAA";
> +  static_assert (array_size (input) == 4, "");
> +  static_assert (array_size (expected) == 7, "");
> +
> +  InternT in[array_size (input)];
> +  ExternT exp[array_size (expected)];
> +  copy (begin (input), end (input), begin (in));
> +  copy (begin (expected), end (expected), begin (exp));
> +  VERIFY (char_traits<InternT>::length (in) == 3);
> +  VERIFY (char_traits<ExternT>::length (exp) == 6);
>  
>    const test_offsets_ok offsets[] = {{0, 0}, {1, 1}, {2, 3}, {3, 6}};
>    for (auto t : offsets)
>      {
> -      char out[array_size (exp) - 1] = {};
> +      ExternT out[array_size (exp) - 1] = {};
>        VERIFY (t.in_size <= array_size (in));
>        VERIFY (t.out_size <= array_size (out));
>        auto state = mbstate_t{};
> -      auto in_next = (const CharT *) nullptr;
> -      auto out_next = (char *) nullptr;
> +      auto in_next = (const InternT *) nullptr;
> +      auto out_next = (ExternT *) nullptr;
>        auto res = codecvt_base::result ();
>  
>        res = cvt.out (state, in, in + t.in_size, in_next, out, out + t.out_size,
> @@ -1106,29 +1132,29 @@ ucs2_to_utf8_out_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
>        VERIFY (res == cvt.ok);
>        VERIFY (in_next == in + t.in_size);
>        VERIFY (out_next == out + t.out_size);
> -      VERIFY (char_traits<char>::compare (out, exp, t.out_size) == 0);
> +      VERIFY (char_traits<ExternT>::compare (out, exp, t.out_size) == 0);
>        if (t.out_size < array_size (out))
>  	VERIFY (out[t.out_size] == 0);
>      }
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -ucs2_to_utf8_out_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +ucs2_to_utf8_out_partial (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    using namespace std;
>    // UTF-8 string of 1-byte CP, 2-byte CP and 3-byte CP
> -  const char16_t in_literal[] = u"b?\uAAAA";
> -  const char exp[] = "b?\uAAAA";
> -  CharT in[array_size (in_literal)] = {};
> -  copy (begin (in_literal), end (in_literal), begin (in));
> -
> -  static_assert (array_size (in_literal) == 4, "");
> -  static_assert (array_size (exp) == 7, "");
> -  static_assert (array_size (in) == 4, "");
> -  VERIFY (char_traits<char16_t>::length (in_literal) == 3);
> -  VERIFY (char_traits<char>::length (exp) == 6);
> -  VERIFY (char_traits<CharT>::length (in) == 3);
> +  const char16_t input[] = u"b?\uAAAA";
> +  const unsigned char expected[] = "b?\uAAAA";
> +  static_assert (array_size (input) == 4, "");
> +  static_assert (array_size (expected) == 7, "");
> +
> +  InternT in[array_size (input)];
> +  ExternT exp[array_size (expected)];
> +  copy (begin (input), end (input), begin (in));
> +  copy (begin (expected), end (expected), begin (exp));
> +  VERIFY (char_traits<InternT>::length (in) == 3);
> +  VERIFY (char_traits<ExternT>::length (exp) == 6);
>  
>    const test_offsets_partial offsets[] = {
>      {1, 0, 0, 0}, // no space for first CP
> @@ -1142,14 +1168,14 @@ ucs2_to_utf8_out_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
>    };
>    for (auto t : offsets)
>      {
> -      char out[array_size (exp) - 1] = {};
> +      ExternT out[array_size (exp) - 1] = {};
>        VERIFY (t.in_size <= array_size (in));
>        VERIFY (t.out_size <= array_size (out));
>        VERIFY (t.expected_in_next <= t.in_size);
>        VERIFY (t.expected_out_next <= t.out_size);
>        auto state = mbstate_t{};
> -      auto in_next = (const CharT *) nullptr;
> -      auto out_next = (char *) nullptr;
> +      auto in_next = (const InternT *) nullptr;
> +      auto out_next = (ExternT *) nullptr;
>        auto res = codecvt_base::result ();
>  
>        res = cvt.out (state, in, in + t.in_size, in_next, out, out + t.out_size,
> @@ -1157,26 +1183,31 @@ ucs2_to_utf8_out_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
>        VERIFY (res == cvt.partial);
>        VERIFY (in_next == in + t.expected_in_next);
>        VERIFY (out_next == out + t.expected_out_next);
> -      VERIFY (char_traits<char>::compare (out, exp, t.expected_out_next) == 0);
> +      VERIFY (char_traits<ExternT>::compare (out, exp, t.expected_out_next)
> +	      == 0);
>        if (t.expected_out_next < array_size (out))
>  	VERIFY (out[t.expected_out_next] == 0);
>      }
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -ucs2_to_utf8_out_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +ucs2_to_utf8_out_error (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    using namespace std;
> -  const char16_t valid_in[] = u"b?\uAAAA\U0010AAAA";
> -  const char exp[] = "b?\uAAAA\U0010AAAA";
> -
> -  static_assert (array_size (valid_in) == 6, "");
> -  static_assert (array_size (exp) == 11, "");
> -  VERIFY (char_traits<char16_t>::length (valid_in) == 5);
> -  VERIFY (char_traits<char>::length (exp) == 10);
> -
> -  test_offsets_error<CharT> offsets[] = {
> +  const char16_t input[] = u"b?\uAAAA\U0010AAAA";
> +  const unsigned char expected[] = "b?\uAAAA\U0010AAAA";
> +  static_assert (array_size (input) == 6, "");
> +  static_assert (array_size (expected) == 11, "");
> +
> +  InternT in[array_size (input)];
> +  ExternT exp[array_size (expected)];
> +  copy (begin (input), end (input), begin (in));
> +  copy (begin (expected), end (expected), begin (exp));
> +  VERIFY (char_traits<InternT>::length (in) == 5);
> +  VERIFY (char_traits<ExternT>::length (exp) == 10);
> +
> +  test_offsets_error<InternT> offsets[] = {
>      {5, 10, 0, 0, 0xD800, 0},
>      {5, 10, 0, 0, 0xDBFF, 0},
>      {5, 10, 0, 0, 0xDC00, 0},
> @@ -1219,18 +1250,17 @@ ucs2_to_utf8_out_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
>  
>    for (auto t : offsets)
>      {
> -      CharT in[array_size (valid_in)] = {};
> -      char out[array_size (exp) - 1] = {};
> +      ExternT out[array_size (exp) - 1] = {};
>        VERIFY (t.in_size <= array_size (in));
>        VERIFY (t.out_size <= array_size (out));
>        VERIFY (t.expected_in_next <= t.in_size);
>        VERIFY (t.expected_out_next <= t.out_size);
> -      copy (begin (valid_in), end (valid_in), begin (in));
> +      auto old_char = in[t.replace_pos];
>        in[t.replace_pos] = t.replace_char;
>  
>        auto state = mbstate_t{};
> -      auto in_next = (const CharT *) nullptr;
> -      auto out_next = (char *) nullptr;
> +      auto in_next = (const InternT *) nullptr;
> +      auto out_next = (ExternT *) nullptr;
>        auto res = codecvt_base::result ();
>  
>        res = cvt.out (state, in, in + t.in_size, in_next, out, out + t.out_size,
> @@ -1238,24 +1268,27 @@ ucs2_to_utf8_out_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
>        VERIFY (res == cvt.error);
>        VERIFY (in_next == in + t.expected_in_next);
>        VERIFY (out_next == out + t.expected_out_next);
> -      VERIFY (char_traits<char>::compare (out, exp, t.expected_out_next) == 0);
> +      VERIFY (char_traits<ExternT>::compare (out, exp, t.expected_out_next)
> +	      == 0);
>        if (t.expected_out_next < array_size (out))
>  	VERIFY (out[t.expected_out_next] == 0);
> +
> +      in[t.replace_pos] = old_char;
>      }
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -ucs2_to_utf8_out (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +ucs2_to_utf8_out (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    ucs2_to_utf8_out_ok (cvt);
>    ucs2_to_utf8_out_partial (cvt);
>    ucs2_to_utf8_out_error (cvt);
>  }
>  
> -template <class CharT>
> +template <class InternT, class ExternT>
>  void
> -test_utf8_ucs2_cvts (const std::codecvt<CharT, char, mbstate_t> &cvt)
> +test_utf8_ucs2_cvt (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
>  {
>    utf8_to_ucs2_in (cvt);
>    ucs2_to_utf8_out (cvt);
> diff --git a/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode_char8_t.cc b/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode_char8_t.cc
> new file mode 100644
> index 000000000..8ab5ba79f
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode_char8_t.cc
> @@ -0,0 +1,53 @@
> +// Copyright (C) 2020-2023 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.
> +
> +// You should have received a copy of the GNU General Public License along
> +// with this library; see the file COPYING3.  If not see
> +// <http://www.gnu.org/licenses/>.
> +
> +// { dg-do run { target c++11 } }
> +// { dg-require-cstdint "" }
> +// { dg-options "-fchar8_t" }
> +
> +#include "codecvt_unicode.h"
> +
> +using namespace std;
> +
> +void
> +test_utf8_utf32_codecvts ()
> +{
> +  using codecvt_c32_c8 = codecvt<char32_t, char8_t, mbstate_t>;
> +  auto &loc_c = locale::classic ();
> +  VERIFY (has_facet<codecvt_c32_c8> (loc_c));
> +
> +  auto &cvt = use_facet<codecvt_c32_c8> (loc_c);
> +  test_utf8_utf32_cvt (cvt);
> +}
> +
> +void
> +test_utf8_utf16_codecvts ()
> +{
> +  using codecvt_c16_c8 = codecvt<char16_t, char8_t, mbstate_t>;
> +  auto &loc_c = locale::classic ();
> +  VERIFY (has_facet<codecvt_c16_c8> (loc_c));
> +
> +  auto &cvt = use_facet<codecvt_c16_c8> (loc_c);
> +  test_utf8_utf16_cvt (cvt);
> +}
> +
> +int
> +main ()
> +{
> +  test_utf8_utf32_codecvts ();
> +  test_utf8_utf16_codecvts ();
> +}
> diff --git a/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode_wchar_t.cc b/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode_wchar_t.cc
> index 4fd1bfec6..6e9152b50 100644
> --- a/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode_wchar_t.cc
> +++ b/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode_wchar_t.cc
> @@ -28,7 +28,7 @@ test_utf8_utf32_codecvts ()
>  {
>  #if __SIZEOF_WCHAR_T__ == 4
>    codecvt_utf8<wchar_t> cvt;
> -  test_utf8_utf32_codecvts (cvt);
> +  test_utf8_utf32_cvt (cvt);
>  #endif
>  }
>  
> @@ -37,7 +37,7 @@ test_utf8_utf16_codecvts ()
>  {
>  #if __SIZEOF_WCHAR_T__ >= 2
>    codecvt_utf8_utf16<wchar_t> cvt;
> -  test_utf8_utf16_cvts (cvt);
> +  test_utf8_utf16_cvt (cvt);
>  #endif
>  }
>  
> @@ -46,7 +46,7 @@ test_utf8_ucs2_codecvts ()
>  {
>  #if __SIZEOF_WCHAR_T__ == 2
>    codecvt_utf8<wchar_t> cvt;
> -  test_utf8_ucs2_cvts (cvt);
> +  test_utf8_ucs2_cvt (cvt);
>  #endif
>  }
>  
> -- 
> 2.34.1
>
  

Patch

diff --git a/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode.cc b/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode.cc
index df1a2b4cc..eafb53a8c 100644
--- a/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode.cc
+++ b/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode.cc
@@ -27,38 +27,38 @@  void
 test_utf8_utf32_codecvts ()
 {
   using codecvt_c32 = codecvt<char32_t, char, mbstate_t>;
-  auto loc_c = locale::classic ();
+  auto &loc_c = locale::classic ();
   VERIFY (has_facet<codecvt_c32> (loc_c));
 
   auto &cvt = use_facet<codecvt_c32> (loc_c);
-  test_utf8_utf32_codecvts (cvt);
+  test_utf8_utf32_cvt (cvt);
 
   codecvt_utf8<char32_t> cvt2;
-  test_utf8_utf32_codecvts (cvt2);
+  test_utf8_utf32_cvt (cvt2);
 }
 
 void
 test_utf8_utf16_codecvts ()
 {
   using codecvt_c16 = codecvt<char16_t, char, mbstate_t>;
-  auto loc_c = locale::classic ();
+  auto &loc_c = locale::classic ();
   VERIFY (has_facet<codecvt_c16> (loc_c));
 
   auto &cvt = use_facet<codecvt_c16> (loc_c);
-  test_utf8_utf16_cvts (cvt);
+  test_utf8_utf16_cvt (cvt);
 
   codecvt_utf8_utf16<char16_t> cvt2;
-  test_utf8_utf16_cvts (cvt2);
+  test_utf8_utf16_cvt (cvt2);
 
   codecvt_utf8_utf16<char32_t> cvt3;
-  test_utf8_utf16_cvts (cvt3);
+  test_utf8_utf16_cvt (cvt3);
 }
 
 void
 test_utf8_ucs2_codecvts ()
 {
   codecvt_utf8<char16_t> cvt;
-  test_utf8_ucs2_cvts (cvt);
+  test_utf8_ucs2_cvt (cvt);
 }
 
 int
diff --git a/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode.h b/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode.h
index fbdc7a35b..690c07215 100644
--- a/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode.h
+++ b/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode.h
@@ -42,33 +42,33 @@  auto constexpr array_size (const T (&)[N]) -> size_t
   return N;
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-utf8_to_utf32_in_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
+utf8_to_utf32_in_ok (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   using namespace std;
   // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
-  const char in[] = "bш\uAAAA\U0010AAAA";
-  const char32_t exp_literal[] = U"bш\uAAAA\U0010AAAA";
-  CharT exp[array_size (exp_literal)] = {};
-  std::copy (begin (exp_literal), end (exp_literal), begin (exp));
-
-  static_assert (array_size (in) == 11, "");
-  static_assert (array_size (exp_literal) == 5, "");
-  static_assert (array_size (exp) == 5, "");
-  VERIFY (char_traits<char>::length (in) == 10);
-  VERIFY (char_traits<char32_t>::length (exp_literal) == 4);
-  VERIFY (char_traits<CharT>::length (exp) == 4);
+  const unsigned char input[] = "bш\uAAAA\U0010AAAA";
+  const char32_t expected[] = U"bш\uAAAA\U0010AAAA";
+  static_assert (array_size (input) == 11, "");
+  static_assert (array_size (expected) == 5, "");
+
+  ExternT in[array_size (input)];
+  InternT exp[array_size (expected)];
+  copy (begin (input), end (input), begin (in));
+  copy (begin (expected), end (expected), begin (exp));
+  VERIFY (char_traits<ExternT>::length (in) == 10);
+  VERIFY (char_traits<InternT>::length (exp) == 4);
 
   test_offsets_ok offsets[] = {{0, 0}, {1, 1}, {3, 2}, {6, 3}, {10, 4}};
   for (auto t : offsets)
     {
-      CharT out[array_size (exp) - 1] = {};
+      InternT out[array_size (exp) - 1] = {};
       VERIFY (t.in_size <= array_size (in));
       VERIFY (t.out_size <= array_size (out));
       auto state = mbstate_t{};
-      auto in_next = (const char *) nullptr;
-      auto out_next = (CharT *) nullptr;
+      auto in_next = (const ExternT *) nullptr;
+      auto out_next = (InternT *) nullptr;
       auto res = codecvt_base::result ();
 
       res = cvt.in (state, in, in + t.in_size, in_next, out, out + t.out_size,
@@ -76,19 +76,19 @@  utf8_to_utf32_in_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
       VERIFY (res == cvt.ok);
       VERIFY (in_next == in + t.in_size);
       VERIFY (out_next == out + t.out_size);
-      VERIFY (char_traits<CharT>::compare (out, exp, t.out_size) == 0);
+      VERIFY (char_traits<InternT>::compare (out, exp, t.out_size) == 0);
       if (t.out_size < array_size (out))
 	VERIFY (out[t.out_size] == 0);
     }
 
   for (auto t : offsets)
     {
-      CharT out[array_size (exp)] = {};
+      InternT out[array_size (exp)] = {};
       VERIFY (t.in_size <= array_size (in));
       VERIFY (t.out_size <= array_size (out));
       auto state = mbstate_t{};
-      auto in_next = (const char *) nullptr;
-      auto out_next = (CharT *) nullptr;
+      auto in_next = (const ExternT *) nullptr;
+      auto out_next = (InternT *) nullptr;
       auto res = codecvt_base::result ();
 
       res
@@ -96,29 +96,29 @@  utf8_to_utf32_in_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
       VERIFY (res == cvt.ok);
       VERIFY (in_next == in + t.in_size);
       VERIFY (out_next == out + t.out_size);
-      VERIFY (char_traits<CharT>::compare (out, exp, t.out_size) == 0);
+      VERIFY (char_traits<InternT>::compare (out, exp, t.out_size) == 0);
       if (t.out_size < array_size (out))
 	VERIFY (out[t.out_size] == 0);
     }
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-utf8_to_utf32_in_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
+utf8_to_utf32_in_partial (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   using namespace std;
   // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
-  const char in[] = "bш\uAAAA\U0010AAAA";
-  const char32_t exp_literal[] = U"bш\uAAAA\U0010AAAA";
-  CharT exp[array_size (exp_literal)] = {};
-  std::copy (begin (exp_literal), end (exp_literal), begin (exp));
-
-  static_assert (array_size (in) == 11, "");
-  static_assert (array_size (exp_literal) == 5, "");
-  static_assert (array_size (exp) == 5, "");
-  VERIFY (char_traits<char>::length (in) == 10);
-  VERIFY (char_traits<char32_t>::length (exp_literal) == 4);
-  VERIFY (char_traits<CharT>::length (exp) == 4);
+  const unsigned char input[] = "bш\uAAAA\U0010AAAA";
+  const char32_t expected[] = U"bш\uAAAA\U0010AAAA";
+  static_assert (array_size (input) == 11, "");
+  static_assert (array_size (expected) == 5, "");
+
+  ExternT in[array_size (input)];
+  InternT exp[array_size (expected)];
+  copy (begin (input), end (input), begin (in));
+  copy (begin (expected), end (expected), begin (exp));
+  VERIFY (char_traits<ExternT>::length (in) == 10);
+  VERIFY (char_traits<InternT>::length (exp) == 4);
 
   test_offsets_partial offsets[] = {
     {1, 0, 0, 0}, // no space for first CP
@@ -144,14 +144,14 @@  utf8_to_utf32_in_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
 
   for (auto t : offsets)
     {
-      CharT out[array_size (exp) - 1] = {};
+      InternT out[array_size (exp) - 1] = {};
       VERIFY (t.in_size <= array_size (in));
       VERIFY (t.out_size <= array_size (out));
       VERIFY (t.expected_in_next <= t.in_size);
       VERIFY (t.expected_out_next <= t.out_size);
       auto state = mbstate_t{};
-      auto in_next = (const char *) nullptr;
-      auto out_next = (CharT *) nullptr;
+      auto in_next = (const ExternT *) nullptr;
+      auto out_next = (InternT *) nullptr;
       auto res = codecvt_base::result ();
 
       res = cvt.in (state, in, in + t.in_size, in_next, out, out + t.out_size,
@@ -159,37 +159,38 @@  utf8_to_utf32_in_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
       VERIFY (res == cvt.partial);
       VERIFY (in_next == in + t.expected_in_next);
       VERIFY (out_next == out + t.expected_out_next);
-      VERIFY (char_traits<CharT>::compare (out, exp, t.expected_out_next) == 0);
+      VERIFY (char_traits<InternT>::compare (out, exp, t.expected_out_next)
+	      == 0);
       if (t.expected_out_next < array_size (out))
 	VERIFY (out[t.expected_out_next] == 0);
     }
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-utf8_to_utf32_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
+utf8_to_utf32_in_error (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   using namespace std;
   // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
-  const char valid_in[] = "bш\uAAAA\U0010AAAA";
-  const char32_t exp_literal[] = U"bш\uAAAA\U0010AAAA";
-  CharT exp[array_size (exp_literal)] = {};
-  std::copy (begin (exp_literal), end (exp_literal), begin (exp));
+  const unsigned char input[] = "bш\uAAAA\U0010AAAA";
+  const char32_t expected[] = U"bш\uAAAA\U0010AAAA";
+  static_assert (array_size (input) == 11, "");
+  static_assert (array_size (expected) == 5, "");
 
-  static_assert (array_size (valid_in) == 11, "");
-  static_assert (array_size (exp_literal) == 5, "");
-  static_assert (array_size (exp) == 5, "");
-  VERIFY (char_traits<char>::length (valid_in) == 10);
-  VERIFY (char_traits<char32_t>::length (exp_literal) == 4);
-  VERIFY (char_traits<CharT>::length (exp) == 4);
+  ExternT in[array_size (input)];
+  InternT exp[array_size (expected)];
+  copy (begin (input), end (input), begin (in));
+  copy (begin (expected), end (expected), begin (exp));
+  VERIFY (char_traits<ExternT>::length (in) == 10);
+  VERIFY (char_traits<InternT>::length (exp) == 4);
 
-  test_offsets_error<char> offsets[] = {
+  test_offsets_error<unsigned char> offsets[] = {
 
     // replace leading byte with invalid byte
-    {1, 4, 0, 0, '\xFF', 0},
-    {3, 4, 1, 1, '\xFF', 1},
-    {6, 4, 3, 2, '\xFF', 3},
-    {10, 4, 6, 3, '\xFF', 6},
+    {1, 4, 0, 0, 0xFF, 0},
+    {3, 4, 1, 1, 0xFF, 1},
+    {6, 4, 3, 2, 0xFF, 3},
+    {10, 4, 6, 3, 0xFF, 6},
 
     // replace first trailing byte with ASCII byte
     {3, 4, 1, 1, 'z', 2},
@@ -197,21 +198,21 @@  utf8_to_utf32_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
     {10, 4, 6, 3, 'z', 7},
 
     // replace first trailing byte with invalid byte
-    {3, 4, 1, 1, '\xFF', 2},
-    {6, 4, 3, 2, '\xFF', 4},
-    {10, 4, 6, 3, '\xFF', 7},
+    {3, 4, 1, 1, 0xFF, 2},
+    {6, 4, 3, 2, 0xFF, 4},
+    {10, 4, 6, 3, 0xFF, 7},
 
     // replace second trailing byte with ASCII byte
     {6, 4, 3, 2, 'z', 5},
     {10, 4, 6, 3, 'z', 8},
 
     // replace second trailing byte with invalid byte
-    {6, 4, 3, 2, '\xFF', 5},
-    {10, 4, 6, 3, '\xFF', 8},
+    {6, 4, 3, 2, 0xFF, 5},
+    {10, 4, 6, 3, 0xFF, 8},
 
     // replace third trailing byte
     {10, 4, 6, 3, 'z', 9},
-    {10, 4, 6, 3, '\xFF', 9},
+    {10, 4, 6, 3, 0xFF, 9},
 
     // replace first trailing byte with ASCII byte, also incomplete at end
     {5, 4, 3, 2, 'z', 4},
@@ -219,30 +220,29 @@  utf8_to_utf32_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
     {9, 4, 6, 3, 'z', 7},
 
     // replace first trailing byte with invalid byte, also incomplete at end
-    {5, 4, 3, 2, '\xFF', 4},
-    {8, 4, 6, 3, '\xFF', 7},
-    {9, 4, 6, 3, '\xFF', 7},
+    {5, 4, 3, 2, 0xFF, 4},
+    {8, 4, 6, 3, 0xFF, 7},
+    {9, 4, 6, 3, 0xFF, 7},
 
     // replace second trailing byte with ASCII byte, also incomplete at end
     {9, 4, 6, 3, 'z', 8},
 
     // replace second trailing byte with invalid byte, also incomplete at end
-    {9, 4, 6, 3, '\xFF', 8},
+    {9, 4, 6, 3, 0xFF, 8},
   };
   for (auto t : offsets)
     {
-      char in[array_size (valid_in)] = {};
-      CharT out[array_size (exp) - 1] = {};
+      InternT out[array_size (exp) - 1] = {};
       VERIFY (t.in_size <= array_size (in));
       VERIFY (t.out_size <= array_size (out));
       VERIFY (t.expected_in_next <= t.in_size);
       VERIFY (t.expected_out_next <= t.out_size);
-      char_traits<char>::copy (in, valid_in, array_size (valid_in));
+      auto old_char = in[t.replace_pos];
       in[t.replace_pos] = t.replace_char;
 
       auto state = mbstate_t{};
-      auto in_next = (const char *) nullptr;
-      auto out_next = (CharT *) nullptr;
+      auto in_next = (const ExternT *) nullptr;
+      auto out_next = (InternT *) nullptr;
       auto res = codecvt_base::result ();
 
       res = cvt.in (state, in, in + t.in_size, in_next, out, out + t.out_size,
@@ -250,48 +250,51 @@  utf8_to_utf32_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
       VERIFY (res == cvt.error);
       VERIFY (in_next == in + t.expected_in_next);
       VERIFY (out_next == out + t.expected_out_next);
-      VERIFY (char_traits<CharT>::compare (out, exp, t.expected_out_next) == 0);
+      VERIFY (char_traits<InternT>::compare (out, exp, t.expected_out_next)
+	      == 0);
       if (t.expected_out_next < array_size (out))
 	VERIFY (out[t.expected_out_next] == 0);
+
+      in[t.replace_pos] = old_char;
     }
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-utf8_to_utf32_in (const std::codecvt<CharT, char, mbstate_t> &cvt)
+utf8_to_utf32_in (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   utf8_to_utf32_in_ok (cvt);
   utf8_to_utf32_in_partial (cvt);
   utf8_to_utf32_in_error (cvt);
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-utf32_to_utf8_out_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
+utf32_to_utf8_out_ok (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   using namespace std;
   // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
-  const char32_t in_literal[] = U"bш\uAAAA\U0010AAAA";
-  const char exp[] = "bш\uAAAA\U0010AAAA";
-  CharT in[array_size (in_literal)] = {};
-  copy (begin (in_literal), end (in_literal), begin (in));
-
-  static_assert (array_size (in_literal) == 5, "");
-  static_assert (array_size (in) == 5, "");
-  static_assert (array_size (exp) == 11, "");
-  VERIFY (char_traits<char32_t>::length (in_literal) == 4);
-  VERIFY (char_traits<CharT>::length (in) == 4);
-  VERIFY (char_traits<char>::length (exp) == 10);
+  const char32_t input[] = U"bш\uAAAA\U0010AAAA";
+  const unsigned char expected[] = "bш\uAAAA\U0010AAAA";
+  static_assert (array_size (input) == 5, "");
+  static_assert (array_size (expected) == 11, "");
+
+  InternT in[array_size (input)];
+  ExternT exp[array_size (expected)];
+  copy (begin (input), end (input), begin (in));
+  copy (begin (expected), end (expected), begin (exp));
+  VERIFY (char_traits<InternT>::length (in) == 4);
+  VERIFY (char_traits<ExternT>::length (exp) == 10);
 
   const test_offsets_ok offsets[] = {{0, 0}, {1, 1}, {2, 3}, {3, 6}, {4, 10}};
   for (auto t : offsets)
     {
-      char out[array_size (exp) - 1] = {};
+      ExternT out[array_size (exp) - 1] = {};
       VERIFY (t.in_size <= array_size (in));
       VERIFY (t.out_size <= array_size (out));
       auto state = mbstate_t{};
-      auto in_next = (const CharT *) nullptr;
-      auto out_next = (char *) nullptr;
+      auto in_next = (const InternT *) nullptr;
+      auto out_next = (ExternT *) nullptr;
       auto res = codecvt_base::result ();
 
       res = cvt.out (state, in, in + t.in_size, in_next, out, out + t.out_size,
@@ -299,29 +302,29 @@  utf32_to_utf8_out_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
       VERIFY (res == cvt.ok);
       VERIFY (in_next == in + t.in_size);
       VERIFY (out_next == out + t.out_size);
-      VERIFY (char_traits<char>::compare (out, exp, t.out_size) == 0);
+      VERIFY (char_traits<ExternT>::compare (out, exp, t.out_size) == 0);
       if (t.out_size < array_size (out))
 	VERIFY (out[t.out_size] == 0);
     }
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-utf32_to_utf8_out_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
+utf32_to_utf8_out_partial (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   using namespace std;
   // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
-  const char32_t in_literal[] = U"bш\uAAAA\U0010AAAA";
-  const char exp[] = "bш\uAAAA\U0010AAAA";
-  CharT in[array_size (in_literal)] = {};
-  copy (begin (in_literal), end (in_literal), begin (in));
-
-  static_assert (array_size (in_literal) == 5, "");
-  static_assert (array_size (in) == 5, "");
-  static_assert (array_size (exp) == 11, "");
-  VERIFY (char_traits<char32_t>::length (in_literal) == 4);
-  VERIFY (char_traits<CharT>::length (in) == 4);
-  VERIFY (char_traits<char>::length (exp) == 10);
+  const char32_t input[] = U"bш\uAAAA\U0010AAAA";
+  const unsigned char expected[] = "bш\uAAAA\U0010AAAA";
+  static_assert (array_size (input) == 5, "");
+  static_assert (array_size (expected) == 11, "");
+
+  InternT in[array_size (input)];
+  ExternT exp[array_size (expected)];
+  copy (begin (input), end (input), begin (in));
+  copy (begin (expected), end (expected), begin (exp));
+  VERIFY (char_traits<InternT>::length (in) == 4);
+  VERIFY (char_traits<ExternT>::length (exp) == 10);
 
   const test_offsets_partial offsets[] = {
     {1, 0, 0, 0}, // no space for first CP
@@ -340,14 +343,14 @@  utf32_to_utf8_out_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
   };
   for (auto t : offsets)
     {
-      char out[array_size (exp) - 1] = {};
+      ExternT out[array_size (exp) - 1] = {};
       VERIFY (t.in_size <= array_size (in));
       VERIFY (t.out_size <= array_size (out));
       VERIFY (t.expected_in_next <= t.in_size);
       VERIFY (t.expected_out_next <= t.out_size);
       auto state = mbstate_t{};
-      auto in_next = (const CharT *) nullptr;
-      auto out_next = (char *) nullptr;
+      auto in_next = (const InternT *) nullptr;
+      auto out_next = (ExternT *) nullptr;
       auto res = codecvt_base::result ();
 
       res = cvt.out (state, in, in + t.in_size, in_next, out, out + t.out_size,
@@ -355,44 +358,49 @@  utf32_to_utf8_out_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
       VERIFY (res == cvt.partial);
       VERIFY (in_next == in + t.expected_in_next);
       VERIFY (out_next == out + t.expected_out_next);
-      VERIFY (char_traits<char>::compare (out, exp, t.expected_out_next) == 0);
+      VERIFY (char_traits<ExternT>::compare (out, exp, t.expected_out_next)
+	      == 0);
       if (t.expected_out_next < array_size (out))
 	VERIFY (out[t.expected_out_next] == 0);
     }
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-utf32_to_utf8_out_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
+utf32_to_utf8_out_error (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   using namespace std;
-  const char32_t valid_in[] = U"bш\uAAAA\U0010AAAA";
-  const char exp[] = "bш\uAAAA\U0010AAAA";
-
-  static_assert (array_size (valid_in) == 5, "");
-  static_assert (array_size (exp) == 11, "");
-  VERIFY (char_traits<char32_t>::length (valid_in) == 4);
-  VERIFY (char_traits<char>::length (exp) == 10);
-
-  test_offsets_error<CharT> offsets[] = {{4, 10, 0, 0, 0x00110000, 0},
-					 {4, 10, 1, 1, 0x00110000, 1},
-					 {4, 10, 2, 3, 0x00110000, 2},
-					 {4, 10, 3, 6, 0x00110000, 3}};
+  // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
+  const char32_t input[] = U"bш\uAAAA\U0010AAAA";
+  const unsigned char expected[] = "bш\uAAAA\U0010AAAA";
+  static_assert (array_size (input) == 5, "");
+  static_assert (array_size (expected) == 11, "");
+
+  InternT in[array_size (input)];
+  ExternT exp[array_size (expected)];
+  copy (begin (input), end (input), begin (in));
+  copy (begin (expected), end (expected), begin (exp));
+  VERIFY (char_traits<InternT>::length (in) == 4);
+  VERIFY (char_traits<ExternT>::length (exp) == 10);
+
+  test_offsets_error<InternT> offsets[] = {{4, 10, 0, 0, 0x00110000, 0},
+					   {4, 10, 1, 1, 0x00110000, 1},
+					   {4, 10, 2, 3, 0x00110000, 2},
+					   {4, 10, 3, 6, 0x00110000, 3}};
 
   for (auto t : offsets)
     {
-      CharT in[array_size (valid_in)] = {};
-      char out[array_size (exp) - 1] = {};
+      ExternT out[array_size (exp) - 1] = {};
       VERIFY (t.in_size <= array_size (in));
       VERIFY (t.out_size <= array_size (out));
       VERIFY (t.expected_in_next <= t.in_size);
       VERIFY (t.expected_out_next <= t.out_size);
-      copy (begin (valid_in), end (valid_in), begin (in));
+      auto old_char = in[t.replace_pos];
       in[t.replace_pos] = t.replace_char;
 
       auto state = mbstate_t{};
-      auto in_next = (const CharT *) nullptr;
-      auto out_next = (char *) nullptr;
+      auto in_next = (const InternT *) nullptr;
+      auto out_next = (ExternT *) nullptr;
       auto res = codecvt_base::result ();
 
       res = cvt.out (state, in, in + t.in_size, in_next, out, out + t.out_size,
@@ -400,56 +408,59 @@  utf32_to_utf8_out_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
       VERIFY (res == cvt.error);
       VERIFY (in_next == in + t.expected_in_next);
       VERIFY (out_next == out + t.expected_out_next);
-      VERIFY (char_traits<char>::compare (out, exp, t.expected_out_next) == 0);
+      VERIFY (char_traits<ExternT>::compare (out, exp, t.expected_out_next)
+	      == 0);
       if (t.expected_out_next < array_size (out))
 	VERIFY (out[t.expected_out_next] == 0);
+
+      in[t.replace_pos] = old_char;
     }
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-utf32_to_utf8_out (const std::codecvt<CharT, char, mbstate_t> &cvt)
+utf32_to_utf8_out (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   utf32_to_utf8_out_ok (cvt);
   utf32_to_utf8_out_partial (cvt);
   utf32_to_utf8_out_error (cvt);
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-test_utf8_utf32_codecvts (const std::codecvt<CharT, char, mbstate_t> &cvt)
+test_utf8_utf32_cvt (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   utf8_to_utf32_in (cvt);
   utf32_to_utf8_out (cvt);
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-utf8_to_utf16_in_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
+utf8_to_utf16_in_ok (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   using namespace std;
   // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
-  const char in[] = "bш\uAAAA\U0010AAAA";
-  const char16_t exp_literal[] = u"bш\uAAAA\U0010AAAA";
-  CharT exp[array_size (exp_literal)] = {};
-  copy (begin (exp_literal), end (exp_literal), begin (exp));
-
-  static_assert (array_size (in) == 11, "");
-  static_assert (array_size (exp_literal) == 6, "");
-  static_assert (array_size (exp) == 6, "");
-  VERIFY (char_traits<char>::length (in) == 10);
-  VERIFY (char_traits<char16_t>::length (exp_literal) == 5);
-  VERIFY (char_traits<CharT>::length (exp) == 5);
+  const unsigned char input[] = "bш\uAAAA\U0010AAAA";
+  const char16_t expected[] = u"bш\uAAAA\U0010AAAA";
+  static_assert (array_size (input) == 11, "");
+  static_assert (array_size (expected) == 6, "");
+
+  ExternT in[array_size (input)];
+  InternT exp[array_size (expected)];
+  copy (begin (input), end (input), begin (in));
+  copy (begin (expected), end (expected), begin (exp));
+  VERIFY (char_traits<ExternT>::length (in) == 10);
+  VERIFY (char_traits<InternT>::length (exp) == 5);
 
   test_offsets_ok offsets[] = {{0, 0}, {1, 1}, {3, 2}, {6, 3}, {10, 5}};
   for (auto t : offsets)
     {
-      CharT out[array_size (exp) - 1] = {};
+      InternT out[array_size (exp) - 1] = {};
       VERIFY (t.in_size <= array_size (in));
       VERIFY (t.out_size <= array_size (out));
       auto state = mbstate_t{};
-      auto in_next = (const char *) nullptr;
-      auto out_next = (CharT *) nullptr;
+      auto in_next = (const ExternT *) nullptr;
+      auto out_next = (InternT *) nullptr;
       auto res = codecvt_base::result ();
 
       res = cvt.in (state, in, in + t.in_size, in_next, out, out + t.out_size,
@@ -457,19 +468,19 @@  utf8_to_utf16_in_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
       VERIFY (res == cvt.ok);
       VERIFY (in_next == in + t.in_size);
       VERIFY (out_next == out + t.out_size);
-      VERIFY (char_traits<CharT>::compare (out, exp, t.out_size) == 0);
+      VERIFY (char_traits<InternT>::compare (out, exp, t.out_size) == 0);
       if (t.out_size < array_size (out))
 	VERIFY (out[t.out_size] == 0);
     }
 
   for (auto t : offsets)
     {
-      CharT out[array_size (exp)] = {};
+      InternT out[array_size (exp)] = {};
       VERIFY (t.in_size <= array_size (in));
       VERIFY (t.out_size <= array_size (out));
       auto state = mbstate_t{};
-      auto in_next = (const char *) nullptr;
-      auto out_next = (CharT *) nullptr;
+      auto in_next = (const ExternT *) nullptr;
+      auto out_next = (InternT *) nullptr;
       auto res = codecvt_base::result ();
 
       res
@@ -477,29 +488,29 @@  utf8_to_utf16_in_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
       VERIFY (res == cvt.ok);
       VERIFY (in_next == in + t.in_size);
       VERIFY (out_next == out + t.out_size);
-      VERIFY (char_traits<CharT>::compare (out, exp, t.out_size) == 0);
+      VERIFY (char_traits<InternT>::compare (out, exp, t.out_size) == 0);
       if (t.out_size < array_size (out))
 	VERIFY (out[t.out_size] == 0);
     }
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-utf8_to_utf16_in_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
+utf8_to_utf16_in_partial (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   using namespace std;
   // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
-  const char in[] = "bш\uAAAA\U0010AAAA";
-  const char16_t exp_literal[] = u"bш\uAAAA\U0010AAAA";
-  CharT exp[array_size (exp_literal)] = {};
-  copy (begin (exp_literal), end (exp_literal), begin (exp));
-
-  static_assert (array_size (in) == 11, "");
-  static_assert (array_size (exp_literal) == 6, "");
-  static_assert (array_size (exp) == 6, "");
-  VERIFY (char_traits<char>::length (in) == 10);
-  VERIFY (char_traits<char16_t>::length (exp_literal) == 5);
-  VERIFY (char_traits<CharT>::length (exp) == 5);
+  const unsigned char input[] = "bш\uAAAA\U0010AAAA";
+  const char16_t expected[] = u"bш\uAAAA\U0010AAAA";
+  static_assert (array_size (input) == 11, "");
+  static_assert (array_size (expected) == 6, "");
+
+  ExternT in[array_size (input)];
+  InternT exp[array_size (expected)];
+  copy (begin (input), end (input), begin (in));
+  copy (begin (expected), end (expected), begin (exp));
+  VERIFY (char_traits<ExternT>::length (in) == 10);
+  VERIFY (char_traits<InternT>::length (exp) == 5);
 
   test_offsets_partial offsets[] = {
     {1, 0, 0, 0}, // no space for first CP
@@ -530,14 +541,14 @@  utf8_to_utf16_in_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
 
   for (auto t : offsets)
     {
-      CharT out[array_size (exp) - 1] = {};
+      InternT out[array_size (exp) - 1] = {};
       VERIFY (t.in_size <= array_size (in));
       VERIFY (t.out_size <= array_size (out));
       VERIFY (t.expected_in_next <= t.in_size);
       VERIFY (t.expected_out_next <= t.out_size);
       auto state = mbstate_t{};
-      auto in_next = (const char *) nullptr;
-      auto out_next = (CharT *) nullptr;
+      auto in_next = (const ExternT *) nullptr;
+      auto out_next = (InternT *) nullptr;
       auto res = codecvt_base::result ();
 
       res = cvt.in (state, in, in + t.in_size, in_next, out, out + t.out_size,
@@ -545,36 +556,38 @@  utf8_to_utf16_in_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
       VERIFY (res == cvt.partial);
       VERIFY (in_next == in + t.expected_in_next);
       VERIFY (out_next == out + t.expected_out_next);
-      VERIFY (char_traits<CharT>::compare (out, exp, t.expected_out_next) == 0);
+      VERIFY (char_traits<InternT>::compare (out, exp, t.expected_out_next)
+	      == 0);
       if (t.expected_out_next < array_size (out))
 	VERIFY (out[t.expected_out_next] == 0);
     }
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-utf8_to_utf16_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
+utf8_to_utf16_in_error (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   using namespace std;
-  const char valid_in[] = "bш\uAAAA\U0010AAAA";
-  const char16_t exp_literal[] = u"bш\uAAAA\U0010AAAA";
-  CharT exp[array_size (exp_literal)] = {};
-  copy (begin (exp_literal), end (exp_literal), begin (exp));
+  // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
+  const unsigned char input[] = "bш\uAAAA\U0010AAAA";
+  const char16_t expected[] = u"bш\uAAAA\U0010AAAA";
+  static_assert (array_size (input) == 11, "");
+  static_assert (array_size (expected) == 6, "");
 
-  static_assert (array_size (valid_in) == 11, "");
-  static_assert (array_size (exp_literal) == 6, "");
-  static_assert (array_size (exp) == 6, "");
-  VERIFY (char_traits<char>::length (valid_in) == 10);
-  VERIFY (char_traits<char16_t>::length (exp_literal) == 5);
-  VERIFY (char_traits<CharT>::length (exp) == 5);
+  ExternT in[array_size (input)];
+  InternT exp[array_size (expected)];
+  copy (begin (input), end (input), begin (in));
+  copy (begin (expected), end (expected), begin (exp));
+  VERIFY (char_traits<ExternT>::length (in) == 10);
+  VERIFY (char_traits<InternT>::length (exp) == 5);
 
-  test_offsets_error<char> offsets[] = {
+  test_offsets_error<unsigned char> offsets[] = {
 
     // replace leading byte with invalid byte
-    {1, 5, 0, 0, '\xFF', 0},
-    {3, 5, 1, 1, '\xFF', 1},
-    {6, 5, 3, 2, '\xFF', 3},
-    {10, 5, 6, 3, '\xFF', 6},
+    {1, 5, 0, 0, 0xFF, 0},
+    {3, 5, 1, 1, 0xFF, 1},
+    {6, 5, 3, 2, 0xFF, 3},
+    {10, 5, 6, 3, 0xFF, 6},
 
     // replace first trailing byte with ASCII byte
     {3, 5, 1, 1, 'z', 2},
@@ -582,21 +595,21 @@  utf8_to_utf16_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
     {10, 5, 6, 3, 'z', 7},
 
     // replace first trailing byte with invalid byte
-    {3, 5, 1, 1, '\xFF', 2},
-    {6, 5, 3, 2, '\xFF', 4},
-    {10, 5, 6, 3, '\xFF', 7},
+    {3, 5, 1, 1, 0xFF, 2},
+    {6, 5, 3, 2, 0xFF, 4},
+    {10, 5, 6, 3, 0xFF, 7},
 
     // replace second trailing byte with ASCII byte
     {6, 5, 3, 2, 'z', 5},
     {10, 5, 6, 3, 'z', 8},
 
     // replace second trailing byte with invalid byte
-    {6, 5, 3, 2, '\xFF', 5},
-    {10, 5, 6, 3, '\xFF', 8},
+    {6, 5, 3, 2, 0xFF, 5},
+    {10, 5, 6, 3, 0xFF, 8},
 
     // replace third trailing byte
     {10, 5, 6, 3, 'z', 9},
-    {10, 5, 6, 3, '\xFF', 9},
+    {10, 5, 6, 3, 0xFF, 9},
 
     // replace first trailing byte with ASCII byte, also incomplete at end
     {5, 5, 3, 2, 'z', 4},
@@ -604,30 +617,29 @@  utf8_to_utf16_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
     {9, 5, 6, 3, 'z', 7},
 
     // replace first trailing byte with invalid byte, also incomplete at end
-    {5, 5, 3, 2, '\xFF', 4},
-    {8, 5, 6, 3, '\xFF', 7},
-    {9, 5, 6, 3, '\xFF', 7},
+    {5, 5, 3, 2, 0xFF, 4},
+    {8, 5, 6, 3, 0xFF, 7},
+    {9, 5, 6, 3, 0xFF, 7},
 
     // replace second trailing byte with ASCII byte, also incomplete at end
     {9, 5, 6, 3, 'z', 8},
 
     // replace second trailing byte with invalid byte, also incomplete at end
-    {9, 5, 6, 3, '\xFF', 8},
+    {9, 5, 6, 3, 0xFF, 8},
   };
   for (auto t : offsets)
     {
-      char in[array_size (valid_in)] = {};
-      CharT out[array_size (exp) - 1] = {};
+      InternT out[array_size (exp) - 1] = {};
       VERIFY (t.in_size <= array_size (in));
       VERIFY (t.out_size <= array_size (out));
       VERIFY (t.expected_in_next <= t.in_size);
       VERIFY (t.expected_out_next <= t.out_size);
-      char_traits<char>::copy (in, valid_in, array_size (valid_in));
+      auto old_char = in[t.replace_pos];
       in[t.replace_pos] = t.replace_char;
 
       auto state = mbstate_t{};
-      auto in_next = (const char *) nullptr;
-      auto out_next = (CharT *) nullptr;
+      auto in_next = (const ExternT *) nullptr;
+      auto out_next = (InternT *) nullptr;
       auto res = codecvt_base::result ();
 
       res = cvt.in (state, in, in + t.in_size, in_next, out, out + t.out_size,
@@ -635,48 +647,51 @@  utf8_to_utf16_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
       VERIFY (res == cvt.error);
       VERIFY (in_next == in + t.expected_in_next);
       VERIFY (out_next == out + t.expected_out_next);
-      VERIFY (char_traits<CharT>::compare (out, exp, t.expected_out_next) == 0);
+      VERIFY (char_traits<InternT>::compare (out, exp, t.expected_out_next)
+	      == 0);
       if (t.expected_out_next < array_size (out))
 	VERIFY (out[t.expected_out_next] == 0);
+
+      in[t.replace_pos] = old_char;
     }
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-utf8_to_utf16_in (const std::codecvt<CharT, char, mbstate_t> &cvt)
+utf8_to_utf16_in (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   utf8_to_utf16_in_ok (cvt);
   utf8_to_utf16_in_partial (cvt);
   utf8_to_utf16_in_error (cvt);
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-utf16_to_utf8_out_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
+utf16_to_utf8_out_ok (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   using namespace std;
   // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
-  const char16_t in_literal[] = u"bш\uAAAA\U0010AAAA";
-  const char exp[] = "bш\uAAAA\U0010AAAA";
-  CharT in[array_size (in_literal)];
-  copy (begin (in_literal), end (in_literal), begin (in));
-
-  static_assert (array_size (in_literal) == 6, "");
-  static_assert (array_size (exp) == 11, "");
-  static_assert (array_size (in) == 6, "");
-  VERIFY (char_traits<char16_t>::length (in_literal) == 5);
-  VERIFY (char_traits<char>::length (exp) == 10);
-  VERIFY (char_traits<CharT>::length (in) == 5);
+  const char16_t input[] = u"bш\uAAAA\U0010AAAA";
+  const unsigned char expected[] = "bш\uAAAA\U0010AAAA";
+  static_assert (array_size (input) == 6, "");
+  static_assert (array_size (expected) == 11, "");
+
+  InternT in[array_size (input)];
+  ExternT exp[array_size (expected)];
+  copy (begin (input), end (input), begin (in));
+  copy (begin (expected), end (expected), begin (exp));
+  VERIFY (char_traits<InternT>::length (in) == 5);
+  VERIFY (char_traits<ExternT>::length (exp) == 10);
 
   const test_offsets_ok offsets[] = {{0, 0}, {1, 1}, {2, 3}, {3, 6}, {5, 10}};
   for (auto t : offsets)
     {
-      char out[array_size (exp) - 1] = {};
+      ExternT out[array_size (exp) - 1] = {};
       VERIFY (t.in_size <= array_size (in));
       VERIFY (t.out_size <= array_size (out));
       auto state = mbstate_t{};
-      auto in_next = (const CharT *) nullptr;
-      auto out_next = (char *) nullptr;
+      auto in_next = (const InternT *) nullptr;
+      auto out_next = (ExternT *) nullptr;
       auto res = codecvt_base::result ();
 
       res = cvt.out (state, in, in + t.in_size, in_next, out, out + t.out_size,
@@ -684,29 +699,29 @@  utf16_to_utf8_out_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
       VERIFY (res == cvt.ok);
       VERIFY (in_next == in + t.in_size);
       VERIFY (out_next == out + t.out_size);
-      VERIFY (char_traits<char>::compare (out, exp, t.out_size) == 0);
+      VERIFY (char_traits<ExternT>::compare (out, exp, t.out_size) == 0);
       if (t.out_size < array_size (out))
 	VERIFY (out[t.out_size] == 0);
     }
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-utf16_to_utf8_out_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
+utf16_to_utf8_out_partial (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   using namespace std;
   // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
-  const char16_t in_literal[] = u"bш\uAAAA\U0010AAAA";
-  const char exp[] = "bш\uAAAA\U0010AAAA";
-  CharT in[array_size (in_literal)];
-  copy (begin (in_literal), end (in_literal), begin (in));
-
-  static_assert (array_size (in_literal) == 6, "");
-  static_assert (array_size (exp) == 11, "");
-  static_assert (array_size (in) == 6, "");
-  VERIFY (char_traits<char16_t>::length (in_literal) == 5);
-  VERIFY (char_traits<char>::length (exp) == 10);
-  VERIFY (char_traits<CharT>::length (in) == 5);
+  const char16_t input[] = u"bш\uAAAA\U0010AAAA";
+  const unsigned char expected[] = "bш\uAAAA\U0010AAAA";
+  static_assert (array_size (input) == 6, "");
+  static_assert (array_size (expected) == 11, "");
+
+  InternT in[array_size (input)];
+  ExternT exp[array_size (expected)];
+  copy (begin (input), end (input), begin (in));
+  copy (begin (expected), end (expected), begin (exp));
+  VERIFY (char_traits<InternT>::length (in) == 5);
+  VERIFY (char_traits<ExternT>::length (exp) == 10);
 
   const test_offsets_partial offsets[] = {
     {1, 0, 0, 0}, // no space for first CP
@@ -732,14 +747,14 @@  utf16_to_utf8_out_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
   };
   for (auto t : offsets)
     {
-      char out[array_size (exp) - 1] = {};
+      ExternT out[array_size (exp) - 1] = {};
       VERIFY (t.in_size <= array_size (in));
       VERIFY (t.out_size <= array_size (out));
       VERIFY (t.expected_in_next <= t.in_size);
       VERIFY (t.expected_out_next <= t.out_size);
       auto state = mbstate_t{};
-      auto in_next = (const CharT *) nullptr;
-      auto out_next = (char *) nullptr;
+      auto in_next = (const InternT *) nullptr;
+      auto out_next = (ExternT *) nullptr;
       auto res = codecvt_base::result ();
 
       res = cvt.out (state, in, in + t.in_size, in_next, out, out + t.out_size,
@@ -747,26 +762,32 @@  utf16_to_utf8_out_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
       VERIFY (res == cvt.partial);
       VERIFY (in_next == in + t.expected_in_next);
       VERIFY (out_next == out + t.expected_out_next);
-      VERIFY (char_traits<char>::compare (out, exp, t.expected_out_next) == 0);
+      VERIFY (char_traits<ExternT>::compare (out, exp, t.expected_out_next)
+	      == 0);
       if (t.expected_out_next < array_size (out))
 	VERIFY (out[t.expected_out_next] == 0);
     }
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-utf16_to_utf8_out_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
+utf16_to_utf8_out_error (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   using namespace std;
-  const char16_t valid_in[] = u"bш\uAAAA\U0010AAAA";
-  const char exp[] = "bш\uAAAA\U0010AAAA";
-
-  static_assert (array_size (valid_in) == 6, "");
-  static_assert (array_size (exp) == 11, "");
-  VERIFY (char_traits<char16_t>::length (valid_in) == 5);
-  VERIFY (char_traits<char>::length (exp) == 10);
-
-  test_offsets_error<CharT> offsets[] = {
+  // UTF-8 string of 1-byte CP, 2-byte CP, 3-byte CP and 4-byte CP
+  const char16_t input[] = u"bш\uAAAA\U0010AAAA";
+  const unsigned char expected[] = "bш\uAAAA\U0010AAAA";
+  static_assert (array_size (input) == 6, "");
+  static_assert (array_size (expected) == 11, "");
+
+  InternT in[array_size (input)];
+  ExternT exp[array_size (expected)];
+  copy (begin (input), end (input), begin (in));
+  copy (begin (expected), end (expected), begin (exp));
+  VERIFY (char_traits<InternT>::length (in) == 5);
+  VERIFY (char_traits<ExternT>::length (exp) == 10);
+
+  test_offsets_error<InternT> offsets[] = {
     {5, 10, 0, 0, 0xD800, 0},
     {5, 10, 0, 0, 0xDBFF, 0},
     {5, 10, 0, 0, 0xDC00, 0},
@@ -796,18 +817,17 @@  utf16_to_utf8_out_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
 
   for (auto t : offsets)
     {
-      CharT in[array_size (valid_in)] = {};
-      char out[array_size (exp) - 1] = {};
+      ExternT out[array_size (exp) - 1] = {};
       VERIFY (t.in_size <= array_size (in));
       VERIFY (t.out_size <= array_size (out));
       VERIFY (t.expected_in_next <= t.in_size);
       VERIFY (t.expected_out_next <= t.out_size);
-      copy (begin (valid_in), end (valid_in), begin (in));
+      auto old_char = in[t.replace_pos];
       in[t.replace_pos] = t.replace_char;
 
       auto state = mbstate_t{};
-      auto in_next = (const CharT *) nullptr;
-      auto out_next = (char *) nullptr;
+      auto in_next = (const InternT *) nullptr;
+      auto out_next = (ExternT *) nullptr;
       auto res = codecvt_base::result ();
 
       res = cvt.out (state, in, in + t.in_size, in_next, out, out + t.out_size,
@@ -815,56 +835,59 @@  utf16_to_utf8_out_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
       VERIFY (res == cvt.error);
       VERIFY (in_next == in + t.expected_in_next);
       VERIFY (out_next == out + t.expected_out_next);
-      VERIFY (char_traits<char>::compare (out, exp, t.expected_out_next) == 0);
+      VERIFY (char_traits<ExternT>::compare (out, exp, t.expected_out_next)
+	      == 0);
       if (t.expected_out_next < array_size (out))
 	VERIFY (out[t.expected_out_next] == 0);
+
+      in[t.replace_pos] = old_char;
     }
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-utf16_to_utf8_out (const std::codecvt<CharT, char, mbstate_t> &cvt)
+utf16_to_utf8_out (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   utf16_to_utf8_out_ok (cvt);
   utf16_to_utf8_out_partial (cvt);
   utf16_to_utf8_out_error (cvt);
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-test_utf8_utf16_cvts (const std::codecvt<CharT, char, mbstate_t> &cvt)
+test_utf8_utf16_cvt (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   utf8_to_utf16_in (cvt);
   utf16_to_utf8_out (cvt);
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-utf8_to_ucs2_in_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
+utf8_to_ucs2_in_ok (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   using namespace std;
   // UTF-8 string of 1-byte CP, 2-byte CP and 3-byte CP
-  const char in[] = "bш\uAAAA";
-  const char16_t exp_literal[] = u"bш\uAAAA";
-  CharT exp[array_size (exp_literal)] = {};
-  copy (begin (exp_literal), end (exp_literal), begin (exp));
-
-  static_assert (array_size (in) == 7, "");
-  static_assert (array_size (exp_literal) == 4, "");
-  static_assert (array_size (exp) == 4, "");
-  VERIFY (char_traits<char>::length (in) == 6);
-  VERIFY (char_traits<char16_t>::length (exp_literal) == 3);
-  VERIFY (char_traits<CharT>::length (exp) == 3);
+  const unsigned char input[] = "bш\uAAAA";
+  const char16_t expected[] = u"bш\uAAAA";
+  static_assert (array_size (input) == 7, "");
+  static_assert (array_size (expected) == 4, "");
+
+  ExternT in[array_size (input)];
+  InternT exp[array_size (expected)];
+  copy (begin (input), end (input), begin (in));
+  copy (begin (expected), end (expected), begin (exp));
+  VERIFY (char_traits<ExternT>::length (in) == 6);
+  VERIFY (char_traits<InternT>::length (exp) == 3);
 
   test_offsets_ok offsets[] = {{0, 0}, {1, 1}, {3, 2}, {6, 3}};
   for (auto t : offsets)
     {
-      CharT out[array_size (exp) - 1] = {};
+      InternT out[array_size (exp) - 1] = {};
       VERIFY (t.in_size <= array_size (in));
       VERIFY (t.out_size <= array_size (out));
       auto state = mbstate_t{};
-      auto in_next = (const char *) nullptr;
-      auto out_next = (CharT *) nullptr;
+      auto in_next = (const ExternT *) nullptr;
+      auto out_next = (InternT *) nullptr;
       auto res = codecvt_base::result ();
 
       res = cvt.in (state, in, in + t.in_size, in_next, out, out + t.out_size,
@@ -872,19 +895,19 @@  utf8_to_ucs2_in_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
       VERIFY (res == cvt.ok);
       VERIFY (in_next == in + t.in_size);
       VERIFY (out_next == out + t.out_size);
-      VERIFY (char_traits<CharT>::compare (out, exp, t.out_size) == 0);
+      VERIFY (char_traits<InternT>::compare (out, exp, t.out_size) == 0);
       if (t.out_size < array_size (out))
 	VERIFY (out[t.out_size] == 0);
     }
 
   for (auto t : offsets)
     {
-      CharT out[array_size (exp)] = {};
+      InternT out[array_size (exp)] = {};
       VERIFY (t.in_size <= array_size (in));
       VERIFY (t.out_size <= array_size (out));
       auto state = mbstate_t{};
-      auto in_next = (const char *) nullptr;
-      auto out_next = (CharT *) nullptr;
+      auto in_next = (const ExternT *) nullptr;
+      auto out_next = (InternT *) nullptr;
       auto res = codecvt_base::result ();
 
       res
@@ -892,29 +915,29 @@  utf8_to_ucs2_in_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
       VERIFY (res == cvt.ok);
       VERIFY (in_next == in + t.in_size);
       VERIFY (out_next == out + t.out_size);
-      VERIFY (char_traits<CharT>::compare (out, exp, t.out_size) == 0);
+      VERIFY (char_traits<InternT>::compare (out, exp, t.out_size) == 0);
       if (t.out_size < array_size (out))
 	VERIFY (out[t.out_size] == 0);
     }
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-utf8_to_ucs2_in_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
+utf8_to_ucs2_in_partial (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   using namespace std;
   // UTF-8 string of 1-byte CP, 2-byte CP and 3-byte CP
-  const char in[] = "bш\uAAAA";
-  const char16_t exp_literal[] = u"bш\uAAAA";
-  CharT exp[array_size (exp_literal)] = {};
-  copy (begin (exp_literal), end (exp_literal), begin (exp));
-
-  static_assert (array_size (in) == 7, "");
-  static_assert (array_size (exp_literal) == 4, "");
-  static_assert (array_size (exp) == 4, "");
-  VERIFY (char_traits<char>::length (in) == 6);
-  VERIFY (char_traits<char16_t>::length (exp_literal) == 3);
-  VERIFY (char_traits<CharT>::length (exp) == 3);
+  const unsigned char input[] = "bш\uAAAA";
+  const char16_t expected[] = u"bш\uAAAA";
+  static_assert (array_size (input) == 7, "");
+  static_assert (array_size (expected) == 4, "");
+
+  ExternT in[array_size (input)];
+  InternT exp[array_size (expected)];
+  copy (begin (input), end (input), begin (in));
+  copy (begin (expected), end (expected), begin (exp));
+  VERIFY (char_traits<ExternT>::length (in) == 6);
+  VERIFY (char_traits<InternT>::length (exp) == 3);
 
   test_offsets_partial offsets[] = {
     {1, 0, 0, 0}, // no space for first CP
@@ -932,14 +955,14 @@  utf8_to_ucs2_in_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
 
   for (auto t : offsets)
     {
-      CharT out[array_size (exp) - 1] = {};
+      InternT out[array_size (exp) - 1] = {};
       VERIFY (t.in_size <= array_size (in));
       VERIFY (t.out_size <= array_size (out));
       VERIFY (t.expected_in_next <= t.in_size);
       VERIFY (t.expected_out_next <= t.out_size);
       auto state = mbstate_t{};
-      auto in_next = (const char *) nullptr;
-      auto out_next = (CharT *) nullptr;
+      auto in_next = (const ExternT *) nullptr;
+      auto out_next = (InternT *) nullptr;
       auto res = codecvt_base::result ();
 
       res = cvt.in (state, in, in + t.in_size, in_next, out, out + t.out_size,
@@ -947,36 +970,37 @@  utf8_to_ucs2_in_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
       VERIFY (res == cvt.partial);
       VERIFY (in_next == in + t.expected_in_next);
       VERIFY (out_next == out + t.expected_out_next);
-      VERIFY (char_traits<CharT>::compare (out, exp, t.expected_out_next) == 0);
+      VERIFY (char_traits<InternT>::compare (out, exp, t.expected_out_next)
+	      == 0);
       if (t.expected_out_next < array_size (out))
 	VERIFY (out[t.expected_out_next] == 0);
     }
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-utf8_to_ucs2_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
+utf8_to_ucs2_in_error (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   using namespace std;
-  const char valid_in[] = "bш\uAAAA\U0010AAAA";
-  const char16_t exp_literal[] = u"bш\uAAAA\U0010AAAA";
-  CharT exp[array_size (exp_literal)] = {};
-  copy (begin (exp_literal), end (exp_literal), begin (exp));
+  const unsigned char input[] = "bш\uAAAA\U0010AAAA";
+  const char16_t expected[] = u"bш\uAAAA\U0010AAAA";
+  static_assert (array_size (input) == 11, "");
+  static_assert (array_size (expected) == 6, "");
 
-  static_assert (array_size (valid_in) == 11, "");
-  static_assert (array_size (exp_literal) == 6, "");
-  static_assert (array_size (exp) == 6, "");
-  VERIFY (char_traits<char>::length (valid_in) == 10);
-  VERIFY (char_traits<char16_t>::length (exp_literal) == 5);
-  VERIFY (char_traits<CharT>::length (exp) == 5);
+  ExternT in[array_size (input)];
+  InternT exp[array_size (expected)];
+  copy (begin (input), end (input), begin (in));
+  copy (begin (expected), end (expected), begin (exp));
+  VERIFY (char_traits<ExternT>::length (in) == 10);
+  VERIFY (char_traits<InternT>::length (exp) == 5);
 
-  test_offsets_error<char> offsets[] = {
+  test_offsets_error<unsigned char> offsets[] = {
 
     // replace leading byte with invalid byte
-    {1, 5, 0, 0, '\xFF', 0},
-    {3, 5, 1, 1, '\xFF', 1},
-    {6, 5, 3, 2, '\xFF', 3},
-    {10, 5, 6, 3, '\xFF', 6},
+    {1, 5, 0, 0, 0xFF, 0},
+    {3, 5, 1, 1, 0xFF, 1},
+    {6, 5, 3, 2, 0xFF, 3},
+    {10, 5, 6, 3, 0xFF, 6},
 
     // replace first trailing byte with ASCII byte
     {3, 5, 1, 1, 'z', 2},
@@ -984,21 +1008,21 @@  utf8_to_ucs2_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
     {10, 5, 6, 3, 'z', 7},
 
     // replace first trailing byte with invalid byte
-    {3, 5, 1, 1, '\xFF', 2},
-    {6, 5, 3, 2, '\xFF', 4},
-    {10, 5, 6, 3, '\xFF', 7},
+    {3, 5, 1, 1, 0xFF, 2},
+    {6, 5, 3, 2, 0xFF, 4},
+    {10, 5, 6, 3, 0xFF, 7},
 
     // replace second trailing byte with ASCII byte
     {6, 5, 3, 2, 'z', 5},
     {10, 5, 6, 3, 'z', 8},
 
     // replace second trailing byte with invalid byte
-    {6, 5, 3, 2, '\xFF', 5},
-    {10, 5, 6, 3, '\xFF', 8},
+    {6, 5, 3, 2, 0xFF, 5},
+    {10, 5, 6, 3, 0xFF, 8},
 
     // replace third trailing byte
     {10, 5, 6, 3, 'z', 9},
-    {10, 5, 6, 3, '\xFF', 9},
+    {10, 5, 6, 3, 0xFF, 9},
 
     // When we see a leading byte of 4-byte CP, we should return error, no
     // matter if it is incomplete at the end or has errors in the trailing
@@ -1020,36 +1044,35 @@  utf8_to_ucs2_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
     {5, 5, 3, 2, 'z', 4},
 
     // replace first trailing byte with invalid byte, also incomplete at end
-    {5, 5, 3, 2, '\xFF', 4},
+    {5, 5, 3, 2, 0xFF, 4},
 
     // replace first trailing byte with ASCII byte, also incomplete at end
     {8, 5, 6, 3, 'z', 7},
     {9, 5, 6, 3, 'z', 7},
 
     // replace first trailing byte with invalid byte, also incomplete at end
-    {8, 5, 6, 3, '\xFF', 7},
-    {9, 5, 6, 3, '\xFF', 7},
+    {8, 5, 6, 3, 0xFF, 7},
+    {9, 5, 6, 3, 0xFF, 7},
 
     // replace second trailing byte with ASCII byte, also incomplete at end
     {9, 5, 6, 3, 'z', 8},
 
     // replace second trailing byte with invalid byte, also incomplete at end
-    {9, 5, 6, 3, '\xFF', 8},
+    {9, 5, 6, 3, 0xFF, 8},
   };
   for (auto t : offsets)
     {
-      char in[array_size (valid_in)] = {};
-      CharT out[array_size (exp) - 1] = {};
+      InternT out[array_size (exp) - 1] = {};
       VERIFY (t.in_size <= array_size (in));
       VERIFY (t.out_size <= array_size (out));
       VERIFY (t.expected_in_next <= t.in_size);
       VERIFY (t.expected_out_next <= t.out_size);
-      char_traits<char>::copy (in, valid_in, array_size (valid_in));
+      auto old_char = in[t.replace_pos];
       in[t.replace_pos] = t.replace_char;
 
       auto state = mbstate_t{};
-      auto in_next = (const char *) nullptr;
-      auto out_next = (CharT *) nullptr;
+      auto in_next = (const ExternT *) nullptr;
+      auto out_next = (InternT *) nullptr;
       auto res = codecvt_base::result ();
 
       res = cvt.in (state, in, in + t.in_size, in_next, out, out + t.out_size,
@@ -1057,48 +1080,51 @@  utf8_to_ucs2_in_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
       VERIFY (res == cvt.error);
       VERIFY (in_next == in + t.expected_in_next);
       VERIFY (out_next == out + t.expected_out_next);
-      VERIFY (char_traits<CharT>::compare (out, exp, t.expected_out_next) == 0);
+      VERIFY (char_traits<InternT>::compare (out, exp, t.expected_out_next)
+	      == 0);
       if (t.expected_out_next < array_size (out))
 	VERIFY (out[t.expected_out_next] == 0);
+
+      in[t.replace_pos] = old_char;
     }
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-utf8_to_ucs2_in (const std::codecvt<CharT, char, mbstate_t> &cvt)
+utf8_to_ucs2_in (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   utf8_to_ucs2_in_ok (cvt);
   utf8_to_ucs2_in_partial (cvt);
   utf8_to_ucs2_in_error (cvt);
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-ucs2_to_utf8_out_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
+ucs2_to_utf8_out_ok (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   using namespace std;
   // UTF-8 string of 1-byte CP, 2-byte CP and 3-byte CP
-  const char16_t in_literal[] = u"bш\uAAAA";
-  const char exp[] = "bш\uAAAA";
-  CharT in[array_size (in_literal)] = {};
-  copy (begin (in_literal), end (in_literal), begin (in));
-
-  static_assert (array_size (in_literal) == 4, "");
-  static_assert (array_size (exp) == 7, "");
-  static_assert (array_size (in) == 4, "");
-  VERIFY (char_traits<char16_t>::length (in_literal) == 3);
-  VERIFY (char_traits<char>::length (exp) == 6);
-  VERIFY (char_traits<CharT>::length (in) == 3);
+  const char16_t input[] = u"bш\uAAAA";
+  const unsigned char expected[] = "bш\uAAAA";
+  static_assert (array_size (input) == 4, "");
+  static_assert (array_size (expected) == 7, "");
+
+  InternT in[array_size (input)];
+  ExternT exp[array_size (expected)];
+  copy (begin (input), end (input), begin (in));
+  copy (begin (expected), end (expected), begin (exp));
+  VERIFY (char_traits<InternT>::length (in) == 3);
+  VERIFY (char_traits<ExternT>::length (exp) == 6);
 
   const test_offsets_ok offsets[] = {{0, 0}, {1, 1}, {2, 3}, {3, 6}};
   for (auto t : offsets)
     {
-      char out[array_size (exp) - 1] = {};
+      ExternT out[array_size (exp) - 1] = {};
       VERIFY (t.in_size <= array_size (in));
       VERIFY (t.out_size <= array_size (out));
       auto state = mbstate_t{};
-      auto in_next = (const CharT *) nullptr;
-      auto out_next = (char *) nullptr;
+      auto in_next = (const InternT *) nullptr;
+      auto out_next = (ExternT *) nullptr;
       auto res = codecvt_base::result ();
 
       res = cvt.out (state, in, in + t.in_size, in_next, out, out + t.out_size,
@@ -1106,29 +1132,29 @@  ucs2_to_utf8_out_ok (const std::codecvt<CharT, char, mbstate_t> &cvt)
       VERIFY (res == cvt.ok);
       VERIFY (in_next == in + t.in_size);
       VERIFY (out_next == out + t.out_size);
-      VERIFY (char_traits<char>::compare (out, exp, t.out_size) == 0);
+      VERIFY (char_traits<ExternT>::compare (out, exp, t.out_size) == 0);
       if (t.out_size < array_size (out))
 	VERIFY (out[t.out_size] == 0);
     }
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-ucs2_to_utf8_out_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
+ucs2_to_utf8_out_partial (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   using namespace std;
   // UTF-8 string of 1-byte CP, 2-byte CP and 3-byte CP
-  const char16_t in_literal[] = u"bш\uAAAA";
-  const char exp[] = "bш\uAAAA";
-  CharT in[array_size (in_literal)] = {};
-  copy (begin (in_literal), end (in_literal), begin (in));
-
-  static_assert (array_size (in_literal) == 4, "");
-  static_assert (array_size (exp) == 7, "");
-  static_assert (array_size (in) == 4, "");
-  VERIFY (char_traits<char16_t>::length (in_literal) == 3);
-  VERIFY (char_traits<char>::length (exp) == 6);
-  VERIFY (char_traits<CharT>::length (in) == 3);
+  const char16_t input[] = u"bш\uAAAA";
+  const unsigned char expected[] = "bш\uAAAA";
+  static_assert (array_size (input) == 4, "");
+  static_assert (array_size (expected) == 7, "");
+
+  InternT in[array_size (input)];
+  ExternT exp[array_size (expected)];
+  copy (begin (input), end (input), begin (in));
+  copy (begin (expected), end (expected), begin (exp));
+  VERIFY (char_traits<InternT>::length (in) == 3);
+  VERIFY (char_traits<ExternT>::length (exp) == 6);
 
   const test_offsets_partial offsets[] = {
     {1, 0, 0, 0}, // no space for first CP
@@ -1142,14 +1168,14 @@  ucs2_to_utf8_out_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
   };
   for (auto t : offsets)
     {
-      char out[array_size (exp) - 1] = {};
+      ExternT out[array_size (exp) - 1] = {};
       VERIFY (t.in_size <= array_size (in));
       VERIFY (t.out_size <= array_size (out));
       VERIFY (t.expected_in_next <= t.in_size);
       VERIFY (t.expected_out_next <= t.out_size);
       auto state = mbstate_t{};
-      auto in_next = (const CharT *) nullptr;
-      auto out_next = (char *) nullptr;
+      auto in_next = (const InternT *) nullptr;
+      auto out_next = (ExternT *) nullptr;
       auto res = codecvt_base::result ();
 
       res = cvt.out (state, in, in + t.in_size, in_next, out, out + t.out_size,
@@ -1157,26 +1183,31 @@  ucs2_to_utf8_out_partial (const std::codecvt<CharT, char, mbstate_t> &cvt)
       VERIFY (res == cvt.partial);
       VERIFY (in_next == in + t.expected_in_next);
       VERIFY (out_next == out + t.expected_out_next);
-      VERIFY (char_traits<char>::compare (out, exp, t.expected_out_next) == 0);
+      VERIFY (char_traits<ExternT>::compare (out, exp, t.expected_out_next)
+	      == 0);
       if (t.expected_out_next < array_size (out))
 	VERIFY (out[t.expected_out_next] == 0);
     }
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-ucs2_to_utf8_out_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
+ucs2_to_utf8_out_error (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   using namespace std;
-  const char16_t valid_in[] = u"bш\uAAAA\U0010AAAA";
-  const char exp[] = "bш\uAAAA\U0010AAAA";
-
-  static_assert (array_size (valid_in) == 6, "");
-  static_assert (array_size (exp) == 11, "");
-  VERIFY (char_traits<char16_t>::length (valid_in) == 5);
-  VERIFY (char_traits<char>::length (exp) == 10);
-
-  test_offsets_error<CharT> offsets[] = {
+  const char16_t input[] = u"bш\uAAAA\U0010AAAA";
+  const unsigned char expected[] = "bш\uAAAA\U0010AAAA";
+  static_assert (array_size (input) == 6, "");
+  static_assert (array_size (expected) == 11, "");
+
+  InternT in[array_size (input)];
+  ExternT exp[array_size (expected)];
+  copy (begin (input), end (input), begin (in));
+  copy (begin (expected), end (expected), begin (exp));
+  VERIFY (char_traits<InternT>::length (in) == 5);
+  VERIFY (char_traits<ExternT>::length (exp) == 10);
+
+  test_offsets_error<InternT> offsets[] = {
     {5, 10, 0, 0, 0xD800, 0},
     {5, 10, 0, 0, 0xDBFF, 0},
     {5, 10, 0, 0, 0xDC00, 0},
@@ -1219,18 +1250,17 @@  ucs2_to_utf8_out_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
 
   for (auto t : offsets)
     {
-      CharT in[array_size (valid_in)] = {};
-      char out[array_size (exp) - 1] = {};
+      ExternT out[array_size (exp) - 1] = {};
       VERIFY (t.in_size <= array_size (in));
       VERIFY (t.out_size <= array_size (out));
       VERIFY (t.expected_in_next <= t.in_size);
       VERIFY (t.expected_out_next <= t.out_size);
-      copy (begin (valid_in), end (valid_in), begin (in));
+      auto old_char = in[t.replace_pos];
       in[t.replace_pos] = t.replace_char;
 
       auto state = mbstate_t{};
-      auto in_next = (const CharT *) nullptr;
-      auto out_next = (char *) nullptr;
+      auto in_next = (const InternT *) nullptr;
+      auto out_next = (ExternT *) nullptr;
       auto res = codecvt_base::result ();
 
       res = cvt.out (state, in, in + t.in_size, in_next, out, out + t.out_size,
@@ -1238,24 +1268,27 @@  ucs2_to_utf8_out_error (const std::codecvt<CharT, char, mbstate_t> &cvt)
       VERIFY (res == cvt.error);
       VERIFY (in_next == in + t.expected_in_next);
       VERIFY (out_next == out + t.expected_out_next);
-      VERIFY (char_traits<char>::compare (out, exp, t.expected_out_next) == 0);
+      VERIFY (char_traits<ExternT>::compare (out, exp, t.expected_out_next)
+	      == 0);
       if (t.expected_out_next < array_size (out))
 	VERIFY (out[t.expected_out_next] == 0);
+
+      in[t.replace_pos] = old_char;
     }
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-ucs2_to_utf8_out (const std::codecvt<CharT, char, mbstate_t> &cvt)
+ucs2_to_utf8_out (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   ucs2_to_utf8_out_ok (cvt);
   ucs2_to_utf8_out_partial (cvt);
   ucs2_to_utf8_out_error (cvt);
 }
 
-template <class CharT>
+template <class InternT, class ExternT>
 void
-test_utf8_ucs2_cvts (const std::codecvt<CharT, char, mbstate_t> &cvt)
+test_utf8_ucs2_cvt (const std::codecvt<InternT, ExternT, mbstate_t> &cvt)
 {
   utf8_to_ucs2_in (cvt);
   ucs2_to_utf8_out (cvt);
diff --git a/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode_char8_t.cc b/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode_char8_t.cc
new file mode 100644
index 000000000..8ab5ba79f
--- /dev/null
+++ b/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode_char8_t.cc
@@ -0,0 +1,53 @@ 
+// Copyright (C) 2020-2023 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run { target c++11 } }
+// { dg-require-cstdint "" }
+// { dg-options "-fchar8_t" }
+
+#include "codecvt_unicode.h"
+
+using namespace std;
+
+void
+test_utf8_utf32_codecvts ()
+{
+  using codecvt_c32_c8 = codecvt<char32_t, char8_t, mbstate_t>;
+  auto &loc_c = locale::classic ();
+  VERIFY (has_facet<codecvt_c32_c8> (loc_c));
+
+  auto &cvt = use_facet<codecvt_c32_c8> (loc_c);
+  test_utf8_utf32_cvt (cvt);
+}
+
+void
+test_utf8_utf16_codecvts ()
+{
+  using codecvt_c16_c8 = codecvt<char16_t, char8_t, mbstate_t>;
+  auto &loc_c = locale::classic ();
+  VERIFY (has_facet<codecvt_c16_c8> (loc_c));
+
+  auto &cvt = use_facet<codecvt_c16_c8> (loc_c);
+  test_utf8_utf16_cvt (cvt);
+}
+
+int
+main ()
+{
+  test_utf8_utf32_codecvts ();
+  test_utf8_utf16_codecvts ();
+}
diff --git a/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode_wchar_t.cc b/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode_wchar_t.cc
index 4fd1bfec6..6e9152b50 100644
--- a/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode_wchar_t.cc
+++ b/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode_wchar_t.cc
@@ -28,7 +28,7 @@  test_utf8_utf32_codecvts ()
 {
 #if __SIZEOF_WCHAR_T__ == 4
   codecvt_utf8<wchar_t> cvt;
-  test_utf8_utf32_codecvts (cvt);
+  test_utf8_utf32_cvt (cvt);
 #endif
 }
 
@@ -37,7 +37,7 @@  test_utf8_utf16_codecvts ()
 {
 #if __SIZEOF_WCHAR_T__ >= 2
   codecvt_utf8_utf16<wchar_t> cvt;
-  test_utf8_utf16_cvts (cvt);
+  test_utf8_utf16_cvt (cvt);
 #endif
 }
 
@@ -46,7 +46,7 @@  test_utf8_ucs2_codecvts ()
 {
 #if __SIZEOF_WCHAR_T__ == 2
   codecvt_utf8<wchar_t> cvt;
-  test_utf8_ucs2_cvts (cvt);
+  test_utf8_ucs2_cvt (cvt);
 #endif
 }