Math.clz32
という標準関数を、UTF-8の処理に使ってみた話です。
Math.clz32
とは
clz
はCount Leading Zeros(先頭にある0の数を数える)の略で、その名の通り、引数を32ビット符号なし整数として、先頭に入る0の個数を数える関数です。
ARMやx64にも搭載されるなどメジャーで高速に実行可能な演算で、他のビット操作を構築する要素としても使えるとのことです。
UTF-8のビットパターン
UTF-8はマルチバイトの文字コードですが、シフトJISで起きた「ダメ文字」(2バイト目にASCIIの範囲まで使っていたことで、ASCIIとしての処理が行われて誤作動する)などの問題を避ける、そして文字の区切りをはっきりさせるために、バイトごとのビットパターンによって使われる場所をはっきり区分けしています1。
16進 | ビットパターン | 役割 |
---|---|---|
0x00 - 0x7F | 0xxxxxxx | 1バイト文字 |
0x80 - 0xBF | 10xxxxxx | マルチバイト文字の2バイト目以降 |
0xC0 - 0xDF | 110xxxxx | 2バイト文字の1バイト目 |
0xE0 - 0xEF | 1110xxxx | 3バイト文字の1バイト目 |
0xF0 - 0xF7 | 11110xxx | 4バイト文字の1バイト目 |
0xF8 - 0xFF | 11111xxx | 未使用 |
ご覧のように、「先頭に続く1の数」でバイトの種類を判定しています。
Math.clz32
で計算
0xFF
とのXORを取れば、0は1に、1は0に反転するので、Math.clz32
で処理するのにふさわしい形となります。そして、上位24ビットは0なので、Math.clz32(value ^ 0xFF) - 24
とすれば、使いやすい値が取れます。特に、2~4がそのまま取れるのも便利なところです。
16進 | ビットパターン | 演算結果 | 役割 |
---|---|---|---|
0x00 - 0x7F | 0xxxxxxx | 0 | 1バイト文字 |
0x80 - 0xBF | 10xxxxxx | 1 | マルチバイト文字の2バイト目以降 |
0xC0 - 0xDF | 110xxxxx | 2 | 2バイト文字の1バイト目 |
0xE0 - 0xEF | 1110xxxx | 3 | 3バイト文字の1バイト目 |
0xF0 - 0xF7 | 11110xxx | 4 | 4バイト文字の1バイト目 |
0xF8 - 0xFF | 11111xxx | 5以上 | 未使用 |
外部リンク
-
この表はビットパターンだけ表すために簡略化したもので、0xC0、0xC1、0xF5-0xF7は正当なUTF-8には登場しません。 ↩