// See the LICENSE file in the project root for more information.
using System.Buffers;
-using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
-using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
using System.Diagnostics;
-using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
+using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
namespace System
{
// NOTE: With the current implementation, Memory<T> and ReadOnlyMemory<T> must have the same layout,
// as code uses Unsafe.As to cast between them.
- // The highest order bit of _index is used to discern whether _arrayOrOwnedMemory is an array or an owned memory
- // if (_index >> 31) == 1, object _arrayOrOwnedMemory is an OwnedMemory<T>
- // else, object _arrayOrOwnedMemory is a T[]
- private readonly object _arrayOrOwnedMemory;
+ // The highest order bit of _index is used to discern whether _object is an array or an owned memory
+ // if (_index >> 31) == 1, object _object is an OwnedMemory<T>
+ // else, object _object is a T[].
+ private readonly object _object;
private readonly int _index;
private readonly int _length;
if (default(T) == null && array.GetType() != typeof(T[]))
ThrowHelper.ThrowArrayTypeMismatchException();
- _arrayOrOwnedMemory = array;
+ _object = array;
_index = 0;
_length = array.Length;
}
if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
ThrowHelper.ThrowArgumentOutOfRangeException();
- _arrayOrOwnedMemory = array;
+ _object = array;
_index = start;
_length = length;
}
if (index < 0 || length < 0)
ThrowHelper.ThrowArgumentOutOfRangeException();
- _arrayOrOwnedMemory = owner;
+ _object = owner;
_index = index | (1 << 31); // Before using _index, check if _index < 0, then 'and' it with RemoveOwnedFlagBitMask
_length = length;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private Memory(object obj, int index, int length)
+ {
+ // No validation performed; caller must provide any necessary validation.
+ _object = obj;
+ _index = index;
+ _length = length;
+ }
+
/// <summary>
/// Defines an implicit conversion of an array to a <see cref="Memory{T}"/>
/// </summary>
public Memory<T> Slice(int start)
{
if ((uint)start > (uint)_length)
- ThrowHelper.ThrowArgumentOutOfRangeException();
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+ }
- if (_index < 0)
- return new Memory<T>((OwnedMemory<T>)_arrayOrOwnedMemory, (_index & RemoveOwnedFlagBitMask) + start, _length - start);
- return new Memory<T>((T[])_arrayOrOwnedMemory, _index + start, _length - start);
+ return new Memory<T>(_object, _index + start, _length - start);
}
/// <summary>
public Memory<T> Slice(int start, int length)
{
if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
+ {
ThrowHelper.ThrowArgumentOutOfRangeException();
-
- if (_index < 0)
- return new Memory<T>((OwnedMemory<T>)_arrayOrOwnedMemory, (_index & RemoveOwnedFlagBitMask) + start, length);
- return new Memory<T>((T[])_arrayOrOwnedMemory, _index + start, length);
+ }
+
+ return new Memory<T>(_object, _index + start, length);
}
/// <summary>
get
{
if (_index < 0)
- return ((OwnedMemory<T>)_arrayOrOwnedMemory).Span.Slice(_index & RemoveOwnedFlagBitMask, _length);
- return new Span<T>((T[])_arrayOrOwnedMemory, _index, _length);
+ return ((OwnedMemory<T>)_object).Span.Slice(_index & RemoveOwnedFlagBitMask, _length);
+ return new Span<T>((T[])_object, _index, _length);
}
}
{
if (_index < 0)
{
- memoryHandle = ((OwnedMemory<T>)_arrayOrOwnedMemory).Pin();
+ memoryHandle = ((OwnedMemory<T>)_object).Pin();
memoryHandle.AddOffset((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf<T>());
}
else
{
- var array = (T[])_arrayOrOwnedMemory;
+ var array = (T[])_object;
var handle = GCHandle.Alloc(array, GCHandleType.Pinned);
void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index);
memoryHandle = new MemoryHandle(null, pointer, handle);
{
if (_index < 0)
{
- ((OwnedMemory<T>)_arrayOrOwnedMemory).Retain();
- memoryHandle = new MemoryHandle((OwnedMemory<T>)_arrayOrOwnedMemory);
+ ((OwnedMemory<T>)_object).Retain();
+ memoryHandle = new MemoryHandle((OwnedMemory<T>)_object);
}
else
{
{
if (_index < 0)
{
- if (((OwnedMemory<T>)_arrayOrOwnedMemory).TryGetArray(out var segment))
+ if (((OwnedMemory<T>)_object).TryGetArray(out var segment))
{
arraySegment = new ArraySegment<T>(segment.Array, segment.Offset + (_index & RemoveOwnedFlagBitMask), _length);
return true;
}
else
{
- arraySegment = new ArraySegment<T>((T[])_arrayOrOwnedMemory, _index, _length);
+ arraySegment = new ArraySegment<T>((T[])_object, _index, _length);
return true;
}
public bool Equals(Memory<T> other)
{
return
- _arrayOrOwnedMemory == other._arrayOrOwnedMemory &&
+ _object == other._object &&
_index == other._index &&
_length == other._length;
}
[EditorBrowsable(EditorBrowsableState.Never)]
public override int GetHashCode()
{
- return CombineHashCodes(_arrayOrOwnedMemory.GetHashCode(), (_index & RemoveOwnedFlagBitMask).GetHashCode(), _length.GetHashCode());
+ return CombineHashCodes(_object.GetHashCode(), _index.GetHashCode(), _length.GetHashCode());
}
private static int CombineHashCodes(int left, int right)
// See the LICENSE file in the project root for more information.
using System.Buffers;
-using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
-using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
using System.Diagnostics;
-using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
+using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
namespace System
{
+ /// <summary>
+ /// Represents a contiguous region of memory, similar to <see cref="ReadOnlySpan{T}"/>.
+ /// Unlike <see cref="ReadOnlySpan{T}"/>, it is not a byref-like type.
+ /// </summary>
[DebuggerDisplay("{DebuggerDisplay,nq}")]
[DebuggerTypeProxy(typeof(MemoryDebugView<>))]
public readonly struct ReadOnlyMemory<T>
// NOTE: With the current implementation, Memory<T> and ReadOnlyMemory<T> must have the same layout,
// as code uses Unsafe.As to cast between them.
- // The highest order bit of _index is used to discern whether _arrayOrOwnedMemory is an array or an owned memory
- // if (_index >> 31) == 1, object _arrayOrOwnedMemory is an OwnedMemory<T>
- // else, object _arrayOrOwnedMemory is a T[]
- private readonly object _arrayOrOwnedMemory;
+ // 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
+ private readonly object _object;
private readonly int _index;
private readonly int _length;
if (array == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
- _arrayOrOwnedMemory = array;
+ _object = array;
_index = 0;
_length = array.Length;
}
if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
ThrowHelper.ThrowArgumentOutOfRangeException();
- _arrayOrOwnedMemory = array;
+ _object = array;
_index = start;
_length = length;
}
-
- // Constructor for internal use only.
+
+ /// <summary>Creates a new memory over the existing object, start, and length. No validation is performed.</summary>
+ /// <param name="obj">The target object.</param>
+ /// <param name="start">The index at which to begin the memory.</param>
+ /// <param name="length">The number of items in the memory.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal ReadOnlyMemory(OwnedMemory<T> owner, int index, int length)
+ internal ReadOnlyMemory(object obj, int start, int length)
{
- if (owner == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.ownedMemory);
- if (index < 0 || length < 0)
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- _arrayOrOwnedMemory = owner;
- _index = index | (1 << 31); // Before using _index, check if _index < 0, then 'and' it with RemoveOwnedFlagBitMask
+ // No validation performed; caller must provide any necessary validation.
+ _object = obj;
+ _index = start;
_length = length;
}
public ReadOnlyMemory<T> Slice(int start)
{
if ((uint)start > (uint)_length)
- ThrowHelper.ThrowArgumentOutOfRangeException();
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+ }
- if (_index < 0)
- return new ReadOnlyMemory<T>((OwnedMemory<T>)_arrayOrOwnedMemory, (_index & RemoveOwnedFlagBitMask) + start, _length - start);
- return new ReadOnlyMemory<T>((T[])_arrayOrOwnedMemory, _index + start, _length - start);
+ return new ReadOnlyMemory<T>(_object, _index + start, _length - start);
}
/// <summary>
public ReadOnlyMemory<T> Slice(int start, int length)
{
if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
- ThrowHelper.ThrowArgumentOutOfRangeException();
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+ }
- if (_index < 0)
- return new ReadOnlyMemory<T>((OwnedMemory<T>)_arrayOrOwnedMemory, (_index & RemoveOwnedFlagBitMask) + start, length);
- return new ReadOnlyMemory<T>((T[])_arrayOrOwnedMemory, _index + start, length);
+ return new ReadOnlyMemory<T>(_object, _index + start, length);
}
/// <summary>
get
{
if (_index < 0)
- return ((OwnedMemory<T>)_arrayOrOwnedMemory).Span.Slice(_index & RemoveOwnedFlagBitMask, _length);
- return new ReadOnlySpan<T>((T[])_arrayOrOwnedMemory, _index, _length);
+ {
+ return ((OwnedMemory<T>)_object).Span.Slice(_index & RemoveOwnedFlagBitMask, _length);
+ }
+ else if (typeof(T) == typeof(char) && _object is string s)
+ {
+ return new ReadOnlySpan<T>(ref Unsafe.As<char, T>(ref s.GetRawStringData()), s.Length).Slice(_index, _length);
+ }
+ else
+ {
+ return new ReadOnlySpan<T>((T[])_object, _index, _length);
+ }
}
}
+ /// <summary>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 the memory's address can be taken and used.
+ /// </param>
public unsafe MemoryHandle Retain(bool pin = false)
{
MemoryHandle memoryHandle;
{
if (_index < 0)
{
- memoryHandle = ((OwnedMemory<T>)_arrayOrOwnedMemory).Pin();
+ memoryHandle = ((OwnedMemory<T>)_object).Pin();
memoryHandle.AddOffset((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf<T>());
}
+ else if (typeof(T) == typeof(char) && _object is string s)
+ {
+ GCHandle handle = GCHandle.Alloc(s, GCHandleType.Pinned);
+ void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref s.GetRawStringData()), _index);
+ memoryHandle = new MemoryHandle(null, pointer, handle);
+ }
else
{
- var array = (T[])_arrayOrOwnedMemory;
+ var array = (T[])_object;
var handle = GCHandle.Alloc(array, GCHandleType.Pinned);
void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index);
memoryHandle = new MemoryHandle(null, pointer, handle);
{
if (_index < 0)
{
- ((OwnedMemory<T>)_arrayOrOwnedMemory).Retain();
- memoryHandle = new MemoryHandle((OwnedMemory<T>)_arrayOrOwnedMemory);
+ ((OwnedMemory<T>)_object).Retain();
+ memoryHandle = new MemoryHandle((OwnedMemory<T>)_object);
}
else
{
{
if (_index < 0)
{
- if (((OwnedMemory<T>)_arrayOrOwnedMemory).TryGetArray(out var segment))
+ if (((OwnedMemory<T>)_object).TryGetArray(out var segment))
{
arraySegment = new ArraySegment<T>(segment.Array, segment.Offset + (_index & RemoveOwnedFlagBitMask), _length);
return true;
}
else
{
- arraySegment = new ArraySegment<T>((T[])_arrayOrOwnedMemory, _index, _length);
- return true;
+ T[] arr = _object as T[];
+ if (typeof(T) != typeof(char) || arr != null)
+ {
+ arraySegment = new ArraySegment<T>(arr, _index, _length);
+ return true;
+ }
}
- arraySegment = default(ArraySegment<T>);
+ arraySegment = default;
return false;
}
/// </summary>
public T[] ToArray() => Span.ToArray();
+ /// <summary>Determines whether the specified object is equal to the current object.</summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool Equals(object obj)
{
public bool Equals(ReadOnlyMemory<T> other)
{
return
- _arrayOrOwnedMemory == other._arrayOrOwnedMemory &&
+ _object == other._object &&
_index == other._index &&
_length == other._length;
}
- [EditorBrowsable( EditorBrowsableState.Never)]
+ /// <summary>Returns the hash code for this <see cref="ReadOnlyMemory{T}"/></summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
public override int GetHashCode()
{
- return CombineHashCodes(_arrayOrOwnedMemory.GetHashCode(), (_index & RemoveOwnedFlagBitMask).GetHashCode(), _length.GetHashCode());
+ return CombineHashCodes(_object.GetHashCode(), _index.GetHashCode(), _length.GetHashCode());
}
private static int CombineHashCodes(int left, int right)
return CombineHashCodes(CombineHashCodes(h1, h2), h3);
}
+ /// <summary>Gets the state of the memory as individual fields.</summary>
+ /// <param name="start">The offset.</param>
+ /// <param name="length">The count.</param>
+ /// <returns>The object.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal object GetObjectStartLength(out int start, out int length)
+ {
+ start = _index;
+ length = _length;
+ return _object;
+ }
}
}