私用領域を使って UTF-8 エンコードされた文字列にバイナリを埋め込む術

ソースコードアスキーアートや罫線文字使って図を頑張って書いて埋め込むのってめんどいことがあるよね」「どうして人類はプレーンテキストに画像も埋め込めるようにしておかなかったんだろうな」みたいなことを考えていたら、「私用領域を使って UTF-8 エンコードされた文字列にバイナリを埋め込もうとしたらどうなるかな」という発想に至った。ということで考えてみよう。

私用領域とは

https://www.unicode.org/versions/Unicode15.0.0/ch23.pdf によれば、私用領域 (private use area) とは 

Private Use Area: U+E000–U+F8FF

The primary Private Use Area consists of code points in the range U+E000 to U+F8FF, for a total of 6,400 private-use characters.

Supplementary Private Use Areas

EncodingStructure.The entire Plane 15, with the exception of the noncharacters U+FFFFE and U+FFFFF, is defined to be the Supplementary Private Use Area-A. The entire Plane 16, with the exception of the noncharacters U+10FFFE and U+10FFFF, is defined to be the Supplementary Private Use Area-B. Together these areas make an addi- tional 131,068 code points available for private use.

とのことである。つまり、基本多言語面の「U+E000 以上 U+F8FF 以下」、第15面の「U+F0000 以上 U+FFFFD 以下」、第16面の「U+100000 以上 U+10FFFD 以下」が私用領域である。

 

ちなみに、上にも書いてあるとおり、U+FFFFE、U+FFFFF、U+10FFFE、U+10FFFF は私用領域ではなくて非文字 (noncharacter) という分類になる。今回の考察では、非文字を避けることを考える。

 

余談:非文字

ところで、そもそも「なぜ FFFE か FFFF で終わるコードポイントが全て非文字なのか」という点について気になったのでちょっと調べてみたところ、なんと 2001 年 10 月 1 日(20 年以上前!)のメーリングリスト http://www.unicode.org/mail-arch/unicode-ml/y2001-m10/0014.html に書いてあった。なんでも、UnicodeISO/IEC 10646 を統合する過程で意図せず紛れ込んでしまった仕様であり、気づいた頃には既に直せなくなっていたから今も残っている、という経緯らしい*1

> > BTW, it doesn't make sense for every code position
> > ending in FFFF or FFFE to be a non character.
>
>It doesn't make much sense, but it is the rule anyway.

This crept in during the merger of ISO/IEC 10646 and Unicode, and when it was
discovered, it was too late to do anything about it.

やっていき

U+0800 以上 U+FFFF 以下のものを UTF-8エンコードすると 3 バイト消費する。基本多言語面の「U+E000 以上 U+F8FF 以下」はここに当てはまるので、6400 種 (= 12.64 bit) をエンコードするのに 24 bit が必要という計算になる。

U+10000 以上 U+10FFFF 以下のものを UTF-8エンコードすると 4 バイト消費する。第 15・16 面の「U+F0000 以上 U+FFFFD 以下」「U+100000 以上 U+10FFFD 以下」はここに当てはまるので、131068 種 (= 16.99995597 bit) をエンコードするのに 32 bit が必要という計算になる。

ということは、基本多言語面のやつのみを使うと 52.68%、追加多言語面のやつのみを使うと 53.12% の効率でエンコードできる。

「普通に 0-9, A-F の文字で 16 進数を書く」よりは若干効率よく、かつプレーンテキストと紛れることが一切ない*2となると、わりと面白い使い方ができるのかもしれない。

 

両方使った場合はどうだろうか。6400 + 131068 種 (17.0687 bit) をエンコードするのに、6400/(6400+131068)(4.6556%)の確率で 24 bit、それ以外において 32 bit を要するので、

53.96% か。じゃあまあほとんど上がらないということで。

結論

  • 最初は全く使い物にならんかと思ったが、意外とアリな気がしてきた
  • それなりには使えそうなので、いつか応用するかもしれん

*1:正直言うと、この記事で真にお伝えしたい最大の面白話は実はこっちである

*2:みなさんも英単語をファイル内検索して Base64 文字列がヒットした経験は一度や二度ではないだろう