create ReadOnlySpan<char> from string (#7276)
authorAdam Sitnik <adam.sitnik@gmail.com>
Wed, 21 Sep 2016 00:12:47 +0000 (02:12 +0200)
committerJan Kotas <jkotas@microsoft.com>
Mon, 31 Oct 2016 00:27:41 +0000 (17:27 -0700)
src/mscorlib/model.xml
src/mscorlib/src/System/ReadOnlySpan.cs
src/mscorlib/src/System/String.cs
src/mscorlib/src/System/ThrowHelper.cs
tests/src/CoreMangLib/system/span/BasicSpanTest.cs

index bb59217..da43327 100644 (file)
       <Member Name="Equals(System.ReadOnlySpan&lt;T&gt;)" />
       <Member Name="TryCopyTo(System.Span&lt;T&gt;)" />
     </Type>
-    <Type Name="System.SpanExtensions">
+    <Type Name="System.SpanExtensions" Condition="FEATURE_SPAN_OF_T">
       <Member Name="AsBytes&lt;T&gt;(System.Span&lt;T&gt;)" />
       <Member Name="AsBytes&lt;T&gt;(System.ReadOnlySpan&lt;T&gt;)" />
       <Member Name="NonPortableCast&lt;TFrom,TTo&gt;(System.Span&lt;TFrom&gt;)" />
       <Member Name="NonPortableCast&lt;TFrom,TTo&gt;(System.ReadOnlySpan&lt;TFrom&gt;)" />
     </Type>
+    <Type Name="System.ReadOnlySpanExtensions" Condition="FEATURE_SPAN_OF_T">
+      <Member Name="Slice(System.String)" />
+      <Member Name="Slice(System.String,System.Int32)" />
+      <Member Name="Slice(System.String,System.Int32,System.Int32)" />
+    </Type>
   </Assembly>
 </ThinModel>
index 38a69bb..44ef1ab 100644 (file)
@@ -203,4 +203,62 @@ namespace System
             return true;
         }
     }
+
+    public static class ReadOnlySpanExtensions
+    {
+        /// <summary>
+        /// Creates a new readonly span over the portion of the target string.
+        /// </summary>
+        /// <param name="text">The target string.</param>
+        /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="text"/> is a null
+        /// reference (Nothing in Visual Basic).</exception>
+        public static ReadOnlySpan<char> Slice(this string text)
+        {
+            if (text == null)
+                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
+
+            return new ReadOnlySpan<char>(ref text.GetFirstCharRef(), text.Length);
+        }
+
+        /// <summary>
+        /// Creates a new readonly span over the portion of the target string, beginning at 'start'.
+        /// </summary>
+        /// <param name="text">The target string.</param>
+        /// <param name="start">The index at which to begin this slice.</param>
+        /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="text"/> is a null
+        /// reference (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentOutOfRangeException">
+        /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;Length).
+        /// </exception>
+        public static ReadOnlySpan<char> Slice(this string text, int start)
+        {
+            if (text == null)
+                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
+            if ((uint)start > (uint)text.Length)
+                ThrowHelper.ThrowArgumentOutOfRangeException();
+
+            return new ReadOnlySpan<char>(ref JitHelpers.AddByRef(ref text.GetFirstCharRef(), start), text.Length - start);
+        }
+
+        /// <summary>
+        /// Creates a new readonly span over the portion of the target string, beginning at <paramref name="start"/>, of given <paramref name="length"/>.
+        /// </summary>
+        /// <param name="text">The target string.</param>
+        /// <param name="start">The index at which to begin this slice.</param>
+        /// <param name="length">The number of items in the span.</param>
+        /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="text"/> is a null
+        /// reference (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentOutOfRangeException">
+        /// Thrown when the specified <paramref name="start"/> or end index is not in range (&lt;0 or &gt;&eq;Length).
+        /// </exception>
+        public static ReadOnlySpan<char> Slice(this string text, int start, int length)
+        {
+            if (text == null)
+                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
+            if ((uint)start >= (uint)text.Length || (uint)length > (uint)(text.Length - start))
+                ThrowHelper.ThrowArgumentOutOfRangeException();
+
+            return new ReadOnlySpan<char>(ref JitHelpers.AddByRef(ref text.GetFirstCharRef(), start), length);
+        }
+    }
 }
\ No newline at end of file
index 4430357..bcf869f 100644 (file)
@@ -884,6 +884,10 @@ namespace System {
                 byte* dstPtr = (byte*) dest;
                 Buffer.Memcpy(dstPtr, srcPtr, len);
             }
-        }      
+        }
+
+        internal ref char GetFirstCharRef() {
+            return ref m_firstChar;
+        }
     }
 }
index ab2afd4..4553ab7 100644 (file)
@@ -352,6 +352,7 @@ namespace System {
         addValueFactory,
         updateValueFactory,
         concurrencyLevel,
+        text,
 
     }
 
index 633ab89..a141eaf 100644 (file)
@@ -73,6 +73,7 @@ class My
         Test(WhenSourceDoesntFitIntoTargetLengthIsZero, "WhenSourceDoesntFitIntoTargetLengthIsZero", ref failedTestsCount);
         Test(WhenSourceFitsIntoTargetOnceLengthIsOne, "WhenSourceFitsIntoTargetOnceLengthIsOne", ref failedTestsCount);
         Test(WhenSourceTypeLargerThaTargetAndOverflowsInt32ThrowsException, "WhenSourceTypeLargerThaTargetAndOverflowsInt32ThrowsException", ref failedTestsCount);
+        Test(CanCreateSpanFromString, "CanCreateSpanFromString", ref failedTestsCount);
 
         Console.WriteLine(string.Format("{0} tests has failed", failedTestsCount));
         Environment.Exit(failedTestsCount);
@@ -608,6 +609,21 @@ class My
         }
     }
 
+    static void CanCreateSpanFromString()
+    {
+        const string fullText = "new Span<byte>()";
+        var spanFromFull = fullText.Slice();
+        AssertEqualContent(fullText, spanFromFull);
+
+        string firstHalfOfString = fullText.Substring(0, fullText.Length / 2);
+        var spanFromFirstHalf = fullText.Slice(0, fullText.Length / 2);
+        AssertEqualContent(firstHalfOfString, spanFromFirstHalf);
+
+        string secondHalfOfString = fullText.Substring(fullText.Length / 2);
+        var spanFromSecondHalf = fullText.Slice(fullText.Length / 2);
+        AssertEqualContent(secondHalfOfString, spanFromSecondHalf);
+    }
+
     static void Test(Action test, string testName, ref int failedTestsCount)
     {
         try
@@ -644,4 +660,13 @@ class My
             throw new Exception(string.Format("Values were not equal! {0} and {1}", left, right));
         }
     }
+
+    static void AssertEqualContent(string text, ReadOnlySpan<char> span)
+    {
+        AssertEqual(text.Length, span.Length);
+        for (int i = 0; i < text.Length; i++)
+        {
+            AssertEqual(text[i], span[i]);
+        }
+    }
 }