From 582f3fcca7c112f2a5791f7304d199803ac5eb2b Mon Sep 17 00:00:00 2001 From: Ethan Moffat Date: Fri, 4 Aug 2023 14:31:50 -0700 Subject: [PATCH 1/2] Add Slice method for EoReader to return slice of underlying data --- go.mod | 2 +- pkg/eolib/data/reader.go | 42 +++++++++++++- pkg/eolib/data/reader_test.go | 103 +++++++++++++++++++++++++++------- 3 files changed, 126 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index 14d9e10..0bb17b7 100644 --- a/go.mod +++ b/go.mod @@ -5,11 +5,11 @@ go 1.20 require ( github.com/stretchr/testify v1.8.4 golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df + golang.org/x/text v0.11.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/text v0.11.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/pkg/eolib/data/reader.go b/pkg/eolib/data/reader.go index 389b9af..e802d74 100644 --- a/pkg/eolib/data/reader.go +++ b/pkg/eolib/data/reader.go @@ -90,6 +90,46 @@ func (r *EoReader) Seek(offset int64, whence int) (i int64, err error) { return } +// SliceFromCurrent creates a new [data.EoReader] from a slice of the reader's underlying data. +// +// The input data of the new reader will start at this reader's current position and contain all remaining data. The position and chunked reading mode of each reader are independent. +// +// The new reader's position starts at zero with chunked reading mode disabled. +func (r *EoReader) SliceFromCurrent() (*EoReader, error) { + return r.SliceFromIndex(r.pos) +} + +// SliceFromIndex creates a new [data.EoReader] from a slice of the reader's underlying data. +// +// The input data of the new reader will start at the specified index, offset from the start of the underlying data array, and contains all remaining data. The position and chunked reading mode of each reader are independent. +// +// The new reader's position starts at zero with chunked reading mode disabled. +func (r *EoReader) SliceFromIndex(index int) (*EoReader, error) { + return r.Slice(index, eolib.Max(0, r.Length()-index)) +} + +// Slice creates a new [data.EoReader] from a slice of the reader's underlying data. +// +// The input data of the new reader will start at the specified index and contain bytes equal to the specified length. The position and chunked reading mode of each reader are independent. +// +// The new reader's position starts at zero with chunked reading mode disabled. +func (r *EoReader) Slice(index int, length int) (ret *EoReader, err error) { + if index < 0 { + err = errors.New("index must not be less than 0") + return + } + + if length < 0 { + err = errors.New("length must not be less than 0") + return + } + + startIndex := eolib.Max(0, eolib.Min(r.Length(), index)) + endIndex := eolib.Min(len(r.data), eolib.Min(r.Length(), length)+startIndex) + + return NewEoReader(r.data[startIndex:endIndex]), nil +} + // GetByte reads a raw byte from the input data. func (r *EoReader) GetByte() byte { return r.readByte() @@ -193,7 +233,7 @@ func (r *EoReader) SetIsChunked(value bool) { // Otherwise, gets the total number of bytes remaining in the input data. func (r *EoReader) Remaining() int { if r.chunkInfo.isChunked { - return r.chunkInfo.nextBreak - r.pos + return r.chunkInfo.nextBreak - eolib.Min(r.pos, r.chunkInfo.nextBreak) } else { return len(r.data) - r.pos } diff --git a/pkg/eolib/data/reader_test.go b/pkg/eolib/data/reader_test.go index e89470b..173c274 100644 --- a/pkg/eolib/data/reader_test.go +++ b/pkg/eolib/data/reader_test.go @@ -8,11 +8,65 @@ import ( "github.com/stretchr/testify/assert" ) +func TestSlice(t *testing.T) { + reader := createReader([]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}) + reader.GetByte() + reader.SetIsChunked(true) + + reader2, err := reader.SliceFromCurrent() + assert.NoError(t, err) + assert.Equal(t, 0, reader2.Position()) + assert.Equal(t, 5, reader2.Remaining()) + assert.False(t, reader2.IsChunked()) + + reader3, err := reader2.SliceFromIndex(1) + assert.NoError(t, err) + assert.Equal(t, 0, reader3.Position()) + assert.Equal(t, 4, reader3.Remaining()) + assert.False(t, reader3.IsChunked()) + + reader4, err := reader3.Slice(1, 2) + assert.NoError(t, err) + assert.Equal(t, 0, reader4.Position()) + assert.Equal(t, 2, reader4.Remaining()) + assert.False(t, reader4.IsChunked()) + + assert.Equal(t, 1, reader.Position()) + assert.Equal(t, 5, reader.Remaining()) + assert.True(t, reader.IsChunked()) +} + +func TestSliceOverRead(t *testing.T) { + reader := createReader([]byte{0x01, 0x02, 0x03}) + + res, _ := reader.Slice(2, 5) + assert.Equal(t, 1, res.Remaining()) + + res, _ = reader.SliceFromIndex(3) + assert.Equal(t, 0, res.Remaining()) + + res, _ = reader.SliceFromIndex(4) + assert.Equal(t, 0, res.Remaining()) + + res, _ = reader.Slice(4, 12345) + assert.Equal(t, 0, res.Remaining()) +} + +func TestSliceNegativeInputs(t *testing.T) { + reader := data.EoReader{} + + _, err := reader.SliceFromIndex(-1) + assert.Error(t, err) + + _, err = reader.Slice(0, -1) + assert.Error(t, err) +} + func TestReaderGetByte(t *testing.T) { byteValues := []byte{0x00, 0x01, 0x02, 0x80, 0xFD, 0xFE, 0xFF} for _, expected := range byteValues { t.Run(fmt.Sprintf("ReadByte_%d", expected), func(t *testing.T) { - reader := data.NewEoReader([]byte{expected}) + reader := createReader([]byte{expected}) actual := reader.GetByte() assert.Equal(t, expected, actual) }) @@ -20,20 +74,20 @@ func TestReaderGetByte(t *testing.T) { } func TestReaderOverReadByte(t *testing.T) { - reader := data.NewEoReader([]byte{}) + reader := createReader([]byte{}) value := reader.GetByte() assert.Equal(t, byte(0x00), value) } func TestReaderGetBytes(t *testing.T) { - reader := data.NewEoReader([]byte{0x01, 0x02, 0x03, 0x04, 0x05}) + reader := createReader([]byte{0x01, 0x02, 0x03, 0x04, 0x05}) assert.Equal(t, []byte{0x01, 0x02, 0x03}, reader.GetBytes(3)) assert.Equal(t, []byte{0x04, 0x05}, reader.GetBytes(10)) assert.Equal(t, []byte{}, reader.GetBytes(1)) } func TestReaderGetChar(t *testing.T) { - reader := data.NewEoReader([]byte{0x01, 0x02, 0x80, 0x81, 0xFD, 0xFE, 0xFF}) + reader := createReader([]byte{0x01, 0x02, 0x80, 0x81, 0xFD, 0xFE, 0xFF}) assert.Equal(t, 0, reader.GetChar()) assert.Equal(t, 1, reader.GetChar()) assert.Equal(t, 127, reader.GetChar()) @@ -106,14 +160,14 @@ func TestReaderGetInt(t *testing.T) { func TestReaderGetString(t *testing.T) { const expected = "Hello, World!" - reader := data.NewEoReader(toBytes(expected)) + reader := createReader(toBytes(expected)) actual, _ := reader.GetString() assert.Equal(t, expected, actual) } func TestReaderGetFixedString(t *testing.T) { const input = "foobar" - reader := data.NewEoReader(toBytes(input)) + reader := createReader(toBytes(input)) actual1, _ := reader.GetFixedString(3) actual2, _ := reader.GetFixedString(3) assert.Equal(t, input[:3], actual1) @@ -122,7 +176,7 @@ func TestReaderGetFixedString(t *testing.T) { func TestReaderGetPaddedString(t *testing.T) { const input = "fooÿbarÿÿÿ" - reader := data.NewEoReader(toBytes(input)) + reader := createReader(toBytes(input)) actual1, _ := reader.GetPaddedString(4) actual2, _ := reader.GetPaddedString(6) assert.Equal(t, "foo", actual1) @@ -131,7 +185,7 @@ func TestReaderGetPaddedString(t *testing.T) { func TestReaderGetStringChunked(t *testing.T) { const input = "Hello,ÿWorld!" - reader := data.NewEoReader(toBytes(input)) + reader := createReader(toBytes(input)) reader.SetIsChunked(true) actual1, _ := reader.GetString() @@ -156,14 +210,14 @@ func TestReaderGetNegativeLengthPaddedString(t *testing.T) { func TestReaderGetEncodedString(t *testing.T) { const input = "!;a-^H s^3a:)" - reader := data.NewEoReader(toBytes(input)) + reader := createReader(toBytes(input)) actual, _ := reader.GetEncodedString() assert.Equal(t, "Hello, World!", actual) } func TestReaderGetFixedEncodedString(t *testing.T) { const input = "^0g[>k" - reader := data.NewEoReader(toBytes(input)) + reader := createReader(toBytes(input)) actual1, _ := reader.GetFixedEncodedString(3) actual2, _ := reader.GetFixedEncodedString(3) assert.Equal(t, "foo", actual1) @@ -172,7 +226,7 @@ func TestReaderGetFixedEncodedString(t *testing.T) { func TestReaderGetPaddedEncodedString(t *testing.T) { const input = "ÿ0^9ÿÿÿ-l=S>k" - reader := data.NewEoReader(toBytes(input)) + reader := createReader(toBytes(input)) actual1, _ := reader.GetPaddedEncodedString(4) actual2, _ := reader.GetPaddedEncodedString(6) actual3, _ := reader.GetPaddedEncodedString(3) @@ -183,7 +237,7 @@ func TestReaderGetPaddedEncodedString(t *testing.T) { func TestReaderGetEncodedStringChunked(t *testing.T) { const input = "E0a3hWÿ!;a-^H" - reader := data.NewEoReader(toBytes(input)) + reader := createReader(toBytes(input)) reader.SetIsChunked(true) actual1, _ := reader.GetEncodedString() @@ -208,7 +262,7 @@ func TestReaderGetNegativeLengthPaddedEncodedString(t *testing.T) { func TestReaderRemaining(t *testing.T) { bytes := []byte{0x01, 0x03, 0x04, 0xFE, 0x05, 0xFE, 0xFE, 0x06, 0xFE, 0xFE, 0xFE} - reader := data.NewEoReader(bytes) + reader := createReader(bytes) assert.Equal(t, len(bytes), reader.Remaining()) @@ -237,7 +291,7 @@ func TestReaderRemainingChunked(t *testing.T) { 0xFF, // chunk delimiter 0x05, 0xFE, 0xFE, 0x06, 0xFE, 0xFE, 0xFE} - reader := data.NewEoReader(bytes) + reader := createReader(bytes) reader.SetIsChunked(true) assert.Equal(t, 3, reader.Remaining()) @@ -261,7 +315,7 @@ func TestReaderNextChunk(t *testing.T) { 0xFF, // chunk delimiter 0x06} - reader := data.NewEoReader(bytes) + reader := createReader(bytes) reader.SetIsChunked(true) assert.Equal(t, 0, reader.Position()) @@ -293,7 +347,7 @@ func TestReaderNextChunkWithChunkedModeToggledInBetween(t *testing.T) { 0xFF, // chunk delimiter 0x06} - reader := data.NewEoReader(bytes) + reader := createReader(bytes) assert.Equal(t, 0, reader.Position()) @@ -320,7 +374,7 @@ func TestReaderNextChunkWithChunkedModeToggledInBetween(t *testing.T) { func TestReaderUnderRead(t *testing.T) { // See: https://github.com/Cirras/eo-protocol/blob/master/docs/chunks.md#1-under-read - reader := data.NewEoReader([]byte{0x7C, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0xFF, 0xCA, 0x31}) + reader := createReader([]byte{0x7C, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0xFF, 0xCA, 0x31}) reader.SetIsChunked(true) assert.Equal(t, 123, reader.GetChar()) // byte representation: 123 = 0x7C @@ -331,7 +385,7 @@ func TestReaderUnderRead(t *testing.T) { func TestOverRead(t *testing.T) { // See: https://github.com/Cirras/eo-protocol/blob/master/docs/chunks.md#2-over-read - reader := data.NewEoReader([]byte{0xFF, 0x7C}) + reader := createReader([]byte{0xFF, 0x7C}) reader.SetIsChunked(true) assert.Equal(t, 0, reader.GetInt()) @@ -342,7 +396,7 @@ func TestOverRead(t *testing.T) { func TestDoubleRead(t *testing.T) { // See: https://github.com/Cirras/eo-protocol/blob/master/docs/chunks.md#3-double-read - reader := data.NewEoReader([]byte{0xFF, 0x7C, 0xCA, 0x31}) + reader := createReader([]byte{0xFF, 0x7C, 0xCA, 0x31}) // Reading all 4 bytes of the input data assert.Equal(t, 790222478, reader.GetInt()) @@ -355,3 +409,14 @@ func TestDoubleRead(t *testing.T) { assert.Equal(t, 123, reader.GetChar()) assert.Equal(t, 12345, reader.GetShort()) } + +func createReader(inp []byte) *data.EoReader { + tmp := make([]byte, len(inp)+20) + for i, b := range inp { + tmp[i+10] = b + } + + reader := data.NewEoReader(tmp) + retReader, _ := reader.Slice(10, len(inp)) + return retReader +} From f9a2dc7193bccfa8a96fbd1b9df504ec0aa451c6 Mon Sep 17 00:00:00 2001 From: Ethan Moffat Date: Fri, 4 Aug 2023 14:35:23 -0700 Subject: [PATCH 2/2] Update eo-protocol to latest --- eo-protocol | 2 +- .../protocol/net/server/structs_generated.go | 32 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/eo-protocol b/eo-protocol index 5d6367e..7032f45 160000 --- a/eo-protocol +++ b/eo-protocol @@ -1 +1 @@ -Subproject commit 5d6367e6fdf339a9a1d6b1e16843420953fd6286 +Subproject commit 7032f4585fadce9f8633934a66b797259a668c5d diff --git a/pkg/eolib/protocol/net/server/structs_generated.go b/pkg/eolib/protocol/net/server/structs_generated.go index f156452..a2fe61a 100644 --- a/pkg/eolib/protocol/net/server/structs_generated.go +++ b/pkg/eolib/protocol/net/server/structs_generated.go @@ -1172,8 +1172,8 @@ func (s *PubFile) Deserialize(reader data.EoReader) (err error) { // PlayersList :: Information about online players. type PlayersList struct { - OnlineCount int - Players []OnlinePlayer + PlayersCount int + Players []OnlinePlayer } func (s *PlayersList) Serialize(writer data.EoWriter) (err error) { @@ -1181,14 +1181,14 @@ func (s *PlayersList) Serialize(writer data.EoWriter) (err error) { defer func() { writer.SanitizeStrings = oldSanitizeStrings }() writer.SanitizeStrings = true - // OnlineCount : field : short - if err = writer.AddShort(s.OnlineCount); err != nil { + // PlayersCount : length : short + if err = writer.AddShort(s.PlayersCount); err != nil { return } writer.AddByte(0xFF) // Players : array : OnlinePlayer - for ndx := 0; ndx < len(s.Players); ndx++ { + for ndx := 0; ndx < s.PlayersCount; ndx++ { if err = s.Players[ndx].Serialize(writer); err != nil { return } @@ -1204,13 +1204,13 @@ func (s *PlayersList) Deserialize(reader data.EoReader) (err error) { defer func() { reader.SetIsChunked(oldIsChunked) }() reader.SetIsChunked(true) - // OnlineCount : field : short - s.OnlineCount = reader.GetShort() + // PlayersCount : length : short + s.PlayersCount = reader.GetShort() if err = reader.NextChunk(); err != nil { return } // Players : array : OnlinePlayer - for ndx := 0; reader.Remaining() > 0; ndx++ { + for ndx := 0; ndx < s.PlayersCount; ndx++ { s.Players = append(s.Players, OnlinePlayer{}) if err = s.Players[ndx].Deserialize(reader); err != nil { return @@ -1227,8 +1227,8 @@ func (s *PlayersList) Deserialize(reader data.EoReader) (err error) { // PlayersListFriends :: Information about online players. Sent in reply to friends list requests. type PlayersListFriends struct { - OnlineCount int - Players []string + PlayersCount int + Players []string } func (s *PlayersListFriends) Serialize(writer data.EoWriter) (err error) { @@ -1236,14 +1236,14 @@ func (s *PlayersListFriends) Serialize(writer data.EoWriter) (err error) { defer func() { writer.SanitizeStrings = oldSanitizeStrings }() writer.SanitizeStrings = true - // OnlineCount : field : short - if err = writer.AddShort(s.OnlineCount); err != nil { + // PlayersCount : length : short + if err = writer.AddShort(s.PlayersCount); err != nil { return } writer.AddByte(0xFF) // Players : array : string - for ndx := 0; ndx < len(s.Players); ndx++ { + for ndx := 0; ndx < s.PlayersCount; ndx++ { if err = writer.AddString(s.Players[ndx]); err != nil { return } @@ -1260,13 +1260,13 @@ func (s *PlayersListFriends) Deserialize(reader data.EoReader) (err error) { defer func() { reader.SetIsChunked(oldIsChunked) }() reader.SetIsChunked(true) - // OnlineCount : field : short - s.OnlineCount = reader.GetShort() + // PlayersCount : length : short + s.PlayersCount = reader.GetShort() if err = reader.NextChunk(); err != nil { return } // Players : array : string - for ndx := 0; reader.Remaining() > 0; ndx++ { + for ndx := 0; ndx < s.PlayersCount; ndx++ { if s.Players[ndx], err = reader.GetString(); err != nil { return }