aboutsummaryrefslogtreecommitdiff
path: root/lib/Utils
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Utils')
-rw-r--r--lib/Utils/src/Memory/MemoryHandle.cs38
-rw-r--r--lib/Utils/tests/Memory/MemoryHandleTest.cs38
2 files changed, 68 insertions, 8 deletions
diff --git a/lib/Utils/src/Memory/MemoryHandle.cs b/lib/Utils/src/Memory/MemoryHandle.cs
index 7a6b4ef..7a7cb6a 100644
--- a/lib/Utils/src/Memory/MemoryHandle.cs
+++ b/lib/Utils/src/Memory/MemoryHandle.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Utils
@@ -71,7 +71,6 @@ namespace VNLib.Utils.Memory
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
- this.ThrowIfClosed();
int len = Convert.ToInt32(_length);
return _length == 0 ? Span<T>.Empty : new Span<T>(Base, len);
}
@@ -116,6 +115,22 @@ namespace VNLib.Utils.Memory
handle = initial;
}
+ /*
+ * Empty handle will disable release, and because the
+ * handle pointer is 0, its considered invalid and will now
+ * allow
+ */
+ /// <summary>
+ /// Initialzies an empty memory handle. Properties will raise exceptions
+ /// when accessed, however <see cref="IMemoryHandle{T}"/> operations are
+ /// considered "safe" meaning they should never raise excpetions
+ /// </summary>
+ public MemoryHandle():base(false)
+ {
+ _length = 0;
+ Heap = null!;
+ }
+
/// <summary>
/// Resizes the current handle on the heap
/// </summary>
@@ -131,6 +146,12 @@ namespace VNLib.Utils.Memory
//Re-alloc (Zero if required)
try
{
+ /*
+ * If resize raises an exception the current block pointer
+ * should still be valid, if its not, the pointer should
+ * be set to 0/-1, which will be considered invalid anyway
+ */
+
Heap.Resize(ref handle, Length, (nuint)sizeof(T), ZeroMemory);
}
//Catch the disposed exception so we can invalidate the current ptr
@@ -143,6 +164,7 @@ namespace VNLib.Utils.Memory
throw;
}
}
+
/// <summary>
/// Gets an offset pointer from the base postion to the number of bytes specified. Performs bounds checks
/// </summary>
@@ -195,7 +217,7 @@ namespace VNLib.Utils.Memory
///<inheritdoc/>
protected override bool ReleaseHandle()
{
- //Return result of free
+ //Return result of free, only if the handle is valid
return Heap.Free(ref handle);
}
@@ -206,14 +228,14 @@ namespace VNLib.Utils.Memory
/// <returns>true if the block of memory is the same, false if the handle's size does not
/// match or the base addresses do not match even if they point to an overlapping address space</returns>
/// <exception cref="ObjectDisposedException"></exception>
- public bool Equals(MemoryHandle<T> other)
+ public bool Equals(MemoryHandle<T>? other)
{
- this.ThrowIfClosed();
- other.ThrowIfClosed();
- return _length == other._length && handle == other.handle;
+ return other != null && IsClosed == other.IsClosed && _length == other._length && handle == other.handle;
}
+
///<inheritdoc/>
- public override bool Equals(object obj) => obj is MemoryHandle<T> oHandle && Equals(oHandle);
+ public override bool Equals(object? obj) => obj is MemoryHandle<T> oHandle && Equals(oHandle);
+
///<inheritdoc/>
public override int GetHashCode() => base.GetHashCode();
diff --git a/lib/Utils/tests/Memory/MemoryHandleTest.cs b/lib/Utils/tests/Memory/MemoryHandleTest.cs
index d890757..f7ab8d4 100644
--- a/lib/Utils/tests/Memory/MemoryHandleTest.cs
+++ b/lib/Utils/tests/Memory/MemoryHandleTest.cs
@@ -176,5 +176,43 @@ namespace VNLib.Utils.Memory.Tests
handle.ResizeIfSmaller(4096);
Assert.IsTrue(handle.Length == 4096);
}
+
+ [TestMethod]
+ public unsafe void EmptyHandleTest()
+ {
+ //Confirm that an empty handle does not raise exceptions when in IMemoryHandle
+ using (IMemoryHandle<byte> thandle = new MemoryHandle<byte>())
+ {
+ Assert.IsTrue(thandle.Length == 0);
+
+ Assert.IsTrue(thandle.Span == Span<byte>.Empty);
+
+ //Empty span should not throw
+ _ = thandle.AsSpan(0);
+
+ //Pin should throw
+ Assert.ThrowsException<ArgumentOutOfRangeException>(() => _ = thandle.Pin(0));
+ }
+
+ //Full ref to mhandle check status
+ using (MemoryHandle<byte> mHandle = new())
+ {
+
+ //Some members should not throw
+ _ = mHandle.ByteLength;
+
+ //Handle should be invalid
+ Assert.IsTrue(mHandle.IsInvalid);
+
+ Assert.IsFalse(mHandle.IsClosed);
+
+ //Confirm empty handle protected values throw
+ Assert.ThrowsException<ArgumentOutOfRangeException>(() => _ = mHandle.GetOffset(0));
+
+ Assert.ThrowsException<ObjectDisposedException>(() => mHandle.Resize(10));
+
+ Assert.ThrowsException<ArgumentOutOfRangeException>(() => mHandle.BasePtr);
+ }
+ }
}
}