rs2322tcp/internal/transport/codec.go
2026-08-10 10:38:14 +02:00

101 lines
2.4 KiB
Go

/*
Package transport contains the network transport definitions for rs2322tcp.
This file implements the framing of control protocol messages. Control
messages are encoded as one JSON object per line (JSON Lines).
Project: rs2322tcp
Module: git.lang-dieter.de/rs2322tcp
*/
package transport
import (
"bufio"
"encoding/json"
"fmt"
"io"
)
///////////////////////////////////////////////////////////////////////////////
// Constants
///////////////////////////////////////////////////////////////////////////////
const (
// MaximumControlMessageSize limits the size of one control message.
MaximumControlMessageSize = 64 * 1024
)
///////////////////////////////////////////////////////////////////////////////
// Writer
///////////////////////////////////////////////////////////////////////////////
// WriteMessage writes one control message as a JSON object followed by
// a newline.
func WriteMessage(w io.Writer, message interface{}) error {
if w == nil {
return fmt.Errorf("writer is nil")
}
if message == nil {
return fmt.Errorf("message is nil")
}
data, err := json.Marshal(message)
if err != nil {
return fmt.Errorf("encode control message: %w", err)
}
if len(data) > MaximumControlMessageSize {
return fmt.Errorf("control message too large: %d bytes",
len(data))
}
data = append(data, '\n')
if _, err := w.Write(data); err != nil {
return fmt.Errorf("write control message: %w", err)
}
return nil
}
///////////////////////////////////////////////////////////////////////////////
// Reader
///////////////////////////////////////////////////////////////////////////////
// ReadMessage reads exactly one JSON Lines control message.
//
// The target must be a pointer to the expected message structure.
func ReadMessage(r *bufio.Reader, target interface{}) error {
if r == nil {
return fmt.Errorf("reader is nil")
}
if target == nil {
return fmt.Errorf("target is nil")
}
data, err := r.ReadBytes('\n')
if err != nil {
if err == io.EOF && len(data) == 0 {
return io.EOF
}
if err == io.EOF {
return fmt.Errorf("incomplete control message: %w", err)
}
return fmt.Errorf("read control message: %w", err)
}
if len(data) > MaximumControlMessageSize {
return fmt.Errorf("control message too large: %d bytes",
len(data))
}
if err := json.Unmarshal(data, target); err != nil {
return fmt.Errorf("decode control message: %w", err)
}
return nil
}