パケット3

課題

ISO 13818-1やARIB B10を読解中ですが,その助けとして解析ツールを自作してます*1.問題は,数千回の単純作業が必要だと判り始めたところです.この単純作業を半自動化したいというのが課題です.
世に出回っているツールは数多ありますが,とりあえず,参考にしている2本.

シェアウェアだし,30日制限だったりなのでフリーウェアを探さないと・・・

対処

ISO,ARIBのデータ構造を記述した表(Table)を文字列でコピーして,awk で整形処理します.

現在(11/7),細々とやってますが,if,else などの分岐を加味しないと,マスク,ビット数,シフト桁などが不正になることに遅ればせながら気付きました.ある程度の簡易的な構文の解析をしないといけないみたいですね.スタックひとつでなんとかなればいいけど・・・それとも再帰させるか・・・と思いきや,ちゃんとバイト毎に分岐が組まれているので,問題がないようです.よかったー(11/9)・・・あれ?入れ子になったifの場合は,考慮が必要ですね・・・*sigh*(11/10).

PDFから文字列コピー

下図のようにPDFの該当部分を選択・コピー(Ctrl-c)します.

図のように所望の部分以外も選ばれることがありますが,それはテキスト上で削除することにします.また,pdf2text などのツールでは,該当部分を探し出す面倒や,改行が削除されてしまうなどの問題があったので,PDFから文字列コピーする方法とします.

awkで処理した結果

関数の整形(txt2function.awk),クラスの整形(txt2class.awk),出力関数の整形(txt2print.awk)の3本の結果をまとめてあります.いずれの結果もC言語の文法的には破綻しているので,後は人手で整形する必要があります.各々に適当なファイルのインクルード文を付加したり,定型の関数コールを埋め込んだりは当該awkを修正してできあがりとします.

/*
 * Table 2-2 . Transport packet of this Recommendation | International Standard
 */
void transport_packet( long& sp )
{
  TransportPacket sec;

  sec.sync_byte                       =packet[sp++];             /* 8:bslbf */
  sec.transport_error_indicator       =(packet[sp]&0x80)>>7;     /* 1:bslbf */
  sec.payload_unit_start_indicator    =(packet[sp]&0x40)>>6;     /* 1:bslbf */
  sec.transport_priority              =(packet[sp]&0x20)>>5;     /* 1:bslbf */
  sec.PID                             =(packet[sp++]&0x1F)<<8 | packet[sp++]; /* 13:uimsbf */
  sec.transport_scrambling_control    =(packet[sp]&0xC0)>>6;     /* 2:bslbf */
  sec.adaptation_field_control        =(packet[sp]&0x30)>>4;     /* 2:bslbf */
  sec.continuity_counter              =packet[sp++]&0x0F;        /* 4:uimsbf */
  if(adaptation_field_control = = '10' || adaptation_field_control = = '11'){
    adaptation_field();
  }
  if(adaptation_field_control = = '01' || adaptation_field_control = = '11') {
    sec.data_byte                       =packet[sp++];           /* 8:bslbf */
  }
}
}

/*
 * Table 2-2 . Transport packet of this Recommendation | International Standard
 */
class TransportPacket
{
  public:
    bslbf	sync_byte:8;
    bslbf	transport_error_indicator:1;
    bslbf	payload_unit_start_indicator:1;
    bslbf	transport_priority:1;
    uimsbf	PID:13;
    bslbf	transport_scrambling_control:2;
    bslbf	adaptation_field_control:2;
    uimsbf	continuity_counter:4;
    if(adaptation_field_control = = '10' || adaptation_field_control = = '11'){
      adaptation_field();
    }
    if(adaptation_field_control = = '01' || adaptation_field_control = = '11') {
      bslbf	data_byte:8;
    }
  }
}
;

void TransportPacket::print()
{
  printf( "\n" );
  printf( "*TransportPacket\n" );
  printf( "sync_byte ........................... : 0x%02X\n" );
  printf( "transport_error_indicator ........... : 0x%02X\n" );
  printf( "payload_unit_start_indicator ........ : 0x%02X\n" );
  printf( "transport_priority .................. : 0x%02X\n" );
  printf( "PID ................................. : 0x%04X\n" );
  printf( "transport_scrambling_control ........ : 0x%02X\n" );
  printf( "adaptation_field_control ............ : 0x%02X\n" );
  printf( "continuity_counter .................. : 0x%02X\n" );
  if(adaptation_field_control = = '10' || adaptation_field_control = = '11'){
    adaptation_field();
  }
  if(adaptation_field_control = = '01' || adaptation_field_control = = '11') {
    printf( "data_byte ........................... : 0x%02X\n" );
  }
}
}

txt2function.awk

awkのソースは一番面倒な関数の整形だけを載せておきます.実行は次のようにします.

$ awk -f txt2function.awk pdf-table.txt >function.txt

BEGIN{
  function_name="";
  level =0;

  mask[1] =lshift(1,7);
  mask[2] =lshift(3,6);
  mask[3] =lshift(7,5);
  mask[4] =lshift(15,4);
  mask[5] =lshift(31,3);
  mask[6] =lshift(63,2);
  mask[7] =lshift(127,1);
}

END{
  if( function_name!="" ){
    print "";
  }
}

# タイトル
/^(表[0-9]+|^Table) [0-9]+-[0-9]+/ {
  if( function_name!="" ){
    print "";
  }

  print "/*";
  print " * " $0;
  print " */";
}

