WXF形式について

WXFは,外部のストレージに適した形式,あるいは他のプログラムとやり取りできる形式でWolfram言語式を忠実に直列化するためのバイナリ形式である.WXFは,多数のプログラミング言語で利用可能な低レベルのネイティブ型を使って簡単に解釈できるので,他のプログラミング言語でWolfram言語を読み書きするための形式として適したものになっている.
Wolfram言語式とその直列化された形式との間の変換のための基本的な関数は,BinarySerializeBinaryDeserializeである.ExportおよびImportには,WXFデータを使ったファイルの読み書きのサポートが組み込まれている.
BinarySerialize[expr]
任意の式 expr のバイナリ表現をWXF形式で与える
BinaryDeserialize[bytearray]
WXF形式のバイナリ表現から式を取り出す
Import[file,"WXF"]
WXFファイルをインポートして,式を返す
Export[file,expr,"WXF"]
任意の式を直列化して,それをWXFファイルとして保存する
ImportByteArray[ba,"WXF"]
データをインポートして式を返す
ExportByteArray[expr,"WXF"]
WXF形式でエキスポートされた expr に対応するByteArrayオブジェクトを生成する
Wolfram言語でWXFを直列化および非直列化する多くの方法

基本構造

WXFのデータは,常にプレーンテキストのASCIIのヘッダの後にバイトの文字列が続く.ヘッダはバイトを復号化する方法を指定し,部分の列を表すバイト列とコロンで分けられている.
バイト配列は,部分型を指定する以下のリストからのトークンで始まる,部分のシーケンスを与えることによって続く.
バイト値
文字表現 (ISO8859-1)
部分型
102"f"
関数
67"C"
符号付き8ビット整数
106"j"
符号付き16ビット整数
105"i"
符号付き32ビット整数
76"L"
符号付き64ビット整数
114"r"
IEEE倍精度実数
83"S"
文字列
66"B"
バイナリ列
115"s"
シンボル
73"I"
大きい整数
82"R"
大きい実数
193"Á"
パックアレー
194"Â"
数値配列
65"A"
連想
58":"
連想における遅延規則
45"-"
連想における規則
WXFトークンの完全リスト
トークンの後には,必要に応じて長さ指定,部分に対する実際のコンテンツ要素の列が続く.

基本的な例

シンボルの直列化された形式に対するバイトを与える:
バイトを"ISO8859-1"の文字として見る:
文字列の直列化された形式に対するバイトを文字として与える:
Range[10]の直列化された形式に対するバイトを与える:
配列の部分にラベルを付ける関数を生成する:
最初の2バイトはヘッダに属し,残りはパックアレーに相当する.ay:
分離子が続くヘッダ:
型,ランク,次元,データを持つパックアレー:

複数の部分を持つ例

前のセクションの例は,単独の部分で構成されていた.トークンリストからの2つのトークンの後に,サイズ情報,データが続いた.このセクションの例には,複数の部分と型が含まれている.以下のインタラクティブな例から見てみる.
Select[OddQ]の直列化された形式に対するバイトを与える:
最初の2バイトはヘッダとコロンの分離子である:
次のバイトは,引数の数が後続する関数のトークンである:
次のバイトは関数の頭部のトークンであり,この場合はシンボルである:
その後は,シンボルのUTF-8表現でのバイト数である:

したがって,次の6バイトはシンボルのUTF-8表現である:
完全な頭部が読まれると,次のバイトは最初の引数のトークンである:
この引数はシンボルなので,次のバイトはその長さであり,その名前が続く:
関数は引数を1つしか持たなかったので,すべてのバイトが読まれたことになる:
次の例は3要素のリストである.このリストは長さ3の関数として表され,頭部Listの後に3つの部分が続く.最初の部分はトークンと"Integer8"を使って整数を簡約形で表すために使われる形式を導入する.最後の部分はByteArrayの表現を表示する.
{1,-1,ByteArray[{1,2,3}]}の直列化された形式に対するバイトを与える:
ヘッダを無視数rと,この式は3つの引数を持つ関数である:
頭部は4バイトのシンボル,つまりListである:
頭部の後には最初の引数,つまり8ビット整数の1が来る:
次の2バイトは第2引数,つまり8ビット整数-1である:
Modを使ってバイトを符号付の値として解釈する.255を想定される-1に変換する:
3番目と最後の引数は値が{1,2,3}の長さ3のバイナリ列である:

ヘッダ

ヘッダはさまざまな長さのプレーンのASCII文字列であり,文字":"で分けられる.WXFの現行バージョン(1.0)では,ヘッダの最初のバイトは文字"8"(バイト値56)である.バイナリ直列化がzip圧縮されると,このことはヘッダの文字"C"で表される.ヘッダは圧縮されることはない.圧縮は,後続のバイト列にのみ適用される.
式を直列化してバイト配列を与える:
最初のバイトは文字8である:
圧縮は,ヘッダの2番目の文字が"C"であることで表される:

