Argument_NativeOverlappedAlreadyFree = 'overlapped' has already been freed.
Argument_AlreadyBoundOrSyncHandle = 'handle' has already been bound to the thread pool, or was not opened for asynchronous I/O.
#if FEATURE_SPAN_OF_T
-Argument_InvalidTypeWithPointersNotSupported = Cannot use type '{0}'. Only value types without pointers are supported.
+Argument_InvalidTypeWithPointersNotSupported = Cannot use type '{0}'. Only value types without pointers or references are supported.
#endif // FEATURE_SPAN_OF_T
;
namespace System
{
/// <summary>
- /// ReadOnlySpan is a uniform API for dealing with arrays and subarrays, strings
- /// and substrings, and unmanaged memory buffers. It adds minimal overhead
- /// to regular accesses and is a struct so that creation and subslicing do
- /// not require additional allocations. It is type- and memory-safe.
+ /// ReadOnlySpan represents contiguous read-only region of arbitrary memory, with performance
+ /// characteristics on par with T[]. Unlike arrays, it can point to either managed
+ /// or native memory, or to memory allocated on the stack. It is type- and memory-safe.
/// </summary>
- public struct ReadOnlySpan<T>
+ public unsafe struct ReadOnlySpan<T>
{
/// <summary>A byref or a native ptr. Do not access directly</summary>
- internal /* readonly */ IntPtr _rawPointer;
+ private readonly IntPtr _rawPointer;
/// <summary>The number of elements this ReadOnlySpan contains.</summary>
- internal readonly int _length;
+ private readonly int _length;
/// <summary>
/// Creates a new span over the entirety of the target array.
if (array == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
- JitHelpers.SetByRef(out _rawPointer, ref JitHelpers.GetArrayData(array));
+ // TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
+ _rawPointer = (IntPtr)Unsafe.AsPointer(ref JitHelpers.GetArrayData(array));
_length = array.Length;
}
{
if (array == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
- if ((uint)start >= (uint)array.Length || (uint)length > (uint)(array.Length - start))
+ if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
ThrowHelper.ThrowArgumentOutOfRangeException();
- JitHelpers.SetByRef(out _rawPointer, ref Unsafe.Add(ref JitHelpers.GetArrayData(array), start));
+ // TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
+ _rawPointer = (IntPtr)Unsafe.AsPointer(ref Unsafe.Add(ref JitHelpers.GetArrayData(array), start));
_length = length;
}
/// Thrown when the specified <paramref name="length"/> is negative.
/// </exception>
[CLSCompliant(false)]
- public unsafe ReadOnlySpan(void* ptr, int length)
+ public unsafe ReadOnlySpan(void* pointer, int length)
{
if (JitHelpers.ContainsReferences<T>())
ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
if (length < 0)
ThrowHelper.ThrowArgumentOutOfRangeException();
- _rawPointer = (IntPtr)ptr;
+ _rawPointer = (IntPtr)pointer;
_length = length;
}
/// <summary>
- /// An internal helper for creating spans. Not for public use.
+ /// An internal helper for creating spans.
/// </summary>
internal ReadOnlySpan(ref T ptr, int length)
{
- JitHelpers.SetByRef(out _rawPointer, ref ptr);
+ // TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
+ _rawPointer = (IntPtr)Unsafe.AsPointer(ref ptr);
_length = length;
}
/// <summary>
+ /// An internal helper for accessing spans.
+ /// </summary>
+ internal unsafe ref T GetRawPointer()
+ {
+ // TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
+ return ref Unsafe.As<IntPtr, T>(ref *(IntPtr *)_rawPointer);
+ }
+
+ /// <summary>
/// Defines an implicit conversion of a <see cref="Span{T}"/> to a <see cref="ReadOnlySpan{T}"/>
/// </summary>
public static implicit operator ReadOnlySpan<T>(Span<T> slice)
{
- return new ReadOnlySpan<T>(ref JitHelpers.GetByRef<T>(ref slice._rawPointer), slice._length);
+ return new ReadOnlySpan<T>(ref slice.GetRawPointer(), slice.Length);
}
/// <summary>
if ((uint)index >= (uint)_length)
ThrowHelper.ThrowIndexOutOfRangeException();
- return Unsafe.Add(ref JitHelpers.GetByRef<T>(ref _rawPointer), index);
+ return Unsafe.Add(ref GetRawPointer(), index);
}
}
return Array.Empty<T>();
var destination = new T[_length];
- SpanHelper.CopyTo<T>(ref JitHelpers.GetArrayData(destination), ref JitHelpers.GetByRef<T>(ref _rawPointer), _length);
+ SpanHelper.CopyTo<T>(ref JitHelpers.GetArrayData(destination), ref GetRawPointer(), _length);
return destination;
}
if ((uint)start > (uint)_length)
ThrowHelper.ThrowArgumentOutOfRangeException();
- return new ReadOnlySpan<T>(ref Unsafe.Add(ref JitHelpers.GetByRef<T>(ref _rawPointer), start), _length - start);
+ return new ReadOnlySpan<T>(ref Unsafe.Add(ref GetRawPointer(), start), _length - start);
}
/// <summary>
/// </exception>
public ReadOnlySpan<T> Slice(int start, int length)
{
- if ((uint)start >= (uint)_length || (uint)length > (uint)(_length - start))
+ if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
ThrowHelper.ThrowArgumentOutOfRangeException();
- return new ReadOnlySpan<T>(ref Unsafe.Add(ref JitHelpers.GetByRef<T>(ref _rawPointer), start), length);
+ return new ReadOnlySpan<T>(ref Unsafe.Add(ref GetRawPointer(), start), length);
}
/// <summary>
/// </summary>
public bool Equals(ReadOnlySpan<T> other)
{
- return (_length == other._length) &&
- (_length == 0 || Unsafe.AreSame(ref JitHelpers.GetByRef<T>(ref _rawPointer), ref JitHelpers.GetByRef<T>(ref other._rawPointer)));
+ return (_length == other.Length) &&
+ (_length == 0 || Unsafe.AreSame(ref GetRawPointer(), ref other.GetRawPointer()));
}
/// <summary>
/// <param name="destination">The span to copy items into.</param>
public bool TryCopyTo(Span<T> destination)
{
- if (_length > destination._length)
+ if ((uint)_length > (uint)destination.Length)
return false;
- SpanHelper.CopyTo<T>(ref JitHelpers.GetByRef<T>(ref destination._rawPointer), ref JitHelpers.GetByRef<T>(ref _rawPointer), _length);
+ SpanHelper.CopyTo<T>(ref destination.GetRawPointer(), ref GetRawPointer(), _length);
return true;
}
}
{
if (text == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
- if ((uint)start >= (uint)text.Length || (uint)length > (uint)(text.Length - start))
+ if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
ThrowHelper.ThrowArgumentOutOfRangeException();
return new ReadOnlySpan<char>(ref Unsafe.Add(ref text.GetFirstCharRef(), start), length);
/// <summary>
/// Contains generic, low-level functionality for manipulating pointers.
/// </summary>
- internal static class Unsafe
+ internal static unsafe class Unsafe
{
/// <summary>
+ /// Returns a pointer to the given by-ref parameter.
+ /// </summary>
+ [NonVersionable]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void* AsPointer<T>(ref T value)
+ {
+ // The body of this function will be replaced by the EE with unsafe code that just returns sizeof !!T
+ // See getILIntrinsicImplementationForUnsafe for how this happens.
+ throw new InvalidOperationException();
+ }
+
+ /// <summary>
/// Returns the size of an object of the given type parameter.
/// </summary>
[NonVersionable]
public static int SizeOf<T>()
{
// The body of this function will be replaced by the EE with unsafe code that just returns sizeof !!T
- // See getILIntrinsicImplementation for how this happens.
+ // See getILIntrinsicImplementationForUnsafe for how this happens.
throw new InvalidOperationException();
}
public static ref TTo As<TFrom, TTo>(ref TFrom source)
{
// The body of this function will be replaced by the EE with unsafe code that just returns sizeof !!T
- // See getILIntrinsicImplementation for how this happens.
+ // See getILIntrinsicImplementationForUnsafe for how this happens.
throw new InvalidOperationException();
}
public static ref T Add<T>(ref T source, int elementOffset)
{
// The body of this function will be replaced by the EE with unsafe code!!!
- // See getILIntrinsicImplementation for how this happens.
+ // See getILIntrinsicImplementationForUnsafe for how this happens.
typeof(T).ToString(); // Type used by the actual method body
throw new InvalidOperationException();
}
public static bool AreSame<T>(ref T left, ref T right)
{
// The body of this function will be replaced by the EE with unsafe code!!!
- // See getILIntrinsicImplementation for how this happens.
+ // See getILIntrinsicImplementationForUnsafe for how this happens.
throw new InvalidOperationException();
}
}
#endif
#if FEATURE_SPAN_OF_T
- static internal ref T GetByRef<T>(ref IntPtr byref)
- {
- // The body of this function will be replaced by the EE with unsafe code!!!
- // See getILIntrinsicImplementation for how this happens.
- throw new InvalidOperationException();
- }
-
- static internal void SetByRef<T>(out IntPtr byref, ref T value)
- {
- // The body of this function will be replaced by the EE with unsafe code!!!
- // See getILIntrinsicImplementation for how this happens.
- throw new InvalidOperationException();
- }
-
static internal bool ByRefLessThan<T>(ref T refA, ref T refB)
{
// The body of this function will be replaced by the EE with unsafe code!!!
- // See getILIntrinsicImplementation for how this happens.
+ // See getILIntrinsicImplementation for how this happens.
throw new InvalidOperationException();
}
/// <returns>true if given type is reference type or value type that contains references</returns>
static internal bool ContainsReferences<T>()
{
- // See getILIntrinsicImplementation for how this happens.
+ // The body of this function will be replaced by the EE with unsafe code!!!
+ // See getILIntrinsicImplementation for how this happens.
throw new InvalidOperationException();
}
namespace System
{
/// <summary>
- /// Span is a uniform API for dealing with arrays and subarrays, strings
- /// and substrings, and unmanaged memory buffers. It adds minimal overhead
- /// to regular accesses and is a struct so that creation and subslicing do
- /// not require additional allocations. It is type- and memory-safe.
+ /// Span represents contiguous region of arbitrary memory, with performance
+ /// characteristics on par with T[]. Unlike arrays, it can point to either managed
+ /// or native memory, or to memory allocated on the stack. It is type- and memory-safe.
/// </summary>
- public struct Span<T>
+ public unsafe struct Span<T>
{
/// <summary>A byref or a native ptr. Do not access directly</summary>
- internal /* readonly */ IntPtr _rawPointer;
+ private readonly IntPtr _rawPointer;
/// <summary>The number of elements this Span contains.</summary>
- internal readonly int _length;
+ private readonly int _length;
/// <summary>
/// Creates a new span over the entirety of the target array.
ThrowHelper.ThrowArrayTypeMismatchException();
}
- JitHelpers.SetByRef(out _rawPointer, ref JitHelpers.GetArrayData(array));
+ // TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
+ _rawPointer = (IntPtr)Unsafe.AsPointer(ref JitHelpers.GetArrayData(array));
_length = array.Length;
}
if (array.GetType() != typeof(T[]))
ThrowHelper.ThrowArrayTypeMismatchException();
}
- if ((uint)start >= (uint)array.Length || (uint)length > (uint)(array.Length - start))
+ if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
ThrowHelper.ThrowArgumentOutOfRangeException();
- JitHelpers.SetByRef(out _rawPointer, ref Unsafe.Add(ref JitHelpers.GetArrayData(array), start));
+ // TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
+ _rawPointer = (IntPtr)Unsafe.AsPointer(ref Unsafe.Add(ref JitHelpers.GetArrayData(array), start));
_length = length;
}
/// Thrown when the specified <paramref name="length"/> is negative.
/// </exception>
[CLSCompliant(false)]
- public unsafe Span(void* ptr, int length)
+ public unsafe Span(void* pointer, int length)
{
if (JitHelpers.ContainsReferences<T>())
ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
if (length < 0)
ThrowHelper.ThrowArgumentOutOfRangeException();
- _rawPointer = (IntPtr)ptr;
+ _rawPointer = (IntPtr)pointer;
_length = length;
}
/// <summary>
- /// An internal helper for creating spans. Not for public use.
+ /// An internal helper for creating spans.
/// </summary>
internal Span(ref T ptr, int length)
{
- JitHelpers.SetByRef(out _rawPointer, ref ptr);
+ // TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
+ _rawPointer = (IntPtr)Unsafe.AsPointer(ref ptr);
_length = length;
}
/// <summary>
+ /// An internal helper for accessing spans.
+ /// </summary>
+ internal unsafe ref T GetRawPointer()
+ {
+ // TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
+ return ref Unsafe.As<IntPtr, T>(ref *(IntPtr*)_rawPointer);
+ }
+
+ /// <summary>
/// Defines an implicit conversion of an array to a <see cref="Span{T}"/>
/// </summary>
public static implicit operator Span<T>(T[] array)
if ((uint)index >= (uint)_length)
ThrowHelper.ThrowIndexOutOfRangeException();
- return Unsafe.Add(ref JitHelpers.GetByRef<T>(ref _rawPointer), index);
+ return Unsafe.Add(ref GetRawPointer(), index);
}
set
{
if ((uint)index >= (uint)_length)
ThrowHelper.ThrowIndexOutOfRangeException();
- Unsafe.Add(ref JitHelpers.GetByRef<T>(ref _rawPointer), index) = value;
+ Unsafe.Add(ref GetRawPointer(), index) = value;
}
}
return Array.Empty<T>();
var destination = new T[_length];
- SpanHelper.CopyTo<T>(ref JitHelpers.GetArrayData(destination), ref JitHelpers.GetByRef<T>(ref _rawPointer), _length);
+ SpanHelper.CopyTo<T>(ref JitHelpers.GetArrayData(destination), ref GetRawPointer(), _length);
return destination;
}
if ((uint)start > (uint)_length)
ThrowHelper.ThrowArgumentOutOfRangeException();
- return new Span<T>(ref Unsafe.Add(ref JitHelpers.GetByRef<T>(ref _rawPointer), start), _length - start);
+ return new Span<T>(ref Unsafe.Add(ref GetRawPointer(), start), _length - start);
}
/// <summary>
/// </exception>
public Span<T> Slice(int start, int length)
{
- if ((uint)start >= (uint)_length || (uint)length > (uint)(_length - start))
+ if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
ThrowHelper.ThrowArgumentOutOfRangeException();
- return new Span<T>(ref Unsafe.Add(ref JitHelpers.GetByRef<T>(ref _rawPointer), start), length);
+ return new Span<T>(ref Unsafe.Add(ref GetRawPointer(), start), length);
}
/// <summary>
/// </summary>
public bool Equals(Span<T> other)
{
- return (_length == other._length) &&
- (_length == 0 || Unsafe.AreSame(ref JitHelpers.GetByRef<T>(ref _rawPointer), ref JitHelpers.GetByRef<T>(ref other._rawPointer)));
+ return (_length == other.Length) &&
+ (_length == 0 || Unsafe.AreSame(ref GetRawPointer(), ref other.GetRawPointer()));
}
/// <summary>
/// <param name="destination">The span to copy items into.</param>
public bool TryCopyTo(Span<T> destination)
{
- if (Length > destination.Length)
+ if ((uint)_length > (uint)destination.Length)
return false;
- SpanHelper.CopyTo<T>(ref JitHelpers.GetByRef<T>(ref destination._rawPointer), ref JitHelpers.GetByRef<T>(ref _rawPointer), _length);
+ SpanHelper.CopyTo<T>(ref destination.GetRawPointer(), ref GetRawPointer(), _length);
return true;
}
/// </exception>
public void Set(ReadOnlySpan<T> values)
{
- if ((uint)values._length > (uint)_length)
+ if ((uint)values.Length > (uint)_length)
ThrowHelper.ThrowArgumentOutOfRangeException();
- SpanHelper.CopyTo<T>(ref JitHelpers.GetByRef<T>(ref _rawPointer), ref JitHelpers.GetByRef<T>(ref values._rawPointer), values._length);
+ SpanHelper.CopyTo<T>(ref GetRawPointer(), ref values.GetRawPointer(), values.Length);
}
}
{
/// <summary>
/// Casts a Span of one primitive type <typeparamref name="T"/> to Span of bytes.
- /// That type may not contain pointers. This is checked at runtime in order to preserve type safety.
+ /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety.
/// </summary>
/// <param name="source">The source slice, of type <typeparamref name="T"/>.</param>
/// <exception cref="System.ArgumentException">
ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
return new Span<byte>(
- ref JitHelpers.GetByRef<byte>(ref source._rawPointer),
- checked((int)(source._length * Unsafe.SizeOf<T>())));
+ ref Unsafe.As<T, byte>(ref source.GetRawPointer()),
+ checked(source.Length * Unsafe.SizeOf<T>()));
}
/// <summary>
/// Casts a ReadOnlySpan of one primitive type <typeparamref name="T"/> to ReadOnlySpan of bytes.
- /// That type may not contain pointers. This is checked at runtime in order to preserve type safety.
+ /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety.
/// </summary>
/// <param name="source">The source slice, of type <typeparamref name="T"/>.</param>
/// <exception cref="System.ArgumentException">
ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
return new ReadOnlySpan<byte>(
- ref JitHelpers.GetByRef<byte>(ref source._rawPointer),
- checked((int)(source._length * Unsafe.SizeOf<T>())));
+ ref Unsafe.As<T, byte>(ref source.GetRawPointer()),
+ checked(source.Length * Unsafe.SizeOf<T>()));
}
/// <summary>
/// Casts a Span of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>.
- /// These types may not contain pointers. This is checked at runtime in order to preserve type safety.
+ /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety.
/// </summary>
/// <remarks>
/// Supported only for platforms that support misaligned memory access.
ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo));
return new Span<TTo>(
- ref JitHelpers.GetByRef<TTo>(ref source._rawPointer),
- checked((int)(source._length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>())));
+ ref Unsafe.As<TFrom, TTo>(ref source.GetRawPointer()),
+ checked((int)((long)source.Length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>())));
}
/// <summary>
/// Casts a ReadOnlySpan of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>.
- /// These types may not contain pointers. This is checked at runtime in order to preserve type safety.
+ /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety.
/// </summary>
/// <remarks>
/// Supported only for platforms that support misaligned memory access.
ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo));
return new ReadOnlySpan<TTo>(
- ref JitHelpers.GetByRef<TTo>(ref source._rawPointer),
- checked((int)(source._length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>())));
+ ref Unsafe.As<TFrom, TTo>(ref source.GetRawPointer()),
+ checked((int)((long)source.Length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>())));
}
}
ArgumentException_NotAllCustomSortingFuncsDefined = Implementations of all the NLS functions must be provided.
ArgumentException_MinSortingVersion = The runtime does not support a version of "{0}" less than {1}.
#if FEATURE_SPAN_OF_T
-Argument_InvalidTypeWithPointersNotSupported = Cannot use type '{0}'. Only value types without pointers are supported.
+Argument_InvalidTypeWithPointersNotSupported = Cannot use type '{0}'. Only value types without pointers or references are supported.
#endif // FEATURE_SPAN_OF_T
}
}
#ifdef FEATURE_SPAN_OF_T
- else if (tk == MscorlibBinder::GetMethod(METHOD__JIT_HELPERS__GET_BYREF)->GetMemberDef())
- {
- // TODO-SPAN: This has potential GC hole. It needs to be JIT intrinsic instead
- static const BYTE ilcode[] = { CEE_LDARG_0, CEE_LDIND_I, CEE_RET };
- methInfo->ILCode = const_cast<BYTE*>(ilcode);
- methInfo->ILCodeSize = sizeof(ilcode);
- methInfo->maxStack = 1;
- methInfo->EHcount = 0;
- methInfo->options = (CorInfoOptions)0;
- return true;
- }
- else if (tk == MscorlibBinder::GetMethod(METHOD__JIT_HELPERS__SET_BYREF)->GetMemberDef())
- {
- // TODO-SPAN: This has potential GC hole. It needs to be JIT intrinsic instead
- static const BYTE ilcode[] = { CEE_LDARG_0, CEE_LDARG_1, CEE_STIND_I, CEE_RET };
- methInfo->ILCode = const_cast<BYTE*>(ilcode);
- methInfo->ILCodeSize = sizeof(ilcode);
- methInfo->maxStack = 2;
- methInfo->EHcount = 0;
- methInfo->options = (CorInfoOptions)0;
- return true;
- }
else if (tk == MscorlibBinder::GetMethod(METHOD__JIT_HELPERS__BYREF_LESSTHAN)->GetMemberDef())
{
// Compare the two arguments
mdMethodDef tk = ftn->GetMemberDef();
+ if (tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__AS_POINTER)->GetMemberDef())
+ {
+ // Return the argument that was passed in.
+ static const BYTE ilcode[] = { CEE_LDARG_0, CEE_CONV_U, CEE_RET };
+ methInfo->ILCode = const_cast<BYTE*>(ilcode);
+ methInfo->ILCodeSize = sizeof(ilcode);
+ methInfo->maxStack = 1;
+ methInfo->EHcount = 0;
+ methInfo->options = (CorInfoOptions)0;
+ return true;
+ }
if (tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__SIZEOF)->GetMemberDef())
{
_ASSERTE(ftn->HasMethodInstantiation());
DEFINE_METHOD(JIT_HELPERS, UNSAFE_CAST_TO_STACKPTR,UnsafeCastToStackPointer, NoSig)
#endif // _DEBUG
#ifdef FEATURE_SPAN_OF_T
-DEFINE_METHOD(JIT_HELPERS, GET_BYREF, GetByRef, NoSig)
-DEFINE_METHOD(JIT_HELPERS, SET_BYREF, SetByRef, NoSig)
DEFINE_METHOD(JIT_HELPERS, BYREF_LESSTHAN, ByRefLessThan, NoSig)
DEFINE_METHOD(JIT_HELPERS, GET_ARRAY_DATA, GetArrayData, NoSig)
DEFINE_METHOD(JIT_HELPERS, CONTAINSREFERENCES, ContainsReferences, NoSig)
#ifdef FEATURE_SPAN_OF_T
DEFINE_CLASS(UNSAFE, CompilerServices, Unsafe)
+DEFINE_METHOD(UNSAFE, AS_POINTER, AsPointer, NoSig)
DEFINE_METHOD(UNSAFE, SIZEOF, SizeOf, NoSig)
DEFINE_METHOD(UNSAFE, BYREF_AS, As, NoSig)
DEFINE_METHOD(UNSAFE, BYREF_ADD, Add, NoSig)
Test(CanAccessItemsViaIndexer, "CanAccessItemsViaIndexer", ref failedTestsCount);
+ Test(TestBoundaryEmptySpan, "TestBoundaryEmptySpan", ref failedTestsCount);
+
Test(ReferenceTypesAreSupported, "ReferenceTypesAreSupported", ref failedTestsCount);
Test(CanUpdateUnderlyingArray, "CanUpdateUnderlyingArray", ref failedTestsCount);
AssertTrue(Sum(subslice) == 5, "Failed to sum subslice");
}
+ static TestBoundaryEmptySpan()
+ {
+ int[] a = new byte[5];
+
+ Span<int> slice = new Span<int>(a, a.Length, 0);
+ AssertEqual(slice.Length, 0);
+
+ Span<int> subSlice = new Span<int>(a).Slice(a.Length, 0);
+ AssertEqual(subSlice.Length, 0);
+ }
+
static void ReferenceTypesAreSupported()
{
var underlyingArray = new ReferenceType[] { new ReferenceType(0), new ReferenceType(1), new ReferenceType(2) };
}
catch (System.ArgumentException ex)
{
- AssertTrue(ex.Message == "Cannot use type 'ValueTypeWithPointers'. Only value types without pointers are supported.",
+ AssertTrue(ex.Message == "Cannot use type 'ValueTypeWithPointers'. Only value types without pointers or references are supported.",
"Exception message is incorrect");
}
}
catch (System.ArgumentException ex)
{
- AssertTrue(ex.Message == "Cannot use type 'ReferenceType'. Only value types without pointers are supported.",
+ AssertTrue(ex.Message == "Cannot use type 'ReferenceType'. Only value types without pointers or references are supported.",
"Exception message is incorrect");
}
}
}
catch (System.ArgumentException ex)
{
- AssertTrue(ex.Message == "Cannot use type 'ValueTypeWithPointers'. Only value types without pointers are supported.",
+ AssertTrue(ex.Message == "Cannot use type 'ValueTypeWithPointers'. Only value types without pointers or references are supported.",
"Exception message is incorrect");
}
}
catch (System.ArgumentException ex)
{
- AssertTrue(ex.Message == "Cannot use type 'ValueTypeWithPointers'. Only value types without pointers are supported.",
+ AssertTrue(ex.Message == "Cannot use type 'ValueTypeWithPointers'. Only value types without pointers or references are supported.",
"Exception message is incorrect");
}
}
catch (System.ArgumentException ex)
{
- AssertTrue(ex.Message == "Cannot use type 'ValueTypeWithPointers'. Only value types without pointers are supported.",
+ AssertTrue(ex.Message == "Cannot use type 'ValueTypeWithPointers'. Only value types without pointers or references are supported.",
"Exception message is incorrect");
}
}