libpack

libpackはCのデータをバイナリ列としてシリアライズするためのライブラリです。

必要性

Cで書かれたプログラム同士が、構造を持ったデータをやり取りする場合、構造体を用いる場合が多いでしょう。同じプログラム内では関数の引数として構造体やそのポインタを渡しますし、同じ計算機上で動くプログラム同士なら伝送路(ソケットや共有メモリを使うでしょう)経由で構造体をコピーするだけでほとんどの場合、データのやりとりが可能です。

しかし、異なるアーキテクチャの計算機同士の通信の場合、Cで同じように定義した構造体であっても、そのバイナリの構造が異なる場合があります。たとえば構造体にはパディング(メモリアドレスの境界にメンバ変数がそろうようにコンパイラが勝手に入れる詰め物)があり、これは処理系により違います。また計算機ごとにバイトオーダー(主にはリトルエンディアンやビッグエンディアン)の違いがあります。

一つのアイデアとして、sprintfのように書式文字列と可変引数により、データをパッケージする方法があります。書式文字列でデータの型とその数や並びを表し、その型の引数を(いくつでも並べて)与えることで、構造体のようなパディングのない、連続的な(シリアライズされた)一つのデータ領域とします。また、バイトオーダーを変換する機能も持たせることができます。

「プログラミング作法(The Practice of Programming) Brian Kernighan, Rob Pike」にまさにこのアイデアが説明されています。たとえば下のようなpack/unpack関数です:


    char buff[100];
    char cv;
    short hv;
    long lv;

    /* シリアライズ */
    pack (buff, "c h l", cv, hv, lv);

    /* デシリアライズ */
    unpack (buff, "c h l", &cv, &hv, &lv);

「プログラミング作法」には、これを拡張して配列などを扱えるようにせよ、という演習問題も含まれています。libpackはそれに対する一つの回答です。

特徴

とにかくシンプルな構成にとどめます。PCなどのパワフルな計算機での使用だけでなく、ロボットや組み込み機器のようなマイコンでの利用も想定しているからです。ANSI-Cの機能のみで実現することにしています。

他の言語

このアイデアは、いくつかのスクリプト言語でバイナリデータを作成するために使われています。

  • Perlのpack/unpack
  • Pythonのstruct
  • Ruby struct

他のライブラリ

プロジェクトを作成してから知りましたが、同様の機能を実現する同名のライブラリを公開している方が居ました。なんてこった。名前まで同じとは。改名が必要? しかし見てみると、機能的にはちょっと不便と思われる部分があります。

書式文字列の仕様

ANSI-Cでは型のサイズは処理系依存であり、sizeof(char) = 1 <= sizeof(short) <= sizeof(long)ということしか決まっていません。おそらく、それぞれのサイズを意識しないですむライブラリにできるのでは、と思いますが、現在はshort=2byte, long=4byte, float=4byte, double=8byteとして内部を書いてしまいました。

書式文字列は、以下のようになります。

c - char, unsigned char h - short, unsigned short 2byte l - long 4byte f - float 4byte d - double 8byte c4 - char4の配列 ! - この文字以下のバイトオーダーを変換する # - 配列の大きさを引数で与える

マイコンを考えるとlong long intや浮動小数点はコンパイラを通らない可能性があるのでとりあえず無しということにします。

参考とする仕様

  • プログラミング作法
  • perl pack/unpack
  • python struct
  • ruby Array#pack
  • Cの書式文字列