本格的に復号していく前に、デコーダのインタフェースと補助関数を作る。既にちょっとコードに書いているけど。
外部インタフェース
外部インタフェースは一旦こういう感じで。出力はまだどういう形式にするか考えていないので一旦無し。
import ( "bufio" "io" ) type Decoder struct { r *bufio.Reader } func NewDecoder(r io.Reader) *Decoder { return &Decoder{ r: bufio.NewReader(r), } } func (d *Decoder) Decode() error { // TODO return nil }
Decoder
には*bufio.Reader
を持たせている。おそらく入力ストリームをバイト単位で読むことが多くなると思われるので、bufio.Reader#ReadByte
を使いたい。
マーカー
JPEGファイルのマーカーも定義しておく。これはもうTable B.1を根性で書き写す。
import "fmt" type Marker byte const ( Marker_SOF0 Marker = 0xC0 Marker_SOF1 = 0xC1 Marker_SOF2 = 0xC2 Marker_SOF3 = 0xC3 Marker_SOF5 = 0xC5 Marker_SOF6 = 0xC6 Marker_SOF7 = 0xC7 Marker_JPG = 0xC8 Marker_SOF9 = 0xC9 Marker_SOF10 = 0xCA Marker_SOF11 = 0xCB Marker_SOF13 = 0xCD Marker_SOF14 = 0xCE Marker_SOF15 = 0xCF Marker_DHT = 0xC4 Marker_DAC = 0xCC Marker_RST_n = 0xD0 Marker_SOI = 0xD8 Marker_EOI = 0xD9 Marker_SOS = 0xDA Marker_DQT = 0xDB Marker_DNL = 0xDC Marker_DRI = 0xDD Marker_DHP = 0xDE Marker_EXP = 0xDF Marker_APP_n = 0xE0 Marker_JPG_n = 0xF0 Marker_COM = 0xFE Marker_TEM = 0x01 Marker_FF = 0x00 Marker_Prefix = 0xFF ) func (m Marker) String() string { return fmt.Sprintf("0xFF%02X", int(m)) }
ここでもおそらく入力ストリームをバイト単位で読むことを想定して、マーカーの値は先頭の0xFF
を取り除いた1バイトだけを記録しておく。最後の2つは厳密にはマーカーではないけど、Prefix部分を表すMarker_Prefix = 0xFF
と、データの中に出現する0xFF
を表現するためのエスケープ用の疑似マーカー0xFF00
のための値Marker_FF = 0x00
も用意しておく。デバッグやエラー出力に便利なのでMarker#String
も一応書いておく。というかこれが書きたくてtype Marker
を定義した。なにかと便利なので。
マーカーと1バイトのデータを読む
次はストリームのデータを読む補助メソッドを作る。マーカーの定義により、1バイトのデータを読んだ時の挙動は以下のようになる。
- 1バイトを読む
- 読んだ値が
0xFF
でない場合は、値をそのままデータとして返却する - 1バイトを読む
- 読んだ値が
0x00
の場合は、0xFF
をデータとして返却する - それ以外の場合は、読んだ値をマーカーとして返却する
実装するとこうなる。
func (d *Decoder) readByteMarker() (byte, Marker, error) { b, err := d.r.ReadByte() if err != nil { return 0, 0, err } if b != Marker_Prefix { return b, 0, nil } m, err := d.r.ReadByte() if err != nil { return 0, 0, err } if m == Marker_FF { return b, 0, nil } return 0, Marker(m), nil }
返却値が0, 0, nil
になる場合が複数あるように見えるが、0xFF00
となるマーカーは存在しないのでこれで成立している。最後のマーカーを返却する時にマーカーとして有効な値になっているかどうかをチェックした方が良いかもしれない。しなくても後でありえない位置にありえないマーカーが来るとエラーになるはずなので、特にここでチェックする必要も無いような予感はする。
マーカーは突然出現するわけではなく、読むべきタイミングというのは予想できる。つまり基本的には「次はマーカーがくるはず」「次はマーカーは来ないはず」と思ってストリームを読む。なので以下の2つのメソッドを用意しておく。
import "errors" var ( ErrUnexpectedByte = errors.New("unexpected byte") ErrUnexpectedMarker = errors.New("unexpected marker") ) func (d *Decoder) readByte() (byte, error) { b, m, err := d.readByteMarker() if err != nil { return 0, err } if m != 0 { return 0, ErrUnexpectedMarker } return b, nil } func (d *Decoder) readMarker() (Marker, error) { b, m, err := d.readByteMarker() if err != nil { return 0, err } if m == 0 { return 0, ErrUnexpectedByte } return m, nil }
複数のバイト値や数値
複数のバイトを一気に読むメソッド。io.Reader#Read
とかで一気に読みたいところだけど、マーカーや0xFF00
が絡んでくる可能性があるので結局1バイトずつ読むことになる。
func (d *Decoder) readBytes(n int) ([]byte, error) { var ret []byte for i := 0; i < n; i++ { b, err := d.readByte() if err != nil { return nil, err } ret = append(ret, b) } return ret, nil }
Annex Fを見たところ、テーブルのほとんどのフィールドは8ビットか16ビットの数値として解釈されるようなので、uint8
, uint16
の単位で読むメソッドも作っておく。さっそくDecoder#readBytes
が役に立った。
func (d *Decoder) readUint8() (uint8, error) { b, err := d.readByte() if err != nil { return 0, err } return uint8(b), nil } func (d *Decoder) readUint16() (uint16, error) { bs, err := d.readBytes(2) if err != nil { return 0, err } return (uint16(bs[0]) << 8) | uint16(bs[1]), nil }
読み戻す
こういうパーサを書く時はトークンを1つ読まなかったことにしたり(unread, unget)、ストリームを消費せずに先頭のトークンを読む(peek)処理があると、最終的に必要かどうかはわからないけどデバッグ中とかでは役に立つ。というか実際に後で役に立ったので作ろう。peekはあとでまた読むのが面倒なので一旦unreadにしておこう。最初に書いたDecoder
とDecoder#readByteMarker
に手を入れる。
まずDecoder
に状態を追加する。
type Decoder struct { r *bufio.Reader // unread用のフィールド prevByte byte prevMarker Marker unreaded bool }
Decoder#readByteMarker
では、値を返す時に返却値をprevByte
,prevMarker
に保存し、unreaded
がtrue
の時は保存した値を返すようにする。
func (d *Decoder) readByteMarker() (byte, Marker, error) { if d.unreaded { d.unreaded = false return d.prevByte, d.prevMarker, nil } b, err := d.r.ReadByte() if err != nil { return 0, 0, err } if b != Marker_Prefix { d.prevByte = b d.prevMarker = 0 return b, 0, nil } m, err := d.r.ReadByte() if err != nil { return 0, 0, err } if m == Marker_FF { d.prevByte = b d.prevMarker = 0 return b, 0, nil } d.prevByte = 0 d.prevMarker = Marker(m) return 0, Marker(m), nil }
unreadの処理本体はこれでいい。
func (d *Decoder) unread() { d.unreaded = true }
これで今のところ必要な素材は揃ったはず。