C++でC風ライブラリを作る(UTF-8からUTF-32への変換編2)
高木です。こんばんは。
10連休もいよいよ最終日になりました。
我々の業界は中小企業であっても10連休は普通ですが、世間的には中小企業で10連休のところは10%程度らしいと聞きました。
飲食店など、もともと土日も仕事があるようなところは仕方がないと思います。
さて、今回はいつもの連載記事です。
前回はざっくりとした実装でしたので、今回は完成度を上げていくことにします。
ところで前回、
UTF-8が途中で終わっていた場合にmbstate_t型のオブジェクトに状態を保存して再開できるようにしないといけない
と書きましたがこれは嘘でした。
正しくは途中で終わっていれば(size_t)-2
を返すだけです。
これを踏まえて下記のように修正してみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | 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
関数の実装は次のようになります。
1 2 3 4 5 6 7 8 9 10 11 | namespace cloverfield { namespace detail { inline mbstate_t* default_mbstate() { thread_local mbstate_t ts{}; return &ts; } } } |
次回はmbrtoct
関数のchar16_t
版を作ることにします。