aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2023-05-09 13:07:35 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2023-05-09 13:07:35 -0400
commit067c692800970e6fc41fbd0df669a6b1d6a07c55 (patch)
tree02f8bc01e00cab8d5b1a3a9135c952d9397778ca
parent0cf4c6227ddfada470a3137cf551f84aff1b3a18 (diff)
Fix exceptions, more test coverage, fix sequences, heap flags, and spelling
-rw-r--r--lib/Utils/src/Async/IAsyncAccessSerializer.cs2
-rw-r--r--lib/Utils/src/Extensions/MemoryExtensions.cs2
-rw-r--r--lib/Utils/src/Memory/MemoryUtil.cs44
-rw-r--r--lib/Utils/src/Memory/SubSequence.cs87
-rw-r--r--lib/Utils/tests/Memory/MemoryHandleTest.cs3
-rw-r--r--lib/Utils/tests/Memory/SubSequenceTest.cs103
6 files changed, 189 insertions, 52 deletions
diff --git a/lib/Utils/src/Async/IAsyncAccessSerializer.cs b/lib/Utils/src/Async/IAsyncAccessSerializer.cs
index 5dce3cd..75f5dd6 100644
--- a/lib/Utils/src/Async/IAsyncAccessSerializer.cs
+++ b/lib/Utils/src/Async/IAsyncAccessSerializer.cs
@@ -28,7 +28,7 @@ using System.Threading.Tasks;
namespace VNLib.Utils.Async
{
/// <summary>
- /// A mutual exclusion primitive that provides asynchronous waites for serialized
+ /// A mutual exclusion primitive that provides asynchronous waits for serialized
/// access to a resource based on a moniker. Similar to the <see cref="Monitor"/>
/// class.
/// </summary>
diff --git a/lib/Utils/src/Extensions/MemoryExtensions.cs b/lib/Utils/src/Extensions/MemoryExtensions.cs
index 237a567..0e898b4 100644
--- a/lib/Utils/src/Extensions/MemoryExtensions.cs
+++ b/lib/Utils/src/Extensions/MemoryExtensions.cs
@@ -308,6 +308,7 @@ namespace VNLib.Utils.Extensions
/// <param name="offset">An offset within the handle</param>
/// <param name="size">The size of the window</param>
/// <returns>The new <see cref="SubSequence{T}"/> within the block</returns>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static SubSequence<T> GetSubSequence<T>(this MemoryHandle<T> block, nuint offset, int size) where T : unmanaged => new (block, offset, size);
@@ -319,6 +320,7 @@ namespace VNLib.Utils.Extensions
/// <param name="offset">An offset within the handle</param>
/// <param name="size">The size of the window</param>
/// <returns>The new <see cref="SubSequence{T}"/> within the block</returns>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static SubSequence<T> GetSubSequence<T>(this MemoryHandle<T> block, nint offset, int size) where T : unmanaged
{
diff --git a/lib/Utils/src/Memory/MemoryUtil.cs b/lib/Utils/src/Memory/MemoryUtil.cs
index e802360..1be46e4 100644
--- a/lib/Utils/src/Memory/MemoryUtil.cs
+++ b/lib/Utils/src/Memory/MemoryUtil.cs
@@ -81,6 +81,8 @@ namespace VNLib.Utils.Memory
/// heap.
/// </summary>
public const int MAX_UNSAFE_POOL_SIZE = 80 * 1024;
+
+ private static readonly int SystemPageSize = Environment.SystemPageSize;
/// <summary>
/// Provides a shared heap instance for the process to allocate memory from.
@@ -166,19 +168,19 @@ namespace VNLib.Utils.Memory
IUnmangedHeap heap;
+ ERRNO userFlags = 0;
+
+ //Try to parse the raw flags to pass to the heap
+ if (nint.TryParse(rawFlagsEnv, NumberStyles.HexNumber, null, out nint result))
+ {
+ userFlags = new(result);
+ }
+
//Check for heap api dll
if (!string.IsNullOrWhiteSpace(heapDllPath))
{
- ERRNO rawFlags = 0;
-
- //Try to parse the raw flags to pass to the heap
- if (nint.TryParse(rawFlagsEnv, NumberStyles.HexNumber, null, out nint result))
- {
- rawFlags = new(result);
- }
-
//Attempt to load the heap
- heap = NativeHeap.LoadHeap(heapDllPath, DllImportSearchPath.SafeDirectories, cFlags, rawFlags);
+ heap = NativeHeap.LoadHeap(heapDllPath, DllImportSearchPath.SafeDirectories, cFlags, userFlags);
}
//No user heap was specified, use fallback
else if (IsWindows)
@@ -192,10 +194,10 @@ namespace VNLib.Utils.Memory
if (!nuint.TryParse(sharedSize, out nuint defaultSize))
{
defaultSize = SHARED_HEAP_INIT_SIZE;
- }
+ }
//Create win32 private heap
- heap = Win32PrivateHeap.Create(defaultSize, cFlags);
+ heap = Win32PrivateHeap.Create(defaultSize, cFlags, flags:userFlags);
}
else
{
@@ -591,7 +593,7 @@ namespace VNLib.Utils.Memory
{
if (offset + count > handle.Length)
{
- throw new ArgumentException("The offset or count is outside of the range of the block of memory");
+ throw new ArgumentOutOfRangeException("The offset or count is outside of the range of the block of memory");
}
}
@@ -686,6 +688,16 @@ namespace VNLib.Utils.Memory
public static Span<T> GetSpan<T>(IntPtr address, int size) => new(address.ToPointer(), size);
/// <summary>
+ /// Gets a <see cref="Span{T}"/> over the block of memory pointed to by the supplied handle.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="handle"></param>
+ /// <param name="size">The size of the span (the size of the block)</param>
+ /// <returns>A span over the block of memory pointed to by the handle of the specified size</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Span<T> GetSpan<T>(MemoryHandle handle, int size) => new(handle.Pointer, size);
+
+ /// <summary>
/// Rounds the requested byte size up to the nearest page
/// number of bytes
/// </summary>
@@ -695,10 +707,10 @@ namespace VNLib.Utils.Memory
public static nuint NearestPage(nuint byteSize)
{
//Get page count by dividing count by number of pages
- nuint pages = (uint)Math.Ceiling(byteSize / (double)Environment.SystemPageSize);
+ nuint pages = (uint)Math.Ceiling(byteSize / (double)SystemPageSize);
//Multiply back to page sizes
- return pages * (nuint)Environment.SystemPageSize;
+ return pages * (nuint)SystemPageSize;
}
/// <summary>
@@ -711,10 +723,10 @@ namespace VNLib.Utils.Memory
public static nint NearestPage(nint byteSize)
{
//Get page count by dividing count by number of pages
- nint pages = (int)Math.Ceiling(byteSize / (double)Environment.SystemPageSize);
+ nint pages = (int)Math.Ceiling(byteSize / (double)SystemPageSize);
//Multiply back to page sizes
- return pages * Environment.SystemPageSize;
+ return pages * SystemPageSize;
}
}
} \ No newline at end of file
diff --git a/lib/Utils/src/Memory/SubSequence.cs b/lib/Utils/src/Memory/SubSequence.cs
index 87e369b..4e3c22c 100644
--- a/lib/Utils/src/Memory/SubSequence.cs
+++ b/lib/Utils/src/Memory/SubSequence.cs
@@ -32,10 +32,19 @@ namespace VNLib.Utils.Memory
/// Represents a subset (or window) of data within a <see cref="MemoryHandle{T}"/>
/// </summary>
/// <typeparam name="T">The unmanaged type to wrap</typeparam>
- public readonly struct SubSequence<T> : IEquatable<SubSequence<T>> where T: unmanaged
+ public readonly record struct SubSequence<T> where T: unmanaged
{
- private readonly MemoryHandle<T> _handle;
- private readonly nuint _offset;
+ readonly nuint _offset;
+
+ /// <summary>
+ /// The handle that owns the memory block
+ /// </summary>
+ public readonly MemoryHandle<T> Handle { get; }
+
+ /// <summary>
+ /// The number of elements in the current sequence
+ /// </summary>
+ public readonly int Size { get; }
/// <summary>
/// Creates a new <see cref="SubSequence{T}"/> to the handle to get a window of the block
@@ -43,23 +52,25 @@ namespace VNLib.Utils.Memory
/// <param name="block"></param>
/// <param name="offset"></param>
/// <param name="size"></param>
+ /// <exception cref="ArgumentNullException"></exception>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
public SubSequence(MemoryHandle<T> block, nuint offset, int size)
{
- _offset = offset;
+ Handle = block ?? throw new ArgumentNullException(nameof(block));
Size = size >= 0 ? size : throw new ArgumentOutOfRangeException(nameof(size));
- _handle = block ?? throw new ArgumentNullException(nameof(block));
- }
+ _offset = offset;
- /// <summary>
- /// The number of elements in the current window
- /// </summary>
- public readonly int Size { get; }
+ //Check handle bounds
+ MemoryUtil.CheckBounds(block, offset, (uint)size);
+ }
+
/// <summary>
/// Gets a <see cref="Span{T}"/> that is offset from the base of the handle
+ /// and the size of the current sequence
/// </summary>
/// <exception cref="ArgumentOutOfRangeException"></exception>
- public readonly Span<T> Span => Size > 0 ? _handle.GetOffsetSpan(_offset, Size) : Span<T>.Empty;
+ public readonly Span<T> Span => Size > 0 ? Handle.GetOffsetSpan(_offset, Size) : Span<T>.Empty;
/// <summary>
/// Slices the current sequence into a smaller <see cref="SubSequence{T}"/>
@@ -67,34 +78,40 @@ namespace VNLib.Utils.Memory
/// <param name="offset">The relative offset from the current window offset</param>
/// <param name="size">The size of the block</param>
/// <returns>A <see cref="SubSequence{T}"/> of the current sequence</returns>
- public readonly SubSequence<T> Slice(nuint offset, int size) => new (_handle, checked(_offset + offset), size);
-
- /// <summary>
- /// Returns the signed 32-bit hashcode
- /// </summary>
- /// <returns>A signed 32-bit integer that represents the hashcode for the current instance</returns>
- /// <exception cref="ObjectDisposedException"></exception>
- public readonly override int GetHashCode() => _handle.GetHashCode() + _offset.GetHashCode();
+ public readonly SubSequence<T> Slice(nuint offset, int size)
+ {
+ //Calc offset
+ nuint newOffset = checked(_offset + offset);
+
+ //Cal max size after the slice
+ int newMaxSize = Size - (int)offset;
- ///<inheritdoc/>
- public readonly bool Equals(SubSequence<T> other) => Span.SequenceEqual(other.Span);
+ if(newMaxSize < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(offset));
+ }
- ///<inheritdoc/>
- public readonly override bool Equals(object? obj) => obj is SubSequence<T> other && Equals(other);
+ if(size > newMaxSize)
+ {
+ throw new ArgumentOutOfRangeException(nameof(size));
+ }
+ return new SubSequence<T>(Handle, newOffset, size > newMaxSize ? newMaxSize : size);
+ }
/// <summary>
- /// Determines if two <see cref="SubSequence{T}"/> are equal
- /// </summary>
- /// <param name="left"></param>
- /// <param name="right"></param>
- /// <returns>True if the sequences are equal, false otherwise</returns>
- public static bool operator ==(SubSequence<T> left, SubSequence<T> right) => left.Equals(right);
- /// <summary>
- /// Determines if two <see cref="SubSequence{T}"/> are not equal
+ /// Slices the current sequence into a smaller <see cref="SubSequence{T}"/>
/// </summary>
- /// <param name="left"></param>
- /// <param name="right"></param>
- /// <returns>True if the sequences are not equal, false otherwise</returns>
- public static bool operator !=(SubSequence<T> left, SubSequence<T> right) => !left.Equals(right);
+ /// <param name="offset">The relative offset from the current window offset</param>
+ /// <returns>A <see cref="SubSequence{T}"/> of the current sequence</returns>
+ public readonly SubSequence<T> Slice(nuint offset)
+ {
+ //Calc offset
+ nuint newOffset = _offset + offset;
+
+ //Calc the new max size of the block (let constructor handle the exception if less than 0)
+ int newMaxSize = (int)((nuint)Size - offset);
+
+ return new SubSequence<T>(Handle, newOffset, newMaxSize);
+ }
}
} \ No newline at end of file
diff --git a/lib/Utils/tests/Memory/MemoryHandleTest.cs b/lib/Utils/tests/Memory/MemoryHandleTest.cs
index f8d9b79..212eb0c 100644
--- a/lib/Utils/tests/Memory/MemoryHandleTest.cs
+++ b/lib/Utils/tests/Memory/MemoryHandleTest.cs
@@ -23,6 +23,7 @@
*/
using System;
+
using Microsoft.VisualStudio.TestTools.UnitTesting;
using VNLib.Utils.Extensions;
@@ -31,6 +32,8 @@ using static VNLib.Utils.Memory.MemoryUtil;
namespace VNLib.Utils.Memory.Tests
{
+
+
[TestClass]
public class MemoryHandleTest
{
diff --git a/lib/Utils/tests/Memory/SubSequenceTest.cs b/lib/Utils/tests/Memory/SubSequenceTest.cs
new file mode 100644
index 0000000..7ef1da9
--- /dev/null
+++ b/lib/Utils/tests/Memory/SubSequenceTest.cs
@@ -0,0 +1,103 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.UtilsTests
+* File: MemoryHandleTest.cs
+*
+* MemoryHandleTest.cs is part of VNLib.UtilsTests which is part of the larger
+* VNLib collection of libraries and utilities.
+*
+* VNLib.UtilsTests is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published
+* by the Free Software Foundation, either version 2 of the License,
+* or (at your option) any later version.
+*
+* VNLib.UtilsTests is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.UtilsTests. If not, see http://www.gnu.org/licenses/.
+*/
+
+using System;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using VNLib.Utils.Extensions;
+
+namespace VNLib.Utils.Memory.Tests
+{
+ [TestClass]
+ public class SubSequenceTest
+ {
+ [TestMethod]
+ public void SubSequenceSliceTest()
+ {
+ const int TestHandleSize = 8192;
+
+ //Alloc handle
+ using MemoryHandle<byte> handle = MemoryUtil.Shared.Alloc<byte>(TestHandleSize, false);
+
+ //Should be able to get an empty span at the beginning
+ Assert.IsTrue(_ = handle.GetSubSequence(0, 0).Span.IsEmpty);
+
+ //Should be able to get an empty span at the end
+ Assert.IsTrue(_ = handle.GetSubSequence(8192, 0).Span.IsEmpty);
+
+ //Test extension bounds checking, may defer to the sequence itself
+
+ //Overrun the handle by offset
+ Assert.ThrowsException<ArgumentOutOfRangeException>(() => _ = handle.GetSubSequence(8193, 1).Span);
+
+ //Overrun the handle by size
+ Assert.ThrowsException<ArgumentOutOfRangeException>(() => _ = handle.GetSubSequence(0, 8193).Span);
+
+ //Overrun the handle by size at the end of a valid handle
+ Assert.ThrowsException<ArgumentOutOfRangeException>(() => _ = handle.GetSubSequence(8192, 1).Span);
+
+ //Negative offset
+ Assert.ThrowsException<ArgumentOutOfRangeException>(() => _ = handle.GetSubSequence(0, -1).Span);
+
+
+ //Test slicing
+
+ SubSequence<byte> full = handle.GetSubSequence(0, TestHandleSize);
+
+ Assert.IsTrue(full.Span.Length == TestHandleSize);
+
+ Assert.ThrowsException<ArgumentOutOfRangeException>(() => _ = full.Slice(0, -1).Span);
+
+ Assert.ThrowsException<ArgumentOutOfRangeException>(() => _ = full.Slice(8192, 1).Span);
+
+ //Test slicing with offset only, size should be the remainder of the handle
+ Assert.IsTrue(full.Slice(100).Span.Length == (TestHandleSize - 100));
+
+ //Slice of slice
+ SubSequence<byte> slice = full.Slice(8190, 2);
+
+ Assert.IsTrue(slice.Span.Length == 2);
+
+ //Allow slice of the exact same size or smaller
+ Assert.IsTrue(slice.Slice(0, 2).Span.Length == 2);
+
+ Assert.ThrowsException<ArgumentOutOfRangeException>(() => _ = slice.Slice(0, 3).Span);
+
+ //Allow empty slice at the end
+ Assert.IsTrue(slice.Slice(2, 0).Span.IsEmpty);
+
+ //Overrun by offset
+ Assert.ThrowsException<ArgumentOutOfRangeException>(() => _ = slice.Slice(3, 0).Span);
+
+ //Overrun by size
+ Assert.ThrowsException<ArgumentOutOfRangeException>(() => _ = slice.Slice(0, 3).Span);
+
+ //Overrun by size at the end of a valid slice
+ Assert.ThrowsException<ArgumentOutOfRangeException>(() => _ = slice.Slice(2, 1).Span);
+
+
+ }
+ }
+}