diff options
Diffstat (limited to 'common/buffer')
| -rw-r--r-- | common/buffer/buffer.go | 34 | ||||
| -rw-r--r-- | common/buffer/get.go | 189 | ||||
| -rw-r--r-- | common/buffer/get_test.go | 107 | ||||
| -rw-r--r-- | common/buffer/option.go | 9 | ||||
| -rw-r--r-- | common/buffer/put.go | 87 |
5 files changed, 426 insertions, 0 deletions
diff --git a/common/buffer/buffer.go b/common/buffer/buffer.go new file mode 100644 index 0000000..afc5f7f --- /dev/null +++ b/common/buffer/buffer.go @@ -0,0 +1,34 @@ +package buffer + +import ( + "errors" +) + +var ErrBadRead = errors.New("bad read") + +type Buffer struct { + buf []byte + off int +} + +func New(opts ...Option) *Buffer { + b := &Buffer{} + + for _, opt := range opts { + opt(b) + } + + return b +} + +func (b *Buffer) Len() int { + return len(b.buf) +} + +func (b *Buffer) Off() int { + return b.off +} + +func (b *Buffer) Bytes() []byte { + return b.buf +} diff --git a/common/buffer/get.go b/common/buffer/get.go new file mode 100644 index 0000000..4f27b70 --- /dev/null +++ b/common/buffer/get.go @@ -0,0 +1,189 @@ +package buffer + +import ( + "encoding/binary" + "math" + "strings" +) + +func (b *Buffer) ReadByte() (byte, error) { + if b.off+1 > len(b.buf) { + return 0, ErrBadRead + } + + r := b.buf[b.off] + b.off += 1 + return r, nil +} + +func (b *Buffer) GetBytes(n int) ([]byte, error) { + if b.off+n > len(b.buf) { + return nil, ErrBadRead + } + + r := b.buf[b.off : b.off+n] + b.off += n + return r, nil +} + +func (b *Buffer) GetUint8() (uint8, error) { + if b.off+1 > len(b.buf) { + return 0, ErrBadRead + } + + r := b.buf[b.off] + b.off++ + return r, nil +} + +func (b *Buffer) GetInt8() (int8, error) { + r, err := b.GetUint8() + if err != nil { + return 0, err + } + + return int8(r), nil +} + +func (b *Buffer) GetInt16() (int16, error) { + r, err := b.GetUint16() + if err != nil { + return 0, err + } + + return int16(r), nil +} + +func (b *Buffer) GetUint16() (uint16, error) { + if b.off+2 > len(b.buf) { + return 0, ErrBadRead + } + + r := binary.LittleEndian.Uint16(b.buf[b.off : b.off+2]) + b.off += 2 + return r, nil +} + +func (b *Buffer) GetInt32() (int32, error) { + r, err := b.GetUint32() + if err != nil { + return 0, err + } + + return int32(r), nil +} + +func (b *Buffer) GetUint32() (uint32, error) { + if b.off+4 > len(b.buf) { + return 0, ErrBadRead + } + + r := binary.LittleEndian.Uint32(b.buf[b.off : b.off+4]) + b.off += 4 + return r, nil +} + +func (b *Buffer) GetFloat32() (float32, error) { + r, err := b.GetUint32() + if err != nil { + return 0, err + } + + return math.Float32frombits(r), nil +} + +func (b *Buffer) GetAngle8() (float32, error) { + r, err := b.ReadByte() + if err != nil { + return 0, err + } + + return float32(int8(r)) * (360.0 / 256), nil +} + +func (b *Buffer) GetAngle16() (float32, error) { + r, err := b.GetUint16() + if err != nil { + return 0, err + } + + return float32(r) * (360.0 / 65536), nil +} + +func (b *Buffer) GetCoord16() (float32, error) { + r, err := b.GetInt16() + if err != nil { + return 0, err + } + + return float32(r) / 8.0, nil +} + +func (b *Buffer) GetCoord32() (float32, error) { + return b.GetFloat32() +} + +func (b *Buffer) GetString() (string, error) { + var str strings.Builder + + for b.off < b.Len() { + r, err := b.ReadByte() + if err != nil { + return "", err + } + + if r == 0xff { + continue + } + + if r == 0x00 { + break + } + + str.WriteByte(r) + } + + return str.String(), nil +} + +func (b *Buffer) PeekInt32() (int32, error) { + if b.off+4 > len(b.buf) { + return 0, ErrBadRead + } + + return int32(binary.LittleEndian.Uint32(b.buf[b.off : b.off+4])), nil +} + +func (b *Buffer) PeekBytes(n int) ([]byte, error) { + if b.off+n > len(b.buf) { + return nil, ErrBadRead + } + + return b.buf[b.off : b.off+n], nil +} + +func (b *Buffer) PeekBytesAt(off, size int) ([]byte, error) { + if off+size > len(b.buf) { + return nil, ErrBadRead + } + + return b.buf[off : off+size], nil +} + +func (b *Buffer) Skip(n int) error { + if b.off+n > len(b.buf) { + return ErrBadRead + } + + b.off += n + return nil +} + +func (b *Buffer) Seek(n int) error { + if n > len(b.buf) { + return ErrBadRead + } + + b.off = n + return nil +} diff --git a/common/buffer/get_test.go b/common/buffer/get_test.go new file mode 100644 index 0000000..a4cd8a2 --- /dev/null +++ b/common/buffer/get_test.go @@ -0,0 +1,107 @@ +package buffer + +import ( + "math" + "reflect" + "testing" +) + +type test[T any] struct { + name string + input []byte + expected T +} + +var byteTests = []test[byte]{ + {"byte test 0", []byte{0x00}, byte(0)}, + {"byte test 255", []byte{0xff}, byte(255)}, +} + +var int16Tests = []test[int16]{ + {"int16 test 0", []byte{0x00, 0x00}, int16(0)}, + {"int16 test -32768", []byte{0x00, 0x80}, math.MinInt16}, + {"int16 test 32767", []byte{0xff, 0x7f}, math.MaxInt16}, +} + +var uint16Tests = []test[uint16]{ + {"uint16 test 0", []byte{0x00, 0x00}, uint16(0)}, + {"uint16 test 65535", []byte{0xff, 0xff}, math.MaxUint16}, +} + +var int32Tests = []test[int32]{ + {"int32 test 0", []byte{0x00, 0x00, 0x00, 0x00}, int32(0)}, + {"int32 test -2147483648", []byte{0x00, 0x00, 0x00, 0x80}, math.MinInt32}, + {"int32 test 2147483647", []byte{0xff, 0xff, 0xff, 0x7f}, math.MaxInt32}, +} + +var uint32Tests = []test[uint32]{ + {"uint32 test 0", []byte{0x00, 0x00, 0x00, 0x00}, uint32(0)}, + {"uint32 test 4294967295", []byte{0xff, 0xff, 0xff, 0xff}, uint32(math.MaxUint32)}, +} + +var float32Tests = []test[float32]{ + {"float32 test 0", []byte{0x00, 0x00, 0x00, 0x00}, float32(0)}, + {"float32 test 1e-45", []byte{0x01, 0x00, 0x00, 0x00}, math.SmallestNonzeroFloat32}, + {"float32 test 3.4028235e+38", []byte{0xff, 0xff, 0x7f, 0x7f}, math.MaxFloat32}, +} + +var angle8Tests = []test[float32]{ + {"angle8 test 0", []byte{0x00}, float32(0)}, + {"angle8 test 90", []byte{0x40}, float32(90)}, + {"angle8 test -90", []byte{0xc0}, float32(-90)}, +} + +var angle16Tests = []test[float32]{ + {"angle16 test 0", []byte{0x00, 0x00}, float32(0)}, + {"angle16 test 90", []byte{0x00, 0x40}, float32(90)}, + {"angle16 test 180", []byte{0x00, 0x80}, float32(180)}, +} + +var coord16Tests = []test[float32]{ + {"coord16 test 0", []byte{0x00, 0x00}, float32(0)}, + {"coord16 test 4095", []byte{0xf8, 0x7f}, float32(4095)}, + {"coord16 test -4096", []byte{0x00, 0x80}, float32(-4096)}, +} + +var coord32Tests = []test[float32]{ + {"coord32 test 0", []byte{0x00, 0x00, 0x00, 0x00}, float32(0)}, + {"coord32 test 90", []byte{0x00, 0x00, 0xb4, 0x42}, float32(90)}, + {"coord32 test -90", []byte{0x00, 0x00, 0xb4, 0xc2}, float32(-90)}, +} + +var stringTests = []test[string]{ + {"string test foo bar baz", []byte{0x66, 0x6f, 0x6f, 0x20, 0x62, 0x61, 0x72, 0x00}, "foo bar"}, + {"string test foo bar baz", []byte{0x66, 0x6f, 0x6f, 0x0a, 0x00}, "foo\n"}, +} + +func runTest[T any](t *testing.T, tests []test[T], f func(*Buffer) (T, error)) { + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + val, err := f(New(WithData(tt.input))) + + if err != nil { + t.Errorf("%s: should not return an error", tt.name) + } + + if !reflect.DeepEqual(val, tt.expected) { + t.Errorf("%s: invalid value returned", tt.name) + t.Logf("actual: %#v", val) + t.Logf("expected: %#v", tt.expected) + } + }) + } +} + +func TestBuffer(t *testing.T) { + runTest(t, byteTests, func(buf *Buffer) (byte, error) { return buf.ReadByte() }) + runTest(t, int16Tests, func(buf *Buffer) (int16, error) { return buf.GetInt16() }) + runTest(t, uint16Tests, func(buf *Buffer) (uint16, error) { return buf.GetUint16() }) + runTest(t, int32Tests, func(buf *Buffer) (int32, error) { return buf.GetInt32() }) + runTest(t, uint32Tests, func(buf *Buffer) (uint32, error) { return buf.GetUint32() }) + runTest(t, float32Tests, func(buf *Buffer) (float32, error) { return buf.GetFloat32() }) + runTest(t, angle8Tests, func(buf *Buffer) (float32, error) { return buf.GetAngle8() }) + runTest(t, angle16Tests, func(buf *Buffer) (float32, error) { return buf.GetAngle16() }) + runTest(t, coord16Tests, func(buf *Buffer) (float32, error) { return buf.GetCoord16() }) + runTest(t, coord32Tests, func(buf *Buffer) (float32, error) { return buf.GetCoord32() }) + runTest(t, stringTests, func(buf *Buffer) (string, error) { return buf.GetString() }) +} diff --git a/common/buffer/option.go b/common/buffer/option.go new file mode 100644 index 0000000..2e802e6 --- /dev/null +++ b/common/buffer/option.go @@ -0,0 +1,9 @@ +package buffer + +type Option func(*Buffer) + +func WithData(data []byte) Option { + return func(b *Buffer) { + b.buf = data + } +} diff --git a/common/buffer/put.go b/common/buffer/put.go new file mode 100644 index 0000000..8a5064e --- /dev/null +++ b/common/buffer/put.go @@ -0,0 +1,87 @@ +package buffer + +import ( + "encoding/binary" + "math" +) + +func (b *Buffer) PutByte(v byte) { + b.off += 1 + b.buf = append(b.buf, v) +} + +func (b *Buffer) PutBytes(v []byte) { + b.off += len(v) + b.buf = append(b.buf, v...) +} + +func (b *Buffer) PutInt8(v int8) { + b.PutUint8(uint8(v)) +} + +func (b *Buffer) PutUint8(v uint8) { + b.off++ + b.buf = append(b.buf, v) +} + +func (b *Buffer) PutInt16(v int16) { + b.PutUint16(uint16(v)) +} + +func (b *Buffer) PutUint16(v uint16) { + b.off += 2 + + tmp := make([]byte, 2) + binary.LittleEndian.PutUint16(tmp, v) + b.buf = append(b.buf, tmp...) +} + +func (b *Buffer) PutInt32(v int32) { + b.PutUint32(uint32(v)) +} + +func (b *Buffer) PutUint32(v uint32) { + b.off += 4 + + tmp := make([]byte, 4) + binary.LittleEndian.PutUint32(tmp, v) + b.buf = append(b.buf, tmp...) +} + +func (b *Buffer) PutFloat32(v float32) { + b.off += 4 + + tmp := make([]byte, 4) + binary.LittleEndian.PutUint32(tmp, math.Float32bits(v)) + b.buf = append(b.buf, tmp...) +} + +func (b *Buffer) PutString(v string) { + b.off += len(v) + 1 + + for i := 0; i < len(v); i++ { + b.PutByte(byte(v[i])) + } + + b.PutByte(0) +} + +func (b *Buffer) PutCoord16(v float32) { + b.PutUint16(uint16(v * 8.0)) +} + +func (b *Buffer) PutCoord32(v float32) { + b.PutFloat32(v) +} + +func (b *Buffer) PutAngle8(v float32) { + b.PutByte(byte(v / (360.0 / 256))) +} + +func (b *Buffer) PutAngle16(v float32) { + b.PutUint16(uint16(v / (360.0 / 65536))) +} + +func (b *Buffer) PutAngle32(v float32) { + b.PutFloat32(v) +} |
