Kaynağa Gözat

DefaultMemberAttribute

Guo Hui 6 yıl önce
ebeveyn
işleme
6e39f4cc6c

+ 2 - 1
Natsu.sln

@@ -82,10 +82,11 @@ EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Text", "Text", "{C3A73929-6C2C-4351-98AE-FE0B066D19A1}"
 	ProjectSection(SolutionItems) = preProject
 		src\Common\System\Text\ConsoleEncoding.cs = src\Common\System\Text\ConsoleEncoding.cs
+		src\Common\System\Text\StringBuilderCache.cs = src\Common\System\Text\StringBuilderCache.cs
 		src\Common\System\Text\StringOrCharArray.cs = src\Common\System\Text\StringOrCharArray.cs
 	EndProjectSection
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chino.Interop", "src\Chino.Interop\Chino.Interop.csproj", "{8EC8AF70-7C81-44A1-A8FC-F4BAD05049B8}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Chino.Interop", "src\Chino.Interop\Chino.Interop.csproj", "{8EC8AF70-7C81-44A1-A8FC-F4BAD05049B8}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution

+ 3 - 2
src/Chino.Interop/Chino.Interop.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <TargetFramework>netcoreapp3.0</TargetFramework>
@@ -8,10 +8,11 @@
     <AssemblyOriginatorKeyFile>$(SolutionDir)tools/Open.snk</AssemblyOriginatorKeyFile>
     <AssemblyVersion>4.2.1.0</AssemblyVersion>
     <Nullable>Enable</Nullable>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
   </PropertyGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\System.Private.CoreLib\System.Private.CoreLib.csproj" />
+    <ProjectReference Include="..\System.Runtime\System.Runtime.csproj" />
   </ItemGroup>
 
 </Project>

+ 9 - 3
src/Chino.Interop/Chino/IO/IO.cs

