Skip to content
This repository was archived by the owner on Nov 1, 2020. It is now read-only.
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 133 additions & 26 deletions src/System.Private.CoreLib/src/System/String.Manipulation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,16 @@ public String Replace(String oldValue, String newValue)
}
}

public String[] Split(char separator, StringSplitOptions options = StringSplitOptions.None)
{
return SplitInternal(separator, Int32.MaxValue, options);
}

public String[] Split(char separator, int count, StringSplitOptions options = StringSplitOptions.None)
{
return SplitInternal(separator, 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
Expand All @@ -922,7 +932,7 @@ public String Replace(String oldValue, String newValue)
//
public String[] Split(params char[] separator)
{
return Split(separator, Int32.MaxValue, StringSplitOptions.None);
return SplitInternal(separator, Int32.MaxValue, StringSplitOptions.None);
}

// Creates an array of strings by splitting this string at each
Expand All @@ -938,15 +948,36 @@ public String[] Split(params char[] separator)
//
public string[] Split(char[] separator, int count)
{
return Split(separator, count, StringSplitOptions.None);
return SplitInternal(separator, count, StringSplitOptions.None);
}

public String[] Split(char[] separator, StringSplitOptions options)
{
return Split(separator, Int32.MaxValue, options);
return SplitInternal(separator, Int32.MaxValue, options);
}

public String[] Split(char[] separator, int count, StringSplitOptions options)
{
return SplitInternal(separator, count, options);
}

private unsafe String[] SplitInternal(char separator, int count, StringSplitOptions options)
{
char* pSeparators = stackalloc char[1];
pSeparators[0] = separator;
return SplitInternal(pSeparators, /*separatorsLength*/ 1, count, options);
}

private unsafe String[] SplitInternal(char[] separator, int count, StringSplitOptions options)
{
fixed (char* pSeparators = separator)
{
int separatorsLength = separator == null ? 0 : separator.Length;
return SplitInternal(pSeparators, separatorsLength, count, options);
}
}

private unsafe String[] SplitInternal(char* separators, int separatorsLength, int count, StringSplitOptions options)
{
if (count < 0)
throw new ArgumentOutOfRangeException("count",
Expand All @@ -968,7 +999,7 @@ public String[] Split(char[] separator, int count, StringSplitOptions options)
}

int[] sepList = new int[Length];
int numReplaces = MakeSeparatorList(separator, sepList);
int numReplaces = MakeSeparatorList(separators, separatorsLength, sepList);

// Handle the special case of no replaces.
if (0 == numReplaces)
Expand All @@ -978,20 +1009,35 @@ public String[] Split(char[] separator, int count, StringSplitOptions options)

if (omitEmptyEntries)
{
return SplitOmitEmptyEntries(sepList, null, numReplaces, count);
return SplitOmitEmptyEntries(sepList, null, 1, numReplaces, count);
}
else
{
return SplitKeepEmptyEntries(sepList, null, numReplaces, count);
return SplitKeepEmptyEntries(sepList, null, 1, numReplaces, count);
}
}

public String[] Split(String separator, StringSplitOptions options = StringSplitOptions.None)
{
return SplitInternal(separator ?? String.Empty, null, Int32.MaxValue, options);
}

public String[] Split(String separator, Int32 count, StringSplitOptions options = StringSplitOptions.None)
{
return SplitInternal(separator ?? String.Empty, null, count, options);
}

public String[] Split(String[] separator, StringSplitOptions options)
{
return Split(separator, Int32.MaxValue, options);
return SplitInternal(null, separator, Int32.MaxValue, options);
}

public String[] Split(String[] separator, Int32 count, StringSplitOptions options)
{
return SplitInternal(null, separator, count, options);
}

private String[] SplitInternal(String separator, String[] separators, Int32 count, StringSplitOptions options)
{
if (count < 0)
{
Expand All @@ -1006,38 +1052,54 @@ public String[] Split(String[] separator, Int32 count, StringSplitOptions option

bool omitEmptyEntries = (options == StringSplitOptions.RemoveEmptyEntries);

if (separator == null || separator.Length == 0)
bool singleSeparator = separator != null;

if (!singleSeparator && (separators == null || separators.Length == 0))
{
return Split((char[])null, count, options);
return SplitInternal((char[])null, count, options);
}

if ((count == 0) || (omitEmptyEntries && this.Length == 0))
{
return Array.Empty<String>();
}

if (count == 1)
if (count == 1 || (singleSeparator && separator.Length == 0))
{
return new String[] { this };
}

int[] sepList = new int[Length];
int[] lengthList = new int[Length];
int numReplaces = MakeSeparatorList(separator, sepList, lengthList);
int[] lengthList;
int defaultLength;
int numReplaces;

if (singleSeparator)
{
lengthList = null;
defaultLength = separator.Length;
numReplaces = MakeSeparatorList(separator, sepList);
}
else
{
lengthList = new int[Length];
defaultLength = 0;
numReplaces = MakeSeparatorList(separators, sepList, lengthList);
}

//Handle the special case of no replaces.
// Handle the special case of no replaces.
if (0 == numReplaces)
{
return new String[] { this };
}

if (omitEmptyEntries)
{
return SplitOmitEmptyEntries(sepList, lengthList, numReplaces, count);
return SplitOmitEmptyEntries(sepList, lengthList, defaultLength, numReplaces, count);
}
else
{
return SplitKeepEmptyEntries(sepList, lengthList, numReplaces, count);
return SplitKeepEmptyEntries(sepList, lengthList, defaultLength, numReplaces, count);
}
}

Expand All @@ -1046,8 +1108,11 @@ public String[] Split(String[] separator, Int32 count, StringSplitOptions option
// the original string will be returned regardless of the count.
//

private String[] SplitKeepEmptyEntries(Int32[] sepList, Int32[] lengthList, Int32 numReplaces, int count)
private String[] SplitKeepEmptyEntries(Int32[] sepList, Int32[] lengthList, Int32 defaultLength, Int32 numReplaces, int count)
{
Debug.Assert(numReplaces >= 0);
Debug.Assert(count >= 2);

int currIndex = 0;
int arrIndex = 0;

Expand All @@ -1061,7 +1126,7 @@ private String[] SplitKeepEmptyEntries(Int32[] sepList, Int32[] lengthList, Int3
for (int i = 0; i < numActualReplaces && currIndex < Length; i++)
{
splitStrings[arrIndex++] = Substring(currIndex, sepList[i] - currIndex);
currIndex = sepList[i] + ((lengthList == null) ? 1 : lengthList[i]);
currIndex = sepList[i] + ((lengthList == null) ? defaultLength : lengthList[i]);
}

//Handle the last string at the end of the array if there is one.
Expand All @@ -1081,8 +1146,11 @@ private String[] SplitKeepEmptyEntries(Int32[] sepList, Int32[] lengthList, Int3


// This function will not keep the Empty String
private String[] SplitOmitEmptyEntries(Int32[] sepList, Int32[] lengthList, Int32 numReplaces, int count)
private String[] SplitOmitEmptyEntries(Int32[] sepList, Int32[] lengthList, Int32 defaultLength, Int32 numReplaces, int count)
{
Debug.Assert(numReplaces >= 0);
Debug.Assert(count >= 2);

// 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.
Expand All @@ -1099,18 +1167,21 @@ private String[] SplitOmitEmptyEntries(Int32[] sepList, Int32[] lengthList, Int3
{
splitStrings[arrIndex++] = Substring(currIndex, sepList[i] - currIndex);
}
currIndex = sepList[i] + ((lengthList == null) ? 1 : lengthList[i]);
currIndex = sepList[i] + ((lengthList == null) ? 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 == null) ? 1 : lengthList[i]);
currIndex += ((lengthList == null) ? 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)
{
Expand All @@ -1135,11 +1206,13 @@ private String[] SplitOmitEmptyEntries(Int32[] sepList, Int32[] lengthList, Int3
// Args: separator -- A string containing all of the split characters.
// sepList -- an array of ints for split char indicies.
//--------------------------------------------------------------------
private unsafe int MakeSeparatorList(char[] separator, int[] sepList)
private unsafe int MakeSeparatorList(char* separators, int separatorsLength, int[] sepList)
{
Debug.Assert(separatorsLength >= 0, "separatorsLength >= 0");

int foundCount = 0;

if (separator == null || separator.Length == 0)
if (separators == null || separatorsLength == 0)
{
fixed (char* pwzChars = &_firstChar)
{
Expand All @@ -1156,14 +1229,13 @@ private unsafe int MakeSeparatorList(char[] separator, int[] sepList)
else
{
int sepListCount = sepList.Length;
int sepCount = separator.Length;
//If they passed in a string of chars, actually look for those chars.
fixed (char* pwzChars = &_firstChar, pSepChars = separator)
fixed (char* pwzChars = &_firstChar)
{
for (int i = 0; i < Length && foundCount < sepListCount; i++)
{
char* pSep = pSepChars;
for (int j = 0; j < sepCount; j++, pSep++)
char* pSep = separators;
for (int j = 0; j < separatorsLength; j++, pSep++)
{
if (pwzChars[i] == *pSep)
{
Expand All @@ -1177,6 +1249,39 @@ private unsafe int MakeSeparatorList(char[] separator, int[] sepList)
return foundCount;
}

//--------------------------------------------------------------------
// This function returns number of the places within baseString where
// instances of the separator string occurs.
// Args: separator -- the separator
// sepList -- an array of ints for split string indicies.
//--------------------------------------------------------------------
private unsafe int MakeSeparatorList(string separator, int[] sepList)
{
Debug.Assert(!string.IsNullOrEmpty(separator), "!string.IsNullOrEmpty(separator)");

int foundCount = 0;
int sepListCount = sepList.Length;
int currentSepLength = separator.Length;

fixed (char* pwzChars = &_firstChar)
{
for (int i = 0; i < Length && foundCount < sepListCount; i++)
{
if (pwzChars[i] == separator[0] && currentSepLength <= Length - i)
{
if (currentSepLength == 1
|| String.CompareOrdinal(this, i, separator, 0, currentSepLength) == 0)
{
sepList[foundCount] = i;
foundCount++;
i += currentSepLength - 1;
}
}
}
}
return foundCount;
}

//--------------------------------------------------------------------
// This function returns the number of the places within this instance where
// instances of separator strings occur.
Expand All @@ -1186,6 +1291,8 @@ private unsafe int MakeSeparatorList(char[] separator, int[] sepList)
//--------------------------------------------------------------------
private unsafe int MakeSeparatorList(String[] separators, int[] sepList, int[] lengthList)
{
Debug.Assert(separators != null && separators.Length > 0, "separators != null && separators.Length > 0");

int foundCount = 0;
int sepListCount = sepList.Length;
int sepCount = separators.Length;
Expand Down