XDR ライブラリパッケージでは、任意の要素で構成される配列を処理するプリミティブが提供されています。xdr_bytes() ルーチンは一般の配列のサブセットを処理します。すなわち、xdr_bytes() ルーチンでは、配列要素のサイズは 1 に決まっており、各要素の外部記述も組み込まれています。一般の配列に対するプリミティブ xdr_array() の引数は、xdr_bytes() の引数より 2 つ多く、配列要素のサイズと、各要素を変換する XDR ルーチンとが渡されます。渡されたXDR ルーチンは、配列の各要素の符号化または復号化のときに呼び出されます。
bool_t xdr_array(xdrs, ap, lp, maxlength, elementsize, xdr_element) XDR *xdrs; char **ap; u_int *lp; u_int maxlength; u_int elementsize; bool_t (*xdr_element)(); |
引数 ap は、配列へのポインタのアドレスです。配列をデシリアライズするときに *ap が NULL の場合は、適切なサイズの配列が割り当てられ、*ap はその配列を指すように設定されます。配列をシリアライズするときは、配列の要素数を *lp から取り出します。配列をデシリアライズするときは、*lp には配列の長さが設定されます。引数 maxlength は、配列に入れることができる最大要素数です。elementsiz は、配列の各要素のサイズ (バイト数) です。C の sizeof() 関数を使用してこの値を調べることができます。xdr_element() ルーチンは、配列の各要素のシリアライズ、デシリアライズ、解放を行うときに呼び出されます。
このほかの合成データ型の説明の前に、3 つのサンプルプログラムを説明します。
ネットワークに接続したマシンのユーザーは、次の 3 つの項目によって識別できます。(a) マシン名。たとえば、krypton。(b) ユーザーの UID。これについては、geteuid(2) のマニュアルページを参照してください。(c) ユーザーが所属するグループ番号。これについては、getgroups(2) のマニュアルページを参照してください。これらの識別情報を持つ構造体と、それに対する XDR ルーチンは例 A-7 のようにコーディングできます。
struct netuser { char *nu_machinename; int nu_uid; u_int nu_glen; int *nu_gids; }; #define NLEN 255 /* マシン名は 255 文字以下 */ #define NGRPS 20 /* ユーザーが所属するグループ数は 20 以下 */ bool_t xdr_netuser(xdrs, nup) XDR *xdrs; struct netuser *nup; { return(xdr_string(xdrs, &nup->nu_machinename, NLEN) && xdr_int(xdrs, &nup->nu_uid) && xdr_array(xdrs, &nup->nu_gids, &nup->nu_glen, NGRPS, sizeof (int), xdr_int)); } |
ネットワークユーザーのグループは、netuser 構造体の配列で表すことができます。構造体の宣言と、それに対する XDR ルーチンは 例 A-8 のようになります。
struct party { u_int p_len; struct netuser *p_nusers; }; #define PLEN 500 /* グループに所属するユーザー数の上限 */ bool_t xdr_party(xdrs, pp) XDR *xdrs; struct party *pp; { return(xdr_array(xdrs, &pp->p_nusers, &pp->p_len, PLEN, sizeof (struct netuser), xdr_netuser)); } |
main に対するよく知られた引数 argc と argv を持つ構造体を作成し、その構造体の配列にコマンドヒストリを保存することができます。構造体の宣言と、その XDR ルーチンは 例 A-9 のようになります。
struct cmd { u_int c_argc; char **c_argv; }; #define ALEN 1000 /* argc は 1000 以下 */ #define NARGC 100 /* 各コマンドの args は 100 以下 */ struct history { u_int h_len; struct cmd *h_cmds; }; #define NCMDS 75 /* ヒストリは 75 コマンドまで */ bool_t xdr_wrapstring(xdrs, sp) XDR *xdrs; char **sp; { return(xdr_string(xdrs, sp, ALEN)); } bool_t xdr_cmd(xdrs, cp) XDR *xdrs; struct cmd *cp; { return(xdr_array(xdrs, &cp->c_argv, &cp->c_argc, NARGC, sizeof (char *), xdr_wrapstring)); } bool_t xdr_history(xdrs, hp) XDR *xdrs; struct history *hp; { return(xdr_array(xdrs, &hp->h_cmds, &hp->h_len, NCMDS, sizeof (struct cmd), xdr_cmd)); } |
このプログラムで最もむずかしいのは、xdr_string() を呼び出すためのルーチン xdr_wrapstring() が必要な点です。xdr_array() が配列要素記述ルーチンを呼び出すときは引数が 2 つしか渡されないため、xdr_string() の第 3 引数を提供するルーチン xdr_wrapstring() が必要になります。