Skip to content

Commit

Permalink
Use early zero-extension
Browse files Browse the repository at this point in the history
  • Loading branch information
xtqqczze committed Dec 15, 2024
1 parent 3aa1ec5 commit a109206
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 22 deletions.
34 changes: 23 additions & 11 deletions src/libraries/System.Private.CoreLib/src/System/ReadOnlySpan.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,23 +62,26 @@ public ReadOnlySpan(T[]? array)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan(T[]? array, int start, int length)
{
// Early zero-extension
nint offset = (nint)(uint)start;

if (array == null)
{
if (start != 0 || length != 0)
if ((int)offset != 0 || length != 0)
ThrowHelper.ThrowArgumentOutOfRangeException();
this = default;
return; // returns default
}
#if TARGET_64BIT
// See comment in Span<T>.Slice for how this works.
if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)array.Length)
if ((ulong)offset + (ulong)(uint)length > (ulong)(uint)array.Length)
ThrowHelper.ThrowArgumentOutOfRangeException();
#else
if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
if ((uint)offset > (uint)array.Length || (uint)length > (uint)array.Length - (uint)offset)
ThrowHelper.ThrowArgumentOutOfRangeException();
#endif

_reference = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), (nint)(uint)start /* force zero-extension */);
_reference = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), offset);
_length = length;
}

Expand Down Expand Up @@ -143,9 +146,12 @@ public ref readonly T this[int index]
[NonVersionable]
get
{
if ((uint)index >= (uint)_length)
// Early zero-extension
nint offset = (nint)(uint)index;

if ((uint)offset >= (uint)_length)
ThrowHelper.ThrowIndexOutOfRangeException();
return ref Unsafe.Add(ref _reference, (nint)(uint)index /* force zero-extension */);
return ref Unsafe.Add(ref _reference, offset);
}
}

Expand Down Expand Up @@ -356,10 +362,13 @@ public override string ToString()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> Slice(int start)
{
if ((uint)start > (uint)_length)
// Early zero-extension
nint offset = (nint)(uint)start;

if ((uint)offset > (uint)_length)
ThrowHelper.ThrowArgumentOutOfRangeException();

return new ReadOnlySpan<T>(ref Unsafe.Add(ref _reference, (nint)(uint)start /* force zero-extension */), _length - start);
return new ReadOnlySpan<T>(ref Unsafe.Add(ref _reference, offset), _length - start);
}

/// <summary>
Expand All @@ -373,16 +382,19 @@ public ReadOnlySpan<T> Slice(int start)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> Slice(int start, int length)
{
// Early zero-extension
nint offset = (nint)(uint)start;

#if TARGET_64BIT
// See comment in Span<T>.Slice for how this works.
if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)_length)
if ((ulong)offset + (ulong)(uint)length > (ulong)(uint)_length)
ThrowHelper.ThrowArgumentOutOfRangeException();
#else
if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
if ((uint)offset > (uint)_length || (uint)length > (uint)_length - (uint)offset)
ThrowHelper.ThrowArgumentOutOfRangeException();
#endif

return new ReadOnlySpan<T>(ref Unsafe.Add(ref _reference, (nint)(uint)start /* force zero-extension */), length);
return new ReadOnlySpan<T>(ref Unsafe.Add(ref _reference, offset), length);
}

/// <summary>
Expand Down
34 changes: 23 additions & 11 deletions src/libraries/System.Private.CoreLib/src/System/Span.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,12 @@ public Span(T[]? array)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span(T[]? array, int start, int length)
{
// Early zero-extension
nint offset = (nint)(uint)start;

if (array == null)
{
if (start != 0 || length != 0)
if ((int)offset != 0 || length != 0)
ThrowHelper.ThrowArgumentOutOfRangeException();
this = default;
return; // returns default
Expand All @@ -76,14 +79,14 @@ public Span(T[]? array, int start, int length)
ThrowHelper.ThrowArrayTypeMismatchException();
#if TARGET_64BIT
// See comment in Span<T>.Slice for how this works.
if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)array.Length)
if ((ulong)offset + (ulong)(uint)length > (ulong)(uint)array.Length)
ThrowHelper.ThrowArgumentOutOfRangeException();
#else
if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
if ((uint)offset > (uint)array.Length || (uint)length > (uint)array.Length - (uint)offset)
ThrowHelper.ThrowArgumentOutOfRangeException();
#endif

_reference = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), (nint)(uint)start /* force zero-extension */);
_reference = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), offset);
_length = length;
}

Expand Down Expand Up @@ -148,9 +151,12 @@ public ref T this[int index]
[NonVersionable]
get
{
if ((uint)index >= (uint)_length)
// Early zero-extension
nint offset = (nint)(uint)index;

if ((uint)offset >= (uint)_length)
ThrowHelper.ThrowIndexOutOfRangeException();
return ref Unsafe.Add(ref _reference, (nint)(uint)index /* force zero-extension */);
return ref Unsafe.Add(ref _reference, offset);
}
}

Expand Down Expand Up @@ -380,10 +386,13 @@ public override string ToString()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> Slice(int start)
{
if ((uint)start > (uint)_length)
// Early zero-extension
nint offset = (nint)(uint)start;

if ((uint)offset > (uint)_length)
ThrowHelper.ThrowArgumentOutOfRangeException();

return new Span<T>(ref Unsafe.Add(ref _reference, (nint)(uint)start /* force zero-extension */), _length - start);
return new Span<T>(ref Unsafe.Add(ref _reference, offset), _length - start);
}

/// <summary>
Expand All @@ -397,21 +406,24 @@ public Span<T> Slice(int start)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> Slice(int start, int length)
{
// Early zero-extension
nint offset = (nint)(uint)start;

#if TARGET_64BIT
// Since start and length are both 32-bit, their sum can be computed across a 64-bit domain
// without loss of fidelity. The cast to uint before the cast to ulong ensures that the
// extension from 32- to 64-bit is zero-extending rather than sign-extending. The end result
// of this is that if either input is negative or if the input sum overflows past Int32.MaxValue,
// that information is captured correctly in the comparison against the backing _length field.
// We don't use this same mechanism in a 32-bit process due to the overhead of 64-bit arithmetic.
if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)_length)
if ((ulong)offset + (ulong)(uint)length > (ulong)(uint)_length)
ThrowHelper.ThrowArgumentOutOfRangeException();
#else
if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
if ((uint)offset > (uint)_length || (uint)length > (uint)_length - (uint)offset)
ThrowHelper.ThrowArgumentOutOfRangeException();
#endif

return new Span<T>(ref Unsafe.Add(ref _reference, (nint)(uint)start /* force zero-extension */), length);
return new Span<T>(ref Unsafe.Add(ref _reference, offset), length);
}

/// <summary>
Expand Down

1 comment on commit a109206

@xtqqczze
Copy link
Owner Author

Choose a reason for hiding this comment

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

Please sign in to comment.