Files
drip/internal/shared/protocol/payload.go

130 lines
3.3 KiB
Go

package protocol
import (
"encoding/json"
"errors"
)
// EncodeDataPayload encodes a data header and payload into a frame payload
// Uses binary encoding (optimized format)
func EncodeDataPayload(header DataHeader, data []byte) ([]byte, error) {
return EncodeDataPayloadV2(header, data)
}
// EncodeDataPayloadV1 encodes using JSON (legacy)
// Format: JSON_HEADER\nDATA
func EncodeDataPayloadV1(header DataHeader, data []byte) ([]byte, error) {
headerBytes, err := json.Marshal(header)
if err != nil {
return nil, err
}
// Combine: header + newline + data
payload := make([]byte, 0, len(headerBytes)+1+len(data))
payload = append(payload, headerBytes...)
payload = append(payload, '\n')
payload = append(payload, data...)
return payload, nil
}
// EncodeDataPayloadV2 encodes using binary format (optimized)
// Format: BINARY_HEADER + DATA
func EncodeDataPayloadV2(header DataHeader, data []byte) ([]byte, error) {
// Convert to binary header
var h2 DataHeaderV2
h2.FromDataHeader(header)
// Encode header to binary
headerBytes := h2.MarshalBinary()
// Combine: binary header + data
payload := make([]byte, 0, len(headerBytes)+len(data))
payload = append(payload, headerBytes...)
payload = append(payload, data...)
return payload, nil
}
// DecodeDataPayload decodes a frame payload into header and data
// Auto-detects protocol version
func DecodeDataPayload(payload []byte) (DataHeader, []byte, error) {
if len(payload) == 0 {
return DataHeader{}, nil, errors.New("empty payload")
}
// Try to detect version:
// - V1 (JSON): starts with '{'
// - V2 (Binary): first byte is flags (0x00-0x1F typically)
if payload[0] == '{' {
// V1: JSON format
return DecodeDataPayloadV1(payload)
}
// V2: Binary format
return DecodeDataPayloadV2(payload)
}
// DecodeDataPayloadV1 decodes JSON format (legacy)
// Format: JSON_HEADER\nDATA
func DecodeDataPayloadV1(payload []byte) (DataHeader, []byte, error) {
// Find newline separator
sepIdx := -1
for i, b := range payload {
if b == '\n' {
sepIdx = i
break
}
}
if sepIdx == -1 {
return DataHeader{}, nil, errors.New("invalid v1 payload: no newline separator")
}
// Parse JSON header
var header DataHeader
if err := json.Unmarshal(payload[:sepIdx], &header); err != nil {
return DataHeader{}, nil, err
}
// Extract data (after newline)
data := payload[sepIdx+1:]
return header, data, nil
}
// DecodeDataPayloadV2 decodes binary format (optimized)
// Format: BINARY_HEADER + DATA
func DecodeDataPayloadV2(payload []byte) (DataHeader, []byte, error) {
if len(payload) < binaryHeaderMinSize {
return DataHeader{}, nil, errors.New("invalid v2 payload: too short")
}
// Decode binary header
var h2 DataHeaderV2
if err := h2.UnmarshalBinary(payload); err != nil {
return DataHeader{}, nil, err
}
// Extract data (after header)
headerSize := h2.Size()
if len(payload) < headerSize {
return DataHeader{}, nil, errors.New("invalid v2 payload: data missing")
}
data := payload[headerSize:]
// Convert to DataHeader
header := h2.ToDataHeader()
return header, data, nil
}
// GetPayloadHeaderSize returns the size of the header in the payload
// This is useful for pre-allocating buffers
func GetPayloadHeaderSize(header DataHeader) int {
var h2 DataHeaderV2
h2.FromDataHeader(header)
return h2.Size()
}