| File: Coordinator\Messages\Message.cs | Web Access |
| Project: src\msbuild\src\Framework\Microsoft.Build.Framework.csproj (Microsoft.Build.Framework) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.IO; using System.Runtime.CompilerServices; namespace Microsoft.Build.Framework.Coordinator; /// <summary> /// Base type shared by <see cref="ClientMessage"/> and <see cref="ServerMessage"/>, holding the wire framing /// logic common to both directions of the coordinator protocol: a single type byte, optionally followed by a /// single extended fields byte when <see cref="ExtendedFieldsByte"/> is non-zero, followed by the message's /// own payload. /// </summary> /// <typeparam name="TMessageType"> /// The enum type that identifies message kinds on the wire. The enum must have <see cref="byte"/> as its /// underlying type. /// </typeparam> internal abstract partial record Message<TMessageType> where TMessageType : struct, Enum { private const byte ExtendedBit = 0x80; static Message() { Assumed.Equal( Enum.GetUnderlyingType(typeof(TMessageType)), typeof(byte), $"{nameof(TMessageType)} must have an underlying type of byte."); } private readonly TMessageType _messageType; /// <summary> /// Capability-gated field flags for this specific message instance. A value of <c>0</c> omits the extended /// fields byte from the wire format; non-zero values cause the byte to be emitted and interpreted by the /// receiving message type. /// </summary> protected virtual byte ExtendedFieldsByte => 0; public TMessageType MessageType => _messageType; protected Message(TMessageType messageType) { _messageType = messageType; } private static byte GetTypeByte(TMessageType messageType) => Unsafe.As<TMessageType, byte>(ref messageType); private static TMessageType GetMessageType(byte typeByte) => Unsafe.As<byte, TMessageType>(ref typeByte); public void WriteTo(BinaryWriter writer) { byte typeByte = GetTypeByte(_messageType); byte extendedFieldsByte = ExtendedFieldsByte; if (extendedFieldsByte != 0) { // UNDONE: The extended fields byte reserves the extended bit for future expansion via more than one byte of extended fields. // For now, we don't support that, so assert that the extended bit is not set. Assumed.Zero(extendedFieldsByte & ExtendedBit, "Extended fields byte must not set the extended bit."); writer.Write((byte)(typeByte | ExtendedBit)); writer.Write(extendedFieldsByte); } else { writer.Write(typeByte); } WritePayload(writer); writer.Flush(); } protected virtual void WritePayload(BinaryWriter writer) { // Descendants can override. } /// <summary> /// Reads the wire type byte and splits it into the type ordinal (with the extended-fields bit masked off) /// and whether an extended fields byte follows. /// </summary> protected static (TMessageType MessageType, bool HasExtendedFields) ReadTypeByte(BinaryReader reader) { byte typeByte = reader.ReadByte(); bool hasExtendedFields = false; if ((typeByte & ExtendedBit) != 0) { hasExtendedFields = true; // Strip the extended-fields bit off the type byte before casting it to TMessageType. typeByte = (byte)(typeByte & ~ExtendedBit); } TMessageType messageType = GetMessageType(typeByte); return (messageType, hasExtendedFields); } /// <summary> /// Reads the single extended fields byte for a message type that supports it. /// </summary> protected static byte ReadExtendedFieldsByte(BinaryReader reader) { byte extendedFieldsByte = reader.ReadByte(); // UNDONE: The extended fields byte reserves the extended bit for future expansion via more than one byte of extended fields. // For now, we don't support that, so assert that the extended bit is not set. Assumed.Zero(extendedFieldsByte & ExtendedBit, "Multi-byte extended fields are not yet supported."); return extendedFieldsByte; } }