/// <summary>
 /// Creates an instance of a WebSocketPayloadReader.
 /// </summary>
 /// <param name="header">The WebSocketHeader associated with this payload.</param>
 public WebSocketPayloadReader(WebSocketHeader header)
 {
     _payloadBytesRemaining = header.PayloadLength;
     _payloadEncoder        = new WebSocketPayloadEncoder(header.MaskingKey);
     _masked = header.Masked;
 }
        /// <summary>
        /// Writes the WebSocketWriteFrame to the provided buffer writer.
        /// </summary>
        /// <param name="message">The message to write.</param>
        /// <param name="output">The buffer writer to write the message to.</param>
        public unsafe void WriteMessage(WebSocketWriteFrame message, IBufferWriter <byte> output)
        {
            if (message.Header.PayloadLength != (ulong)message.Payload.Length)
            {
                throw new WebSocketFrameException($"Header payload length ({message.Header.PayloadLength}) does not equal supplied payload length ({message.Payload.Length})");
            }

            var headerSize = 2;
            var extendedPayloadLengthSize = 0;
            var maskPosition = 2;

            if (message.Header.PayloadLength > 125)
            {
                if (message.Header.PayloadLength <= ushort.MaxValue)
                {
                    extendedPayloadLengthSize = 2;
                    maskPosition += 2;
                }
                else
                {
                    extendedPayloadLengthSize = 8;
                    maskPosition += 8;
                }
            }

            if (message.Header.Masked)
            {
                headerSize += extendedPayloadLengthSize + 4;
            }
            else
            {
                headerSize += extendedPayloadLengthSize;
            }

            Span <byte> headerSpan = (stackalloc byte[14]).Slice(0, headerSize);

            headerSpan[0] = (byte)message.Header.Opcode;

            if (message.Header.Fin)
            {
                headerSpan[0] |= 0b1000_0000;
            }

            switch (extendedPayloadLengthSize)
            {
            case 2:
                headerSpan[1] = 126;
                BinaryPrimitives.WriteUInt16BigEndian(headerSpan.Slice(2), (ushort)message.Header.PayloadLength);
                break;

            case 8:
                headerSpan[1] = 127;
                BinaryPrimitives.WriteUInt64BigEndian(headerSpan.Slice(2), message.Header.PayloadLength);
                break;

            default:
                headerSpan[1] = (byte)message.Header.PayloadLength;
                break;
            }

            if (message.Header.Masked)
            {
                headerSpan[1] |= 0b1000_0000;

                var maskingKey = message.Header.MaskingKey;
                MemoryMarshal.Write(headerSpan.Slice(maskPosition), ref maskingKey);

                if (!message.MaskingComplete)
                {
                    var encoder = new WebSocketPayloadEncoder(message.Header.MaskingKey);
                    encoder.MaskUnmaskPayload(message.Payload, message.Header.PayloadLength, out var _);
                    message.MaskingComplete = true;
                }
            }

            output.Write(headerSpan);
            if (message.Payload.IsSingleSegment)
            {
                output.Write(message.Payload.FirstSpan);
            }
            else
            {
                foreach (var memory in message.Payload)
                {
                    output.Write(memory.Span);
                }
            }
        }