From 12391e9a207b60b41a074600fc2373ad3eb1c3ab Mon Sep 17 00:00:00 2001 From: vnugent Date: Wed, 26 Jun 2024 21:01:15 -0400 Subject: feat(server): Server arch update, Memory struct access --- lib/Plugins.Essentials/src/HttpEntity.cs | 75 +++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) (limited to 'lib/Plugins.Essentials/src/HttpEntity.cs') diff --git a/lib/Plugins.Essentials/src/HttpEntity.cs b/lib/Plugins.Essentials/src/HttpEntity.cs index ff728e3..a4788a3 100644 --- a/lib/Plugins.Essentials/src/HttpEntity.cs +++ b/lib/Plugins.Essentials/src/HttpEntity.cs @@ -30,7 +30,9 @@ using System.Diagnostics; using System.Collections.Generic; using System.Runtime.CompilerServices; +using VNLib.Utils.IO; using VNLib.Net.Http; +using VNLib.Plugins.Essentials.Content; using VNLib.Plugins.Essentials.Sessions; using VNLib.Plugins.Essentials.Extensions; @@ -220,7 +222,41 @@ namespace VNLib.Plugins.Essentials Entity.CloseResponse( code, type, - new MemStreamWrapper(ms, (int)length) + entity: new MemStreamWrapper(ms, (int)length) + ); + + return; + } + + /* + * Readonly vn streams can also use a shortcut to avoid http buffer allocation and + * async streaming. This is done by wrapping the stream in a memory response reader + * + * Allocating a memory manager requires that the stream is readonly + */ + if (stream is VnMemoryStream vms && length < int.MaxValue) + { + Entity.CloseResponse( + code, + type, + entity: new VnStreamWrapper(vms, (int)length) + ); + + return; + } + + /* + * Files can have a bit more performance using the RandomAccess library when reading + * sequential segments without buffering. It avoids a user-space copy and async reading + * performance without the file being opened as async. + */ + if(stream is FileStream fs) + { + Entity.CloseResponse( + code, + type, + entity: new DirectFileStream(fs.SafeFileHandle), + length ); return; @@ -270,6 +306,40 @@ namespace VNLib.Plugins.Essentials void IHttpEvent.DangerousChangeProtocol(IAlternateProtocol protocolHandler) => Entity.DangerousChangeProtocol(protocolHandler); + private sealed class VnStreamWrapper(VnMemoryStream memStream, int length) : IMemoryResponseReader + { + //Store memory buffer, causes an internal allocation, so avoid calling mutliple times + readonly ReadOnlyMemory _memory = memStream.AsMemory(); + + readonly int length = length; + + /* + * Stream may be offset by the caller, it needs + * to be respected during streaming. + */ + int read = (int)memStream.Position; + + /// + public int Remaining + { + get + { + Debug.Assert(length - read >= 0); + return length - read; + } + } + + /// + public void Advance(int written) => read += written; + + /// + public void Close() => memStream.Dispose(); + + /// + public ReadOnlyMemory GetMemory() => _memory.Slice(read, Remaining); + } + + private sealed class MemStreamWrapper(MemoryStream memStream, int length) : IMemoryResponseReader { readonly int length = length; @@ -280,6 +350,7 @@ namespace VNLib.Plugins.Essentials */ int read = (int)memStream.Position; + /// public int Remaining { get @@ -289,11 +360,13 @@ namespace VNLib.Plugins.Essentials } } + /// public void Advance(int written) => read += written; /// public void Close() => memStream.Dispose(); + /// public ReadOnlyMemory GetMemory() { byte[] intBuffer = memStream.GetBuffer(); -- cgit