strcpy_sとかmemcpy_s

職場で勤務時間のいくらかは学習時間に充てろという話があって、

社内の研修プログラムの受講とかで時間を費やすなら話は簡単だが、

そういうわけでもないなら何かネタをみつけてやらないといけない。

それで最近はセキュアコーディングというところでC言語の勉強をしている。

ソースコードの解析でこういうのは脆弱性の温床だという指摘はいろいろ見てきたが、

背景や打開策などわかっていない部分もあったので真面目に勉強するかと。


よく知られた話なのだがC標準ライブラリのstrcpy関数はけっこうこわい関数である。

C言語では文字列は文字の配列をNULL文字で終端することで表すのが通常で、

strcpyはNULL文字を検出するまで文字を右から左にコピーする関数である。

この関数が怖いのはNULL文字が出てくるまで際限なく転送してしまうことで、

転送先の配列のサイズなんて考えないわけである。

この結果、想定外の入力が与えられるとメモリ上の他のデータを破壊してしまうことがある。

それが脆弱性の原因となることがしばしばあるのである。


とはいえ、こういう文字列関係の関数は仕事ではほぼ使わないですね。

なのであまり興味はなかったのだが、この問題を緩和するための関数が導入されていたらしい。

C11(ISO/IEC 9899:2011)のAnnex Kで規定された”Bounds-checking interfaces”である。

strcpyに代表される危険な関数に境界チェック機能を追加したものを規定していて、

strcpyに対して strcpy_s のように “_s” を付けた関数名を使う。

NULL文字での終端を前提とした文字列関係の関数は基本的にあるわけだが、

memcpy_s というものも存在するらしい。

memcpyは指定されたサイズのデータを右から左にコピーする関数である。

strcpyと違って指定されたサイズを転送するので、あまりリスクはないと思っていた。

でも境界チェック機能付きのものがあるんですね。


そもそもこの境界チェック付きの関数とはどういうものか、strcpy_sの使用例を見てみる。

char buf[128];
errno_t err;

strcpy(buf, msg); err = strcpy_s(buf, sizeof(buf), msg); if(err != 0){ /* エラー処理 */ }

strcpyとstrcpy_sの使用上の差は2つある。

1つは転送先(上記では”buf”)とともに転送先のサイズ(上記では”sizeof(buf)”)を引数加えること。

もう1つは結果としてエラーコードが返ってくること。

従来のstrcpyは失敗することは基本的に想定しなくてよかったのだが、

境界チェックでひっかかると転送失敗ということになる。

その成否を表す値が返り値として渡されるわけである。

それをエラー処理につなげる必要があるかは設計によるが。


memcpy_sも同じような考えで転送先のサイズを渡すわけである。

memcpy(buf, rcv, len);
err = memcpy_s(buf, sizeof(buf), rcv, len);
if(err != 0){ /* エラー処理 */ }

単純に転送先のサイズより転送サイズが大きければエラーにするだけである。

この転送サイズを示すlenというのは、例えばデータ構造のヘッダに格納されているデータだと、

もしかすると想定外の値が入っていて転送先を破壊する危険がある。

本来はそういうことを意識してチェックする必要がある。

通常のmemcpyでもこういう書き方をすれば、上記のmemcpy_sはほぼ同じであろう。

if(len > sizeof(buf)){
   //エラー処理 }else{
   memcpy(buf, rcv, len); }

ただ、こういうことを個別に考えずとも徹底的に点検できるようにmemcpy_s というのがあるのだろう。


ところでこの”Bounds-checking interfaces”だが、C11ではオプション機能と位置づけられている。

必ずしも対応しているわけではなく、対応しているコンパイラでも

__STDC_WANT_LIB_EXT1__という定数を1にセットしなければならないという。

どうもこれはMicrosoftの提案でC11の規格書に掲載された経緯があるらしい。

STR07-C. 境界チェックインタフェースを使用し、文字列操作を行う既存のコードの脅威を緩和する (JPCERT)

そういえばVisual Studioで_s付きの関数を使えという警告が出てきたことがあったっけ。

元々はMicrosoft独自拡張だったが、規格書に掲載されて一応は標準規格にはなったが、

この拡張について懐疑的に思っているベンダーもあるらしい。


とはいえ、この関数は単なる境界チェックだけでもないんですね。

strcpy_s で転送先のサイズを超過する場合、転送先にはNULL文字を1文字だけ書き込む。

すなわち、転送に失敗した場合は転送先を空文字にするわけですね。

strcpyがNULL文字が出現するまで際限なく転送するという問題に対して、

昔からある標準ライブラリ関数のstrncpyを使うことで最大の転送数が決められるのだが、

これは最大の転送数に達するとNULL終端せずに終わってしまうので、後の処理で問題を引き起こす危険がある。

