public void ByteByByteTest() { WritableBuffer writableBuffer = default; for (int i = 1; i <= 1024 * 1024; i++) { writableBuffer = _pipe.Writer.Alloc(100); writableBuffer.Advance(1); writableBuffer.Commit(); Assert.Equal(i, _pipe.Length); } writableBuffer.FlushAsync(); for (int i = 1024 * 1024 - 1; i >= 0; i--) { var result = _pipe.Reader.ReadAsync().GetResult(); var consumed = result.Buffer.Slice(1).Start; Assert.Equal(i + 1, result.Buffer.Length); _pipe.Reader.Advance(consumed, consumed); Assert.Equal(i, _pipe.Length); } }
private static void WriteAsciiString(ref WritableBuffer buffer, ReadOnlySpan <char> value) { if (value == null || value.Length == 0) { return; } while (value.Length != 0) { buffer.Ensure(); var span = buffer.Buffer.Span; int bytesToWrite = Math.Min(value.Length, span.Length); // todo: Vector.Narrow for (int i = 0; i < bytesToWrite; i++) { span[i] = (byte)value[i]; } buffer.Advance(bytesToWrite); buffer.Commit(); value = value.Slice(bytesToWrite); } }
private void OnRead(UvStreamHandle handle, int status) { if (status == 0) { // A zero status does not indicate an error or connection end. It indicates // there is no data to be read right now. // See the note at http://docs.libuv.org/en/v1.x/stream.html#c.uv_read_cb. _inputBuffer.Commit(); return; } var normalRead = status > 0; var normalDone = status == EOF; var errorDone = !(normalDone || normalRead); var readCount = normalRead ? status : 0; if (!normalRead) { handle.ReadStop(); } IOException error = null; if (errorDone) { Exception uvError; handle.Libuv.Check(status, out uvError); error = new IOException(uvError.Message, uvError); // REVIEW: Should we treat ECONNRESET as an error? // Ignore the error for now _input.CompleteWriter(); } else { _inputBuffer.Advance(readCount); var task = _inputBuffer.FlushAsync(); if (!task.IsCompleted) { // If there's back pressure handle.ReadStop(); // Resume reading when task continues task.ContinueWith((t, state) => ((UvTcpConnection)state).StartReading(), this); } } if (normalDone || _input.Writing.IsCompleted) { _input.CompleteWriter(); } }
public void ReceiveBeginComplete(uint bytesTransferred) { if (bytesTransferred == 0 || _input.Writing.IsCompleted) { _input.CompleteWriter(); } else { _buffer.Advance((int)bytesTransferred); _buffer.Commit(); ProcessReceives(); } }
public void ReceiveBeginComplete(uint bytesTransferred) { if (bytesTransferred == 0 || _input.ReaderCompleted.IsCompleted) { _input.CompleteWriting(); } else { _buffer.CommitBytes((int)bytesTransferred); _buffer.Commit(); ProcessReceives(); } }
private static void WriteUtf8String(ref WritableBuffer buffer, ReadOnlySpan <char> value) { if (value == null || value.Length == 0) { return; } var encoder = TextEncoder.Utf8; while (value.Length != 0) { buffer.Ensure(4); // be able to write at least one character (worst case) var span = buffer.Buffer.Span; encoder.TryEncode(value, span, out int charsConsumed, out int bytesWritten); buffer.Advance(bytesWritten); buffer.Commit(); value = value.Slice(charsConsumed); } }
private void OnRead(UvStreamHandle handle, int status) { if (status == 0) { // EAGAIN/EWOULDBLOCK so just return the buffer. // http://docs.libuv.org/en/v1.x/stream.html#c.uv_read_cb Debug.Assert(_currentWritableBuffer != null); _currentWritableBuffer.Value.Commit(); } else if (status > 0) { Log.ConnectionRead(ConnectionId, status); Debug.Assert(_currentWritableBuffer != null); var currentWritableBuffer = _currentWritableBuffer.Value; currentWritableBuffer.Advance(status); var flushTask = currentWritableBuffer.FlushAsync(); if (!flushTask.IsCompleted) { // We wrote too many bytes to the reader, so pause reading and resume when // we hit the low water mark. _ = ApplyBackpressureAsync(flushTask); } } else { // Given a negative status, it's possible that OnAlloc wasn't called. _currentWritableBuffer?.Commit(); _socket.ReadStop(); IOException error = null; if (status == LibuvConstants.EOF) { Log.ConnectionReadFin(ConnectionId); } else { handle.Libuv.Check(status, out var uvError); // Log connection resets at a lower (Debug) level. if (LibuvConstants.IsConnectionReset(status)) { Log.ConnectionReset(ConnectionId); error = new ConnectionResetException(uvError.Message, uvError); } else { Log.ConnectionError(ConnectionId, uvError); error = new IOException(uvError.Message, uvError); } } // Complete after aborting the connection Input.Complete(error); } // Cleanup state from last OnAlloc. This is safe even if OnAlloc wasn't called. _currentWritableBuffer = null; _bufferHandle.Dispose(); }
private async void OnRead(UvStreamHandle handle, int status) { if (status == 0) { // A zero status does not indicate an error or connection end. It indicates // there is no data to be read right now. // See the note at http://docs.libuv.org/en/v1.x/stream.html#c.uv_read_cb. _inputBuffer?.Commit(); _inputBuffer = null; return; } var normalRead = status > 0; var normalDone = status == EOF; var errorDone = !(normalDone || normalRead); var readCount = normalRead ? status : 0; if (!normalRead) { handle.ReadStop(); } IOException error = null; if (errorDone) { Exception uvError; handle.Libuv.Check(status, out uvError); error = new IOException(uvError.Message, uvError); _inputBuffer?.Commit(); // REVIEW: Should we treat ECONNRESET as an error? // Ignore the error for now _input.Writer.Complete(); } else { var inputBuffer = _inputBuffer.Value; _inputBuffer = null; inputBuffer.Advance(readCount); inputBuffer.Commit(); // Flush if there was data if (readCount > 0) { var awaitable = inputBuffer.FlushAsync(); if (!awaitable.IsCompleted) { // If there's back pressure handle.ReadStop(); // Resume reading when the awaitable completes if (await awaitable) { StartReading(); } else { // We're done writing, the reading is gone _input.Writer.Complete(); } } } } if (normalDone) { _input.Writer.Complete(); } }
public void Commit() => _innerBuffer.Commit();