diff --git a/src/mscorlib/shared/System/Convert.cs b/src/mscorlib/shared/System/Convert.cs index 02fbbdbc3e69..b16eb7bf2270 100644 --- a/src/mscorlib/shared/System/Convert.cs +++ b/src/mscorlib/shared/System/Convert.cs @@ -2276,7 +2276,13 @@ public static byte ToByte(String value, int fromBase) throw new ArgumentException(SR.Arg_InvalidBase); } Contract.EndContractBlock(); - int r = ParseNumbers.StringToInt(value, fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsUnsigned); + + if (value == null) + { + return 0; + } + + int r = ParseNumbers.StringToInt(value.AsReadOnlySpan(), fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsUnsigned); if (r < Byte.MinValue || r > Byte.MaxValue) ThrowByteOverflowException(); return (byte)r; @@ -2294,7 +2300,13 @@ public static sbyte ToSByte(String value, int fromBase) throw new ArgumentException(SR.Arg_InvalidBase); } Contract.EndContractBlock(); - int r = ParseNumbers.StringToInt(value, fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsI1); + + if (value == null) + { + return 0; + } + + int r = ParseNumbers.StringToInt(value.AsReadOnlySpan(), fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsI1); if (fromBase != 10 && r <= Byte.MaxValue) return (sbyte)r; @@ -2314,7 +2326,13 @@ public static short ToInt16(String value, int fromBase) throw new ArgumentException(SR.Arg_InvalidBase); } Contract.EndContractBlock(); - int r = ParseNumbers.StringToInt(value, fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsI2); + + if (value == null) + { + return 0; + } + + int r = ParseNumbers.StringToInt(value.AsReadOnlySpan(), fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsI2); if (fromBase != 10 && r <= UInt16.MaxValue) return (short)r; @@ -2335,7 +2353,13 @@ public static ushort ToUInt16(String value, int fromBase) throw new ArgumentException(SR.Arg_InvalidBase); } Contract.EndContractBlock(); - int r = ParseNumbers.StringToInt(value, fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsUnsigned); + + if (value == null) + { + return 0; + } + + int r = ParseNumbers.StringToInt(value.AsReadOnlySpan(), fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsUnsigned); if (r < UInt16.MinValue || r > UInt16.MaxValue) ThrowUInt16OverflowException(); return (ushort)r; @@ -2352,7 +2376,9 @@ public static int ToInt32(String value, int fromBase) throw new ArgumentException(SR.Arg_InvalidBase); } Contract.EndContractBlock(); - return ParseNumbers.StringToInt(value, fromBase, ParseNumbers.IsTight); + return value != null ? + ParseNumbers.StringToInt(value.AsReadOnlySpan(), fromBase, ParseNumbers.IsTight) : + 0; } // Parses value in base fromBase. fromBase can only @@ -2367,7 +2393,9 @@ public static uint ToUInt32(String value, int fromBase) throw new ArgumentException(SR.Arg_InvalidBase); } Contract.EndContractBlock(); - return (uint)ParseNumbers.StringToInt(value, fromBase, ParseNumbers.TreatAsUnsigned | ParseNumbers.IsTight); + return value != null ? + (uint)ParseNumbers.StringToInt(value.AsReadOnlySpan(), fromBase, ParseNumbers.TreatAsUnsigned | ParseNumbers.IsTight) : + 0; } // Parses value in base fromBase. fromBase can only @@ -2381,7 +2409,9 @@ public static long ToInt64(String value, int fromBase) throw new ArgumentException(SR.Arg_InvalidBase); } Contract.EndContractBlock(); - return ParseNumbers.StringToLong(value, fromBase, ParseNumbers.IsTight); + return value != null ? + ParseNumbers.StringToLong(value.AsReadOnlySpan(), fromBase, ParseNumbers.IsTight) : + 0; } // Parses value in base fromBase. fromBase can only @@ -2396,7 +2426,9 @@ public static ulong ToUInt64(String value, int fromBase) throw new ArgumentException(SR.Arg_InvalidBase); } Contract.EndContractBlock(); - return (ulong)ParseNumbers.StringToLong(value, fromBase, ParseNumbers.TreatAsUnsigned | ParseNumbers.IsTight); + return value != null ? + (ulong)ParseNumbers.StringToLong(value.AsReadOnlySpan(), fromBase, ParseNumbers.TreatAsUnsigned | ParseNumbers.IsTight) : + 0; } // Convert the byte value to a string in base fromBase diff --git a/src/mscorlib/shared/System/Guid.cs b/src/mscorlib/shared/System/Guid.cs index b8b4dd5f0fb2..4a3855b1063b 100644 --- a/src/mscorlib/shared/System/Guid.cs +++ b/src/mscorlib/shared/System/Guid.cs @@ -2,10 +2,10 @@ // 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.Runtime.InteropServices; -using System.Runtime.CompilerServices; using System.Diagnostics; using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System { @@ -225,7 +225,7 @@ internal Exception GetGuidParseException() return _innerException; default: - Debug.Assert(false, "Unknown GuidParseFailure: " + _failure); + Debug.Fail("Unknown GuidParseFailure: " + _failure); return new FormatException(SR.Format_GuidUnrecognized); } } @@ -249,7 +249,7 @@ public Guid(string g) GuidResult result = new GuidResult(); result.Init(GuidParseThrowStyle.All); - if (TryParseGuid(g, GuidStyles.Any, ref result)) + if (TryParseGuid(g.AsReadOnlySpan(), GuidStyles.Any, ref result)) { this = result._parsedGuid; } @@ -259,14 +259,11 @@ public Guid(string g) } } - public static Guid Parse(string input) - { - if (input == null) - { - throw new ArgumentNullException(nameof(input)); - } - Contract.EndContractBlock(); + public static Guid Parse(string input) => + Parse(input != null ? input.AsReadOnlySpan() : throw new ArgumentNullException(nameof(input))); + public static Guid Parse(ReadOnlySpan input) + { GuidResult result = new GuidResult(); result.Init(GuidParseThrowStyle.AllButOverflow); if (TryParseGuid(input, GuidStyles.Any, ref result)) @@ -280,6 +277,17 @@ public static Guid Parse(string input) } public static bool TryParse(string input, out Guid result) + { + if (input == null) + { + result = default(Guid); + return false; + } + + return TryParse(input.AsReadOnlySpan(), out result); + } + + public static bool TryParse(ReadOnlySpan input, out Guid result) { GuidResult parseResult = new GuidResult(); parseResult.Init(GuidParseThrowStyle.None); @@ -290,19 +298,20 @@ public static bool TryParse(string input, out Guid result) } else { - result = Empty; + result = default(Guid); return false; } } - public static Guid ParseExact(string input, string format) - { - if (input == null) - throw new ArgumentNullException(nameof(input)); + public static Guid ParseExact(string input, string format) => + ParseExact(input != null ? input.AsReadOnlySpan() : throw new ArgumentNullException(nameof(input)), format); + public static Guid ParseExact(ReadOnlySpan input, string format) + { if (format == null) + { throw new ArgumentNullException(nameof(format)); - + } if (format.Length != 1) { // all acceptable format strings are of length 1 @@ -310,30 +319,30 @@ public static Guid ParseExact(string input, string format) } GuidStyles style; - char formatCh = format[0]; - if (formatCh == 'D' || formatCh == 'd') - { - style = GuidStyles.DigitFormat; - } - else if (formatCh == 'N' || formatCh == 'n') - { - style = GuidStyles.NumberFormat; - } - else if (formatCh == 'B' || formatCh == 'b') - { - style = GuidStyles.BraceFormat; - } - else if (formatCh == 'P' || formatCh == 'p') - { - style = GuidStyles.ParenthesisFormat; - } - else if (formatCh == 'X' || formatCh == 'x') - { - style = GuidStyles.HexFormat; - } - else + switch (format[0]) { - throw new FormatException(SR.Format_InvalidGuidFormatSpecification); + case 'D': + case 'd': + style = GuidStyles.DigitFormat; + break; + case 'N': + case 'n': + style = GuidStyles.NumberFormat; + break; + case 'B': + case 'b': + style = GuidStyles.BraceFormat; + break; + case 'P': + case 'p': + style = GuidStyles.ParenthesisFormat; + break; + case 'X': + case 'x': + style = GuidStyles.HexFormat; + break; + default: + throw new FormatException(SR.Format_InvalidGuidFormatSpecification); } GuidResult result = new GuidResult(); @@ -350,40 +359,50 @@ public static Guid ParseExact(string input, string format) public static bool TryParseExact(string input, string format, out Guid result) { - if (format == null || format.Length != 1) + if (input == null) { - result = Empty; + result = default(Guid); return false; } - GuidStyles style; - char formatCh = format[0]; + return TryParseExact(input.AsReadOnlySpan(), format, out result); + } - if (formatCh == 'D' || formatCh == 'd') - { - style = GuidStyles.DigitFormat; - } - else if (formatCh == 'N' || formatCh == 'n') - { - style = GuidStyles.NumberFormat; - } - else if (formatCh == 'B' || formatCh == 'b') - { - style = GuidStyles.BraceFormat; - } - else if (formatCh == 'P' || formatCh == 'p') - { - style = GuidStyles.ParenthesisFormat; - } - else if (formatCh == 'X' || formatCh == 'x') + public static bool TryParseExact(ReadOnlySpan input, string format, out Guid result) + { + if (format == null || format.Length != 1) { - style = GuidStyles.HexFormat; + result = default(Guid); + return false; } - else + + GuidStyles style; + switch (format[0]) { - // invalid guid format specification - result = Empty; - return false; + case 'D': + case 'd': + style = GuidStyles.DigitFormat; + break; + case 'N': + case 'n': + style = GuidStyles.NumberFormat; + break; + case 'B': + case 'b': + style = GuidStyles.BraceFormat; + break; + case 'P': + case 'p': + style = GuidStyles.ParenthesisFormat; + break; + case 'X': + case 'x': + style = GuidStyles.HexFormat; + break; + default: + // invalid guid format specification + result = default(Guid); + return false; } GuidResult parseResult = new GuidResult(); @@ -395,19 +414,14 @@ public static bool TryParseExact(string input, string format, out Guid result) } else { - result = Empty; + result = default(Guid); return false; } } - private static bool TryParseGuid(string g, GuidStyles flags, ref GuidResult result) + private static bool TryParseGuid(ReadOnlySpan guidString, GuidStyles flags, ref GuidResult result) { - if (g == null) - { - result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); - return false; - } - string guidString = g.Trim(); //Remove Whitespace + guidString = guidString.Trim(); // Remove whitespace from beginning and end if (guidString.Length == 0) { @@ -416,7 +430,7 @@ private static bool TryParseGuid(string g, GuidStyles flags, ref GuidResult resu } // Check for dashes - bool dashesExistInString = (guidString.IndexOf('-', 0) >= 0); + bool dashesExistInString = guidString.IndexOf('-') >= 0; if (dashesExistInString) { @@ -513,7 +527,7 @@ private static bool TryParseGuid(string g, GuidStyles flags, ref GuidResult resu } // Check if it's of the form {0xdddddddd,0xdddd,0xdddd,{0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd}} - private static bool TryParseGuidWithHexPrefix(string guidString, ref GuidResult result) + private static bool TryParseGuidWithHexPrefix(ReadOnlySpan guidString, ref GuidResult result) { int numStart = 0; int numLen = 0; @@ -522,7 +536,7 @@ private static bool TryParseGuidWithHexPrefix(string guidString, ref GuidResult guidString = EatAllWhitespace(guidString); // Check for leading '{' - if (string.IsNullOrEmpty(guidString) || guidString[0] != '{') + if (guidString.Length == 0 || guidString[0] != '{') { result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidBrace)); return false; @@ -544,7 +558,7 @@ private static bool TryParseGuidWithHexPrefix(string guidString, ref GuidResult return false; } - if (!StringToInt(guidString.Substring(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._a, ref result)) + if (!StringToInt(guidString.Slice(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._a, ref result)) return false; // Check for '0x' @@ -563,7 +577,7 @@ private static bool TryParseGuidWithHexPrefix(string guidString, ref GuidResult } // Read in the number - if (!StringToShort(guidString.Substring(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._b, ref result)) + if (!StringToShort(guidString.Slice(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._b, ref result)) return false; // Check for '0x' if (!IsHexPrefix(guidString, numStart + numLen + 1)) @@ -581,7 +595,7 @@ private static bool TryParseGuidWithHexPrefix(string guidString, ref GuidResult } // Read in the number - if (!StringToShort(guidString.Substring(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._c, ref result)) + if (!StringToShort(guidString.Slice(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._c, ref result)) return false; // Check for '{' @@ -593,9 +607,14 @@ private static bool TryParseGuidWithHexPrefix(string guidString, ref GuidResult // Prepare for loop numLen++; - byte[] bytes = new byte[8]; + Span bytes; + unsafe + { + byte* tmpBytes = stackalloc byte[8]; + bytes = new Span(tmpBytes, 8); + } - for (int i = 0; i < 8; i++) + for (int i = 0; i < bytes.Length; i++) { // Check for '0x' if (!IsHexPrefix(guidString, numStart + numLen + 1)) @@ -629,7 +648,7 @@ private static bool TryParseGuidWithHexPrefix(string guidString, ref GuidResult // Read in the number int signedNumber; - if (!StringToInt(guidString.Substring(numStart, numLen), -1, ParseNumbers.IsTight, out signedNumber, ref result)) + if (!StringToInt(guidString.Slice(numStart, numLen), -1, ParseNumbers.IsTight, out signedNumber, ref result)) { return false; } @@ -671,7 +690,7 @@ private static bool TryParseGuidWithHexPrefix(string guidString, ref GuidResult } // Check if it's of the form dddddddddddddddddddddddddddddddd - private static bool TryParseGuidWithNoStyle(string guidString, ref GuidResult result) + private static bool TryParseGuidWithNoStyle(ReadOnlySpan guidString, ref GuidResult result) { int startPos = 0; int temp; @@ -704,19 +723,19 @@ private static bool TryParseGuidWithNoStyle(string guidString, ref GuidResult re return false; } - if (!StringToInt(guidString.Substring(startPos, 8) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._a, ref result)) + if (!StringToInt(guidString.Slice(startPos, 8) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._a, ref result)) return false; startPos += 8; - if (!StringToShort(guidString.Substring(startPos, 4), -1, ParseNumbers.IsTight, out result._parsedGuid._b, ref result)) + if (!StringToShort(guidString.Slice(startPos, 4), -1, ParseNumbers.IsTight, out result._parsedGuid._b, ref result)) return false; startPos += 4; - if (!StringToShort(guidString.Substring(startPos, 4), -1, ParseNumbers.IsTight, out result._parsedGuid._c, ref result)) + if (!StringToShort(guidString.Slice(startPos, 4), -1, ParseNumbers.IsTight, out result._parsedGuid._c, ref result)) return false; startPos += 4; - if (!StringToInt(guidString.Substring(startPos, 4), -1, ParseNumbers.IsTight, out temp, ref result)) + if (!StringToInt(guidString.Slice(startPos, 4), -1, ParseNumbers.IsTight, out temp, ref result)) return false; startPos += 4; @@ -746,7 +765,7 @@ private static bool TryParseGuidWithNoStyle(string guidString, ref GuidResult re } // Check if it's of the form [{|(]dddddddd-dddd-dddd-dddd-dddddddddddd[}|)] - private static bool TryParseGuidWithDashes(string guidString, ref GuidResult result) + private static bool TryParseGuidWithDashes(ReadOnlySpan guidString, ref GuidResult result) { int startPos = 0; int temp; @@ -830,13 +849,13 @@ private static bool TryParseGuidWithDashes(string guidString, ref GuidResult res return true; } - private static bool StringToShort(string str, int requiredLength, int flags, out short result, ref GuidResult parseResult) + private static bool StringToShort(ReadOnlySpan str, int requiredLength, int flags, out short result, ref GuidResult parseResult) { int parsePos = 0; return StringToShort(str, ref parsePos, requiredLength, flags, out result, ref parseResult); } - private static bool StringToShort(string str, ref int parsePos, int requiredLength, int flags, out short result, ref GuidResult parseResult) + private static bool StringToShort(ReadOnlySpan str, ref int parsePos, int requiredLength, int flags, out short result, ref GuidResult parseResult) { result = 0; int x; @@ -845,13 +864,13 @@ private static bool StringToShort(string str, ref int parsePos, int requiredLeng return retValue; } - private static bool StringToInt(string str, int requiredLength, int flags, out int result, ref GuidResult parseResult) + private static bool StringToInt(ReadOnlySpan str, int requiredLength, int flags, out int result, ref GuidResult parseResult) { int parsePos = 0; return StringToInt(str, ref parsePos, requiredLength, flags, out result, ref parseResult); } - private static bool StringToInt(string str, ref int parsePos, int requiredLength, int flags, out int result, ref GuidResult parseResult) + private static bool StringToInt(ReadOnlySpan str, ref int parsePos, int requiredLength, int flags, out int result, ref GuidResult parseResult) { result = 0; @@ -898,7 +917,7 @@ private static bool StringToInt(string str, ref int parsePos, int requiredLength return true; } - private static unsafe bool StringToLong(string str, ref int parsePos, int flags, out long result, ref GuidResult parseResult) + private static unsafe bool StringToLong(ReadOnlySpan str, ref int parsePos, int flags, out long result, ref GuidResult parseResult) { result = 0; @@ -937,33 +956,43 @@ private static unsafe bool StringToLong(string str, ref int parsePos, int flags, return true; } - private static string EatAllWhitespace(string str) + private static ReadOnlySpan EatAllWhitespace(ReadOnlySpan str) { + // Find the first whitespace character. If there is none, just return the input. + int i; + for (i = 0; i < str.Length && !char.IsWhiteSpace(str[i]); i++) ; + if (i == str.Length) + { + return str; + } + + // There was at least one whitespace. Copy over everything prior to it to a new array. + var chArr = new char[str.Length]; int newLength = 0; - char[] chArr = new char[str.Length]; - char curChar; + if (i > 0) + { + newLength = i; + str.Slice(0, i).CopyTo(chArr); + } - // Now get each char from str and if it is not whitespace add it to chArr - for (int i = 0; i < str.Length; i++) + // Loop through the remaining chars, copying over non-whitespace. + for (; i < str.Length; i++) { - curChar = str[i]; - if (!char.IsWhiteSpace(curChar)) + char c = str[i]; + if (!char.IsWhiteSpace(c)) { - chArr[newLength++] = curChar; + chArr[newLength++] = c; } } - // Return a new string based on chArr - return new string(chArr, 0, newLength); + // Return the string with the whitespace removed. + return new ReadOnlySpan(chArr, 0, newLength); } - private static bool IsHexPrefix(string str, int i) - { - if (str.Length > i + 1 && str[i] == '0' && (char.ToLowerInvariant(str[i + 1]) == 'x')) - return true; - else - return false; - } + private static bool IsHexPrefix(ReadOnlySpan str, int i) => + i + 1 < str.Length && + str[i] == '0' && + (str[i + 1] == 'x' || char.ToLowerInvariant(str[i + 1]) == 'x'); [MethodImpl(MethodImplOptions.AggressiveInlining)] private void WriteByteHelper(Span destination) diff --git a/src/mscorlib/shared/System/ParseNumbers.cs b/src/mscorlib/shared/System/ParseNumbers.cs index f59bc323db45..3b02250b0eda 100644 --- a/src/mscorlib/shared/System/ParseNumbers.cs +++ b/src/mscorlib/shared/System/ParseNumbers.cs @@ -28,19 +28,14 @@ internal static class ParseNumbers private const int MinRadix = 2; private const int MaxRadix = 36; - public static unsafe long StringToLong(string s, int radix, int flags) + public static unsafe long StringToLong(ReadOnlySpan s, int radix, int flags) { int pos = 0; return StringToLong(s, radix, flags, ref pos); } - public static long StringToLong(string s, int radix, int flags, ref int currPos) + public static long StringToLong(ReadOnlySpan s, int radix, int flags, ref int currPos) { - if (s == null) - { - return 0; - } - int i = currPos; // Do some radix checking. @@ -120,19 +115,14 @@ public static long StringToLong(string s, int radix, int flags, ref int currPos) return result; } - public static int StringToInt(string s, int radix, int flags) + public static int StringToInt(ReadOnlySpan s, int radix, int flags) { int pos = 0; return StringToInt(s, radix, flags, ref pos); } - public static int StringToInt(string s, int radix, int flags, ref int currPos) + public static int StringToInt(ReadOnlySpan s, int radix, int flags, ref int currPos) { - if (s == null) - { - return 0; - } - // They're requied to tell me where to start parsing. int i = currPos; @@ -524,14 +514,14 @@ public static string LongToString(long n, int radix, int width, char paddingChar return result; } - private static void EatWhiteSpace(string s, ref int i) + private static void EatWhiteSpace(ReadOnlySpan s, ref int i) { int localIndex = i; for (; localIndex < s.Length && char.IsWhiteSpace(s[localIndex]); localIndex++); i = localIndex; } - private static long GrabLongs(int radix, string s, ref int i, bool isUnsigned) + private static long GrabLongs(int radix, ReadOnlySpan s, ref int i, bool isUnsigned) { ulong result = 0; ulong maxVal; @@ -592,7 +582,7 @@ private static long GrabLongs(int radix, string s, ref int i, bool isUnsigned) return (long)result; } - private static int GrabInts(int radix, string s, ref int i, bool isUnsigned) + private static int GrabInts(int radix, ReadOnlySpan s, ref int i, bool isUnsigned) { uint result = 0; uint maxVal;