struct をletでパターンマッチングする
#[allow(dead_code)] struct St { i: i32, j: i32, k: i32, } fn new() -> St { St{i:1, j:2, k:3} } fn main() { let St { i, // フィールド名と同名で束縛 k: alt_name, // フィールド名と異なる名前に束縛(順序が変わってもOK) .. // いらないフィールドは .. で省略できる } = new(); println!("{} {}", i, alt_name); // 1, 3 }
Result について
Result<T, E> という型が、std::result というモジュールに定義されています。
この型は、ファイルの入出力やオープンのような失敗する可能性のある関数の戻り型として使われます。
宣言は以下のような感じ。
enum Result<T, E> { Ok(T), Err(E) }
enumなのでパターンマッチして使用するわけです。
たとえば、ファイルをオープンするのに、以下のようなコードを書きます。
use std::fs::File; let mut file = match File::open("some.txt") { Ok(file) => {file} // Okなら、そのまま返す。 Err(why) => {panic!(why.to_string())} // 失敗時は、panicでプログラム終了 }
unwrap() : エラー時に panic を呼び出す。
いちいち match して Okの値を取り出すのは面倒なので、Okの値を取り出す unwrap() という関数があります。
let mut file = File::open("some.txt").unwrap();
file には、Ok(x) の x が代入されます。 open が Err(why) を返すと、unwrap() は why の値を表示してpanicします。
"{:?}"でフォーマットして出力するので、あまり見やすい表示とは言えませんが。
thread '<main>' panicked at 'called `Result::unwrap()` on an `Err` value: Error { repr: Os(2) }', /home/rust/src/libcore/result.rs:729
ok().expect(パニック時の表示文字列)
why
を捨てていいなら、Result<T,E>::ok() で Option
let mut file = File::open("some.txt").ok().expect("open error");
この場合は、エラー原因である why を捨てているので、ファイルのオープンエラーが発生したことはわかりますが、その理由がわかりません。
try!
マクロで早期リターン
try!
マクロを使うとエラー時にはResult
fn read_from(fname: &str) -> io::Result<String> { let mut s = String::new(); let mut file = try!(File::open(fname)); try!(file.read_to_string(& mut s)); return Ok(s); } fn main() { match read_from("some.txt") { Ok(s) => {println!("{}", s); Err(why) => {println!("{}", whly.to_string())} }; }
その他の関数
Result<T, E>には、他にも以下の関数があります。
and(res: Result<U,E>) -> Result<U, E> - Ok 時には、resを返す。Err時には selfを返す。
and_then(処理) - Ok時には、Ok値を引数に処理を実行し、Err時には、Err(why) を返す。
or(res: Result<U, E>) - Ok時には、selfを、Err時には、resを返す。
or_else(処理) - Err 時にはErr値を引数にとる処理を実行し、Ok 時にはResultのまま返す。
unwrap_or(opt:T) - Err 時には opt を返す。
unwrap_or_else(処理) - 返り値が、Resultではなくて、Tになる。
unwrap_err - Errの値を返す。Okの場合は、panicする。
使用例
use std::fs::File; use std::io; use std::io::Read; fn read_from(fname: & str) -> io::Result<String> { return File::open(fname).and_then(|mut file| { let mut s = String::new(); //try!(file.read_to_string(& mut s)); //return Ok(s); return file.read_to_string(& mut s).and(Ok(s)); }); } fn main() { match read_from("unwrap.rs") { Ok(s) => {println!("{}", s)} Err(why) => {println!("{}", why.to_string())} }; }
add_libraryのメモ
メモ代わりの参考訳
add_library(<name> [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] source1 [source2 ...])
<name>
はターゲットの論理名で、プロジェクト内でユニークでなければならない。
STATIC
は静的ライブラリである。SHARED
は実行時に動的にリンクされる。MOULDE
は動的にdlopenのような関数を用いてロードされる。プラグインなどで使用される。
ライブラリのタイプを指定しない場合は、 BUILD_SHARED_LIBS
が ON
ならば、 SHARED
に、 OFFなら STATIC
になる。
SHARED
と MODULE
は POSITION_INDEPENDENT_CODE
が自動的に True
に設定される。
なお、 STATIC
の同設定は CMAKE_POSITION_INDEPENDENT_CODE
の設定値で自動的に初期化される。
ビルド結果は、ソースツリーに関連するビルドツリーに出力される。
`LIBRARY_OUTPUT_DIRECTORY
と RUNTIME_OUTPUT_DIRECTORY
の2つのプロパティで出力先を制御できる。
OUTPUT_NAME
で出力されるファイル名の <name>
の部分を変更できる。
EXCLUDE_FROM_ALL
を引数として与えると、ALLターゲットから除外できる。
オブジェクトライブラリ
add_library(<name> OBJECT <source1> [<source2> ...])
以下のように他のターゲットとリンクする。以下では other ターゲットに OBJECT
指定してビルドした objlib
をリンクしている。
add_library(other $<TARGET_OBJECTS:objlib>)
ライブラリの別名
add_library(<name> ALIAS <target>)
[tcpdump] libpcap でキャプチャデータを解析する際のメモ
libpcap で、保存されたキャプチャファイルを解析する方法を調べたのでメモ。
おおまかなソース
#include <pcap.h> const u_char *pkt; char pcap_errbuf[PCAP_ERRBUF_SIZE]; const char *pcap_path = "..."; pcap_t *pcap = pcap_open_offline_with_tstamp_precision(pcap_path, PCAP_TSTAMP_PRECISION_NANO, errbuf); if (pcap == NULL) { fprintf(stderr, "pcap_open: error %s\n", errbuf); exit(EXIT_FAILURE); } struct pcap_pkthdr pkthdr; while ((pkt = pcap_next(pcap, &pkthdr))) { } pcap_close(pcap);
おおまかな流れ
pcap_open
または、その亜種でパケットファイルをオープンする。 パケットファイルのハンドルとして、 pcap_t が割り当てられる。パケットを処理するには、以下の選択肢がある。
pcap_next()
でパケットを取り出し、処理する。pcap_offline_read()
でコールバックを指定して、libpcap内部でループさせる。user引数で任意のユーザデータを渡せる。
pcap_next()
は内部的にpcap_dispatch()
を1度づつ呼び出しているので、pcap_offline_read()
のほうが 効率がよさそう。ただ、コールバックベースでプログラム書くのは面倒であれば、pcap_next()
でもOK。pcap_close()
でハンドルを閉じる。
pcap_next
を使った場合の説明
pcapファイルのオープン
pcap_t *pcap_open_offline_with_tstamp_precision(...)
で pcapファイルをオープンする。
上のサンプルでは、ナノ秒精度のタイムスタンプを保持したキャプチャファイルを読み込むので、
PCAP_TSTAMP_PRECISION_NANO
を指定した。
エラー時には、NULL
が返り、errbufにエラーメッセージが格納される。
パケットの読み込み
const u_char *pcap_next(pcap_t *, struct pcap_pkthdr *)
でパケットを読み取る。
pcap_pkthdr にはパケットのキャプチャ時刻とパケット長が設定され、関数の戻り値にパケットのバイト列へのポインタが格納される。
struct pcap_pkthdr { struct timeval ts; bpf_u_int32 caplen; bpf_u_int32 len;
先頭部分は、Ethernetのフレームなので自力でパースして、IPのヘッダの先頭のポインタを計算する。 具体的な方法はTBW
(VLANなどの対応が面倒だったけど割愛。この辺面倒みてくれるライブラリはきっとあるはず・・・)
あとは、netinet/ip.h
や netinet/tcp.h
、netinet/udp.h
の定義を元に、パースする。
pcapファイルのクローズ
pcap_close(pcap_t*)
で pcap ファイルを閉じる。
フィルタの利用
tcpdumpのフィルタ式を利用できる。
フィルタ式のコンパイル
struct bpf_program
の変数を宣言する。pcap_compile(pcap_t *, struct bpf_program*, int, int)
を呼び出す。(pcapはpcap_open済みのもの) 第2引数は最適化するかどうか、第3引数はネットマスク(キャプチャファイルの場合は 0 でも問題ないようだが・・・・)
方法1:フィルタ式の設定
pcap_setfiler(pcap_t *, struct bpf_program*)
でフィルタを設定する。
後続の pcap_next
の呼び出しは、フィルタにマッチするパケットだけを返す。
方法2:フィルタ式の適用
pcap_setfilter
を呼び出すかわりに、pcap_next が返したパケットがフィルタ式を満たすかどうかを、
ユーザーコードで確認する。
pcap_offline_filter(struct bpf_program*, struct pcap_pkthdr*, const u_char* pkt)
を呼び出す。pktにはpcap_nextの戻り値を指定する。フィルタに適合する場合は、非ゼロの値を返す。
tcpdump での vlan利用時の注意点(IEEE802.1Q)
フィルタ式にport n
みたいな指定をしても、VLANタギングされているパケットがマッチしないので、
vlan and (port n)
のように指定すればいい。
(tcpdump -d
でパケットマッチングコードを見る限り、vlan 指定しない場合は、12-13バイト目がIPv6でもIPv4かどうかだけがチェックされるのでマッチしない)
もし、VLANタグがついているパケットと、ついていないパケットの両方にフィルタ式 expr
を適用したい場合は、
"expr or (vlan and expr)"
のように指定する。