提交 1b134bcc 编写于 作者: P Pharring

Reduce allocations in CommandLineParser.CondenseDoubledBackslashes

Remove unused CommandLineSplitter class and update unit tests. (changeset 1407192)
上级 a7e4c1ec
......@@ -12,9 +12,9 @@ public class CommonCommandLineParserTests : TestBase
{
private const int EN_US = 1033;
private void VerifyCommandLineSplitter(string commandLine, string[] expected)
private void VerifyCommandLineSplitter(string commandLine, string[] expected, bool removeHashComments = false)
{
string[] actual = CommandLineSplitter.SplitCommandLine(commandLine);
var actual = CommandLineParser.SplitCommandLineIntoArguments(commandLine, removeHashComments).ToArray();
Assert.Equal(expected.Length, actual.Length);
for (int i = 0; i < actual.Length; ++i)
......@@ -94,6 +94,8 @@ public void TestCommandLineSplitter()
new string[] { @"\""abc def""", @"\""abc", @"def""" });
VerifyCommandLineSplitter(@" \\\\""abc def"" \\\\\""abc def"" ",
new string[] { @"\\""abc def""", @"\\""abc", @"def""" });
VerifyCommandLineSplitter(@"abc #Comment ignored",
new string[] { @"abc" }, removeHashComments: true);
}
[Fact]
......
......@@ -7,7 +7,7 @@
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
......@@ -509,15 +509,20 @@ private static IEnumerable<string> Split(string str, Func<char, bool> splitHere)
/// </summary>
private static string CondenseDoubledBackslashes(string input)
{
int i = input.IndexOf('\\');
// Simple case -- no backslashes.
if (!input.Contains('\\'))
if (i < 0)
return input;
StringBuilder builder = new StringBuilder();
int backslashCount = 0;
var pooledBuilder = PooledStringBuilder.GetInstance();
var builder = pooledBuilder.Builder;
builder.Append(input, 0, i);
foreach (char c in input)
int backslashCount = 0;
do
{
char c = input[i];
if (c == '\\')
{
++backslashCount;
......@@ -537,10 +542,10 @@ private static string CondenseDoubledBackslashes(string input)
builder.Append(c);
backslashCount = 0;
}
}
} while (++i < input.Length);
AddBackslashes(builder, backslashCount);
return builder.ToString();
return pooledBuilder.ToStringAndFree();
}
/// <summary>
......@@ -865,153 +870,4 @@ internal static bool TryParseUInt16(string value, out ushort result)
return true;
}
}
/// <remarks>
/// Rules for command line parsing, according to MSDN:
///
/// Arguments are delimited by white space, which is either a space or a tab.
///
/// A string surrounded by double quotation marks ("string") is interpreted
/// as a single argument, regardless of white space contained within.
/// A quoted string can be embedded in an argument.
///
/// A double quotation mark preceded by a backslash (\") is interpreted as a
/// literal double quotation mark character (").
///
/// Backslashes are interpreted literally, unless they immediately precede a
/// double quotation mark.
///
/// If an even number of backslashes is followed by a double quotation mark,
/// one backslash is placed in the argv array for every pair of backslashes,
/// and the double quotation mark is interpreted as a string delimiter.
///
/// If an odd number of backslashes is followed by a double quotation mark,
/// one backslash is placed in the argv array for every pair of backslashes,
/// and the double quotation mark is "escaped" by the remaining backslash,
/// causing a literal double quotation mark (") to be placed in argv.
/// </remarks>
internal static class CommandLineSplitter
{
public static bool IsDelimiter(char c)
{
return c == ' ' || c == '\t';
}
public static bool IsQuote(char c)
{
// Only double quotes are respected, according to MSDN.
return c == '\"';
}
private const char Backslash = '\\';
// Split a command line by the same rules as Main would get the commands.
public static string[] SplitCommandLine(string commandLine)
{
bool inQuotes = false;
int backslashCount = 0;
return commandLine.Split(c =>
{
if (c == Backslash)
{
backslashCount += 1;
}
else if (IsQuote(c))
{
if ((backslashCount & 1) != 1)
{
inQuotes = !inQuotes;
}
backslashCount = 0;
}
else
{
backslashCount = 0;
}
return !inQuotes && IsDelimiter(c);
})
.Select(arg => arg.Trim().CondenseDoubledBackslashes().TrimMatchingQuotes())
.Where(arg => !string.IsNullOrEmpty(arg))
.ToArray();
}
// Split a string, based on whether "splitHere" returned true on each character.
private static IEnumerable<string> Split(this string str,
Func<char, bool> splitHere)
{
int nextPiece = 0;
for (int c = 0; c < str.Length; c++)
{
if (splitHere(str[c]))
{
yield return str.Substring(nextPiece, c - nextPiece);
nextPiece = c + 1;
}
}
yield return str.Substring(nextPiece);
}
// Trim leading and trailing quotes from a string, if they are there. Only trims
// one pair.
private static string TrimMatchingQuotes(this string input)
{
if ((input.Length >= 2) &&
(IsQuote(input[0])) &&
(IsQuote(input[input.Length - 1])))
{
return input.Substring(1, input.Length - 2);
}
else
{
return input;
}
}
// Condense double backslashes that precede a quotation mark to single backslashes.
private static string CondenseDoubledBackslashes(this string input)
{
// Simple case -- no backslashes.
if (!input.Contains(Backslash))
return input;
StringBuilder builder = new StringBuilder();
int doubleQuoteCount = 0;
foreach (char c in input)
{
if (c == Backslash)
{
++doubleQuoteCount;
}
else
{
// Add right amount of pending backslashes.
if (IsQuote(c))
{
AddBackslashes(builder, doubleQuoteCount / 2);
}
else
{
AddBackslashes(builder, doubleQuoteCount);
}
builder.Append(c);
doubleQuoteCount = 0;
}
}
AddBackslashes(builder, doubleQuoteCount);
return builder.ToString();
}
private static void AddBackslashes(StringBuilder builder, int count)
{
for (int i = 0; i < count; ++i)
builder.Append(Backslash);
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册