C++でC風ライブラリを作る(UTF-16またはUTF-32からUTF-8への変換編)
高木です。こんばんは。
伊関が入社して以来、このブログもにわかに活気づいた感があります。
ひとつひとつの記事のクオリティを上げるには限界があるので、とにかくアウトプットを増やせばそのうちのいくらかは世の中の人たちのお役に立てるものが出てくるだろうと考えています。
いろんな人が多くの投稿をしてくれるのはありがたいことです。
さて、最近の私は週1投稿のペースを守っています。
ところが、他のメンバーが頑張っているのであっという間に私の投稿は流れていってしまいます。
今回は前回に引き続き、UTF-16からUTF-8への変換、そしてUTF-32からUTF-8への変換もいっしょにやってしまおうと思います。
多重定義する両方のctrtomb
関数に共通する部分をutf32_to_utf8
関数に切り出し、それぞれから呼び出すようにしました。
また、mbstate_t
型は前回まではstd::uint32_t
型そのものでしたが、誤使用を避けるため構造体にしました。
さらに、不正なサロゲートペアに対するエラー処理を強化しました。
以下にコードを掲載します。
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 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | namespace cloverfield { namespace detail { inline int utf32_to_utf8(char* s, char32_t c32) { int result = -1; 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; } return result; } } constexpr int mb_len_max = 4; constexpr int mb_cur_max = 4; struct mbstate_t { std::uint32_t state; }; inline std::size_t ctrtomb(char* s, char32_t c32, mbstate_t* ps) { if (ps == nullptr) { thread_local mbstate_t ts{}; ps = &ts; } if (s == nullptr) { static char buf[mb_len_max]; s = buf; c32 = 0; } int t; std::size_t result = -1; if (c32 != 0 && (ps->state & 0xfc00) == 0xd800) goto illegal_sequence; t = detail::utf32_to_utf8(s, c32); if (t < 0) goto illegal_sequence; result = t; ps->state = c32; return result; illegal_sequence: ps->state = -1; throw std::invalid_argument("cloverfield::ctrtomb"); } 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; } int t; std::size_t result = -1; char32_t c32 = c16; if (c32 != 0) { if ((ps->state & 0xfc00) == 0xd800) { if ((c16 & 0xfc00) == 0xdc00) c32 = ((ps->state & 0x3ff) << 10 | c16 & 0x3ff) + 0x10000; else goto illegal_sequence; } else if ((c16 & 0xfc00) == 0xd800) { ps->state = c16; return 0; } } t = detail::utf32_to_utf8(s, c32); if (t < 0) goto illegal_sequence; result = t; ps->state = c32; return result; illegal_sequence: ps->state = -1; throw std::invalid_argument("cloverfield::ctrtomb"); } } |
前回に引き続き、ほとんどコードを掲載するだけの手抜きな内容になってしまいましたね。
次回は引数にmbstate_t*
を取らないcttomb
関数に再度取り組みことにします。