UTF-8

文字コードについて悩むのをやめて、ゴリゴリとコーディングすることにした。UTF-8のmanpageRFC3629日本語訳を参考にしてUCS-4-->UTF-8へのエンコーディングルーチン。

/* 一文字のUCS-4をUTF-8オクテット列にエンコードする */
size_t ucs4_to_utf8(char * buf, const size_t size, const unsigned src)
{
  size_t n;

  assert(buf != NULL && size != 0);

  if(src <= 0x0000007f) n = 1;
  else if(src <= 0x000007ff) n = 2;
  else if(src <= 0x0000fffd)
  {
    if(src >= 0x0000d800 && src <= 0x0000dfff) n = 0;
    else n = 3;
  }
  else if(src <= 0x0000ffff) n = 0;
  else if(src <= 0x001fffff) n = 4;
  else if(src <= 0x03ffffff) n = 5;
  else if(src <= 0x7fffffff) n = 6;
  else n = 0;

  if(size < n) return 0;

  switch(n)
  {
    case 6:
      buf[5] = 0x80 | (src & 0x3f);
      buf[4] = 0x80 | ((src >> 6) & 0x3f);
      buf[3] = 0x80 | ((src >> 12) & 0x3f);
      buf[2] = 0x80 | ((src >> 18) & 0x3f);
      buf[1] = 0x80 | ((src >> 24) & 0x3f);
      buf[0] = 0xfc | ((src >> 30) & 0x01);
      break;
    case 5:
      buf[4] = 0x80 | (src & 0x3f);
      buf[3] = 0x80 | ((src >> 6) & 0x3f);
      buf[2] = 0x80 | ((src >> 12) & 0x3f);
      buf[1] = 0x80 | ((src >> 18) & 0x3f);
      buf[0] = 0xf8 | ((src >> 24) & 0x03);
      break;
    case 4:
      buf[3] = 0x80 | (src & 0x3f);
      buf[2] = 0x80 | ((src >> 6) & 0x3f);
      buf[1] = 0x80 | ((src >> 12) & 0x3f);
      buf[0] = 0xf0 | ((src >> 18) & 0x07);
      break;
    case 3:
      buf[2] = 0x80 | (src & 0x3f);
      buf[1] = 0x80 | ((src >> 6) & 0x3f);
      buf[0] = 0xe0 | ((src >> 12) & 0x0f);
      break;
    case 2:
      buf[1] = 0x80 | (src & 0x3f);
      buf[0] = 0xc0 | ((src >> 6) & 0x1f);
      break;
    case 1:
      buf[0] = src;
      break;
  }

  return n;
}

ビット操作とかあまり慣れてなくて電卓片手に書いたんだけど、一発でコンパイル通ってバグらしいバグもナシ。あとは使いでを良くするためにこいつを使った文字列変換関数とか書けばいいな。インターフェイスや返り値の仕様なんかはWinAPIのWideCharToMultiByte()とかを参考にすればいいのかしら?