package core import ( "bytes" "encoding/binary" "fmt" "io" "time" ) type Wrapper interface { Wrap() (PayloadType, []any) } type PayloadType uint8 const ( PayloadUnknown PayloadType = 0x00 PayloadSuccess = 0x01 PayloadError = 0x02 PayloadHandshake = 0x03 PayloadAuth = 0x04 PayloadMessage = 0x05 PayloadTest = 0xFF ) type ErrorType uint8 const ( ErrorUnknown ErrorType = 0x00 ErrorAuthFailed = 0x01 ) type Frame struct { Length uint16 Type PayloadType Payload []any } type Binary struct { Length uint16 Payload []byte } func Encode(w Wrapper) ([]byte, error) { payloadType, payload := w.Wrap() buf := bytes.NewBuffer(make([]byte, 3)) for _, p := range payload { switch v := p.(type) { case string: strBytes := []byte(v) err := binary.Write(buf, binary.BigEndian, uint16(len(strBytes))) if err != nil { return []byte{}, fmt.Errorf("cannot encode string length: %v", err) } _, err = buf.Write(strBytes) if err != nil { return []byte{}, fmt.Errorf("cannot encode string: %v", err) } case []byte: err := binary.Write(buf, binary.BigEndian, uint16(len(v))) if err != nil { return []byte{}, fmt.Errorf("cannot encode byte slice length: %v", err) } _, err = buf.Write(v) if err != nil { return []byte{}, fmt.Errorf("cannot encode byte slice: %v", err) } case time.Time: err := binary.Write(buf, binary.BigEndian, uint64(v.Unix())) if err != nil { return []byte{}, fmt.Errorf("cannot encode time: %v", err) } default: err := binary.Write(buf, binary.BigEndian, v) if err != nil { return []byte{}, fmt.Errorf("cannot encode: %v", err) } } } frame := buf.Bytes() frame[0] = uint8(payloadType) binary.BigEndian.PutUint16(frame[1:], uint16(len(frame)-3)) return frame, nil } func Decode(buf io.Reader) (any, error) { var pTypeByte uint8 err := binary.Read(buf, binary.BigEndian, &pTypeByte) if err != nil { return nil, fmt.Errorf("cannot read payload type: %v", err) } pType := PayloadType(pTypeByte) var pLen uint16 err = binary.Read(buf, binary.BigEndian, &pLen) if err != nil { return nil, fmt.Errorf("cannot read payload length: %v", err) } switch pType { case PayloadSuccess: return Success{}, nil case PayloadError: return DecodeError(buf) case PayloadHandshake: return DecodeHandshake(buf) case PayloadAuth: return DecodeAuth(buf) case PayloadMessage: return DecodeMessage(buf) case PayloadTest: return DecodeTest(buf) default: return nil, fmt.Errorf("invalid payload type: %v", pType) } return pType, nil } func decodeNumeric(buf io.Reader, ptr any) error { err := binary.Read(buf, binary.BigEndian, ptr) if err != nil { return fmt.Errorf("cannot decode: %v", err) } return nil } func decodeTime(buf io.Reader, ptr *time.Time) error { var timeBuf uint64 err := binary.Read(buf, binary.BigEndian, &timeBuf) if err != nil { return fmt.Errorf("cannot decode time: %v", err) } *ptr = time.Unix(int64(timeBuf), 0) return nil } func decodeBin(buf io.Reader, ptr *[]byte) error { var payloadLen uint16 err := binary.Read(buf, binary.BigEndian, &payloadLen) if err != nil { return fmt.Errorf("cannot decode payload length: %v", err) } payload := make([]byte, payloadLen) _, err = buf.Read(payload) if err != nil { return fmt.Errorf("cannot decode payload: %v", err) } *ptr = payload return nil } func decodeString(buf io.Reader, ptr *string) error { var strBytes []byte err := decodeBin(buf, &strBytes) if err != nil { return err } *ptr = string(strBytes) return nil }