Go歴1日で突撃した #gocon でGo欲を触発され、とにかく何でもいいからGoで書きたくなり、Go版ipcalcを書いてみた
概要
- https://github.com/goldeneggg/ipcl
- CIDR形式(例:192.168.1.0/24)の文字列から、取り得るホスト数やIPアドレスのレンジ等を算出・出力する
- 現状公開されてる計算ツールだと「CIDRを一覧で記載したファイルを入力して一括parse」が可能なものが見当たらなかったので対応した
Go本体のテストコードに助けられた
- 今回Goを書いてみて最初に浮かぶ感想はこれ。「この処理って標準パッケージ使ってどう書けばいいんだろ?」と迷った時、公式ドキュメントももちろんだが Go本体のテストコードを読むのが良い 。テストの書き方も学べて一石二鳥(Goに限った話じゃ無いかもしれないけど他言語のテストコード読んだこと無いので比較は出来ない。さーせん )
- Goにはアサーションが無いので、テストの成否は「テスト対象関数/メソッドの戻り値 の内の1つ であるerrorオブジェクト 」で判定するスタイル。当然本体のテストコードも然り
- そしてこの本体テストコードの「エラーハンドリング→エラー有無での後続処理の切り替え」あたりの書き方が自実装コードに適用出来る場面が割りとあった
- 今回の用途で言えばnetパッケージの
ip_test.go
のコードにだいぶ助けられた
- 今回の用途で言えばnetパッケージの
- Goのソースコード(テストコードも含む)は、 Mercurialでローカルに落とす、もしくはここでオンラインで読める 。
-
hoge.go
のテストはhoge_test.go
という名前にするのがGo流
-
備忘
net パッケージ
- 今回一番お世話になったGo標準パッケージで、TCP/IP, UDP, ソケット、ネットワークI/Oに関連する機能は一通り網羅されている
IP型
-
IP
型は、ひとつのIPアドレスを表す型で、実体は[]byte
型、16byteのバイト配列 - CIDRのパースは
func ParseCIDR(s string) (IP, *IPNet, error)
を使えば一発でやってくれる- 戻り値の
IP
がネットワークアドレスになっている
- 戻り値の
-
func IPv4(a, b, c, d byte) IP
は、IPアドレスの各オクテットの値をbyteで渡してIP型で返す関数。- 戻り値のIPは4byteではなく16byteで返ってくるが、これは公式ドキュメントを読む限りIPv6もフォローする為に汎用性を持たせたものと思われる
- 例えば
172.168.1.0
のIP型表現は内部的に[]byte
型でこうなってる →[0][0][0][0][0][0][0][0][0][0][0][0][172][168][1][0]
- 4byteで欲しい場合は、
func (ip IP) To4() IP
メソッドで変換する
IPNet型
- 「CIDRで表現されるIPネットワークそのもの」を表すオブジェクト
- 実体は「ネットワークアドレスを表現する
IP
型」「ネットマスクを表す`IPMask型」の2つのフィールドを持つstructになっている
- 実体は「ネットワークアドレスを表現する
-
func (n *IPNet) Contains(ip IP) bool
で、指定したIP(IPアドレス)がそのネットワーク範囲内のものか?を判定する事が出来る
IPMask型
- ネットマスクを表す型で、同じく実体は
[]byte
型 - プレフィックス長からIPMask型のオブジェクトを取得したい場合は
func CIDRMask(ones, bits int) IPMask
を使う。bits
にはIPv4なら32を指定
for文
- 初期値や事後処理で複数パラメータを取るfor文は
for i, f, t := 0, 0, 8; i < 4; i, f, t = i+1, f+8, t+8 { ... }
って具合に、変数宣言時同様に代入演算子は1つに集約して書く
数値を2進, 16進で表記したい
-
fmt.Sprintf()
を使う -
%b
2進, 前ゼロ埋め8桁で表記したければ%08b
するfmt.Sprintf("%08b", 3) // = "00000011"
-
%x
16進, 前ゼロ埋め2桁で表記したければ%02x
とするfmt.Sprintf("%02x", 15) // = "0f"