| 1 | #include <iconv.h> |
|---|
| 2 | #include "EncodeString.hpp" |
|---|
| 3 | |
|---|
| 4 | using namespace std; |
|---|
| 5 | |
|---|
| 6 | EncodeStringImpl::cnmap_t const EncodeStringImpl::CharsetNames = { |
|---|
| 7 | { chset_Utf8, "UTF-8" }, { chset_C99, "C99" }, { chset_Java, "JAVA" }, |
|---|
| 8 | { chset_Ucs2, "UCS-2" }, { chset_Ucs2Be, "UCS-2BE" }, { chset_Ucs2Le, "UCS-2LE" }, |
|---|
| 9 | { chset_Ucs4, "UCS-4" }, { chset_Ucs4Be, "UCS-4BE" }, { chset_Ucs4Le, "UCS-4LE" }, |
|---|
| 10 | { chset_Utf16, "UTF-16" }, { chset_Utf16Be, "UTF-16BE" }, { chset_Utf16Le, "UTF-16LE" }, |
|---|
| 11 | { chset_Utf32, "UTF-32" }, { chset_Utf32Be, "UTF-32BE" }, { chset_Utf32Le, "UTF-32LE" }, |
|---|
| 12 | { chset_Utf7, "UTF-7" }, |
|---|
| 13 | { chset_EucJp, "EUC-JP" }, { chset_EucJis0213, "EUC-JISX0213" }, |
|---|
| 14 | { chset_Iso2022Jp, "ISO-2022-JP" }, { chset_Iso2022Jp2, "ISO-2022-JP2" }, |
|---|
| 15 | { chset_Iso2022Jp1, "ISO-2022-JP1" }, { chset_Iso2022Jp3, "ISO-2022-JP3" }, |
|---|
| 16 | { chset_ShiftJis, "SHIFT_JIS" }, { chset_Cp932, "CP932" }, { chset_ShiftJisX0213, "SHIFT_JISX0213" }, |
|---|
| 17 | }; |
|---|
| 18 | |
|---|
| 19 | EncodeStringImpl::EncodeStringImpl() |
|---|
| 20 | { |
|---|
| 21 | result_length = 0; |
|---|
| 22 | } |
|---|
| 23 | |
|---|
| 24 | void EncodeStringImpl::encode(void const *src, size_t src_length, size_t chr_size, charset_t from_charset, |
|---|
| 25 | charset_t to_charset) |
|---|
| 26 | { |
|---|
| 27 | if (from_charset == to_charset) { |
|---|
| 28 | char const *p = reinterpret_cast<char const *>(src); |
|---|
| 29 | result.assign(p, &p[(src_length + 1) * chr_size]); |
|---|
| 30 | return; |
|---|
| 31 | } |
|---|
| 32 | class auto_iconv_t { |
|---|
| 33 | const iconv_t impl; |
|---|
| 34 | public: |
|---|
| 35 | auto_iconv_t(charset_t from_cs, charset_t to_cs) : |
|---|
| 36 | impl(iconv_open(CharsetNames.find(to_cs)->second, CharsetNames.find(from_cs)->second)) |
|---|
| 37 | { |
|---|
| 38 | if (impl == reinterpret_cast<iconv_t>(-1)) { |
|---|
| 39 | throw EncodeStringException(string("LIBICONV initialize error: please check character set name \"") + |
|---|
| 40 | CharsetNames.find(from_cs)->second + "\"(from) or \"" + CharsetNames.find(to_cs)->second + |
|---|
| 41 | "\"(to)"); |
|---|
| 42 | } |
|---|
| 43 | } |
|---|
| 44 | ~auto_iconv_t() { iconv_close(impl); } |
|---|
| 45 | iconv_t get() const { return impl; } |
|---|
| 46 | } iconv_handle(from_charset, to_charset); |
|---|
| 47 | size_t const src_size = src_length * chr_size; |
|---|
| 48 | result.resize(src_size + TerminateNullCharNum, '\0'); // ヌル文字分余裕を持って確保する。 |
|---|
| 49 | char const *from_current = reinterpret_cast<char const *>(src); |
|---|
| 50 | char *to_current = &result[0]; |
|---|
| 51 | size_t from_left = src_size, to_left = src_size; |
|---|
| 52 | |
|---|
| 53 | #if defined(_ICONV_H) |
|---|
| 54 | while (iconv(iconv_handle.get(), const_cast<char **>(&from_current), &from_left, &to_current, &to_left) == |
|---|
| 55 | static_cast<size_t>(-1)) { |
|---|
| 56 | #elif defined(_LIBICONV_H) |
|---|
| 57 | while (iconv(iconv_handle.get(), &from_current, &from_left, &to_current, &to_left) == static_cast<size_t>(-1)) { |
|---|
| 58 | #endif //_ICONV_H, _LIBICONV_H |
|---|
| 59 | // ループ内はエラー処理 |
|---|
| 60 | |
|---|
| 61 | if (errno == EINVAL) // マルチバイトの下位オクテットが欠けている? |
|---|
| 62 | break; |
|---|
| 63 | |
|---|
| 64 | switch (errno) { |
|---|
| 65 | case E2BIG: // 出力先バッファが足りない |
|---|
| 66 | // バッファを拡張し、継続 |
|---|
| 67 | { |
|---|
| 68 | size_t pre_size = result.size(); |
|---|
| 69 | result.resize(pre_size + src_size, '\0'); |
|---|
| 70 | to_current = &result[pre_size - TerminateNullCharNum - to_left]; |
|---|
| 71 | to_left += src_size; |
|---|
| 72 | } |
|---|
| 73 | break; |
|---|
| 74 | case EILSEQ: // あり得ないコードが出現 |
|---|
| 75 | // エラー回避し、続きから変換を継続 |
|---|
| 76 | from_current++; |
|---|
| 77 | from_left--; |
|---|
| 78 | break; |
|---|
| 79 | case EBADF: // 変換に失敗 (原因不明) |
|---|
| 80 | throw EncodeStringException("LIBICONV bad conversion error (EBADF)"); |
|---|
| 81 | default: // 未定義のエラー |
|---|
| 82 | throw EncodeStringException("LIBICONV unknown error"); |
|---|
| 83 | } |
|---|
| 84 | } |
|---|
| 85 | result_length = result.size() - to_left - TerminateNullCharNum; |
|---|
| 86 | } |
|---|