using StellaOps.Router.Common.Enums;
using StellaOps.Router.Common.Models;
namespace StellaOps.Router.Transport.Udp;
///
/// Handles serialization and deserialization of frames for UDP transport.
/// Frame format: [1-byte frame type][16-byte correlation GUID][remaining data]
///
public static class UdpFrameProtocol
{
private const int FrameTypeSize = 1;
private const int CorrelationIdSize = 16;
private const int HeaderSize = FrameTypeSize + CorrelationIdSize;
///
/// Parses a frame from a datagram.
///
/// The datagram data.
/// The parsed frame.
/// Thrown when the datagram is too small.
public static Frame ParseFrame(ReadOnlySpan data)
{
if (data.Length < HeaderSize)
{
throw new InvalidOperationException(
$"Datagram too small: {data.Length} bytes, minimum is {HeaderSize}");
}
var frameType = (FrameType)data[0];
var correlationId = new Guid(data.Slice(FrameTypeSize, CorrelationIdSize));
var payload = data.Length > HeaderSize
? data[HeaderSize..].ToArray()
: Array.Empty();
return new Frame
{
Type = frameType,
CorrelationId = correlationId.ToString("N"),
Payload = payload
};
}
///
/// Serializes a frame to a datagram.
///
/// The frame to serialize.
/// The serialized datagram bytes.
public static byte[] SerializeFrame(Frame frame)
{
// Parse or generate correlation ID
var correlationGuid = frame.CorrelationId is not null &&
Guid.TryParse(frame.CorrelationId, out var parsed)
? parsed
: Guid.NewGuid();
var payloadLength = frame.Payload.Length;
var buffer = new byte[HeaderSize + payloadLength];
// Write frame type
buffer[0] = (byte)frame.Type;
// Write correlation ID
correlationGuid.TryWriteBytes(buffer.AsSpan(FrameTypeSize, CorrelationIdSize));
// Write payload
if (payloadLength > 0)
{
frame.Payload.Span.CopyTo(buffer.AsSpan(HeaderSize));
}
return buffer;
}
///
/// Gets the header size for UDP frames.
///
public static int GetHeaderSize() => HeaderSize;
}