レングス圧縮 (Varint)

WXFでは,長さやサイズを表すすべての整数はvarint法を使って直列化される.varintは指示可変長形式であり,整数が小さくなるにつれ必要なバイト数が少なくなる.最後のバイト以外の各バイトに最上位ビット(MSB)がある.MSBは,ストリームの後続のバイトもvarintの一部ならば,それは連続マーカーとしての役割を果たすことを示す.小さい整数では必要なバイトも少ない.各バイトの下位7ビットは,最下位グループを先頭として,整数のバイナリ表現を保存する.
以下は,500のvarint表現を構築する例である.
500を二進表現したときの各桁の数字:
最下位ビットを先頭として,ビットを7個のグループにする:
各グループのビットを最上位ビットから順に並べ,それぞれが8ビットずつになるようグループに充填する:
最初のバイト以外の各バイトの最初のビットをまとめて,varintのバイナリ形式を得る:
二進法の数字からバイト値を構築することで,500のvarint表現が与えられる:
varintで符号化されたバイト列{244,3}500に復号化する逆操作を下で説明する.ここではそれぞれの初期のvarintビットに固有の色が割り当てられている:

文字列,シンボル,非機械数

文字列,記号,非機械数は同じ形式を使って表される.最初のバイトはトークンであり,次にvarint形式で符号化されたバイト数,次に式の文字列InputFormに対応する,UTF-8で符号化されたUnicode文字列が続く.非機械数は,表現するのに$SystemWordLengthのビット以上を必要とする任意精度実数,または整数である.
シンボル
トークン
表現
String"S"
Unicodeの文字列
Symbol"s"
System`シンボルを除く,コンテキストを指定するシンボルの完全修飾名
任意精度実数"R"
仮数,そして最終的には精度と指数を指定する桁表現
大きい整数"I"
桁の列
InputFormベースの型
「不思議の国のアリス」の最初の500文字を直列化する:
ヘッダの後の最初のバイトは文字列のトークンである:
次の2バイトは前の例で見られるように,varintで符号化した500である:
残りのバイトは,UTF-8における文字列のコンテンツである:
非機械数を直列化する:
トークン以外,直列化は文字列の場合と同じである:
桁数が1つ少ない数には,長さの符号化に1バイトしか必要としない:

機械整数の直列化

機械整数は以下の表の最小の整数型によって分類される.整数型は,値の後にその整数の2の補数表現を続けることで表すことができる.バイト順は常にリトルエンディアンである.
トークン
定義
型のサイズ
"C"符号付き8ビット整数
"j"符号付き16ット整数
"i"符号付き32ット整数
"L"符号付き64ット整数
整数のトークン,それらが関連付けられた型,各表現で使われるバイト数
このバイトは2^14の直列化に対応する:
2バイトのヘッダを飛ばして,最初のトークンを文字として表示する:
トークンの次のバイトを見る:
前のバイトのペアを反転して,それを整数に変換する:
負の整数のバイナリ表現では,2の補数が使われる.Nビットの整数 α があるとすると,その2の補数 β は,2N: α+β=2N についてその補数である.数の反転は,2の補数を取ることで実行される.
8ビットの整数1の2の補数:
2の補数は-1の8ビットバイナリ表現である:
負の16ビット整数を直列化する:
最後の2つのバイトは整数値である:
1対のバイトをその十進形式に変換する:
その値は,10000の16ビットの2の補数である:

機械実数の直列化

機械実数は,文字"r"の後にIEEE 754標準の倍精度浮動小数点値によるメモリ表現を続けることで表される.機械整数の場合は,バイト順は常にリトルエンディアンである.
実数を直列化する:
2バイトのヘッダを飛ばして,最初のトークンをバイトとして,そして文字として表示する:
次のバイトは,IEEE 754標準の実数値である:
機械精度の複素数は,2つの機械精度実数の関数として直列化される.以下の図表は2つの実数が後続する頭部Complexを強調している.
機械精度の複素数を直列化する:
ヘッダの後の最初のバイトは頭部がComplexの長さ2の関数である:
前の例で示したように,次の9バイトは実部であり,実数値4.の直列化に合致する:
残りのバイトは虚部であり,実数値4.である:

関数の直列化

WXFでは関数は,文字"f"の後にvarint形式の式の長さを置いて表される.要素の数は,頭部の場合は1を増分とした長さに等しい.頭部と部分は直列化された任意の式である.特に頭部は関数でもあり得る.Select[OddQ][{1,2,3}]は頭部がSelect[OddQ]の長さ1の関数であるが,その頭部自身も頭部Selectを持つ長さ1の関数である.
式が評価されないように,Unevaluatedを使って,式を直列化する:
最初の2バイトは長さ1の関数に対応する:
次の16バイトは以前に示した頭部Select[OddQ]の直列化である.
残りのバイトは引数であり,最初にトークンと長さが来る:
その後に頭部が続く:
3つの機械整数で式が完成する:

連想の直列化

連想は,文字"A"の後に長さと規則を置いて表す.
連想の規則は文字"-"で,遅延規則は文字":"で表される.これらの文字の直後には,任意の2つの直列化された式が続く.連想の規則の長さは常に2であるため,省略される.以下に示すのは,簡単な連想の直列化である.
連想を直列化する:
最初のバイトは2つの要素の連想に対応する:
次に,暗示的に2つの部分を持つ規則が続く.長さは省略される:
遅延規則が最後の部分になる:
前の例の規則は連想の一部であった.Associationの一部ではないRuleRuleDelayedは関数として直列化される.直列化された形式は,次の例で示すように,パックの密度が低い.
規則のリストを直列化する:
最初のバイトは2要素のリストを宣言する:
最初の要素は頭部がRuleの長さ2の関数である:
規則の引数は,連想の場合と同様に同じである:
2番目の要素も長さ2の関数であるが,頭部はRuleDelayedである:
同様に,遅延された規則の要素も変化はない:
直列化された長さはほぼ文字列FullFormのサイズと同じになる:
連想はより簡潔な形式に直列化される:

バイナリ列

バイナリ列はトークン"B"で表される.これらは文字列と同じパターンに従うが,バイト列はUTF-8の文字ではなく任意である.ByteArrayはバイナリ列として直列化される.
バイナリ配列を直列化する:
ヘッダの後の最初のバイトは,バイナリ列のトークンにバイナリデータの長さが続いたものである:
次のバイトがデータである:
バイトをUTF-8に復号化することは必ずしも成功するとは限らない:
バイトは常に"ISO8859-1"を使った文字列として,表すことができる:

数値配列

配列は,機械精度の数値による多次元の表である.配列は,配列の型を指定するトークン,値の型を指定するトークン,varint形式による階数,整数列としてのvarint形式による次元,データの順で表される.
WXF形式には,トークン"Á"(バイト値193)で表されるパック配列,およびトークン"Â"(バイト値194)で表される数値配列の2つの配列の型がある.この2つはわずかに異なっている.大きい違いとして,以下の表に示すように,サポートされる値の型がある.
整数値
16進法表現における値
配列型
00016
8ビット符号付き整数配列
10116
16ビット符号付き整数配列
20216
32ビット符号付き整数配列
30316
64ビット符号付き整数配列(64ビットシステムのみ)
342216
IEEE単精度実数配列(float)
352316
IEEE倍精度実数配列(double)
513316
IEEE単精度複素数配列
523416
IEEE倍精度複素数配列
パック配列に対して有効な値型のトークン
整数値
16進表現における値
配列型
00016
8ビット符号付き整数配列
161016
8ビット符号なし整数配列
10116
16ビット符号付き整数配列
171116
16ビット符号なし整数配列
20216
32ビット符号付き整数配列
181216
32ビット符号なし整数配列
30316
64ビット符号付き整数配列
191316
64ビット符号なし整数配列
342216
IEEE単精度実数配列(float)
352316
IEEE倍精度実数配列(double)
503316
IEEE単精度複素数配列
513416
IEEE倍精度複素数配列
数値配列に対して有効な値型のトークン
パック配列によってサポートされる整数範囲はシステム語長$SystemWordLengthによって異なる.32ビット環境では-231から231-1,64ビット環境では-263から263-1となる.
行列を定義する:
それをパックアレーとして直列化する:
ヘッダの後の最初のバイトはパックアレーのトークンである:
次のバイトは16ビットの符号付き整数の配列を示している:
次のバイトは配列のランクと次元である:
その後のバイトは値である:
それぞれの16ビット整数から十進形式を再構築することができる.まず,バイトのペアをグループ化する:
それぞれのペアはリトルエイディアンの16ビットの長さの整数である.その値は,ビットシフト演算を使って再構築される:
以下のインタラクティブな図表は,パックする前の前出の行列の直列化を示している.要素の列は頭部Listのネストされた関数を含んでいるため,全く異なっている.内部のリストは,整数値に対応する3つの部分を持っている.整数値のバイナリ表現はパックアレーの場合に見られたもの(リトルエイディアンの符号付16ビット整数)と似ている
配列の値型のトークンはビットフィールドとして構築される.下位の4ビットはバイトの数値型の大きさのログを保存し,上位の4ビットは数値型を表す.複素型の場合,大きさは正の整数を指すので,例えば単精度複素型は8バイトの大きさを持つとみなされる.
0000
0001
0010
0011
0100
値型のトークンの下位4つのビットと,対応する型のバイトサイズ
0000
整数
0001
符号なし整数
0010
実数
0011
複素数
値方のトークンの上位4ビットと対応する数値型
Wolfram言語を使うと,倍精度実数の配列に対応するビットフィールドを構築することが可能である.
倍精度実数は8バイトの長さである.底2の対数は3である:
ビット表現を求める:
実数に対応する数値型の連結から作られたビットフィールドと,その型のサイズを見る:
上記のビット列を変換して,想定されるバイト値を取り出す: