大阪市中央区 システムソフトウェア開発会社

営業時間:平日09:15〜18:15
MENU

C++でC風ライブラリを作る(UTF-16からUTF-8への変換編2)

著者:高木信尚
公開日:2019/04/08
最終更新日:2019/04/07
カテゴリー:技術情報

高木です。おはようございます。

前回ctrtomb関数のchar16_t型版の仕様を考えました。
今回は、その仕様に基づいた実装を行っていきます。

あまり能書きが長いのは嫌になってきますので、早速コードを掲載することにします。

namespace cloverfield
{
  using mbstate_t = std::uint32_t;

  inline std::size_t ctrtomb(char* s, char16_t c16, mbstate_t* ps)
  {
    if (ps == nullptr)
    {
      thread_local mbstate_t ts{};
      ps = &ts;
    }

    if (s == nullptr)
    {
      static char buf[mb_len_max];
      s = buf;
      c16 = 0;
    }

    std::size_t result = -1;
    char32_t c32 = c16;
    if ((c16 & 0xfc00) == 0xd800)
    {
      *ps = c16;
      return 0;
    }
    else if ((c16 & 0xfc00) == 0xdc00)
    {
      if ((*ps & 0xfc00) != 0xd800)
        goto illegal_sequence;
      c32 = ((*ps & 0x3ff) << 10 | c16 & 0x3ff) + 0x10000;
    }

    if (c32 == 0)
    {
      s[0] = '\0';
      result = 1;
    }
    else if (c32 < 0x80)
    {
      *s = static_cast<char>(c32);
      result = 1;
    }
    else if (c32 < 0x800)
    {
      s[0] = static_cast<char>(0xc0 | (c32 >> 6));
      s[1] = static_cast<char>(0x80 | (c32 & 0x3f));
      result = 2;
    }
    else if (c32 < 0x10000)
    {
      s[0] = static_cast<char>(0xe0 | (c32 >> 12));
      s[1] = static_cast<char>(0x80 | ((c32 >> 6) & 0x3f));
      s[2] = static_cast<char>(0x80 | (c32 & 0x3f));
      result = 3;
    }
    else if (c32 < 0x110000)
    {
      s[0] = static_cast<char>(0xf0 | (c32 >> 18));
      s[1] = static_cast<char>(0x80 | ((c32 >> 12) & 0x3f));
      s[2] = static_cast<char>(0x80 | ((c32 >> 6) & 0x3f));
      s[3] = static_cast<char>(0x80 | (c32 & 0x3f));
      result = 4;
    }
    else
    {
      goto illegal_sequence;
    }
    *ps = c32;
    return result;

illegal_sequence:
    *ps = -1;
    throw std::invalid_argument("cloverfield::ctrtomb");
  }
}

ちょっと乱雑になってしまいましたが、こんな感じでどうでしょうか?
UTF-32からUTF-8への変換部分については、別の関数にして流用可能にしたほうがいいかもしれませんね。

今回はいったんここまでにして、次回、char32_t型版のctrtomb関数を実装する際にその対応を行うことにしましょう。

    上に戻る