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; }