aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Module.Taskfile.yaml54
-rw-r--r--lib/Plugins.Essentials/src/EventProcessor.cs4
-rw-r--r--lib/Plugins.Runtime/src/LivePlugin.cs40
-rw-r--r--lib/Utils/src/ERRNO.cs25
-rw-r--r--lib/Utils/src/Extensions/CacheExtensions.cs77
-rw-r--r--lib/Utils/src/IO/TemporayIsolatedFile.cs57
-rw-r--r--lib/Utils/src/Memory/ArrayPoolBuffer.cs30
-rw-r--r--lib/Utils/src/Memory/ForwardOnlyMemoryReader.cs8
-rw-r--r--lib/Utils/src/Memory/ForwardOnlyMemoryWriter.cs69
-rw-r--r--lib/Utils/src/Memory/MemoryUtil.cs143
-rw-r--r--lib/Utils/src/Memory/NativeHeap.cs17
-rw-r--r--lib/Utils/src/Memory/PrivateBuffersMemoryPool.cs14
-rw-r--r--lib/Utils/src/Memory/PrivateStringManager.cs23
-rw-r--r--lib/Utils/src/Memory/SubSequence.cs7
-rw-r--r--lib/Utils/src/Memory/VnString.cs164
-rw-r--r--lib/Utils/src/Native/SafeLibraryHandle.cs3
-rw-r--r--lib/Utils/src/Resources/ManagedLibrary.cs80
-rw-r--r--lib/Utils/tests/.runsettings3
-rw-r--r--lib/Utils/tests/IO/VnMemoryStreamTests.cs15
-rw-r--r--lib/Utils/tests/Memory/MemoryHandleTest.cs2
-rw-r--r--lib/Utils/tests/Memory/MemoryUtilTests.cs116
-rw-r--r--lib/Utils/tests/Memory/NativeHeapTests.cs10
22 files changed, 548 insertions, 413 deletions
diff --git a/Module.Taskfile.yaml b/Module.Taskfile.yaml
index 6ec8fc6..99ea9b2 100644
--- a/Module.Taskfile.yaml
+++ b/Module.Taskfile.yaml
@@ -10,42 +10,68 @@
version: '3'
-vars:
- INT_DIR: '{{ .SCRATCH_DIR }}/obj/{{ .MODULE_NAME }}/'
- MS_ARGS: '/p:RunAnalyzersDuringBuild=false /p:IntermediateOutputPath="{{.INT_DIR}}" /p:UseCommonOutputDirectory=true /p:BuildInParallel=true /p:MultiProcessorCompilation=true /p:ErrorOnDuplicatePublishOutputFiles=false'
+vars:
+ MS_ARGS: '/p:RunAnalyzersDuringBuild=false /p:UseCommonOutputDirectory=true /p:BuildInParallel=true /p:MultiProcessorCompilation=true /p:ErrorOnDuplicatePublishOutputFiles=false'
PACK_OUT: '{{ .OUTPUT_DIR }}/{{ .HEAD_SHA }}/pkg'
tasks:
+ default:
+ desc: 'Builds the managed libraries in this module for development'
+ cmds:
+ - dotnet build -c debug {{ .MS_ARGS }}
+
+
#called by build pipeline to sync repo
update:
cmds:
- git reset --hard #clean up any local changes
- - git remote update
+ - git remote update
- git pull origin {{ .BRANCH_NAME }} --verify-signatures
#re-write semver after hard reset
- dotnet-gitversion.exe /updateprojectfiles
#called by build pipeline to build module
build:
+ desc: "Used by vnbuild to build the entire module at CI time"
+ vars:
+ INT_DIR: '{{ .SCRATCH_DIR }}/obj/{{ .MODULE_NAME }}/'
+ MS_ARGS: '{{ .MS_ARGS }} /p:IntermediateOutputPath="{{ .INT_DIR }}"'
cmds:
- - echo "building module {{.MODULE_NAME}}"
-
+ - cmd: echo "building module {{ .MODULE_NAME }}"
+ silent: true
+
#build debug mode first
- task: build_debug
+ vars: { MS_ARGS: '{{ .MS_ARGS }}'}
- task: build_release
+ vars: { MS_ARGS: '{{ .MS_ARGS }}'}
publish:
+ desc: "Used by vnbuild to prepare the packages for build servers"
cmds:
#git archive in the module directory
- - git archive --format {{.ARCHIVE_FILE_FORMAT}} --output {{.ARCHIVE_FILE_NAME}} HEAD
+ - git archive --format {{ .ARCHIVE_FILE_FORMAT }} --output {{ .ARCHIVE_FILE_NAME }} HEAD
#push packages to the sleet feed (feed path is vnbuild global)
- - sleet push "{{.PACK_OUT}}/debug/" --source debug --config "{{.SLEET_CONFIG_PATH}}" --force
- - sleet push "{{.PACK_OUT}}/release/" --source release --config "{{.SLEET_CONFIG_PATH}}" --force
+ - sleet push "{{ .PACK_OUT }}/debug/" --source debug --config "{{ .SLEET_CONFIG_PATH }}" --force
+ - sleet push "{{ .PACK_OUT }}/release/" --source release --config "{{ .SLEET_CONFIG_PATH }}" --force
test:
+ desc: "Runs managed tests against the entire solution and all loaded test projects"
+ vars:
+ RPMALLOC_LIB_PATH: '{{ .USER_WORKING_DIR }}/lib/Utils.Memory/vnlib_rpmalloc/build/Debug/vnlib_rpmalloc'
+ MIMALLOC_LIB_PATH: '{{ .USER_WORKING_DIR }}/lib/Utils.Memory/vnlib_mimalloc/build/Debug/vnlib_mimalloc'
cmds:
- - cmd: dotnet test --verbosity normal
+ - cmd: echo "Ensure you have run 'task dev-init' before running tests to build native libraries"
+ silent: true
+ - cmd: dotnet test
+ {{ .CLI_ARGS }}
+ --verbosity normal
+ --framework {{ .FRAMEWORK | default "net8.0" }}
+ --environment VNLIB_SHARED_HEAP_DIAGNOSTICS="1"
+ --environment TEST_RPMALLOC_LIB_PATH="{{ .RPMALLOC_LIB_PATH }}"
+ --environment TEST_MIMALLOC_LIB_PATH="{{ .MIMALLOC_LIB_PATH }}"
+
#called by build pipeline to clean module
clean:
@@ -72,12 +98,12 @@ tasks:
build_debug:
internal: true
cmds:
- - dotnet publish -c debug {{.MS_ARGS}}
- - dotnet pack -c debug {{.MS_ARGS}} -o "{{.PACK_OUT}}/debug/" -p:PackageVersion={{.BUILD_VERSION}}
+ - dotnet publish -c debug {{ .MS_ARGS }}
+ - dotnet pack -c debug {{ .MS_ARGS }} -o "{{ .PACK_OUT }}/debug/" -p:PackageVersion={{ .BUILD_VERSION }}
build_release:
internal: true
cmds:
- - dotnet publish -c release {{.MS_ARGS}}
- - dotnet pack -c release {{.MS_ARGS}} -o "{{.PACK_OUT}}/release/" -p:PackageVersion={{.BUILD_VERSION}}
+ - dotnet publish -c release {{ .MS_ARGS }}
+ - dotnet pack -c release {{ .MS_ARGS }} -o "{{ .PACK_OUT }}/release/" -p:PackageVersion={{ .BUILD_VERSION }}
\ No newline at end of file
diff --git a/lib/Plugins.Essentials/src/EventProcessor.cs b/lib/Plugins.Essentials/src/EventProcessor.cs
index 908ad07..c3082a1 100644
--- a/lib/Plugins.Essentials/src/EventProcessor.cs
+++ b/lib/Plugins.Essentials/src/EventProcessor.cs
@@ -416,8 +416,12 @@ namespace VNLib.Plugins.Essentials
//set last modified time as the files last write time
entity.Server.LastModified(fileLastModified);
+
//Open the file handle directly, reading will always be sequentially read and async
+
+#pragma warning disable CA2000 // Dispose objects before losing scope
DirectFileStream dfs = DirectFileStream.Open(filename);
+#pragma warning restore CA2000 // Dispose objects before losing scope
long endOffset = checked((long)entity.Server.Range.End);
long startOffset = checked((long)entity.Server.Range.Start);
diff --git a/lib/Plugins.Runtime/src/LivePlugin.cs b/lib/Plugins.Runtime/src/LivePlugin.cs
index 3ed2ad6..26276dd 100644
--- a/lib/Plugins.Runtime/src/LivePlugin.cs
+++ b/lib/Plugins.Runtime/src/LivePlugin.cs
@@ -26,6 +26,7 @@ using System;
using System.Linq;
using System.Reflection;
+using VNLib.Utils.Resources;
using VNLib.Plugins.Attributes;
namespace VNLib.Plugins.Runtime
@@ -76,18 +77,14 @@ namespace VNLib.Plugins.Runtime
Plugin = plugin;
OriginAsm = originAsm;
PluginType = plugin.GetType();
- GetConsoleHandler();
+ PluginConsoleHandler = GetConsoleHandler(plugin);
}
- private void GetConsoleHandler()
+ private static ConsoleEventHandlerSignature? GetConsoleHandler(IPlugin plugin)
{
- //Get the console handler method from the plugin instance
- MethodInfo? handler = (from m in PluginType.GetMethods()
- where m.GetCustomAttribute<ConsoleEventHandlerAttribute>() != null
- select m)
- .FirstOrDefault();
//Get a delegate handler for the plugin
- PluginConsoleHandler = handler?.CreateDelegate<ConsoleEventHandlerSignature>(Plugin);
+ return ManagedLibrary.GetMethodsWithAttribute<ConsoleEventHandlerAttribute, ConsoleEventHandlerSignature>(plugin)
+ .FirstOrDefault();
}
/// <summary>
@@ -97,18 +94,9 @@ namespace VNLib.Plugins.Runtime
/// <param name="configData">The host configuration DOM</param>
internal void InitConfig(ReadOnlySpan<byte> configData)
{
- //Get the console handler method from the plugin instance
- MethodInfo? confHan = PluginType.GetMethods().Where(static m => m.GetCustomAttribute<ConfigurationInitalizerAttribute>() != null)
- .FirstOrDefault();
- //Get a delegate handler for the plugin
- ConfigInitializer? configInit = confHan?.CreateDelegate<ConfigInitializer>(Plugin);
- if (configInit == null)
- {
- return;
- }
-
- //Invoke
- configInit.Invoke(configData);
+ ManagedLibrary.GetMethodsWithAttribute<ConfigurationInitalizerAttribute, ConfigInitializer>(Plugin!)
+ .FirstOrDefault()
+ ?.Invoke(configData);
}
/// <summary>
@@ -118,15 +106,9 @@ namespace VNLib.Plugins.Runtime
/// <param name="cliArgs">The current process's CLI args</param>
internal void InitLog(string[] cliArgs)
{
- //Get the console handler method from the plugin instance
- MethodInfo? logInit = (from m in PluginType.GetMethods()
- where m.GetCustomAttribute<LogInitializerAttribute>() != null
- select m)
- .FirstOrDefault();
- //Get a delegate handler for the plugin
- LogInitializer? logFunc = logInit?.CreateDelegate<LogInitializer>(Plugin);
- //Invoke
- logFunc?.Invoke(cliArgs);
+ ManagedLibrary.GetMethodsWithAttribute<LogInitializerAttribute, LogInitializer>(Plugin!)
+ .FirstOrDefault()
+ ?.Invoke(cliArgs);
}
/// <summary>
diff --git a/lib/Utils/src/ERRNO.cs b/lib/Utils/src/ERRNO.cs
index 684a3c7..0a95780 100644
--- a/lib/Utils/src/ERRNO.cs
+++ b/lib/Utils/src/ERRNO.cs
@@ -30,8 +30,12 @@ namespace VNLib.Utils
/// <summary>
/// Implements a C style integer error code type. Size is platform dependent
/// </summary>
+ /// <remarks>
+ /// Creates a new <see cref="ERRNO"/> from the specified error value
+ /// </remarks>
+ /// <param name="errno">The value of the error to represent</param>
[StructLayout(LayoutKind.Sequential)]
- public readonly struct ERRNO : IEquatable<ERRNO>, ISpanFormattable, IFormattable
+ public readonly struct ERRNO(nint errno) : IEquatable<ERRNO>, ISpanFormattable, IFormattable
{
/// <summary>
/// Represents a successfull error code (true)
@@ -43,13 +47,7 @@ namespace VNLib.Utils
/// </summary>
public static readonly ERRNO E_FAIL = false;
- private readonly nint ErrorCode;
-
- /// <summary>
- /// Creates a new <see cref="ERRNO"/> from the specified error value
- /// </summary>
- /// <param name="errno">The value of the error to represent</param>
- public ERRNO(nint errno) => ErrorCode = errno;
+ private readonly nint ErrorCode = errno;
/// <summary>
/// Creates a new <see cref="ERRNO"/> from an <see cref="int"/> error code. null = 0 = false
@@ -130,13 +128,14 @@ namespace VNLib.Utils
}
return false;
}
+#pragma warning disable CA1305 // Specify IFormatProvider
/// <summary>
/// The integer error value of the current instance in radix 10
/// </summary>
/// <returns>The radix 10 formatted error code</returns>
- public readonly override string ToString() => ErrorCode.ToString();
+ public override readonly string ToString() => ErrorCode.ToString();
/// <summary>
/// Formats the internal nint error code as a string in specified format
/// </summary>
@@ -144,11 +143,15 @@ namespace VNLib.Utils
/// <returns>The formatted error code</returns>
public readonly string ToString(string format) => ErrorCode.ToString(format);
+#pragma warning restore CA1305 // Specify IFormatProvider
+
///<inheritdoc/>
- public readonly bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider? provider) => ErrorCode.TryFormat(destination, out charsWritten, format, provider);
+ public readonly bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider? provider)
+ => ErrorCode.TryFormat(destination, out charsWritten, format, provider);
///<inheritdoc/>
- public readonly string ToString(string? format, IFormatProvider? formatProvider) => ErrorCode.ToString(format, formatProvider);
+ public readonly string ToString(string? format, IFormatProvider? formatProvider)
+ => ErrorCode.ToString(format, formatProvider);
public static ERRNO operator +(ERRNO err, int add) => new(err.ErrorCode + add);
public static ERRNO operator +(ERRNO err, nint add) => new(err.ErrorCode + add);
diff --git a/lib/Utils/src/Extensions/CacheExtensions.cs b/lib/Utils/src/Extensions/CacheExtensions.cs
index 665e282..7efc36d 100644
--- a/lib/Utils/src/Extensions/CacheExtensions.cs
+++ b/lib/Utils/src/Extensions/CacheExtensions.cs
@@ -52,10 +52,7 @@ namespace VNLib.Utils.Extensions
/// </remarks>
public static void StoreRecord<TKey, T>(this IDictionary<TKey, T> store, TKey key, T record) where T : ICacheable
{
- if (store is null)
- {
- throw new ArgumentNullException(nameof(store));
- }
+ ArgumentNullException.ThrowIfNull(store);
T ?oldRecord = default;
lock (store)
@@ -128,10 +125,7 @@ namespace VNLib.Utils.Extensions
/// </remarks>
public static ERRNO TryGetOrEvictRecord<TKey, T>(this IDictionary<TKey, T> store, TKey key, out T? value) where T : ICacheable
{
- if (store is null)
- {
- throw new ArgumentNullException(nameof(store));
- }
+ ArgumentNullException.ThrowIfNull(store);
value = default;
//Cache current date time before entering the lock
@@ -194,10 +188,7 @@ namespace VNLib.Utils.Extensions
/// <returns>True if the record was found and evicted</returns>
public static bool EvictRecord<TKey, T>(this IDictionary<TKey, T> store, TKey key) where T : ICacheable
{
- if (store is null)
- {
- throw new ArgumentNullException(nameof(store));
- }
+ ArgumentNullException.ThrowIfNull(store);
T? record = default;
lock (store)
@@ -218,10 +209,8 @@ namespace VNLib.Utils.Extensions
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="T"></typeparam>
- public static void CollectRecords<TKey, T>(this IDictionary<TKey, T> store) where T : ICacheable
- {
- CollectRecords(store, DateTime.UtcNow);
- }
+ public static void CollectRecords<TKey, T>(this IDictionary<TKey, T> store) where T : ICacheable
+ => CollectRecords(store, DateTime.UtcNow);
/// <summary>
/// Evicts all expired records from the store
@@ -232,10 +221,7 @@ namespace VNLib.Utils.Extensions
/// <param name="validAfter">A time that specifies the time which expired records should be evicted</param>
public static void CollectRecords<TKey, T>(this IDictionary<TKey, T> store, DateTime validAfter) where T : ICacheable
{
- if (store is null)
- {
- throw new ArgumentNullException(nameof(store));
- }
+ ArgumentNullException.ThrowIfNull(store);
//Build a query to get the keys that belong to the expired records
IEnumerable<KeyValuePair<TKey, T>> expired = store.Where(s => s.Value.Expires < validAfter);
//temp list for expired records
@@ -273,15 +259,8 @@ namespace VNLib.Utils.Extensions
/// <param name="useCtx">A callback method that will be passed the record to use within an exclusive context</param>
public static void UseRecord<TKey, T, TState>(this IDictionary<TKey, T> store, TKey key, TState state, Action<T, TState> useCtx) where T: ICacheable
{
- if (store is null)
- {
- throw new ArgumentNullException(nameof(store));
- }
-
- if (useCtx is null)
- {
- throw new ArgumentNullException(nameof(useCtx));
- }
+ ArgumentNullException.ThrowIfNull(store);
+ ArgumentNullException.ThrowIfNull(useCtx);
lock (store)
{
@@ -303,15 +282,8 @@ namespace VNLib.Utils.Extensions
/// <param name="useCtx">A callback method that will be passed the record to use within an exclusive context</param>
public static void UseRecord<TKey, T>(this IDictionary<TKey, T> store, TKey key, Action<T> useCtx) where T : ICacheable
{
- if (store is null)
- {
- throw new ArgumentNullException(nameof(store));
- }
-
- if (useCtx is null)
- {
- throw new ArgumentNullException(nameof(useCtx));
- }
+ ArgumentNullException.ThrowIfNull(store);
+ ArgumentNullException.ThrowIfNull(useCtx);
lock (store)
{
@@ -335,17 +307,15 @@ namespace VNLib.Utils.Extensions
/// <param name="state">A user-token type state parameter to pass to the use callback method</param>
/// <param name="useCtx">A callback method that will be passed the record to use within an exclusive context</param>
/// <remarks>If the record is found, but is expired, the record is evicted from the store. The callback is never invoked</remarks>
- public static void UseIfValid<TKey, T, TState>(this IDictionary<TKey, T> store, TKey key, TState state, Action<T, TState> useCtx) where T : ICacheable
+ public static void UseIfValid<TKey, T, TState>(
+ this IDictionary<TKey, T> store,
+ TKey key,
+ TState state,
+ Action<T, TState> useCtx
+ ) where T : ICacheable
{
- if (store is null)
- {
- throw new ArgumentNullException(nameof(store));
- }
-
- if (useCtx is null)
- {
- throw new ArgumentNullException(nameof(useCtx));
- }
+ ArgumentNullException.ThrowIfNull(store);
+ ArgumentNullException.ThrowIfNull(useCtx);
DateTime now = DateTime.UtcNow;
T? record;
@@ -376,15 +346,8 @@ namespace VNLib.Utils.Extensions
/// <remarks>If the record is found, but is expired, the record is evicted from the store. The callback is never invoked</remarks>
public static void UseIfValid<TKey, T>(this IDictionary<TKey, T> store, TKey key, Action<T> useCtx) where T : ICacheable
{
- if (store is null)
- {
- throw new ArgumentNullException(nameof(store));
- }
-
- if (useCtx is null)
- {
- throw new ArgumentNullException(nameof(useCtx));
- }
+ ArgumentNullException.ThrowIfNull(store);
+ ArgumentNullException.ThrowIfNull(useCtx);
DateTime now = DateTime.UtcNow;
T? record;
diff --git a/lib/Utils/src/IO/TemporayIsolatedFile.cs b/lib/Utils/src/IO/TemporayIsolatedFile.cs
deleted file mode 100644
index 3bee92b..0000000
--- a/lib/Utils/src/IO/TemporayIsolatedFile.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
-* Copyright (c) 2022 Vaughn Nugent
-*
-* Library: VNLib
-* Package: VNLib.Utils
-* File: TemporayIsolatedFile.cs
-*
-* TemporayIsolatedFile.cs is part of VNLib.Utils which is part of the larger
-* VNLib collection of libraries and utilities.
-*
-* VNLib.Utils 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.Utils 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.Utils. If not, see http://www.gnu.org/licenses/.
-*/
-
-using System;
-using System.IO;
-using System.IO.IsolatedStorage;
-
-namespace VNLib.Utils.IO
-{
- /// <summary>
- /// Allows for temporary files to be generated, used, then removed from an <see cref="IsolatedStorageFile"/>
- /// </summary>
- public sealed class TemporayIsolatedFile : BackingStream<IsolatedStorageFileStream>
- {
- private readonly IsolatedStorageDirectory Storage;
- private readonly string Filename;
- /// <summary>
- /// Creates a new temporary filestream within the specified <see cref="IsolatedStorageFile"/>
- /// </summary>
- /// <param name="storage">The file store to genreate temporary files within</param>
- public TemporayIsolatedFile(IsolatedStorageDirectory storage)
- {
- //Store ref
- this.Storage = storage;
- //Creaet a new random filename
- this.Filename = Path.GetRandomFileName();
- //try to created a new file within the isolaged storage
- this.BaseStream = storage.CreateFile(this.Filename);
- }
- protected override void OnClose()
- {
- //Remove the file from the storage
- Storage.DeleteFile(this.Filename);
- }
- }
-} \ No newline at end of file
diff --git a/lib/Utils/src/Memory/ArrayPoolBuffer.cs b/lib/Utils/src/Memory/ArrayPoolBuffer.cs
index e728cd7..67789de 100644
--- a/lib/Utils/src/Memory/ArrayPoolBuffer.cs
+++ b/lib/Utils/src/Memory/ArrayPoolBuffer.cs
@@ -150,20 +150,16 @@ namespace VNLib.Utils.Memory
/// <returns>A memory structure over the buffer</returns>
/// <exception cref="ObjectDisposedException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
- public Memory<T> AsMemory()
- {
- Check();
- return new Memory<T>(Buffer, 0, InitSize);
- }
+ public Memory<T> AsMemory() => AsMemory(start: 0, InitSize);
/// <summary>
/// Gets a memory structure around the internal buffer
/// </summary>
- /// <param name="count">The number of elements included in the result</param>
+ /// <param name="start">The number of elements included in the result</param>
/// <returns>A memory structure over the buffer</returns>
/// <exception cref="ObjectDisposedException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
- public Memory<T> AsMemory(int count) => AsMemory()[..count];
+ public Memory<T> AsMemory(int start) => AsMemory(start, InitSize - start);
/// <summary>
/// Gets a memory structure around the internal buffer
@@ -173,18 +169,20 @@ namespace VNLib.Utils.Memory
/// <returns>A memory structure over the buffer</returns>
/// <exception cref="ObjectDisposedException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
- public Memory<T> AsMemory(int start, int count) => AsMemory().Slice(start, count);
+ public Memory<T> AsMemory(int start, int count)
+ {
+ Check();
+ //Memory constructor will check for array bounds
+ return new(Buffer, start, count);
+ }
/// <summary>
/// Gets an array segment around the internal buffer
/// </summary>
/// <returns>The internal array segment</returns>
/// <exception cref="ObjectDisposedException"></exception>
- public ArraySegment<T> AsArraySegment()
- {
- Check();
- return new ArraySegment<T>(Buffer, 0, InitSize);
- }
+ public ArraySegment<T> AsArraySegment() => GetOffsetWrapper(0, InitSize);
+
/// <summary>
/// Gets an array segment around the internal buffer
@@ -194,10 +192,8 @@ namespace VNLib.Utils.Memory
/// <exception cref="ArgumentOutOfRangeException"></exception>
public ArraySegment<T> AsArraySegment(int start, int count)
{
- if(start< 0 || count < 0)
- {
- throw new ArgumentOutOfRangeException(start < 0 ? nameof(start) : nameof(count), "Cannot be less than zero");
- }
+ ArgumentOutOfRangeException.ThrowIfNegative(start);
+ ArgumentOutOfRangeException.ThrowIfNegative(count);
MemoryUtil.CheckBounds(Buffer, (uint)start, (uint)count);
diff --git a/lib/Utils/src/Memory/ForwardOnlyMemoryReader.cs b/lib/Utils/src/Memory/ForwardOnlyMemoryReader.cs
index 53d3d77..df3990c 100644
--- a/lib/Utils/src/Memory/ForwardOnlyMemoryReader.cs
+++ b/lib/Utils/src/Memory/ForwardOnlyMemoryReader.cs
@@ -59,13 +59,17 @@ namespace VNLib.Utils.Memory
/// The number of elements remaining in the window
/// </summary>
public readonly int WindowSize => _size - _position;
-
/// <summary>
/// Advances the window position the specified number of elements
/// </summary>
/// <param name="count">The number of elements to advance the widnow position</param>
- public void Advance(int count) => _position += count;
+ public void Advance(int count)
+ {
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(count, WindowSize);
+
+ _position += count;
+ }
/// <summary>
/// Resets the sliding window to the begining of the buffer
diff --git a/lib/Utils/src/Memory/ForwardOnlyMemoryWriter.cs b/lib/Utils/src/Memory/ForwardOnlyMemoryWriter.cs
index e0fbe41..791d63c 100644
--- a/lib/Utils/src/Memory/ForwardOnlyMemoryWriter.cs
+++ b/lib/Utils/src/Memory/ForwardOnlyMemoryWriter.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Utils
@@ -29,43 +29,39 @@ namespace VNLib.Utils.Memory
/// <summary>
/// Provides a mutable sliding buffer writer
/// </summary>
- public record struct ForwardOnlyMemoryWriter<T>
+ /// <param name="Buffer">The buffer to write data to</param>
+ public record struct ForwardOnlyMemoryWriter<T>(Memory<T> Buffer)
{
- /// <summary>
- /// The buffer for writing output data to
- /// </summary>
- public readonly Memory<T> Buffer { get; }
+ private int _written;
/// <summary>
/// The number of characters written to the buffer
/// </summary>
- public int Written { readonly get; set; }
+ public int Written
+ {
+ readonly get => _written;
+ set
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(value);
+ _written = value;
+ }
+ }
/// <summary>
/// The number of characters remaining in the buffer
/// </summary>
- public readonly int RemainingSize => Buffer.Length - Written;
+ public readonly int RemainingSize => Buffer.Length - _written;
/// <summary>
/// The remaining buffer window
/// </summary>
- public readonly Memory<T> Remaining => Buffer[Written..];
-
- /// <summary>
- /// Creates a new <see cref="ForwardOnlyWriter{T}"/> assigning the specified buffer
- /// </summary>
- /// <param name="buffer">The buffer to write data to</param>
- public ForwardOnlyMemoryWriter(Memory<T> buffer)
- {
- Buffer = buffer;
- Written = 0;
- }
-
+ public readonly Memory<T> Remaining => Buffer[_written..];
+
/// <summary>
/// Returns a compiled string from the characters written to the buffer
/// </summary>
/// <returns>A string of the characters written to the buffer</returns>
- public readonly override string ToString() => Buffer[..Written].ToString();
+ public readonly override string ToString() => Buffer[.._written].ToString();
/// <summary>
/// Appends a sequence to the buffer
@@ -75,15 +71,14 @@ namespace VNLib.Utils.Memory
public void Append(ReadOnlyMemory<T> data)
{
//Make sure the current window is large enough to buffer the new string
- if (data.Length > RemainingSize)
- {
- throw new ArgumentOutOfRangeException(nameof(Remaining), "The internal buffer does not have enough buffer space");
- }
- Memory<T> window = Buffer[Written..];
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(data.Length, RemainingSize, nameof(data));
+
+ Memory<T> window = Buffer[_written..];
+
//write data to window
data.CopyTo(window);
- //update char position
- Written += data.Length;
+
+ Advance(data.Length);
}
/// <summary>
@@ -94,12 +89,10 @@ namespace VNLib.Utils.Memory
public void Append(T c)
{
//Make sure the current window is large enough to buffer the new string
- if (RemainingSize == 0)
- {
- throw new ArgumentOutOfRangeException(nameof(Remaining), "The internal buffer does not have enough buffer space");
- }
+ ArgumentOutOfRangeException.ThrowIfZero(RemainingSize);
+
//Write data to buffer and increment the buffer position
- Buffer.Span[Written++] = c;
+ Buffer.Span[_written++] = c;
}
/// <summary>
@@ -109,17 +102,15 @@ namespace VNLib.Utils.Memory
/// <exception cref="ArgumentOutOfRangeException"></exception>
public void Advance(int count)
{
- if (count > RemainingSize)
- {
- throw new ArgumentOutOfRangeException(nameof(count), count, "Cannot advance past the end of the buffer");
- }
- Written += count;
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(count, RemainingSize);
+
+ _written += count;
}
/// <summary>
/// Resets the writer by setting the <see cref="Written"/>
/// property to 0.
/// </summary>
- public void Reset() => Written = 0;
+ public void Reset() => _written = 0;
}
}
diff --git a/lib/Utils/src/Memory/MemoryUtil.cs b/lib/Utils/src/Memory/MemoryUtil.cs
index 1d3bccb..cbaaa2f 100644
--- a/lib/Utils/src/Memory/MemoryUtil.cs
+++ b/lib/Utils/src/Memory/MemoryUtil.cs
@@ -155,7 +155,8 @@ namespace VNLib.Utils.Memory
/// <returns>An <see cref="IUnmangedHeap"/> for the current process</returns>
/// <exception cref="SystemException"></exception>
/// <exception cref="DllNotFoundException"></exception>
- public static IUnmangedHeap InitializeNewHeapForProcess(bool globalZero = false) => InitHeapInternal(false, false, globalZero);
+ public static IUnmangedHeap InitializeNewHeapForProcess(bool globalZero = false)
+ => InitHeapInternal(false, false, globalZero);
private static IUnmangedHeap InitHeapInternal(bool isShared, bool enableStats, bool globalZero)
{
@@ -228,6 +229,19 @@ namespace VNLib.Utils.Memory
#region Zero
+ [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
+ private static void ZeroByRef<T>(ref T src, uint elements)
+ {
+ Debug.Assert(Unsafe.IsNullRef(ref src) == false, "Null reference passed to ZeroByRef");
+
+ //Call init block on bytes
+ Unsafe.InitBlock(
+ ref Refs.AsByte(ref src, 0),
+ value: 0,
+ byteCount: ByteCount<T>(elements)
+ );
+ }
+
/// <summary>
/// Zeros a block of memory of umanged type. If Windows is detected at runtime, calls RtlSecureZeroMemory Win32 function
/// </summary>
@@ -241,11 +255,10 @@ namespace VNLib.Utils.Memory
return;
}
- //Calls memset
ZeroByRef(
- ref Refs.AsByte(block, 0),
- (uint)block.Length
- );
+ ref MemoryMarshal.GetReference(block), //Get typed reference
+ (uint)block.Length //block must be a positive value
+ );
}
/// <summary>
@@ -260,28 +273,18 @@ namespace VNLib.Utils.Memory
{
return;
}
-
- uint byteSize = ByteCount<T>((uint)block.Length);
//Pin memory and get pointer
using MemoryHandle handle = block.Pin();
- //Calls memset
- Unsafe.InitBlock(handle.Pointer, 0, byteSize);
- }
-
- [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
- private static void ZeroByRef<T>(ref T src, uint elements)
- {
- Debug.Assert(Unsafe.IsNullRef(ref src) == false, "Null reference passed to ZeroByRef");
- //Call init block on bytes
+ //Calls memset
Unsafe.InitBlock(
- ref Refs.AsByte(ref src, 0),
- 0,
- ByteCount<T>(elements)
+ startAddress: handle.Pointer,
+ value: 0,
+ byteCount: ByteCount<T>((uint)block.Length)
);
}
-
+
/*
* Initializing a non-readonly span/memory as of .NET 6.0 is a reference
* reintpretation, essentially a pointer cast, so there is little/no cost
@@ -294,7 +297,8 @@ namespace VNLib.Utils.Memory
/// <typeparam name="T">The unmanaged</typeparam>
/// <param name="block">The block of memory to initialize</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void InitializeBlock<T>(Span<T> block) where T : struct => UnsafeZeroMemory<T>(block);
+ public static void InitializeBlock<T>(Span<T> block) where T : struct
+ => UnsafeZeroMemory<T>(block);
/// <summary>
/// Initializes a block of memory with zeros
@@ -302,7 +306,8 @@ namespace VNLib.Utils.Memory
/// <typeparam name="T">The unmanaged</typeparam>
/// <param name="block">The block of memory to initialize</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void InitializeBlock<T>(Memory<T> block) where T : struct => UnsafeZeroMemory<T>(block);
+ public static void InitializeBlock<T>(Memory<T> block) where T : struct
+ => UnsafeZeroMemory<T>(block);
/// <summary>
/// Initializes the entire array with zeros
@@ -398,7 +403,8 @@ namespace VNLib.Utils.Memory
/// <param name="block">A pointer to the block of memory to zero</param>
/// <param name="itemCount">The number of elements in the block to zero</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void InitializeBlock<T>(IntPtr block, int itemCount) where T : unmanaged => InitializeBlock((T*)block, itemCount);
+ public static void InitializeBlock<T>(IntPtr block, int itemCount) where T : unmanaged
+ => InitializeBlock((T*)block, itemCount);
/// <summary>
/// Zeroes a block of memory pointing to the structure
@@ -434,7 +440,8 @@ namespace VNLib.Utils.Memory
/// <typeparam name="T">The structure type</typeparam>
/// <param name="structPtr">The pointer to the allocated structure</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void ZeroStruct<T>(void* structPtr) where T: unmanaged => ZeroStruct((T*)structPtr);
+ public static void ZeroStruct<T>(void* structPtr) where T: unmanaged
+ => ZeroStruct((T*)structPtr);
/// <summary>
/// Zeroes a block of memory pointing to the structure
@@ -442,7 +449,8 @@ namespace VNLib.Utils.Memory
/// <typeparam name="T">The structure type</typeparam>
/// <param name="block">The pointer to the allocated structure</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void ZeroStruct<T>(IntPtr block) where T : unmanaged => ZeroStruct<T>(block.ToPointer());
+ public static void ZeroStruct<T>(IntPtr block) where T : unmanaged
+ => ZeroStruct<T>(block.ToPointer());
#endregion
@@ -462,10 +470,11 @@ namespace VNLib.Utils.Memory
ThrowIfNullRef(in source, nameof(target));
ThrowIfNullRef(ref target, nameof(target));
- //Recover byte reference of target struct
- ref byte dst = ref Unsafe.As<T, byte>(ref target);
-
- Unsafe.CopyBlockUnaligned(ref dst, in source, (uint)sizeof(T));
+ Unsafe.CopyBlockUnaligned(
+ destination: ref Unsafe.As<T, byte>(ref target), //Recover byte reference of target struct
+ in source,
+ byteCount: ByteCount<T>(1u)
+ );
}
/// <summary>
@@ -485,11 +494,12 @@ namespace VNLib.Utils.Memory
ThrowIfNullRef(in source, nameof(source));
ThrowIfNullRef(in target, nameof(target));
- //Recover byte reference to struct
- ref byte src = ref Unsafe.As<T, byte>(ref Unsafe.AsRef(in source));
-
//Memmove
- Unsafe.CopyBlockUnaligned(ref target, ref src, (uint)sizeof(T));
+ Unsafe.CopyBlockUnaligned(
+ ref target,
+ in Unsafe.As<T, byte>(ref Unsafe.AsRef(in source)), //Recover byte reference to struct
+ byteCount: ByteCount<T>(1u)
+ );
}
@@ -594,7 +604,8 @@ namespace VNLib.Utils.Memory
/// <param name="target">A reference to the first byte of the memory location to copy the struct data to</param>
/// <exception cref="ArgumentNullException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void CopyStruct<T>(void* source, ref byte target) where T : unmanaged => CopyStruct((T*)source, ref target);
+ public static void CopyStruct<T>(void* source, ref byte target) where T : unmanaged
+ => CopyStruct((T*)source, ref target);
/// <summary>
/// Copies the memory of the structure pointed to by the source pointer to the target
@@ -660,7 +671,8 @@ namespace VNLib.Utils.Memory
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void CopyStruct<T>(void* source, Span<byte> target) where T : unmanaged => CopyStruct((T*)source, target);
+ public static void CopyStruct<T>(void* source, Span<byte> target) where T : unmanaged
+ => CopyStruct((T*)source, target);
/// <summary>
/// Copies the memory of the structure pointed to by the source pointer to the target
@@ -675,7 +687,8 @@ namespace VNLib.Utils.Memory
/// <param name="target">A reference to the first byte of the memory location to copy the struct data to</param>
/// <exception cref="ArgumentNullException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void CopyStruct<T>(IntPtr source, ref byte target) where T : unmanaged => CopyStruct(ref GetRef<T>(source), ref target);
+ public static void CopyStruct<T>(IntPtr source, ref byte target) where T : unmanaged
+ => CopyStruct(ref GetRef<T>(source), ref target);
/// <summary>
@@ -694,7 +707,7 @@ namespace VNLib.Utils.Memory
Unsafe.CopyBlockUnaligned(
ref Refs.AsByte(ref target, 0),
in Refs.AsByteR(in source, 0),
- (uint)sizeof(T)
+ byteCount: ByteCount<T>(1u)
);
}
@@ -711,7 +724,11 @@ namespace VNLib.Utils.Memory
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(target);
- Unsafe.CopyBlockUnaligned(target, source, (uint)sizeof(T));
+ Unsafe.CopyBlockUnaligned(
+ destination: target,
+ source,
+ byteCount: ByteCount<T>(1u)
+ );
}
@@ -735,7 +752,7 @@ namespace VNLib.Utils.Memory
return;
}
- //Check bounds
+ //Check bounds (will verify that count is a positive integer)
CheckBounds(source, sourceOffset, count);
CheckBounds(dest, destOffset, (uint)count);
@@ -1151,7 +1168,7 @@ namespace VNLib.Utils.Memory
public static nuint ByteSize<T>(IMemoryHandle<T> handle)
{
ArgumentNullException.ThrowIfNull(handle);
- return checked(handle.Length * (nuint)Unsafe.SizeOf<T>());
+ return ByteCount<T>(handle.Length);
}
/// <summary>
@@ -1162,7 +1179,8 @@ namespace VNLib.Utils.Memory
/// <returns>The number of bytes pointed to by the handle</returns>
/// <exception cref="ArgumentNullException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static nuint ByteSize<T>(in UnsafeMemoryHandle<T> handle) where T : unmanaged => checked(handle.Length * (nuint)sizeof(T));
+ public static nuint ByteSize<T>(in UnsafeMemoryHandle<T> handle) where T : unmanaged
+ => ByteCount<T>(handle.Length);
/// <summary>
/// Gets the byte multiple of the length parameter
@@ -1172,7 +1190,8 @@ namespace VNLib.Utils.Memory
/// <returns>The byte multiple of the number of elments</returns>
/// <exception cref="OverflowException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static nuint ByteCount<T>(nuint elementCount) => checked(elementCount * (nuint)Unsafe.SizeOf<T>());
+ public static nuint ByteCount<T>(nuint elementCount)
+ => checked(elementCount * (nuint)Unsafe.SizeOf<T>());
/// <summary>
/// Gets the byte multiple of the length parameter
@@ -1182,7 +1201,8 @@ namespace VNLib.Utils.Memory
/// <returns>The byte multiple of the number of elments</returns>
/// <exception cref="OverflowException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static uint ByteCount<T>(uint elementCount) => checked(elementCount * (uint)Unsafe.SizeOf<T>());
+ public static uint ByteCount<T>(uint elementCount)
+ => checked(elementCount * (uint)Unsafe.SizeOf<T>());
/// <summary>
/// Gets the byte multiple of the length parameter. NOTE: Does not verify negative values
@@ -1192,7 +1212,8 @@ namespace VNLib.Utils.Memory
/// <returns>The byte multiple of the number of elments</returns>
/// <exception cref="OverflowException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static nint ByteCount<T>(nint elementCount) => checked(elementCount * Unsafe.SizeOf<T>());
+ public static nint ByteCount<T>(nint elementCount)
+ => checked(elementCount * Unsafe.SizeOf<T>());
/// <summary>
/// Gets the byte multiple of the length parameter. NOTE: Does not verify negative values
@@ -1202,7 +1223,8 @@ namespace VNLib.Utils.Memory
/// <returns>The byte multiple of the number of elments</returns>
/// <exception cref="OverflowException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int ByteCount<T>(int elementCount) => checked(elementCount * Unsafe.SizeOf<T>());
+ public static int ByteCount<T>(int elementCount)
+ => checked(elementCount * Unsafe.SizeOf<T>());
/// <summary>
/// Checks if the offset/count paramters for the given memory handle
@@ -1243,12 +1265,8 @@ namespace VNLib.Utils.Memory
/// <param name="count">The number of bytes expected to be assigned or dereferrenced</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void CheckBounds<T>(Span<T> block, int offset, int count)
- {
- ArgumentOutOfRangeException.ThrowIfNegative(offset);
- ArgumentOutOfRangeException.ThrowIfNegative(count);
- ArgumentOutOfRangeException.ThrowIfGreaterThan(offset + count, block.Length, nameof(count));
- }
+ public static void CheckBounds<T>(Span<T> block, int offset, int count)
+ => CheckBounds((ReadOnlySpan<T>)block, offset, count);
/// <summary>
/// Checks if the offset/count paramters for the given block
@@ -1277,12 +1295,8 @@ namespace VNLib.Utils.Memory
/// <param name="count">The number of bytes expected to be assigned or dereferrenced</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void CheckBounds<T>(Memory<T> block, int offset, int count)
- {
- ArgumentOutOfRangeException.ThrowIfNegative(offset);
- ArgumentOutOfRangeException.ThrowIfNegative(count);
- ArgumentOutOfRangeException.ThrowIfGreaterThan(offset + count, block.Length, nameof(count));
- }
+ public static void CheckBounds<T>(Memory<T> block, int offset, int count)
+ => CheckBounds((ReadOnlyMemory<T>)block, offset, count);
/// <summary>
/// Checks if the offset/count paramters for the given block
@@ -1406,7 +1420,8 @@ namespace VNLib.Utils.Memory
/// <param name="size">The size of the sequence</param>
/// <returns>The span pointing to the memory at the supplied addres</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Span<T> GetSpan<T>(IntPtr address, int size) => new(address.ToPointer(), size);
+ 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.
@@ -1417,7 +1432,8 @@ namespace VNLib.Utils.Memory
/// <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>(ref readonly MemoryHandle handle, int size) => new(handle.Pointer, size);
+ public static Span<T> GetSpan<T>(ref readonly MemoryHandle handle, int size)
+ => new(handle.Pointer, size);
/// <summary>
/// Gets a <see cref="Span{T}"/> over the block of memory pointed to by the supplied handle.
@@ -1427,7 +1443,8 @@ namespace VNLib.Utils.Memory
/// <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);
+ public static Span<T> GetSpan<T>(MemoryHandle handle, int size)
+ => new(handle.Pointer, size);
/// <summary>
/// Recovers a reference to the supplied pointer
@@ -1436,7 +1453,8 @@ namespace VNLib.Utils.Memory
/// <param name="address">The base address to cast to a reference</param>
/// <returns>The reference to the supplied address</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ref T GetRef<T>(IntPtr address) => ref Unsafe.AsRef<T>(address.ToPointer());
+ public static ref T GetRef<T>(IntPtr address)
+ => ref Unsafe.AsRef<T>(address.ToPointer());
/// <summary>
/// Recovers a reference to the supplied pointer
@@ -1458,7 +1476,8 @@ namespace VNLib.Utils.Memory
/// <param name="handle">A reference to the handle to get the intpr for</param>
/// <returns>A managed pointer from the handle</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static IntPtr GetIntptr(ref readonly MemoryHandle handle) => new(handle.Pointer);
+ public static IntPtr GetIntptr(ref readonly MemoryHandle handle)
+ => new(handle.Pointer);
/// <summary>
/// Rounds the requested byte size up to the nearest page
diff --git a/lib/Utils/src/Memory/NativeHeap.cs b/lib/Utils/src/Memory/NativeHeap.cs
index fb9612c..9a2e19a 100644
--- a/lib/Utils/src/Memory/NativeHeap.cs
+++ b/lib/Utils/src/Memory/NativeHeap.cs
@@ -62,9 +62,9 @@ namespace VNLib.Utils.Memory
//Create a flags structure with defaults
UnmanagedHeapDescriptor hFlags = new()
{
- CreationFlags = creationFlags,
- Flags = flags,
- HeapPointer = IntPtr.Zero
+ CreationFlags = creationFlags,
+ Flags = flags,
+ HeapPointer = IntPtr.Zero
};
//Create the heap
@@ -117,15 +117,18 @@ namespace VNLib.Utils.Memory
///<inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- protected override IntPtr AllocBlock(nuint elements, nuint size, bool zero) => MethodTable.Alloc(handle, elements, size, zero);
+ protected override IntPtr AllocBlock(nuint elements, nuint size, bool zero)
+ => MethodTable.Alloc(handle, elements, size, zero);
///<inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- protected override IntPtr ReAllocBlock(IntPtr block, nuint elements, nuint size, bool zero) => MethodTable.Realloc(handle, block, elements, size, zero);
+ protected override IntPtr ReAllocBlock(IntPtr block, nuint elements, nuint size, bool zero)
+ => MethodTable.Realloc(handle, block, elements, size, zero);
///<inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- protected override bool FreeBlock(IntPtr block) => MethodTable.Free(handle, block);
+ protected override bool FreeBlock(IntPtr block)
+ => MethodTable.Free(handle, block);
///<inheritdoc/>
protected override bool ReleaseHandle()
@@ -139,7 +142,7 @@ namespace VNLib.Utils.Memory
//Cleanup the method table
MethodTable = default;
- Trace.WriteLine($"Successfully deestroyed user defined heap 0x{handle:x}");
+ Trace.WriteLine($"Successfully destroyed user defined heap 0x{handle:x}");
return ret;
}
diff --git a/lib/Utils/src/Memory/PrivateBuffersMemoryPool.cs b/lib/Utils/src/Memory/PrivateBuffersMemoryPool.cs
index a17a906..8a752f9 100644
--- a/lib/Utils/src/Memory/PrivateBuffersMemoryPool.cs
+++ b/lib/Utils/src/Memory/PrivateBuffersMemoryPool.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Utils
@@ -50,7 +50,8 @@ namespace VNLib.Utils.Memory
///<exception cref="OutOfMemoryException"></exception>
///<exception cref="ObjectDisposedException"></exception>
///<exception cref="ArgumentOutOfRangeException"></exception>
- public override IMemoryOwner<T> Rent(int minBufferSize = 0) => Heap.DirectAlloc<T>(minBufferSize, false);
+ public override IMemoryOwner<T> Rent(int minBufferSize = 0)
+ => Heap.DirectAlloc<T>(minBufferSize, zero: false);
/// <summary>
/// Allocates a new <see cref="MemoryManager{T}"/> of a different data type from the pool
@@ -58,13 +59,10 @@ namespace VNLib.Utils.Memory
/// <typeparam name="TDifType">The unmanaged data type to allocate for</typeparam>
/// <param name="minBufferSize">Minumum size of the buffer</param>
/// <returns>The memory owner of a different data type</returns>
- public IMemoryOwner<TDifType> Rent<TDifType>(int minBufferSize = 0) where TDifType : unmanaged => Heap.DirectAlloc<TDifType>(minBufferSize, false);
+ public IMemoryOwner<TDifType> Rent<TDifType>(int minBufferSize = 0) where TDifType : unmanaged
+ => Heap.DirectAlloc<TDifType>(minBufferSize, zero: false);
///<inheritdoc/>
- protected override void Dispose(bool disposing)
- {
- //Dispose the heap
- Heap.Dispose();
- }
+ protected override void Dispose(bool disposing) => Heap.Dispose();
}
}
diff --git a/lib/Utils/src/Memory/PrivateStringManager.cs b/lib/Utils/src/Memory/PrivateStringManager.cs
index 2bc825c..1a7b3cf 100644
--- a/lib/Utils/src/Memory/PrivateStringManager.cs
+++ b/lib/Utils/src/Memory/PrivateStringManager.cs
@@ -61,11 +61,11 @@ namespace VNLib.Utils.Memory
private void SetValue(int index, string? value)
{
//Try to get the old reference and erase it
- StringRef strRef = ProtectedElements[index];
+ ref StringRef strRef = ref ProtectedElements[index];
strRef.Erase();
- //Set the new value and determine if it is interned
- ProtectedElements[index] = StringRef.Create(value);
+ //Assign new string reference
+ strRef = StringRef.Create(value);
}
/// <summary>
@@ -78,7 +78,7 @@ namespace VNLib.Utils.Memory
protected string? CopyStringAtIndex(int index)
{
Check();
- StringRef str = ProtectedElements[index];
+ ref readonly StringRef str = ref ProtectedElements[index];
if(str.Value is null)
{
@@ -101,7 +101,8 @@ namespace VNLib.Utils.Memory
}
///<inheritdoc/>
- protected override void Free() => Array.ForEach(ProtectedElements, static p => p.Erase());
+ protected override void Free()
+ => Array.ForEach(ProtectedElements, static p => p.Erase());
/// <summary>
/// Erases the contents of the supplied string if it
@@ -109,10 +110,14 @@ namespace VNLib.Utils.Memory
/// not be erased, nor will a null string
/// </summary>
/// <param name="str">The reference to the string to zero</param>
- public static void EraseString(string? str) => StringRef.Create(str).Erase();
+ public static void EraseString(string? str)
+ => StringRef.Create(str).Erase();
- private readonly record struct StringRef(string? Value, bool IsInterned)
+ private readonly struct StringRef(string? value, bool isInterned)
{
+ public readonly string? Value = value;
+ public readonly bool IsInterned = isInterned;
+
public readonly void Erase()
{
/*
@@ -125,8 +130,8 @@ namespace VNLib.Utils.Memory
}
}
- internal static StringRef Create(string? str) => str is null ?
- new(null, false)
+ internal static StringRef Create(string? str) => str is null
+ ? new(value: null, isInterned: false)
: new(str, string.IsInterned(str) != null);
}
}
diff --git a/lib/Utils/src/Memory/SubSequence.cs b/lib/Utils/src/Memory/SubSequence.cs
index 80aa084..57c902e 100644
--- a/lib/Utils/src/Memory/SubSequence.cs
+++ b/lib/Utils/src/Memory/SubSequence.cs
@@ -58,12 +58,13 @@ namespace VNLib.Utils.Memory
{
ArgumentNullException.ThrowIfNull(block);
ArgumentOutOfRangeException.ThrowIfNegative(size);
- Size = size;
- Handle = block;
- _offset = offset;
//Check handle bounds
MemoryUtil.CheckBounds(block, offset, (uint)size);
+
+ Size = size;
+ Handle = block;
+ _offset = offset;
}
/// <summary>
diff --git a/lib/Utils/src/Memory/VnString.cs b/lib/Utils/src/Memory/VnString.cs
index 429de43..e0f7283 100644
--- a/lib/Utils/src/Memory/VnString.cs
+++ b/lib/Utils/src/Memory/VnString.cs
@@ -64,7 +64,11 @@ namespace VNLib.Utils.Memory
/// </summary>
public bool IsEmpty => Length == 0;
- private VnString(SubSequence<char> sequence) => _stringSequence = sequence;
+ private VnString(IMemoryHandle<char>? handle, SubSequence<char> sequence)
+ {
+ Handle = handle;
+ _stringSequence = sequence;
+ }
private VnString(IMemoryHandle<char> handle, nuint start, int length)
{
@@ -155,15 +159,8 @@ namespace VNLib.Utils.Memory
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static VnString ConsumeHandle(IMemoryHandle<char> handle, nuint start, int length)
{
- if (handle is null)
- {
- throw new ArgumentNullException(nameof(handle));
- }
-
- if (length < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(length));
- }
+ ArgumentNullException.ThrowIfNull(handle);
+ ArgumentOutOfRangeException.ThrowIfNegative(length);
//Check handle bounts
MemoryUtil.CheckBounds(handle, start, (nuint)length);
@@ -217,37 +214,46 @@ namespace VNLib.Utils.Memory
try
{
int length = 0;
- //span ref to bin buffer
- Span<byte> buffer = binBuffer.Span;
+
//Run in checked context for overflows
checked
{
do
- {
- //read
- int read = stream.Read(buffer);
- //guard
+ {
+ int read = stream.Read(binBuffer.Span);
+
if (read <= 0)
{
break;
}
+
//Slice into only the read data
- ReadOnlySpan<byte> readbytes = buffer[..read];
+ ReadOnlySpan<byte> readbytes = binBuffer.AsSpan(0, read);
+
//get num chars
int numChars = encoding.GetCharCount(readbytes);
+
//Guard for overflow
if (((ulong)(numChars + length)) >= int.MaxValue)
{
throw new OverflowException();
}
+
//Re-alloc buffer
charBuffer.ResizeIfSmaller(length + numChars);
+
//Decode and update position
- _= encoding.GetChars(readbytes, charBuffer.Span.Slice(length, numChars));
+ _= encoding.GetChars(
+ bytes: readbytes,
+ chars: charBuffer.AsSpan(length, numChars)
+ );
+
//Update char count
length += numChars;
+
} while (true);
}
+
return ConsumeHandle(charBuffer, 0, length);
}
catch
@@ -330,11 +336,15 @@ namespace VNLib.Utils.Memory
charBuffer.ResizeIfSmaller(length + numChars);
//Decode and update position
- _ = encoding.GetChars(binBuffer.GetSpan()[..read], charBuffer.Span.Slice(length, numChars));
+ _ = encoding.GetChars(
+ bytes: binBuffer.GetSpan()[..read],
+ chars: charBuffer.AsSpan(length, numChars)
+ );
//Update char count
length += numChars;
} while (true);
+
return ConsumeHandle(charBuffer, 0, length);
}
catch
@@ -355,7 +365,6 @@ namespace VNLib.Utils.Memory
/// <exception cref="ObjectDisposedException"></exception>
public char CharAt(int index)
{
- //Check
Check();
//Check bounds
@@ -377,18 +386,21 @@ namespace VNLib.Utils.Memory
/// <exception cref="ObjectDisposedException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public VnString Substring(int start, int count)
- {
- //Check
+ {
Check();
+
ArgumentOutOfRangeException.ThrowIfNegative(start, nameof(start));
ArgumentOutOfRangeException.ThrowIfNegative(count, nameof(count));
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(start + count, Length, nameof(start));
- //get sub-sequence slice for the current string
- SubSequence<char> sub = _stringSequence.Slice((nuint)start, count);
-
- //Create new string with offsets pointing to same internal referrence
- return new VnString(sub);
+ /*
+ * Slice the string and do not pass the handle even if we have it because the new
+ * instance does own the buffer
+ */
+ return new VnString(
+ handle: null,
+ _stringSequence.Slice((nuint)start, count)
+ );
}
/// <summary>
@@ -418,12 +430,18 @@ namespace VNLib.Utils.Memory
{
get
{
- //get start
- int start = range.Start.IsFromEnd ? Length - range.Start.Value : range.Start.Value;
- //Get end
- int end = range.End.IsFromEnd ? Length - range.End.Value : range.End.Value;
- //Handle strings with no ending range
- return (end >= start) ? Substring(start, (end - start)) : Substring(start);
+
+ int start = range.Start.IsFromEnd
+ ? (Length - range.Start.Value)
+ : range.Start.Value;
+
+ int end = range.End.IsFromEnd
+ ? (Length - range.End.Value)
+ : range.End.Value;
+
+ return (end >= start)
+ ? Substring(start, (end - start))
+ : Substring(start);
}
}
#pragma warning restore IDE0057 // Use range operator
@@ -461,47 +479,85 @@ namespace VNLib.Utils.Memory
public static explicit operator VnString(string value) => new (value);
public static explicit operator VnString(ReadOnlySpan<char> value) => new (value);
public static explicit operator VnString(char[] value) => new (value);
- ///<inheritdoc/>
+
+ /// <inheritdoc/>
+ /// <remarks>
+ /// NOTE: Avoid this overload if possible. If no explict overload is provided,
+ /// it's assumed the datatype is not supported and will return false
+ /// </remarks>
public override bool Equals(object? obj)
{
- if(obj is null)
- {
- return false;
- }
return obj switch
{
- VnString => Equals(obj as VnString), //Use operator overload
- string => Equals(obj as string), //Use operator overload
- char[] => Equals(obj as char[]), //Use operator overload
+ VnString => Equals(obj as VnString),
+ string => Equals(obj as string),
+ char[] => Equals(obj as char[]),
_ => false,
};
}
+
+ ///<inheritdoc/>
+ public bool Equals(ReadOnlySpan<char> other, StringComparison stringComparison = StringComparison.Ordinal)
+ => Length == other.Length && AsSpan().Equals(other, stringComparison);
+
+ ///<inheritdoc/>
+ public bool Equals(VnString? other)
+ => Equals(other, StringComparison.Ordinal);
+
///<inheritdoc/>
- public bool Equals(VnString? other) => other is not null && Equals(other.AsSpan());
+ public bool Equals(VnString? other, StringComparison stringComparison)
+ => other is not null && Equals(other.AsSpan(), stringComparison);
+
///<inheritdoc/>
- public bool Equals(VnString? other, StringComparison stringComparison) => other is not null && Equals(other.AsSpan(), stringComparison);
+ public bool Equals(string? other)
+ => Equals(other, StringComparison.Ordinal);
+
///<inheritdoc/>
- public bool Equals(string? other) => Equals(other.AsSpan());
+ public bool Equals(string? other, StringComparison stringComparison)
+ => Equals(other.AsSpan(), stringComparison);
+
///<inheritdoc/>
- public bool Equals(string? other, StringComparison stringComparison) => Equals(other.AsSpan(), stringComparison);
+ public bool Equals(char[]? other)
+ => Equals(other, StringComparison.Ordinal);
+
///<inheritdoc/>
- public bool Equals(char[]? other) => Equals(other.AsSpan());
+ public bool Equals(char[]? other, StringComparison stringComparison)
+ => Equals(other.AsSpan(), stringComparison);
+
///<inheritdoc/>
- public bool Equals(char[]? other, StringComparison stringComparison) => Equals(other.AsSpan(), stringComparison);
+ public bool Equals(in SubSequence<char> other)
+ => Equals(in other, StringComparison.Ordinal);
+
///<inheritdoc/>
- public bool Equals(ReadOnlySpan<char> other, StringComparison stringComparison = StringComparison.Ordinal) => Length == other.Length && AsSpan().Equals(other, stringComparison);
+ public bool Equals(in SubSequence<char> other, StringComparison stringComparison)
+ => Length == other.Size && Equals(other.Span, stringComparison);
+
///<inheritdoc/>
- public bool Equals(in SubSequence<char> other) => Length == other.Size && AsSpan().SequenceEqual(other.Span);
+ public int CompareTo(string? other)
+ => CompareTo(other, StringComparison.Ordinal);
+
///<inheritdoc/>
- public int CompareTo(string? other) => AsSpan().CompareTo(other, StringComparison.Ordinal);
+ ///<exception cref="ArgumentNullException"></exception>
+ public int CompareTo(string? other, StringComparison stringComparison)
+ {
+ ArgumentNullException.ThrowIfNull(other);
+ return CompareTo(other.AsSpan(), stringComparison);
+ }
+
///<inheritdoc/>
///<exception cref="ArgumentNullException"></exception>
public int CompareTo(VnString? other)
{
ArgumentNullException.ThrowIfNull(other);
- return AsSpan().CompareTo(other.AsSpan(), StringComparison.Ordinal);
+ return CompareTo(other.AsSpan(), StringComparison.Ordinal);
}
+ public int CompareTo(ReadOnlySpan<char> other)
+ => AsSpan().CompareTo(other, StringComparison.Ordinal);
+
+ public int CompareTo(ReadOnlySpan<char> other, StringComparison comparison)
+ => AsSpan().CompareTo(other, comparison);
+
/// <summary>
/// Gets a hashcode for the underyling string by using the .NET <see cref="string.GetHashCode()"/>
/// method on the character representation of the data
@@ -512,7 +568,8 @@ namespace VNLib.Utils.Memory
/// a character span etc
/// </remarks>
/// <exception cref="ObjectDisposedException"></exception>
- public override int GetHashCode() => GetHashCode(StringComparison.Ordinal);
+ public override int GetHashCode()
+ => GetHashCode(StringComparison.Ordinal);
/// <summary>
/// Gets a hashcode for the underyling string by using the .NET <see cref="string.GetHashCode()"/>
@@ -525,7 +582,8 @@ namespace VNLib.Utils.Memory
/// a character span etc
/// </remarks>
/// <exception cref="ObjectDisposedException"></exception>
- public int GetHashCode(StringComparison stringComparison) => string.GetHashCode(AsSpan(), stringComparison);
+ public int GetHashCode(StringComparison stringComparison)
+ => string.GetHashCode(AsSpan(), stringComparison);
///<inheritdoc/>
protected override void Free() => Handle?.Dispose();
diff --git a/lib/Utils/src/Native/SafeLibraryHandle.cs b/lib/Utils/src/Native/SafeLibraryHandle.cs
index 4b4ead4..263ac0c 100644
--- a/lib/Utils/src/Native/SafeLibraryHandle.cs
+++ b/lib/Utils/src/Native/SafeLibraryHandle.cs
@@ -39,7 +39,8 @@ namespace VNLib.Utils.Native
///<inheritdoc/>
public override bool IsInvalid => handle == IntPtr.Zero;
- private SafeLibraryHandle(IntPtr libHandle, bool ownsHandle) : base(IntPtr.Zero, ownsHandle) => SetHandle(libHandle);
+ private SafeLibraryHandle(IntPtr libHandle, bool ownsHandle) : base(IntPtr.Zero, ownsHandle)
+ => SetHandle(libHandle);
/// <summary>
/// Loads a native function pointer from the library of the specified name and
diff --git a/lib/Utils/src/Resources/ManagedLibrary.cs b/lib/Utils/src/Resources/ManagedLibrary.cs
index c899156..5faaa19 100644
--- a/lib/Utils/src/Resources/ManagedLibrary.cs
+++ b/lib/Utils/src/Resources/ManagedLibrary.cs
@@ -263,7 +263,8 @@ namespace VNLib.Utils.Resources
/// <param name="flags">The optional method binind flags</param>
/// <returns>The delegate if found <see langword="null"/> otherwise</returns>
/// <exception cref="ArgumentNullException"></exception>
- public static TDelegate? TryGetStaticMethod<TDelegate>(Type type, string methodName, BindingFlags flags = BindingFlags.Public) where TDelegate : Delegate
+ public static TDelegate? TryGetStaticMethod<TDelegate>(Type type, string methodName, BindingFlags flags = BindingFlags.Public)
+ where TDelegate : Delegate
=> TryGetMethodInternal<TDelegate>(type, methodName, null, flags | BindingFlags.Static);
/// <summary>
@@ -276,7 +277,8 @@ namespace VNLib.Utils.Resources
/// <param name="flags">The optional method binind flags</param>
/// <returns>The delegate if found <see langword="null"/> otherwise</returns>
/// <exception cref="ArgumentNullException"></exception>
- public static TDelegate? TryGetStaticMethod<TDelegate, TType>(string methodName,BindingFlags flags = BindingFlags.Public) where TDelegate : Delegate
+ public static TDelegate? TryGetStaticMethod<TDelegate, TType>(string methodName,BindingFlags flags = BindingFlags.Public)
+ where TDelegate : Delegate
=> TryGetMethodInternal<TDelegate>(typeof(TType), methodName, null, flags | BindingFlags.Static);
private static TDelegate? TryGetMethodInternal<TDelegate>(Type type, string methodName, object? target, BindingFlags flags) where TDelegate : Delegate
@@ -293,5 +295,79 @@ namespace VNLib.Utils.Resources
return type.GetMethod(methodName, flags, delegateArgs)
?.CreateDelegate<TDelegate>(target);
}
+
+ /*
+ * NOTE: These functions cannot be optimized (condensed) any furhter. IE: static
+ * and instance method searches. This is because the C# compiler will embed the
+ * call to getType() of the object instead of loading reflection if possible
+ * at runtime. This can cause the type to be undefined at runtime and will not
+ * be able to find some members
+ */
+
+ /// <summary>
+ /// Gets an array of methods that have the specified attribute and match the
+ /// delegate signature of the desired type, and returns them as an array of delegates.
+ /// </summary>
+ /// <typeparam name="TAttr">The function attribute type</typeparam>
+ /// <typeparam name="TFunc">The function delegate type</typeparam>
+ /// <param name="obj">The object instance to get the method delegates for</param>
+ /// <param name="flags">The method binding flags to search for</param>
+ /// <returns>An array of function with the desired attribute assigned, an empty array if no methods are found</returns>
+ public static TFunc[] GetMethodsWithAttribute<TAttr, TFunc>(object obj, BindingFlags flags = BindingFlags.Public | BindingFlags.Instance)
+ where TFunc : Delegate
+ where TAttr : Attribute
+ {
+ ArgumentNullException.ThrowIfNull(obj);
+
+ //Get the delegate type
+ Type funcType = typeof(TFunc);
+
+ //Get the delegate method signature
+ Type[] delegateArgs = funcType.GetMethod("Invoke")!
+ .GetParameters()
+ .Select(static p => p.ParameterType)
+ .ToArray();
+
+ //Get the method with the attribute that matches the same signature as the delegate
+ return obj.GetType()
+ .GetMethods(flags)
+ .Where(static m => m.GetCustomAttribute(typeof(TAttr)) != null)
+ .Where(m => m.GetParameters().Select(static p => p.ParameterType).SequenceEqual(delegateArgs))
+ .Select(method => method.CreateDelegate<TFunc>(obj))
+ .ToArray();
+ }
+
+ /// <summary>
+ /// Gets an array of static methods that have the specified attribute and match the
+ /// delegate signature of the desired type, and returns them as an array of delegates.
+ /// </summary>
+ /// <typeparam name="TAttr">The function attribute type</typeparam>
+ /// <typeparam name="TFunc">The function delegate type</typeparam>
+ /// <param name="classType">Type of the static class to get methods for</param>
+ /// <param name="flags">The method binding flags to search for</param>
+ /// <returns>An array of function with the desired attribute assigned, an empty array if no methods are found</returns>
+ public static TFunc[] GetStaticMethodsWithAttribute<TAttr, TFunc>(Type classType, BindingFlags flags = BindingFlags.Public | BindingFlags.Static)
+ where TFunc : Delegate
+ where TAttr : Attribute
+ {
+ ArgumentNullException.ThrowIfNull(classType);
+
+ //Get the delegate type
+ Type funcType = typeof(TFunc);
+
+ //Get the delegate method signature
+ Type[] delegateArgs = funcType.GetMethod("Invoke")!
+ .GetParameters()
+ .Select(static p => p.ParameterType)
+ .ToArray();
+
+ //Get the method with the attribute that matches the same signature as the delegate
+ return classType.GetType()
+ .GetMethods(flags)
+ .Where(static m => m.GetCustomAttribute(typeof(TAttr)) != null)
+ .Where(m => m.GetParameters().Select(static p => p.ParameterType).SequenceEqual(delegateArgs))
+ .Select(method => method.CreateDelegate<TFunc>(null))
+ .ToArray();
+ }
}
}
diff --git a/lib/Utils/tests/.runsettings b/lib/Utils/tests/.runsettings
index 0e7a703..b13ddb7 100644
--- a/lib/Utils/tests/.runsettings
+++ b/lib/Utils/tests/.runsettings
@@ -3,6 +3,9 @@
<RunConfiguration>
<EnvironmentVariables>
<VNLIB_SHARED_HEAP_DIAGNOSTICS>1</VNLIB_SHARED_HEAP_DIAGNOSTICS>
+ <TEST_RPMALLOC_LIB_PATH>../../../../../Utils.Memory/vnlib_rpmalloc/build/Debug/vnlib_rpmalloc.dll</TEST_RPMALLOC_LIB_PATH>
+ <TEST_MIMALLOC_LIB_PATH>../../../../../Utils.Memory/vnlib_mimalloc/build/Debug/vnlib_mimalloc.dll</TEST_MIMALLOC_LIB_PATH>
+
</EnvironmentVariables>
</RunConfiguration>
</RunSettings> \ No newline at end of file
diff --git a/lib/Utils/tests/IO/VnMemoryStreamTests.cs b/lib/Utils/tests/IO/VnMemoryStreamTests.cs
index 9bcb823..6bbf328 100644
--- a/lib/Utils/tests/IO/VnMemoryStreamTests.cs
+++ b/lib/Utils/tests/IO/VnMemoryStreamTests.cs
@@ -47,8 +47,11 @@ namespace VNLib.Utils.IO.Tests
Assert.IsTrue(vms.CanWrite == true);
}
+ //Handle should throw since the stream owns the handle and it gets dispoed
+ Assert.ThrowsException<ObjectDisposedException>(handle.ThrowIfClosed);
+
//From existing data
- ReadOnlySpan<byte> testSpan = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
+ ReadOnlySpan<byte> testSpan = [1, 2, 3, 4, 5, 6, 7, 8];
using (VnMemoryStream vms = new (privateHeap, testSpan))
{
Assert.IsTrue(vms.Length == testSpan.Length);
@@ -125,19 +128,13 @@ namespace VNLib.Utils.IO.Tests
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]);
- }
+ Assert.IsTrue(memory.Span.SequenceEqual(testData));
//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]);
- }
+ Assert.IsTrue(array.AsSpan().SequenceEqual(testData));
}
}
} \ No newline at end of file
diff --git a/lib/Utils/tests/Memory/MemoryHandleTest.cs b/lib/Utils/tests/Memory/MemoryHandleTest.cs
index 8880010..32d8883 100644
--- a/lib/Utils/tests/Memory/MemoryHandleTest.cs
+++ b/lib/Utils/tests/Memory/MemoryHandleTest.cs
@@ -23,7 +23,6 @@
*/
using System;
-using System.Runtime.CompilerServices;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -89,6 +88,7 @@ namespace VNLib.Utils.Memory.Tests
handle.Span[120] = 10;
Assert.IsTrue(*handle.GetOffset(120) == 10);
+ Assert.IsTrue(handle.GetOffsetRef(120) == 10);
}
diff --git a/lib/Utils/tests/Memory/MemoryUtilTests.cs b/lib/Utils/tests/Memory/MemoryUtilTests.cs
index 68bc35c..bdf8a02 100644
--- a/lib/Utils/tests/Memory/MemoryUtilTests.cs
+++ b/lib/Utils/tests/Memory/MemoryUtilTests.cs
@@ -1,5 +1,6 @@
using System;
using System.Buffers;
+using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
@@ -33,39 +34,54 @@ namespace VNLib.Utils.Memory.Tests
//TODO verify the heap type by loading a dynamic heap dll
}
- [TestMethod()]
- public void UnsafeZeroMemoryTest()
- {
- //Get random data buffer as a readonly span
- ReadOnlyMemory<byte> buffer = RandomNumberGenerator.GetBytes(1024);
+ private static bool AllZero<T>(Span<T> span) where T : struct
+ => AllZero((ReadOnlySpan<T>)span);
- //confirm buffer is not all zero
- Assert.IsFalse(AllZero(buffer.Span));
-
- //Zero readonly memory
- MemoryUtil.UnsafeZeroMemory(buffer);
-
- //Confirm all zero
- Assert.IsTrue(AllZero(buffer.Span));
- }
-
- private static bool AllZero(ReadOnlySpan<byte> span)
+ private static bool AllZero<T>(ReadOnlySpan<T> span)
+ where T : struct
{
- for (int i = 0; i < span.Length; i++)
+ ReadOnlySpan<byte> asBytes = MemoryMarshal.Cast<T, byte>(span);
+
+ for (int i = 0; i < asBytes.Length; i++)
{
- if (span[i] != 0)
+ if (asBytes[i] != 0)
{
return false;
}
}
+
return true;
}
[TestMethod()]
- public void UnsafeZeroMemoryTest1()
+ public void UnsafeZeroMemoryTest()
{
- //Get random data buffer as a readonly span
- ReadOnlySpan<byte> buffer = RandomNumberGenerator.GetBytes(1024);
+ TestZeroWithDataType<byte>();
+ TestZeroWithDataType<sbyte>();
+ TestZeroWithDataType<short>();
+ TestZeroWithDataType<ushort>();
+ TestZeroWithDataType<int>();
+ TestZeroWithDataType<uint>();
+ TestZeroWithDataType<long>();
+ TestZeroWithDataType<ulong>();
+ TestZeroWithDataType<float>();
+ TestZeroWithDataType<double>();
+ TestZeroWithDataType<decimal>();
+ TestZeroWithDataType<char>();
+ TestZeroWithDataType<bool>();
+ TestZeroWithDataType<nint>();
+ TestZeroWithDataType<nuint>();
+ TestZeroWithDataType<TestStruct>();
+ }
+
+
+ private static void TestZeroWithDataType<T>()
+ where T : struct
+ {
+ Trace.WriteLine($"Testing unsafe zero with data type {typeof(T).Name}");
+
+ //Get a random buffer that is known to not be all zeros of a given data type
+ ReadOnlySpan<T> buffer = MemoryMarshal.Cast<byte, T>(RandomNumberGenerator.GetBytes(1024));
//confirm buffer is not all zero
Assert.IsFalse(AllZero(buffer));
@@ -545,7 +561,7 @@ namespace VNLib.Utils.Memory.Tests
Assert.IsTrue(byteSize == (nuint)Environment.SystemPageSize);
}
- using(IMemoryHandle<byte> safeByteBuffer = MemoryUtil.SafeAllocNearestPage(TEST_1, false))
+ using (IMemoryHandle<byte> safeByteBuffer = MemoryUtil.SafeAllocNearestPage(TEST_1, false))
{
nuint byteSize = MemoryUtil.ByteSize(safeByteBuffer);
@@ -712,7 +728,7 @@ namespace VNLib.Utils.Memory.Tests
Assert.ThrowsException<ArgumentNullException>(() => MemoryUtil.Copy(ReadOnlyMemory<byte>.Empty, 0, null, 0, 1));
Assert.ThrowsException<ArgumentNullException>(() => MemoryUtil.Copy(ReadOnlySpan<byte>.Empty, 0, null, 0, 1));
-
+
Assert.ThrowsException<ArgumentNullException>(() => MemoryUtil.CopyArray((IMemoryHandle<byte>)null, 0, testArray, 0, 1));
Assert.ThrowsException<ArgumentNullException>(() => MemoryUtil.CopyArray(testHandle, 0, null, 0, 1));
@@ -736,7 +752,7 @@ namespace VNLib.Utils.Memory.Tests
Assert.ThrowsException<ArgumentOutOfRangeException>(() => MemoryUtil.CopyArray(testHandle, 0, Array.Empty<byte>(), 0, 1));
Assert.ThrowsException<ArgumentOutOfRangeException>(() => MemoryUtil.CopyArray(Array.Empty<byte>(), 0, Array.Empty<byte>(), 0, 1));
-
+
/*
@@ -826,10 +842,10 @@ namespace VNLib.Utils.Memory.Tests
MemoryUtil.CopyArray(testArray, 0, testHandle, 0, 0);
MemoryUtil.CopyArray(testArray, 0, [], 0, 0);
- /*
- * Test negative values for span/memory overloads that
- * accept integers
- */
+ /*
+ * Test negative values for span/memory overloads that
+ * accept integers
+ */
Assert.ThrowsException<ArgumentOutOfRangeException>(() => MemoryUtil.Copy(testHandle, -1, testMem2, 0, 16));
Assert.ThrowsException<ArgumentOutOfRangeException>(() => MemoryUtil.Copy(testHandle, 0, testMem2, -1, 16));
@@ -845,5 +861,49 @@ namespace VNLib.Utils.Memory.Tests
Assert.ThrowsException<ArgumentOutOfRangeException>(() => MemoryUtil.Copy(testMem.Span, -1, testHandle, 0, 16));
Assert.ThrowsException<ArgumentOutOfRangeException>(() => MemoryUtil.Copy(testMem.Span, 0, testHandle, 0, -1));
}
+
+ [TestMethod]
+ public unsafe void ByteSizeTest()
+ {
+ Assert.AreEqual(
+ MemoryUtil.ByteCount<byte>(16),
+ actual: 16
+ );
+
+ Assert.AreEqual(
+ MemoryUtil.ByteCount<int>(16),
+ actual: 16 * sizeof(int)
+ );
+
+ Assert.AreEqual(
+ MemoryUtil.ByteCount<long>(16),
+ actual: 16 * sizeof(long)
+ );
+
+ Assert.AreEqual(
+ MemoryUtil.ByteCount<float>(16),
+ actual: 16 * sizeof(float)
+ );
+
+ Assert.AreEqual(
+ MemoryUtil.ByteCount<double>(16),
+ actual: 16 * sizeof(double)
+ );
+
+ Assert.AreEqual(
+ MemoryUtil.ByteCount<nint>(16),
+ actual: 16 * sizeof(nint)
+ );
+
+ Assert.AreEqual(
+ MemoryUtil.ByteCount<TestStruct>(16),
+ actual: 16 * sizeof(TestStruct)
+ );
+
+ Assert.AreEqual(
+ MemoryUtil.ByteCount<TestStruct>(0),
+ actual: 0
+ );
+ }
}
} \ No newline at end of file
diff --git a/lib/Utils/tests/Memory/NativeHeapTests.cs b/lib/Utils/tests/Memory/NativeHeapTests.cs
index 8653bd0..a7072ed 100644
--- a/lib/Utils/tests/Memory/NativeHeapTests.cs
+++ b/lib/Utils/tests/Memory/NativeHeapTests.cs
@@ -1,20 +1,22 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
+using System.Runtime.InteropServices;
namespace VNLib.Utils.Memory.Tests
{
[TestClass()]
public class NativeHeapTests
{
- const string RpMallocLibPath = "../../../../../Utils.Memory/vnlib_rpmalloc/build/Debug/vnlib_rpmalloc.dll";
- const string MimallocLibPath = "../../../../../Utils.Memory/vnlib_mimalloc/build/Debug/vnlib_mimalloc.dll";
+ private static string? RpMallocLibPath => Environment.GetEnvironmentVariable("TEST_RPMALLOC_LIB_PATH");
+
+ private static string? MimallocLibPath => Environment.GetEnvironmentVariable("TEST_MIMALLOC_LIB_PATH");
[TestMethod()]
public void LoadInTreeRpmallocTest()
{
//Try to load the shared heap
- using NativeHeap heap = NativeHeap.LoadHeap(RpMallocLibPath, System.Runtime.InteropServices.DllImportSearchPath.SafeDirectories, HeapCreation.Shared, 0);
+ using NativeHeap heap = NativeHeap.LoadHeap(RpMallocLibPath, DllImportSearchPath.SafeDirectories, HeapCreation.Shared, flags: 0);
Assert.IsFalse(heap.IsInvalid);
@@ -36,7 +38,7 @@ namespace VNLib.Utils.Memory.Tests
public void LoadInTreeMimallocTest()
{
//Try to load the shared heap
- using NativeHeap heap = NativeHeap.LoadHeap(MimallocLibPath, System.Runtime.InteropServices.DllImportSearchPath.SafeDirectories, HeapCreation.Shared, 0);
+ using NativeHeap heap = NativeHeap.LoadHeap(MimallocLibPath, DllImportSearchPath.SafeDirectories, HeapCreation.Shared, flags: 0);
Assert.IsFalse(heap.IsInvalid);