aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2023-03-25 14:25:14 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2023-03-25 14:25:14 -0400
commit6b5ca9e49e33eb3e03d6f7333661da7e6d0546fa (patch)
tree8a389e9097c256b28cac8f268b93b2101d6a4cba
parentd066a3d7ec26fd6919c514aeaccc4d6b086dc9c7 (diff)
Defer cors to host/middleware/user code
-rw-r--r--lib/Plugins.Essentials/src/Accounts/AccountUtils.cs47
-rw-r--r--lib/Plugins.Essentials/src/Endpoints/ProtectionSettings.cs43
-rw-r--r--lib/Plugins.Essentials/src/Endpoints/ResourceEndpointBase.cs15
-rw-r--r--lib/Plugins.Essentials/src/EventProcessor.cs2
-rw-r--r--lib/Plugins.Essentials/src/FileProcessArgs.cs80
-rw-r--r--lib/Plugins.Essentials/src/TimestampedCounter.cs95
-rw-r--r--lib/Utils/src/Memory/MemoryHandle.cs38
-rw-r--r--lib/Utils/tests/Memory/MemoryHandleTest.cs38
8 files changed, 164 insertions, 194 deletions
diff --git a/lib/Plugins.Essentials/src/Accounts/AccountUtils.cs b/lib/Plugins.Essentials/src/Accounts/AccountUtils.cs
index 75c3388..13cff7b 100644
--- a/lib/Plugins.Essentials/src/Accounts/AccountUtils.cs
+++ b/lib/Plugins.Essentials/src/Accounts/AccountUtils.cs
@@ -308,7 +308,7 @@ namespace VNLib.Plugins.Essentials.Accounts
IClientAuthorization auth = provider.AuthorizeClient(entity, secInfo, user);
//Clear flags
- user.FailedLoginCount(0);
+ user.ClearFailedLoginCount();
//Store variables
entity.Session.UserID = user.UserID;
@@ -526,20 +526,42 @@ namespace VNLib.Plugins.Essentials.Accounts
public static TimestampedCounter FailedLoginCount(this IUser user)
{
ulong value = user.GetValueType<string, ulong>(FAILED_LOGIN_ENTRY);
- return (TimestampedCounter)value;
+ return TimestampedCounter.FromUInt64(value);
}
+
+ /// <summary>
+ /// Clears any pending flc count.
+ /// </summary>
+ /// <param name="user"></param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void ClearFailedLoginCount(this IUser user)
+ {
+ //Cast the counter to a ulong and store as a ulong
+ user.SetValueType(FAILED_LOGIN_ENTRY, (ulong)0);
+ }
+
/// <summary>
/// Sets the number of failed login attempts for the current session
/// </summary>
/// <param name="user"></param>
+ /// <param name="time">Explicitly sets the time of the internal counter</param>
/// <param name="value">The value to set the failed login attempt count</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void FailedLoginCount(this IUser user, uint value)
+ public static void FailedLoginCount(this IUser user, uint value, DateTimeOffset time)
{
- TimestampedCounter counter = new(value);
+ TimestampedCounter counter = TimestampedCounter.FromValues(value, time);
//Cast the counter to a ulong and store as a ulong
- user.SetValueType(FAILED_LOGIN_ENTRY, (ulong)counter);
+ user.SetValueType(FAILED_LOGIN_ENTRY, counter.ToUInt64());
}
+
+ /// <summary>
+ /// Sets the number of failed login attempts for the current session
+ /// </summary>
+ /// <param name="user"></param>
+ /// <param name="value">The value to set the failed login attempt count</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void FailedLoginCount(this IUser user, uint value) => FailedLoginCount(user, value, DateTimeOffset.UtcNow);
+
/// <summary>
/// Sets the number of failed login attempts for the current session
/// </summary>
@@ -549,17 +571,26 @@ namespace VNLib.Plugins.Essentials.Accounts
public static void FailedLoginCount(this IUser user, TimestampedCounter value)
{
//Cast the counter to a ulong and store as a ulong
- user.SetValueType(FAILED_LOGIN_ENTRY, (ulong)value);
+ user.SetValueType(FAILED_LOGIN_ENTRY, value.ToUInt64());
}
+
+ /// <summary>
+ /// Increments the failed login attempt count
+ /// </summary>
+ /// <param name="user"></param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void FailedLoginIncrement(this IUser user) => FailedLoginIncrement(user, DateTimeOffset.UtcNow);
+
/// <summary>
/// Increments the failed login attempt count
/// </summary>
/// <param name="user"></param>
+ /// <param name="time">Explicitly set the time of the counter</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void FailedLoginIncrement(this IUser user)
+ public static void FailedLoginIncrement(this IUser user, DateTimeOffset time)
{
TimestampedCounter current = user.FailedLoginCount();
- user.FailedLoginCount(current.Count + 1);
+ user.FailedLoginCount(current.Count + 1, time);
}
#endregion
diff --git a/lib/Plugins.Essentials/src/Endpoints/ProtectionSettings.cs b/lib/Plugins.Essentials/src/Endpoints/ProtectionSettings.cs
index 77620ac..7e1e394 100644
--- a/lib/Plugins.Essentials/src/Endpoints/ProtectionSettings.cs
+++ b/lib/Plugins.Essentials/src/Endpoints/ProtectionSettings.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Essentials
@@ -31,7 +31,7 @@ namespace VNLib.Plugins.Essentials.Endpoints
/// for connection pre-checks. Settings are the most
/// strict by default
/// </summary>
- public readonly struct ProtectionSettings : IEquatable<ProtectionSettings>
+ public readonly record struct ProtectionSettings
{
/// <summary>
/// Requires TLS be enabled for all incomming requets (or loopback adapter)
@@ -45,12 +45,6 @@ namespace VNLib.Plugins.Essentials.Endpoints
public readonly bool DisableSessionsRequired { get; init; }
/// <summary>
- /// Allows connections that define cross-site sec headers
- /// to be processed or denied (denied by default)
- /// </summary>
- public readonly bool DisableCrossSiteDenied { get; init; }
-
- /// <summary>
/// Enables referr match protection. Requires that if a referer header is
/// set that it matches the current origin
/// </summary>
@@ -64,40 +58,9 @@ namespace VNLib.Plugins.Essentials.Endpoints
public readonly bool DisableBrowsersOnly { get; init; }
/// <summary>
- /// If the connection has a valid session, verifies that the
- /// stored session origin matches the client's origin header.
- /// (confirms the session is coming from the same origin it
- /// was created on)
- /// </summary>
- public readonly bool DisableVerifySessionCors { get; init; }
-
- /// <summary>
/// Disables response caching, by setting the cache control headers appropriatly.
/// Default is disabled
/// </summary>
- public readonly bool EnableCaching { get; init; }
-
-
- ///<inheritdoc/>
- public override bool Equals(object obj) => obj is ProtectionSettings settings && Equals(settings);
- ///<inheritdoc/>
- public override int GetHashCode() => base.GetHashCode();
-
- ///<inheritdoc/>
- public static bool operator ==(ProtectionSettings left, ProtectionSettings right) => left.Equals(right);
- ///<inheritdoc/>
- public static bool operator !=(ProtectionSettings left, ProtectionSettings right) => !(left == right);
-
- ///<inheritdoc/>
- public bool Equals(ProtectionSettings other)
- {
- return DisabledTlsRequired == other.DisabledTlsRequired &&
- DisableSessionsRequired == other.DisableSessionsRequired &&
- DisableCrossSiteDenied == other.DisableCrossSiteDenied &&
- DisableRefererMatch == other.DisableRefererMatch &&
- DisableBrowsersOnly == other.DisableBrowsersOnly &&
- DisableVerifySessionCors == other.DisableVerifySessionCors &&
- EnableCaching == other.EnableCaching;
- }
+ public readonly bool EnableCaching { get; init; }
}
}
diff --git a/lib/Plugins.Essentials/src/Endpoints/ResourceEndpointBase.cs b/lib/Plugins.Essentials/src/Endpoints/ResourceEndpointBase.cs
index 4af3c30..7411789 100644
--- a/lib/Plugins.Essentials/src/Endpoints/ResourceEndpointBase.cs
+++ b/lib/Plugins.Essentials/src/Endpoints/ResourceEndpointBase.cs
@@ -174,21 +174,6 @@ namespace VNLib.Plugins.Essentials.Endpoints
return false;
}
- /*
- * If sessions are required, verify cors is set, and the client supplied an origin header,
- * verify that it matches the origin that was specified during session initialization
- */
- if ((!EndpointProtectionSettings.DisableSessionsRequired & !EndpointProtectionSettings.DisableVerifySessionCors) && entity.Server.Origin != null && !entity.Session.CrossOriginMatch)
- {
- return false;
- }
-
- //Enforce cross-site
- if (!EndpointProtectionSettings.DisableCrossSiteDenied && entity.Server.IsCrossSite())
- {
- return false;
- }
-
return true;
}
diff --git a/lib/Plugins.Essentials/src/EventProcessor.cs b/lib/Plugins.Essentials/src/EventProcessor.cs
index ccaa1a1..820af30 100644
--- a/lib/Plugins.Essentials/src/EventProcessor.cs
+++ b/lib/Plugins.Essentials/src/EventProcessor.cs
@@ -513,7 +513,7 @@ namespace VNLib.Plugins.Essentials
//See if the last modifed header was set
DateTimeOffset? ifModifedSince = entity.Server.LastModified();
//If the header was set, check the date, if the file has been modified since, continue sending the file
- if (ifModifedSince != null && ifModifedSince.Value > fileLastModified)
+ if (ifModifedSince.HasValue && ifModifedSince.Value > fileLastModified)
{
//File has not been modified
entity.CloseResponse(HttpStatusCode.NotModified);
diff --git a/lib/Plugins.Essentials/src/FileProcessArgs.cs b/lib/Plugins.Essentials/src/FileProcessArgs.cs
index dae695b..5ed4667 100644
--- a/lib/Plugins.Essentials/src/FileProcessArgs.cs
+++ b/lib/Plugins.Essentials/src/FileProcessArgs.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Essentials
@@ -25,6 +25,8 @@
using System;
using System.Net;
+#nullable enable
+
namespace VNLib.Plugins.Essentials
{
/// <summary>
@@ -69,7 +71,13 @@ namespace VNLib.Plugins.Essentials
/// <summary>
/// Specifies operations the file processor will follow during request handling
/// </summary>
- public readonly struct FileProcessArgs : IEquatable<FileProcessArgs>
+ /// <param name="Alternate">
+ /// The routine the file processor should execute
+ /// </param>
+ /// <param name="Routine">
+ /// An optional alternate path for the given routine
+ /// </param>
+ public readonly record struct FileProcessArgs(FpRoutine Routine, string Alternate)
{
/// <summary>
/// Signals the file processor should complete with a <see cref="FpRoutine.Deny"/> routine
@@ -91,79 +99,15 @@ namespace VNLib.Plugins.Essentials
/// Signals the file processor should not process the connection
/// </summary>
public static readonly FileProcessArgs VirtualSkip = new (FpRoutine.VirtualSkip);
- /// <summary>
- /// The routine the file processor should execute
- /// </summary>
- public readonly FpRoutine Routine { get; init; }
- /// <summary>
- /// An optional alternate path for the given routine
- /// </summary>
- public readonly string Alternate { get; init; }
+
/// <summary>
/// Initializes a new <see cref="FileProcessArgs"/> with the specified routine
/// and empty <see cref="Alternate"/> path
/// </summary>
/// <param name="routine">The file processing routine to execute</param>
- public FileProcessArgs(FpRoutine routine)
- {
- this.Routine = routine;
- this.Alternate = string.Empty;
- }
- /// <summary>
- /// Initializes a new <see cref="FileProcessArgs"/> with the specified routine
- /// and alternate path
- /// </summary>
- /// <param name="routine"></param>
- /// <param name="alternatePath"></param>
- public FileProcessArgs(FpRoutine routine, string alternatePath)
- {
- this.Routine = routine;
- this.Alternate = alternatePath;
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="arg1"></param>
- /// <param name="arg2"></param>
- /// <returns></returns>
- public static bool operator == (FileProcessArgs arg1, FileProcessArgs arg2)
- {
- return arg1.Equals(arg2);
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="arg1"></param>
- /// <param name="arg2"></param>
- /// <returns></returns>
- public static bool operator != (FileProcessArgs arg1, FileProcessArgs arg2)
- {
- return !arg1.Equals(arg2);
- }
- ///<inheritdoc/>
- public bool Equals(FileProcessArgs other)
- {
- //make sure the routine types match
- if (Routine != other.Routine)
- {
- return false;
- }
- //Next make sure the hashcodes of the alternate paths match
- return (Alternate?.GetHashCode(StringComparison.OrdinalIgnoreCase)) == (other.Alternate?.GetHashCode(StringComparison.OrdinalIgnoreCase));
- }
- ///<inheritdoc/>
- public override bool Equals(object obj)
- {
- return obj is FileProcessArgs args && Equals(args);
- }
- /// <summary>
- /// <inheritdoc/>
- /// </summary>
- /// <returns></returns>
- public override int GetHashCode()
+ public FileProcessArgs(FpRoutine routine):this(routine, string.Empty)
{
- return base.GetHashCode();
}
}
} \ No newline at end of file
diff --git a/lib/Plugins.Essentials/src/TimestampedCounter.cs b/lib/Plugins.Essentials/src/TimestampedCounter.cs
index 19cb8ec..b2ad70c 100644
--- a/lib/Plugins.Essentials/src/TimestampedCounter.cs
+++ b/lib/Plugins.Essentials/src/TimestampedCounter.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Essentials
@@ -24,56 +24,32 @@
using System;
-
-#nullable enable
-
namespace VNLib.Plugins.Essentials
{
+
/// <summary>
/// Stucture that allows for convient storage of a counter value
/// and a second precision timestamp into a 64-bit unsigned integer
/// </summary>
- public readonly struct TimestampedCounter : IEquatable<TimestampedCounter>
+ public readonly record struct TimestampedCounter(uint Count, uint UnixMs)
{
/// <summary>
/// The time the count was last modifed
/// </summary>
- public readonly DateTimeOffset LastModified;
- /// <summary>
- /// The last failed login attempt count value
- /// </summary>
- public readonly uint Count;
-
- /// <summary>
- /// Initalizes a new flc structure with the current UTC date
- /// and the specified count value
- /// </summary>
- /// <param name="count">FLC current count</param>
- public TimestampedCounter(uint count) : this(DateTimeOffset.UtcNow, count)
- { }
-
- private TimestampedCounter(DateTimeOffset dto, uint count)
- {
- Count = count;
- LastModified = dto;
- }
-
- /// <summary>
- /// Compacts and converts the counter value and timestamp into
- /// a 64bit unsigned integer
- /// </summary>
- /// <param name="count">The counter to convert</param>
- public static explicit operator ulong(TimestampedCounter count) => count.ToUInt64();
+ public readonly DateTimeOffset LastModified => DateTimeOffset.FromUnixTimeSeconds(UnixMs);
/// <summary>
/// Compacts and converts the counter value and timestamp into
/// a 64bit unsigned integer
/// </summary>
/// <returns>The uint64 compacted value</returns>
- public ulong ToUInt64()
+ public readonly ulong ToUInt64()
{
//Upper 32 bits time, lower 32 bits count
- ulong value = (ulong)LastModified.ToUnixTimeSeconds() << 32;
+ ulong value = UnixMs;
+ //Lshift to upper32
+ value <<= 32;
+ //Set count as lower32
value |= Count;
return value;
}
@@ -82,24 +58,6 @@ namespace VNLib.Plugins.Essentials
/// The previously compacted <see cref="TimestampedCounter"/>
/// value to cast back to a counter
/// </summary>
- /// <param name="value"></param>
- public static explicit operator TimestampedCounter(ulong value) => FromUInt64(value);
-
- ///<inheritdoc/>
- public override bool Equals(object? obj) => obj is TimestampedCounter counter && Equals(counter);
- ///<inheritdoc/>
- public override int GetHashCode() => this.ToUInt64().GetHashCode();
- ///<inheritdoc/>
- public static bool operator ==(TimestampedCounter left, TimestampedCounter right) => left.Equals(right);
- ///<inheritdoc/>
- public static bool operator !=(TimestampedCounter left, TimestampedCounter right) => !(left == right);
- ///<inheritdoc/>
- public bool Equals(TimestampedCounter other) => ToUInt64() == other.ToUInt64();
-
- /// <summary>
- /// The previously compacted <see cref="TimestampedCounter"/>
- /// value to cast back to a counter
- /// </summary>
/// <param name="value">The uint64 encoded <see cref="TimestampedCounter"/></param>
/// <returns>
/// The decoded <see cref="TimestampedCounter"/> from its
@@ -108,10 +66,39 @@ namespace VNLib.Plugins.Essentials
public static TimestampedCounter FromUInt64(ulong value)
{
//Upper 32 bits time, lower 32 bits count
- long time = (long)(value >> 32);
+ uint time = (uint)(value >> 32);
uint count = (uint)(value & uint.MaxValue);
- //Init dto struct
- return new(DateTimeOffset.FromUnixTimeSeconds(time), count);
+
+ return new TimestampedCounter(count, time);
}
+
+ /// <summary>
+ /// Creates a new <see cref="TimestampedCounter"/> from the given counter
+ /// value and the <see cref="DateTimeOffset"/> unix ms value
+ /// </summary>
+ /// <param name="count"></param>
+ /// <param name="time">The internal time to store in the counter</param>
+ /// <returns>An initialized <see cref="TimestampedCounter"/></returns>
+ /// <exception cref="StackOverflowException"></exception>
+ public static TimestampedCounter FromValues(uint count, DateTimeOffset time)
+ {
+ //The time in seconds truncated to a uint32
+ uint sec = Convert.ToUInt32(time.ToUnixTimeSeconds());
+ return new TimestampedCounter(count, sec);
+ }
+
+ /// <summary>
+ /// The previously compacted <see cref="TimestampedCounter"/>
+ /// value to cast back to a counter
+ /// </summary>
+ /// <param name="value"></param>
+ public static explicit operator TimestampedCounter(ulong value) => FromUInt64(value);
+
+ /// <summary>
+ /// Compacts and converts the counter value and timestamp into
+ /// a 64bit unsigned integer
+ /// </summary>
+ /// <param name="count">The counter to convert</param>
+ public static explicit operator ulong(TimestampedCounter count) => count.ToUInt64();
}
} \ No newline at end of file
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);
+ }
+ }
}
}