C++でC風ライブラリを作る(UTF-16からUTF-8への変換編2)
高木です。おはようございます。
前回はctrtomb
関数のchar16_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 | 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への変換部分については、別の関数にして流用可能にしたほうがいいかもしれませんね。
今回はいったんここまでにして、次回、ctrtomb
関数を実装する際にその対応を行うことにしましょう。