そういう問題を引き起こしにくいので使いやすいのではないか。


正直、NULL文字で終端されたchar型の配列というのは、

文字列を格納する手段としてはあまり使いやすいものでもないような気はする。

そういう環境で文字列を扱うことの是非は当然ある。

ただ、それが必要な場合にstrcpy_sのような関数を使うのは効果的に思う。

一方でmemcpyは外側でしっかり対策する方が効くのではないかなと思う。

でも徹底的にチェックするという意味でmemcpy_sを推奨したいという考えを持っている人もいるということだろう。


じゃあ、どうやると強固なプログラムを作れるのか?

それはプログラムの性質によるところもあるでしょう。

こうするのが安全と単純に言える話でもないのかなと思った。

バイトオーダーを選んでunpack

簡単なデータ変換を行うスクリプトをRubyで書いていた。

なんでRubyかというと、他の目的でRubyを使っている環境だったから。

処理を書いている中で、そういえばエンディアンを指定して整数とバイト列を往来するにはどうすればよいのだろうと。


Rubyではバイナリデータも文字列として扱うようですね。

Rubyでは文字列にエンコーディング情報が付加されているのだが、

ASCII-8BITってのがバイナリデータを扱うエンコーディングだと。

文字列を数字の配列に変換するためにはunpackメソッドを使うそう。

それでサンプルコードを見るとこんなことが書かれていた。

int_le = bytedata.byteslice(idx,4).unpack("L<")[0]
int_be = bytedata.byteslice(idx,4).unpack("L>")[0]

そもそもpack, unpackというのが慣れないところはあるが、

“<“という記号は一体何を表しているのかと気になった。


pack, unpackってのはPerlに由来しているのかな?

$text = pack("C*",@byte);
@byte = unpack("C*",$text);

のような感じで文字列$textと1byte単位の配列@byteを変換するのが典型的な使い方。

それぞれ引数の”C”がunsigned charに対応するというわけである。

ここには2byte以上の型もあり、32bit符号無し整数に対応するのが”L”である。

ただ、この”L”はバイトオーダーが環境依存である。(多くの場合はリトルエンディアンと考えられているが)

そこで “<“を付加するとリトルエンディアン、”>”を付加するとビックエンディアンと指定できる仕組みがあると。


こういうのってPerlにもあるのかと調べたらあるらしい。

もっともPerlでもRubyでもそうなのだが、ビックエンディアンの32bit符号無し整数は “N” でも表現できるそう。

なぜ “N”なのかというとネットワークバイトオーダーという意味らしい。

こちらの方がPerlでは一般的な書き方のようである。

逆にリトルエンディアンの32bit整数は “V” と書くそう。


このあたりは環境によりいろいろあるという話だが。

ただ、ちょっと前に.NET Frameworkのこの手の処理の実装例を見て驚いたんだけど……

BitConverter Class (Microsoft Learn)

この中でビッグエンディアンに対応させるためにはどうするかという話で、

IsLittleEndianがtrueならば、Array.Reverseでバイト列の順番を反転する処理を入れるとなっている。

果たして.NET Frameworkが動く環境でビックエンディアンがあるのかは疑問なのだが、

これを見て、実行環境のバイトオーダーと不一致ならばバイト順を反転させることで対応してくれということらしい。

もうちょっとなんとかならんのかと思うが。

充電しながら音楽が聴けるケーブルの謎

最近はコンセントの付いた列車も多いけど、

USB Type-Cでイヤホンを接続していると、同時には充電できない。

ちょっと不便だなと思っていたので、メルカリでこんなのを買った。

ハイレゾ対応 給電付き USB Type-C変換ケーブル(高耐久モデル) MPA-C35CSDPDBK (ELECOM)

新品で買うとけっこうするのだが、買って不要になった人が安くで出品していた。

これと一般的なアナログ接続のイヤホンも中古で安く買って組み合わせれば完成。


これとUSB Power Delivery対応のACアダプタと組み合わせると、

急速充電しながら、音声を出すことができていた。

というわけでとりあえず目的は達成出来そうである。

ただ、ふと気になったのである。どうやってUSB Type-Cからの音声出力とPDのネゴシエーションを両立しているのだろうと。


最近のスマートフォン・タブレットPCでは3.5mmミニジャックが付かないことが増え、

有線での音声出力手段がUSB Type-Cコネクタのみということも増えた。

このような機器の場合Type-Cのコネクタからアナログ音声出力ができる「Audio Adapter Accessory Mode」に対応しているのが普通で、

この場合はコネクタ形状の変換だけで3.5mmミニジャックを出すことができる。

規格上はこの方式はオーディオジャックへの変換のみが認められ、

ヘッドホンから直接Type-Cのコネクタを出す場合は適用できないそう。

