package core import ( "encoding/binary" "fmt" "io" "strings" "time" ) type Marshaler interface { Marshal() []any } type DataType uint8 const ( TypeUnknown DataType = 0x00 TypeHandshake = 0x01 TypePing = 0x02 TypePong = 0x03 TypeTest = 0xFF ) func ReadDataType(r io.Reader) (DataType, error) { var data uint8 if err := read(r, &data); err != nil { return TypeUnknown, err } dType := DataType(data) return dType, nil } type Handshake struct { Version uint8 } func (t Handshake) Marshal() []any { return []any{t.Version} } func ReadHandshake(r io.Reader) (Handshake, error) { var t Handshake err := read(r, &t.Version) return t, err } type Test struct { Message string } func (t Test) Marshal() []any { return []any{append([]byte(t.Message), 0x0)} } func ReadTest(r io.Reader) (Test, error) { var t Test err := readString(r, &t.Message) return t, err } type Ping struct{} func (t Ping) Marshal() []any { return []any{} } type Pong struct { Timestamp time.Time } func (t Pong) Marshal() []any { return []any{uint64(t.Timestamp.Unix())} } func ReadPong(r io.Reader) (Pong, error) { var ( t Pong timestamp uint64 ) err := read(r, ×tamp) t.Timestamp = time.Unix(int64(timestamp), 0) return t, err } // func readString(r io.Reader, ptr *string) error { var ( sb strings.Builder buf byte ) for { if err := read(r, &buf); err != nil { return err } if buf == 0x0 { break } if err := sb.WriteByte(buf); err != nil { fmt.Errorf("cannot write byte to string buffer: %v", err) } } *ptr = sb.String() return nil } func read(r io.Reader, ptr any) error { err := binary.Read(r, binary.BigEndian, ptr) if err != nil { return fmt.Errorf("cannot read: %v", err) } return nil }