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
14 changes: 7 additions & 7 deletions src/Common/src/CoreLib/System/Collections/Generic/Dictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ private void CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
{
if (entries[i].hashCode >= 0)
{
array[index + i] = new KeyValuePair<TKey, TValue>(entries[i].key, entries[i].value);
array[index++] = new KeyValuePair<TKey, TValue>(entries[i].key, entries[i].value);
}
}
}
Expand Down Expand Up @@ -822,7 +822,7 @@ void ICollection.CopyTo(Array array, int index)
{
if (entries[i].hashCode >= 0)
{
dictEntryArray[index + i] = new DictionaryEntry(entries[i].key, entries[i].value);
dictEntryArray[index++] = new DictionaryEntry(entries[i].key, entries[i].value);
}
}
}
Expand All @@ -842,7 +842,7 @@ void ICollection.CopyTo(Array array, int index)
{
if (entries[i].hashCode >= 0)
{
objects[index + i] = new KeyValuePair<TKey, TValue>(entries[i].key, entries[i].value);
objects[index++] = new KeyValuePair<TKey, TValue>(entries[i].key, entries[i].value);
}
}
}
Expand Down Expand Up @@ -1210,7 +1210,7 @@ public void CopyTo(TKey[] array, int index)
Entry[] entries = _dictionary._entries;
for (int i = 0; i < count; i++)
{
if (entries[i].hashCode >= 0) array[index + i] = entries[i].key;
if (entries[i].hashCode >= 0) array[index++] = entries[i].key;
}
}

Expand Down Expand Up @@ -1270,7 +1270,7 @@ void ICollection.CopyTo(Array array, int index)
{
for (int i = 0; i < count; i++)
{
if (entries[i].hashCode >= 0) objects[index + i] = entries[i].key;
if (entries[i].hashCode >= 0) objects[index++] = entries[i].key;
}
}
catch (ArrayTypeMismatchException)
Expand Down Expand Up @@ -1393,7 +1393,7 @@ public void CopyTo(TValue[] array, int index)
Entry[] entries = _dictionary._entries;
for (int i = 0; i < count; i++)
{
if (entries[i].hashCode >= 0) array[index + i] = entries[i].value;
if (entries[i].hashCode >= 0) array[index++] = entries[i].value;
}
}

Expand Down Expand Up @@ -1453,7 +1453,7 @@ void ICollection.CopyTo(Array array, int index)
{
for (int i = 0; i < count; i++)
{
if (entries[i].hashCode >= 0) objects[index + i] = entries[i].value;
if (entries[i].hashCode >= 0) objects[index++] = entries[i].value;
}
}
catch (ArrayTypeMismatchException)
Expand Down
2 changes: 1 addition & 1 deletion src/Common/src/CoreLib/System/Collections/Generic/List.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,12 +163,12 @@ public T this[int index]

set
{
_version++;
if ((uint)index >= (uint)_size)
{
ThrowHelper.ThrowArgumentOutOfRange_IndexException();
}
_items[index] = value;
_version++;
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/Common/src/CoreLib/System/IntPtr.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ public unsafe override bool Equals(Object obj)
return false;
}

unsafe bool IEquatable<IntPtr>.Equals(IntPtr value)
unsafe bool IEquatable<IntPtr>.Equals(IntPtr other)
{
return _value == value._value;
return _value == other._value;
}

public unsafe override int GetHashCode()
Expand Down
44 changes: 42 additions & 2 deletions src/Common/src/CoreLib/System/Memory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -270,9 +270,49 @@ public Span<T> Span
public bool TryCopyTo(Memory<T> destination) => Span.TryCopyTo(destination.Span);