すなわちDAC内蔵でなければType-C接続のヘッドホンは作れないということである。

本当にこのルールが守られているのかはよくわからないけど。


で、これとUSB PDは共存できるのかという話だが、できなさそうな気もするけど、できるという文献もあってよくわからない。

ただ、今回購入したケーブルはAudio Adapter Accessory Modeではなかった。

商品説明をよく読んでみると「DAC搭載」という記載がある。

ノートPCに接続してみるとUSB Audioとして認識して音を出せた。

というわけでUSB 2.0のオーディオデバイスと、USB PDの共存ということのようである。


ここでふと気になったのだが、USB PD対応のACアダプタとの組み合わせであれば、

給電能力を通信でACアダプタからスマートフォンに伝達できそうだが、

昔ながらのUSB BC(5V×1.5A)の場合は給電能力の伝達ができないのでは? と思った。

USB BCというのはUSB 1.1/2.0の通信ラインD+,D-をショートすることで1.5Aまで供給できることを示す仕組みである。

本来USB 1.1/2.0では500mAまでしか電流を引くことができない。

これでは充電に遅すぎるので1.5Aに拡張したのがUSB BCで長い歴史を持つ。

(Appleはこれを2.4Aに拡張したり、いろいろバリエーションはあるようだけど)

でも、このケーブルではD+/D-はUSBオーディオに接続されているはず。


というわけで、Type-A~Type-Cのケーブルを使って、充電器と変換器を繋ぐと、

なんとスマートフォンの表示は「急速充電中」になった。

これは予想外だった。5V×500mAの範囲で充電する「低速充電中」になると思ったから。

果たしてこの急速充電がどのようなプロファイルでの充電を指しているのかはよくわからないけど、

5V×1.5Aより大電力じゃないと急速充電を名乗るには値しないと思うが?

でも、そのためには接続されている充電器の能力をUSB PD以外の方法で把握しないといけないと思うが。


どうにもよくわからないので、調べていると、このケーブルにはUSB PDのコントローラも入っているのではと。

tomorrow56 (ThousanDIY)  / 【100均ガジェット分解】(69)オーディオDACを内蔵して「充電しながら使えるUSB Type-C変換ケーブル」 (note)

USB Type-Cではデバイス→ホストという向きに電力を送ることもできる。

この双方向給電の管理はUSB PDにより実現されているそう。

この変換ケーブルは双方向給電が必須である。

DACを駆動するには電力が必要で、充電器がないときはスマートフォンから供給する必要があるが、

充電器があるときは充電器からスマートフォンとDACに電力供給が行われるためである。

世の中には2つの機器と通信ができるUSB PDコントローラというのがあるようで、

このコントローラが充電器・スマートフォン双方とUSB PDのネゴシエーションを行っているのではないかということである。


そう言われると合点が行く部分もあって、それがこの仕様である。

USB Power Delivery対応 ○(最大27W対応)

27Wというのは9V×3Aのことだと思う。

USB Type-Cのケーブルで最大60W対応というのをよく見るが、20V×3Aまで対応という意味である。

3A超の電流を流す場合、安全面からケーブルをeMarkerで識別しなければならない。

裏返せば20V×3AまではType-Cのケーブルは通る可能性があるということである。

でも、このケーブルは単に電力を右から左に送るだけではなく、DACも搭載されている。

おそらくその都合、電圧も上限を決める必要があったのではないか。

単なるケーブルであれば制御は出来ないが、USB PDのコントローラがあれば、

充電器側が9V以上に対応していても、これを切り捨てるという対応ができる。


USB PD非対応の充電器を正しく認識できているのかはよくわからないが、

正しく認識した上でスマートフォンに見せていれば一応問題ないことになる。

でも、実際そういう挙動になっているかはよくわからない。

充電器の給電能力以上に電流を引いてしまうとトラブルの元なので、

ここは正しくコントロールできてないと危ないのだけど……


というわけで思ったよりは複雑な変換ケーブルだった。

ここまで賢いなら何でも出来ますよねという感じでしょうかね。

continue文に振り回されてた

これが終わったら、これをやる……というのがずっと続いてきた仕事、

やっと終わりが見えてきた感がある。

1年ちょっと前からずっとこんな状況だった覚えがある。

まだプロジェクト全体としてはやるべきこともいろいろあるけど。


このプロジェクトの計画段階の甘さにはいろいろ思うところがある。

その中でメンテナンス用のソフトウェアを作る必要があった。

社内で作れる人を新規に割り当てるのはおおよそ困難な状況、

自分はマイコンをどうにかするのが優先である。

そんな中で外注に出して、一定の部分は業者のアイデアも借りながら作り上げた。

ただ、正直なところ、担当者の理解度が悪くて、こういうパターンではうまく動かないのだがと、