@@ -8,19 +8,19 @@ public static partial class Interop
 {
     public static partial class IO
     {
-        public static SafeFileHandle StdinHandle
+        public static extern SafeFileHandle StdinHandle
         {
             [MethodImpl(MethodImplOptions.InternalCall)]
             get;
         }
 
-        public static SafeFileHandle StdoutHandle
+        public static extern SafeFileHandle StdoutHandle
         {
             [MethodImpl(MethodImplOptions.InternalCall)]
             get;
         }
 
-        public static SafeFileHandle StderrHandle
+        public static extern SafeFileHandle StderrHandle
         {
             [MethodImpl(MethodImplOptions.InternalCall)]
             get;
@@ -34,5 +34,11 @@ public static partial class Interop
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         public static extern bool IsConsoleHandle(SafeFileHandle handle);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern bool StdinReady();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern unsafe int ReadStdin(byte* buffer, int bufferSize);
     }
 }

+ 54 - 0
src/Chino.Interop/Chino/Text/Text.cs

@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+using Chino;
+
+public static partial class Interop
+{
+    public static partial class Text
+    {
+        /// <summary>
+        /// Takes a string and applies a formatting to it to transform
+        /// parameters to input values (such as replacing %s in the string with a variable)
+        /// </summary>
+        /// <param name="str">The output buffer to put the transformed data into</param>
+        /// <param name="size">The size of the output buffer</param>
+        /// <param name="format">The input string with wildcards to be replaced</param>
+        /// <param name="arg1">The argument to replace a wildcard with</param>
+        /// <remarks>
+        /// Since snprintf has a variadic parameter, which cannot be described by C#, we have different
+        /// PInvokes declared for each one we need. The PInvoke layer will take care of putting the
+        /// arguments in correctly.
+        /// </remarks>
+        /// <returns>
+        /// Returns the number of characters (excluding null terminator) written to the buffer on
+        /// success; if the return value is equal to the size then the result may have been truncated.
+        /// On failure, returns a negative value.
+        /// </returns>
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern unsafe int SNPrintF(byte* str, int size, string format, string arg1);
+
+        /// <summary>
+        /// Takes a string and applies a formatting to it to transform
+        /// parameters to input values (such as replacing %s in the string with a variable)
+        /// </summary>
+        /// <param name="str">The output buffer to put the transformed data into</param>
+        /// <param name="size">The size of the output buffer</param>
+        /// <param name="format">The input string with wildcards to be replaced</param>
+        /// <param name="arg1">The argument to replace a wildcard with</param>
+        /// <remarks>
+        /// Since snprintf has a variadic parameter, which cannot be described by C#, we have different
+        /// PInvokes declared for each one we need. The PInvoke layer will take care of putting the
+        /// arguments in correctly.
+        /// </remarks>
+        /// <returns>
+        /// Returns the number of characters (excluding null terminator) written to the buffer on
+        /// success; if the return value is equal to the size then the result may have been truncated.
+        /// On failure, returns a negative value.
+        /// </returns>
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern unsafe int SNPrintF(byte* str, int size, string format, int arg1);
+    }
+}

+ 7 - 5
src/System.Private.CoreLib/System/Text/StringBuilderCache.cs → src/Common/System/Text/StringBuilderCache.cs

@@ -2,6 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
+#nullable enable
 namespace System.Text
 {
     /// <summary>Provide a cached reusable instance of stringbuilder per thread.</summary>
@@ -14,11 +15,11 @@ namespace System.Text
         private const int DefaultCapacity = 16; // == StringBuilder.DefaultCapacity
 
         // WARNING: We allow diagnostic tools to directly inspect this member (t_cachedInstance).
-        // See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details. 
-        // Please do not change the type, the name, or the semantic usage of this member without understanding the implication for tools. 
+        // See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details.
+        // Please do not change the type, the name, or the semantic usage of this member without understanding the implication for tools.
         // Get in touch with the diagnostics team if you have questions.
-        //[ThreadStatic]
-        private static StringBuilder t_cachedInstance;
+        [ThreadStatic]
+        private static StringBuilder? t_cachedInstance;
 
         /// <summary>Get a StringBuilder for the specified capacity.</summary>
         /// <remarks>If a StringBuilder of an appropriate size is cached, it will be returned and the cache emptied.</remarks>
@@ -26,7 +27,7 @@ namespace System.Text
         {
             if (capacity <= MaxBuilderSize)
             {
-                StringBuilder sb = t_cachedInstance;
+                StringBuilder? sb = t_cachedInstance;
                 if (sb != null)
                 {
                     // Avoid stringbuilder block fragmentation by getting a new StringBuilder
@@ -39,6 +40,7 @@ namespace System.Text
                     }
                 }
             }
+
             return new StringBuilder(capacity);
         }
 

+ 2 - 1
src/System.Collections/System.Collections.csproj

@@ -7,6 +7,7 @@
     <SignAssembly>true</SignAssembly>
     <AssemblyOriginatorKeyFile>$(SolutionDir)tools/Open.snk</AssemblyOriginatorKeyFile>
     <AssemblyVersion>4.2.1.0</AssemblyVersion>
+    <Nullable>Enable</Nullable>
   </PropertyGroup>
 
   <ItemGroup>
@@ -23,7 +24,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\System.Private.CoreLib\System.Private.CoreLib.csproj" />
+    <ProjectReference Include="..\System.Runtime\System.Runtime.csproj" />
   </ItemGroup>
 
 </Project>

+ 4 - 1
src/System.Console/System.Console.csproj

@@ -25,12 +25,15 @@
     <Compile Include="..\Common\System\Text\StringOrCharArray.cs">
       <Link>Common\System\Text\StringOrCharArray.cs</Link>
     </Compile>
+    <Compile Include="..\Common\System\Text\StringBuilderCache.cs">
+      <Link>Common\System\Text\StringBuilderCache.cs</Link>
+    </Compile>
   </ItemGroup>
 
   <ItemGroup>
+    <ProjectReference Include="..\System.Runtime\System.Runtime.csproj" />
     <ProjectReference Include="..\Chino.Interop\Chino.Interop.csproj" />
     <ProjectReference Include="..\System.Collections\System.Collections.csproj" />
-    <ProjectReference Include="..\System.Private.CoreLib\System.Private.CoreLib.csproj" />
   </ItemGroup>
 
 </Project>

+ 2 - 0
src/System.Console/System/ConsolePal.Chino.cs

@@ -667,6 +667,8 @@ namespace System
 
         public static void MoveBufferArea(int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight, int targetLeft, int targetTop)
         {
+            var t = new Type();
+            var i = t[1];
             throw new PlatformNotSupportedException();
         }
 

+ 105 - 121
src/System.Console/System/IO/StdInReader.cs

@@ -73,7 +73,7 @@ namespace System.IO
 
         internal unsafe int ReadStdin(byte* buffer, int bufferSize)
         {
-            int result = Interop.CheckIo(Interop.Sys.ReadStdin(buffer, bufferSize));
+            int result = Interop.IO.ReadStdin(buffer, bufferSize);
             Debug.Assert(result >= 0 && result <= bufferSize); // may be 0 if hits EOL
             return result;
         }
@@ -88,118 +88,110 @@ namespace System.IO
             Debug.Assert(_tmpKeys.Count == 0);
             string? readLineStr = null;
 
-            Interop.Sys.InitializeConsoleBeforeRead();
-            try
+            // Read key-by-key until we've read a line.
+            while (true)
             {
-                // Read key-by-key until we've read a line.
-                while (true)
+                // Read the next key.  This may come from previously read keys, from previously read but
+                // unprocessed data, or from an actual stdin read.
+                bool previouslyProcessed;
+                ConsoleKeyInfo keyInfo = ReadKey(out previouslyProcessed);
+                if (!consumeKeys && keyInfo.Key != ConsoleKey.Backspace) // backspace is the only character not written out in the below if/elses.
                 {
-                    // Read the next key.  This may come from previously read keys, from previously read but
-                    // unprocessed data, or from an actual stdin read.
-                    bool previouslyProcessed;
-                    ConsoleKeyInfo keyInfo = ReadKey(out previouslyProcessed);
-                    if (!consumeKeys && keyInfo.Key != ConsoleKey.Backspace) // backspace is the only character not written out in the below if/elses.
-                    {
-                        _tmpKeys.Push(keyInfo);
-                    }
+                    _tmpKeys.Push(keyInfo);
+                }
 
-                    // Handle the next key.  Since for other functions we may have ended up reading some of the user's
-                    // input, we need to be able to handle manually processing that input, and so we do that processing
-                    // for all input.  As such, we need to special-case a few characters, e.g. recognizing when Enter is
-                    // pressed to end a line.  We also need to handle Backspace specially, to fix up both our buffer of
-                    // characters and the position of the cursor.  More advanced processing would be possible, but we
-                    // try to keep this very simple, at least for now.
-                    if (keyInfo.Key == ConsoleKey.Enter)
+                // Handle the next key.  Since for other functions we may have ended up reading some of the user's
+                // input, we need to be able to handle manually processing that input, and so we do that processing
+                // for all input.  As such, we need to special-case a few characters, e.g. recognizing when Enter is
+                // pressed to end a line.  We also need to handle Backspace specially, to fix up both our buffer of
+                // characters and the position of the cursor.  More advanced processing would be possible, but we
+                // try to keep this very simple, at least for now.
+                if (keyInfo.Key == ConsoleKey.Enter)
+                {
+                    readLineStr = _readLineSB.ToString();
+                    _readLineSB.Clear();
+                    if (!previouslyProcessed)
                     {
-                        readLineStr = _readLineSB.ToString();
-                        _readLineSB.Clear();
-                        if (!previouslyProcessed)
-                        {
-                            Console.WriteLine();
-                        }
-                        break;
+                        Console.WriteLine();
                     }
-                    else if (IsEol(keyInfo.KeyChar))
+                    break;
+                }
+                else if (IsEol(keyInfo.KeyChar))
+                {
+                    string line = _readLineSB.ToString();
+                    _readLineSB.Clear();
+                    if (line.Length > 0)
                     {
-                        string line = _readLineSB.ToString();
-                        _readLineSB.Clear();
-                        if (line.Length > 0)
-                        {
-                            readLineStr = line;
-                        }
-                        break;
+                        readLineStr = line;
                     }
-                    else if (keyInfo.Key == ConsoleKey.Backspace)
+                    break;
+                }
+                else if (keyInfo.Key == ConsoleKey.Backspace)
+                {
+                    int len = _readLineSB.Length;
+                    if (len > 0)
                     {
-                        int len = _readLineSB.Length;
-                        if (len > 0)
+                        _readLineSB.Length = len - 1;
+                        if (!previouslyProcessed)
                         {
-                            _readLineSB.Length = len - 1;
-                            if (!previouslyProcessed)
+                            // The ReadLine input may wrap accross terminal rows and we need to handle that.
+                            // note: ConsolePal will cache the cursor position to avoid making many slow cursor position fetch operations.
+                            if (ConsolePal.TryGetCursorPosition(out int left, out int top, reinitializeForRead: true) &&
+                                left == 0 && top > 0)
                             {
-                                // The ReadLine input may wrap accross terminal rows and we need to handle that.
-                                // note: ConsolePal will cache the cursor position to avoid making many slow cursor position fetch operations.
-                                if (ConsolePal.TryGetCursorPosition(out int left, out int top, reinitializeForRead: true) &&
-                                    left == 0 && top > 0)
+                                if (s_clearToEol == null)
                                 {
-                                    if (s_clearToEol == null)
-                                    {
-                                        s_clearToEol = ConsolePal.TerminalFormatStrings.Instance.ClrEol ?? string.Empty;
-                                    }
-
-                                    // Move to end of previous line
-                                    ConsolePal.SetCursorPosition(ConsolePal.WindowWidth - 1, top - 1);
-                                    // Clear from cursor to end of the line
-                                    ConsolePal.WriteStdoutAnsiString(s_clearToEol, mayChangeCursorPosition: false);
+                                    s_clearToEol = ConsolePal.TerminalFormatStrings.Instance.ClrEol ?? string.Empty;
                                 }
-                                else
-                                {
-                                    if (s_moveLeftString == null)
-                                    {
-                                        string? moveLeft = ConsolePal.TerminalFormatStrings.Instance.CursorLeft;
-                                        s_moveLeftString = !string.IsNullOrEmpty(moveLeft) ? moveLeft + " " + moveLeft : string.Empty;
-                                    }
 
-                                    Console.Write(s_moveLeftString);
+                                // Move to end of previous line
+                                ConsolePal.SetCursorPosition(ConsolePal.WindowWidth - 1, top - 1);
+                                // Clear from cursor to end of the line
+                                ConsolePal.WriteStdoutAnsiString(s_clearToEol, mayChangeCursorPosition: false);
+                            }
+                            else
+                            {
+                                if (s_moveLeftString == null)
+                                {
+                                    string? moveLeft = ConsolePal.TerminalFormatStrings.Instance.CursorLeft;
+                                    s_moveLeftString = !string.IsNullOrEmpty(moveLeft) ? moveLeft + " " + moveLeft : string.Empty;
                                 }
+
+                                Console.Write(s_moveLeftString);
                             }
                         }
                     }
-                    else if (keyInfo.Key == ConsoleKey.Tab)
+                }
+                else if (keyInfo.Key == ConsoleKey.Tab)
+                {
+                    _readLineSB.Append(keyInfo.KeyChar);
+                    if (!previouslyProcessed)
                     {
-                        _readLineSB.Append(keyInfo.KeyChar);
-                        if (!previouslyProcessed)
-                        {
-                            Console.Write(' ');
-                        }
+                        Console.Write(' ');
                     }
-                    else if (keyInfo.Key == ConsoleKey.Clear)
+                }
+                else if (keyInfo.Key == ConsoleKey.Clear)
+                {
+                    _readLineSB.Clear();
+                    if (!previouslyProcessed)
                     {
-                        _readLineSB.Clear();
-                        if (!previouslyProcessed)
-                        {
-                            Console.Clear();
-                        }
+                        Console.Clear();
                     }
-                    else if (keyInfo.KeyChar != '\0')
+                }
+                else if (keyInfo.KeyChar != '\0')
+                {
+                    _readLineSB.Append(keyInfo.KeyChar);
+                    if (!previouslyProcessed)
                     {
-                        _readLineSB.Append(keyInfo.KeyChar);
-                        if (!previouslyProcessed)
-                        {
-                            Console.Write(keyInfo.KeyChar);
-                        }
+                        Console.Write(keyInfo.KeyChar);
                     }
                 }
             }
-            finally
-            {
-                Interop.Sys.UninitializeConsoleAfterRead();
 
-                // If we're not consuming the read input, make the keys available for a future read
-                while (_tmpKeys.Count > 0)
-                {
-                    _availableKeys.Push(_tmpKeys.Pop());
-                }
+            // If we're not consuming the read input, make the keys available for a future read
+            while (_tmpKeys.Count > 0)
+            {
+                _availableKeys.Push(_tmpKeys.Pop());
             }
 
             return readLineStr;
@@ -381,46 +373,38 @@ namespace System.IO
             }
 
             previouslyProcessed = false;
-            Interop.Sys.InitializeConsoleBeforeRead();
-            try
-            {
-                ConsoleKey key;
-                char ch;
-                bool isAlt, isCtrl, isShift;
+            ConsoleKey key;
+            char ch;
+            bool isAlt, isCtrl, isShift;
 
-                if (IsUnprocessedBufferEmpty())
+            if (IsUnprocessedBufferEmpty())
+            {
+                // Read in bytes
+                byte* bufPtr = stackalloc byte[BytesToBeRead];
+                int result = ReadStdin(bufPtr, BytesToBeRead);
+                if (result > 0)
                 {
-                    // Read in bytes
-                    byte* bufPtr = stackalloc byte[BytesToBeRead];
-                    int result = ReadStdin(bufPtr, BytesToBeRead);
-                    if (result > 0)
-                    {
-                        // Append them
-                        AppendExtraBuffer(bufPtr, result);
-                    }
-                    else
-                    {
-                        // Could be empty if EOL entered on its own.  Pick one of the EOL characters we have,
-                        // or just use 0 if none are available.
-                        return new ConsoleKeyInfo((char)
-                            (ConsolePal.s_veolCharacter != ConsolePal.s_posixDisableValue ? ConsolePal.s_veolCharacter :
-                             ConsolePal.s_veol2Character != ConsolePal.s_posixDisableValue ? ConsolePal.s_veol2Character :
-                             ConsolePal.s_veofCharacter != ConsolePal.s_posixDisableValue ? ConsolePal.s_veofCharacter :
-                             0),
-                            default(ConsoleKey), false, false, false);
-                    }
+                    // Append them
+                    AppendExtraBuffer(bufPtr, result);
+                }
+                else
+                {
+                    // Could be empty if EOL entered on its own.  Pick one of the EOL characters we have,
+                    // or just use 0 if none are available.
+                    return new ConsoleKeyInfo((char)
+                        (ConsolePal.s_veolCharacter != ConsolePal.s_posixDisableValue ? ConsolePal.s_veolCharacter :
+                         ConsolePal.s_veol2Character != ConsolePal.s_posixDisableValue ? ConsolePal.s_veol2Character :
+                         ConsolePal.s_veofCharacter != ConsolePal.s_posixDisableValue ? ConsolePal.s_veofCharacter :
+                         0),
+                        default(ConsoleKey), false, false, false);
                 }
-
-                MapBufferToConsoleKey(out key, out ch, out isShift, out isAlt, out isCtrl);
-                return new ConsoleKeyInfo(ch, key, isShift, isAlt, isCtrl);
-            }
-            finally
-            {
-                Interop.Sys.UninitializeConsoleAfterRead();
             }
+
+            MapBufferToConsoleKey(out key, out ch, out isShift, out isAlt, out isCtrl);
+            return new ConsoleKeyInfo(ch, key, isShift, isAlt, isCtrl);
         }
 
         /// <summary>Gets whether there's input waiting on stdin.</summary>
-        internal bool StdinReady { get { return Interop.Sys.StdinReady(); } }
+        internal bool StdinReady { get { return Interop.IO.StdinReady(); } }
     }
 }

