概要:
- 現行のRaw型をString型として読み替える。
- 現行の FixRaw, raw 16, raw 32 は、FixString, string 16, string 32 になる
- Binary型を新設する。
- バイト列はBinary型で保存する。そうでなければString型で保存する。
文字列とバイナリの区別が曖昧な言語が存在する。例えば:
- 文字列とバイト列を区別する型がそもそも無い言語(PHP, C++, Erlang, OCaml)
- 文字列を表すためにフラグ等の付加情報が使われるが、文字列なのに付加情報が付与されていないケースが一般的に存在する言語(Ruby, Perl, Python 2)
これらの言語を weak-string languages と呼ぶ。
一方で、区別が明確な dynamically-typed languages も存在する(JavaScript、Objective-C、Python 3)。また、区別が明確な statically-typed languages が存在する(Java、C#、Scala)。これらの言語を strong-string languages と呼ぶ。
strong-string and dynamically-typed languages が受信者となる オブジェクトのやりとりでは、文字列として送信/保存したデータを文字列として、バイト列として送信/保存したバイト列をバイト列として、透過的に復元したいという要求が存在する。(※この要求は、その他の組み合わせの通信では存在しない)
一方で、weak-string languages では、すべての文字列に対して「これは文字列である」とマーカを付与する、あるいはすべてのバイト列に「これはバイト列である」というマーカを付与する作業は手間がかかる。従ってこれらの言語で書かれたプログラムで、文字列とバイト列が明確に区別できることを期待することは現実的ではない。
そこで、次のメリットを同時に満たす変更を提案する:
- strong-string languages 同士の通信においては、文字列を文字列として、バイト列をバイト列として透過的に復元できるようにする
- weak-string languages と strong-string languages が混在した場合のデータ交換では:
- weak-string languages においてすべてのバイト列にマーカを付ける作業を行えば、strong-string languages で透過的に型を復元できるようにする
- すべての文字列ではなくすべてのバイト列にマーカを付けるのは、バイト列の方が文字列より数が少なく、その作業の方が簡単だという仮定に基づいている
- そうでなくても、strong-string languages 側のアプリケーションで適切な実装を行えば、透過的ではないがデータ交換が行えるようにする
- weak-string languages 同士の通信では、既存のmsgpackとの互換性を維持する
- Binary: バイト列
- String: UTF-8でエンコードされた文字列
- UTF-8としてinvalidなバイトシーケンスを含む文字列が保存されていることもある。この理由は次の3つ:
- Unicodeは、その正しい取り扱いをmsgpackの全言語の実装にさせるには、複雑すぎるため
- シリアライズ時にvalidateを行うと性能にインパクトがあるため(strong-string languages であっても、UTF-8としてinvalidなバイトシーケンスを含む文字列オブジェクトを簡単に作れる言語が存在することに注意;例:Python2)
- weak-string languages で書かれたプログラムでは、文字列とバイト列が明確に区別できることを期待することは現実的ではなく、結果としてバイト列がStringとして扱われることがあるため
- invalidなバイトシーケンスを含む文字列を検出した場合の動作は、実装に依存する
- ただし、invalidなバイトシーケンスを含む文字列をどのように取り扱うかは、アプリケーションが決定するべき仕事である
- UTF-8としてinvalidなバイトシーケンスを含む文字列が保存されていることもある。この理由は次の3つ:
UTF-8としてinvalidなバイトシーケンスを含むStringを受け取った場合の動作は実装に依存する。例外を発生させて弾く実装を行っても良いし、その場合に限りバイト列型を返すといった実装でも良い。しかし、UTF-8としてinvalidなバイトシーケンスを含むString型オブジェクトが保存されていたとしても、アプリケーションが望めば元のバイト列を取り出せるようにする手段も提供することが、強く推奨される(SHOULD or MUST)。
0xa0-0xbf FixString (0bytes - 31bytes String type) // changed
0xd5 binary 8 (0bytes - 255bytes Raw type) // new
0xd6 binary 16 (256bytes - 65535bytes Raw type) // new
0xd7 binary 32 (65536bytes - 4294967295bytes Raw type) // new
0xd8 reserved
0xd9 string 8 (32bytes - 255bytes String type) // new
0xda string 16 (256bytes - 65535bytes String type) // changed from raw 16
0xdb string 32 (65536bytes - 4294967295bytes String type) // changed from raw 32
-
シリアライザ:
- バイト列はBinary型として保存する
- 文字列はString型として保存する
- ただし、既存の実装との互換性を維持するために、バイト列もString型として保存するオプションを実装しても良い
-
デシリアライザ:
- String型またはBinary型をデシリアライズしたら、それと分かるオブジェクトを返す
- String型にUTF-8としてinvalidなバイトシーケンスが含まれていた場合に、アプリケーションがそれらをハンドリングできるようにする機能を提供するべきである(SHOULD)
- この実装方法は特に規定しない。次のような方法が考えられる:
- invalidなバイトシーケンスを発見したら、そのオリジナルのバイト列をフィールドに持つオブジェクトのインスタンスを返す
- invalidなバイトシーケンスを含むことができる文字列クラスを組み込み型とは別に作成し、invalidなバイトシーケンスが含まれるか否かに関わらず、常にそれを返すモードを実装する
- invalidなバイトシーケンスを発見したら指定されていたコールバック関数を呼び出し、その関数の返値を返す
- シリアライザ:
- バイト列か文字列かが自明でなければ、String型として保存する
- UTF-8のvalidationは行わなくてよい。String型はUTF-8としてinvalidなバイトシーケンスを許容する
- ユーザーが明示的に「これはバイト列だ」とヒントを設定したオブジェクトを受け取った場合、それはBinary型として保存するべきである(SHOULD)
- デシリアライザ:
- String型のvalidationを行うべきではない(SHOULD NOT)
- なぜなら、アプリケーションがinvalidなバイトシーケンスを含むStringの扱いを決めるべきだから
- ただし、オプションを有効にすればString型でvalidationを機能を実装をしてもよい(MAY)
- Binary型を受け取った場合、何らかのフラグが立った(String型とは区別できる情報を含む)オブジェクトを返すオプションを実装しても良い(MAY)
- この挙動は、MessagePackを入力として、MessagePackを出力するような、中間処理を行うツールで、出力先でも型情報を維持しなければならないケースで必要になる
- この機能が必要なケースは、稀である
- 実装の方法には、例えばPHPにおいては、特定のオプションがonであったら、Binary型を受け取った場合に、バイト列をフィールドとして所有するMessagePackBinaryクラスのインスタンスを返す方法ある
- String型のvalidationを行うべきではない(SHOULD NOT)
- マイナーバージョンアップで、新設されるBinary型をバイト列として返す実装をリリースする
- この時点で、新しいエンコーダとの互換性が達成される
- メジャーバージョンアップで、バイト列を新設Binary型でシリアライズする実装をリリースする
- 同時に、区別が厳格な環境のエンコーダにおいて、バイト列もStringとして保存するオプションを提供する
- これは、新しいエンコーダに切り替えた場合でも、書き出されるバイト列が変化しないようにするために存在する
- 既存の実装と互換性を維持するには、メジャーバージョンアップすると同時に、このオプションをONにする
- また、ソースコードレベルの互換性を維持するため、String型とBinary型の両方をバイト列として返すモードを提供する
- 同時に、区別が厳格な環境のエンコーダにおいて、バイト列もStringとして保存するオプションを提供する
「用語」で触れられている言語の種類を増やしたい。コメント求む。
この変更後のMessagePackの呼び方。
- 案1:現行の仕様をMessagePack 0.9 と呼び、新しい仕様を MessagePack 1.0 と呼ぶ
- 案2:現行の仕様をMessagePack 1.0 と呼び、新しい仕様を MessagePack 1.1 と呼ぶ
様々な懸念点が払拭されていてStringの定義も簡潔になり、素晴らしいと思います!
バージョンに関しては、ライバルとなるJSONが http://www.json.org/fatfree.html で
という哲学を表明しており、1.1だろうが2.0だろうがバージョンつけた時点で負け的な感じはあります。
今回の仕様がvery stableであると確信が持てるのであれば、、、微妙なところではありますが「バイナリフォーマットにおけるstringの扱い」という最難関を乗り越えた印象はあるので、今後の変更はもうないか、あっても今回以上に大きな変更はもう二度となさそうな気はするので、