そんなことが開発期間中ずっと繰り返されていたのが実情である。


それでも普通に使う分にはあまり問題ないものはできた。

ちょっと気になる動きはあるけど、メンテナンス用のツールだし……

と、いろいろ使う中で、こういう使い方をするとエラーが出る仕様だよなと確認したら、

なぜかエラーが出ずに動いてしまうということが何度かあった。

とはいえ、とりあえず使う分には困らないので後回しだとやっていたが、

他の作業も一段落したのでバグを直していくかと作業をしていた。

とっくに検収は終わっているし、小修正だし自分でやりますかと。


ソースコード見ると、イマイチな作り方だなと思うところはけっこうあったのだが、

そんな中で考慮が足らんと思ったのが continue文のこんな使い方だった。

for(....){
   if(....){
     ....; continue; //A
   }
   if(....){
     ....; continue; //B
   }
   if(....){
     .... //C
   }else{
     .... //D
   }
   if(cnt >= CNT_MAX){
     .... //E
   } }

continue文というのはループの終わりまでジャンプする文である。

このfor文はデータを順次処理していくのだが、カウント数が一定に達すると出力処理(上記の処理E)を行う。

処理Aをしたら、以後のif文の判定もなく終わりまでジャンプ、

処理Bをしたら、以後のif文の判定もなく終わりまでジャンプという形である。

このように後ろの処理をスキップすることで効率的な処理にしたつもりだったのだが、

条件によっては処理Aの時点で処理Eを行うべき条件を満たすことがあり、

さらに他の条件が重なると、エラーも無く処理が終わってしまうが、生成されるデータは壊れていると。


なんでこれにcontinue文を使ったのかは謎で、

if(....){
   .... //A
}else if(....){
   .... //B
}else if(....){
   .... //C
}else{
   .... //D
}
if(cnt >= CNT_MAX){
   .... //E
}

とelse ifを連ねていけば無駄なく記述できるためである。

他にも考慮が漏れてるパターンがあり、正しく処理Eに分岐するように修正したら直った。


というわけであまりこれはcontinue文を使う意味はわからなかったのだが、

こういう処理を書いているとcontinue文を使った方がよいかと悩むことも。

for(....){
  ...
  if(!error){
    ....
  }else{
    ....
    if(!error){
      ....
    }else{
      ....
    }
  } }

でもこんな風にif-elseのネストを深くする方を選ぶかなぁ。

これはこれで読みにくいことも事実だが、さっきの処理Eみたいに後ろに必須の処理がある場合も多いので。


break文はないとうまく記述できないことも多いが、

continue文はなくてもあまり困らないんじゃないかなと思う。

コーディングルールで使用を禁止しているところも多いんじゃないか。

できるだけ手を加える範囲を小さくしながら問題を解決していくのに苦心したが、

全くダメなフローでもなかったので、ちょっと手を加えれば使えた。

これでとりあえず発見した問題は解決したはず。

intの幅が32bitじゃないと怖いから?

定時内の移動中は仕事にちょっと関係する勉強でもするか、

ということでセキュアコーディングの勉強をしていた。

その中で、前々からなんで静的解析で指摘されるのだろうと思っていた部分の真の意味がわかった。

が、それは本当に心配するべきことなのだろうか? と思った。


(1u<<31)のような1をビットシフトしてデータを作る記述は、

特にマイコンのGPIO操作などではあまりによく見る記述である。

これは結果としては 0x80000000u と同じことを指すものの、

31bit目を1にするという明確な意図が伝わりやすいし、

(1u<<FOO_CH)のようにビット番号を他の定数を使って表したりもする。

あまりに当たり前すぎる表現だが、これがいちいちケチが付くのである。


これが意図した結果が得られることは机上検討で明らかなので、それでよいことにしているが、

果たしてこの記述にどのようなリスクがあるのだろうか?

答えはこの記述はint型のビット幅に依存するということのようだった。

1u<<31 が 0x80000000u と同じにならない場合があると。


1uというのはunsigned int型の1を指す。

さらに言えばC言語で整数演算するときはint型あるいはunsigned int型に揃える汎整数拡張というのがある。

この汎整数拡張というのもくせ者で、特にビット反転では思いもしない結果を生じさせかねないもので、

そういう問題を未然に防ぐためのコーディングというのもあるのだけど。

ただ、1u<<31 を32bit幅のレジスタなどに代入する分には何も直感に反する動きはないように思う。


しかしここには前提条件が合ってint型が32bit幅であることである。

いろいろデータ型モデルはあるが、今どきはほぼintは32bit幅である。

しかし16bitのマイコンなどではintが16bitというものも存在する。

この場合 1u<<31 は16bit幅のunsigned int型から押し出されて0になってしまう。

これにより 1u<<31 の結果は直感に反するものになりますよということである。