+ 4 - 4
src/System.Console/System/TermInfo.cs

@@ -767,8 +767,8 @@ namespace System
                 // Determine how much space is needed to store the formatted string.
                 string? stringArg = arg as string;
                 int neededLength = stringArg != null ?
-                    Interop.Sys.SNPrintF(null, 0, format, stringArg) :
-                    Interop.Sys.SNPrintF(null, 0, format, (int)arg);
+                    Interop.Text.SNPrintF(null, 0, format, stringArg) :
+                    Interop.Text.SNPrintF(null, 0, format, (int)arg);
                 if (neededLength == 0)
                 {
                     return string.Empty;
@@ -783,8 +783,8 @@ namespace System
                 fixed (byte* ptr = &bytes[0])
                 {
                     int length = stringArg != null ?
-                        Interop.Sys.SNPrintF(ptr, bytes.Length, format, stringArg) :
-                        Interop.Sys.SNPrintF(ptr, bytes.Length, format, (int)arg);
+                        Interop.Text.SNPrintF(ptr, bytes.Length, format, stringArg) :
+                        Interop.Text.SNPrintF(ptr, bytes.Length, format, (int)arg);
                     if (length != neededLength)
                     {
                         throw new InvalidOperationException(SR.InvalidOperation_PrintF);

+ 3 - 0
src/System.Private.CoreLib/System.Private.CoreLib.csproj

@@ -28,6 +28,9 @@
       <GenerateSource>true</GenerateSource>
       <ClassName>System.SR</ClassName>
     </None>
+    <Compile Include="..\Common\System\Text\StringBuilderCache.cs">
+      <Link>Common\System\Text\StringBuilderCache.cs</Link>
+    </Compile>
   </ItemGroup>
   <ItemGroup>
     <Folder Include="System\Collections\ObjectModel\" />

+ 85 - 0
src/System.Private.CoreLib/System/Collections/Generic/ValueListBuilder.cs

@@ -0,0 +1,85 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Buffers;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace System.Collections.Generic
+{
+    internal ref partial struct ValueListBuilder<T>
+    {
+        private Span<T> _span;
+        private T[] _arrayFromPool;
+        private int _pos;
+
+        public ValueListBuilder(Span<T> initialSpan)
+        {
+            _span = initialSpan;
+            _arrayFromPool = null;
+            _pos = 0;
+        }
+
+        public int Length
+        {
+            get => _pos;
+            set
+            {
+                Debug.Assert(value >= 0);
+                Debug.Assert(value <= _span.Length);
+                _pos = value;
+            }
+        }
+
+        public ref T this[int index]
+        {
+            get
+            {
+                Debug.Assert(index < _pos);
+                return ref _span[index];
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Append(T item)
+        {
+            int pos = _pos;
+            if (pos >= _span.Length)
+                Grow();
+
+            _span[pos] = item;
+            _pos = pos + 1;
+        }
+
+        public ReadOnlySpan<T> AsSpan()
+        {
+            return _span.Slice(0, _pos);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Dispose()
+        {
+            if (_arrayFromPool != null)
+            {
+                ArrayPool<T>.Shared.Return(_arrayFromPool);
+                _arrayFromPool = null;
+            }
+        }
+
+        private void Grow()
+        {
+            T[] array = ArrayPool<T>.Shared.Rent(_span.Length * 2);
+
+            bool success = _span.TryCopyTo(array);
+            Debug.Assert(success);
+
+            T[] toReturn = _arrayFromPool;
+            _span = _arrayFromPool = array;
+            if (toReturn != null)
+            {
+                ArrayPool<T>.Shared.Return(toReturn);
+            }
+        }
+    }
+}

+ 21 - 0
src/System.Private.CoreLib/System/Reflection/DefaultMemberAttribute.cs

@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)]
+    public sealed class DefaultMemberAttribute : Attribute
+    {
+        // You must provide the name of the member, this is required
+        public DefaultMemberAttribute(string memberName)
+        {
+            MemberName = memberName;
+        }
+
+        // A get accessor to return the name from the attribute.
+        // NOTE: There is no setter because the name must be provided
+        //    to the constructor.  The name is not optional.
+        public string MemberName { get; }
+    }
+}

+ 32 - 0
src/System.Private.CoreLib/System/Runtime/CompilerServices/ReferenceAssemblyAttribute.cs

@@ -0,0 +1,32 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Attribute: ReferenceAssemblyAttribute
+**
+** Purpose: Identifies an assembly as being a "reference 
+**    assembly", meaning it contains public surface area but
+**    no usable implementation.  Reference assemblies 
+**    should be loadable for introspection, but not execution.
+**
+============================================================*/
+
+namespace System.Runtime.CompilerServices
+{
+    [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
+    public sealed class ReferenceAssemblyAttribute : Attribute
+    {
+        public ReferenceAssemblyAttribute()
+        {
+        }
+
+        public ReferenceAssemblyAttribute(string description)
+        {
+            Description = description;
+        }
+
+        public string Description { get; }
+    }
+}

+ 30 - 0
src/System.Private.CoreLib/System/Runtime/CompilerServices/RuntimeCompatibilityAttribute.cs

@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: Mark up the program to indicate various legacy or new opt-in behaviors.
+**
+**
+=============================================================================*/
+
+namespace System.Runtime.CompilerServices
+{
+    [AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)]
+    public sealed class RuntimeCompatibilityAttribute : Attribute
+    {
+        public RuntimeCompatibilityAttribute()
+        {
+            // legacy behavior is the default, and WrapNonExceptionThrows is implicitly
+            // false thanks to the CLR's guarantee of zeroed memory.
+        }
+
+        // If a non-CLSCompliant exception (i.e. one that doesn't derive from System.Exception) is
+        // thrown, should it be wrapped up in a System.Runtime.CompilerServices.RuntimeWrappedException
+        // instance when presented to catch handlers?
+        public bool WrapNonExceptionThrows { get; set; }
+    }
+}

+ 12 - 0
src/System.Private.CoreLib/System/Runtime/CompilerServices/SpecialNameAttribute.cs

@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices 
+{
+    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Struct)]
+    public sealed class SpecialNameAttribute : Attribute
+    {
+        public SpecialNameAttribute() { }
+    }
+}

+ 143 - 0
src/System.Private.CoreLib/System/Runtime/InteropServices/Marshal.cs

@@ -0,0 +1,143 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Collections.Generic;
+using System.Reflection;
+using System.Text;
+using System.Runtime.CompilerServices;
+using System.Diagnostics;
+
+namespace System.Runtime.InteropServices
+{
+    public enum CustomQueryInterfaceMode
+    {
+        Ignore = 0,
+        Allow = 1
+    }
+
+    /// <summary>
+    /// This class contains methods that are mainly used to marshal between unmanaged
+    /// and managed types.
+    /// </summary>
+    public static partial class Marshal
+    {
+#if FEATURE_COMINTEROP
+        internal static Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046");
+#endif //FEATURE_COMINTEROP
+
+        private const int LMEM_FIXED = 0;
+        private const int LMEM_MOVEABLE = 2;
+#if !FEATURE_PAL
+        private const long HiWordMask = unchecked((long)0xffffffffffff0000L);
+#endif //!FEATURE_PAL
+
+        // Win32 has the concept of Atoms, where a pointer can either be a pointer
+        // or an int.  If it's less than 64K, this is guaranteed to NOT be a 
+        // pointer since the bottom 64K bytes are reserved in a process' page table.
+        // We should be careful about deallocating this stuff.  Extracted to
+        // a function to avoid C# problems with lack of support for IntPtr.
+        // We have 2 of these methods for slightly different semantics for NULL.
+        private static bool IsWin32Atom(IntPtr ptr)
+        {
+#if FEATURE_PAL
+            return false;
+#else
+            long lPtr = (long)ptr;
+            return 0 == (lPtr & HiWordMask);
+#endif
+        }
+
+        /// <summary>
+        /// The default character size for the system. This is always 2 because
+        /// the framework only runs on UTF-16 systems.
+        /// </summary>
+        public static readonly int SystemDefaultCharSize = 2;
+
+        public static void Copy(int[] source, int startIndex, IntPtr destination, int length)
+        {
+            CopyToNative(source, startIndex, destination, length);
+        }
+
+        public static void Copy(char[] source, int startIndex, IntPtr destination, int length)
+        {
+            CopyToNative(source, startIndex, destination, length);
+        }
+
+        public static void Copy(short[] source, int startIndex, IntPtr destination, int length)
+        {
+            CopyToNative(source, startIndex, destination, length);
+        }
+
+        public static void Copy(long[] source, int startIndex, IntPtr destination, int length)
+        {
+            CopyToNative(source, startIndex, destination, length);
+        }
+
+        public static void Copy(float[] source, int startIndex, IntPtr destination, int length)
+        {
+            CopyToNative(source, startIndex, destination, length);
+        }
+
+        public static void Copy(double[] source, int startIndex, IntPtr destination, int length)
+        {
+            CopyToNative(source, startIndex, destination, length);
+        }
+
+        public static void Copy(byte[] source, int startIndex, IntPtr destination, int length)
+        {
+            CopyToNative(source, startIndex, destination, length);
+        }
+
+        public static void Copy(IntPtr[] source, int startIndex, IntPtr destination, int length)
+        {
+            CopyToNative(source, startIndex, destination, length);
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void CopyToNative(object source, int startIndex, IntPtr destination, int length);
+
+        public static void Copy(IntPtr source, int[] destination, int startIndex, int length)
+        {
+            CopyToManaged(source, destination, startIndex, length);
+        }
+
+        public static void Copy(IntPtr source, char[] destination, int startIndex, int length)
+        {
+            CopyToManaged(source, destination, startIndex, length);
+        }
+
+        public static void Copy(IntPtr source, short[] destination, int startIndex, int length)
+        {
+            CopyToManaged(source, destination, startIndex, length);
+        }
+
+        public static void Copy(IntPtr source, long[] destination, int startIndex, int length)
+        {
+            CopyToManaged(source, destination, startIndex, length);
+        }
+
+        public static void Copy(IntPtr source, float[] destination, int startIndex, int length)
+        {
+            CopyToManaged(source, destination, startIndex, length);
+        }
+
+        public static void Copy(IntPtr source, double[] destination, int startIndex, int length)
+        {
+            CopyToManaged(source, destination, startIndex, length);
+        }
+
+        public static void Copy(IntPtr source, byte[] destination, int startIndex, int length)
+        {
+            CopyToManaged(source, destination, startIndex, length);
+        }
+
+        public static void Copy(IntPtr source, IntPtr[] destination, int startIndex, int length)
+        {
+            CopyToManaged(source, destination, startIndex, length);
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void CopyToManaged(IntPtr source, object destination, int startIndex, int length);
+    }
+}

+ 1081 - 1
src/System.Private.CoreLib/System/String.Manipulation.cs

@@ -1,7 +1,15 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Buffers;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using System.Text;
+using Internal.Runtime.CompilerServices;
 
 namespace System
 {
@@ -133,6 +141,90 @@ namespace System
             return result;
         }
 
+        public static string Concat<T>(IEnumerable<T> values)
+        {
+            if (values == null)
+                throw new ArgumentNullException(nameof(values));
+
+            if (typeof(T) == typeof(char))
+            {
+                // Special-case T==char, as we can handle that case much more efficiently,
+                // and string.Concat(IEnumerable<char>) can be used as an efficient
+                // enumerable-based equivalent of new string(char[]).
+                using (IEnumerator<char> en = Unsafe.As<IEnumerable<char>>(values).GetEnumerator())
+                {
+                    if (!en.MoveNext())
+                    {
+                        // There weren't any chars.  Return the empty string.
+                        return Empty;
+                    }
+
+                    char c = en.Current; // save the first char
+
+                    if (!en.MoveNext())
+                    {
+                        // There was only one char.  Return a string from it directly.
+                        return CreateFromChar(c);
+                    }
+
+                    // Create the StringBuilder, add the chars we've already enumerated,
+                    // add the rest, and then get the resulting string.
+                    StringBuilder result = StringBuilderCache.Acquire();
+                    result.Append(c); // first value
+                    do
+                    {
+                        c = en.Current;
+                        result.Append(c);
+                    }
+                    while (en.MoveNext());
+                    return StringBuilderCache.GetStringAndRelease(result);
+                }
+            }
+            else
+            {
+                using (IEnumerator<T> en = values.GetEnumerator())
+                {
+                    if (!en.MoveNext())
+                        return string.Empty;
+
+                    // We called MoveNext once, so this will be the first item
+                    T currentValue = en.Current;
+
+                    // Call ToString before calling MoveNext again, since
+                    // we want to stay consistent with the below loop
+                    // Everything should be called in the order
+                    // MoveNext-Current-ToString, unless further optimizations
+                    // can be made, to avoid breaking changes
+                    string firstString = currentValue?.ToString();
+
+                    // If there's only 1 item, simply call ToString on that
+                    if (!en.MoveNext())
+                    {
+                        // We have to handle the case of either currentValue
+                        // or its ToString being null
+                        return firstString ?? string.Empty;
+                    }
+
+                    StringBuilder result = StringBuilderCache.Acquire();
+
+                    result.Append(firstString);
+
+                    do
+                    {
+                        currentValue = en.Current;
+
+                        if (currentValue != null)
+                        {
+                            result.Append(currentValue.ToString());
+                        }
+                    }
+                    while (en.MoveNext());
+
+                    return StringBuilderCache.GetStringAndRelease(result);
+                }
+            }
+        }
+
         public static string Concat(IEnumerable<string> values)
         {
             if (values == null)
@@ -721,5 +813,993 @@ namespace System
                 result :
                 JoinCore(separator, separatorLength, (string[])value.Clone(), startIndex, count);
         }
+
+        public string PadLeft(int totalWidth) => PadLeft(totalWidth, ' ');
+
+        public string PadLeft(int totalWidth, char paddingChar)
+        {
+            if (totalWidth < 0)
+                throw new ArgumentOutOfRangeException(nameof(totalWidth), SR.ArgumentOutOfRange_NeedNonNegNum);
+            int oldLength = Length;
+            int count = totalWidth - oldLength;
+            if (count <= 0)
+                return this;
+            string result = FastAllocateString(totalWidth);
+            unsafe
+            {
+                fixed (char* dst = &result._firstChar)
+                {
+                    for (int i = 0; i < count; i++)
+                        dst[i] = paddingChar;
+                    fixed (char* src = &_firstChar)
+                    {
+                        wstrcpy(dst + count, src, oldLength);
+                    }
+                }
+            }
+            return result;
+        }
+
+        public string PadRight(int totalWidth) => PadRight(totalWidth, ' ');
+
+        public string PadRight(int totalWidth, char paddingChar)
+        {
+            if (totalWidth < 0)
+                throw new ArgumentOutOfRangeException(nameof(totalWidth), SR.ArgumentOutOfRange_NeedNonNegNum);
+            int oldLength = Length;
+            int count = totalWidth - oldLength;
+            if (count <= 0)
+                return this;
+            string result = FastAllocateString(totalWidth);
+            unsafe
+            {
+                fixed (char* dst = &result._firstChar)
+                {
+                    fixed (char* src = &_firstChar)
+                    {
+                        wstrcpy(dst, src, oldLength);
+                    }
+                    for (int i = 0; i < count; i++)
+                        dst[oldLength + i] = paddingChar;
+                }
+            }
+            return result;
+        }
+
+        public string Remove(int startIndex, int count)
+        {
+            if (startIndex < 0)
+                throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
+            if (count < 0)
+                throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NegativeCount);
+            int oldLength = this.Length;
+            if (count > oldLength - startIndex)
+                throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_IndexCount);
+
+            if (count == 0)
+                return this;
+            int newLength = oldLength - count;
+            if (newLength == 0)
+                return string.Empty;
+
+            string result = FastAllocateString(newLength);
+            unsafe
+            {
+                fixed (char* src = &_firstChar)
+                {
+                    fixed (char* dst = &result._firstChar)
+                    {
+                        wstrcpy(dst, src, startIndex);
+                        wstrcpy(dst + startIndex, src + startIndex + count, newLength - startIndex);
+                    }
+                }
+            }
+            return result;
+        }
+
+        // a remove that just takes a startindex.
+        public string Remove(int startIndex)
+        {
+            if (startIndex < 0)
+                throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
+
+            if (startIndex >= Length)
+                throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndexLessThanLength);
+
+            return Substring(0, startIndex);
+        }
+
+        public string Replace(string oldValue, string newValue, bool ignoreCase, CultureInfo culture)
+        {
+            return ReplaceCore(oldValue, newValue, culture, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
+        }
+
+        public string Replace(string oldValue, string newValue, StringComparison comparisonType)
+        {
+            switch (comparisonType)
+            {
+                case StringComparison.CurrentCulture:
+                case StringComparison.CurrentCultureIgnoreCase:
+                    return ReplaceCore(oldValue, newValue, CultureInfo.CurrentCulture, GetCaseCompareOfComparisonCulture(comparisonType));
+
+                case StringComparison.InvariantCulture:
+                case StringComparison.InvariantCultureIgnoreCase:
+                    return ReplaceCore(oldValue, newValue, CultureInfo.InvariantCulture, GetCaseCompareOfComparisonCulture(comparisonType));
+
+                case StringComparison.Ordinal:
+                    return Replace(oldValue, newValue);
+
+                case StringComparison.OrdinalIgnoreCase:
+                    return ReplaceCore(oldValue, newValue, CultureInfo.InvariantCulture, CompareOptions.OrdinalIgnoreCase);
+
+                default:
+                    throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
+            }
+        }
+
+        private unsafe string ReplaceCore(string oldValue, string newValue, CultureInfo culture, CompareOptions options)
+        {
+            if (oldValue == null)
+                throw new ArgumentNullException(nameof(oldValue));
+            if (oldValue.Length == 0)
+                throw new ArgumentException(SR.Argument_StringZeroLength, nameof(oldValue));
+
+            // If they asked to replace oldValue with a null, replace all occurrences
+            // with the empty string.
+            if (newValue == null)
+                newValue = string.Empty;
+
+            CultureInfo referenceCulture = culture ?? CultureInfo.CurrentCulture;
+            StringBuilder result = StringBuilderCache.Acquire();
+
+            int startIndex = 0;
+            int index = 0;
+
+            int matchLength = 0;
+
+            bool hasDoneAnyReplacements = false;
+            CompareInfo ci = referenceCulture.CompareInfo;
+
+            do
+            {
+                index = ci.IndexOf(this, oldValue, startIndex, this.Length - startIndex, options, &matchLength);
+                if (index >= 0)
+                {
+                    // append the unmodified portion of string
+                    result.Append(this, startIndex, index - startIndex);
+
+                    // append the replacement
+                    result.Append(newValue);
+
+                    startIndex = index + matchLength;
+                    hasDoneAnyReplacements = true;
+                }
+                else if (!hasDoneAnyReplacements)
+                {
+                    // small optimization,
+                    // if we have not done any replacements,
+                    // we will return the original string
+                    StringBuilderCache.Release(result);
+                    return this;
+                }
+                else
+                {
+                    result.Append(this, startIndex, this.Length - startIndex);
+                }
+            } while (index >= 0);
+
+            return StringBuilderCache.GetStringAndRelease(result);
+        }
+
+        // Replaces all instances of oldChar with newChar.
+        //
+        public string Replace(char oldChar, char newChar)
+        {
+            if (oldChar == newChar)
+                return this;
+
+            unsafe
+            {
+                int remainingLength = Length;
+
+                fixed (char* pChars = &_firstChar)
+                {
+                    char* pSrc = pChars;
+
+                    while (remainingLength > 0)
+                    {
+                        if (*pSrc == oldChar)
+                        {
+                            break;
+                        }
+
+                        remainingLength--;
+                        pSrc++;
+                    }
+                }
+
+                if (remainingLength == 0)
+                    return this;
+
+                string result = FastAllocateString(Length);
+
+                fixed (char* pChars = &_firstChar)
+                {
+                    fixed (char* pResult = &result._firstChar)
+                    {
+                        int copyLength = Length - remainingLength;
+
+                        //Copy the characters already proven not to match.
+                        if (copyLength > 0)
+                        {
+                            wstrcpy(pResult, pChars, copyLength);
+                        }
+
+                        //Copy the remaining characters, doing the replacement as we go.
+                        char* pSrc = pChars + copyLength;
+                        char* pDst = pResult + copyLength;
+
+                        do
+                        {
+                            char currentChar = *pSrc;
+                            if (currentChar == oldChar)
+                                currentChar = newChar;
+                            *pDst = currentChar;
+
+                            remainingLength--;
+                            pSrc++;
+                            pDst++;
+                        } while (remainingLength > 0);
+                    }
+                }
+
+                return result;
+            }
+        }
+
+        public string Replace(string oldValue, string newValue)
+        {
+            if (oldValue == null)
+                throw new ArgumentNullException(nameof(oldValue));
+            if (oldValue.Length == 0)
+                throw new ArgumentException(SR.Argument_StringZeroLength, nameof(oldValue));
+
+            // Api behavior: if newValue is null, instances of oldValue are to be removed.
+            if (newValue == null)
+                newValue = string.Empty;
+
+            Span<int> initialSpan = stackalloc int[StackallocIntBufferSizeLimit];
+            var replacementIndices = new ValueListBuilder<int>(initialSpan);
+
+            unsafe
+            {
+                fixed (char* pThis = &_firstChar)
+                {
+                    int matchIdx = 0;
+                    int lastPossibleMatchIdx = this.Length - oldValue.Length;
+                    while (matchIdx <= lastPossibleMatchIdx)
+                    {
+                        char* pMatch = pThis + matchIdx;
+                        for (int probeIdx = 0; probeIdx < oldValue.Length; probeIdx++)
+                        {
+                            if (pMatch[probeIdx] != oldValue[probeIdx])
+                            {
+                                goto Next;
+                            }
+                        }
+                        // Found a match for the string. Record the location of the match and skip over the "oldValue."
+                        replacementIndices.Append(matchIdx);
+                        matchIdx += oldValue.Length;
+                        continue;
+
+                    Next:
+                        matchIdx++;
+                    }
+                }
+            }
+
+            if (replacementIndices.Length == 0)
+                return this;
+
+            // String allocation and copying is in separate method to make this method faster for the case where
+            // nothing needs replacing.
+            string dst = ReplaceHelper(oldValue.Length, newValue, replacementIndices.AsSpan());
+
+            replacementIndices.Dispose();
+
+            return dst;
+        }
+
+        private string ReplaceHelper(int oldValueLength, string newValue, ReadOnlySpan<int> indices)
+        {
+            Debug.Assert(indices.Length > 0);
+
+            long dstLength = this.Length + ((long)(newValue.Length - oldValueLength)) * indices.Length;
+            if (dstLength > int.MaxValue)
+                throw new OutOfMemoryException();
+            string dst = FastAllocateString((int)dstLength);
+
+            Span<char> dstSpan = new Span<char>(ref dst.GetRawStringData(), dst.Length);
+
+            int thisIdx = 0;
+            int dstIdx = 0;
+
+            for (int r = 0; r < indices.Length; r++)
+            {
+                int replacementIdx = indices[r];
+
+                // Copy over the non-matching portion of the original that precedes this occurrence of oldValue.
+                int count = replacementIdx - thisIdx;
+                if (count != 0)
+                {
+                    this.AsSpan(thisIdx, count).CopyTo(dstSpan.Slice(dstIdx));
+                    dstIdx += count;
+                }
+                thisIdx = replacementIdx + oldValueLength;
+
+                // Copy over newValue to replace the oldValue.
+                newValue.AsSpan().CopyTo(dstSpan.Slice(dstIdx));
+                dstIdx += newValue.Length;
+            }
+
+            // Copy over the final non-matching portion at the end of the string.
+            Debug.Assert(this.Length - thisIdx == dstSpan.Length - dstIdx);
+            this.AsSpan(thisIdx).CopyTo(dstSpan.Slice(dstIdx));
+
+            return dst;
+        }
+
+        public string[] Split(char separator, StringSplitOptions options = StringSplitOptions.None)
+        {
+            return SplitInternal(new ReadOnlySpan<char>(ref separator, 1), int.MaxValue, options);
+        }
+
+        public string[] Split(char separator, int count, StringSplitOptions options = StringSplitOptions.None)
+        {
+            return SplitInternal(new ReadOnlySpan<char>(ref separator, 1), count, options);
+        }
+
+        // Creates an array of strings by splitting this string at each
+        // occurrence of a separator.  The separator is searched for, and if found,
+        // the substring preceding the occurrence is stored as the first element in
+        // the array of strings.  We then continue in this manner by searching
+        // the substring that follows the occurrence.  On the other hand, if the separator
+        // is not found, the array of strings will contain this instance as its only element.
+        // If the separator is null
+        // whitespace (i.e., Character.IsWhitespace) is used as the separator.
+        //
+        public string[] Split(params char[] separator)
+        {
+            return SplitInternal(separator, int.MaxValue, StringSplitOptions.None);
+        }
+
+        // Creates an array of strings by splitting this string at each
+        // occurrence of a separator.  The separator is searched for, and if found,
+        // the substring preceding the occurrence is stored as the first element in
+        // the array of strings.  We then continue in this manner by searching
+        // the substring that follows the occurrence.  On the other hand, if the separator
+        // is not found, the array of strings will contain this instance as its only element.
+        // If the separator is the empty string (i.e., string.Empty), then
+        // whitespace (i.e., Character.IsWhitespace) is used as the separator.
+        // If there are more than count different strings, the last n-(count-1)
+        // elements are concatenated and added as the last string.
+        //
+        public string[] Split(char[] separator, int count)
+        {
+            return SplitInternal(separator, count, StringSplitOptions.None);
+        }
+
+        public string[] Split(char[] separator, StringSplitOptions options)
+        {
+            return SplitInternal(separator, int.MaxValue, options);
+        }
+
+        public string[] Split(char[] separator, int count, StringSplitOptions options)
+        {
+            return SplitInternal(separator, count, options);
+        }
+
+        private string[] SplitInternal(ReadOnlySpan<char> separators, int count, StringSplitOptions options)
+        {
+            if (count < 0)
+                throw new ArgumentOutOfRangeException(nameof(count),
+                    SR.ArgumentOutOfRange_NegativeCount);
+
+            if (options < StringSplitOptions.None || options > StringSplitOptions.RemoveEmptyEntries)
+                throw new ArgumentException(SR.Format(SR.Arg_EnumIllegalVal, options));
+
+            bool omitEmptyEntries = (options == StringSplitOptions.RemoveEmptyEntries);
+
+            if ((count == 0) || (omitEmptyEntries && Length == 0))
+            {
+                return Array.Empty<string>();
+            }
+
+            if (count == 1)
+            {
+                return new string[] { this };
+            }
+
+            Span<int> initialSpan = stackalloc int[StackallocIntBufferSizeLimit];
+            var sepListBuilder = new ValueListBuilder<int>(initialSpan);
+
+            MakeSeparatorList(separators, ref sepListBuilder);
+            ReadOnlySpan<int> sepList = sepListBuilder.AsSpan();
+
+            // Handle the special case of no replaces.
+            if (sepList.Length == 0)
+            {
+                return new string[] { this };
+            }
+
+            string[] result = omitEmptyEntries
+                ? SplitOmitEmptyEntries(sepList, default, 1, count)
+                : SplitKeepEmptyEntries(sepList, default, 1, count);
+
+            sepListBuilder.Dispose();
+
+            return result;
+        }
+
+        public string[] Split(string separator, StringSplitOptions options = StringSplitOptions.None)
+        {
+            return SplitInternal(separator ?? string.Empty, null, int.MaxValue, options);
+        }
+
+        public string[] Split(string separator, int count, StringSplitOptions options = StringSplitOptions.None)
+        {
+            return SplitInternal(separator ?? string.Empty, null, count, options);
+        }
+
+        public string[] Split(string[] separator, StringSplitOptions options)
+        {
+            return SplitInternal(null, separator, int.MaxValue, options);
+        }
+
+        public string[] Split(string[] separator, int count, StringSplitOptions options)
+        {
+            return SplitInternal(null, separator, count, options);
+        }
+
+        private string[] SplitInternal(string separator, string[] separators, int count, StringSplitOptions options)
+        {
+            if (count < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(count),
+                    SR.ArgumentOutOfRange_NegativeCount);
+            }
+
+            if (options < StringSplitOptions.None || options > StringSplitOptions.RemoveEmptyEntries)
+            {
+                throw new ArgumentException(SR.Format(SR.Arg_EnumIllegalVal, (int)options));
+            }
+
+            bool omitEmptyEntries = (options == StringSplitOptions.RemoveEmptyEntries);
+
+            bool singleSeparator = separator != null;
+
+            if (!singleSeparator && (separators == null || separators.Length == 0))
+            {
+                return SplitInternal((char[])null, count, options);
+            }
+
+            if ((count == 0) || (omitEmptyEntries && Length == 0))
+            {
+                return Array.Empty<string>();
+            }
+
+            if (count == 1 || (singleSeparator && separator.Length == 0))
+            {
+                return new string[] { this };
+            }
+
+            if (singleSeparator)
+            {
+                return SplitInternal(separator, count, options);
+            }
+
+            Span<int> sepListInitialSpan = stackalloc int[StackallocIntBufferSizeLimit];
+            var sepListBuilder = new ValueListBuilder<int>(sepListInitialSpan);
+
+            Span<int> lengthListInitialSpan = stackalloc int[StackallocIntBufferSizeLimit];
+            var lengthListBuilder = new ValueListBuilder<int>(lengthListInitialSpan);
+
+            MakeSeparatorList(separators, ref sepListBuilder, ref lengthListBuilder);
+            ReadOnlySpan<int> sepList = sepListBuilder.AsSpan();
+            ReadOnlySpan<int> lengthList = lengthListBuilder.AsSpan();
+
+            // Handle the special case of no replaces.
+            if (sepList.Length == 0)
+            {
+                return new string[] { this };
+            }
+
+            string[] result = omitEmptyEntries
+                ? SplitOmitEmptyEntries(sepList, lengthList, 0, count)
+                : SplitKeepEmptyEntries(sepList, lengthList, 0, count);
+
+            sepListBuilder.Dispose();
+            lengthListBuilder.Dispose();
+
+            return result;
+        }
+
+        private string[] SplitInternal(string separator, int count, StringSplitOptions options)
+        {
+            Span<int> sepListInitialSpan = stackalloc int[StackallocIntBufferSizeLimit];
+            var sepListBuilder = new ValueListBuilder<int>(sepListInitialSpan);
+
+            MakeSeparatorList(separator, ref sepListBuilder);
+            ReadOnlySpan<int> sepList = sepListBuilder.AsSpan();
+            if (sepList.Length == 0)
+            {
+                // there are no separators so sepListBuilder did not rent an array from pool and there is no need to dispose it
+                return new string[] { this };
+            }
+
+            string[] result = options == StringSplitOptions.RemoveEmptyEntries
+                ? SplitOmitEmptyEntries(sepList, default, separator.Length, count)
+                : SplitKeepEmptyEntries(sepList, default, separator.Length, count);
+
+            sepListBuilder.Dispose();
+
+            return result;
+        }
+
+        private string[] SplitKeepEmptyEntries(ReadOnlySpan<int> sepList, ReadOnlySpan<int> lengthList, int defaultLength, int count)
+        {
+            Debug.Assert(count >= 2);
+
+            int currIndex = 0;
+            int arrIndex = 0;
+
+            count--;
+            int numActualReplaces = (sepList.Length < count) ? sepList.Length : count;
+
+            //Allocate space for the new array.
+            //+1 for the string from the end of the last replace to the end of the string.
+            string[] splitStrings = new string[numActualReplaces + 1];
+
+            for (int i = 0; i < numActualReplaces && currIndex < Length; i++)
+            {
+                splitStrings[arrIndex++] = Substring(currIndex, sepList[i] - currIndex);
+                currIndex = sepList[i] + (lengthList.IsEmpty ? defaultLength : lengthList[i]);
+            }
+
+            //Handle the last string at the end of the array if there is one.
+            if (currIndex < Length && numActualReplaces >= 0)
+            {
+                splitStrings[arrIndex] = Substring(currIndex);
+            }
+            else if (arrIndex == numActualReplaces)
+            {
+                //We had a separator character at the end of a string.  Rather than just allowing
+                //a null character, we'll replace the last element in the array with an empty string.
+                splitStrings[arrIndex] = string.Empty;
+            }
+
+            return splitStrings;
+        }
+
+
+        // This function will not keep the Empty string
+        private string[] SplitOmitEmptyEntries(ReadOnlySpan<int> sepList, ReadOnlySpan<int> lengthList, int defaultLength, int count)
+        {
+            Debug.Assert(count >= 2);
+
+            int numReplaces = sepList.Length;
+
+            // Allocate array to hold items. This array may not be
+            // filled completely in this function, we will create a
+            // new array and copy string references to that new array.
+            int maxItems = (numReplaces < count) ? (numReplaces + 1) : count;
+            string[] splitStrings = new string[maxItems];
+
+            int currIndex = 0;
+            int arrIndex = 0;
+
+            for (int i = 0; i < numReplaces && currIndex < Length; i++)
+            {
+                if (sepList[i] - currIndex > 0)
+                {
+                    splitStrings[arrIndex++] = Substring(currIndex, sepList[i] - currIndex);
+                }
+                currIndex = sepList[i] + (lengthList.IsEmpty ? defaultLength : lengthList[i]);
+                if (arrIndex == count - 1)
+                {
+                    // If all the remaining entries at the end are empty, skip them
+                    while (i < numReplaces - 1 && currIndex == sepList[++i])
+                    {
+                        currIndex += (lengthList.IsEmpty ? defaultLength : lengthList[i]);
+                    }
+                    break;
+                }
+            }
+
+            // we must have at least one slot left to fill in the last string.
+            Debug.Assert(arrIndex < maxItems);
+
+            //Handle the last string at the end of the array if there is one.
+            if (currIndex < Length)
+            {
+                splitStrings[arrIndex++] = Substring(currIndex);
+            }
+
+            string[] stringArray = splitStrings;
+            if (arrIndex != maxItems)
+            {
+                stringArray = new string[arrIndex];
+                for (int j = 0; j < arrIndex; j++)
+                {
+                    stringArray[j] = splitStrings[j];
+                }
+            }
+            return stringArray;
+        }
+
+        /// <summary>
+        /// Uses ValueListBuilder to create list that holds indexes of separators in string.
+        /// </summary>
+        /// <param name="separators"><see cref="ReadOnlySpan{T}"/> of separator chars</param>
+        /// <param name="sepListBuilder"><see cref="ValueListBuilder{T}"/> to store indexes</param>
+        /// <returns></returns>
+        private void MakeSeparatorList(ReadOnlySpan<char> separators, ref ValueListBuilder<int> sepListBuilder)
+        {
+            char sep0, sep1, sep2;
+
+            switch (separators.Length)
+            {
+                // Special-case no separators to mean any whitespace is a separator.
+                case 0:
+                    for (int i = 0; i < Length; i++)
+                    {
+                        if (char.IsWhiteSpace(this[i]))
+                        {
+                            sepListBuilder.Append(i);
+                        }
+                    }
+                    break;
+
+                // Special-case the common cases of 1, 2, and 3 separators, with manual comparisons against each separator.
+                case 1:
+                    sep0 = separators[0];
+                    for (int i = 0; i < Length; i++)
+                    {
+                        if (this[i] == sep0)
+                        {
+                            sepListBuilder.Append(i);
+                        }
+                    }
+                    break;
+                case 2:
+                    sep0 = separators[0];
+                    sep1 = separators[1];
+                    for (int i = 0; i < Length; i++)
+                    {
+                        char c = this[i];
+                        if (c == sep0 || c == sep1)
+                        {
+                            sepListBuilder.Append(i);
+                        }
+                    }
+                    break;
+                case 3:
+                    sep0 = separators[0];
+                    sep1 = separators[1];
+                    sep2 = separators[2];
+                    for (int i = 0; i < Length; i++)
+                    {
+                        char c = this[i];
+                        if (c == sep0 || c == sep1 || c == sep2)
+                        {
+                            sepListBuilder.Append(i);
+                        }
+                    }
+                    break;
+
+                // Handle > 3 separators with a probabilistic map, ala IndexOfAny.
+                // This optimizes for chars being unlikely to match a separator.
+                default:
+                    unsafe
+                    {
+                        ProbabilisticMap map = default;
+                        uint* charMap = (uint*)&map;
+                        InitializeProbabilisticMap(charMap, separators);
+
+                        for (int i = 0; i < Length; i++)
+                        {
+                            char c = this[i];
+                            if (IsCharBitSet(charMap, (byte)c) && IsCharBitSet(charMap, (byte)(c >> 8)) &&
+                                separators.Contains(c))
+                            {
+                                sepListBuilder.Append(i);
+                            }
+                        }
+                    }
+                    break;
+            }
+        }
+
+        /// <summary>
+        /// Uses ValueListBuilder to create list that holds indexes of separators in string.
+        /// </summary>
+        /// <param name="separator">separator string</param>
+        /// <param name="sepListBuilder"><see cref="ValueListBuilder{T}"/> to store indexes</param>
+        /// <returns></returns>
+        private void MakeSeparatorList(string separator, ref ValueListBuilder<int> sepListBuilder)
+        {
+            Debug.Assert(!IsNullOrEmpty(separator), "!string.IsNullOrEmpty(separator)");
+
+            int currentSepLength = separator.Length;
+
+            for (int i = 0; i < Length; i++)
+            {
+                if (this[i] == separator[0] && currentSepLength <= Length - i)
+                {
+                    if (currentSepLength == 1
+                        || this.AsSpan(i, currentSepLength).SequenceEqual(separator))
+                    {
+                        sepListBuilder.Append(i);
+                        i += currentSepLength - 1;
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Uses ValueListBuilder to create list that holds indexes of separators in string and list that holds length of separator strings.
+        /// </summary>
+        /// <param name="separators">separator strngs</param>
+        /// <param name="sepListBuilder"><see cref="ValueListBuilder{T}"/> for separator indexes</param>
+        /// <param name="lengthListBuilder"><see cref="ValueListBuilder{T}"/> for separator length values</param>
+        private void MakeSeparatorList(string[] separators, ref ValueListBuilder<int> sepListBuilder, ref ValueListBuilder<int> lengthListBuilder)
+        {
+            Debug.Assert(separators != null && separators.Length > 0, "separators != null && separators.Length > 0");
+
+            int sepCount = separators.Length;
+
+            for (int i = 0; i < Length; i++)
+            {
+                for (int j = 0; j < separators.Length; j++)
+                {
+                    string separator = separators[j];
+                    if (IsNullOrEmpty(separator))
+                    {
+                        continue;
+                    }
+                    int currentSepLength = separator.Length;
+                    if (this[i] == separator[0] && currentSepLength <= Length - i)
+                    {
+                        if (currentSepLength == 1
+                            || this.AsSpan(i, currentSepLength).SequenceEqual(separator))
+                        {
+                            sepListBuilder.Append(i);
+                            lengthListBuilder.Append(currentSepLength);
+                            i += currentSepLength - 1;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        // Returns a substring of this string.
+        //
+        public string Substring(int startIndex) => Substring(startIndex, Length - startIndex);
+
+        public string Substring(int startIndex, int length)
+        {
+            if (startIndex < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
+            }
+
+            if (startIndex > Length)
+            {
+                throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndexLargerThanLength);
+            }
+
+            if (length < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NegativeLength);
+            }
+
+            if (startIndex > Length - length)
+            {
+                throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_IndexLength);
+            }
+
+            if (length == 0)
+            {
+                return string.Empty;
+            }
+
+            if (startIndex == 0 && length == this.Length)
+            {
+                return this;
+            }
+
+            return InternalSubString(startIndex, length);
+        }
+
+        private unsafe string InternalSubString(int startIndex, int length)
+        {
+            Debug.Assert(startIndex >= 0 && startIndex <= this.Length, "StartIndex is out of range!");
+            Debug.Assert(length >= 0 && startIndex <= this.Length - length, "length is out of range!");
+
+            string result = FastAllocateString(length);
+
+            fixed (char* dest = &result._firstChar)
+            fixed (char* src = &_firstChar)
+            {
+                wstrcpy(dest, src + startIndex, length);
+            }
+
+            return result;
+        }
+
+        // Trims the whitespace from both ends of the string.  Whitespace is defined by
+        // char.IsWhiteSpace.
+        //
+        public string Trim() => TrimWhiteSpaceHelper(TrimType.Both);
+
+        // Removes a set of characters from the beginning and end of this string.
+        public unsafe string Trim(char trimChar) => TrimHelper(&trimChar, 1, TrimType.Both);
+
+        // Removes a set of characters from the beginning and end of this string.
+        public unsafe string Trim(params char[] trimChars)
+        {
+            if (trimChars == null || trimChars.Length == 0)
+            {
+                return TrimWhiteSpaceHelper(TrimType.Both);
+            }
+            fixed (char* pTrimChars = &trimChars[0])
+            {
+                return TrimHelper(pTrimChars, trimChars.Length, TrimType.Both);
+            }
+        }
+
+        // Removes a set of characters from the beginning of this string.
+        public string TrimStart() => TrimWhiteSpaceHelper(TrimType.Head);
+
+        // Removes a set of characters from the beginning of this string.
+        public unsafe string TrimStart(char trimChar) => TrimHelper(&trimChar, 1, TrimType.Head);
+
+        // Removes a set of characters from the beginning of this string.
+        public unsafe string TrimStart(params char[] trimChars)
+        {
+            if (trimChars == null || trimChars.Length == 0)
+            {
+                return TrimWhiteSpaceHelper(TrimType.Head);
+            }
+            fixed (char* pTrimChars = &trimChars[0])
+            {
+                return TrimHelper(pTrimChars, trimChars.Length, TrimType.Head);
+            }
+        }
+
+        // Removes a set of characters from the end of this string.
+        public string TrimEnd() => TrimWhiteSpaceHelper(TrimType.Tail);
+
+        // Removes a set of characters from the end of this string.
+        public unsafe string TrimEnd(char trimChar) => TrimHelper(&trimChar, 1, TrimType.Tail);
+
+        // Removes a set of characters from the end of this string.
+        public unsafe string TrimEnd(params char[] trimChars)
+        {
+            if (trimChars == null || trimChars.Length == 0)
+            {
+                return TrimWhiteSpaceHelper(TrimType.Tail);
+            }
+            fixed (char* pTrimChars = &trimChars[0])
+            {
+                return TrimHelper(pTrimChars, trimChars.Length, TrimType.Tail);
+            }
+        }
+
+        private string TrimWhiteSpaceHelper(TrimType trimType)
+        {
+            // end will point to the first non-trimmed character on the right.
+            // start will point to the first non-trimmed character on the left.
+            int end = Length - 1;
+            int start = 0;
+
+            // Trim specified characters.
+            if (trimType != TrimType.Tail)
+            {
+                for (start = 0; start < Length; start++)
+                {
+                    if (!char.IsWhiteSpace(this[start]))
+                    {
+                        break;
+                    }
+                }
+            }
+
+            if (trimType != TrimType.Head)
+            {
+                for (end = Length - 1; end >= start; end--)
+                {
+                    if (!char.IsWhiteSpace(this[end]))
+                    {
+                        break;
+                    }
+                }
+            }
+
+            return CreateTrimmedString(start, end);
+        }
+
+        private unsafe string TrimHelper(char* trimChars, int trimCharsLength, TrimType trimType)
+        {
+            Debug.Assert(trimChars != null);
+            Debug.Assert(trimCharsLength > 0);
+
+            // end will point to the first non-trimmed character on the right.
+            // start will point to the first non-trimmed character on the left.
+            int end = Length - 1;
+            int start = 0;
+
+            // Trim specified characters.
+            if (trimType != TrimType.Tail)
+            {
+                for (start = 0; start < Length; start++)
+                {
+                    int i = 0;
+                    char ch = this[start];
+                    for (i = 0; i < trimCharsLength; i++)
+                    {
+                        if (trimChars[i] == ch)
+                        {
+                            break;
+                        }
+                    }
+                    if (i == trimCharsLength)
+                    {
+                        // The character is not in trimChars, so stop trimming.
+                        break;
+                    }
+                }
+            }
+
+            if (trimType != TrimType.Head)
+            {
+                for (end = Length - 1; end >= start; end--)
+                {
+                    int i = 0;
+                    char ch = this[end];
+                    for (i = 0; i < trimCharsLength; i++)
+                    {
+                        if (trimChars[i] == ch)
+                        {
+                            break;
+                        }
+                    }
+                    if (i == trimCharsLength)
+                    {
+                        // The character is not in trimChars, so stop trimming.
+                        break;
+                    }
+                }
+            }
+
+            return CreateTrimmedString(start, end);
+        }
+
+        private string CreateTrimmedString(int start, int end)
+        {
+            int len = end - start + 1;
+            return
+                len == Length ? this :
+                len == 0 ? string.Empty :
+                InternalSubString(start, len);
+        }
+
+        private enum TrimType
+        {
+            Head = 0,
+            Tail = 1,
+            Both = 2
+        }
     }
 }

+ 13 - 0
src/System.Private.CoreLib/System/StringSplitOptions.cs

@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System
+{
+    [Flags]
+    public enum StringSplitOptions
+    {
+        None = 0,
+        RemoveEmptyEntries = 1
+    }
+}

+ 2 - 0
src/System.Private.CoreLib/System/Type.cs

@@ -5,6 +5,8 @@ namespace System
 {
     public class Type
     {
+        public int this[int i] => 0;
+
         public static Type GetTypeFromHandle(RuntimeTypeHandle handle)
         {
             return handle._runtimeType;

+ 1 - 0
src/System.Runtime/TypeForwards.cs

@@ -69,6 +69,7 @@ using System.Threading;
 [assembly: TypeForwardedTo(typeof(IAsyncResult))]
 [assembly: TypeForwardedTo(typeof(IDisposable))]
 [assembly: TypeForwardedTo(typeof(HashCode))]
+[assembly: TypeForwardedTo(typeof(IndexerNameAttribute))]
 [assembly: TypeForwardedTo(typeof(Exception))]
 [assembly: TypeForwardedTo(typeof(ArgumentException))]
 [assembly: TypeForwardedTo(typeof(ArgumentNullException))]