# 関数
/^.*\(\) *{$/ {
  split( $0, array, /_/ );
  class_name ="";
  for( var in array ){
    class_name =class_name toupper(substr(array[var],1,1)) substr(array[var],2);
  }
  gsub( /\(\) *{/, "", class_name ); 

  function_name =$0;
  gsub( /\(\) *{/, "", function_name );
  print "void " function_name "( long& sp )";
  print "{";
  print "  "class_name" sec;"
  print "";

  level =1;
  bits=0;
}

# メンバ
/^.* [0-9]+ .*$/ {
  mssg ="";

  bytes =int(($2+7)/8);
  after_bits =bits+$2;
  mask_bits  =$2 % 8;

  if( after_bits>=8 ) SP="sp++";
  else                SP="sp";

  for( i=0; i<level; i++ ) mssg =mssg "  ";

  space =32-length($1);
  if( space<=0 ) space =0;

  if( mask_bits==0 ){
    if( bytes==1 ){
      mssg =mssg sprintf( "sec.%s%*s=packet[%s]", $1, space, " ", SP );
    }else{
      mssg =mssg sprintf( "sec.%s%*s=packet[%s]<<%d", $1, space, " ", SP, (bytes-1)*8 );
    }
  }else{
    sbits =9-bits-$2;
    MK =rshift(mask[mask_bits],bits);
    if( bytes==1 ){
      if( sbits==1 ){
	mssg =mssg sprintf( "sec.%s%*s=packet[%s]&0x%02X", $1, space, " ", SP, MK );
      }else{
	mssg =mssg sprintf( "sec.%s%*s=(packet[%s]&0x%02X)>>%d", $1, space, " ", SP, MK, (sbits-1) );
      }
    }else{
      mssg =mssg sprintf( "sec.%s%*s=(packet[%s]&0x%02X)<<%d", $1, space, " ", SP, MK, (bytes-1)*8 );
    }
  }

  for( j=1; j<bytes; j++ ){
    k =bytes-j-1;
    if( k>0 ){
      mssg =mssg sprintf( " | packet[%s]<<%d", SP, k*8 );
    }else{
      mssg =mssg sprintf( " | packet[%s]", SP );
    }
  }
  
  space =64-length(mssg);
  if( space<0 ) space =1;
  printf( "%s;%*s/* %s:%s */\n", mssg, space, " ", $2, $3 );

  bits =after_bits % 8;
}


# 条件分岐
/^if *\(/ {
  for( i=0; i<level; i++ ) printf( "  " );
  mssg =$0;
  gsub( /= =/, "==", mssg );
  gsub( /if *\( */, "if( ", mssg );
  gsub( / *\) *{/, " ){", mssg );
  print mssg;
  level = level+1;
}

/^else if *\(/ {
  for( i=0; i<level; i++ ) printf( "  " );
  mssg =$0;
  gsub( /= =/, "==", mssg );
  gsub( /if *\( */, "if( ", mssg );
  gsub( / *\) *{/, " ){", mssg );
  print mssg;
  level = level+1;
}

/^else *{/ {
  for( i=0; i<level; i++ ) printf( "  " );
  mssg =$0;
  gsub( / *{/, " {", mssg );
  print mssg;
  level = level+1;
}

# 繰り返し
/^for *\(.*{/ {
  for( i=0; i<level; i++ ) printf( "  " );
  mssg =$0;
  gsub( /= */, "=", mssg );
  gsub( /for *\( */, "for( ", mssg );
  gsub( / *\) *{/, " ){", mssg );
  print mssg;
  level = level+1;
}

/^for *\(.*\)$/ {
  for( i=0; i<level; i++ ) printf( "  " );
  mssg =$0;
  gsub( /= */, "=", mssg );
  gsub( /for *\( */, "for( ", mssg );
  print mssg;
}

/^ *} *$/ { 
  level =level -1;
  for( i=0; i<level; i++ ) printf( "  " );
  print $0;
}

/^[a-zA-Z0-9_]+\(\)$/{
  for( i=0; i<level; i++ ) printf( "  " );
  print $0";";
}
上記コード中のバグ

awk の array の列挙「for(..in..)」では順序が不定になってしまいます.

$ echo "abc_def_ghi_jkl" \
> | awk '{split($0,array,/_/); for( var in array ){ print var, array[var]; }}'
4 jkl
1 abc
2 def
3 ghi

本来は,下記コードのように「for(;;)」しなければなりませんでした.

$ echo "abc_def_ghi_jkl" \
>| awk '{num=split($0,array,/_/); for( i=1; i<=num; i++ ){ print i, array[i]; }}'
1 abc
2 def
3 ghi
4 jkl

補遺

肝はクラスのメンバ変数をビットフィールドとして宣言してしまうことと,ISO,ARIBのビット列での宣言を,C言語のビット演算に置き換えるところです.これは,ISO,ARIBの定義が区分毎にバイト単位になっているため,割合に簡単に置き換えが可能になっています.そうでなければ,もっとややこしい事になっていたと想像します.規格のビット列の宣言は,つぎのようになっています.

データ名 ビット数 累計(8の剰余)
sync_byte 8 bslbf 8 これは1バイト
transport_error_indicator 1 bslbf 1
payload_unit_start_indicator 1 bslbf 2
transport_priority 1 bslbf 3
PID 13 uimsbf 16 ここで2バイト
transport_scrambling_control 2 bslbf 2
adaptation_field_control 2 bslbf 4
continuity_counter 4 uimsbf 8 ここで1バイト


「if(」,」「if (」などと半角空白があったりなかったりしています.意外と人間くさい感じがして,何だか親しみが持てました.

*1:自作をすることで,読解の助けにするという意味で,解析ツールを作ることを目的としていません

*2:このツール,別ID表示と共に下の方にPESとしての表示もしている.なぜ,わざわざ別のID用の表示をするのだろうか?