なるほど。確かに16bitのマイコンってのもあるから移植性の問題はあるかもしれない。

でも、そのときにこのコードをそのまま使うのは現実的ではない気もするが。

1uという整数リテラルは16bit幅、32bit幅、めったにないが64bit幅のintいずれでも表現できる。

一方で0x80000000uという整数リテラルは32bit以上の幅でなければunsigned intで表現できない。

このような場合はlongやlong longを適用することになる。

なのでintが16bitでlongが32bitの場合は 0x80000000u はunsigned long型になるはず。

この観点では 1u<<31 より 0x80000000u の方が移植性がよさそうである。


しかし、コンパイラによってintの幅が違うって不便じゃない。

というのは当然あって、それで stdint.h にuint32_t などビット幅固定の型が用意されている。

そして実はここには整数リテラル用のマクロってのもある。

UINT32_C(1) のように書くと最低32bitの幅のある符号無し型の整数リテラルを生成できる。

intが32bit幅の場合は UINT32_C(1)は 1u、intが16bit幅・longが32bit幅の場合は 1UL となる。

これを使って UINT32_C(1)<<31 って書けば文句ないだろ?

と静的解析にかけたのだがマクロ展開後の 1u<<31 で解析されたのか何も変わらなかった。


静的解析ツールが問題ないと判断できる書き方は ((uint32_t)1)<<31

のような表記になる。

でもこんなのをビットシフトで数値を作る度に書いていては括弧だらけで読みにくくなってしまう。

で、なんでこれが問題なのかというところにたちもどって考えて見ると、

int型が32bitより狭い環境に移植する場合に問題となると。

じゃあint型は32bitであると規定できれば問題にならないのでは? と。


16bitのマイコンのことはあるが、32bitも64bitもintは32bitであると考えてよい。

long型のサイズの方がアテにならない

32bit環境ではほぼ ILP32 で、int, long, ポインタ型がいずれも32bit幅である。

64bit環境では LP64 と LLP64 の2パターンがほとんどである。

LP64は intが32bit、longとポインタ型が64bitである。

LLP64は int, longは32bit、ポインタ型とlong long intが64bit型である。

UNIX系ではポインタ型をlongに代入する使い方が多くみられ、

その用途ではLP64が移植性がよいという理由で主流になっているよう。

とはいえ、いずれもint型は32bit幅である。

汎整数拡張の都合、int型の幅が変わる影響が大きいので、そこは回避したということなんだろう。


ちなみに16bit→32bitの移植ではintの幅は変わるが、longの幅はいずれも32bitなので、

この部分だけ見るとlongの方が移植性がよいように思えた。

この経緯から32bitを表すためにlongを使っているコードも多いんですよね。

この観点ではLP64は余計なお世話なのだが……

こういう歴史も考えれば、将来どうかというのを予測するのは難しいが、

int型が32bitより狭くなるということは考えないという決断はあってもいいように思う。


他にも想定しているリスクに見合わないような指摘もありますけどね。

ただ、想定しているリスクが何なのかは正しく勉強しておかないといけないと思ったのだった。

未定義の挙動と処理系定義の挙動

先日、このBlogを書くための調べごとの中で気づいたことがあって、

それはC言語で符号付き整数の演算でオーバーフロー時の挙動は未定義ということである。

えっ、そうだったの? それだと仕事で作ってるシステムで問題じゃないかと。


未定義というのは、コンパイラやプロセッサの都合のいいように決めてよいということである。

int16_tの場合、通常は最大値は32767、最小値は-32768である。

int16_t型の変数xが32767のとき、x++;がどうなるかは不明である。

この処理単独ならば、ほぼ間違えなく-32768になるのですが。

ただ、他の処理と合わせた最適化の中でどうなるかはわからない。


一方で符号無し整数型のオーバーフロー時の挙動は明確である。

uint16_tの場合、通常は0~65535の値を表すことができるが、

この範囲を超える場合は65536で割った余りとなると規定されている。

すなわちuint16_t型の変数yが65535のとき、y++;をすると0になる。

こういう動作をラップアラウンドって言ってますね。

ちなみにこの考え方は負の数を代入するときにも適用され、

-64をuint16_t型にキャストすると -64+65536=65472 となる。

これは負の数は2の補数で表現されると言っているのと同じである。


逆に符号なし整数を符号付き整数に変換する場合の挙動だが、

符号付き整数型の範囲外の値をどうするかは、処理系定義となっている。

処理系定義ってのはコンパイラやプロセッサの都合のいいように決めて良いということだが、

さっきも未定義の挙動と違って、マニュアルで定義されているということである。

とはいえ、ほとんどの場合はビットパターンをそのまま転記する。

そして最上位ビットが1の場合は2の補数で表された負の数として扱われる。

