X-Git-Url: https://git.r.bdr.sh/rbdr/mobius/blobdiff_plain/67d1f7231b807771b57c8f0e63dd796a03130eaf..a55350daaf83498b7a237c027ad0dd2377f06fee:/hotline/field.go diff --git a/hotline/field.go b/hotline/field.go index c5ae55b..2fcb41a 100644 --- a/hotline/field.go +++ b/hotline/field.go @@ -2,7 +2,8 @@ package hotline import ( "encoding/binary" - "github.com/jhalter/mobius/concat" + "io" + "slices" ) // List of Hotline protocol field types taken from the official 1.9 protocol document @@ -66,38 +67,69 @@ const ( ) type Field struct { - ID []byte // Type of field - FieldSize []byte // Size of the data part - Data []byte // Actual field content + ID [2]byte // Type of field + FieldSize [2]byte // Size of the data part + Data []byte // Actual field content + + readOffset int // Internal offset to track read progress } type requiredField struct { ID int minLen int - maxLen int } func NewField(id uint16, data []byte) Field { - idBytes := make([]byte, 2) - binary.BigEndian.PutUint16(idBytes, id) + f := Field{Data: data} + binary.BigEndian.PutUint16(f.ID[:], id) + binary.BigEndian.PutUint16(f.FieldSize[:], uint16(len(data))) + + return f +} - bs := make([]byte, 2) - binary.BigEndian.PutUint16(bs, uint16(len(data))) +// fieldScanner implements bufio.SplitFunc for parsing byte slices into complete tokens +func fieldScanner(data []byte, _ bool) (advance int, token []byte, err error) { + if len(data) < minFieldLen { + return 0, nil, nil + } - return Field{ - ID: idBytes, - FieldSize: bs, - Data: data, + // tranLen represents the length of bytes that are part of the transaction + neededSize := minFieldLen + int(binary.BigEndian.Uint16(data[2:4])) + if neededSize > len(data) { + return 0, nil, nil } + + return neededSize, data[0:neededSize], nil } -func (f Field) Payload() []byte { - return concat.Slices(f.ID, f.FieldSize, f.Data) +// Read implements io.Reader for Field +func (f *Field) Read(p []byte) (int, error) { + buf := slices.Concat(f.ID[:], f.FieldSize[:], f.Data) + + if f.readOffset >= len(buf) { + return 0, io.EOF // All bytes have been read + } + + n := copy(p, buf[f.readOffset:]) + f.readOffset += n + + return n, nil +} + +// Write implements io.Writer for Field +func (f *Field) Write(p []byte) (int, error) { + f.ID = [2]byte(p[0:2]) + f.FieldSize = [2]byte(p[2:4]) + + i := int(binary.BigEndian.Uint16(f.FieldSize[:])) + f.Data = p[4 : 4+i] + + return minFieldLen + i, nil } func getField(id int, fields *[]Field) *Field { for _, field := range *fields { - if id == int(binary.BigEndian.Uint16(field.ID)) { + if id == int(binary.BigEndian.Uint16(field.ID[:])) { return &field } }