Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/System.Memory/src/System.Memory.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,13 @@
<Reference Include="System.Reflection" />
<Reference Include="System.Resources.ResourceManager" />
<Reference Include="System.Runtime" />
<Reference Include="System.Numerics.Vectors" />
<Reference Condition="'$(TargetGroup)' != 'netstandard1.0'" Include="System.Runtime.CompilerServices.Unsafe" />
<ProjectReference Condition="'$(TargetGroup)' == 'netstandard1.0'" Include="..\..\System.Runtime.CompilerServices.Unsafe\ref\System.Runtime.CompilerServices.Unsafe.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(IsPartialFacadeAssembly)' == 'true'">
<Reference Include="System.Runtime.CompilerServices.Unsafe" />
<Reference Include="System.Numerics.Vectors" />
<ReferenceFromRuntime Include="System.Private.CoreLib" />
<ProjectReference Include="..\..\System.Runtime\src\System.Runtime.csproj" />
<ProjectReference Include="..\..\System.Diagnostics.Debug\src\System.Diagnostics.Debug.csproj" />
Expand Down
1 change: 0 additions & 1 deletion src/System.Memory/src/System/Pinnable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// 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.Diagnostics;
using System.Runtime.InteropServices;

namespace System
Expand Down
43 changes: 19 additions & 24 deletions src/System.Memory/src/System/ReadOnlySpan.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ public struct ReadOnlySpan<T>
public ReadOnlySpan(T[] array)
{
if (array == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
if (default(T) == null && array.GetType() != typeof(T[]))
ThrowHelper.ThrowArgumentNullException_Array();
// Check will be Jitted out for ValueTypes
if (!SpanHelpers.IsValueType<T>() && array.GetType() != typeof(T[]))
ThrowHelper.ThrowArrayTypeMismatchException_ArrayTypeMustBeExactMatch(typeof(T));

_length = array.Length;
Expand All @@ -52,16 +53,13 @@ public ReadOnlySpan(T[] array)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan(T[] array, int start)
{
if (array == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
if (default(T) == null && array.GetType() != typeof(T[]))
if (array == null || (uint)start > (uint)array.Length)
ThrowHelper.ThrowArgumentNullOrOutOfRangeException(array);
// Check will be Jitted out for ValueTypes
if (!SpanHelpers.IsValueType<T>() && array.GetType() != typeof(T[]))
ThrowHelper.ThrowArrayTypeMismatchException_ArrayTypeMustBeExactMatch(typeof(T));

int arrayLength = array.Length;
if ((uint)start > (uint)arrayLength)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);

_length = arrayLength - start;
_length = array.Length - start;
_pinnable = Unsafe.As<Pinnable<T>>(array);
_byteOffset = SpanHelpers.PerTypeValues<T>.ArrayAdjustment.Add<T>(start);
}
Expand All @@ -82,12 +80,11 @@ public ReadOnlySpan(T[] array, int start)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan(T[] array, int start, int length)
{
if (array == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
if (default(T) == null && array.GetType() != typeof(T[]))
if (array == null || (uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
ThrowHelper.ThrowArgumentNullOrOutOfRangeException(array);
// Check will be Jitted out for ValueTypes
if (!SpanHelpers.IsValueType<T>() && array.GetType() != typeof(T[]))
ThrowHelper.ThrowArrayTypeMismatchException_ArrayTypeMustBeExactMatch(typeof(T));
if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);

_length = length;
_pinnable = Unsafe.As<Pinnable<T>>(array);
Expand All @@ -111,10 +108,11 @@ public ReadOnlySpan(T[] array, int start, int length)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe ReadOnlySpan(void* pointer, int length)
{
// Check will be Jitted out for valid types
if (SpanHelpers.IsReferenceOrContainsReferences<T>())
ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T));
if (length < 0)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
ThrowHelper.ThrowArgumentOutOfRangeException_Length();

_length = length;
_pinnable = null;
Expand All @@ -138,10 +136,8 @@ public unsafe ReadOnlySpan(void* pointer, int length)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<T> DangerousCreate(object obj, ref T objectData, int length)
{
if (obj == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.obj);
if (length < 0)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length);
if (obj == null || length < 0)
ThrowHelper.ThrowArgumentNullOrOutOfRangeException(length);

Pinnable<T> pinnable = Unsafe.As<Pinnable<T>>(obj);
IntPtr byteOffset = Unsafe.ByteOffset<T>(ref pinnable.Data, ref objectData);
Expand Down Expand Up @@ -315,11 +311,10 @@ public override int GetHashCode()
public ReadOnlySpan<T> Slice(int start)
{
if ((uint)start > (uint)_length)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
ThrowHelper.ThrowArgumentOutOfRangeException_Start();

IntPtr newOffset = _byteOffset.Add<T>(start);
int length = _length - start;
return new ReadOnlySpan<T>(_pinnable, newOffset, length);
return new ReadOnlySpan<T>(_pinnable, newOffset, _length - start);
}

/// <summary>
Expand All @@ -334,7 +329,7 @@ public ReadOnlySpan<T> Slice(int start)
public ReadOnlySpan<T> Slice(int start, int length)
{
if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
ThrowHelper.ThrowArgumentOutOfRangeException_Start();

IntPtr newOffset = _byteOffset.Add<T>(start);
return new ReadOnlySpan<T>(_pinnable, newOffset, length);
Expand Down
72 changes: 34 additions & 38 deletions src/System.Memory/src/System/Span.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ public struct Span<T>
public Span(T[] array)
{
if (array == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
if (default(T) == null && array.GetType() != typeof(T[]))
ThrowHelper.ThrowArgumentNullException_Array();
// Check will be Jitted out for ValueTypes
if (!SpanHelpers.IsValueType<T>() && array.GetType() != typeof(T[]))
ThrowHelper.ThrowArrayTypeMismatchException_ArrayTypeMustBeExactMatch(typeof(T));

_length = array.Length;
Expand All @@ -52,16 +53,13 @@ public Span(T[] array)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span(T[] array, int start)
{
if (array == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
if (default(T) == null && array.GetType() != typeof(T[]))
if (array == null || (uint)start > (uint)array.Length)
ThrowHelper.ThrowArgumentNullOrOutOfRangeException(array);
// Check will be Jitted out for ValueTypes
if (!SpanHelpers.IsValueType<T>() && array.GetType() != typeof(T[]))
ThrowHelper.ThrowArrayTypeMismatchException_ArrayTypeMustBeExactMatch(typeof(T));

int arrayLength = array.Length;
if ((uint)start > (uint)arrayLength)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);

_length = arrayLength - start;
_length = array.Length - start;
_pinnable = Unsafe.As<Pinnable<T>>(array);
_byteOffset = SpanHelpers.PerTypeValues<T>.ArrayAdjustment.Add<T>(start);
}
Expand All @@ -82,12 +80,11 @@ public Span(T[] array, int start)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span(T[] array, int start, int length)
{
if (array == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
if (default(T) == null && array.GetType() != typeof(T[]))
if (array == null || (uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
ThrowHelper.ThrowArgumentNullOrOutOfRangeException(array);
// Check will be Jitted out for ValueTypes
if (!SpanHelpers.IsValueType<T>() && array.GetType() != typeof(T[]))
ThrowHelper.ThrowArrayTypeMismatchException_ArrayTypeMustBeExactMatch(typeof(T));
if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);

_length = length;
_pinnable = Unsafe.As<Pinnable<T>>(array);
Expand All @@ -111,10 +108,11 @@ public Span(T[] array, int start, int length)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe Span(void* pointer, int length)
{
// Check will be Jitted out for valid types
if (SpanHelpers.IsReferenceOrContainsReferences<T>())
ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T));
if (length < 0)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
ThrowHelper.ThrowArgumentOutOfRangeException_Length();

_length = length;
_pinnable = null;
Expand All @@ -138,10 +136,8 @@ public unsafe Span(void* pointer, int length)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> DangerousCreate(object obj, ref T objectData, int length)
{
if (obj == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.obj);
if (length < 0)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length);
if (obj == null || length < 0)
ThrowHelper.ThrowArgumentNullOrOutOfRangeException(length);

Pinnable<T> pinnable = Unsafe.As<Pinnable<T>>(obj);
IntPtr byteOffset = Unsafe.ByteOffset<T>(ref pinnable.Data, ref objectData);
Expand Down Expand Up @@ -244,6 +240,7 @@ public unsafe void Clear()

var byteLength = (UIntPtr)((uint)length * Unsafe.SizeOf<T>());

// Branch will be Jit eliminated
if ((Unsafe.SizeOf<T>() & (sizeof(IntPtr) - 1)) != 0)
{
if (_pinnable == null)
Expand All @@ -261,6 +258,7 @@ public unsafe void Clear()
}
else
{
// Branch will be Jit eliminated
if (SpanHelpers.IsReferenceOrContainsReferences<T>())
{
UIntPtr pointerSizedLength = (UIntPtr)((length * Unsafe.SizeOf<T>()) / sizeof(IntPtr));
Expand Down Expand Up @@ -288,6 +286,7 @@ public unsafe void Fill(T value)
if (length == 0)
return;

// Branch will be Jit eliminated
if (Unsafe.SizeOf<T>() == 1)
{
byte fill = Unsafe.As<T, byte>(ref value);
Expand Down Expand Up @@ -463,11 +462,10 @@ public override int GetHashCode()
public Span<T> Slice(int start)
{
if ((uint)start > (uint)_length)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
ThrowHelper.ThrowArgumentOutOfRangeException_Start();

IntPtr newOffset = _byteOffset.Add<T>(start);
int length = _length - start;
return new Span<T>(_pinnable, newOffset, length);
return new Span<T>(_pinnable, newOffset, _length - start);
}

/// <summary>
Expand All @@ -482,7 +480,7 @@ public Span<T> Slice(int start)
public Span<T> Slice(int start, int length)
{
if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
ThrowHelper.ThrowArgumentOutOfRangeException_Start();

IntPtr newOffset = _byteOffset.Add<T>(start);
return new Span<T>(_pinnable, newOffset, length);
Expand Down Expand Up @@ -562,6 +560,7 @@ public static class Span
public static Span<byte> AsBytes<T>(this Span<T> source)
where T : struct
{
// Check will be Jitted out for valid types
if (SpanHelpers.IsReferenceOrContainsReferences<T>())
ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T));

Expand All @@ -584,6 +583,7 @@ public static Span<byte> AsBytes<T>(this Span<T> source)
public static ReadOnlySpan<byte> AsBytes<T>(this ReadOnlySpan<T> source)
where T : struct
{
// Check will be Jitted out for valid types
if (SpanHelpers.IsReferenceOrContainsReferences<T>())
ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T));

Expand All @@ -610,9 +610,10 @@ public static Span<TTo> NonPortableCast<TFrom, TTo>(this Span<TFrom> source)
where TFrom : struct
where TTo : struct
{
// Check will be Jitted out for valid types
if (SpanHelpers.IsReferenceOrContainsReferences<TFrom>())
ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(TFrom));

// Check will be Jitted out for valid types
if (SpanHelpers.IsReferenceOrContainsReferences<TTo>())
ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(TTo));

Expand All @@ -639,9 +640,10 @@ public static ReadOnlySpan<TTo> NonPortableCast<TFrom, TTo>(this ReadOnlySpan<TF
where TFrom : struct
where TTo : struct
{
// Check will be Jitted out for valid types
if (SpanHelpers.IsReferenceOrContainsReferences<TFrom>())
ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(TFrom));

// Check will be Jitted out for valid types
if (SpanHelpers.IsReferenceOrContainsReferences<TTo>())
ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(TTo));

Expand All @@ -658,7 +660,7 @@ public static ReadOnlySpan<TTo> NonPortableCast<TFrom, TTo>(this ReadOnlySpan<TF
public static ReadOnlySpan<char> Slice(this string text)
{
if (text == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
ThrowHelper.ThrowArgumentNullException_Text();

return new ReadOnlySpan<char>(Unsafe.As<Pinnable<char>>(text), StringAdjustment, text.Length);
}
Expand All @@ -675,16 +677,13 @@ public static ReadOnlySpan<char> Slice(this string text)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<char> Slice(this string text, int start)
{
if (text == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
int textLength = text.Length;
if ((uint)start > (uint)textLength)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
if (text == null || (uint)start > (uint)text.Length)
ThrowHelper.ThrowArgumentNullOrOutOfRangeException(text);

unsafe
{
byte* byteOffset = ((byte*)StringAdjustment) + (uint)(start * sizeof(char));
return new ReadOnlySpan<char>(Unsafe.As<Pinnable<char>>(text), (IntPtr)byteOffset, textLength - start);
return new ReadOnlySpan<char>(Unsafe.As<Pinnable<char>>(text), (IntPtr)byteOffset, text.Length - start);
}
}

Expand All @@ -701,11 +700,8 @@ public static ReadOnlySpan<char> Slice(this string text, int start)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<char> Slice(this string text, int start, int length)
{
if (text == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
int textLength = text.Length;
if ((uint)start > (uint)textLength || (uint)length > (uint)(textLength - start))
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
if (text == null || (uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
ThrowHelper.ThrowArgumentNullOrOutOfRangeException(text);

unsafe
{
Expand Down
9 changes: 8 additions & 1 deletion src/System.Memory/src/System/SpanExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// 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.Diagnostics;
using System.Runtime.CompilerServices;

namespace System
Expand Down Expand Up @@ -32,6 +31,10 @@ public static int IndexOf<T>(this Span<T> span, T value)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOf(this Span<byte> span, byte value)
{
if (!BitConverter.IsLittleEndian)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we care about endianness here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The locate byte doesn't work on big-endian as it highlights the wrong byte (due to order reversal) aspnet/KestrelHttpServer#1138 (comment); don't know if its worth the effort to come up with something that does work on big-endian though /cc @halter73

Copy link
Copy Markdown
Member

@KrzysztofCwalina KrzysztofCwalina Feb 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, makes sense. Do you know how much this branch adds (if any) to "early" searches? i.e. search that finds the byte at index 0.

Copy link
Copy Markdown
Member Author

@benaadams benaadams Feb 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may suffer from a static base initalization check if its first use; and not do branch elimination. Subsequent uses (in different function) will branch eliminate.

@jkotas would there me any mileage in adding a use of BitConverter.IsLittleEndian to something like CommonlyUsedGenericInstantiations as they are runtime fixed and mainly used for branch elimination; so the check is pre-paid?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already issue for it https://github.com/dotnet/coreclr/issues/9701 serendipity 😺

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added comment to dotnet/coreclr#9701 on what would be needed to fix it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 For keeping a big-endian path working. It would be nice if we could test on big-endian architectures.

In Kestrel, we declared big-endian bankruptcy a while ago, so Kestrel now throws on startup if it detects that it's running on a big-endian machine. I don't think that's really an option for corefx APIs.

{
return SpanHelpers.IndexOfBigEndian(ref span.DangerousGetPinnableReference(), value, span.Length);
}
return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), value, span.Length);
}

Expand Down Expand Up @@ -131,6 +134,10 @@ public static int IndexOf<T>(this ReadOnlySpan<T> span, T value)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOf(this ReadOnlySpan<byte> span, byte value)
{
if (!BitConverter.IsLittleEndian)
{
return SpanHelpers.IndexOfBigEndian(ref span.DangerousGetPinnableReference(), value, span.Length);
}
return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), value, span.Length);
}

Expand Down
Loading