符号無しの 65472u をint16_t型に変換すると -64 となるわけである。


なので冒頭に「int16_t型の変数xが32767のとき、x++;がどうなるかは不明」と書いたが、

x = (int16_t)( ((uint16_t)x) + 1u); だと x=32767のとき-32768、x=-1のとき0となるとほぼ断言できる。

符号無しで 32767+1=32768 と演算して、これをint16_t型に変換すると、

典型的な処理系定義の動作ではこれを2の補数で表された負の数と解釈し、-32768 となる。

負の数をuint16_tに変換するというのは65536を加えた値にすることで、

int16_t型の-1はuint16_tにキャストすると 65565となる。これに1加えるとラップアラウンドして0となる。

0は符号付き型に変換しても当然0なので、これは処理系定義の動作も関係ない。


負の数がオーバーフローしたときの動作はわからないのに、

符号付き・符号無しとも範囲外の値を扱う場合のルールは決まるんですね。

ちょっと変な気もするのだが、ここにはいろいろ事情もあるよう。

int32_t型の変数x,y について (x-y)>10 という条件判定を行うことを考える。

ここで x=-2147483630, y=2147483640 の場合、

単純な数学的な計算では x-y=-4294967270 となり 10より小さい。

しかし、-4294967270 はint32_t型の範囲を超えてしまう。

2の補数形式で表して32bitに入らない部分は切り捨てると 26 となるので、

もしそのようにして計算した結果で大小比較を行えば10より大きいということになる。


果たしてどちらをお望みかというのは一概には言えない話だが、

符号付き整数型のように最大値に1加えると最小値に戻る、

というような考え方を適用すると後者の挙動となる。

32bitのプロセッサだと、結局は32bitにして大小比較しないといけない。

なので、ほぼ後者の挙動しか考えられないところである。

ただ、64bitのプロセッサの場合、64bitで演算して大小比較するという方法もとれる。

この場合は前者のような挙動になることも考えられる。コンパイラ次第である。

(int32_t)( ((uint32_t)x) – ((uint32_t)y) )>10 と書けば、確実に後者の挙動となる。

x=-2147483630は uint32_t型では 2147483666 となり、

2147483666-2147483640=26 なので 10より大となる。


なので、まさにこういう書換をしたんですよね。

そしたら、実行コードとしては何も変わらないものが生成された。

最適化レベルにも依存するのかもしれないが、32bitのプロセッサではほぼこういう動きしかしないのだろう。

というわけで思った挙動をするコードは元々生成できていたことが確認出来たと。

今後、コンパイラの設定変更や移植があっても同等の処理になることは保証できると。

(処理系定義の動作があるので、そこに差分がないのが前提だが)


手元にあった64bitプロセッサのコンパイラでも同じことをやってみたが、

こちらは一例だけではあるが、そちらも同じコードが生成できていた。

これこそ最適化レベルや前後のコード次第の面もあるとは思うが。

なのでこれが問題になるケースって本当にあるのかな? と思ってしまうが、

forループのループ変数で発生した場合とか、実際に問題になるケースはいろいろあるらしい。

今回のケースでは特に問題はないということが確認出来たということである。

休暇の間にタイマの評価

明日から徳島に向けてお出かけ。帰ってくるのは月曜日である。

金曜・月曜と休暇を取るわけだが、ここで気づいたのである。

それは長時間かかる評価をやるのに絶好の機会じゃないかと。


長時間かかる評価というのはタイマである。

最初、タイマの最大時間は3600秒、すなわち1時間にしようと考えていたのだが、

それでは足りないという話になった。そんな気はしてたが。

そこで別のシステムを参考にして 86400秒、すなわち24時間を上限とすることになった。

ということは少なくとも24時間は動かさないと検証できないわけである。


24時間ってなら普通の週末に動かしてても達しますが。

なので、それでもいいかなと思ったんだけど。

より長い期間をかけたほうがいい評価であることは確かである。

というわけで今日からやるかと思ったのだが、電源をつけっぱなしで去るには配線がとっちらかっていたので、

配線をきれいに片付けて、安全点検をしてと、けっこう手間はかかった。


あとはフリーランタイマーのオーバーフローというのもありますが。

これはスタートの値を変更することで、すぐにオーバーフローが起こるようにして影響がないか確認している。

ただ、フリーランタイマーを使用する動作は様々あって、

しかも、そのときに動作がおかしくなってもバレないものもあるよなと。

フリーランタイマーを使う処理は基本的にどれも同じ書き方をしているから、

代表パターンで影響がないのを見れば大丈夫とは言えそうだけど。


ただ、この休暇中に動かしてもオーバーフローしないものがあるなと。

最初はオーバーフローすると思っていたのだが、ちょっと考え方が間違えていた。

