EarOwlの日記: B-CAS 関連まとめ
ネット上で得た各種情報をもとに、 B-CAS カードの仕組み、不正使用関連の現状等について自分なりに理解した内容をまとめてみました。
正確性は保証しないのでそのつもりで。
参照した情報源は最下部に記載。
B-CAS カードの仕組み
あなたのタレコミがスラッシュドットを支えます。さぁ、タレこめ!
ネット上で得た各種情報をもとに、 B-CAS カードの仕組み、不正使用関連の現状等について自分なりに理解した内容をまとめてみました。
正確性は保証しないのでそのつもりで。
参照した情報源は最下部に記載。
B-CAS カードの仕組み
素直にコーディングするなら、バッファに使う型は符号付きではなく符号なしの 8bit 整数の配列/ポインタにすると思うんだけど、なぜか某ライブラリの API は符号付き整数のポインタを要求するんだな…。
『API が要求する型にはなるべく合わせる』というポリシーから、当初はライブラリの方に合わせて符号付きの型でバッファを確保するようにコーディングしたのだけれど、『バッファから 2バイト分取り出して 16bit 整数値にする』処理で、符号拡張の影響で間違った値になってしまうバグを作り込んでしまった。
バッファは符号付きの型のままで、 16bit 整数値を取り出す処理の方で明示的に型変換をすることで対処しようかとも考えたけれど、『さすがにこれは API の仕様の方がおかしいだろう』と考え直し、バッファを符号なしにして、ライブラリの API の呼び出し時に符号なし→符号付きに型変換するようにした。
文字/文字列として使用する型を、 (typedef signed char my_char_t みたいに) signed/unsigned を指定して typedef しないで欲しい。
某組込向けライブラリがこれをやっていて、しかも char 型と符号の有無が異なるせいで、そのライブラリの API を呼び出す箇所で明示的に型変換しないと Warning が出る。
C言語が使われるのがほぼパフォーマンスやメモリ使用量の制限の厳しい条件下だとしても、コードの全てにそのような条件でのチューニングが必要とは限らない、むしろチューニングが必要なのは一部のコードのみとなるケースがほとんどなのではないでしょうか。
例えば処理速度なら、『プログラムの処理にかかる時間の80%はコード全体の20%の部分が占める』とすれば、処理速度の要求がある程度厳しい場合でも、コード全体のうちおよそ 80% についてはチューニングは不要であることが多いと思われます。
チューニングが必要な部分と不要な部分で別の言語を使うべきとまでは言わないですよね?
データの扱い方によっては…具体的に言うと、『1回分のデータ』をセットで扱うことが無く、むしろ気温は気温だけで、湿度は湿度だけでといった具合にデータの種類毎に扱うような場合はむしろそちらの方が適切ということも有り得ますね。
『適切なデータ構造』というのは、そのデータがどのように扱われるのかにも依存するということで。
もちろんパフォーマンスやメモリ使用量の制約がある場合は、それに応じた形にする必要がありますが、それはあくまでも『特殊な条件』という意識が必要なのではないかと思います。
一般的にはやはりバグを生まないことが重要だと思い、そのために読みやすさ、間違いの犯しにくさを実現する方法を考えています。
グローバル変数は static にして外部へは公開せず、インターフェース関数を公開するというのは確かにその通りですが、今回の日記ではそこが主眼ではないので static は付けませんでした。
それよりも『データ構造の詳細はどうでも良いこと』という主張には疑問です。
単に正しく動作させるというだけなら配列だろうが構造体だろうが問題となることはありませんが、保守性や流用性を考えたとき、適切なデータ構造を選択するということは重要になってくると思います。
実際、データ構造が適切でないために、それらのデータを扱う処理の記述が複雑になり、取り除くことの難しいバグの温床となっているソースコードは今までに数多く見てきました。
今回のように単純な例ならバグに繋がることは少ないと思いますが、常に適切なデータ構造を選択するよう心掛けることが、より複雑なデータを扱う際にバグを減らすことに繋がると考えます。
例として、気温・湿度・気圧・風向・風速の 5つの int 型のデータを 100回分保存するバッファを考える。
よく見かけるのが、
int weather_data[100][5];
という形で確保して、
weather_data[n][0] = temp;
weather_data[n][1] = hum;
…
という風に保存するという書き方。 (もっと酷いものだと int weather_data[5 * 100]; なんていうのもあるが…。)
これだと、『1回分のデータ配列中の何番目にどのデータ (気温・湿度…) が保存されているか』ということがソースコード上で明確でなくなってしまう。
配列のインデックスとデータを対応付ける #define を
組込系のソースコードなので gcc の拡張機能のような特殊なことをするまでもなく、スタートアップルーチンが main 関数の後にさらに別の関数を呼ぶようになっているというだけではあるのですが、それなら main 関数の前に初期化用の別関数を呼ぶのが普通だろうと。
某社から受け取ったソースコード。
main 関数では初期化処理のみを行い、主要な処理は main 関数を抜けた後に別の関数を呼ぶということを素でやっている…。
そういうのはネタだけにして欲しい…。
私の前に開発を担当していた人によると、そのソースコードは『後で直すつもりで雑な書き方のままの箇所が多く残っている』ということだったのですが、個人的にはこういうコーディングの進め方にはあまり賛同できない。
当初からきれいに書くことを意識していても、規模が大きくなるにつれて汚い部分が出てきてしまうことはままある。
まして最初から雑な書き方で進めていったら、後からの修正では雑な部分を取り除ききれなかったり、修正にかかる負担がかえって大きくなったりしてしまう。
さらに、とりあえず動作するコードを早急に書かなければならない場合でも、雑な分早く書くことが出来るかというと、あまりそうは思えない。むしろきれいに書くことを心がけた方が、見通しが良くなり早く完成させられるように思われる。
まったくその通りなんですが…
プログラミングは専門外の人が、必要にかられて取り急ぎ作ったようなものならともかく、仕事として受けておきながらこれは無いだろう、と…。
ソフトウェアは『設計ができる』と『動くものが作れる』の間の落差が激しくて、しかもそれを分かっていない、『動くものが作れる』レベルで十分だと思っている人が (自称) プログラマの中にすら沢山いるような気がします。
その1 : GUI 編
ある数値の表示にテキストボックスを使っていて…その数値を保存するときには…テキストボックスからテキストを取得して…文字列→数値変換をして…保存している…
さらに…そのテキストボックスはデータの表示に使うだけで…ユーザーがデータを入力する必要は無いもの…それなのに入力が禁止されていない…
その2 : DB (Access) 編
mdb ファイルが複数あって…複数の mdb ファイルにまたがるいくつかのテーブルで…オートナンバーで振られる ID の値が…一致する前提になっている…
つまり…DB にデータを追加する際は…作業者は ID が一致するように注意を払わなければならない…
殴り書きの設計書ではいくら内容が正しくても認めてもらえないのと同じように、殴り書きのソースコードもいくら動作が正しくても完成したと見なしちゃダメだよなぁ…。
前回の日記はプロジェクトの進め方等にかかわる部分でしたが、今度はプログラムの中身について。
そもそもこのプロジェクトの目的は、今までバラバラに作成されてきた、各種のターゲットに対する似たような処理を行うツール一式を、一つにまとめた上で、可能なところを自動化して効率を上げることと、今後の分析等のためにデータを蓄積することらしい。
ということで、本来ならば各種のターゲットに対する処理の共通点・相違点を一通り調査・理解し、メンテナンス性を考慮した設計が求められるところのはずが、どうやら特定のターゲットに対し動作するものを早急に要求されたこともあって、設計が不十分なままそのターゲットに対応する部分だけ動作するものを作ったっぽい。
犯人は巨人ファンでA型で眼鏡をかけている -- あるハッカー