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

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

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

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

高木です。こんばんは。

10連休もいよいよ最終日になりました。
我々の業界は中小企業であっても10連休は普通ですが、世間的には中小企業で10連休のところは10%程度らしいと聞きました。
飲食店など、もともと土日も仕事があるようなところは仕方がないと思います。

さて、今回はいつもの連載記事です。
前回はざっくりとした実装でしたので、今回は完成度を上げていくことにします。

ところで前回、

UTF-8が途中で終わっていた場合にmbstate_t型のオブジェクトに状態を保存して再開できるようにしないといけない

と書きましたがこれは嘘でした。
正しくは途中で終わっていれば(size_t)-2を返すだけです。

これを踏まえて下記のように修正してみました。

namespace cloverfield
{
  inline std::size_t mbrtoct(char32_t* pc32, char const* s, std::size_t n, mbstate_t* ps)
  {
    if (n < 1)
      return -2;

    int c = static_cast<unsigned char>(*s);
    char32_t c32;
    std::size_t r;

    if (ps == nullptr)
      ps = detail::default_mbstate();

    if (c < 0x80)
    {
      c32 = c;
      if (c32 == 0)
        r = 0;
      else
        r = 1;
    }
    else if ((c & 0xe0) == 0xc0)
    {
      if (n < 2)
        return -2;
      c32 = (c & 0x1f) << 6 | s[1] & 0x3f;
      if (c32 < 0x80)
        goto illegal_sequence;
      else
        r = 2;
    }
    else if ((c & 0xf0) == 0xe0)
    {
      if (n < 3)
        return -2;
      c32 = (c & 0xf) << 12 | (s[1] & 0x3f) << 6 | s[2] & 0x3f;
      if (c32 < 0x800)
        goto illegal_sequence;
      else
        r = 3;
    }
    else if ((c & 0xf8) == 0xf0)
    {
      if (n < 4)
        return -2;
      c32 = (c & 0x7) << 18 | (s[1] & 0x3f) << 12 | (s[2] & 0x3f) << 6 | s[3] & 0x3f;
      if (c32 < 0x10000 || 0x10ffff < c32)
        goto illegal_sequence;
      else
        r = 4;
    }
    else
    {
      goto illegal_sequence;
    }

    if (pc32 != nullptr)
      *pc32 = c32;
    ps->state = c32;
    return r;

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

いつもはpsが空ポインタの場合、その都度大体のオブジェクトを用意していたのですが、今回から一カ所にまとめ、default_mbstate関数で引っ張ってこれるようにしました。
default_mbstate関数の実装は次のようになります。

namespace cloverfield
{
  namespace detail
  {
    inline mbstate_t* default_mbstate()
    {
      thread_local mbstate_t ts{};
      return &ts;
    }
  }
}

次回はmbrtoct関数のchar16_t版を作ることにします。

    上に戻る