aboutsummaryrefslogtreecommitdiffstats
path: root/common/buffer
diff options
context:
space:
mode:
authorGravatar BanceDev 2026-02-16 16:31:54 -0500
committerGravatar BanceDev 2026-02-16 16:31:54 -0500
commitca90ebdfa8789654766c5d7969baa7afacd9ebd2 (patch)
tree9693e0c7a5af6713f4c5e39372dcf22d05844ec3 /common/buffer
initial commitHEADmain
Diffstat (limited to 'common/buffer')
-rw-r--r--common/buffer/buffer.go34
-rw-r--r--common/buffer/get.go189
-rw-r--r--common/buffer/get_test.go107
-rw-r--r--common/buffer/option.go9
-rw-r--r--common/buffer/put.go87
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)
+}