パケット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 (」などと半角空白があったりなかったりしています.意外と人間くさい感じがして,何だか親しみが持てました.