diff options
author | vnugent <public@vaughnnugent.com> | 2023-03-09 21:41:15 -0500 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2023-03-09 21:41:15 -0500 |
commit | 606e9b3331e73d68da38ec8139051ee1d0149061 (patch) | |
tree | 617e9ed2ee0a4be9c4fbf4c47116366648056f5b | |
parent | 5ddef0fcb742e77b99a0e17015d2eea0a1d4131a (diff) |
Minor HTTP buffs
-rw-r--r-- | lib/Net.Http/src/Core/Request/HttpInputStream.cs | 29 | ||||
-rw-r--r-- | lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs | 6 | ||||
-rw-r--r-- | lib/Net.Http/src/Helpers/CoreBufferHelpers.cs | 71 | ||||
-rw-r--r-- | lib/Net.Http/src/Helpers/InitDataBuffer.cs | 136 | ||||
-rw-r--r-- | lib/Utils/src/Extensions/MemoryExtensions.cs | 11 |
5 files changed, 176 insertions, 77 deletions
diff --git a/lib/Net.Http/src/Core/Request/HttpInputStream.cs b/lib/Net.Http/src/Core/Request/HttpInputStream.cs index e210776..3b929af 100644 --- a/lib/Net.Http/src/Core/Request/HttpInputStream.cs +++ b/lib/Net.Http/src/Core/Request/HttpInputStream.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Net.Http @@ -46,15 +46,19 @@ namespace VNLib.Net.Http.Core private Stream? InputStream; private long _position; - private ISlindingWindowBuffer<byte>? _initalData; + private InitDataBuffer? _initalData; public HttpInputStream(Func<Stream> getTransport) => GetTransport = getTransport; internal void OnComplete() { - //Dispose the inigial data buffer - _initalData?.Close(); - _initalData = null; + //Dispose the initial data buffer if set + if (_initalData.HasValue) + { + _initalData.Value.Release(); + _initalData = null; + } + //Remove stream cache copy InputStream = null; //Reset position @@ -69,7 +73,7 @@ namespace VNLib.Net.Http.Core /// </summary> /// <param name="contentLength">The number of bytes to allow being read from the transport or initial buffer</param> /// <param name="initial">Entity body data captured on initial read</param> - internal void Prepare(long contentLength, ISlindingWindowBuffer<byte>? initial) + internal void Prepare(long contentLength, in InitDataBuffer? initial) { ContentLength = contentLength; _initalData = initial; @@ -102,10 +106,10 @@ namespace VNLib.Net.Http.Core ForwardOnlyWriter<byte> writer = new(buffer[..bytesToRead]); //See if all data is internally buffered - if (_initalData != null && _initalData.AccumulatedSize > 0) + if (_initalData.HasValue && _initalData.Value.Remaining > 0) { //Read as much as possible from internal buffer - ERRNO read = _initalData.Read(writer.Remaining); + ERRNO read = _initalData.Value.Read(writer.Remaining); //Advance writer writer.Advance(read); @@ -134,6 +138,7 @@ namespace VNLib.Net.Http.Core { return ReadAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask(); } + public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default) { //Calculate the amount of data that can be read into the buffer @@ -147,10 +152,10 @@ namespace VNLib.Net.Http.Core ForwardOnlyMemoryWriter<byte> writer = new(buffer[..bytesToRead]); //See if all data is internally buffered - if (_initalData != null && _initalData.AccumulatedSize > 0) + if (_initalData.HasValue && _initalData.Value.Remaining > 0) { //Read as much as possible from internal buffer - ERRNO read = _initalData.Read(writer.Remaining.Span); + ERRNO read = _initalData.Value.Read(writer.Remaining.Span); //Advance writer writer.Advance(read); @@ -183,12 +188,14 @@ namespace VNLib.Net.Http.Core public async ValueTask DiscardRemainingAsync(int maxBufferSize) { long remaining = Remaining; + if(remaining == 0) { return; } + //See if all data has already been buffered - if(_initalData != null && remaining <= _initalData.AccumulatedSize) + if(_initalData.HasValue && remaining <= _initalData.Value.Remaining) { //All data has been buffred, so just clear the buffer _position = Length; diff --git a/lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs b/lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs index f876528..f633a4a 100644 --- a/lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs +++ b/lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Net.Http @@ -521,9 +521,9 @@ namespace VNLib.Net.Http.Core else if (parseState.ContentLength > 0) { //Open a temp buffer to store initial data in - ISlindingWindowBuffer<byte>? initData = reader.GetReminaingData(parseState.ContentLength); + InitDataBuffer? initData = reader.GetReminaingData(parseState.ContentLength); //Setup the input stream and capture the initial data from the reader, and wrap the transport stream to read data directly - Request.InputStream.Prepare(parseState.ContentLength, initData); + Request.InputStream.Prepare(parseState.ContentLength, in initData); Request.HasEntityBody = true; } //Success! diff --git a/lib/Net.Http/src/Helpers/CoreBufferHelpers.cs b/lib/Net.Http/src/Helpers/CoreBufferHelpers.cs index 15c617c..719f4f8 100644 --- a/lib/Net.Http/src/Helpers/CoreBufferHelpers.cs +++ b/lib/Net.Http/src/Helpers/CoreBufferHelpers.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Net.Http @@ -31,10 +31,7 @@ * allocations and help provide memory optimization. */ - - using System; -using System.IO; using System.Buffers; using System.Security; using System.Threading; @@ -51,54 +48,11 @@ namespace VNLib.Net.Http.Core /// </summary> internal static class CoreBufferHelpers { - private sealed class InitDataBuffer : ISlindingWindowBuffer<byte> - { - private readonly ArrayPool<byte> pool; - private readonly int size; - - private byte[]? buffer; - - public InitDataBuffer(ArrayPool<byte> pool, int size) - { - this.buffer = pool.Rent(size, true); - this.pool = pool; - this.size = size; - WindowStartPos = 0; - WindowEndPos = 0; - } - - public int WindowStartPos { get; set; } - public int WindowEndPos { get; set; } - Memory<byte> ISlindingWindowBuffer<byte>.Buffer => buffer.AsMemory(0, size); - - public void Advance(int count) - { - WindowEndPos += count; - } - - public void AdvanceStart(int count) - { - WindowStartPos += count; - } - - public void Reset() - { - WindowStartPos = 0; - WindowEndPos = 0; - } - - //Release the buffer back to the pool - void ISlindingWindowBuffer<byte>.Close() - { - pool.Return(buffer!); - buffer = null; - } - } - /// <summary> /// An internal HTTP character binary pool for HTTP specific internal buffers /// </summary> public static ArrayPool<byte> HttpBinBufferPool { get; } = ArrayPool<byte>.Create(); + /// <summary> /// An <see cref="IUnmangedHeap"/> used for internal HTTP buffers /// </summary> @@ -117,7 +71,7 @@ namespace VNLib.Net.Http.Core public static UnsafeMemoryHandle<byte> GetBinBuffer(int size, bool zero) { //Calc buffer size to the nearest page size - size = (size / 4096 + 1) * 4096; + size = (int)MemoryUtil.NearestPage(size); //If rpmalloc lib is loaded, use it if (MemoryUtil.IsRpMallocLoaded) @@ -137,7 +91,7 @@ namespace VNLib.Net.Http.Core public static IMemoryOwner<byte> GetMemory(int size, bool zero) { //Calc buffer size to the nearest page size - size = (size / 4096 + 1) * 4096; + size = (int)MemoryUtil.NearestPage(size); //If rpmalloc lib is loaded, use it if (MemoryUtil.IsRpMallocLoaded) @@ -167,7 +121,7 @@ namespace VNLib.Net.Http.Core /// <param name="reader"></param> /// <param name="maxContentLength">Maximum content size to clamp the remaining buffer window to</param> /// <returns></returns> - public static ISlindingWindowBuffer<byte>? GetReminaingData<T>(this ref T reader, long maxContentLength) where T: struct, IVnTextReader + public static InitDataBuffer? GetReminaingData<T>(this ref T reader, long maxContentLength) where T: struct, IVnTextReader { //clamp max available to max content length int available = Math.Clamp(reader.Available, 0, (int)maxContentLength); @@ -175,13 +129,14 @@ namespace VNLib.Net.Http.Core { return null; } - //Alloc sliding window buffer - ISlindingWindowBuffer<byte> buffer = new InitDataBuffer(HttpBinBufferPool, available); - //Read remaining data - reader.ReadRemaining(buffer.RemainingBuffer.Span); - //Advance the buffer to the end of available data - buffer.Advance(available); - return buffer; + + //Creates the new initial data buffer + InitDataBuffer buf = InitDataBuffer.AllocBuffer(HttpBinBufferPool, available); + + //Read remaining data into the buffer's data segment + _ = reader.ReadRemaining(buf.DataSegment); + + return buf; } } diff --git a/lib/Net.Http/src/Helpers/InitDataBuffer.cs b/lib/Net.Http/src/Helpers/InitDataBuffer.cs new file mode 100644 index 0000000..36e1f11 --- /dev/null +++ b/lib/Net.Http/src/Helpers/InitDataBuffer.cs @@ -0,0 +1,136 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Net.Http +* File: InitDataBuffer.cs +* +* InitDataBuffer.cs is part of VNLib.Net.Http which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Net.Http is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* VNLib.Net.Http is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using VNLib.Utils; +using VNLib.Utils.Extensions; + +namespace VNLib.Net.Http.Core +{ + /// <summary> + /// A structure that buffers data remaining from an initial transport read. Stored + /// data will be read by copying. + /// </summary> + internal readonly struct InitDataBuffer + { + const int POSITION_SEG_SIZE = sizeof(int); + + readonly int _dataSize; + readonly byte[] _buffer; + readonly ArrayPool<byte> _pool; + + + InitDataBuffer(ArrayPool<byte> pool, byte[] buffer, int size) + { + _pool = pool; + _buffer = buffer; + _dataSize = size; + } + + /// <summary> + /// Allocates the correct size buffer for the given data size + /// </summary> + /// <param name="pool">The pool to allocate the array from</param> + /// <param name="dataSize">The size of the remaining data segment</param> + /// <returns>The newly allocated data buffer</returns> + internal static InitDataBuffer AllocBuffer(ArrayPool<byte> pool, int dataSize) + { + //Alloc buffer + byte[] buffer = pool.Rent(dataSize + POSITION_SEG_SIZE, true); + + return new(pool, buffer, dataSize); + } + + readonly Span<byte> _positionSegment + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _buffer.AsSpan(0, POSITION_SEG_SIZE); + } + + /// <summary> + /// Gets the entire internal data segment to read/write data to/from + /// </summary> + internal readonly Span<byte> DataSegment + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _buffer.AsSpan(POSITION_SEG_SIZE, _dataSize); + } + + private readonly int Position + { + //Reading/wriging the data buffe postion segment + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => MemoryMarshal.Read<int>(_positionSegment); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => MemoryMarshal.Write(_positionSegment, ref value); + } + + /// <summary> + /// Get the amount of data remaining in the data buffer + /// </summary> + internal readonly int Remaining + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _dataSize - Position; + } + + /// <summary> + /// Reads data from the internal buffer into the supplied buffer + /// </summary> + /// <param name="buffer">The buffer to write data to</param> + /// <returns>The number of bytes written to the output buffer</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal readonly ERRNO Read(Span<byte> buffer) + { + //Calc how many bytes can be read into the output buffer + int bytesToRead = Math.Min(Remaining, buffer.Length); + + Span<byte> btr = DataSegment[Position..bytesToRead]; + + //Write data to output buffer + btr.CopyTo(buffer); + + //Update position pointer + Position += bytesToRead; + + //Return the number of bytes read + return bytesToRead; + } + + /// <summary> + /// Releases the internal buffer back to its pool + /// </summary> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal readonly void Release() + { + //Return buffer back to pool + _pool.Return(_buffer); + } + } +} diff --git a/lib/Utils/src/Extensions/MemoryExtensions.cs b/lib/Utils/src/Extensions/MemoryExtensions.cs index 17ad79d..237a567 100644 --- a/lib/Utils/src/Extensions/MemoryExtensions.cs +++ b/lib/Utils/src/Extensions/MemoryExtensions.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -28,10 +28,11 @@ using System.Buffers; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; +using VNLib.Utils.Memory; +using VNLib.Utils.Resources; + namespace VNLib.Utils.Extensions { - using Utils.Memory; - using VNLib.Utils.Resources; /// <summary> /// Provides memory based extensions to .NET and VNLib memory abstractions @@ -52,7 +53,7 @@ namespace VNLib.Utils.Extensions //Pool buffer handles are considered "safe" so im reusing code for now return new(pool, size, zero); } - + /// <summary> /// Retreives a buffer that is at least the reqested length, and clears the array from 0-size. /// <br></br> @@ -70,7 +71,7 @@ namespace VNLib.Utils.Extensions //If zero flag is set, zero only the used section if (zero) { - Array.Fill(arr, default); + arr.AsSpan().Clear(); } return arr; } |