aboutsummaryrefslogtreecommitdiff
path: root/lib/Utils
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2024-06-26 21:01:15 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2024-06-26 21:01:15 -0400
commit12391e9a207b60b41a074600fc2373ad3eb1c3ab (patch)
tree5a3396a889a2226aaabdf9aee5cd7cb4f5b04e31 /lib/Utils
parent92e182ceaf843f8d859d38faa8b2c0ff53207ff6 (diff)
feat(server): Server arch update, Memory struct access
Diffstat (limited to 'lib/Utils')
-rw-r--r--lib/Utils/src/IO/VnMemoryStream.cs58
-rw-r--r--lib/Utils/tests/IO/VnMemoryStreamTests.cs44
2 files changed, 95 insertions, 7 deletions
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<byte> _buffer;
+
//Default owns handle
private readonly bool OwnsHandle = true;
+ //Lazy loaded memory wrapper
+ private MemoryManager<byte>? _memoryWrapper;
+
/// <summary>
/// Creates a new <see cref="VnMemoryStream"/> pointing to the begining of memory, and consumes the handle.
/// </summary>
@@ -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<byte> asMemManager = _buffer.ToMemoryManager(false);
+ //Get/alloc the internal memory manager and get the block
+ ReadOnlyMemory<byte> 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<byte> window = asMemManager.Memory.Slice((int)_position, blockSize);
+
+ ReadOnlyMemory<byte> 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);
}
+
+ /// <summary>
+ /// Returns a <see cref="ReadOnlyMemory{T}"/> 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.
+ /// </summary>
+ /// <returns>
+ /// A memory snapshot of the stream.
+ /// </returns>
+ /// <remarks>
+ /// This function causes an internal allocation on the first call. After the first call
+ /// to this function, all calls are thread-safe.
+ /// </remarks>
+ public ReadOnlyMemory<byte> 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<byte> 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];
+ }
/// <summary>
/// If the current stream is a readonly stream, creates a shallow copy for reading only.
@@ -589,5 +631,7 @@ namespace VNLib.Utils.IO
/// <exception cref="NotSupportedException"></exception>
public object Clone() => GetReadonlyShallowCopy();
+ private MemoryManager<byte> 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<NotSupportedException>(() => 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<byte> 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<byte> 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