/// <summary>
/// Returns a handle for the array.
/// <param name="pin">If pin is true, the GC will not move the array and hence its address can be taken</param>
/// Creates a handle for the memory.
/// The GC will not move the array until the returned <see cref="MemoryHandle"/>
/// is disposed, enabling taking and using the memory's address.
/// </summary>
public unsafe MemoryHandle Pin()
{
if (_index < 0)
{
return ((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
return 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
return new MemoryHandle(null, 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;
Expand Down
38 changes: 36 additions & 2 deletions src/Common/src/CoreLib/System/ReadOnlyMemory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,10 +226,44 @@ public ReadOnlySpan<T> Span
/// <param name="destination">The span to copy items into.</param>
public bool TryCopyTo(Memory<T> destination) => Span.TryCopyTo(destination.Span);

/// <summary>Creates a handle for the memory.</summary>
/// <summary>
/// Creates a handle for the memory.
/// The GC will not move the array until the returned <see cref="MemoryHandle"/>
/// is disposed, enabling taking and using the memory's address.
/// </summary>
public unsafe MemoryHandle Pin()
{
if (_index < 0)
{
return ((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
return 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
return new MemoryHandle(null, 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 the memory's address can be taken and used.
/// is disposed, enabling taking and using the memory's address.
/// </param>
public unsafe MemoryHandle Retain(bool pin = false)
{
Expand Down
128 changes: 68 additions & 60 deletions src/Common/src/CoreLib/System/SpanHelpers.Char.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,79 +84,87 @@ public static unsafe int IndexOf(ref char searchSpace, char value, int length)
{
Debug.Assert(length >= 0);

uint uValue = value; // Use uint for comparisons to avoid unnecessary 8->32 extensions
IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
IntPtr nLength = (IntPtr)length;
#if !netstandard11
if (Vector.IsHardwareAccelerated && length >= Vector<ushort>.Count * 2)
fixed (char* pChars = &searchSpace)
{
const int elementsPerByte = sizeof(ushort) / sizeof(byte);
int unaligned = ((int)Unsafe.AsPointer(ref searchSpace) & (Vector<byte>.Count - 1)) / elementsPerByte;
nLength = (IntPtr)((Vector<ushort>.Count - unaligned) & (Vector<ushort>.Count - 1));
}
SequentialScan:
char* pCh = pChars;
char* pEndCh = pCh + length;

#if !netstandard11
if (Vector.IsHardwareAccelerated && length >= Vector<ushort>.Count * 2)
{
const int elementsPerByte = sizeof(ushort) / sizeof(byte);
int unaligned = ((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) / elementsPerByte;
length = ((Vector<ushort>.Count - unaligned) & (Vector<ushort>.Count - 1));
}
SequentialScan:
#endif
while ((byte*)nLength >= (byte*)4)
{
nLength -= 4;

if (uValue == Unsafe.Add(ref searchSpace, index))
goto Found;
if (uValue == Unsafe.Add(ref searchSpace, index + 1))
goto Found1;
if (uValue == Unsafe.Add(ref searchSpace, index + 2))
goto Found2;
if (uValue == Unsafe.Add(ref searchSpace, index + 3))
goto Found3;

index += 4;
}
while (length >= 4)
{
length -= 4;

if (*pCh == value)
goto Found;
if (*(pCh + 1) == value)
goto Found1;
if (*(pCh + 2) == value)
goto Found2;
if (*(pCh + 3) == value)
goto Found3;

pCh += 4;
}

while ((byte*)nLength > (byte*)0)
{
nLength -= 1;
while (length > 0)
{
length -= 1;

if (uValue == Unsafe.Add(ref searchSpace, index))
goto Found;
if (*pCh == value)
goto Found;

index += 1;
}
pCh += 1;
}
#if !netstandard11
if (Vector.IsHardwareAccelerated && ((int)(byte*)index < length))
{
nLength = (IntPtr)((length - (int)(byte*)index) & ~(Vector<ushort>.Count - 1));
// We get past SequentialScan only if IsHardwareAccelerated is true. However, we still have the redundant check to allow
// the JIT to see that the code is unreachable and eliminate it when the platform does not have hardware accelerated.
if (Vector.IsHardwareAccelerated && pCh < pEndCh)
{
length = (int)((pEndCh - pCh) & ~(Vector<ushort>.Count - 1));

// Get comparison Vector
Vector<ushort> vComparison = new Vector<ushort>(value);
// Get comparison Vector
Vector<ushort> vComparison = new Vector<ushort>(value);

while ((byte*)nLength > (byte*)index)
{
var vMatches = Vector.Equals(vComparison, Unsafe.ReadUnaligned<Vector<ushort>>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref searchSpace, index))));
if (Vector<ushort>.Zero.Equals(vMatches))
while (length > 0)
{
index += Vector<ushort>.Count;
continue;
// Using Unsafe.Read instead of ReadUnaligned since the search space is pinned and pCh is always vector aligned
Debug.Assert(((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) == 0);
Vector<ushort> vMatches = Vector.Equals(vComparison, Unsafe.Read<Vector<ushort>>(pCh));
if (Vector<ushort>.Zero.Equals(vMatches))
{
pCh += Vector<ushort>.Count;
length -= Vector<ushort>.Count;
continue;
}
// Find offset of first match
return (int)(pCh - pChars) + LocateFirstFoundChar(vMatches);
}
// Find offset of first match
return (int)(byte*)index + LocateFirstFoundChar(vMatches);
}

if ((int)(byte*)index < length)
{
nLength = (IntPtr)(length - (int)(byte*)index);
goto SequentialScan;
if (pCh < pEndCh)
{
length = (int)(pEndCh - pCh);
goto SequentialScan;
}
}
}
#endif
return -1;
Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
return (int)(byte*)index;
Found1:
return (int)(byte*)(index + 1);
Found2:
return (int)(byte*)(index + 2);
Found3:
return (int)(byte*)(index + 3);
return -1;
Found3:
pCh++;
Found2:
pCh++;
Found1:
pCh++;
Found:
return (int)(pCh - pChars);
}
}

#if !netstandard11
Expand Down
Loading