というか書いているときにちょっとマズいことに気づいてしまった。

その内容次第ではそもそも86400秒のタイマーの試験もやり直しかもしれない。


少なくとも途中で停止せずに完走してほしいものだが。

試験対象の機器は多分大丈夫で、心配なのは測定用のPCですね。

Windowsのファイル名ソート

昨日・今日と立て続けにファイル名に数字が入る場合のソート順に翻弄されていた。

というのもWindowsではファイル名に数字が含まれる場合、

数字以前の部分が同じファイル同士では数値の大小で名前順が決まる。

foo_1.jpg → foo_2.jpg → foo_12.jpg → foo_100.jpg のような順になるということである。


けっこう変なアルゴリズムだと思うんですけどね。

というのも普通に文字コードの大きい順に並べた場合、

foo_2.jpg と foo_12.jpg は5文字目が ‘2’>’1′ ということで、後者の方が小さいという判定になる。

でも数字同士の場合は、このような単純な文字コードの大小で判定せず、

その後ろに続く数値を使って 1<12 という判定にする仕組みを導入していると。


何が起きたかというと、その後に何も考えずにファイル名でソートする処理を行ったらヘンテコな順序になってしまったと。

ファイル名を付けた人はWindowsの名前順を前提にしていたのだが、

わざわざこんな複雑なアルゴリズムでソートすることはそうそうない。

この問題の解決策は容易で、0を頭に付けて桁数を揃えれば良い。

foo_2.jpg, foo_12.jpg, foo_100.jpg となっているのを、

foo_002.jpg, foo_012.jpg, foo_100.jpg とすれば想定した順序になる。

ファイル名を連番で振り直すツールを使って、Windowsの名前順に bar_001.jpg のように改名する方法でもよい。


ただ、こういうのも余計なお世話じゃないかと思ったのが今日の話で、

部品名のフォルダが並んでいるところからファイルを探すとき、

12345 と 12A45 の間がとても大きく離れていたことである。

この間に 5678 のようなフォルダ名がたくさん挟まってしまうと。

基本的に頭の2桁でシリーズを表すような構造になってるので、

文字コード順に揃えると同じシリーズは同じようなところに集まるはず。

でもWindowsでは 12 < 5678 < 12345 のような判断をしてしまう。

12A45 は数値の大小比較では 12 として扱われるためである。

ということでいらんことしやがってというやつである。


なんでこんなアルゴリズムを採用してるんだろう?

と思ったのだけど、ファイル名に連番を振るとき、

最初はせいぜい100個程度だろうと foo_01.jpg から振り始めて、

でも想定外に数が増えたとき foo_99.jpg の次をどうしようとなったとき、

そこから桁数を増やして foo_100.jpg と振っても期待通りのソートがされる点にメリットがあるのだろう。

そこに期待している人がどれぐらいいるのか? とは思うんですけどね。

薬に付いてるのはGS1コード

滞在中の食卓にはいろいろなものが置かれていて雑多もよいところだが、

その中に薬のPTPシート、比較的よくある置物ではないかと思う。

で、気になったのがバーコードがあって (01)049… という番号が書かれていること。

EANコードだと日本は49か45はじまり、多分EANコードを表しているのだろうが、

なぜ(01)と頭に付いているのだろうか。ちょっと気になったので調べたら意外といろいろあったので書いておく。


最近は医療機関で薬が処方されることも全然ないですね。

現在、薬には調剤包装単位・販売包装単位・元梱包装単位のそれぞれにバーコードを振る必要がある。

薬局に納入される最小単位が販売包装単位でここにバーコードがあればよさそうだが、

錠剤ならPTPシート、注射薬ならアンプルなどの調剤包装単位にもバーコードを付けることが求められている。

なぜ(01)という接頭子を付けているのか? これは薬のバーコードは他の情報も含むことが期待されているからである。

医療用医薬品を特定するための符号の容器への表示等について (pdf) (厚生労働省)

調剤包装単位としては特定生物由来製品を除いては商品コードのコード化のみでよい。

しかし、販売包装単位では商品コードに加え、有効期限・ロット番号の記載、

その販売包装単位をまとめた元梱包装単位にはこれに加え数量のコード化も必要となっている。


では、どうやってコード化するかというと、GS1アプリケーション識別子を使う。

商品コードは(01)、有効期限は(17)、ロット番号は(10)、数量は(30)で符号化できる。

(01)149…(17)280600(10)ZZ00 といった具合である。

調剤包装単位でもこのような情報をコーディングしていることもある。

PTPシートやアンプルになってしまっても、ロット番号や有効期限がわかるという点では好ましい形態である。


商品コードは14桁で表し、EANコードより1桁多い。

14桁のGTIN-14は商品をまとめて詰めた箱に振られるITFコードに使われる。

