diff --git a/dif/decoder.go b/dif/decoder.go index fb28f8e302075f136cff230d9abdf16f9930a2cf..7fc2998fb1b1bdab74c08818735ebb1765642113 100644 --- a/dif/decoder.go +++ b/dif/decoder.go @@ -95,7 +95,7 @@ func (dec *Decoder) Decode(dif *DIF) error { dif.Header.DTC = binary.BigEndian.Uint32(hdr[1 : 1+4]) dif.Header.ATC = binary.BigEndian.Uint32(hdr[5 : 5+4]) dif.Header.GTC = binary.BigEndian.Uint32(hdr[9 : 9+4]) - copy(dif.Header.AbsBCID[:], hdr[13:15+4]) + copy(dif.Header.AbsBCID[:], hdr[13:13+6]) copy(dif.Header.TimeDIFTC[:], hdr[19:19+3]) dif.Frames = dif.Frames[:0] @@ -203,8 +203,9 @@ func (dec *Decoder) readU8() uint8 { } func (dec *Decoder) readU16() uint16 { - dec.load(2) - return binary.BigEndian.Uint16(dec.buf[:2]) + const n = 2 + dec.load(n) + return binary.BigEndian.Uint16(dec.buf[:n]) } func (dec *Decoder) load(n int) { diff --git a/dif/encoder.go b/dif/encoder.go new file mode 100644 index 0000000000000000000000000000000000000000..322dce9821d585d944f778fe8f1661118b1405fe --- /dev/null +++ b/dif/encoder.go @@ -0,0 +1,105 @@ +// Copyright 2020 The go-lpc Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package dif + +import ( + "encoding/binary" + "io" + + "github.com/go-lpc/mim/internal/crc16" + "golang.org/x/xerrors" +) + +type Encoder struct { + w io.Writer + buf []byte + err error + crc crc16.Hash16 +} + +func NewEncoder(w io.Writer) *Encoder { + return &Encoder{ + w: w, + buf: make([]byte, 8), + crc: crc16.New(nil), + } +} + +func (enc *Encoder) crcw(p []byte) { + _, _ = enc.crc.Write(p) // can not fail. +} + +func (enc *Encoder) reset() { + enc.crc.Reset() +} + +func (enc *Encoder) Encode(dif *DIF) error { + if dif == nil { + return nil + } + + enc.reset() + + enc.writeU8(gbHeader) + if enc.err != nil { + return xerrors.Errorf("dif: could not write global header marker: %w", enc.err) + } + + enc.writeU8(dif.Header.ID) + enc.writeU32(dif.Header.DTC) + enc.writeU32(dif.Header.ATC) + enc.writeU32(dif.Header.GTC) + enc.write(dif.Header.AbsBCID[:]) + enc.write(dif.Header.TimeDIFTC[:]) + enc.writeU8(0) // nlines + + enc.writeU8(frHeader) + for _, frame := range dif.Frames { + enc.writeU8(frame.Header) + enc.write(frame.BCID[:]) + enc.write(frame.Data[:]) + } + enc.writeU8(frTrailer) + crc := enc.crc.Sum16() // gb-trailer not part of CRC-16 + enc.writeU8(gbTrailer) + enc.writeU16(crc) + + return enc.err +} + +func (enc *Encoder) write(p []byte) { + if enc.err != nil { + return + } + _, enc.err = enc.w.Write(p) + enc.crcw(p) +} + +func (enc *Encoder) writeU8(v uint8) { + const n = 1 + enc.reserve(n) + enc.buf[0] = v + enc.write(enc.buf[:n]) +} + +func (enc *Encoder) writeU16(v uint16) { + const n = 2 + enc.reserve(n) + binary.BigEndian.PutUint16(enc.buf[:n], v) + enc.write(enc.buf[:n]) +} + +func (enc *Encoder) writeU32(v uint32) { + const n = 4 + enc.reserve(n) + binary.BigEndian.PutUint32(enc.buf[:n], v) + enc.write(enc.buf[:n]) +} + +func (enc *Encoder) reserve(n int) { + if cap(enc.buf) < n { + enc.buf = append(enc.buf[:len(enc.buf)], make([]byte, n-cap(enc.buf))...) + } +} diff --git a/dif/decoder_test.go b/dif/rw_test.go similarity index 82% rename from dif/decoder_test.go rename to dif/rw_test.go index ba568944b145aa6af6c5563ec168ad70619c29b8..257361ed0540c338c381b2fa0c90d109c72a2f39 100644 --- a/dif/decoder_test.go +++ b/dif/rw_test.go @@ -7,11 +7,117 @@ package dif import ( "bytes" "io" + "reflect" "testing" "golang.org/x/xerrors" ) +func TestCodec(t *testing.T) { + const ( + difID = 0x42 + ) + + for _, tc := range []struct { + name string + dif DIF + }{ + { + name: "normal", + dif: DIF{ + Header: GlobalHeader{ + ID: difID, + DTC: 10, + ATC: 11, + GTC: 12, + AbsBCID: [6]uint8{1, 2, 3, 4, 5, 6}, + TimeDIFTC: [3]uint8{10, 11, 12}, + }, + Frames: []Frame{ + { + Header: 1, + BCID: [3]uint8{10, 11, 12}, + Data: [16]uint8{0xa, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + }, + { + Header: 2, + BCID: [3]uint8{20, 21, 22}, + Data: [16]uint8{ + 0xb, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 210, 211, 212, 213, 214, 215, + }, + }, + }, + }, + }, + { + name: "no-frame", + dif: DIF{ + Header: GlobalHeader{ + ID: difID, + DTC: 10, + ATC: 11, + GTC: 12, + AbsBCID: [6]uint8{1, 2, 3, 4, 5, 6}, + TimeDIFTC: [3]uint8{10, 11, 12}, + }, + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + buf := new(bytes.Buffer) + enc := NewEncoder(buf) + err := enc.Encode(&tc.dif) + if err != nil { + t.Fatalf("could not encode dif frames: %+v", err) + } + + dec := NewDecoder(difID, buf) + var got DIF + err = dec.Decode(&got) + if err != nil { + t.Fatalf("could not decode dif frames: %+v", err) + } + + if got, want := got, tc.dif; !reflect.DeepEqual(got, want) { + t.Fatalf("invalid r/w round-trip:\ngot= %#v\nwant=%#v", got, want) + } + }) + } +} + +func TestEncoder(t *testing.T) { + { + buf := new(bytes.Buffer) + enc := NewEncoder(buf) + + if got, want := enc.Encode(nil), error(nil); got != want { + t.Fatalf("invalid nil-dif encoding: got=%v, want=%v", got, want) + } + } + { + buf := failingWriter{n: 0} + enc := NewEncoder(&buf) + if got, want := enc.Encode(&DIF{}), xerrors.Errorf("dif: could not write global header marker: %w", io.ErrUnexpectedEOF); got.Error() != want.Error() { + t.Fatalf("invalid error:\ngot= %+v\nwant=%+v", got, want) + } + } + +} + +type failingWriter struct { + n int + cur int +} + +func (w *failingWriter) Write(p []byte) (int, error) { + w.cur += len(p) + if w.cur >= w.n { + return 0, io.ErrUnexpectedEOF + } + return len(p), nil +} + func TestDecoder(t *testing.T) { const ( difID = 0x42