diff options
Diffstat (limited to 'lib/Utils')
-rw-r--r-- | lib/Utils/src/Memory/MemoryHandle.cs | 38 | ||||
-rw-r--r-- | lib/Utils/tests/Memory/MemoryHandleTest.cs | 38 |
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); + } + } } } |