UTF-8 の後続バイトの範囲チェックは 0x80 から 0xBF までだけでは不十分

stackoverflow の質問と回答 (たとえば[こちら](http://stackoverflow.com/q/3911536/531320)) を読むと、後続バイトの範囲を `0x80` から `0xBF` までだけであるととして、次のようなビット演算を使うコードがたくさん見られます。

```c
#include <stdio.h>

#define utf8_trail(c) (((c) & 0xC0) == 0x80)

int main(void) {

    for (int i = 0; i < 0x100; ++i) {

        if (!utf8_trail(i)) {
            printf("%X は不正な後続バイトです。\n", i);
        }

    }

    return 0;
}
```

UTF-8 の仕様では先行バイトに応じて2番目のバイトの値の範囲が変わることがあるので、上記のコードでは不完全です。

不正なバイト列を示します。先行バイトが `0xE0` の場合、2番目のバイトの範囲が `0xA0` から `0xBF` になりますので、`0x80` から `0xBF` までの範囲のバイトは不正なバイトになります。

```c
#include <stdio.h>

#define utf8_trail(c) (((c) & 0xC0) == 0x80)

int main(void) {

    // 2番目のバイトは不正です。
    unsigned char str[] = {0xE0, 0x80, 0xBF, 0x0};

    if (utf8_trail(str[1])) {
        printf("%X は不正なバイトにも関わらず見逃されました。\n", str[1]);
    }

    return 0;
}
```

UTF-8 を扱う文字列関数を自分で定義するとき、バイトの値の範囲表を仕様のドキュメントで確認しましょう。
Unicode Standard 7.0 の[3章](http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf)に次のような表が記載されています (Table 3-7. Well-Formed UTF-8 Byte Sequences)。

          コードポイント  1番目のバイト 2番目のバイト 3番目のバイト 4番目のバイト
      U+0000 -   U+007F   00 - 7F
      U+0080 -   U+07FF   C2 - DF    80 - BF
      U+0800 -   U+0FFF   E0         A0 - BF     80 - BF
      U+1000 -   U+CFFF   E1 - EC    80 - BF     80 - BF
      U+D000 -   U+D7FF   ED         80 - 9F     80 - BF
      U+E000 -   U+FFFF   EE - EF    80 - BF     80 - BF
     U+10000 -  U+3FFFF   F0         90 - BF     80 - BF    80 - BF
     U+40000 -  U+FFFFF   F1 - F3    80 - BF     80 - BF    80 - BF
    U+100000 - U+10FFFF   F4         80 - 8F     80 - BF    80 - BF

[RFC 3629](https://tools.ietf.org/html/rfc3629#section-3) では次のように定義されています。

```
UTF8-octets = *( UTF8-char )
UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
UTF8-1      = %x00-7F
UTF8-2      = %xC2-DF UTF8-tail
UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / %xF4 %x80-8F 2( UTF8-tail )
UTF8-tail   = %x80-BF
```

範囲表をもとに妥当な3バイト文字であるかを判定するコードを書いてみると次のようになります。PCRE の pcre_valid_utf8.c を参考にさせていただきました (mpyw さん情報提供ありがとうございました)。3バイト文字以外を含む完全な実装は[こちらの記事](http://qiita.com/masakielastic/items/432b774569b44f959b1c)をご参照ください。

```c
#include <stdio.h>

#define utf8_trail(c) (((c) & 0xC0) == 0x80)
int utf8mb3_is_valid(const char*);

int main(void)
{
    // 2番目が不正なバイト
    char str[] = {0xE0, 0x80, 0xBF, 0x0};
    // 妥当な3バイト文字
    char str2[] = {0xE0, 0xA0, 0xBF, 0x0};

    if (!utf8mb3_is_valid(str)) {
        puts("str は不正なバイト列です。");
    }

    if (utf8mb3_is_valid(str2)) {
        puts("str2 は妥当な3バイト文字です。");
    }

    return 0;
}

int utf8mb3_is_valid(const char *str)
{
    unsigned char lead = *str;
    unsigned char trail = *(str + 1);
    unsigned char ab = 2; /* Number of additional bytes */

    if (0xE0 > lead || lead > 0xED) {
        return 0;
    }

    if (lead == 0xE0 && 0xA0 > trail) {
        return 0;
    }

    if (lead == 0xED && trail > 0x9F) {
        return 0;
    }

    do {

        trail = *(++str);
        if (!utf8_trail(trail)) {
            return 0;
        }

    } while (--ab);

    return 1;
}
```

`utf8mb3_is_valid` の引数を `const unsigned char` にすれば、`lead` と `trail` を宣言しなくてすみますが、標準関数との互換性を考慮して、const char* にしました ([STR04-C](https://www.jpcert.or.jp/sc-rules/c-str04-c.html))。

文字列関数を定義する場合、不正なバイト列であっても、一部の有効な範囲をもとにバイト数を数える必要があるので、先行バイトが `0xE0` と　`0xED` である場合の範囲チェックは前のほうにしました。

`0xE0, 0xA0, 0xBF` の場合、先行バイトが正しく、2番目のバイトが正しくないので、バイト数は1バイトになります。そして、次の `0xA0`、`0xBF` はそれぞれ先行バイトとして正しくないので、文字列関数はこの不正なバイト列は3文字として扱うことになります。

くわしくは Unicode Standard の「Table 3-8. Use of U+FFFD in UTF-8 Conversion」をご参照ください。Ruby の文字列メソッドや PHP の mbstring や `htmlspecialchars` などが対応しています。