データの符号化と復号化
Simple Wiki Based Contents Management System
Home Topics Projects Software Misc
ソフトウェア関連 >> RtORBについて >> RtORBの動作について >> データの符号化と復号化

データの符号化と復号化

データの符号化と復号化は、何故必要なのでしょうか?
それは、2つのプロセス間で通信する場合に、どんなデータを送るかを予め決めたおかないと正しいデータの送受信が出来ないので、決まったルールでどんなコンピュータでも扱える形にしましょうということです。
これは、全く同じコンピュータ間で、相手がどのような形のデータ構造を要求しているのかが分かっていれば、あまり問題にならないのですが、CORBAのような分散オブジェクトを実装する場合には、予めデータ構造を決めておくことは、その利用を制限することになりますのでよくありません。そのため、通常のプログラミングで扱うデータ構造を効率よく表現する必要がありますが、一般に、いろいろなOSやCPU上で動くプログラムには、内部表現が統一されていません。
たとえば、mips系のCPUでは、ビッグエンディアンで数字を表現しますが、Intel x86系のCPUでは、リトルエンディアンで数字を表現しています。このエンディアン(バイトオーダーともいう)というのは、多バイトのデータをメモリ上に配置する方式ことで、4バイトで表現される100という整数があった場合に、ビッグエンディアンのCPUでは、
 0x00, 0x00, 0x00, 0x64
となっているのに対し、リトルエンディアンでは、
 0x64, 0x00, 0x00, 0x00
と表現されています。このような違いを吸収し、どのようなOS上のCORBAオブジェクトとも通信できるように、すべてのデータをバイト列に変換して送るということをしています。(エンディアンについては、Wikipediaを見てください。詳しく書いてあります。)
このために必要なのが、符号化処理(marshaling)と復号化処理(unmarshaling)です。この符号化に関する定義はすでに、RtORBの動作についてで述べましたので、そちらを参照してください。
以下では、RtORBで実装しているmarshalingとunmarshalingについて述べていきます。

TypeCode

符号化と復号化の処理の説明の前に、CORBAで扱うデータ型について述べます。CORBAで扱うデータ型をどのように表現し、他のCORBAオブジェクトと共通認識を持つために、TypeCodeと呼ばれるものが定義されています。CORBAでのリモート呼び出しで、データ交換を行うときに、そのデータ表現がどうなっているかを表したものがTypCodeです。TypeCode型については、RtORBの動作についてでも述べていますので、RtORBでの構造については省略します。
TypeCodeには、RepositoryIDと呼ばれる文字列が定義されており、この文字列によって一意に決定されます。例えば、CORBAネーミングサービスのCosNamingにおけるIstringという型は、
 module CosNaming {
   typedef string Istring;
 };
と定義されています。このRepositoryIDは、
 IDL::omg.org/CosNaming/Istring:1.0
となっており、データ型の種類を示すkind は、tk_alias(=21)になっています。また、CORBAの基本データ型のlongの場合には、RepositoryIDが
 IDL::omg.org/CORBA/long:1.0
となっています。もちろん、このデータ型ののkindは、tk_long(= 3)となっています。このkindとTypeCodeの中の定義(構造体や配列のように要素ある場合は、そのメンバーのTypeCodeが再帰的に、TypeCodeに定義されています)によって符号化と復号化を行います。

RtORBに実装された符号化処理(marshaling)

