Skip to content

Commit

Permalink
[corlib] Default to fast Span<T> (#9204)
Browse files Browse the repository at this point in the history
Part of #7249
  • Loading branch information
marek-safar committed Jul 12, 2018
1 parent 493153f commit 262c5f4
Show file tree
Hide file tree
Showing 28 changed files with 398 additions and 144 deletions.
2 changes: 1 addition & 1 deletion external/api-snapshot
2 changes: 1 addition & 1 deletion external/corert
Original file line number Diff line number Diff line change
Expand Up @@ -1550,14 +1550,17 @@ public void TestBitArraySerialization ()
public void TestHashtableSerialization ()
{
var collection = new HashtableContainer ();
var expectedOutput = "{\"Items\":[{\"Key\":\"key1\",\"Value\":\"banana\"},{\"Key\":\"key2\",\"Value\":\"apple\"}]}";

var expectedOutput_A = "{\"Items\":[{\"Key\":\"key1\",\"Value\":\"banana\"},{\"Key\":\"key2\",\"Value\":\"apple\"}]}";
var expectedOutput_B = "{\"Items\":[{\"Key\":\"key2\",\"Value\":\"apple\"},{\"Key\":\"key1\",\"Value\":\"banana\"}]}";

var serializer = new DataContractJsonSerializer (collection.GetType ());
var stream = new MemoryStream ();
serializer.WriteObject (stream, collection);

stream.Position = 0;
Assert.AreEqual (expectedOutput, new StreamReader (stream).ReadToEnd (), "#1");
var read = new StreamReader (stream).ReadToEnd ();

Assert.IsTrue (expectedOutput_A == read || expectedOutput_B == read, "#1");
}

[Test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using System.Collections;
using System.ComponentModel;
using NUnit.Framework;
using System.Linq;

namespace MonoTests.System.XmlSerialization
{
Expand Down Expand Up @@ -721,7 +722,7 @@ public void ReadXml (XmlReader reader)
public void WriteXml (XmlWriter writer)
{
writer.WriteStartElement ("data");
foreach (DictionaryEntry entry in data)
foreach (DictionaryEntry entry in data.Cast<DictionaryEntry> ().OrderBy (l => l.Key))
{
writer.WriteElementString ((string)entry.Key, (string)entry.Value);
}
Expand Down
2 changes: 1 addition & 1 deletion mcs/class/System.XML/Test/XmlFiles/literal-data.xml
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,9 @@
<anyType xsi:type="xsd:string">strings</anyType>
</ttList>aa<xmltext>33</xmltext>bb<xmltext>776</xmltext><special>
<data>
<two>2</two>
<one>1</one>
<three>3</three>
<two>2</two>
</data>
</special><dummyStringArray xmlns="mm">
<ArrayOfString xsi:nil="true" />
Expand Down
2 changes: 1 addition & 1 deletion mcs/class/corlib/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ ifdef MONO_FEATURE_APPLE_X509
LIB_MCS_FLAGS += -d:MONO_FEATURE_APPLE_X509
endif

COREFX_FLAGS := -d:FEATURE_PORTABLE_SPAN
COREFX_FLAGS := -d:BIT64

This comment has been minimized.

Copy link
@jaykrell

jaykrell Jul 13, 2018

Contributor

How does this not break 32bit builds?


WARNING_ABOUT_DISABLED_WARNING=1635
LOCAL_MCS_FLAGS = -unsafe -nostdlib -nowarn:612,618,3001,3002,3003,$(WARNING_ABOUT_DISABLED_WARNING) -d:INSIDE_CORLIB,MONO_CULTURE_DATA -d:LIBC $(REFERENCE_SOURCES_FLAGS) $(COREFX_FLAGS)
Expand Down
30 changes: 30 additions & 0 deletions mcs/class/corlib/ReferenceSources/Buffer.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
#if BIT64
using nuint = System.UInt64;
#else
using nuint = System.UInt32;
#endif

using System.Runtime.CompilerServices;
using System.Runtime;

namespace System
{
partial class Buffer
Expand Down Expand Up @@ -213,7 +222,28 @@ public static unsafe void MemoryCopy (void* source, void* destination, ulong des

internal static unsafe void Memmove (byte *dest, byte *src, uint len)
{
if (((nuint)dest - (nuint)src < len) || ((nuint)src - (nuint)dest < len))
goto PInvoke;
Memcpy (dest, src, (int) len);
return;

PInvoke:
RuntimeImports.Memmove(dest, src, len);
}

internal static void Memmove<T>(ref T destination, ref T source, nuint elementCount)
{
if (!RuntimeHelpers.IsReferenceOrContainsReferences<T>()) {
unsafe {
fixed (byte* pDestination = &Unsafe.As<T, byte>(ref destination), pSource = &Unsafe.As<T, byte>(ref source))
Memmove(pDestination, pSource, (uint)elementCount * (uint)Unsafe.SizeOf<T>());
}
} else {
unsafe {
fixed (byte* pDestination = &Unsafe.As<T, byte>(ref destination), pSource = &Unsafe.As<T, byte>(ref source))
RuntimeImports.Memmove_wbarrier(pDestination, pSource, (uint)elementCount, typeof(T).TypeHandle.Value);
}
}
}
}
}
24 changes: 14 additions & 10 deletions mcs/class/corlib/ReferenceSources/String.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,19 @@
//
//

#if BIT64
using nuint = System.UInt64;

This comment has been minimized.

Copy link
@jaykrell

jaykrell Jul 12, 2018

Contributor

Why isn't this System.UIntPtr?

This comment has been minimized.

Copy link
@marek-safar

marek-safar Jul 12, 2018

Author Member

Because SequenceEqual requires it

This comment has been minimized.

Copy link
@jaykrell

jaykrell Jul 12, 2018

Contributor

I mean, why is it not just:

using nuint = System.UIntPtr;

Is that, like, insufficiently constant?

This comment has been minimized.

Copy link
@marek-safar

marek-safar Jul 12, 2018

Author Member

yes

#else
using nuint = System.UInt32;
#endif

using System.Runtime.CompilerServices;
using System.Text;
using System.Globalization;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Diagnostics.Private;

namespace System
{
Expand All @@ -51,15 +59,6 @@ partial class String

public static readonly String Empty;

public unsafe static implicit operator ReadOnlySpan<char> (String value)
{
if (value == null)
return default;

fixed (void* start = &value._firstChar)
return new ReadOnlySpan<char> (start, value.Length);
}

internal unsafe int IndexOfUnchecked (string value, int startIndex, int count)
{
int valueLen = value.Length;
Expand Down Expand Up @@ -202,7 +201,12 @@ internal bool StartsWithOrdinalUnchecked (String value)
if (this.Length < value.Length || _firstChar != value._firstChar)
return false;

return value.Length == 1 ? true : StartsWithOrdinalHelper (this, value);
return value.Length == 1 ?
true :
SpanHelpers.SequenceEqual (
ref Unsafe.As<char, byte> (ref this.GetRawStringData ()),
ref Unsafe.As<char, byte> (ref value.GetRawStringData ()),
((nuint)value.Length) * 2);
}

[MethodImplAttribute (MethodImplOptions.InternalCall)]
Expand Down
44 changes: 43 additions & 1 deletion mcs/class/corlib/ReferenceSources/TextInfo.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace System.Globalization
{
Expand Down Expand Up @@ -198,7 +199,7 @@ char ToLowerInternal (char c)
return c;
}

static unsafe int InternalCompareStringOrdinalIgnoreCase (String strA, int indexA, String strB, int indexB, int lenA, int lenB)
internal static unsafe int InternalCompareStringOrdinalIgnoreCase (String strA, int indexA, String strB, int indexB, int lenA, int lenB)
{
if (strA == null) {
return strB == null ? 0 : -1;
Expand Down Expand Up @@ -229,6 +230,47 @@ static unsafe int InternalCompareStringOrdinalIgnoreCase (String strA, int index
return lengthA - lengthB;
}
}

internal void ToLowerAsciiInvariant (ReadOnlySpan<char> source, Span<char> destination)
{
for (int i = 0; i < source.Length; i++)
{
destination [i] = ToLowerAsciiInvariant (source [i]);
}
}

internal void ToUpperAsciiInvariant (ReadOnlySpan<char> source, Span<char> destination)
{
for (int i = 0; i < source.Length; i++)
{
destination [i] = ToUpperAsciiInvariant (source[i]);
}
}

internal unsafe void ChangeCase (ReadOnlySpan<char> source, Span<char> destination, bool toUpper)
{
if (source.IsEmpty)
return;

var ti = CultureInfo.CurrentCulture.TextInfo;

fixed (char* pSource = &MemoryMarshal.GetReference (source))
fixed (char* pResult = &MemoryMarshal.GetReference (destination)) {
int length = 0;
char* a = pSource, b = pResult;
if (toUpper) {
while (length < source.Length) {
*b++ = ti.ToUpper (*a++);
length++;
}
} else {
while (length < source.Length) {
*b++ = ti.ToLower (*a++);
length++;
}
}
}
}
}

static class TextInfoToUpperData
Expand Down
15 changes: 15 additions & 0 deletions mcs/class/corlib/System/Array.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,21 @@ internal bool InternalArray__ICollection_get_IsReadOnly ()
return true;
}

// adapted to the Mono array layout
[StructLayout(LayoutKind.Sequential)]
private class RawData
{
public IntPtr Bounds;
public IntPtr Count;
public byte Data;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ref byte GetRawSzArrayData()
{
return ref Unsafe.As<RawData>(this).Data;
}

internal IEnumerator<T> InternalArray__IEnumerable_GetEnumerator<T> ()
{
if (Length == 0)
Expand Down
36 changes: 30 additions & 6 deletions mcs/class/corlib/corefx/CompareInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,22 +90,44 @@ unsafe int IndexOfCore (string source, string target, int startIndex, int count,

unsafe int IndexOfCore (ReadOnlySpan<char> source, ReadOnlySpan<char> target, CompareOptions options, int* matchLengthPtr)
{
throw new NotImplementedException ();
// TODO: optimize

var s = new string (source);
var t = new string (target);

return IndexOfCore (s, t, 0, s.Length, options, matchLengthPtr);
}

int IndexOfOrdinalCore (ReadOnlySpan<char> source, ReadOnlySpan<char> value, bool ignoreCase)
{
throw new NotImplementedException ();
// TODO: optimize

var s = new string (source);
var v = new string (value);

if (!ignoreCase)
return s.IndexOfUnchecked (v, 0, s.Length);

return s.IndexOfUncheckedIgnoreCase (v, 0, s.Length);
}

int CompareString (ReadOnlySpan<char> string1, string string2, CompareOptions options)
{
throw new NotImplementedException ();
// TODO: optimize

var s1 = new string (string1);

return internal_compare_switch (s1, 0, s1.Length, string2, 0, string2.Length, options);
}

int CompareString (ReadOnlySpan<char> string1, ReadOnlySpan<char> string2, CompareOptions options)
{
throw new NotImplementedException ();
// TODO: optimize

var s1 = new string (string1);
var s2 = new string (string2);

return internal_compare_switch (s1, 0, s1.Length, new string (s2), 0, s2.Length, options);
}

unsafe static bool IsSortable (char *text, int length)
Expand Down Expand Up @@ -137,7 +159,8 @@ bool StartsWith (string source, string prefix, CompareOptions options)

bool StartsWith (ReadOnlySpan<char> source, ReadOnlySpan<char> prefix, CompareOptions options)
{
throw new NotImplementedException ();
// TODO: optimize
return StartsWith (new string (source), new string (prefix), options);
}

bool EndsWith (string source, string suffix, CompareOptions options)
Expand All @@ -153,7 +176,8 @@ bool EndsWith (string source, string suffix, CompareOptions options)

bool EndsWith (ReadOnlySpan<char> source, ReadOnlySpan<char> suffix, CompareOptions options)
{
throw new NotImplementedException ();
// TODO: optimize
return EndsWith (new string (source), new string (suffix), options);
}

internal int GetHashCodeOfStringCore (string source, CompareOptions options)
Expand Down
26 changes: 0 additions & 26 deletions mcs/class/corlib/corefx/MemoryExtensions.cs

This file was deleted.

30 changes: 0 additions & 30 deletions mcs/class/corlib/corefx/MemoryMarshal.cs

This file was deleted.

0 comments on commit 262c5f4

Please sign in to comment.