1個単位のEANコードの頭に梱包形態を表す1~8の数字を加える。

ただ、医薬品では調剤包装単位は0、販売包装単位は1、元梱包装単位は2はじまりとするローカルルールがあるよう。

さらに調剤包装単位と販売包装単位は先頭の桁以外も異なる必要があるという。

医薬品以外の用途ではGTIN-13(EANコード)の頭を0で埋めれば良い。


このGS1という方式はけっこういろいろな情報を表現できるよう。

GS1-128シンボル (GS1 Japan)

日付も製造日を表す場合は(11)になったりする。有効期限などと併用もできる。

これまで出てきたもの以外だと商品の量を表すコードが様々ある。

GTIN-14の頭を9にすると不定貫商品を表すコードとなり、

(10)949…(3113)007500 とすると、例えば長さ7.500mの棒を表現できたりする。

(3113)というのはm単位の長さを小数第3位からの6桁で表すことを示している。

量り売り商品のバーコードをインストアコードではなく、販売元が振ることもできるのである。

あと、使用例があるかは定かではないのだがGSRNというのがある。

会員コードなどをGS1事業者コード(バーコードの先頭7/9桁)をベースに一意に付与する方法で、

(8018)49xxxxxxxyyyyyyyyy というような形で付与できるらしい。


一般的にはCODE128のバーコードで表現する。

(01)149… といった要素ごとに頭にFNC1という特殊文字を付けて、

[FNC1]+01149… と符号化してそれぞれくっつけていく。

ただ、アプリケーション識別子によっては固定長と決まっていることがあり、

その場合は後ろの[FNC1]は省略可能と定められている。

医薬品のバーコードで使うものでは (01)(17)は固定長である。

なので[FNC1]+01149…1728060010ZZ99 のようにFNC1を挟まずにくっつけることも可能である。

ただ、調剤包装単位と販売包装単位ではCODE128ではないらしい。

GS1データバー限定型、GS1データバー限定型合成シンボルを使うことと書かれている。

これはどうも独自のバーコード規格らしく、なかなか複雑なことを言っている。

ただ、実はGS1データバー限定型は(01)の表現しかできず、

さらに先頭は0 or 1でなければならない。ほぼ13桁分しか符号化されていないのだった。


日本だとQRコードの方がなじみがありそうですけどね。

これもGS1の表現方式の1つとして認められている。じゃあそっちの方がよいのでは?

QRコード (GS1)

QRコードにはFNC1に相当する特殊文字がないが ]Q3 で表すという。

FNC1が必要な場所の考え方は同じなので固定長の後は省略可能である。

不思議な話ですけどね。そもそもGS1データバーってなんやねんという感もある。

浮動小数点演算の微調整

4月に職場に来た人の歓迎会をやってほしいという話があって、

いろいろ考えるもなかなか思うようにいかないものである。

でもとりあえず手配はできたからいいか。


浮動小数点数は難しいなと思うことがあった。

マイコンで測定した値を一定の演算を行うと、例えば-100~100の値に変換される。

この値が指定された値を出たらフラグを立てるという処理がある。

ここで-100, 100ピッタリを指定した場合、フラグは立ってはいけないと思った。

ところがこの処理、浮動小数点演算で行わざるを得ず、変換後の値が-100~100をわずかにはみ出すことが判明。

この結果、-100, 100ピッタリを指定した場合もフラグが立つことがあると。


整数演算でやればこういう問題は避けられた可能性はあるが……

指定された値が –100, 100 ピッタリの場合に動作する処理を加えようかとか、

比較する前に整数化してしまうか、とかいろいろ考えたのだが、

浮動小数点演算の量が増えるのはあまり好ましいことではないか……

なんてことを考えた結果、変換時のパラメータを微調整することで解決した。

例えばパラメータに 1666.666 となっていたのを 1666.668 にするとか。

最終的な演算結果では浮動小数点の仮数部の1digitの差とか、そんなところを突き詰める微調整だった。

結果として従来のアルゴリズムはほぼ変えずに問題を解決できた。


こういう解決方法ってどうですかね? とチームリーダーに相談にいくと、

微調整により他の部分の演算結果が若干変わるけど影響ないかと。

測定結果のばらつきの方が大きいだろうと断言できる話である。

しかし、これだけ細かい微調整だと、移植などで影響も受けそうだが。

特に移植を想定しているわけではないが気になるところである。


過去に浮動小数点演算であれこれやったことが一度あったが、

そのときは測定値の端がとんでもない数字に吹っ飛ぶ仕組みだった。

なのでこういう問題は考えることもなかった。

そもそも –100, 100のような端の値を指定したときにフラグが立たないなんて約束はどこにもなかったのだが、

社内ではこういう考えが一般的なので、これも大丈夫だっけ? とテストすると案の定ダメという。