RtORBでは、データの符号化処理は、marshal_by_typecode関数で行われます。この関数は、TypeCodeを引数として符号化処理を行いますので、構造体や配列などは、再帰呼び出しを行うことになります。
RtORBを安全なコードで書く場合には、再帰呼び出しの部分を修正しなければいけないかもしれません。
marshal_by_typecode関数は、giop-marshal.cに記述されています。この関数は、(octet *buf, void *argv, CORBA_TypeCode tc, int *current)を引数として、次のような処理を行っています。bufは、符号化された結果を入れるバッファ、argvは、符号化するデータへのポインタ、tcは、符号化するデータのTypeCode、currentは、符号化したデータをbufに入れるときのオフセットになります。
  1. まず、tcを調べてNULLであれば、0を返します。
  2. CORBA_TypeCode_add_dynamicを実行してtcをdynamic_tcsという大域変数で指定されているハッシュテーブルに登録しています。この処理は、動的なTypeCodeをサポートするための実験的なコードですので、現在は必要ありません。
  3. 次に、tcがtk_aliasであれば、スキップして実体のTypeCodeに変換します。これは、SKIP_ALISAというマクロで行っており、このマクロは、corba.hに記載されています。
  4. 次にtc->kindにしたがってデータの符号化を行っていきます。
    • tk_null, tk_voidの場合:この場合には、何も符号化しませんので、*currentを返して終了します。
    • tk_char, tk_octet, tk_booleanの場合:1バイトデータですので、argvをchar*にキャストし、marshalOctet関数でbufに内容をコピーしてcurrentを1つ進めます。
    • tk_shortの場合:argvをshort*にキャストし、marshalShort関数でbufにコピーして、currentを2つ進めます。
    • tk_ushortの場合:argvをunsigned short*にキャストし、marshalShort関数でbufにコピーして、currentを2つ進めます。
    • tk_longの場合:argvをint32_t *にキャストし、marshalLong関数でbufにコピーして、currentを4つ進めます。
    • tk_ulongの場合:argvをuint32_t*にキャストし、marshalLong関数でbufにコピーして、currentを4つ進めます。
    • tk_floatの場合:argvをfloat*にキャストし、marshalFloat関数でbufにコピーして、currentを4つ進めます。
    • tk_doubleの場合:argvをdouble*にキャストし、marshalDouble関数でbufにコピーして、currentを8つ進めます。
    • tk_stringの場合:argvをchar**にキャストし、marshalString関数でbufにコピーして、currentを4+文字列の長さ分進めます。
    • tk_sequenceの場合:
      • size_of_typecode(tc->member_type[0], F_DEMARSHAL)を行いsequenceの要素のデータの長さをskipにセットします。この時size_of_typecode関数には、F_DEMARSHALを2番目の引数で与えますが、これは、コンピュータ内部のNativeのデータ長をとるという指定です。またcpos=0にセットしておきます。
      • argvをCORBA_SequenceBase*にキャストし、sbという変数にセットします。CORBA_SequenceBase型は、RtORB内ですべてのSequence型が共通部分を表す型です。
      • marshalLong関数で符号化するデータの配列の長さ(sb->_length)を符号化します。
      • align_of_typecode関数で、Sequenceの要素の型のAlignmentを求めてalign_baseにセットします。この関数もF_DEMARSHALを2番目の引数で与えますが、これは、コンピュータ内部のNativeのデータでのAlignmentを求めています。
      • 次に、Sequenceの長さの分だけ、以下の処理を繰返します。
        • align_baseのアライメントにあうようにcposを進めます。
        • 符号化するためのデータの先頭ポインタを、_buffer =(char *)sb->_buffer + cpos; で _bufferにセットします。
        • marshal_by_typecode関数を再帰呼び出しで、この_bufferを要素のTypeCodeで符号化します。
        • cpos をskipだけ進めます。
    • tk_exception, tk_structの場合:
      • Address_Alignment(current, align_of_typecode(tc,F_MARSHAL));を実行して、符号化してデータ格納のためのオフセットを更新します。ここで、align_of_typecode関数の第2引数でF_MARSHALが与えられていますが、これによってCORBAのデータ構造におけるアライメントを得ることができます。この時、cpos=0にセットしておきます。
      • strcutのメンバーの数だけ以下の処理を行います。
        • ctc = tc->member_type[i];としてi番目のメンバーのTypeCodeをctcにセットします。
        • Address_Alignment(&cpos, align_of_typecode(ctc,F_DEMARSHAL));を実行して、cposをctcのアライメントに合うように進めます。この時のアライメントは、コンピュータ内部のNativeのデータアライメントになります。
        • buffer = (void **)((char *)argv + cpos);を実行して、符号化するデータの先頭ポインタを_bufferにセットします。
        • marshal_by_typecode関数を再帰呼び出しし、ctcのTypCodeで、_bufferを先頭ポインタにもつデータを符号化します。
        • cpos = cpos + size_of_typecode(ctc, F_DEMARSHAL);を実行して、符号化したデータ長分 cposを進めます。
    • tk_unionの場合:
      • Address_Alignment(current, align_of_typecode(tc,F_MARSHAL));を実行して、符号化してデータ格納のためのオフセットを更新します。これは、structの時と同様の処理です。この時、void **args = (void **)argv; void **discrim = (void **)argv; というふうに、argsとdiscrimに符号化を行うデータの先頭ポインタをセットします。
      • marshal_by_typecode関数を再帰呼出し、tc->discriminatorのTypeCodeでargsを符号化します。
      • idx = get_union_switch(tc->discriminator, discrim);を実行して、idxに符号化するデータが、unionのどのデータであるかのインデックスをidxにセットします。
      • n = size_of_typecode(tc->discriminator, F_DEMARSHAL); として、tc->discriminatorで表されているunionのデータインデックスのデータ長をnにセットします。
      • Address_Alignment関数で、汎用ポインタのサイズをアライメントとしてnを調整します。これは、RtORBでは、unionを構造体として表現しているためです。(もしかしたら、このルーチンは間違っている可能性がありますが、OpenRTM-aistを使用する上で、unionの部分は使っていませんので影響ありません)
      • args = (void **)((char *)args + n); として、nだけ符号化するデータの先頭ポインタを進めます。
      • idxがtc->default_indexでなかった場合に次の処理をします。(ここでは、idx==tc->default_index時の処理がないので、正常動作しない可能性が大きいです。後日書きなおしますが、多分この部分を削除でよさそうです。)
      • 次に、tc->member_count分だけ以下の処理を繰返します。
        • ctc = tc->member_type[i];としてi番目のメンバーのTypeCodeをctcにセットします。
        • もし、tc->member_label[i] == idx であった場合に、marshal_by_typecode関数を再帰呼び出しをして、データを符号化します。
        • n = size_of_typecode(ctc, F_DEMARSHAL);を実行して、i番目のコンピュータ内部のデータ長をセットします。
        • Address_Alignment(&n, sizeof(void *));を実行して、nを汎用ポインタの長さのアライメントに合うように進めます。
        • args = (void **)((char *)args + n); として、nだけ符号化するデータの先頭ポインタを進めます。
    • tk_objrefの場合:
      • CORBA_Object obj = *(CORBA_Object *)argv;として符号化するデータをCORBA_Object型にキャストします。
      • Address_Alignment(current, 4); で4のアライメントを実行します。
      • obj が NULLである場合には、下記の処理をします。
        • marshalLong関数で1をbufにコピー
        • marshalOctet関数で'\0'をbufにコピー
        • marshalLong関数で0をコピー(ここの処理がちょっと怪しい)
      • objがNULL出ない場合には、次の処理を行う。
        • obj->_ior_stringがNULLのときは、CORBA_Object__to_string関数でobj->_ior_stringをセットする。
        • ior = (char *)obj->_ior_stringとして、iorにオブジェクト参照をセット
        • ior_byte = (char *)String2Octet(ior + 12); iorは、文字列になっているので、これをString2Octet関数でバイト列に変換
      • バイト列に変換したior_byteの長さを len = strlen(ior+12) / 2 でlenにセットする。
        • memcpy関数でior_byteをその長さ分bufにコピー
        • RtORB_free関数でior_byteを解放する。
        • *current += len;としてcurrentを進める
    • tk_anyの場合:
      • CORBA_any *any = (CORBA_any *)argv;として符号化するデータをCORBA_any*にキャストする
      • CORBA_TypeCode tc_ = any->_type; として、CORBA_anyの実TypeCodeをtc_にセットする。
      • tc_ がNULLのときは、tc_をtk_nullのTypeCodeにする
      • Address_Alignment(current, 4);でアライメント調整をする
      • marshal_typecode関数でTypeCodeを符号化する。
      • char *v = CORBA_any_get_encoded(any, &len);を実行して、CORAB_anyのエンコードされたデータをとってる。これは、CORBA_anyの実装では、strcutやsequenceの複合型であった場合には、encodeされた形でデータを保持していることを前提としているため
      • vがNULLでなければ、すでに符号化されているはずなので、memcpy関数でbufにコピーする
      • vがNULLの場合には、stringを含む基本データ型であるので、CORBA_any_get_value関数でデータの先頭ポインタをargsにセットし、marshal_by_typecode関数の再帰呼び出しでデータを符号化する。
    • 上記以外の場合: RtORBではサポートしていないので、エラーメッセージを出す。

RtORBに実装された復号化処理(unmarshaling)

RtORBでは、データの復号化処理は、demarshal_by_typecode関数で行われます。これも符号化の処理と同じようにTypeCodeを引数として処理を行いますので、再帰呼び出しを行いつつ処理を進めることになります。
基本的には、marshal_by_typecode関数と符号化されたByteOrderを考慮しながらの逆処理を行うことになります。