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/Utils/src/IO/VnMemoryStream.cs | 58 +++++++++++++++++++++++++++---- lib/Utils/tests/IO/VnMemoryStreamTests.cs | 44 +++++++++++++++++++++++ 2 files changed, 95 insertions(+), 7 deletions(-) (limited to 'lib/Utils') diff --git a/lib/Utils/src/IO/VnMemoryStream.cs b/lib/Utils/src/IO/VnMemoryStream.cs index 2c604b2..4d51a08 100644 --- a/lib/Utils/src/IO/VnMemoryStream.cs +++ b/lib/Utils/src/IO/VnMemoryStream.cs @@ -49,9 +49,13 @@ namespace VNLib.Utils.IO //Memory private readonly IResizeableMemoryHandle _buffer; + //Default owns handle private readonly bool OwnsHandle = true; + //Lazy loaded memory wrapper + private MemoryManager? _memoryWrapper; + /// /// Creates a new pointing to the begining of memory, and consumes the handle. /// @@ -259,27 +263,31 @@ namespace VNLib.Utils.IO cancellationToken.ThrowIfCancellationRequested(); + //Memory manager requires 32bit or less in length if(_length < Int32.MaxValue) { - //Safe to alloc a memory manager to do copy - using MemoryManager asMemManager = _buffer.ToMemoryManager(false); + //Get/alloc the internal memory manager and get the block + ReadOnlyMemory asMemory = AsMemory(); + + Debug.Assert(asMemory.Length >= LenToPosDiff, "Internal memory block smaller than desired for stream copy"); /* * CopyTo starts at the current position, as if calling Read() * so the reader must be offset to match and the _length gives us the * actual length of the stream and therefor the segment size - */ + */ - while(LenToPosDiff > 0) + while (LenToPosDiff > 0) { int blockSize = Math.Min((int)LenToPosDiff, bufferSize); - Memory window = asMemManager.Memory.Slice((int)_position, blockSize); + + ReadOnlyMemory window = asMemory.Slice((int)_position, blockSize); //write async await destination.WriteAsync(window, cancellationToken); //Update position - _position+= bufferSize; + _position += bufferSize; } } else @@ -581,6 +589,40 @@ namespace VNLib.Utils.IO //Get span with no offset return _buffer.AsSpan(0, len); } + + /// + /// Returns a structure which is a window of the buffered + /// data as it currently sits. For writeable straems, you must call this function + /// every time the size of the stream changes. The memory structure is just a "pointer" to + /// the internal buffer. + /// + /// + /// A memory snapshot of the stream. + /// + /// + /// This function causes an internal allocation on the first call. After the first call + /// to this function, all calls are thread-safe. + /// + public ReadOnlyMemory AsMemory() + { + /* + * Safe cast stram length to int, because memory window requires a 32bit + * integer. Also will throw before allocating the mmemory manager + */ + + int len = Convert.ToInt32(_length); + + //Defer/lazy init the memory manager + MemoryManager asMemory = LazyInitializer.EnsureInitialized(ref _memoryWrapper, AllocMemManager); + + Debug.Assert(asMemory != null); + + /* + * Buffer window may be larger than the actual stream legnth, so + * slice the memory to the actual length of the stream + */ + return asMemory.Memory[..len]; + } /// /// If the current stream is a readonly stream, creates a shallow copy for reading only. @@ -589,5 +631,7 @@ namespace VNLib.Utils.IO /// public object Clone() => GetReadonlyShallowCopy(); + private MemoryManager AllocMemManager() => _buffer.ToMemoryManager(false); + } -} \ No newline at end of file +} diff --git a/lib/Utils/tests/IO/VnMemoryStreamTests.cs b/lib/Utils/tests/IO/VnMemoryStreamTests.cs index 9742197..9bcb823 100644 --- a/lib/Utils/tests/IO/VnMemoryStreamTests.cs +++ b/lib/Utils/tests/IO/VnMemoryStreamTests.cs @@ -95,5 +95,49 @@ namespace VNLib.Utils.IO.Tests Assert.ThrowsException(() => vms.WriteByte(0)); } + + [TestMethod()] + public void GetMemOrSpanTest() + { + //Alloc stream with some initial buffer size + using VnMemoryStream vms = new(1024, false); + + //Ensure since no data was written, the returned windows are empty + Assert.IsTrue(vms.AsSpan().IsEmpty); + Assert.IsTrue(vms.AsMemory().IsEmpty); + + //Write some data + byte[] testData = [1, 2, 3, 4, 5, 6, 7, 8]; + vms.Write(testData); + + Assert.AreEqual(vms.Length, testData.Length); + + //Get the data as a span + ReadOnlySpan span = vms.AsSpan(); + Assert.AreEqual(span.Length, testData.Length); + + for (int i = 0; i < span.Length; i++) + { + Assert.AreEqual(span[i], testData[i]); + } + + //Get the data as a memory + ReadOnlyMemory memory = vms.AsMemory(); + Assert.AreEqual(memory.Length, testData.Length); + + for (int i = 0; i < memory.Length; i++) + { + Assert.AreEqual(memory.Span[i], testData[i]); + } + + //Get the data as a byte array + byte[] array = vms.ToArray(); + Assert.AreEqual(array.Length, testData.Length); + + for (int i = 0; i < array.Length; i++) + { + Assert.AreEqual(array[i], testData[i]); + } + } } } \ No newline at end of file -- cgit