Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAhson Khan <ahkha@microsoft.com>2018-03-30 18:27:25 +0300
committerAhson Khan <ahkha@microsoft.com>2018-03-31 03:07:37 +0300
commit78f37847c0aaff95ca33d3c81434552811ed9734 (patch)
treeaf83d2ab922bbe3dec0819300c78eff3a4a426e8 /src
parent916b9fd28b7e43d0f1c4c26e29ab4c64b07450ec (diff)
Change OwnedMemory to MemoryManager and add an IMemoryOwner. (#17340)
* Change OwnedMemory to MemoryManager and add an IMemoryOwner. * Fix comments. * Reset start and length if TryGetMemoryManager returns false. * Reset start and length if TryGetMemoryManager returns false [actually]. * Re-order MemoryHandle ctor parameters and rename TOwner to TManager. * Fix formatting, remove unused code, and fix impl of Pin() Signed-off-by: dotnet-bot <dotnet-bot@microsoft.com>
Diffstat (limited to 'src')
-rw-r--r--src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems5
-rw-r--r--src/System.Private.CoreLib/shared/System/Buffers/IMemoryOwner.cs17
-rw-r--r--src/System.Private.CoreLib/shared/System/Buffers/IPinnable.cs25
-rw-r--r--src/System.Private.CoreLib/shared/System/Buffers/IRetainable.cs26
-rw-r--r--src/System.Private.CoreLib/shared/System/Buffers/MemoryHandle.cs29
-rw-r--r--src/System.Private.CoreLib/shared/System/Buffers/MemoryManager.cs66
-rw-r--r--src/System.Private.CoreLib/shared/System/Buffers/OwnedMemory.cs95
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/FileStreamCompletionSource.Win32.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Memory.cs154
-rw-r--r--src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs84
-rw-r--r--src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs67
11 files changed, 266 insertions, 304 deletions
diff --git a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
index 114358017..d5c432e12 100644
--- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
+++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
@@ -46,9 +46,10 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\ArrayPool.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\ArrayPoolEventSource.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\ConfigurableArrayPool.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\IRetainable.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\IMemoryOwner.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\IPinnable.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\MemoryHandle.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\OwnedMemory.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\MemoryManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\TlsOverPerCoreLockedStacksArrayPool.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Utilities.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Byte.cs" />
diff --git a/src/System.Private.CoreLib/shared/System/Buffers/IMemoryOwner.cs b/src/System.Private.CoreLib/shared/System/Buffers/IMemoryOwner.cs
new file mode 100644
index 000000000..44f16c582
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Buffers/IMemoryOwner.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Buffers
+{
+ /// <summary>
+ /// Owner of Memory<typeparamref name="T"/> that is responsible for disposing the underlying memory appropriately.
+ /// </summary>
+ public interface IMemoryOwner<T> : IDisposable
+ {
+ /// <summary>
+ /// Returns a Memory<typeparamref name="T"/>.
+ /// </summary>
+ Memory<T> Memory { get; }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Buffers/IPinnable.cs b/src/System.Private.CoreLib/shared/System/Buffers/IPinnable.cs
new file mode 100644
index 000000000..623e716a0
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Buffers/IPinnable.cs
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Buffers
+{
+ /// <summary>
+ /// Provides a mechanism for pinning and unpinning objects to prevent the GC from moving them.
+ /// </summary>
+ public interface IPinnable
+ {
+ /// <summary>
+ /// Call this method to indicate that the IPinnable object can not be moved by the garbage collector.
+ /// The address of the pinned object can be taken.
+ /// <param name="elementIndex">The offset to the element within the memory at which the returned <see cref="MemoryHandle"/> points to.</param>
+ /// </summary>
+ MemoryHandle Pin(int elementIndex);
+
+ /// <summary>
+ /// Call this method to indicate that the IPinnable object no longer needs to be pinned.
+ /// The garbage collector is free to move the object now.
+ /// </summary>
+ void Unpin();
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Buffers/IRetainable.cs b/src/System.Private.CoreLib/shared/System/Buffers/IRetainable.cs
deleted file mode 100644
index 6ac508859..000000000
--- a/src/System.Private.CoreLib/shared/System/Buffers/IRetainable.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Runtime;
-using System.Runtime.CompilerServices;
-
-namespace System.Buffers
-{
- /// <summary>
- /// Provides a mechanism for manual lifetime management.
- /// </summary>
- public interface IRetainable
- {
- /// <summary>
- /// Call this method to indicate that the IRetainable object is in use.
- /// Do not dispose until Release is called.
- /// </summary>
- void Retain();
- /// <summary>
- /// Call this method to indicate that the IRetainable object is no longer in use.
- /// The object can now be disposed.
- /// </summary>
- bool Release();
- }
-} \ No newline at end of file
diff --git a/src/System.Private.CoreLib/shared/System/Buffers/MemoryHandle.cs b/src/System.Private.CoreLib/shared/System/Buffers/MemoryHandle.cs
index 754403862..231add1c0 100644
--- a/src/System.Private.CoreLib/shared/System/Buffers/MemoryHandle.cs
+++ b/src/System.Private.CoreLib/shared/System/Buffers/MemoryHandle.cs
@@ -12,53 +12,48 @@ namespace System.Buffers
/// </summary>
public unsafe struct MemoryHandle : IDisposable
{
- private IRetainable _retainable;
private void* _pointer;
private GCHandle _handle;
+ private IPinnable _pinnable;
/// <summary>
/// Creates a new memory handle for the memory.
/// </summary>
- /// <param name="retainable">reference to manually managed object</param>
- /// <param name="pointer">pointer to memory, or null if a pointer was not provided when the handle was created</param>
+ /// <param name="pointer">pointer to memory</param>
+ /// <param name="pinnable">reference to manually managed object, or default if there is no memory manager</param>
/// <param name="handle">handle used to pin array buffers</param>
[CLSCompliant(false)]
- public MemoryHandle(IRetainable retainable, void* pointer = null, GCHandle handle = default(GCHandle))
+ public MemoryHandle(void* pointer, GCHandle handle = default, IPinnable pinnable = default)
{
- _retainable = retainable;
_pointer = pointer;
_handle = handle;
+ _pinnable = pinnable;
}
/// <summary>
- /// Returns the pointer to memory, or null if a pointer was not provided when the handle was created.
+ /// Returns the pointer to memory, where the memory is assumed to be pinned and hence the address won't change.
/// </summary>
[CLSCompliant(false)]
public void* Pointer => _pointer;
/// <summary>
- /// Returns false if the pointer to memory is null.
+ /// Frees the pinned handle and releases IPinnable.
/// </summary>
- public bool HasPointer => _pointer != null;
-
- /// <summary>
- /// Frees the pinned handle and releases IRetainable.
- /// </summary>
- public void Dispose()
+ public void Dispose()
{
if (_handle.IsAllocated)
{
_handle.Free();
}
- if (_retainable != null)
+ if (_pinnable != null)
{
- _retainable.Release();
- _retainable = null;
+ _pinnable.Unpin();
+ _pinnable = null;
}
_pointer = null;
}
}
-} \ No newline at end of file
+}
diff --git a/src/System.Private.CoreLib/shared/System/Buffers/MemoryManager.cs b/src/System.Private.CoreLib/shared/System/Buffers/MemoryManager.cs
new file mode 100644
index 000000000..cc47e02a2
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Buffers/MemoryManager.cs
@@ -0,0 +1,66 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime;
+using System.Runtime.CompilerServices;
+
+namespace System.Buffers
+{
+ /// <summary>
+ /// Manager of Memory<typeparamref name="T"/> that provides the implementation.
+ /// </summary>
+ public abstract class MemoryManager<T> : IMemoryOwner<T>, IPinnable
+ {
+ /// <summary>
+ /// The number of items in the Memory<typeparamref name="T"/>.
+ /// </summary>
+ public virtual int Length => GetSpan().Length;
+
+ /// <summary>
+ /// Returns a Memory<typeparamref name="T"/>.
+ /// </summary>
+ public virtual Memory<T> Memory => new Memory<T>(this, 0, Length);
+
+ /// <summary>
+ /// Returns a span wrapping the underlying memory.
+ /// </summary>
+ public abstract Span<T> GetSpan();
+
+ /// <summary>
+ /// Returns a handle to the memory that has been pinned and hence its address can be taken.
+ /// <param name="elementIndex">The offset to the element within the memory at which the returned <see cref="MemoryHandle"/> points to. (default = 0)</param>
+ /// </summary>
+ public abstract MemoryHandle Pin(int elementIndex = 0);
+
+ /// <summary>
+ /// Lets the garbage collector know that the object is free to be moved now.
+ /// </summary>
+ public abstract void Unpin();
+
+ /// <summary>
+ /// Returns an array segment.
+ /// <remarks>Returns the default array segment if not overriden.</remarks>
+ /// </summary>
+ protected internal virtual bool TryGetArray(out ArraySegment<T> segment)
+ {
+ segment = default;
+ return false;
+ }
+
+ /// <summary>
+ /// Implements IDisposable.
+ /// </summary>
+ void IDisposable.Dispose()
+ {
+ Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// Clean up of any leftover managed and unmanaged resources.
+ /// </summary>
+ protected abstract void Dispose(bool disposing);
+
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Buffers/OwnedMemory.cs b/src/System.Private.CoreLib/shared/System/Buffers/OwnedMemory.cs
deleted file mode 100644
index 8acd5b224..000000000
--- a/src/System.Private.CoreLib/shared/System/Buffers/OwnedMemory.cs
+++ /dev/null
@@ -1,95 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Runtime;
-using System.Runtime.CompilerServices;
-
-namespace System.Buffers
-{
- /// <summary>
- /// Owner of Memory<typeparamref name="T"/> that provides appropriate lifetime management mechanisms for it.
- /// </summary>
- public abstract class OwnedMemory<T> : IDisposable, IRetainable
- {
- /// <summary>
- /// The number of items in the Memory<typeparamref name="T"/>.
- /// </summary>
- public abstract int Length { get; }
-
- /// <summary>
- /// Returns a span wrapping the underlying memory.
- /// </summary>
- public abstract Span<T> Span { get; }
-
- /// <summary>
- /// Returns a Memory<typeparamref name="T"/> if the underlying memory has not been freed.
- /// </summary>
- /// <exception cref="System.ObjectDisposedException">
- /// Thrown when the underlying memory has already been disposed.
- /// </exception>
- public Memory<T> Memory
- {
- get
- {
- if (IsDisposed)
- {
- ThrowHelper.ThrowObjectDisposedException_MemoryDisposed();
- }
- return new Memory<T>(owner: this, 0, Length);
- }
- }
-
- /// <summary>
- /// Returns a handle for the array that has been pinned and hence its address can be taken
- /// </summary>
- public abstract MemoryHandle Pin(int byteOffset = 0);
-
- /// <summary>
- /// Returns an array segment.
- /// </summary>
- protected internal abstract bool TryGetArray(out ArraySegment<T> segment);
-
- /// <summary>
- /// Implements IDisposable.
- /// </summary>
- /// <exception cref="System.InvalidOperationException">
- /// Throw when there are still retained references to the memory
- /// </exception>
- public void Dispose()
- {
- if (IsRetained)
- {
- ThrowHelper.ThrowInvalidOperationException_OutstandingReferences();
- }
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- /// <summary>
- /// Clean up of any leftover managed and unmanaged resources.
- /// </summary>
- protected abstract void Dispose(bool disposing);
-
- /// <summary>
- /// Return true if someone is holding a reference to the memory.
- /// </summary>
- protected abstract bool IsRetained { get; }
-
- /// <summary>
- /// Return true if the underlying memory has been freed.
- /// </summary>
- public abstract bool IsDisposed { get; }
-
- /// <summary>
- /// Implements IRetainable. Prevent accidental disposal of the memory.
- /// </summary>
- public abstract void Retain();
-
- /// <summary>
- /// Implements IRetainable. The memory can now be diposed.
- /// </summary>
- public abstract bool Release();
-
- }
-}
diff --git a/src/System.Private.CoreLib/shared/System/IO/FileStreamCompletionSource.Win32.cs b/src/System.Private.CoreLib/shared/System/IO/FileStreamCompletionSource.Win32.cs
index 82274b131..62ace0918 100644
--- a/src/System.Private.CoreLib/shared/System/IO/FileStreamCompletionSource.Win32.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/FileStreamCompletionSource.Win32.cs
@@ -246,7 +246,7 @@ namespace System.IO
internal MemoryFileStreamCompletionSource(FileStream stream, int numBufferedBytes, ReadOnlyMemory<byte> memory) :
base(stream, numBufferedBytes, bytes: null) // this type handles the pinning, so null is passed for bytes
{
- _handle = memory.Retain(pin: true);
+ _handle = memory.Pin();
}
internal override void ReleaseNativeResource()
diff --git a/src/System.Private.CoreLib/shared/System/Memory.cs b/src/System.Private.CoreLib/shared/System/Memory.cs
index bb2b1557a..6eb5af665 100644
--- a/src/System.Private.CoreLib/shared/System/Memory.cs
+++ b/src/System.Private.CoreLib/shared/System/Memory.cs
@@ -26,15 +26,18 @@ namespace System
// as code uses Unsafe.As to cast between them.
// The highest order bit of _index is used to discern whether _object is an array/string or an owned memory
- // if (_index >> 31) == 1, object _object is an OwnedMemory<T>
- // else, object _object is a T[] or a string. It can only be a string if the Memory<T> was created by
+ // if (_index >> 31) == 1, object _object is an MemoryManager<T>
+ // else, object _object is a T[] or a string.
+ // if (_length >> 31) == 1, _object is a pre-pinned array, so Pin() will not allocate a new GCHandle
+ // else, Pin() needs to allocate a new GCHandle to pin the object.
+ // It can only be a string if the Memory<T> was created by
// using unsafe / marshaling code to reinterpret a ReadOnlyMemory<char> wrapped around a string as
// a Memory<T>.
private readonly object _object;
private readonly int _index;
private readonly int _length;
- private const int RemoveOwnedFlagBitMask = 0x7FFFFFFF;
+ private const int RemoveFlagsBitMask = 0x7FFFFFFF;
/// <summary>
/// Creates a new memory over the entirety of the target array.
@@ -110,22 +113,72 @@ namespace System
_length = length;
}
- // Constructor for internal use only.
+ /// <summary>
+ /// Creates a new memory from a memory manager that provides specific method implementations beginning
+ /// at 'start' index and ending at 'end' index (exclusive).
+ /// </summary>
+ /// <param name="manager">The memory manager.</param>
+ /// <param name="start">The index at which to begin the memory.</param>
+ /// <param name="length">The number of items in the memory.</param>
+ /// <remarks>Returns default when <paramref name="manager"/> is null.</remarks>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;=Length).
+ /// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal Memory(OwnedMemory<T> owner, int index, int length)
+ public Memory(MemoryManager<T> manager, int start, int length)
{
- // No validation performed; caller must provide any necessary validation.
- _object = owner;
- _index = index | (1 << 31); // Before using _index, check if _index < 0, then 'and' it with RemoveOwnedFlagBitMask
+ if (manager == null)
+ {
+ if (start != 0 || length != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+ this = default;
+ return; // returns default
+ }
+ if ((uint)start > (uint)manager.Length || (uint)length > (uint)(manager.Length - start))
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ _object = manager;
+ _index = start | (1 << 31); // Before using _index, check if _index < 0, then 'and' it with RemoveFlagsBitMask
_length = length;
}
+ /// <summary>
+ /// Creates a new memory over the portion of the pre-pinned target array beginning
+ /// at 'start' index and ending at 'end' index (exclusive).
+ /// </summary>
+ /// <param name="array">The pre-pinned target array.</param>
+ /// <param name="start">The index at which to begin the memory.</param>
+ /// <param name="length">The number of items in the memory.</param>
+ /// <remarks>Returns default when <paramref name="array"/> is null.</remarks>
+ /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;=Length).
+ /// </exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private Memory(object obj, int index, int length)
+ public static Memory<T> CreateFromPinnedArray(T[] array, int start, int length)
+ {
+ if (array == null)
+ {
+ if (start != 0 || length != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+ return default;
+ }
+ if (default(T) == null && array.GetType() != typeof(T[]))
+ ThrowHelper.ThrowArrayTypeMismatchException();
+ if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ // Before using _length, check if _length < 0, then 'and' it with RemoveFlagsBitMask
+ return new Memory<T>((object)array, start, length | (1 << 31));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private Memory(object obj, int start, int length)
{
// No validation performed; caller must provide any necessary validation.
_object = obj;
- _index = index;
+ _index = start;
_length = length;
}
@@ -153,12 +206,12 @@ namespace System
/// <summary>
/// The number of items in the memory.
/// </summary>
- public int Length => _length;
+ public int Length => _length & RemoveFlagsBitMask;
/// <summary>
/// Returns true if Length is 0.
/// </summary>
- public bool IsEmpty => _length == 0;
+ public bool IsEmpty => (_length & RemoveFlagsBitMask) == 0;
/// <summary>
/// For <see cref="Memory{Char}"/>, returns a new instance of string that represents the characters pointed to by the memory.
@@ -168,9 +221,9 @@ namespace System
{
if (typeof(T) == typeof(char))
{
- return (_object is string str) ? str.Substring(_index, _length) : Span.ToString();
+ return (_object is string str) ? str.Substring(_index, _length & RemoveFlagsBitMask) : Span.ToString();
}
- return string.Format("System.Memory<{0}>[{1}]", typeof(T).Name, _length);
+ return string.Format("System.Memory<{0}>[{1}]", typeof(T).Name, _length & RemoveFlagsBitMask);
}
/// <summary>
@@ -183,12 +236,13 @@ namespace System
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Memory<T> Slice(int start)
{
- if ((uint)start > (uint)_length)
+ int actualLength = _length & RemoveFlagsBitMask;
+ if ((uint)start > (uint)actualLength)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
}
- return new Memory<T>(_object, _index + start, _length - start);
+ return new Memory<T>(_object, _index + start, actualLength - start);
}
/// <summary>
@@ -202,7 +256,8 @@ namespace System
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Memory<T> Slice(int start, int length)
{
- if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
+ int actualLength = _length & RemoveFlagsBitMask;
+ if ((uint)start > (uint)actualLength || (uint)length > (uint)(actualLength - start))
{
ThrowHelper.ThrowArgumentOutOfRangeException();
}
@@ -220,10 +275,12 @@ namespace System
{
if (_index < 0)
{
- return ((OwnedMemory<T>)_object).Span.Slice(_index & RemoveOwnedFlagBitMask, _length);
+ Debug.Assert(_length >= 0);
+ return ((MemoryManager<T>)_object).GetSpan().Slice(_index & RemoveFlagsBitMask, _length);
}
else if (typeof(T) == typeof(char) && _object is string s)
{
+ Debug.Assert(_length >= 0);
// This is dangerous, returning a writable span for a string that should be immutable.
// However, we need to handle the case where a ReadOnlyMemory<char> was created from a string
// and then cast to a Memory<T>. Such a cast can only be done with unsafe or marshaling code,
@@ -237,7 +294,7 @@ namespace System
}
else if (_object != null)
{
- return new Span<T>((T[])_object, _index, _length);
+ return new Span<T>((T[])_object, _index, _length & RemoveFlagsBitMask);
}
else
{
@@ -278,7 +335,7 @@ namespace System
{
if (_index < 0)
{
- return ((OwnedMemory<T>)_object).Pin((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf<T>());
+ return ((MemoryManager<T>)_object).Pin((_index & RemoveFlagsBitMask));
}
else if (typeof(T) == typeof(char) && _object is string s)
{
@@ -293,72 +350,21 @@ namespace System
#else
void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref s.GetRawStringData()), _index);
#endif // FEATURE_PORTABLE_SPAN
- return new MemoryHandle(null, pointer, handle);
+ return new MemoryHandle(pointer, handle);
}
else if (_object is T[] array)
{
- var handle = GCHandle.Alloc(array, GCHandleType.Pinned);
+ GCHandle handle = _length < 0 ? default : GCHandle.Alloc(array, GCHandleType.Pinned);
#if FEATURE_PORTABLE_SPAN
void* pointer = Unsafe.Add<T>((void*)handle.AddrOfPinnedObject(), _index);
#else
void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index);
#endif // FEATURE_PORTABLE_SPAN
- return new MemoryHandle(null, pointer, handle);
+ return new MemoryHandle(pointer, handle);
}
return default;
}
- /// <summary>[Obsolete, use Pin()] Creates a handle for the memory.</summary>
- /// <param name="pin">
- /// If pin is true, the GC will not move the array until the returned <see cref="MemoryHandle"/>
- /// is disposed, enabling taking and using the memory's address.
- /// </param>
- public unsafe MemoryHandle Retain(bool pin = false)
- {
- MemoryHandle memoryHandle = default;
- if (pin)
- {
- if (_index < 0)
- {
- memoryHandle = ((OwnedMemory<T>)_object).Pin((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf<T>());
- }
- else if (typeof(T) == typeof(char) && _object is string s)
- {
- // This case can only happen if a ReadOnlyMemory<char> was created around a string
- // and then that was cast to a Memory<char> using unsafe / marshaling code. This needs
- // to work, however, so that code that uses a single Memory<char> field to store either
- // a readable ReadOnlyMemory<char> or a writable Memory<char> can still be pinned and
- // used for interop purposes.
- GCHandle handle = GCHandle.Alloc(s, GCHandleType.Pinned);
-#if FEATURE_PORTABLE_SPAN
- void* pointer = Unsafe.Add<T>((void*)handle.AddrOfPinnedObject(), _index);
-#else
- void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref s.GetRawStringData()), _index);
-#endif // FEATURE_PORTABLE_SPAN
- memoryHandle = new MemoryHandle(null, pointer, handle);
- }
- else if (_object is T[] array)
- {
- var handle = GCHandle.Alloc(array, GCHandleType.Pinned);
-#if FEATURE_PORTABLE_SPAN
- void* pointer = Unsafe.Add<T>((void*)handle.AddrOfPinnedObject(), _index);
-#else
- void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index);
-#endif // FEATURE_PORTABLE_SPAN
- memoryHandle = new MemoryHandle(null, pointer, handle);
- }
- }
- else
- {
- if (_index < 0)
- {
- ((OwnedMemory<T>)_object).Retain();
- memoryHandle = new MemoryHandle((OwnedMemory<T>)_object);
- }
- }
- return memoryHandle;
- }
-
/// <summary>
/// Copies the contents from the memory into a new array. This heap
/// allocates, so should generally be avoided, however it is sometimes
diff --git a/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs b/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs
index dca7db3df..ca0e7d888 100644
--- a/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs
+++ b/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs
@@ -26,13 +26,15 @@ namespace System
// as code uses Unsafe.As to cast between them.
// The highest order bit of _index is used to discern whether _object is an array/string or an owned memory
- // if (_index >> 31) == 1, _object is an OwnedMemory<T>
- // else, _object is a T[] or string
+ // if (_index >> 31) == 1, _object is an MemoryManager<T>
+ // else, _object is a T[] or string.
+ // if (_length >> 31) == 1, _object is a pre-pinned array, so Pin() will not allocate a new GCHandle
+ // else, Pin() needs to allocate a new GCHandle to pin the object.
private readonly object _object;
private readonly int _index;
private readonly int _length;
- internal const int RemoveOwnedFlagBitMask = 0x7FFFFFFF;
+ internal const int RemoveFlagsBitMask = 0x7FFFFFFF;
/// <summary>
/// Creates a new memory over the entirety of the target array.
@@ -115,12 +117,12 @@ namespace System
/// <summary>
/// The number of items in the memory.
/// </summary>
- public int Length => _length;
+ public int Length => _length & RemoveFlagsBitMask;
/// <summary>
/// Returns true if Length is 0.
/// </summary>
- public bool IsEmpty => _length == 0;
+ public bool IsEmpty => (_length & RemoveFlagsBitMask) == 0;
/// <summary>
/// For <see cref="ReadOnlyMemory{Char}"/>, returns a new instance of string that represents the characters pointed to by the memory.
@@ -130,9 +132,9 @@ namespace System
{
if (typeof(T) == typeof(char))
{
- return (_object is string str) ? str.Substring(_index, _length) : Span.ToString();
+ return (_object is string str) ? str.Substring(_index, _length & RemoveFlagsBitMask) : Span.ToString();
}
- return string.Format("System.ReadOnlyMemory<{0}>[{1}]", typeof(T).Name, _length);
+ return string.Format("System.ReadOnlyMemory<{0}>[{1}]", typeof(T).Name, _length & RemoveFlagsBitMask);
}
/// <summary>
@@ -145,12 +147,13 @@ namespace System
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlyMemory<T> Slice(int start)
{
- if ((uint)start > (uint)_length)
+ int actualLength = _length & RemoveFlagsBitMask;
+ if ((uint)start > (uint)actualLength)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
}
- return new ReadOnlyMemory<T>(_object, _index + start, _length - start);
+ return new ReadOnlyMemory<T>(_object, _index + start, actualLength - start);
}
/// <summary>
@@ -164,7 +167,8 @@ namespace System
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlyMemory<T> Slice(int start, int length)
{
- if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
+ int actualLength = _length & RemoveFlagsBitMask;
+ if ((uint)start > (uint)actualLength || (uint)length > (uint)(actualLength - start))
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
}
@@ -182,10 +186,12 @@ namespace System
{
if (_index < 0)
{
- return ((OwnedMemory<T>)_object).Span.Slice(_index & RemoveOwnedFlagBitMask, _length);
+ Debug.Assert(_length >= 0);
+ return ((MemoryManager<T>)_object).GetSpan().Slice(_index & RemoveFlagsBitMask, _length);
}
else if (typeof(T) == typeof(char) && _object is string s)
{
+ Debug.Assert(_length >= 0);
#if FEATURE_PORTABLE_SPAN
return new ReadOnlySpan<T>(Unsafe.As<Pinnable<T>>(s), MemoryExtensions.StringAdjustment, s.Length).Slice(_index, _length);
#else
@@ -194,7 +200,7 @@ namespace System
}
else if (_object != null)
{
- return new ReadOnlySpan<T>((T[])_object, _index, _length);
+ return new ReadOnlySpan<T>((T[])_object, _index, _length & RemoveFlagsBitMask);
}
else
{
@@ -235,7 +241,7 @@ namespace System
{
if (_index < 0)
{
- return ((OwnedMemory<T>)_object).Pin((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf<T>());
+ return ((MemoryManager<T>)_object).Pin((_index & RemoveFlagsBitMask));
}
else if (typeof(T) == typeof(char) && _object is string s)
{
@@ -245,67 +251,21 @@ namespace System
#else
void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref s.GetRawStringData()), _index);
#endif // FEATURE_PORTABLE_SPAN
- return new MemoryHandle(null, pointer, handle);
+ return new MemoryHandle(pointer, handle);
}
else if (_object is T[] array)
{
- var handle = GCHandle.Alloc(array, GCHandleType.Pinned);
+ GCHandle handle = _length < 0 ? default : GCHandle.Alloc(array, GCHandleType.Pinned);
#if FEATURE_PORTABLE_SPAN
void* pointer = Unsafe.Add<T>((void*)handle.AddrOfPinnedObject(), _index);
#else
void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index);
#endif // FEATURE_PORTABLE_SPAN
- return new MemoryHandle(null, pointer, handle);
+ return new MemoryHandle(pointer, handle);
}
return default;
}
- /// <summary>[Obsolete, use Pin()] Creates a handle for the memory.</summary>
- /// <param name="pin">
- /// If pin is true, the GC will not move the array until the returned <see cref="MemoryHandle"/>
- /// is disposed, enabling taking and using the memory's address.
- /// </param>
- public unsafe MemoryHandle Retain(bool pin = false)
- {
- MemoryHandle memoryHandle = default;
- if (pin)
- {
- if (_index < 0)
- {
- memoryHandle = ((OwnedMemory<T>)_object).Pin((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf<T>());
- }
- else if (typeof(T) == typeof(char) && _object is string s)
- {
- GCHandle handle = GCHandle.Alloc(s, GCHandleType.Pinned);
-#if FEATURE_PORTABLE_SPAN
- void* pointer = Unsafe.Add<T>((void*)handle.AddrOfPinnedObject(), _index);
-#else
- void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref s.GetRawStringData()), _index);
-#endif // FEATURE_PORTABLE_SPAN
- memoryHandle = new MemoryHandle(null, pointer, handle);
- }
- else if (_object is T[] array)
- {
- var handle = GCHandle.Alloc(array, GCHandleType.Pinned);
-#if FEATURE_PORTABLE_SPAN
- void* pointer = Unsafe.Add<T>((void*)handle.AddrOfPinnedObject(), _index);
-#else
- void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index);
-#endif // FEATURE_PORTABLE_SPAN
- memoryHandle = new MemoryHandle(null, pointer, handle);
- }
- }
- else
- {
- if (_index < 0)
- {
- ((OwnedMemory<T>)_object).Retain();
- memoryHandle = new MemoryHandle((OwnedMemory<T>)_object);
- }
- }
- return memoryHandle;
- }
-
/// <summary>
/// Copies the contents from the memory into a new array. This heap
/// allocates, so should generally be avoided, however it is sometimes
diff --git a/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs b/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs
index b4b17b037..073dd76cd 100644
--- a/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs
+++ b/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs
@@ -5,6 +5,7 @@
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Collections.Generic;
+using System.Diagnostics;
#if !netstandard
using Internal.Runtime.CompilerServices;
@@ -27,19 +28,20 @@ namespace System.Runtime.InteropServices
object obj = memory.GetObjectStartLength(out int index, out int length);
if (index < 0)
{
- if (((OwnedMemory<T>)obj).TryGetArray(out ArraySegment<T> arraySegment))
+ Debug.Assert(length >= 0);
+ if (((MemoryManager<T>)obj).TryGetArray(out ArraySegment<T> arraySegment))
{
- segment = new ArraySegment<T>(arraySegment.Array, arraySegment.Offset + (index & ReadOnlyMemory<T>.RemoveOwnedFlagBitMask), length);
+ segment = new ArraySegment<T>(arraySegment.Array, arraySegment.Offset + (index & ReadOnlyMemory<T>.RemoveFlagsBitMask), length);
return true;
}
}
else if (obj is T[] arr)
{
- segment = new ArraySegment<T>(arr, index, length);
+ segment = new ArraySegment<T>(arr, index, length & ReadOnlyMemory<T>.RemoveFlagsBitMask);
return true;
}
- if (length == 0)
+ if ((length & ReadOnlyMemory<T>.RemoveFlagsBitMask) == 0)
{
#if FEATURE_PORTABLE_SPAN
segment = new ArraySegment<T>(SpanHelpers.PerTypeValues<T>.EmptyArray);
@@ -54,40 +56,49 @@ namespace System.Runtime.InteropServices
}
/// <summary>
- /// Gets an <see cref="OwnedMemory{T}"/> from the underlying read-only memory.
- /// If unable to get the <typeparamref name="TOwner"/> type, returns false.
+ /// Gets an <see cref="MemoryManager{T}"/> from the underlying read-only memory.
+ /// If unable to get the <typeparamref name="TManager"/> type, returns false.
/// </summary>
/// <typeparam name="T">The element type of the <paramref name="memory" />.</typeparam>
- /// <typeparam name="TOwner">The type of <see cref="OwnedMemory{T}"/> to try and retrive.</typeparam>
- /// <param name="memory">The memory to get the owner for.</param>
- /// <param name="owner">The returned owner of the <see cref="ReadOnlyMemory{T}"/>.</param>
+ /// <typeparam name="TManager">The type of <see cref="MemoryManager{T}"/> to try and retrive.</typeparam>
+ /// <param name="memory">The memory to get the manager for.</param>
+ /// <param name="manager">The returned manager of the <see cref="ReadOnlyMemory{T}"/>.</param>
/// <returns>A <see cref="bool"/> indicating if it was successful.</returns>
- public static bool TryGetOwnedMemory<T, TOwner>(ReadOnlyMemory<T> memory, out TOwner owner)
- where TOwner : OwnedMemory<T>
+ public static bool TryGetMemoryManager<T, TManager>(ReadOnlyMemory<T> memory, out TManager manager)
+ where TManager : MemoryManager<T>
{
- TOwner localOwner; // Use register for null comparison rather than byref
- owner = localOwner = memory.GetObjectStartLength(out int index, out int length) as TOwner;
- return !ReferenceEquals(owner, null);
+ TManager localManager; // Use register for null comparison rather than byref
+ manager = localManager = memory.GetObjectStartLength(out _, out _) as TManager;
+ return !ReferenceEquals(manager, null);
}
/// <summary>
- /// Gets an <see cref="OwnedMemory{T}"/> and <paramref name="start" />, <paramref name="length" /> from the underlying read-only memory.
- /// If unable to get the <typeparamref name="TOwner"/> type, returns false.
+ /// Gets an <see cref="MemoryManager{T}"/> and <paramref name="start" />, <paramref name="length" /> from the underlying read-only memory.
+ /// If unable to get the <typeparamref name="TManager"/> type, returns false.
/// </summary>
/// <typeparam name="T">The element type of the <paramref name="memory" />.</typeparam>
- /// <typeparam name="TOwner">The type of <see cref="OwnedMemory{T}"/> to try and retrive.</typeparam>
- /// <param name="memory">The memory to get the owner for.</param>
- /// <param name="owner">The returned owner of the <see cref="ReadOnlyMemory{T}"/>.</param>
- /// <param name="start">The offset from the start of the <paramref name="owner" /> that the <paramref name="memory" /> represents.</param>
- /// <param name="length">The length of the <paramref name="owner" /> that the <paramref name="memory" /> represents.</param>
+ /// <typeparam name="TManager">The type of <see cref="MemoryManager{T}"/> to try and retrive.</typeparam>
+ /// <param name="memory">The memory to get the manager for.</param>
+ /// <param name="manager">The returned manager of the <see cref="ReadOnlyMemory{T}"/>.</param>
+ /// <param name="start">The offset from the start of the <paramref name="manager" /> that the <paramref name="memory" /> represents.</param>
+ /// <param name="length">The length of the <paramref name="manager" /> that the <paramref name="memory" /> represents.</param>
/// <returns>A <see cref="bool"/> indicating if it was successful.</returns>
- public static bool TryGetOwnedMemory<T, TOwner>(ReadOnlyMemory<T> memory, out TOwner owner, out int start, out int length)
- where TOwner : OwnedMemory<T>
+ public static bool TryGetMemoryManager<T, TManager>(ReadOnlyMemory<T> memory, out TManager manager, out int start, out int length)
+ where TManager : MemoryManager<T>
{
- TOwner localOwner; // Use register for null comparison rather than byref
- owner = localOwner = memory.GetObjectStartLength(out start, out length) as TOwner;
- start &= ReadOnlyMemory<T>.RemoveOwnedFlagBitMask;
- return !ReferenceEquals(owner, null);
+ TManager localManager; // Use register for null comparison rather than byref
+ manager = localManager = memory.GetObjectStartLength(out start, out length) as TManager;
+ start &= ReadOnlyMemory<T>.RemoveFlagsBitMask;
+
+ Debug.Assert(length >= 0);
+
+ if (ReferenceEquals(manager, null))
+ {
+ start = default;
+ length = default;
+ return false;
+ }
+ return true;
}
/// <summary>
@@ -113,6 +124,8 @@ namespace System.Runtime.InteropServices
{
if (memory.GetObjectStartLength(out int offset, out int count) is string s)
{
+ Debug.Assert(offset >= 0);
+ Debug.Assert(count >= 0);
text = s;
start = offset;
length = count;