本格的に復号していく前に、デコーダのインタフェースと補助関数を作る。既にちょっとコードに書いているけど。
外部インタフェース
外部インタフェースは一旦こういう感じで。出力はまだどういう形式にするか考えていないので一旦無し。
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
}
これで今のところ必要な素材は揃ったはず。