public static async ValueTask SendMessageAsync <T>(this WebSocket socket, IMessageWriter writer, T message, WebSocketMessageType type, CancellationToken token = default) { _buffer.Clear(); writer.WriteMessage(message, _buffer); await socket.SendAsync(_buffer.WrittenMemory, type, true, token); }
/// <summary> /// Resets the <see cref="Utf8JsonWriter"/> internal state so that it can be re-used with the new instance of <see cref="Stream" />. /// </summary> /// <param name="utf8Json">An instance of <see cref="Stream" /> used as a destination for writing JSON text into.</param> /// <remarks> /// The <see cref="Utf8JsonWriter"/> will continue to use the original writer options /// but now write to the passed in <see cref="Stream" /> as the new destination. /// </remarks> /// <exception cref="ArgumentNullException"> /// Thrown when the instance of <see cref="Stream" /> that is passed in is null. /// </exception> /// <exception cref="ObjectDisposedException"> /// The instance of <see cref="Utf8JsonWriter"/> has been disposed. /// </exception> public void Reset(Stream utf8Json) { CheckNotDisposed(); if (utf8Json == null) { throw new ArgumentNullException(nameof(utf8Json)); } if (!utf8Json.CanWrite) { throw new ArgumentException(SR.StreamNotWritable); } _stream = utf8Json; if (_arrayBufferWriter == null) { _arrayBufferWriter = new ArrayBufferWriter <byte>(); } else { _arrayBufferWriter.Clear(); } _output = null; ResetHelper(); }
public async Task RunAndBlockAsync(Uri url, CancellationToken cancel) { var error = "Error."; var bufferWriter = new ArrayBufferWriter <byte>(CHUNK_SIZE); try { using (_ws = new ClientWebSocket()) { await _ws.ConnectAsync(url, cancel).ConfigureAwait(false); // WebsocketConnected!.Invoke(this); while (true) { var result = await _ws.ReceiveAsync(bufferWriter.GetMemory(CHUNK_SIZE), cancel); bufferWriter.Advance(result.Count); if (result.MessageType == WebSocketMessageType.Close) { var closeMessage = CloseCodes.GetErrorCodeMessage((int?)_ws.CloseStatus ?? 0).Message; error = $"Websocket closed ({_ws.CloseStatus}): {_ws.CloseStatusDescription} {closeMessage}"; break; } if (result.EndOfMessage) { var pr = PayloadReceived; var data = bufferWriter.WrittenMemory.ToArray(); bufferWriter.Clear(); if (!(pr is null)) { await pr.Invoke(data); } } } } } catch (WebSocketException ex) { Log.Warning("Disconnected, check your internet connection..."); Log.Debug(ex, "Websocket Exception in websocket client"); } catch (OperationCanceledException) { // ignored } catch (Exception ex) { Log.Error(ex, "Error in websocket client. {Message}", ex.Message); } finally { bufferWriter.Clear(); _ws = null; await ClosedAsync(error).ConfigureAwait(false); } }
public void Deserialize_LowAllocate() { for (int i = 0; i < 10000; i++) { buffer.Clear(); var deserializer = CryptoDtoDeserializer.DeserializeIgnoreSequence(buffer, remoteChannel, cryptoDtoPacket); } }
/// <summary> /// Resets the <see cref="Utf8JsonWriter"/> internal state so that it can be re-used. /// </summary> /// <remarks> /// The <see cref="Utf8JsonWriter"/> will continue to use the original writer options /// and the original output as the destination (either <see cref="IBufferWriter{Byte}" /> or <see cref="Stream" />). /// </remarks> public void Reset() { if (_arrayBufferWriter != null) { _arrayBufferWriter.Clear(); } ResetHelper(); }
public async Task WriteAndCopyToStreamAsync() { var output = new ArrayBufferWriter <byte>(); WriteData(output, 100); using var memStream = new MemoryStream(100); Assert.Equal(100, output.WrittenCount); ReadOnlyMemory <byte> outputMemory = output.WrittenMemory.ToArray(); ReadOnlyMemory <byte> transient = output.WrittenMemory; Assert.True(transient.Span[0] != 0); await memStream.WriteAsync(transient.ToArray(), 0, transient.Length); output.Clear(); Assert.True(transient.Span[0] == 0); Assert.Equal(0, output.WrittenCount); byte[] streamOutput = memStream.ToArray(); Assert.True(ReadOnlyMemory <byte> .Empty.Span.SequenceEqual(output.WrittenMemory.Span)); Assert.True(ReadOnlySpan <byte> .Empty.SequenceEqual(output.WrittenMemory.Span)); Assert.Equal(outputMemory.Length, streamOutput.Length); Assert.True(outputMemory.Span.SequenceEqual(streamOutput)); }
/// <summary> /// Resets the <see cref="Utf8JsonWriter"/> internal state so that it can be re-used. /// </summary> /// <remarks> /// The <see cref="Utf8JsonWriter"/> will continue to use the original writer options /// and the original output as the destination (either <see cref="IBufferWriter{Byte}" /> or <see cref="Stream" />). /// </remarks> /// <exception cref="ObjectDisposedException"> /// The instance of <see cref="Utf8JsonWriter"/> has been disposed. /// </exception> public void Reset() { CheckNotDisposed(); _arrayBufferWriter?.Clear(); ResetHelper(); }
/// <summary> /// Reads messages from web socket and writes them to channel. /// Complete() will be called on channel writer when a close message is received from socket. /// On exception or cancellation, channel writer will not be set to complete. /// </summary> /// <exception cref="WebSocketException"></exception> /// <exception cref="OperationCanceledException"></exception> internal static async Task ReceiveLoop(this ChannelWriter <Message> channelWriter, WebSocket socket, CancellationToken cancellationToken) { Requires.NotNull(channelWriter, nameof(channelWriter)); Requires.NotNull(socket, nameof(socket)); var bufferWriter = new ArrayBufferWriter <byte>(); while (await ReadMessageAsync().ConfigureAwait(false)) { } async Task <bool> ReadMessageAsync() { ValueWebSocketReceiveResult result; do { result = await socket.ReceiveAsync(bufferWriter.GetMemory(), cancellationToken).ConfigureAwait(false); switch (result.MessageType) { case WebSocketMessageType.Text: case WebSocketMessageType.Binary: bufferWriter.Advance(result.Count); break; case WebSocketMessageType.Close: return(false); } } while (!result.EndOfMessage); channelWriter.TryWrite(new Message(Convert(result.MessageType), bufferWriter.WrittenSpan.ToArray())); bufferWriter.Clear(); return(true);
private const uint hpSMT = 0x534D5400u; // SMT public ReadOnlyMemory <byte> Sign(KeyPair keyPair, out Hash256 hash) { Signers = null; TxnSignature = null; SigningPubKey = keyPair.PublicKey.GetCanoncialBytes(); var bufferWriter = new ArrayBufferWriter <byte>(); System.Buffers.Binary.BinaryPrimitives.WriteUInt32BigEndian(bufferWriter.GetSpan(4), hpSTX); bufferWriter.Advance(4); Serialize(bufferWriter, true); // Calculate signature and serialize again TxnSignature = keyPair.Sign(bufferWriter.WrittenSpan); bufferWriter.Clear(); System.Buffers.Binary.BinaryPrimitives.WriteUInt32BigEndian(bufferWriter.GetSpan(4), hpTXN); bufferWriter.Advance(4); Serialize(bufferWriter, false); using (var sha512 = System.Security.Cryptography.SHA512.Create()) { Span <byte> hashSpan = stackalloc byte[64]; sha512.TryComputeHash(bufferWriter.WrittenSpan, hashSpan, out var bytesWritten); hash = new Hash256(hashSpan.Slice(0, 32)); } return(bufferWriter.WrittenMemory.Slice(4)); }
private void Grow(int requiredSize) { if (_memory.Length == 0) { FirstGrow(requiredSize); return; } int sizeHint = Math.Max(DefaultGrowthSize, requiredSize); Debug.Assert(BytesPending != 0); if (_stream != null) { Debug.Assert(_arrayBufferWriter != null); _arrayBufferWriter.Advance(BytesPending); Debug.Assert(BytesPending == _arrayBufferWriter.WrittenCount); _stream.Write(_arrayBufferWriter.WrittenSpan); _arrayBufferWriter.Clear(); _memory = _arrayBufferWriter.GetMemory(sizeHint); Debug.Assert(_memory.Length >= sizeHint); } else { Debug.Assert(_output != null); _output.Advance(BytesPending); _memory = _output.GetMemory(sizeHint); if (_memory.Length < sizeHint) { // ToDo - replace this throw new Exception("Need more Span size"); } } BytesCommitted += BytesPending; BytesPending = 0; }
public T Clone <T>(T item) { _bufferWriter.Clear(); _jsonWriter.Reset(); JsonSerializer.Serialize(_jsonWriter, item, _options); return(JsonSerializer.Deserialize <T>(_bufferWriter.WrittenSpan, _options)); }
public static void RenderToBuffer() { const string placeholder = "%s"; var template = "Hello, %s!%s".AsTemplate(placeholder); var writer = new ArrayBufferWriter<char>(); template.Render(writer, Rewrite); Equal("Hello, world!!", writer.BuildString()); writer.Clear(); template = "%s%s".AsTemplate(placeholder); template.Render(writer, Rewrite); Equal("world!", writer.BuildString()); writer.Clear(); template = "%s!!%s".AsTemplate(placeholder); template.Render(writer, Rewrite); Equal("world!!!", writer.BuildString()); }
//Can we change the ChaCha20Poly1305 input to some kind of ICrypto interface or action with 'Encrypt' and 'Decrypt'? private void Pack(IBufferWriter <byte> output, CryptoDtoHeaderDto header, ChaCha20Poly1305 crypto, ReadOnlySpan <byte> dtoNameBuffer, ReadOnlySpan <byte> dtoBuffer) { lock (bufferLock) { headerBuffer.Clear(); MessagePackSerializer.Serialize(headerBuffer, header); ReadOnlySpan <byte> headerBytes = headerBuffer.WrittenSpan; ushort headerLength = (ushort)headerBytes.Length; BinaryPrimitives.WriteUInt16LittleEndian(headerLengthBytes, headerLength); ushort dtoNameLength = (ushort)dtoNameBuffer.Length; BinaryPrimitives.WriteUInt16LittleEndian(dtoNameLengthBytes, dtoNameLength); ushort dtoLength = (ushort)dtoBuffer.Length; BinaryPrimitives.WriteUInt16LittleEndian(dtoLengthBytes, dtoLength); switch (header.Mode) { case CryptoDtoMode.ChaCha20Poly1305: { int adLength = 2 + headerLength; int aeLength = 2 + dtoNameLength + 2 + dtoLength; // Copy data into associated data buffer adBuffer.Clear(); adBuffer.Write(headerLengthBytes); adBuffer.Write(headerBytes); // Copy data into authenticated encryption buffer aeBuffer.Clear(); aeBuffer.Write(dtoNameLengthBytes); aeBuffer.Write(dtoNameBuffer); aeBuffer.Write(dtoLengthBytes); aeBuffer.Write(dtoBuffer); Span <byte> nonceSpan = new Span <byte>(nonceBytes); BinaryPrimitives.WriteUInt64LittleEndian(nonceSpan.Slice(4), header.Sequence); var adSpan = adBuffer.WrittenSpan; var aeSpan = aeBuffer.WrittenSpan; var cipherTextSpan = cipherText.Span.Slice(0, aeLength); cipherTextSpan.Clear(); Span <byte> tagSpan = tagBuffer; tagSpan.Clear(); crypto.Encrypt(nonceSpan, aeSpan, cipherTextSpan, tagSpan, adSpan); output.Write(adSpan); output.Write(cipherTextSpan); output.Write(tagSpan); break; } default: throw new CryptographicException("Mode not recognised"); } } }
// Hint for callers using ArrayBufferWriter<byte> - output.WrittenSpan contains the serialised data public void Serialize <T>(IBufferWriter <byte> output, CryptoDtoChannel channel, CryptoDtoMode mode, T dto) { lock (bufferLock) { ReadOnlySpan <byte> dtoNameBuffer = GetDtoNameBytes <T>(); dtoBuffer.Clear(); MessagePackSerializer.Serialize(dtoBuffer, dto); Pack(output, channel, mode, dtoNameBuffer, dtoBuffer.WrittenSpan); } }
public void Reset() { PayloadLength = null; if (_bufferWriter != null) { // Reuse existing buffer writer _bufferWriter.Clear(); } _array = null; _state = InternalState.Initialized; }
public void SerializeToBufferLessTimes() { var buffer = new ArrayBufferWriter <byte>(); var obj = new SampleObject(); for (var i = 0; i < 3; i++) { buffer.Clear(); MessagePackSerializer.Serialize(buffer, obj); } }
public void Init(ICharMetric cdi) { _buffer.Clear(); _indexOnLine = cdi.IndexEachLine; _location = new Point2D((int)cdi.Left, cdi.Y); _buffer.GetSpan(1)[0] = cdi.Char; _buffer.Advance(1); _font = cdi.Font; _color = cdi.Color; }
public void Reset() { _compressionProvider = ResolveCompressionProvider(); _payloadLength = null; if (_bufferWriter != null) { // Reuse existing buffer writer _bufferWriter.Clear(); } _state = InternalState.Initialized; }
public void Pack_ZeroAllocate() { for (int i = 0; i < 10000; i++) { if (cryptoChannelStore.TryGetChannel("Benchmark", out CryptoDtoChannel channel)) { buffer.Clear(); serializer.Pack(buffer, channel, CryptoDtoMode.ChaCha20Poly1305, typeNameBytes, dtoBytes); var packetBytes = buffer.WrittenSpan; } } }
public ReadOnlyMemory <byte> Sign(ReadOnlySpan <ValueTuple <AccountId, KeyPair> > signers, out Hash256 hash) { var signerArray = new Signer[signers.Length]; for (int i = 0; i < signers.Length; ++i) { var signer = new Signer(); signer.Account = signers[i].Item1; signer.SigningPubKey = signers[i].Item2.PublicKey.GetCanoncialBytes(); signer.TxnSignature = null; signerArray[i] = signer; } Signers = Array.AsReadOnly(signerArray); TxnSignature = null; SigningPubKey = null; var bufferWriter = new ArrayBufferWriter <byte>(); // Calculate signatures and then serialize again for (int i = 0; i < signers.Length; ++i) { // For each signer we need to write the account being signed for the the buffer, sign that then rewind System.Buffers.Binary.BinaryPrimitives.WriteUInt32BigEndian(bufferWriter.GetSpan(4), hpSMT); bufferWriter.Advance(4); Serialize(bufferWriter, true); Signers[i].Account.CopyTo(bufferWriter.GetSpan(20)); bufferWriter.Advance(20); signerArray[i].TxnSignature = signers[i].Item2.Sign(bufferWriter.WrittenSpan); bufferWriter.Clear(); } Array.Sort <Signer>(signerArray, (x, y) => x.Account.CompareTo(y.Account)); Signers = Array.AsReadOnly(signerArray); System.Buffers.Binary.BinaryPrimitives.WriteUInt32BigEndian(bufferWriter.GetSpan(4), hpTXN); bufferWriter.Advance(4); Serialize(bufferWriter, false); using (var sha512 = System.Security.Cryptography.SHA512.Create()) { Span <byte> hashSpan = stackalloc byte[64]; sha512.TryComputeHash(bufferWriter.WrittenSpan, hashSpan, out var bytesWritten); hash = new Hash256(hashSpan.Slice(0, 32)); } return(bufferWriter.WrittenMemory.Slice(4)); }
public void TestHandshakeActThreeResponderSide(string actOneInput, string actThreeInput) { _noiseProtocol = GetResponderNoiseProtocol(); var buffer = new ArrayBufferWriter <byte>(); //read _noiseProtocol.Handshake(new ReadOnlySequence <byte>(actOneInput.ToByteArray()), buffer); buffer.Clear(); //write _noiseProtocol.Handshake(new ReadOnlySequence <byte>(actThreeInput.ToByteArray()), buffer); Assert.Equal(buffer.WrittenCount, 0); Assert.Empty(buffer.WrittenSpan.ToArray()); }
public void Clear() { var output = new ArrayBufferWriter <T>(256); int previousAvailable = output.FreeCapacity; WriteData(output, 2); Assert.True(output.FreeCapacity < previousAvailable); Assert.True(output.WrittenCount > 0); Assert.False(ReadOnlySpan <T> .Empty.SequenceEqual(output.WrittenSpan)); Assert.False(ReadOnlyMemory <T> .Empty.Span.SequenceEqual(output.WrittenMemory.Span)); Assert.True(output.WrittenSpan.SequenceEqual(output.WrittenMemory.Span)); output.Clear(); Assert.Equal(0, output.WrittenCount); Assert.True(ReadOnlySpan <T> .Empty.SequenceEqual(output.WrittenSpan)); Assert.True(ReadOnlyMemory <T> .Empty.Span.SequenceEqual(output.WrittenMemory.Span)); Assert.Equal(previousAvailable, output.FreeCapacity); }
public void TestHandshakeActTwoInitiatorSide(string expectedInputHex) { _noiseProtocol = GetInitiatorNoiseProtocol(); var buffer = new ArrayBufferWriter <byte>(); //write _noiseProtocol.Handshake(new ReadOnlySequence <byte>(), buffer); buffer.Clear(); //read & write _noiseProtocol.Handshake(new ReadOnlySequence <byte>(expectedInputHex.ToByteArray()), buffer); //encrypted null so nothing to decrypt Assert.Equal(buffer.WrittenCount, 66); Assert.NotEmpty(buffer.WrittenSpan.ToArray()); }
public async Task Write(object message) { _serializer.Serialize(_buffer, message); ReadOnlyMemory <byte> messageMemory = _buffer.WrittenMemory; (int messageSize, ReadOnlyMemory <byte> messageSizeMemory) = WriteMessageSize(); _messageLogger.Log(messageSizeMemory); _messageLogger.Log(messageMemory); await _stream.WriteAsync(messageSizeMemory); await _stream.WriteAsync(messageMemory); _buffer.Clear(); }
public void TestHandshakeActThreeInitiatorSide(string actTwoInput, string expectedOutputHex) { _noiseProtocol = GetInitiatorNoiseProtocol(); var buffer = new ArrayBufferWriter <byte>(); //write _noiseProtocol.Handshake(new ReadOnlySequence <byte>(), buffer); buffer.Clear(); //read & write _noiseProtocol.Handshake(new ReadOnlySequence <byte>(actTwoInput.ToByteArray()), buffer); var expectedOutput = expectedOutputHex.ToByteArray(); Assert.Equal(buffer.WrittenCount, expectedOutput.Length); Assert.Equal(buffer.WrittenSpan.ToArray(), expectedOutput); }
public int ReadMessageLength(ReadOnlySequence <byte> encryptedHeader) //TODO David add tests { if (_transport == null) { throw new InvalidOperationException("Must complete handshake before reading messages"); } _inputHeaderCache.Clear(); _transport.ReadMessage(encryptedHeader, _inputHeaderCache); ushort messageLengthDecrypted = BinaryPrimitives.ReadUInt16BigEndian(_inputHeaderCache .WrittenSpan); // TODO Dan test header size bigger then 2 bytes // Return the message length plus the 16 byte mac data // the caller does not need to know the message has mac data return(messageLengthDecrypted + AEAD_TAG_SIZE); }
public void Dispose() { writer.Dispose(); buffer.Clear(); }
public string[] ReadHeaderLines() { if (State != ReaderState.Headers) { throw new InvalidOperationException(); } List <string> lines = new List <string>(); var outputBuffer = new ArrayBufferWriter <byte>(); if (endOfClearText) { // If we were reading clear text before then we consumed // the first two dashes of the separator. outputBuffer.Write(new[] { (byte)'-', (byte)'-' }); } for (; ;) { var b = innerStream.ReadByte(); switch (b) { case -1: return(Array.Empty <string>()); case '\n': if (!ignoreNL) { goto case '\r'; } ignoreNL = false; break; case '\r': ignoreNL = b == '\r'; // Non-empty line string?line = null; if (outputBuffer.WrittenCount > 0) { line = Encoding.ASCII.GetString(outputBuffer.WrittenSpan); outputBuffer.Clear(); } if (!string.IsNullOrWhiteSpace(line)) { lines.Add(line); } else if (lines.Count > 0) { // TODO: Verify the headers headerEndLineLength = lines.First().Length - 2; // -2 for BEGIN -> END State = !endOfClearText && Equals(lines.FirstOrDefault(), "-----BEGIN PGP SIGNED MESSAGE-----") ? ReaderState.ClearText : ReaderState.Base64; return(lines.ToArray()); } break; default: outputBuffer.GetSpan(1)[0] = (byte)b; outputBuffer.Advance(1); break; } } }
// Currently supports https://github.com/grafana/loki/blob/master/docs/api.md#post-lokiapiv1push public void Format(IEnumerable <LogEvent> logEvents, ITextFormatter formatter, TextWriter output) { if (logEvents == null) { throw new ArgumentNullException(nameof(logEvents)); } if (output == null) { throw new ArgumentNullException(nameof(output)); } if (!logEvents.Any()) { return; } var timestampOverride = SystemClock.Instance.GetCurrentInstant(); // process labels for grouping/sorting var sortedStreams = logEvents .GroupBy(x => x.Properties .Where(prop => _labelNames.Contains(prop.Key)) .Select(prop => new KeyValuePair <string, string>(prop.Key, cleanseString(prop.Value.ToString()))) .Concat(_labelNames.Contains("level") ? new[] { new KeyValuePair <string, string>("level", GetLevel(x.Level)) } : new KeyValuePair <string, string>[] { }) .Concat(_globalLabels).ToHashSet(), new HashSetComparer()) .Select(stream => new KeyValuePair <HashSet <KeyValuePair <string, string> >, IOrderedEnumerable <LogEvent> >(stream.Key, stream.OrderBy(log => log.Timestamp))); var logLineBuffer = new ArrayBufferWriter <byte>(); using var logLineJsonWriter = new Utf8JsonWriter(logLineBuffer); var outputBuffer = new ArrayBufferWriter <byte>(); using var jsonWriter = new Utf8JsonWriter(outputBuffer); jsonWriter.WriteStartObject(); jsonWriter.WriteStartArray("streams"); foreach (var stream in sortedStreams) { jsonWriter.WriteStartObject(); jsonWriter.WriteStartObject("stream"); foreach (var label in stream.Key) { jsonWriter.WriteString(label.Key, label.Value); } jsonWriter.WriteEndObject(); jsonWriter.WriteStartArray("values"); foreach (var logEvent in stream.Value) { jsonWriter.WriteStartArray(); var timestamp = this._preserveTimestamps ? Instant.FromDateTimeOffset(logEvent.Timestamp) : timestampOverride; jsonWriter.WriteStringValue((timestamp.ToUnixTimeTicks() * 100).ToString()); // Construct a json object for the log line logLineJsonWriter.WriteStartObject(); logLineJsonWriter.WriteString("message", logEvent.RenderMessage()); foreach (var property in logEvent.Properties) { logLineJsonWriter.WriteString(property.Key, cleanseString(property.Value.ToString())); } if (this._preserveTimestamps == false) { logLineJsonWriter.WriteString("timestamp", Instant.FromDateTimeOffset(logEvent.Timestamp).ToString()); } if (logEvent.Exception != null) { var sb = new StringBuilder(); var e = logEvent.Exception; while (e != null) { sb.AppendLine(e.Message); sb.AppendLine(e.StackTrace); e = e.InnerException; } logLineJsonWriter.WriteString("exception", sb.ToString()); } logLineJsonWriter.WriteEndObject(); logLineJsonWriter.Flush(); jsonWriter.WriteStringValue(Encoding.UTF8.GetString(logLineBuffer.WrittenSpan)); jsonWriter.WriteEndArray(); logLineJsonWriter.Reset(); logLineBuffer.Clear(); } jsonWriter.WriteEndArray(); jsonWriter.WriteEndObject(); } jsonWriter.WriteEndArray(); jsonWriter.WriteEndObject(); jsonWriter.Flush(); output.Write(Encoding.UTF8.GetString(outputBuffer.WrittenSpan)); }
public int ReadClearText(Span <byte> buffer) { if (State != ReaderState.ClearText) { throw new InvalidOperationException(); } // We usually try to output one character for one input character // but new line and dash sequences can produce up to 4 characters // at once, so size the buffer to accommodate that. We can also // overshoot the buffer with pending whitespace. var outputBuffer = new ArrayBufferWriter <byte>(buffer.Length + 3); if (pendingData != null) { outputBuffer.Write(pendingData); } while (!endOfClearText && outputBuffer.WrittenCount < buffer.Length) { var b = innerStream.ReadByte(); // End of stream if (b == -1) { break; } switch (b) { case ' ': case '\t': // Collect pending whitespace because it could be trailing whitespace // at end of line pendingWhitespace.GetSpan(1)[0] = (byte)b; pendingWhitespace.Advance(1); break; case '\r': case '\n': // Ignore \n that was still part of header if (ignoreNL && b == '\n') { ignoreNL = false; break; } // Discard any pending whitespace pendingWhitespace.Clear(); // Read next character after the new line var nextB = innerStream.ReadByte(); if (b == '\r' && nextB == '\n') { nextB = innerStream.ReadByte(); } if (nextB == '-') { // New line followed byt dash. Now we are looking either at the end of // clear text or a dash escape nextB = innerStream.ReadByte(); if (nextB == ' ') { // Dash escape, remove it and flush the new line outputBuffer.Write(new[] { (byte)'\r', (byte)'\n' }); } else if (nextB == '-') { // Possible end of clear text endOfClearText = true; break; } else { // Invalid clear text, flush the new lind and output it var clearText = outputBuffer.GetSpan(4); clearText[0] = (byte)'\r'; clearText[1] = (byte)'\n'; clearText[2] = (byte)'-'; clearText[3] = (byte)nextB; outputBuffer.Advance(4); } } else { // Flush the new line outputBuffer.Write(new[] { (byte)'\r', (byte)'\n' }); if (nextB == '\r' || nextB == '\n') { b = nextB; goto case '\r'; } else if (nextB == ' ' || nextB == '\t') { pendingWhitespace.GetSpan(1)[0] = (byte)nextB; pendingWhitespace.Advance(1); } else { outputBuffer.GetSpan(1)[0] = (byte)nextB; outputBuffer.Advance(1); } } break; default: // Flush any pending whitespace if (pendingWhitespace.WrittenCount > 0) { outputBuffer.Write(pendingWhitespace.WrittenSpan); pendingWhitespace.Clear(); } outputBuffer.GetSpan(1)[0] = (byte)b; outputBuffer.Advance(1); break; } } if (outputBuffer.WrittenCount > buffer.Length) { pendingData = outputBuffer.WrittenSpan.Slice(buffer.Length).ToArray(); outputBuffer.WrittenSpan.Slice(0, buffer.Length).CopyTo(buffer); return(buffer.Length); } else { if (endOfClearText) { State = ReaderState.Headers; } pendingData = null; outputBuffer.WrittenSpan.CopyTo(buffer); return(outputBuffer.WrittenCount); } }